Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
 help / color / mirror / Atom feed
* [FFmpeg-devel] [PATCH 00/24] Subtitle Filtering 2022
@ 2022-01-14  1:13 ffmpegagent
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 01/24] avcodec, avutil: Move enum AVSubtitleType to avutil, add new and deprecate old values ffmpegagent
                   ` (24 more replies)
  0 siblings, 25 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-14  1:13 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: softworkz


Subtitle Filtering 2022
=======================

This is a substantial update to the earlier subtitle filtering patch series.
A primary goal has been to address others' concerns as much as possible on
one side and to provide more clarity and control over the way things are
working. Clarity is is specifically important to allow for a better
understanding of the need for a subtitle start pts value that can be
different from the frame's pts value. This is done by refactoring the
subtitle timing fields in AVFrame, adding a frame field to indicate repeated
subtitle frames, and finally the full removal of the heartbeat
functionality, replaced by a new 'subfeed' filter that provides different
modes for arbitrating subtitle frames in a filter graph. Finally, each
subtitle filter's documentation has been amended by a section describing the
filter's timeline behavior (in v2 update).

The update also includes major improvements to graphicsub2text and lots of
other details.

Versioning is restarting at v1 due to the new submission procedure.

All changes from previous version v24:


AVFrame
=======

 * Removed sub_start_time The start time is now added to the subtitle
   start_pts during decoding The sub_end_time field is adjusted accordingly
 * Renamed sub_end_time to duration which it is effectively after removing
   the start_time
 * Added a sub-struct 'subtitle_timing' to av frame Contains subtitle_pts
   renamed to 'subtitle_timing.start_pts' and 'subtitle_timing.duration'
 * Change both fields to (fixed) time_base AV_TIMEBASE
 * add repeat_sub field provides a clear indication whether a subtitle frame
   is an actual subtitle event or a repeated subtitle frame in a filter
   graph


Heartbeat Removal
=================

 * completely removed the earlier heartbeat implementation
 * filtering arbitration is now implemented in a new filter: 'subfeed'
 * subfeed will be auto-inserted for compatiblity with sub2video command
   lines
 * the new behavior is not exactly identical to the earlier behavior, but it
   basically allows to achieve the same results


New 'subfeed' Filter
====================

 * a versatile filter for solving all kinds of problems with subtile frame
   flow in filter graphs
 * Can be inserted at any position in a graph
 * Auto-inserted for sub2video command lines (in repeat-mode)
 * Allows duration fixup delay input frames with unknown duration and infer
   duration from start of subsequent frame
 * Provides multiple modes of operation:
   * repeat mode (default) Queues input frames Outputs frames at a fixed
     (configurable) rate Either sends a matching input frame (repeatedly) or
     empty frames otherwise
   * scatter mode similar to repeat mode, but splits input frames by
     duration into small segments with same content
   * forward mode No fixed output rate Useful in combination with duration
     fixup or overlap fixup


ffmpeg Tool Changes
===================

 * delay subtitle output stream initialization (like for audio and video)
   This is needed for example when a format header depends on having
   received an initial frame to derive certain header values from
 * decoding: set subtitle frame size from decoding context
 * re-init graph when subtitle size changes
 * always insert subscale filter for sub2video command lines (to ensure
   correct scaling)


Subtitle Encoding
=================

 * ignore repeated frames for encoding based on repeat_sub field in AVFrame
 * support multi-area encoding for text subtitles Subtitle OCR can create
   multiple areas at different positions. Previously, the texts were always
   squashed into a single area ('subtitle rect'), which was not ideal.
   Multiple text areas are now generally supported:
   * ASS Encoder Changed to use the 'receive_packet' encoding API A single
     frame with multiple text areas will create multiple packets now
   * All other text subtitle encoders A newline is inserted between the text
     from multiple areas


graphicsub2text (OCR)
=====================

 * enhanced preprocessing
   * using elbg algorithm for color quantization
   * detection and removal of text outlines
   * map-based identification of colors per word (text, outline, background)
 * add option for duration fixup
 * add option to dump preprocessing bitmaps
 * Recognize formatting and apply as ASS inline styles
   * per word(!)
   * paragraph alignment
   * positioning
   * font names
   * font size
   * font style (italic, underline, bold)
   * text color, outline color


Other Filter Changes
====================

 * all: Make sure to forward all link properties (time base, frame rate, w,
   h) where appropriate
 * overlaytextsubs: request frames on the subtitle input
 * overlaytextsubs: disable read-order checking
 * overlaytextsubs: improve implementation of render_latest_only
 * overlaytextsubs: ensure equal in/out video formats
 * splitcc: derive framerate from realtime_latency
 * graphicsub2video: implement caching of converted frames
 * graphicsub2video: use 1x1 output frame size as long as subtitle size is
   unknown (0x0)

Plus a dozen of things I forgot..

softworkz (24):
  avcodec,avutil: Move enum AVSubtitleType to avutil, add new and
    deprecate old values
  avutil/frame: Prepare AVFrame for subtitle handling
  avcodec/subtitles: Introduce new frame-based subtitle decoding API
  avfilter/subtitles: Update vf_subtitles to use new decoding api
  avcodec,avutil: Move ass helper functions to avutil as avpriv_ and
    extend ass dialog parsing
  avcodec/subtitles: Migrate subtitle encoders to frame-based API
  avcodec/subtitles: Replace deprecated enum values
  fftools/play,probe: Adjust for subtitle changes
  avfilter/subtitles: Add subtitles.c for subtitle frame allocation
  avfilter/avfilter: Handle subtitle frames
  avfilter/avfilter: Fix hardcoded input index
  avfilter/sbuffer: Add sbuffersrc and sbuffersink filters
  avfilter/overlaygraphicsubs: Add overlaygraphicsubs and
    graphicsub2video filters
  avfilter/overlaytextsubs: Add overlaytextsubs and textsubs2video
    filters
  avfilter/textmod: Add textmod, censor and show_speaker filters
  avfilter/stripstyles: Add stripstyles filter
  avfilter/splitcc: Add splitcc filter for closed caption handling
  avfilter/graphicsub2text: Add new graphicsub2text filter (OCR)
  avfilter/subscale: Add filter for scaling and/or re-arranging
    graphical subtitles
  avfilter/subfeed: add subtitle feed filter
  fftools/ffmpeg: Introduce subtitle filtering new frame-based subtitle
    encoding
  avutil/ass_split: Add parsing of hard-space tags (\h)
  avcodec/webvttenc: convert hard-space tags to  
  doc/APIchanges: update for subtitle filtering changes

 configure                                     |    7 +-
 doc/APIchanges                                |   24 +
 doc/filters.texi                              |  756 ++++++++++
 fftools/ffmpeg.c                              |  493 +++----
 fftools/ffmpeg.h                              |   13 +-
 fftools/ffmpeg_filter.c                       |  235 +++-
 fftools/ffmpeg_hw.c                           |    2 +-
 fftools/ffmpeg_opt.c                          |    3 +-
 fftools/ffplay.c                              |  102 +-
 fftools/ffprobe.c                             |   47 +-
 libavcodec/Makefile                           |   56 +-
 libavcodec/ass.h                              |  144 +-
 libavcodec/assdec.c                           |    4 +-
 libavcodec/assenc.c                           |  191 ++-
 libavcodec/avcodec.h                          |   32 +-
 libavcodec/ccaption_dec.c                     |   19 +-
 libavcodec/codec_desc.c                       |   11 +
 libavcodec/codec_desc.h                       |    8 +
 libavcodec/decode.c                           |   56 +-
 libavcodec/dvbsubdec.c                        |    2 +-
 libavcodec/dvbsubenc.c                        |   96 +-
 libavcodec/dvdsubdec.c                        |    2 +-
 libavcodec/dvdsubenc.c                        |  102 +-
 libavcodec/encode.c                           |   57 +-
 libavcodec/internal.h                         |   22 +
 libavcodec/jacosubdec.c                       |    2 +-
 libavcodec/libaribb24.c                       |    2 +-
 libavcodec/libzvbi-teletextdec.c              |   14 +-
 libavcodec/microdvddec.c                      |    7 +-
 libavcodec/movtextdec.c                       |    3 +-
 libavcodec/movtextenc.c                       |  126 +-
 libavcodec/mpl2dec.c                          |    2 +-
 libavcodec/pgssubdec.c                        |    2 +-
 libavcodec/realtextdec.c                      |    2 +-
 libavcodec/samidec.c                          |    2 +-
 libavcodec/srtdec.c                           |    2 +-
 libavcodec/srtenc.c                           |  116 +-
 libavcodec/subviewerdec.c                     |    2 +-
 libavcodec/tests/avcodec.c                    |    2 -
 libavcodec/textdec.c                          |    4 +-
 libavcodec/ttmlenc.c                          |  114 +-
 libavcodec/utils.c                            |  184 +++
 libavcodec/version.h                          |    2 +-
 libavcodec/webvttdec.c                        |    2 +-
 libavcodec/webvttenc.c                        |  100 +-
 libavcodec/xsubdec.c                          |    2 +-
 libavcodec/xsubenc.c                          |   88 +-
 libavfilter/Makefile                          |   16 +
 libavfilter/allfilters.c                      |   14 +
 libavfilter/avfilter.c                        |   30 +-
 libavfilter/avfilter.h                        |   11 +
 libavfilter/avfiltergraph.c                   |    5 +
 libavfilter/buffersink.c                      |   54 +
 libavfilter/buffersink.h                      |    7 +
 libavfilter/buffersrc.c                       |   72 +
 libavfilter/buffersrc.h                       |    1 +
 libavfilter/formats.c                         |   22 +
 libavfilter/formats.h                         |    3 +
 libavfilter/internal.h                        |   19 +-
 libavfilter/sf_graphicsub2text.c              | 1132 +++++++++++++++
 libavfilter/sf_splitcc.c                      |  385 +++++
 libavfilter/sf_stripstyles.c                  |  209 +++
 libavfilter/sf_subfeed.c                      |  366 +++++
 libavfilter/sf_subscale.c                     |  884 ++++++++++++
 libavfilter/sf_textmod.c                      |  710 ++++++++++
 libavfilter/subtitles.c                       |   63 +
 libavfilter/subtitles.h                       |   44 +
 libavfilter/vf_overlaygraphicsubs.c           |  765 ++++++++++
 libavfilter/vf_overlaytextsubs.c              |  671 +++++++++
 libavfilter/vf_subtitles.c                    |   56 +-
 libavutil/Makefile                            |    4 +
 {libavcodec => libavutil}/ass.c               |   91 +-
 libavutil/ass_internal.h                      |  135 ++
 {libavcodec => libavutil}/ass_split.c         |   37 +-
 .../ass_split_internal.h                      |   32 +-
 libavutil/frame.c                             |  211 ++-
 libavutil/frame.h                             |   85 +-
 libavutil/subfmt.c                            |   45 +
 libavutil/subfmt.h                            |  115 ++
 libavutil/version.h                           |    3 +-
 tests/ref/fate/filter-overlay-dvdsub-2397     |  182 +--
 tests/ref/fate/mov-mp4-ttml-dfxp              |    8 +-
 tests/ref/fate/mov-mp4-ttml-stpp              |    8 +-
 tests/ref/fate/sub-dvb                        |  162 ++-
 tests/ref/fate/sub-textenc                    |   10 +-
 tests/ref/fate/sub-ttmlenc                    |    8 +-
 tests/ref/fate/sub-webvttenc                  |   10 +-
 tests/ref/fate/sub2video                      | 1091 ++++++++++++++-
 tests/ref/fate/sub2video_basic                | 1239 +++++++++++++++--
 tests/ref/fate/sub2video_time_limited         |   78 +-
 90 files changed, 10991 insertions(+), 1366 deletions(-)
 create mode 100644 libavfilter/sf_graphicsub2text.c
 create mode 100644 libavfilter/sf_splitcc.c
 create mode 100644 libavfilter/sf_stripstyles.c
 create mode 100644 libavfilter/sf_subfeed.c
 create mode 100644 libavfilter/sf_subscale.c
 create mode 100644 libavfilter/sf_textmod.c
 create mode 100644 libavfilter/subtitles.c
 create mode 100644 libavfilter/subtitles.h
 create mode 100644 libavfilter/vf_overlaygraphicsubs.c
 create mode 100644 libavfilter/vf_overlaytextsubs.c
 rename {libavcodec => libavutil}/ass.c (65%)
 create mode 100644 libavutil/ass_internal.h
 rename {libavcodec => libavutil}/ass_split.c (93%)
 rename libavcodec/ass_split.h => libavutil/ass_split_internal.h (86%)
 create mode 100644 libavutil/subfmt.c
 create mode 100644 libavutil/subfmt.h


base-commit: c936c319bd54f097cc1d75b1ee1c407d53215d71
Published-As: https://github.com/ffstaging/FFmpeg/releases/tag/pr-ffstaging-18%2Fsoftworkz%2Fsubmit_subfiltering-v1
Fetch-It-Via: git fetch https://github.com/ffstaging/FFmpeg pr-ffstaging-18/softworkz/submit_subfiltering-v1
Pull-Request: https://github.com/ffstaging/FFmpeg/pull/18
-- 
ffmpeg-codebot
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH 01/24] avcodec, avutil: Move enum AVSubtitleType to avutil, add new and deprecate old values
  2022-01-14  1:13 [FFmpeg-devel] [PATCH 00/24] Subtitle Filtering 2022 ffmpegagent
@ 2022-01-14  1:13 ` ffmpegagent
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 02/24] avutil/frame: Prepare AVFrame for subtitle handling ffmpegagent
                   ` (23 subsequent siblings)
  24 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-14  1:13 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: softworkz

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/avcodec.h | 19 +------------
 libavutil/Makefile   |  1 +
 libavutil/subfmt.h   | 68 ++++++++++++++++++++++++++++++++++++++++++++
 libavutil/version.h  |  1 +
 4 files changed, 71 insertions(+), 18 deletions(-)
 create mode 100644 libavutil/subfmt.h

diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index ec1a0566a4..fe5a83cf85 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -35,6 +35,7 @@
 #include "libavutil/frame.h"
 #include "libavutil/log.h"
 #include "libavutil/pixfmt.h"
+#include "libavutil/subfmt.h"
 #include "libavutil/rational.h"
 
 #include "codec.h"
@@ -2238,24 +2239,6 @@ typedef struct AVHWAccel {
  * @}
  */
 
-enum AVSubtitleType {
-    SUBTITLE_NONE,
-
-    SUBTITLE_BITMAP,                ///< A bitmap, pict will be set
-
-    /**
-     * Plain text, the text field must be set by the decoder and is
-     * authoritative. ass and pict fields may contain approximations.
-     */
-    SUBTITLE_TEXT,
-
-    /**
-     * Formatted text, the ass field must be set by the decoder and is
-     * authoritative. pict and text fields may contain approximations.
-     */
-    SUBTITLE_ASS,
-};
-
 #define AV_SUBTITLE_FLAG_FORCED 0x00000001
 
 typedef struct AVSubtitleRect {
diff --git a/libavutil/Makefile b/libavutil/Makefile
index d17876df1a..ce644f4d48 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -74,6 +74,7 @@ HEADERS = adler32.h                                                     \
           sha512.h                                                      \
           spherical.h                                                   \
           stereo3d.h                                                    \
+          subfmt.h                                                      \
           threadmessage.h                                               \
           time.h                                                        \
           timecode.h                                                    \
diff --git a/libavutil/subfmt.h b/libavutil/subfmt.h
new file mode 100644
index 0000000000..791b45519f
--- /dev/null
+++ b/libavutil/subfmt.h
@@ -0,0 +1,68 @@
+/*
+ * 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
+ */
+
+#ifndef AVUTIL_SUBFMT_H
+#define AVUTIL_SUBFMT_H
+
+#include "version.h"
+
+enum AVSubtitleType {
+
+    /**
+     * Subtitle format unknown.
+     */
+    AV_SUBTITLE_FMT_NONE = -1,
+
+    /**
+     * Subtitle format unknown.
+     */
+    AV_SUBTITLE_FMT_UNKNOWN = 0,
+#if FF_API_OLD_SUBTITLES
+    SUBTITLE_NONE = 0,          ///< Deprecated, use AV_SUBTITLE_FMT_NONE instead.
+#endif
+
+    /**
+     * Bitmap area in AVSubtitleRect.data, pixfmt AV_PIX_FMT_PAL8.
+     */
+    AV_SUBTITLE_FMT_BITMAP = 1,
+#if FF_API_OLD_SUBTITLES
+    SUBTITLE_BITMAP = 1,        ///< Deprecated, use AV_SUBTITLE_FMT_BITMAP instead.
+#endif
+
+    /**
+     * Plain text in AVSubtitleRect.text.
+     */
+    AV_SUBTITLE_FMT_TEXT = 2,
+#if FF_API_OLD_SUBTITLES
+    SUBTITLE_TEXT = 2,          ///< Deprecated, use AV_SUBTITLE_FMT_TEXT instead.
+#endif
+
+    /**
+     * Text Formatted as per ASS specification, contained AVSubtitleRect.ass.
+     */
+    AV_SUBTITLE_FMT_ASS = 3,
+#if FF_API_OLD_SUBTITLES
+    SUBTITLE_ASS = 3,           ///< Deprecated, use AV_SUBTITLE_FMT_ASS instead.
+#endif
+
+    AV_SUBTITLE_FMT_NB,         ///< number of subtitle formats, DO NOT USE THIS if you want to link with shared libav* because the number of formats might differ between versions.
+};
+
+#endif /* AVUTIL_SUBFMT_H */
diff --git a/libavutil/version.h b/libavutil/version.h
index 953aac9d94..5bf48f6304 100644
--- a/libavutil/version.h
+++ b/libavutil/version.h
@@ -110,6 +110,7 @@
 #define FF_API_COLORSPACE_NAME          (LIBAVUTIL_VERSION_MAJOR < 58)
 #define FF_API_AV_MALLOCZ_ARRAY         (LIBAVUTIL_VERSION_MAJOR < 58)
 #define FF_API_FIFO_PEEK2               (LIBAVUTIL_VERSION_MAJOR < 58)
+#define FF_API_OLD_SUBTITLES            (LIBAVUTIL_VERSION_MAJOR < 58)
 
 /**
  * @}
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH 02/24] avutil/frame: Prepare AVFrame for subtitle handling
  2022-01-14  1:13 [FFmpeg-devel] [PATCH 00/24] Subtitle Filtering 2022 ffmpegagent
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 01/24] avcodec, avutil: Move enum AVSubtitleType to avutil, add new and deprecate old values ffmpegagent
@ 2022-01-14  1:13 ` ffmpegagent
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 03/24] avcodec/subtitles: Introduce new frame-based subtitle decoding API ffmpegagent
                   ` (22 subsequent siblings)
  24 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-14  1:13 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: softworkz

From: softworkz <softworkz@hotmail.com>

Root commit for adding subtitle filtering capabilities.
In detail:

- Add type (AVMediaType) field to AVFrame
  Replaces previous way of distinction which was based on checking
  width and height to determine whether a frame is audio or video
- Add subtitle fields to AVFrame
- Add new struct AVSubtitleArea, similar to AVSubtitleRect, but
  different allocation logic. Cannot and must not be used
  interchangeably, hence the new struct

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavutil/Makefile |   1 +
 libavutil/frame.c  | 211 ++++++++++++++++++++++++++++++++++++++++-----
 libavutil/frame.h  |  85 +++++++++++++++++-
 libavutil/subfmt.c |  45 ++++++++++
 libavutil/subfmt.h |  47 ++++++++++
 5 files changed, 364 insertions(+), 25 deletions(-)
 create mode 100644 libavutil/subfmt.c

diff --git a/libavutil/Makefile b/libavutil/Makefile
index ce644f4d48..8bc0a14942 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -160,6 +160,7 @@ OBJS = adler32.o                                                        \
        slicethread.o                                                    \
        spherical.o                                                      \
        stereo3d.o                                                       \
+       subfmt.o                                                         \
        threadmessage.o                                                  \
        time.o                                                           \
        timecode.o                                                       \
diff --git a/libavutil/frame.c b/libavutil/frame.c
index 8997c85e35..2b95830b6f 100644
--- a/libavutil/frame.c
+++ b/libavutil/frame.c
@@ -26,6 +26,7 @@
 #include "imgutils.h"
 #include "mem.h"
 #include "samplefmt.h"
+#include "subfmt.h"
 #include "hwcontext.h"
 
 #define CHECK_CHANNELS_CONSISTENCY(frame) \
@@ -50,6 +51,9 @@ const char *av_get_colorspace_name(enum AVColorSpace val)
     return name[val];
 }
 #endif
+
+static int frame_copy_subtitles(AVFrame *dst, const AVFrame *src, int copy_data);
+
 static void get_frame_defaults(AVFrame *frame)
 {
     memset(frame, 0, sizeof(*frame));
@@ -70,7 +74,12 @@ static void get_frame_defaults(AVFrame *frame)
     frame->colorspace          = AVCOL_SPC_UNSPECIFIED;
     frame->color_range         = AVCOL_RANGE_UNSPECIFIED;
     frame->chroma_location     = AVCHROMA_LOC_UNSPECIFIED;
-    frame->flags               = 0;
+    frame->num_subtitle_areas  = 0;
+    frame->subtitle_areas      = NULL;
+    frame->subtitle_header     = NULL;
+    frame->repeat_sub          = 0;
+    frame->subtitle_timing.start_pts = 0;
+    frame->subtitle_timing.duration  = 0;
 }
 
 static void free_side_data(AVFrameSideData **ptr_sd)
@@ -240,23 +249,55 @@ static int get_audio_buffer(AVFrame *frame, int align)
 
 }
 
+static int get_subtitle_buffer(AVFrame *frame)
+{
+    // Buffers in AVFrame->buf[] are not used in case of subtitle frames.
+    // To accomodate with existing code, checking ->buf[0] to determine
+    // whether a frame is ref-counted or has data, we're adding a 1-byte
+    // buffer here, which marks the subtitle frame to contain data.
+    frame->buf[0] = av_buffer_alloc(1);
+    if (!frame->buf[0]) {
+        av_frame_unref(frame);
+        return AVERROR(ENOMEM);
+    }
+
+    frame->extended_data = frame->data;
+
+    return 0;
+}
+
 int av_frame_get_buffer(AVFrame *frame, int align)
+{
+    if (frame->width > 0 && frame->height > 0)
+        frame->type = AVMEDIA_TYPE_VIDEO;
+    else if (frame->nb_samples > 0 && (frame->channel_layout || frame->channels > 0))
+        frame->type = AVMEDIA_TYPE_AUDIO;
+
+    return av_frame_get_buffer2(frame, align);
+}
+
+int av_frame_get_buffer2(AVFrame *frame, int align)
 {
     if (frame->format < 0)
         return AVERROR(EINVAL);
 
-    if (frame->width > 0 && frame->height > 0)
+    switch(frame->type) {
+    case AVMEDIA_TYPE_VIDEO:
         return get_video_buffer(frame, align);
-    else if (frame->nb_samples > 0 && (frame->channel_layout || frame->channels > 0))
+    case AVMEDIA_TYPE_AUDIO:
         return get_audio_buffer(frame, align);
-
-    return AVERROR(EINVAL);
+    case AVMEDIA_TYPE_SUBTITLE:
+        return get_subtitle_buffer(frame);
+    default:
+        return AVERROR(EINVAL);
+    }
 }
 
 static int frame_copy_props(AVFrame *dst, const AVFrame *src, int force_copy)
 {
     int ret, i;
 
+    dst->type                   = src->type;
     dst->key_frame              = src->key_frame;
     dst->pict_type              = src->pict_type;
     dst->sample_aspect_ratio    = src->sample_aspect_ratio;
@@ -288,6 +329,12 @@ static int frame_copy_props(AVFrame *dst, const AVFrame *src, int force_copy)
     dst->colorspace             = src->colorspace;
     dst->color_range            = src->color_range;
     dst->chroma_location        = src->chroma_location;
+    dst->repeat_sub             = src->repeat_sub;
+    dst->subtitle_timing.start_pts = src->subtitle_timing.start_pts;
+    dst->subtitle_timing.duration  = src->subtitle_timing.duration;
+
+    if ((ret = av_buffer_replace(&dst->subtitle_header, src->subtitle_header)) < 0)
+        return ret;
 
     av_dict_copy(&dst->metadata, src->metadata, 0);
 
@@ -329,6 +376,7 @@ int av_frame_ref(AVFrame *dst, const AVFrame *src)
     av_assert1(dst->width == 0 && dst->height == 0);
     av_assert1(dst->channels == 0);
 
+    dst->type           = src->type;
     dst->format         = src->format;
     dst->width          = src->width;
     dst->height         = src->height;
@@ -342,7 +390,7 @@ int av_frame_ref(AVFrame *dst, const AVFrame *src)
 
     /* duplicate the frame data if it's not refcounted */
     if (!src->buf[0]) {
-        ret = av_frame_get_buffer(dst, 0);
+        ret = av_frame_get_buffer2(dst, 0);
         if (ret < 0)
             goto fail;
 
@@ -364,6 +412,10 @@ int av_frame_ref(AVFrame *dst, const AVFrame *src)
         }
     }
 
+    /* copy subtitle areas */
+    if (src->type == AVMEDIA_TYPE_SUBTITLE)
+        frame_copy_subtitles(dst, src, 0);
+
     if (src->extended_buf) {
         dst->extended_buf = av_calloc(src->nb_extended_buf,
                                       sizeof(*dst->extended_buf));
@@ -434,7 +486,7 @@ AVFrame *av_frame_clone(const AVFrame *src)
 
 void av_frame_unref(AVFrame *frame)
 {
-    int i;
+    unsigned i, n;
 
     if (!frame)
         return;
@@ -453,6 +505,21 @@ void av_frame_unref(AVFrame *frame)
     av_buffer_unref(&frame->opaque_ref);
     av_buffer_unref(&frame->private_ref);
 
+    av_buffer_unref(&frame->subtitle_header);
+
+    for (i = 0; i < frame->num_subtitle_areas; i++) {
+        AVSubtitleArea *area = frame->subtitle_areas[i];
+
+        for (n = 0; n < FF_ARRAY_ELEMS(area->buf); n++)
+            av_buffer_unref(&area->buf[n]);
+
+        av_freep(&area->text);
+        av_freep(&area->ass);
+        av_free(area);
+    }
+
+    av_freep(&frame->subtitle_areas);
+
     if (frame->extended_data != frame->data)
         av_freep(&frame->extended_data);
 
@@ -472,18 +539,28 @@ void av_frame_move_ref(AVFrame *dst, AVFrame *src)
 
 int av_frame_is_writable(AVFrame *frame)
 {
-    int i, ret = 1;
+    AVSubtitleArea *area;
+    int ret = 1;
 
     /* assume non-refcounted frames are not writable */
     if (!frame->buf[0])
         return 0;
 
-    for (i = 0; i < FF_ARRAY_ELEMS(frame->buf); i++)
+    for (unsigned i = 0; i < FF_ARRAY_ELEMS(frame->buf); i++)
         if (frame->buf[i])
             ret &= !!av_buffer_is_writable(frame->buf[i]);
-    for (i = 0; i < frame->nb_extended_buf; i++)
+    for (unsigned i = 0; i < frame->nb_extended_buf; i++)
         ret &= !!av_buffer_is_writable(frame->extended_buf[i]);
 
+    for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+        area = frame->subtitle_areas[i];
+        if (area) {
+            for (unsigned n = 0; n < FF_ARRAY_ELEMS(area->buf); n++)
+                if (area->buf[n])
+                    ret &= !!av_buffer_is_writable(area->buf[n]);
+            }
+    }
+
     return ret;
 }
 
@@ -499,6 +576,7 @@ int av_frame_make_writable(AVFrame *frame)
         return 0;
 
     memset(&tmp, 0, sizeof(tmp));
+    tmp.type           = frame->type;
     tmp.format         = frame->format;
     tmp.width          = frame->width;
     tmp.height         = frame->height;
@@ -509,7 +587,7 @@ int av_frame_make_writable(AVFrame *frame)
     if (frame->hw_frames_ctx)
         ret = av_hwframe_get_buffer(frame->hw_frames_ctx, &tmp, 0);
     else
-        ret = av_frame_get_buffer(&tmp, 0);
+        ret = av_frame_get_buffer2(&tmp, 0);
     if (ret < 0)
         return ret;
 
@@ -544,14 +622,22 @@ AVBufferRef *av_frame_get_plane_buffer(AVFrame *frame, int plane)
     uint8_t *data;
     int planes, i;
 
-    if (frame->nb_samples) {
-        int channels = frame->channels;
-        if (!channels)
-            return NULL;
-        CHECK_CHANNELS_CONSISTENCY(frame);
-        planes = av_sample_fmt_is_planar(frame->format) ? channels : 1;
-    } else
+    switch(frame->type) {
+    case AVMEDIA_TYPE_VIDEO:
         planes = 4;
+        break;
+    case AVMEDIA_TYPE_AUDIO:
+        {
+            int channels = frame->channels;
+            if (!channels)
+                return NULL;
+            CHECK_CHANNELS_CONSISTENCY(frame);
+            planes = av_sample_fmt_is_planar(frame->format) ? channels : 1;
+            break;
+        }
+    default:
+        return NULL;
+    }
 
     if (plane < 0 || plane >= planes || !frame->extended_data[plane])
         return NULL;
@@ -675,17 +761,98 @@ static int frame_copy_audio(AVFrame *dst, const AVFrame *src)
     return 0;
 }
 
+static int av_subtitle_area2area(AVSubtitleArea *dst, const AVSubtitleArea *src, int copy_data)
+{
+    dst->x         =  src->x;
+    dst->y         =  src->y;
+    dst->w         =  src->w;
+    dst->h         =  src->h;
+    dst->nb_colors =  src->nb_colors;
+    dst->type      =  src->type;
+    dst->flags     =  src->flags;
+
+    switch (dst->type) {
+    case AV_SUBTITLE_FMT_BITMAP:
+
+        for (unsigned i = 0; i < AV_NUM_BUFFER_POINTERS; i++) {
+            if (src->h > 0 && src->w > 0 && src->buf[i]) {
+                dst->buf[0] = av_buffer_ref(src->buf[i]);
+                if (!dst->buf[i])
+                    return AVERROR(ENOMEM);
+
+                if (copy_data) {
+                    const int ret = av_buffer_make_writable(&dst->buf[i]);
+                    if (ret < 0)
+                        return ret;
+                }
+
+                dst->linesize[i] = src->linesize[i];
+            }
+        }
+
+        memcpy(&dst->pal[0], &src->pal[0], sizeof(src->pal[0]) * 256);
+
+        break;
+    case AV_SUBTITLE_FMT_TEXT:
+
+        if (src->text) {
+            dst->text = av_strdup(src->text);
+            if (!dst->text)
+                return AVERROR(ENOMEM);
+        }
+
+        break;
+    case AV_SUBTITLE_FMT_ASS:
+
+        if (src->ass) {
+            dst->ass = av_strdup(src->ass);
+            if (!dst->ass)
+                return AVERROR(ENOMEM);
+        }
+
+        break;
+    default:
+
+        av_log(NULL, AV_LOG_ERROR, "Subtitle area has invalid format: %d", dst->type);
+        return AVERROR(ENOMEM);
+    }
+
+    return 0;
+}
+
+static int frame_copy_subtitles(AVFrame *dst, const AVFrame *src, int copy_data)
+{
+    dst->format = src->format;
+
+    dst->num_subtitle_areas = src->num_subtitle_areas;
+
+    if (src->num_subtitle_areas) {
+        dst->subtitle_areas = av_malloc_array(src->num_subtitle_areas, sizeof(AVSubtitleArea *));
+
+        for (unsigned i = 0; i < src->num_subtitle_areas; i++) {
+            dst->subtitle_areas[i] = av_mallocz(sizeof(AVSubtitleArea));
+            av_subtitle_area2area(dst->subtitle_areas[i], src->subtitle_areas[i], copy_data);
+        }
+    }
+
+    return 0;
+}
+
 int av_frame_copy(AVFrame *dst, const AVFrame *src)
 {
     if (dst->format != src->format || dst->format < 0)
         return AVERROR(EINVAL);
 
-    if (dst->width > 0 && dst->height > 0)
+    switch(dst->type) {
+    case AVMEDIA_TYPE_VIDEO:
         return frame_copy_video(dst, src);
-    else if (dst->nb_samples > 0 && dst->channels > 0)
+    case AVMEDIA_TYPE_AUDIO:
         return frame_copy_audio(dst, src);
-
-    return AVERROR(EINVAL);
+    case AVMEDIA_TYPE_SUBTITLE:
+        return frame_copy_subtitles(dst, src, 1);
+    default:
+        return AVERROR(EINVAL);
+    }
 }
 
 void av_frame_remove_side_data(AVFrame *frame, enum AVFrameSideDataType type)
diff --git a/libavutil/frame.h b/libavutil/frame.h
index 18e239f870..ed519c5e2b 100644
--- a/libavutil/frame.h
+++ b/libavutil/frame.h
@@ -25,7 +25,6 @@
 #ifndef AVUTIL_FRAME_H
 #define AVUTIL_FRAME_H
 
-#include <stddef.h>
 #include <stdint.h>
 
 #include "avutil.h"
@@ -34,6 +33,7 @@
 #include "rational.h"
 #include "samplefmt.h"
 #include "pixfmt.h"
+#include "subfmt.h"
 #include "version.h"
 
 
@@ -285,7 +285,7 @@ typedef struct AVRegionOfInterest {
 } AVRegionOfInterest;
 
 /**
- * This structure describes decoded (raw) audio or video data.
+ * This structure describes decoded (raw) audio, video or subtitle data.
  *
  * AVFrame must be allocated using av_frame_alloc(). Note that this only
  * allocates the AVFrame itself, the buffers for the data must be managed
@@ -399,7 +399,7 @@ typedef struct AVFrame {
     /**
      * format of the frame, -1 if unknown or unset
      * Values correspond to enum AVPixelFormat for video frames,
-     * enum AVSampleFormat for audio)
+     * enum AVSampleFormat for audio, AVSubtitleType for subtitles)
      */
     int format;
 
@@ -681,6 +681,53 @@ typedef struct AVFrame {
      * for the target frame's private_ref field.
      */
     AVBufferRef *private_ref;
+
+    /**
+     * Media type of the frame (audio, video, subtitles..)
+     *
+     * See AVMEDIA_TYPE_xxx
+     */
+    enum AVMediaType type;
+
+    /**
+     * Number of items in the @ref subtitle_areas array.
+     */
+    unsigned num_subtitle_areas;
+
+    /**
+     * Array of subtitle areas, may be empty.
+     */
+    AVSubtitleArea **subtitle_areas;
+
+    /**
+     * Header containing style information for text subtitles.
+     */
+    AVBufferRef *subtitle_header;
+
+    /**
+     * Indicates that a subtitle frame is a repeated frame for arbitrating flow
+     * in a filter graph.
+     * The field subtitle_timing.start_pts always indicates the original presentation
+     * time, while the frame's pts field may be different.
+     */
+    int repeat_sub;
+
+    struct SubtitleTiming
+    {
+        /**
+         * The display start time, in AV_TIME_BASE.
+         *
+         * For subtitle frames, AVFrame.pts is populated from the packet pts value,
+         * which is not always the same as this value.
+         */
+        int64_t start_pts;
+
+        /**
+         * Display duration, in AV_TIME_BASE.
+         */
+        int64_t duration;
+
+    } subtitle_timing;
 } AVFrame;
 
 
@@ -757,6 +804,8 @@ void av_frame_move_ref(AVFrame *dst, AVFrame *src);
 /**
  * Allocate new buffer(s) for audio or video data.
  *
+ * Note: For subtitle data, use av_frame_get_buffer2
+ *
  * The following fields must be set on frame before calling this function:
  * - format (pixel format for video, sample format for audio)
  * - width and height for video
@@ -776,9 +825,39 @@ void av_frame_move_ref(AVFrame *dst, AVFrame *src);
  *              recommended to pass 0 here unless you know what you are doing.
  *
  * @return 0 on success, a negative AVERROR on error.
+ *
+ * @deprecated Use @ref av_frame_get_buffer2 instead and set @ref AVFrame.type
+ * before calling.
  */
+attribute_deprecated
 int av_frame_get_buffer(AVFrame *frame, int align);
 
+/**
+ * Allocate new buffer(s) for audio, video or subtitle data.
+ *
+ * The following fields must be set on frame before calling this function:
+ * - format (pixel format for video, sample format for audio)
+ * - width and height for video
+ * - nb_samples and channel_layout for audio
+ * - type (AVMediaType)
+ *
+ * This function will fill AVFrame.data and AVFrame.buf arrays and, if
+ * necessary, allocate and fill AVFrame.extended_data and AVFrame.extended_buf.
+ * For planar formats, one buffer will be allocated for each plane.
+ *
+ * @warning: if frame already has been allocated, calling this function will
+ *           leak memory. In addition, undefined behavior can occur in certain
+ *           cases.
+ *
+ * @param frame frame in which to store the new buffers.
+ * @param align Required buffer size alignment. If equal to 0, alignment will be
+ *              chosen automatically for the current CPU. It is highly
+ *              recommended to pass 0 here unless you know what you are doing.
+ *
+ * @return 0 on success, a negative AVERROR on error.
+ */
+int av_frame_get_buffer2(AVFrame *frame, int align);
+
 /**
  * Check if the frame data is writable.
  *
diff --git a/libavutil/subfmt.c b/libavutil/subfmt.c
new file mode 100644
index 0000000000..c72ebe2a43
--- /dev/null
+++ b/libavutil/subfmt.c
@@ -0,0 +1,45 @@
+/*
+ * 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
+ */
+
+#include <string.h>
+#include "common.h"
+#include "subfmt.h"
+
+static const char sub_fmt_info[AV_SUBTITLE_FMT_NB][24] = {
+    [AV_SUBTITLE_FMT_UNKNOWN] = "Unknown subtitle format",
+    [AV_SUBTITLE_FMT_BITMAP]  = "Graphical subtitles",
+    [AV_SUBTITLE_FMT_TEXT]    = "Text subtitles (plain)",
+    [AV_SUBTITLE_FMT_ASS]     = "Text subtitles (ass)",
+};
+
+const char *av_get_subtitle_fmt_name(enum AVSubtitleType sub_fmt)
+{
+    if (sub_fmt < 0 || sub_fmt >= AV_SUBTITLE_FMT_NB)
+        return NULL;
+    return sub_fmt_info[sub_fmt];
+}
+
+enum AVSubtitleType av_get_subtitle_fmt(const char *name)
+{
+    for (int i = 0; i < AV_SUBTITLE_FMT_NB; i++)
+        if (!strcmp(sub_fmt_info[i], name))
+            return i;
+    return AV_SUBTITLE_FMT_NONE;
+}
diff --git a/libavutil/subfmt.h b/libavutil/subfmt.h
index 791b45519f..3b8a0613b2 100644
--- a/libavutil/subfmt.h
+++ b/libavutil/subfmt.h
@@ -21,6 +21,9 @@
 #ifndef AVUTIL_SUBFMT_H
 #define AVUTIL_SUBFMT_H
 
+#include <stdint.h>
+
+#include "buffer.h"
 #include "version.h"
 
 enum AVSubtitleType {
@@ -65,4 +68,48 @@ enum AVSubtitleType {
     AV_SUBTITLE_FMT_NB,         ///< number of subtitle formats, DO NOT USE THIS if you want to link with shared libav* because the number of formats might differ between versions.
 };
 
+typedef struct AVSubtitleArea {
+#define AV_NUM_BUFFER_POINTERS 1
+
+    enum AVSubtitleType type;
+    int flags;
+
+    int x;         ///< top left corner  of area.
+    int y;         ///< top left corner  of area.
+    int w;         ///< width            of area.
+    int h;         ///< height           of area.
+    int nb_colors; ///< number of colors in bitmap palette (@ref pal).
+
+    /**
+     * Buffers and line sizes for the bitmap of this subtitle.
+     *
+     * @{
+     */
+    AVBufferRef *buf[AV_NUM_BUFFER_POINTERS];
+    int linesize[AV_NUM_BUFFER_POINTERS];
+    /**
+     * @}
+     */
+
+    uint32_t pal[256]; ///< RGBA palette for the bitmap.
+
+    char *text;        ///< 0-terminated plain UTF-8 text
+    char *ass;         ///< 0-terminated ASS/SSA compatible event line.
+
+} AVSubtitleArea;
+
+/**
+ * Return the name of sub_fmt, or NULL if sub_fmt is not
+ * recognized.
+ */
+const char *av_get_subtitle_fmt_name(enum AVSubtitleType sub_fmt);
+
+/**
+ * Return a subtitle format corresponding to name, or AV_SUBTITLE_FMT_NONE
+ * on error.
+ *
+ * @param name Subtitle format name.
+ */
+enum AVSubtitleType av_get_subtitle_fmt(const char *name);
+
 #endif /* AVUTIL_SUBFMT_H */
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH 03/24] avcodec/subtitles: Introduce new frame-based subtitle decoding API
  2022-01-14  1:13 [FFmpeg-devel] [PATCH 00/24] Subtitle Filtering 2022 ffmpegagent
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 01/24] avcodec, avutil: Move enum AVSubtitleType to avutil, add new and deprecate old values ffmpegagent
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 02/24] avutil/frame: Prepare AVFrame for subtitle handling ffmpegagent
@ 2022-01-14  1:13 ` ffmpegagent
  2022-01-14 17:53   ` Andreas Rheinhardt
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 04/24] avfilter/subtitles: Update vf_subtitles to use new decoding api ffmpegagent
                   ` (21 subsequent siblings)
  24 siblings, 1 reply; 217+ messages in thread
From: ffmpegagent @ 2022-01-14  1:13 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: softworkz

From: softworkz <softworkz@hotmail.com>

- Add avcodec_decode_subtitle3 which takes subtitle frames,
  serving as compatibility shim to legacy subtitle decoding
- Add additional methods for conversion between old and new API

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/avcodec.h    |   8 +-
 libavcodec/codec_desc.c |  11 +++
 libavcodec/codec_desc.h |   8 ++
 libavcodec/decode.c     |  56 ++++++++++--
 libavcodec/internal.h   |  22 +++++
 libavcodec/utils.c      | 184 ++++++++++++++++++++++++++++++++++++++++
 6 files changed, 280 insertions(+), 9 deletions(-)

diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index fe5a83cf85..9d59f6e840 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -1675,7 +1675,7 @@ typedef struct AVCodecContext {
 
     /**
      * Header containing style information for text subtitles.
-     * For SUBTITLE_ASS subtitle type, it should contain the whole ASS
+     * For AV_SUBTITLE_FMT_ASS subtitle type, it should contain the whole ASS
      * [Script Info] and [V4+ Styles] section, plus the [Events] line and
      * the Format line following. It shouldn't include any Dialogue line.
      * - encoding: Set/allocated/freed by user (before avcodec_open2())
@@ -2415,7 +2415,10 @@ int avcodec_close(AVCodecContext *avctx);
  * Free all allocated data in the given subtitle struct.
  *
  * @param sub AVSubtitle to free.
+ *
+ * @deprecated Use the regular frame based encode and decode APIs instead.
  */
+attribute_deprecated
 void avsubtitle_free(AVSubtitle *sub);
 
 /**
@@ -2508,7 +2511,10 @@ enum AVChromaLocation avcodec_chroma_pos_to_enum(int xpos, int ypos);
  *                 must be freed with avsubtitle_free if *got_sub_ptr is set.
  * @param[in,out] got_sub_ptr Zero if no subtitle could be decompressed, otherwise, it is nonzero.
  * @param[in] avpkt The input AVPacket containing the input buffer.
+ *
+ * @deprecated Use the new decode API (avcodec_send_packet, avcodec_receive_frame) instead.
  */
+attribute_deprecated
 int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub,
                             int *got_sub_ptr,
                             AVPacket *avpkt);
diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
index 0974ee03de..e48e4532ba 100644
--- a/libavcodec/codec_desc.c
+++ b/libavcodec/codec_desc.c
@@ -3548,3 +3548,14 @@ enum AVMediaType avcodec_get_type(enum AVCodecID codec_id)
     const AVCodecDescriptor *desc = avcodec_descriptor_get(codec_id);
     return desc ? desc->type : AVMEDIA_TYPE_UNKNOWN;
 }
+
+enum AVSubtitleType avcodec_descriptor_get_subtitle_format(const AVCodecDescriptor *codec_descriptor)
+{
+    if(codec_descriptor->props & AV_CODEC_PROP_BITMAP_SUB)
+        return AV_SUBTITLE_FMT_BITMAP;
+
+    if(codec_descriptor->props & AV_CODEC_PROP_TEXT_SUB)
+        return AV_SUBTITLE_FMT_ASS;
+
+    return AV_SUBTITLE_FMT_UNKNOWN;
+}
diff --git a/libavcodec/codec_desc.h b/libavcodec/codec_desc.h
index 126b52df47..ba68d24e0e 100644
--- a/libavcodec/codec_desc.h
+++ b/libavcodec/codec_desc.h
@@ -121,6 +121,14 @@ const AVCodecDescriptor *avcodec_descriptor_next(const AVCodecDescriptor *prev);
  */
 const AVCodecDescriptor *avcodec_descriptor_get_by_name(const char *name);
 
+/**
+ * Return subtitle format from a codec descriptor
+ *
+ * @param codec_descriptor codec descriptor
+ * @return                 the subtitle type (e.g. bitmap, text)
+ */
+enum AVSubtitleType avcodec_descriptor_get_subtitle_format(const AVCodecDescriptor *codec_descriptor);
+
 /**
  * @}
  */
diff --git a/libavcodec/decode.c b/libavcodec/decode.c
index 0912f86a14..ab8a6ea6ff 100644
--- a/libavcodec/decode.c
+++ b/libavcodec/decode.c
@@ -576,6 +576,39 @@ static int decode_receive_frame_internal(AVCodecContext *avctx, AVFrame *frame)
     return ret;
 }
 
+static int decode_subtitle2_priv(AVCodecContext *avctx, AVSubtitle *sub,
+                                 int *got_sub_ptr, AVPacket *avpkt);
+
+static int decode_subtitle_shim(AVCodecContext *avctx, AVFrame *frame, AVPacket *avpkt)
+{
+    int ret, got_sub_ptr = 0;
+    AVSubtitle subtitle = { 0 };
+
+    if (frame->buf[0])
+        return AVERROR(EAGAIN);
+
+    av_frame_unref(frame);
+
+    ret = decode_subtitle2_priv(avctx, &subtitle, &got_sub_ptr, avpkt);
+
+    if (ret >= 0 && got_sub_ptr) {
+        frame->type = AVMEDIA_TYPE_SUBTITLE;
+        frame->format = subtitle.format;
+        ret = av_frame_get_buffer2(frame, 0);
+
+        if (ret >= 0)
+            ret = ff_frame_put_subtitle(frame, &subtitle);
+
+        frame->width = avctx->width;
+        frame->height = avctx->height;
+        frame->pkt_dts = avpkt->dts;
+    }
+
+    avsubtitle_free(&subtitle);
+
+    return ret;
+}
+
 int attribute_align_arg avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt)
 {
     AVCodecInternal *avci = avctx->internal;
@@ -590,6 +623,9 @@ int attribute_align_arg avcodec_send_packet(AVCodecContext *avctx, const AVPacke
     if (avpkt && !avpkt->size && avpkt->data)
         return AVERROR(EINVAL);
 
+    if (avctx->codec_type == AVMEDIA_TYPE_SUBTITLE)
+        return decode_subtitle_shim(avctx, avci->buffer_frame, avpkt);
+
     av_packet_unref(avci->buffer_pkt);
     if (avpkt && (avpkt->data || avpkt->side_data_elems)) {
         ret = av_packet_ref(avci->buffer_pkt, avpkt);
@@ -651,7 +687,9 @@ int attribute_align_arg avcodec_receive_frame(AVCodecContext *avctx, AVFrame *fr
 
     if (avci->buffer_frame->buf[0]) {
         av_frame_move_ref(frame, avci->buffer_frame);
-    } else {
+    } else if (avctx->codec_type == AVMEDIA_TYPE_SUBTITLE)
+        return AVERROR(EAGAIN);
+    else {
         ret = decode_receive_frame_internal(avctx, frame);
         if (ret < 0)
             return ret;
@@ -802,9 +840,8 @@ static int utf8_check(const uint8_t *str)
     return 1;
 }
 
-int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub,
-                             int *got_sub_ptr,
-                             AVPacket *avpkt)
+static int decode_subtitle2_priv(AVCodecContext *avctx, AVSubtitle *sub,
+                             int *got_sub_ptr, AVPacket *avpkt)
 {
     int ret = 0;
 
@@ -850,10 +887,7 @@ int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub,
                                                  avctx->pkt_timebase, ms);
         }
 
-        if (avctx->codec_descriptor->props & AV_CODEC_PROP_BITMAP_SUB)
-            sub->format = 0;
-        else if (avctx->codec_descriptor->props & AV_CODEC_PROP_TEXT_SUB)
-            sub->format = 1;
+        sub->format = avcodec_descriptor_get_subtitle_format(avctx->codec_descriptor);
 
         for (unsigned i = 0; i < sub->num_rects; i++) {
             if (avctx->sub_charenc_mode != FF_SUB_CHARENC_MODE_IGNORE &&
@@ -874,6 +908,12 @@ int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub,
     return ret;
 }
 
+int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub,
+                             int *got_sub_ptr, AVPacket *avpkt)
+{
+    return decode_subtitle2_priv(avctx, sub, got_sub_ptr, avpkt);
+}
+
 enum AVPixelFormat avcodec_default_get_format(struct AVCodecContext *avctx,
                                               const enum AVPixelFormat *fmt)
 {
diff --git a/libavcodec/internal.h b/libavcodec/internal.h
index 72ca1553f6..ff63974f7f 100644
--- a/libavcodec/internal.h
+++ b/libavcodec/internal.h
@@ -363,4 +363,26 @@ int ff_int_from_list_or_default(void *ctx, const char * val_name, int val,
 
 void ff_dvdsub_parse_palette(uint32_t *palette, const char *p);
 
+/**
+ * Copies subtitle data from AVSubtitle to AVFrame.
+ *
+ * @deprecated This is a compatibility method for interoperability with
+ * the legacy subtitle API.
+ */
+int ff_frame_put_subtitle(AVFrame* frame, const AVSubtitle* sub);
+
+/**
+ * Copies subtitle data from AVFrame to AVSubtitle.
+ *
+ * @deprecated This is a compatibility method for interoperability with
+ * the legacy subtitle API.
+ */
+int ff_frame_get_subtitle(AVSubtitle* sub, AVFrame* frame);
+
+#if defined(_WIN32) && CONFIG_SHARED && !defined(BUILDING_avcodec)
+#    define av_export_avcodec __declspec(dllimport)
+#else
+#    define av_export_avcodec
+#endif
+
 #endif /* AVCODEC_INTERNAL_H */
diff --git a/libavcodec/utils.c b/libavcodec/utils.c
index b19befef21..72c742c176 100644
--- a/libavcodec/utils.c
+++ b/libavcodec/utils.c
@@ -813,6 +813,190 @@ int av_get_audio_frame_duration(AVCodecContext *avctx, int frame_bytes)
     return FFMAX(0, duration);
 }
 
+static int subtitle_area2rect(AVSubtitleRect *dst, const AVSubtitleArea *src)
+{
+    dst->x         =  src->x;
+    dst->y         =  src->y;
+    dst->w         =  src->w;
+    dst->h         =  src->h;
+    dst->nb_colors =  src->nb_colors;
+    dst->type      =  src->type;
+    dst->flags     =  src->flags;
+
+    switch (dst->type) {
+    case AV_SUBTITLE_FMT_BITMAP:
+
+        if (src->h > 0 && src->w > 0 && src->buf[0]) {
+            uint32_t *pal;
+            AVBufferRef *buf = src->buf[0];
+            dst->data[0] = av_mallocz(buf->size);
+            memcpy(dst->data[0], buf->data, buf->size);
+            dst->linesize[0] = src->linesize[0];
+
+            dst->data[1] = av_mallocz(256 * 4);
+            pal = (uint32_t *)dst->data[1];
+
+            for (unsigned i = 0; i < 256; i++) {
+                pal[i] = src->pal[i];
+            }
+        }
+
+        break;
+    case AV_SUBTITLE_FMT_TEXT:
+
+        if (src->text)
+            dst->text = av_strdup(src->text);
+        else
+            dst->text = av_strdup("");
+
+        if (!dst->text)
+            return AVERROR(ENOMEM);
+
+        break;
+    case AV_SUBTITLE_FMT_ASS:
+
+        if (src->ass)
+            dst->ass = av_strdup(src->ass);
+        else
+            dst->ass = av_strdup("");
+
+        if (!dst->ass)
+            return AVERROR(ENOMEM);
+
+        break;
+    default:
+
+        av_log(NULL, AV_LOG_ERROR, "Subtitle rect has invalid format: %d", dst->type);
+        return AVERROR(EINVAL);
+    }
+
+    return 0;
+}
+
+static int subtitle_rect2area(AVSubtitleArea *dst, const AVSubtitleRect *src)
+{
+    dst->x         =  src->x;
+    dst->y         =  src->y;
+    dst->w         =  src->w;
+    dst->h         =  src->h;
+    dst->nb_colors =  src->nb_colors;
+    dst->type      =  src->type;
+    dst->flags     =  src->flags;
+
+    switch (dst->type) {
+    case AV_SUBTITLE_FMT_BITMAP:
+
+        if (src->h > 0 && src->w > 0 && src->data[0]) {
+            AVBufferRef *buf = av_buffer_allocz(src->h * src->linesize[0]);
+            memcpy(buf->data, src->data[0], buf->size);
+
+            dst->buf[0] = buf;
+            dst->linesize[0] = src->linesize[0];
+        }
+
+        if (src->data[1]) {
+            uint32_t *pal = (uint32_t *)src->data[1];
+
+            for (unsigned i = 0; i < 256; i++) {
+                dst->pal[i] = pal[i];
+            }
+        }
+
+        break;
+    case AV_SUBTITLE_FMT_TEXT:
+
+        if (src->text) {
+            dst->text = av_strdup(src->text);
+            if (!dst->text)
+                return AVERROR(ENOMEM);
+        }
+
+        break;
+    case AV_SUBTITLE_FMT_ASS:
+
+        if (src->ass) {
+            dst->ass = av_strdup(src->ass);
+            if (!dst->ass)
+                return AVERROR(ENOMEM);
+        }
+
+        break;
+    default:
+
+        av_log(NULL, AV_LOG_ERROR, "Subtitle area has invalid format: %d", dst->type);
+        return AVERROR(EINVAL);
+    }
+
+    return 0;
+}
+
+/**
+ * Copies subtitle data from AVSubtitle (deprecated) to AVFrame
+ *
+ * @note This is a compatibility method for conversion to the legacy API
+ */
+int ff_frame_put_subtitle(AVFrame *frame, const AVSubtitle *sub)
+{
+    frame->format = sub->format;
+    frame->subtitle_timing.start_pts = sub->pts;
+    frame->subtitle_timing.start_pts += av_rescale_q(sub->start_display_time, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q);
+    frame->subtitle_timing.duration = av_rescale_q(sub->end_display_time - sub->start_display_time, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q);
+
+    if (sub->num_rects) {
+        frame->subtitle_areas = av_malloc_array(sub->num_rects, sizeof(AVSubtitleArea*));
+        if (!frame->subtitle_areas)
+            return AVERROR(ENOMEM);
+
+        for (unsigned i = 0; i < sub->num_rects; i++) {
+            int ret;
+            frame->subtitle_areas[i] = av_mallocz(sizeof(AVSubtitleArea));
+            if (!frame->subtitle_areas[i])
+                return AVERROR(ENOMEM);
+            ret = subtitle_rect2area(frame->subtitle_areas[i], sub->rects[i]);
+            if (ret < 0) {
+                frame->num_subtitle_areas = i;
+                return ret;
+            }
+        }
+    }
+
+    frame->num_subtitle_areas = sub->num_rects;
+    return 0;
+}
+
+/**
+ * Copies subtitle data from AVFrame to AVSubtitle (deprecated)
+ *
+ * @note This is a compatibility method for conversion to the legacy API
+ */
+int ff_frame_get_subtitle(AVSubtitle *sub, AVFrame *frame)
+{
+    const int64_t duration_ms = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, (AVRational){ 1, 1000 });
+
+    sub->start_display_time = 0;
+    sub->end_display_time = (int32_t)duration_ms;
+    sub->pts = frame->subtitle_timing.start_pts;
+
+    if (frame->num_subtitle_areas) {
+        sub->rects = av_malloc_array(frame->num_subtitle_areas, sizeof(AVSubtitleRect*));
+        if (!sub->rects)
+            return AVERROR(ENOMEM);
+
+        for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+            int ret;
+            sub->rects[i] = av_mallocz(sizeof(AVSubtitleRect));
+            ret = subtitle_area2rect(sub->rects[i], frame->subtitle_areas[i]);
+            if (ret < 0) {
+                sub->num_rects = i;
+                return ret;
+            }
+        }
+    }
+
+    sub->num_rects = frame->num_subtitle_areas;
+    return 0;
+}
+
 int av_get_audio_frame_duration2(AVCodecParameters *par, int frame_bytes)
 {
     int duration = get_audio_frame_duration(par->codec_id, par->sample_rate,
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH 04/24] avfilter/subtitles: Update vf_subtitles to use new decoding api
  2022-01-14  1:13 [FFmpeg-devel] [PATCH 00/24] Subtitle Filtering 2022 ffmpegagent
                   ` (2 preceding siblings ...)
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 03/24] avcodec/subtitles: Introduce new frame-based subtitle decoding API ffmpegagent
@ 2022-01-14  1:13 ` ffmpegagent
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 05/24] avcodec, avutil: Move ass helper functions to avutil as avpriv_ and extend ass dialog parsing ffmpegagent
                   ` (20 subsequent siblings)
  24 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-14  1:13 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: softworkz

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavfilter/vf_subtitles.c | 56 +++++++++++++++++++++++++++++---------
 1 file changed, 43 insertions(+), 13 deletions(-)

diff --git a/libavfilter/vf_subtitles.c b/libavfilter/vf_subtitles.c
index 3fc4eeb63d..25e217e845 100644
--- a/libavfilter/vf_subtitles.c
+++ b/libavfilter/vf_subtitles.c
@@ -35,14 +35,12 @@
 # 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"
 
 typedef struct AssContext {
     const AVClass *class;
@@ -292,6 +290,29 @@ static int attachment_is_font(AVStream * st)
     return 0;
 }
 
+static int decode(AVCodecContext *avctx, AVFrame *frame, int *got_frame, AVPacket *pkt)
+{
+    int ret;
+
+    *got_frame = 0;
+
+    if (pkt) {
+        ret = avcodec_send_packet(avctx, pkt);
+        // In particular, we don't expect AVERROR(EAGAIN), because we read all
+        // decoded frames with avcodec_receive_frame() until done.
+        if (ret < 0 && ret != AVERROR_EOF)
+            return ret;
+    }
+
+    ret = avcodec_receive_frame(avctx, frame);
+    if (ret < 0 && ret != AVERROR(EAGAIN))
+        return ret;
+    if (ret >= 0)
+        *got_frame = 1;
+
+    return 0;
+}
+
 AVFILTER_DEFINE_CLASS(subtitles);
 
 static av_cold int init_subtitles(AVFilterContext *ctx)
@@ -306,6 +327,7 @@ static av_cold int init_subtitles(AVFilterContext *ctx)
     AVStream *st;
     AVPacket pkt;
     AssContext *ass = ctx->priv;
+    enum AVSubtitleType subtitle_format;
 
     /* Init libass */
     ret = init(ctx);
@@ -386,13 +408,17 @@ static av_cold int init_subtitles(AVFilterContext *ctx)
         ret = AVERROR_DECODER_NOT_FOUND;
         goto end;
     }
+
     dec_desc = avcodec_descriptor_get(st->codecpar->codec_id);
-    if (dec_desc && !(dec_desc->props & AV_CODEC_PROP_TEXT_SUB)) {
+    subtitle_format = avcodec_descriptor_get_subtitle_format(dec_desc);
+
+    if (subtitle_format != AV_SUBTITLE_FMT_ASS) {
         av_log(ctx, AV_LOG_ERROR,
-               "Only text based subtitles are currently supported\n");
-        ret = AVERROR_PATCHWELCOME;
+               "Only text based subtitles are supported by this filter\n");
+        ret = AVERROR_INVALIDDATA;
         goto end;
     }
+
     if (ass->charenc)
         av_dict_set(&codec_opts, "sub_charenc", ass->charenc, 0);
 
@@ -448,27 +474,31 @@ static av_cold int init_subtitles(AVFilterContext *ctx)
                                   dec_ctx->subtitle_header_size);
     while (av_read_frame(fmt, &pkt) >= 0) {
         int i, got_subtitle;
-        AVSubtitle sub = {0};
+        AVFrame *sub = av_frame_alloc();
+        if (!sub) {
+            ret = AVERROR(ENOMEM);
+            goto end;
+        }
 
         if (pkt.stream_index == sid) {
-            ret = avcodec_decode_subtitle2(dec_ctx, &sub, &got_subtitle, &pkt);
+            ret = decode(dec_ctx, sub, &got_subtitle, &pkt);
             if (ret < 0) {
                 av_log(ctx, AV_LOG_WARNING, "Error decoding: %s (ignored)\n",
                        av_err2str(ret));
             } else if (got_subtitle) {
-                const int64_t start_time = av_rescale_q(sub.pts, AV_TIME_BASE_Q, av_make_q(1, 1000));
-                const int64_t duration   = sub.end_display_time;
-                for (i = 0; i < sub.num_rects; i++) {
-                    char *ass_line = sub.rects[i]->ass;
+                const int64_t start_time = av_rescale_q(sub->subtitle_timing.start_pts, AV_TIME_BASE_Q, av_make_q(1, 1000));
+                const int64_t duration   = av_rescale_q(sub->subtitle_timing.duration, AV_TIME_BASE_Q, av_make_q(1, 1000));
+                for (i = 0; i < sub->num_subtitle_areas; i++) {
+                    char *ass_line = sub->subtitle_areas[i]->ass;
                     if (!ass_line)
-                        break;
+                        continue;
                     ass_process_chunk(ass->track, ass_line, strlen(ass_line),
                                       start_time, duration);
                 }
             }
         }
         av_packet_unref(&pkt);
-        avsubtitle_free(&sub);
+        av_frame_free(&sub);
     }
 
 end:
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH 05/24] avcodec, avutil: Move ass helper functions to avutil as avpriv_ and extend ass dialog parsing
  2022-01-14  1:13 [FFmpeg-devel] [PATCH 00/24] Subtitle Filtering 2022 ffmpegagent
                   ` (3 preceding siblings ...)
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 04/24] avfilter/subtitles: Update vf_subtitles to use new decoding api ffmpegagent
@ 2022-01-14  1:13 ` ffmpegagent
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 06/24] avcodec/subtitles: Migrate subtitle encoders to frame-based API ffmpegagent
                   ` (19 subsequent siblings)
  24 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-14  1:13 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: softworkz

From: softworkz <softworkz@hotmail.com>

Also add

- hard_space callback (for upcoming fix)
- extensible callback (for future extension)

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/Makefile                           |  56 +++----
 libavcodec/ass.h                              | 144 ++++++------------
 libavcodec/assdec.c                           |   2 +-
 libavcodec/assenc.c                           |   2 +-
 libavcodec/ccaption_dec.c                     |  19 +--
 libavcodec/jacosubdec.c                       |   2 +-
 libavcodec/libaribb24.c                       |   2 +-
 libavcodec/libzvbi-teletextdec.c              |  14 +-
 libavcodec/microdvddec.c                      |   7 +-
 libavcodec/movtextdec.c                       |   3 +-
 libavcodec/movtextenc.c                       |  20 +--
 libavcodec/mpl2dec.c                          |   2 +-
 libavcodec/realtextdec.c                      |   2 +-
 libavcodec/samidec.c                          |   2 +-
 libavcodec/srtdec.c                           |   2 +-
 libavcodec/srtenc.c                           |  16 +-
 libavcodec/subviewerdec.c                     |   2 +-
 libavcodec/textdec.c                          |   4 +-
 libavcodec/ttmlenc.c                          |  15 +-
 libavcodec/webvttdec.c                        |   2 +-
 libavcodec/webvttenc.c                        |  16 +-
 libavutil/Makefile                            |   2 +
 {libavcodec => libavutil}/ass.c               |  91 +++++------
 libavutil/ass_internal.h                      | 135 ++++++++++++++++
 {libavcodec => libavutil}/ass_split.c         |  30 ++--
 .../ass_split_internal.h                      |  32 ++--
 26 files changed, 355 insertions(+), 269 deletions(-)
 rename {libavcodec => libavutil}/ass.c (65%)
 create mode 100644 libavutil/ass_internal.h
 rename {libavcodec => libavutil}/ass_split.c (94%)
 rename libavcodec/ass_split.h => libavutil/ass_split_internal.h (86%)

diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index cfc70a3eaf..80bf8ff2d2 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -215,10 +215,10 @@ OBJS-$(CONFIG_APNG_DECODER)            += png.o pngdec.o pngdsp.o
 OBJS-$(CONFIG_APNG_ENCODER)            += png.o pngenc.o
 OBJS-$(CONFIG_ARBC_DECODER)            += arbc.o
 OBJS-$(CONFIG_ARGO_DECODER)            += argo.o
-OBJS-$(CONFIG_SSA_DECODER)             += assdec.o ass.o
-OBJS-$(CONFIG_SSA_ENCODER)             += assenc.o ass.o
-OBJS-$(CONFIG_ASS_DECODER)             += assdec.o ass.o
-OBJS-$(CONFIG_ASS_ENCODER)             += assenc.o ass.o
+OBJS-$(CONFIG_SSA_DECODER)             += assdec.o
+OBJS-$(CONFIG_SSA_ENCODER)             += assenc.o
+OBJS-$(CONFIG_ASS_DECODER)             += assdec.o
+OBJS-$(CONFIG_ASS_ENCODER)             += assenc.o
 OBJS-$(CONFIG_ASV1_DECODER)            += asvdec.o asv.o mpeg12data.o
 OBJS-$(CONFIG_ASV1_ENCODER)            += asvenc.o asv.o mpeg12data.o
 OBJS-$(CONFIG_ASV2_DECODER)            += asvdec.o asv.o mpeg12data.o
@@ -259,7 +259,7 @@ OBJS-$(CONFIG_BRENDER_PIX_DECODER)     += brenderpix.o
 OBJS-$(CONFIG_C93_DECODER)             += c93.o
 OBJS-$(CONFIG_CAVS_DECODER)            += cavs.o cavsdec.o cavsdsp.o \
                                           cavsdata.o
-OBJS-$(CONFIG_CCAPTION_DECODER)        += ccaption_dec.o ass.o
+OBJS-$(CONFIG_CCAPTION_DECODER)        += ccaption_dec.o
 OBJS-$(CONFIG_CDGRAPHICS_DECODER)      += cdgraphics.o
 OBJS-$(CONFIG_CDTOONS_DECODER)         += cdtoons.o
 OBJS-$(CONFIG_CDXL_DECODER)            += cdxl.o
@@ -434,7 +434,7 @@ OBJS-$(CONFIG_INTERPLAY_ACM_DECODER)   += interplayacm.o
 OBJS-$(CONFIG_INTERPLAY_DPCM_DECODER)  += dpcm.o
 OBJS-$(CONFIG_INTERPLAY_VIDEO_DECODER) += interplayvideo.o
 OBJS-$(CONFIG_IPU_DECODER)             += mpeg12dec.o mpeg12.o mpeg12data.o
-OBJS-$(CONFIG_JACOSUB_DECODER)         += jacosubdec.o ass.o
+OBJS-$(CONFIG_JACOSUB_DECODER)         += jacosubdec.o
 OBJS-$(CONFIG_JPEG2000_ENCODER)        += j2kenc.o mqcenc.o mqc.o jpeg2000.o \
                                           jpeg2000dwt.o
 OBJS-$(CONFIG_JPEG2000_DECODER)        += jpeg2000dec.o jpeg2000.o jpeg2000dsp.o \
@@ -456,7 +456,7 @@ OBJS-$(CONFIG_MAGICYUV_ENCODER)        += magicyuvenc.o
 OBJS-$(CONFIG_MDEC_DECODER)            += mdec.o mpeg12.o mpeg12data.o
 OBJS-$(CONFIG_METASOUND_DECODER)       += metasound.o metasound_data.o \
                                           twinvq.o
-OBJS-$(CONFIG_MICRODVD_DECODER)        += microdvddec.o ass.o
+OBJS-$(CONFIG_MICRODVD_DECODER)        += microdvddec.o
 OBJS-$(CONFIG_MIMIC_DECODER)           += mimic.o
 OBJS-$(CONFIG_MJPEG_DECODER)           += mjpegdec.o mjpegdec_common.o
 OBJS-$(CONFIG_MJPEG_QSV_DECODER)       += qsvdec.o
@@ -471,8 +471,8 @@ OBJS-$(CONFIG_MLP_ENCODER)             += mlpenc.o mlp.o
 OBJS-$(CONFIG_MMVIDEO_DECODER)         += mmvideo.o
 OBJS-$(CONFIG_MOBICLIP_DECODER)        += mobiclip.o
 OBJS-$(CONFIG_MOTIONPIXELS_DECODER)    += motionpixels.o
-OBJS-$(CONFIG_MOVTEXT_DECODER)         += movtextdec.o ass.o
-OBJS-$(CONFIG_MOVTEXT_ENCODER)         += movtextenc.o ass_split.o
+OBJS-$(CONFIG_MOVTEXT_DECODER)         += movtextdec.o
+OBJS-$(CONFIG_MOVTEXT_ENCODER)         += movtextenc.o
 OBJS-$(CONFIG_MP1_DECODER)             += mpegaudiodec_fixed.o
 OBJS-$(CONFIG_MP1FLOAT_DECODER)        += mpegaudiodec_float.o
 OBJS-$(CONFIG_MP2_DECODER)             += mpegaudiodec_fixed.o
@@ -513,7 +513,7 @@ OBJS-$(CONFIG_MPEG4_MEDIACODEC_DECODER) += mediacodecdec.o
 OBJS-$(CONFIG_MPEG4_OMX_ENCODER)       += omx.o
 OBJS-$(CONFIG_MPEG4_V4L2M2M_DECODER)   += v4l2_m2m_dec.o
 OBJS-$(CONFIG_MPEG4_V4L2M2M_ENCODER)   += v4l2_m2m_enc.o
-OBJS-$(CONFIG_MPL2_DECODER)            += mpl2dec.o ass.o
+OBJS-$(CONFIG_MPL2_DECODER)            += mpl2dec.o
 OBJS-$(CONFIG_MSA1_DECODER)            += mss3.o
 OBJS-$(CONFIG_MSCC_DECODER)            += mscc.o
 OBJS-$(CONFIG_MSMPEG4V1_DECODER)       += msmpeg4dec.o msmpeg4.o msmpeg4data.o
@@ -566,7 +566,7 @@ OBJS-$(CONFIG_PGX_DECODER)             += pgxdec.o
 OBJS-$(CONFIG_PHOTOCD_DECODER)         += photocd.o
 OBJS-$(CONFIG_PICTOR_DECODER)          += pictordec.o cga_data.o
 OBJS-$(CONFIG_PIXLET_DECODER)          += pixlet.o
-OBJS-$(CONFIG_PJS_DECODER)             += textdec.o ass.o
+OBJS-$(CONFIG_PJS_DECODER)             += textdec.o
 OBJS-$(CONFIG_PNG_DECODER)             += png.o pngdec.o pngdsp.o
 OBJS-$(CONFIG_PNG_ENCODER)             += png.o pngenc.o
 OBJS-$(CONFIG_PPM_DECODER)             += pnmdec.o pnm.o
@@ -599,7 +599,7 @@ OBJS-$(CONFIG_RALF_DECODER)            += ralf.o
 OBJS-$(CONFIG_RASC_DECODER)            += rasc.o
 OBJS-$(CONFIG_RAWVIDEO_DECODER)        += rawdec.o
 OBJS-$(CONFIG_RAWVIDEO_ENCODER)        += rawenc.o
-OBJS-$(CONFIG_REALTEXT_DECODER)        += realtextdec.o ass.o
+OBJS-$(CONFIG_REALTEXT_DECODER)        += realtextdec.o
 OBJS-$(CONFIG_RL2_DECODER)             += rl2.o
 OBJS-$(CONFIG_ROQ_DECODER)             += roqvideodec.o roqvideo.o
 OBJS-$(CONFIG_ROQ_ENCODER)             += roqvideoenc.o roqvideo.o elbg.o
@@ -614,7 +614,7 @@ OBJS-$(CONFIG_RV20_DECODER)            += rv10.o
 OBJS-$(CONFIG_RV20_ENCODER)            += rv20enc.o
 OBJS-$(CONFIG_RV30_DECODER)            += rv30.o rv34.o rv30dsp.o
 OBJS-$(CONFIG_RV40_DECODER)            += rv40.o rv34.o rv40dsp.o
-OBJS-$(CONFIG_SAMI_DECODER)            += samidec.o ass.o htmlsubtitles.o
+OBJS-$(CONFIG_SAMI_DECODER)            += samidec.o htmlsubtitles.o
 OBJS-$(CONFIG_S302M_DECODER)           += s302m.o
 OBJS-$(CONFIG_S302M_ENCODER)           += s302menc.o
 OBJS-$(CONFIG_SANM_DECODER)            += sanm.o
@@ -649,13 +649,13 @@ OBJS-$(CONFIG_SPEEDHQ_ENCODER)         += speedhq.o mpeg12data.o mpeg12enc.o spe
 OBJS-$(CONFIG_SPEEX_DECODER)           += speexdec.o
 OBJS-$(CONFIG_SP5X_DECODER)            += sp5xdec.o
 OBJS-$(CONFIG_SRGC_DECODER)            += mscc.o
-OBJS-$(CONFIG_SRT_DECODER)             += srtdec.o ass.o htmlsubtitles.o
-OBJS-$(CONFIG_SRT_ENCODER)             += srtenc.o ass_split.o
-OBJS-$(CONFIG_STL_DECODER)             += textdec.o ass.o
-OBJS-$(CONFIG_SUBRIP_DECODER)          += srtdec.o ass.o htmlsubtitles.o
-OBJS-$(CONFIG_SUBRIP_ENCODER)          += srtenc.o ass_split.o
-OBJS-$(CONFIG_SUBVIEWER1_DECODER)      += textdec.o ass.o
-OBJS-$(CONFIG_SUBVIEWER_DECODER)       += subviewerdec.o ass.o
+OBJS-$(CONFIG_SRT_DECODER)             += srtdec.o htmlsubtitles.o
+OBJS-$(CONFIG_SRT_ENCODER)             += srtenc.o
+OBJS-$(CONFIG_STL_DECODER)             += textdec.o
+OBJS-$(CONFIG_SUBRIP_DECODER)          += srtdec.o htmlsubtitles.o
+OBJS-$(CONFIG_SUBRIP_ENCODER)          += srtenc.o
+OBJS-$(CONFIG_SUBVIEWER1_DECODER)      += textdec.o
+OBJS-$(CONFIG_SUBVIEWER_DECODER)       += subviewerdec.o
 OBJS-$(CONFIG_SUNRAST_DECODER)         += sunrast.o
 OBJS-$(CONFIG_SUNRAST_ENCODER)         += sunrastenc.o
 OBJS-$(CONFIG_LIBRSVG_DECODER)         += librsvgdec.o
@@ -665,8 +665,8 @@ OBJS-$(CONFIG_SVQ1_DECODER)            += svq1dec.o svq1.o h263data.o
 OBJS-$(CONFIG_SVQ1_ENCODER)            += svq1enc.o svq1.o  h263data.o  \
                                           h263.o ituh263enc.o
 OBJS-$(CONFIG_SVQ3_DECODER)            += svq3.o mpegutils.o h264data.o
-OBJS-$(CONFIG_TEXT_DECODER)            += textdec.o ass.o
-OBJS-$(CONFIG_TEXT_ENCODER)            += srtenc.o ass_split.o
+OBJS-$(CONFIG_TEXT_DECODER)            += textdec.o
+OBJS-$(CONFIG_TEXT_ENCODER)            += srtenc.o
 OBJS-$(CONFIG_TAK_DECODER)             += takdec.o tak.o takdsp.o
 OBJS-$(CONFIG_TARGA_DECODER)           += targa.o
 OBJS-$(CONFIG_TARGA_ENCODER)           += targaenc.o rle.o
@@ -686,7 +686,7 @@ OBJS-$(CONFIG_TSCC_DECODER)            += tscc.o msrledec.o
 OBJS-$(CONFIG_TSCC2_DECODER)           += tscc2.o
 OBJS-$(CONFIG_TTA_DECODER)             += tta.o ttadata.o ttadsp.o
 OBJS-$(CONFIG_TTA_ENCODER)             += ttaenc.o ttaencdsp.o ttadata.o
-OBJS-$(CONFIG_TTML_ENCODER)            += ttmlenc.o ass_split.o
+OBJS-$(CONFIG_TTML_ENCODER)            += ttmlenc.o
 OBJS-$(CONFIG_TWINVQ_DECODER)          += twinvqdec.o twinvq.o metasound_data.o
 OBJS-$(CONFIG_TXD_DECODER)             += txd.o
 OBJS-$(CONFIG_ULTI_DECODER)            += ulti.o
@@ -741,15 +741,15 @@ OBJS-$(CONFIG_VP9_MEDIACODEC_DECODER)  += mediacodecdec.o
 OBJS-$(CONFIG_VP9_RKMPP_DECODER)       += rkmppdec.o
 OBJS-$(CONFIG_VP9_VAAPI_ENCODER)       += vaapi_encode_vp9.o
 OBJS-$(CONFIG_VP9_QSV_ENCODER)         += qsvenc_vp9.o
-OBJS-$(CONFIG_VPLAYER_DECODER)         += textdec.o ass.o
+OBJS-$(CONFIG_VPLAYER_DECODER)         += textdec.o
 OBJS-$(CONFIG_VP9_V4L2M2M_DECODER)     += v4l2_m2m_dec.o
 OBJS-$(CONFIG_VQA_DECODER)             += vqavideo.o
 OBJS-$(CONFIG_WAVPACK_DECODER)         += wavpack.o wavpackdata.o dsd.o
 OBJS-$(CONFIG_WAVPACK_ENCODER)         += wavpackdata.o wavpackenc.o
 OBJS-$(CONFIG_WCMV_DECODER)            += wcmv.o
 OBJS-$(CONFIG_WEBP_DECODER)            += webp.o
-OBJS-$(CONFIG_WEBVTT_DECODER)          += webvttdec.o ass.o
-OBJS-$(CONFIG_WEBVTT_ENCODER)          += webvttenc.o ass_split.o
+OBJS-$(CONFIG_WEBVTT_DECODER)          += webvttdec.o
+OBJS-$(CONFIG_WEBVTT_ENCODER)          += webvttenc.o
 OBJS-$(CONFIG_WMALOSSLESS_DECODER)     += wmalosslessdec.o wma_common.o
 OBJS-$(CONFIG_WMAPRO_DECODER)          += wmaprodec.o wma.o wma_common.o
 OBJS-$(CONFIG_WMAV1_DECODER)           += wmadec.o wma.o wma_common.o aactab.o
@@ -1040,7 +1040,7 @@ OBJS-$(CONFIG_PCM_ALAW_AT_ENCODER)        += audiotoolboxenc.o
 OBJS-$(CONFIG_PCM_MULAW_AT_ENCODER)       += audiotoolboxenc.o
 OBJS-$(CONFIG_LIBAOM_AV1_DECODER)         += libaomdec.o
 OBJS-$(CONFIG_LIBAOM_AV1_ENCODER)         += libaomenc.o
-OBJS-$(CONFIG_LIBARIBB24_DECODER)         += libaribb24.o ass.o
+OBJS-$(CONFIG_LIBARIBB24_DECODER)         += libaribb24.o
 OBJS-$(CONFIG_LIBCELT_DECODER)            += libcelt_dec.o
 OBJS-$(CONFIG_LIBCODEC2_DECODER)          += libcodec2.o
 OBJS-$(CONFIG_LIBCODEC2_ENCODER)          += libcodec2.o
@@ -1091,7 +1091,7 @@ OBJS-$(CONFIG_LIBX265_ENCODER)            += libx265.o
 OBJS-$(CONFIG_LIBXAVS_ENCODER)            += libxavs.o
 OBJS-$(CONFIG_LIBXAVS2_ENCODER)           += libxavs2.o
 OBJS-$(CONFIG_LIBXVID_ENCODER)            += libxvid.o
-OBJS-$(CONFIG_LIBZVBI_TELETEXT_DECODER)   += libzvbi-teletextdec.o ass.o
+OBJS-$(CONFIG_LIBZVBI_TELETEXT_DECODER)   += libzvbi-teletextdec.o
 
 # parsers
 OBJS-$(CONFIG_AAC_LATM_PARSER)         += latm_parser.o
diff --git a/libavcodec/ass.h b/libavcodec/ass.h
index 2c260e4e78..8bc13d7ab8 100644
--- a/libavcodec/ass.h
+++ b/libavcodec/ass.h
@@ -23,117 +23,73 @@
 #define AVCODEC_ASS_H
 
 #include "avcodec.h"
-#include "libavutil/bprint.h"
-
-#define ASS_DEFAULT_PLAYRESX 384
-#define ASS_DEFAULT_PLAYRESY 288
-
-/**
- * @name Default values for ASS style
- * @{
- */
-#define ASS_DEFAULT_FONT        "Arial"
-#define ASS_DEFAULT_FONT_SIZE   16
-#define ASS_DEFAULT_COLOR       0xffffff
-#define ASS_DEFAULT_BACK_COLOR  0
-#define ASS_DEFAULT_BOLD        0
-#define ASS_DEFAULT_ITALIC      0
-#define ASS_DEFAULT_UNDERLINE   0
-#define ASS_DEFAULT_ALIGNMENT   2
-#define ASS_DEFAULT_BORDERSTYLE 1
-/** @} */
+#include "libavutil/ass_internal.h"
 
 typedef struct FFASSDecoderContext {
     int readorder;
 } FFASSDecoderContext;
 
-/**
- * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS.
- * Can specify all fields explicitly
- *
- * @param avctx pointer to the AVCodecContext
- * @param play_res_x subtitle frame width
- * @param play_res_y subtitle frame height
- * @param font name of the default font face to use
- * @param font_size default font size to use
- * @param primary_color default text color to use (ABGR)
- * @param secondary_color default secondary text color to use (ABGR)
- * @param outline_color default outline color to use (ABGR)
- * @param back_color default background color to use (ABGR)
- * @param bold 1 for bold text, 0 for normal text
- * @param italic 1 for italic text, 0 for normal text
- * @param underline 1 for underline text, 0 for normal text
- * @param border_style 1 for outline, 3 for opaque box
- * @param alignment position of the text (left, center, top...), defined after
- *                  the layout of the numpad (1-3 sub, 4-6 mid, 7-9 top)
- * @return >= 0 on success otherwise an error code <0
- */
-int ff_ass_subtitle_header_full(AVCodecContext *avctx,
+static inline int ff_ass_subtitle_header_full(AVCodecContext *avctx,
                                 int play_res_x, int play_res_y,
                                 const char *font, int font_size,
                                 int primary_color, int secondary_color,
                                 int outline_color, int back_color,
                                 int bold, int italic, int underline,
-                                int border_style, int alignment);
-/**
- * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS.
- *
- * @param avctx pointer to the AVCodecContext
- * @param font name of the default font face to use
- * @param font_size default font size to use
- * @param color default text color to use (ABGR)
- * @param back_color default background color to use (ABGR)
- * @param bold 1 for bold text, 0 for normal text
- * @param italic 1 for italic text, 0 for normal text
- * @param underline 1 for underline text, 0 for normal text
- * @param alignment position of the text (left, center, top...), defined after
- *                  the layout of the numpad (1-3 sub, 4-6 mid, 7-9 top)
- * @return >= 0 on success otherwise an error code <0
- */
-int ff_ass_subtitle_header(AVCodecContext *avctx,
-                           const char *font, int font_size,
-                           int color, int back_color,
-                           int bold, int italic, int underline,
-                           int border_style, int alignment);
+                                int border_style, int alignment)
+{
+    avctx->subtitle_header = (uint8_t *)avpriv_ass_get_subtitle_header_full(
+                                play_res_x, play_res_y, font, font_size,
+                                primary_color, secondary_color, outline_color,
+                                back_color, bold,italic,underline,border_style,alignment,
+                                !(avctx->flags & AV_CODEC_FLAG_BITEXACT));
 
-/**
- * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS
- * with default style.
- *
- * @param avctx pointer to the AVCodecContext
- * @return >= 0 on success otherwise an error code <0
- */
-int ff_ass_subtitle_header_default(AVCodecContext *avctx);
+    if (!avctx->subtitle_header)
+        return AVERROR(ENOMEM);
+    avctx->subtitle_header_size = strlen((char *)avctx->subtitle_header);
+    return 0;
+}
 
-/**
- * Craft an ASS dialog string.
- */
-char *ff_ass_get_dialog(int readorder, int layer, const char *style,
-                        const char *speaker, const char *text);
+static inline int ff_ass_subtitle_header_default(AVCodecContext *avctx)
+{
+    avctx->subtitle_header = (uint8_t *)avpriv_ass_get_subtitle_header_default(!(avctx->flags & AV_CODEC_FLAG_BITEXACT));
+
+    if (!avctx->subtitle_header)
+        return AVERROR(ENOMEM);
+    avctx->subtitle_header_size = strlen((char *)avctx->subtitle_header);
+    return 0;
+}
+
+static inline void ff_ass_decoder_flush(AVCodecContext *avctx)
+{
+    FFASSDecoderContext *s = avctx->priv_data;
+    if (!(avctx->flags2 & AV_CODEC_FLAG2_RO_FLUSH_NOOP))
+        s->readorder = 0;
+}
 
 /**
  * Add an ASS dialog to a subtitle.
  */
-int ff_ass_add_rect(AVSubtitle *sub, const char *dialog,
+static inline int avpriv_ass_add_rect(AVSubtitle *sub, const char *dialog,
                     int readorder, int layer, const char *style,
-                    const char *speaker);
+                    const char *speaker)
+{
+    char *ass_str;
+    AVSubtitleRect **rects;
 
-/**
- * Helper to flush a text subtitles decoder making use of the
- * FFASSDecoderContext.
- */
-void ff_ass_decoder_flush(AVCodecContext *avctx);
+    rects = av_realloc_array(sub->rects, sub->num_rects+1, sizeof(*sub->rects));
+    if (!rects)
+        return AVERROR(ENOMEM);
+    sub->rects = rects;
+    rects[sub->num_rects]       = av_mallocz(sizeof(*rects[0]));
+    if (!rects[sub->num_rects])
+        return AVERROR(ENOMEM);
+    rects[sub->num_rects]->type = SUBTITLE_ASS;
+    ass_str = avpriv_ass_get_dialog(readorder, layer, style, speaker, dialog);
+    if (!ass_str)
+        return AVERROR(ENOMEM);
+    rects[sub->num_rects]->ass = ass_str;
+    sub->num_rects++;
+    return 0;
+}
 
-/**
- * Escape a text subtitle using ASS syntax into an AVBPrint buffer.
- * Newline characters will be escaped to \N.
- *
- * @param buf pointer to an initialized AVBPrint buffer
- * @param p source text
- * @param size size of the source text
- * @param linebreaks additional newline chars, which will be escaped to \N
- * @param keep_ass_markup braces and backslash will not be escaped if set
- */
-void ff_ass_bprint_text_event(AVBPrint *buf, const char *p, int size,
-                             const char *linebreaks, int keep_ass_markup);
 #endif /* AVCODEC_ASS_H */
diff --git a/libavcodec/assdec.c b/libavcodec/assdec.c
index 319279490c..7802a44e71 100644
--- a/libavcodec/assdec.c
+++ b/libavcodec/assdec.c
@@ -22,7 +22,7 @@
 #include <string.h>
 
 #include "avcodec.h"
-#include "ass.h"
+#include "libavutil/ass_internal.h"
 #include "internal.h"
 #include "libavutil/internal.h"
 #include "libavutil/mem.h"
diff --git a/libavcodec/assenc.c b/libavcodec/assenc.c
index a6d107ded2..b0e475834b 100644
--- a/libavcodec/assenc.c
+++ b/libavcodec/assenc.c
@@ -22,7 +22,7 @@
 #include <string.h>
 
 #include "avcodec.h"
-#include "ass.h"
+#include "libavutil/ass_internal.h"
 #include "internal.h"
 #include "libavutil/avstring.h"
 #include "libavutil/internal.h"
diff --git a/libavcodec/ccaption_dec.c b/libavcodec/ccaption_dec.c
index 27c61527f6..27eef75657 100644
--- a/libavcodec/ccaption_dec.c
+++ b/libavcodec/ccaption_dec.c
@@ -272,15 +272,12 @@ static av_cold int init_decoder(AVCodecContext *avctx)
     ctx->bg_color = CCCOL_BLACK;
     ctx->rollup = 2;
     ctx->cursor_row = 10;
-    ret = ff_ass_subtitle_header(avctx, "Monospace",
+    ret = ff_ass_subtitle_header_full(avctx, ASS_DEFAULT_PLAYRESX, ASS_DEFAULT_PLAYRESY, "Monospace",
                                  ASS_DEFAULT_FONT_SIZE,
-                                 ASS_DEFAULT_COLOR,
-                                 ASS_DEFAULT_BACK_COLOR,
-                                 ASS_DEFAULT_BOLD,
-                                 ASS_DEFAULT_ITALIC,
-                                 ASS_DEFAULT_UNDERLINE,
-                                 3,
-                                 ASS_DEFAULT_ALIGNMENT);
+                                 ASS_DEFAULT_COLOR, ASS_DEFAULT_COLOR,
+                                 ASS_DEFAULT_BACK_COLOR, ASS_DEFAULT_BACK_COLOR,
+                                 ASS_DEFAULT_BOLD, ASS_DEFAULT_ITALIC, ASS_DEFAULT_UNDERLINE,
+                                 3, ASS_DEFAULT_ALIGNMENT);
     if (ret < 0) {
         return ret;
     }
@@ -886,7 +883,7 @@ static int decode(AVCodecContext *avctx, void *data, int *got_sub, AVPacket *avp
                                                      AV_TIME_BASE_Q, ms_tb);
             else
                 sub->end_display_time = -1;
-            ret = ff_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL);
+            ret = avpriv_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL);
             if (ret < 0)
                 return ret;
             ctx->last_real_time = sub->pts;
@@ -896,7 +893,7 @@ static int decode(AVCodecContext *avctx, void *data, int *got_sub, AVPacket *avp
 
     if (!bptr && !ctx->real_time && ctx->buffer[!ctx->buffer_index].str[0]) {
         bidx = !ctx->buffer_index;
-        ret = ff_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL);
         if (ret < 0)
             return ret;
         sub->pts = ctx->buffer_time[1];
@@ -914,7 +911,7 @@ static int decode(AVCodecContext *avctx, void *data, int *got_sub, AVPacket *avp
         capture_screen(ctx);
         ctx->buffer_changed = 0;
 
-        ret = ff_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL);
         if (ret < 0)
             return ret;
         sub->end_display_time = -1;
diff --git a/libavcodec/jacosubdec.c b/libavcodec/jacosubdec.c
index 698895a86b..6a53ec3e34 100644
--- a/libavcodec/jacosubdec.c
+++ b/libavcodec/jacosubdec.c
@@ -183,7 +183,7 @@ static int jacosub_decode_frame(AVCodecContext *avctx,
 
         av_bprint_init(&buffer, JSS_MAX_LINESIZE, JSS_MAX_LINESIZE);
         jacosub_to_ass(avctx, &buffer, ptr);
-        ret = ff_ass_add_rect(sub, buffer.str, s->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, buffer.str, s->readorder++, 0, NULL, NULL);
         av_bprint_finalize(&buffer, NULL);
         if (ret < 0)
             return ret;
diff --git a/libavcodec/libaribb24.c b/libavcodec/libaribb24.c
index 0766c0079d..3fb7e5f16e 100644
--- a/libavcodec/libaribb24.c
+++ b/libavcodec/libaribb24.c
@@ -273,7 +273,7 @@ next_region:
         av_log(avctx, AV_LOG_DEBUG, "Styled ASS line: %s\n",
                buf.str);
 
-        ret = ff_ass_add_rect(sub, buf.str, b24->read_order++,
+        ret = avpriv_ass_add_rect(sub, buf.str, b24->read_order++,
                               0, NULL, NULL);
     }
 
diff --git a/libavcodec/libzvbi-teletextdec.c b/libavcodec/libzvbi-teletextdec.c
index 1073d6a0bd..bd9edc34d7 100644
--- a/libavcodec/libzvbi-teletextdec.c
+++ b/libavcodec/libzvbi-teletextdec.c
@@ -152,12 +152,12 @@ static char *create_ass_text(TeletextContext *ctx, const char *text)
     AVBPrint buf;
 
     av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
-    ff_ass_bprint_text_event(&buf, text, strlen(text), "", 0);
+    avpriv_ass_bprint_text_event(&buf, text, strlen(text), "", 0);
     if (!av_bprint_is_complete(&buf)) {
         av_bprint_finalize(&buf, NULL);
         return NULL;
     }
-    dialog = ff_ass_get_dialog(ctx->readorder++, 0, NULL, NULL, buf.str);
+    dialog = avpriv_ass_get_dialog(ctx->readorder++, 0, NULL, NULL, buf.str);
     av_bprint_finalize(&buf, NULL);
     return dialog;
 }
@@ -224,7 +224,7 @@ static int gen_sub_text(TeletextContext *ctx, AVSubtitleRect *sub_rect, vbi_page
         }
         av_log(ctx, AV_LOG_DEBUG, "subtext:%s:txetbus\n", sub_rect->ass);
     } else {
-        sub_rect->type = SUBTITLE_NONE;
+        sub_rect->type = AV_SUBTITLE_FMT_NONE;
     }
     av_bprint_finalize(&buf, NULL);
     return 0;
@@ -394,7 +394,7 @@ static int gen_sub_ass(TeletextContext *ctx, AVSubtitleRect *sub_rect, vbi_page
 
     if (buf.len) {
         sub_rect->type = SUBTITLE_ASS;
-        sub_rect->ass = ff_ass_get_dialog(ctx->readorder++, 0, is_subtitle_page ? "Subtitle" : "Teletext", NULL, buf.str);
+        sub_rect->ass = avpriv_ass_get_dialog(ctx->readorder++, 0, is_subtitle_page ? "Subtitle" : "Teletext", NULL, buf.str);
 
         if (!sub_rect->ass) {
             av_bprint_finalize(&buf, NULL);
@@ -402,7 +402,7 @@ static int gen_sub_ass(TeletextContext *ctx, AVSubtitleRect *sub_rect, vbi_page
         }
         av_log(ctx, AV_LOG_DEBUG, "subtext:%s:txetbus\n", sub_rect->ass);
     } else {
-        sub_rect->type = SUBTITLE_NONE;
+        sub_rect->type = AV_SUBTITLE_FMT_NONE;
     }
     av_bprint_finalize(&buf, NULL);
     return 0;
@@ -462,7 +462,7 @@ static int gen_sub_bitmap(TeletextContext *ctx, AVSubtitleRect *sub_rect, vbi_pa
 
     if (vc >= vcend) {
         av_log(ctx, AV_LOG_DEBUG, "dropping empty page %3x\n", page->pgno);
-        sub_rect->type = SUBTITLE_NONE;
+        sub_rect->type = AV_SUBTITLE_FMT_NONE;
         return 0;
     }
 
@@ -695,7 +695,7 @@ static int teletext_decode_frame(AVCodecContext *avctx, void *data, int *got_sub
         sub->num_rects = 0;
         sub->pts = ctx->pages->pts;
 
-        if (ctx->pages->sub_rect->type != SUBTITLE_NONE) {
+        if (ctx->pages->sub_rect->type != AV_SUBTITLE_FMT_NONE) {
             sub->rects = av_malloc(sizeof(*sub->rects));
             if (sub->rects) {
                 sub->num_rects = 1;
diff --git a/libavcodec/microdvddec.c b/libavcodec/microdvddec.c
index c45fe043bf..fc09db8997 100644
--- a/libavcodec/microdvddec.c
+++ b/libavcodec/microdvddec.c
@@ -310,7 +310,7 @@ static int microdvd_decode_frame(AVCodecContext *avctx,
         }
     }
     if (new_line.len) {
-        int ret = ff_ass_add_rect(sub, new_line.str, s->readorder++, 0, NULL, NULL);
+        int ret = avpriv_ass_add_rect(sub, new_line.str, s->readorder++, 0, NULL, NULL);
         av_bprint_finalize(&new_line, NULL);
         if (ret < 0)
             return ret;
@@ -363,8 +363,9 @@ static int microdvd_init(AVCodecContext *avctx)
             }
         }
     }
-    return ff_ass_subtitle_header(avctx, font_buf.str, font_size, color,
-                                  ASS_DEFAULT_BACK_COLOR, bold, italic,
+    return ff_ass_subtitle_header_full(avctx, ASS_DEFAULT_PLAYRESX, ASS_DEFAULT_PLAYRESY,
+                                  font_buf.str, font_size, color, color,
+                                  ASS_DEFAULT_BACK_COLOR, ASS_DEFAULT_BACK_COLOR, bold, italic,
                                   underline, ASS_DEFAULT_BORDERSTYLE,
                                   alignment);
 }
diff --git a/libavcodec/movtextdec.c b/libavcodec/movtextdec.c
index 825632ca9b..9b17bac9ea 100644
--- a/libavcodec/movtextdec.c
+++ b/libavcodec/movtextdec.c
@@ -22,7 +22,6 @@
 #include "avcodec.h"
 #include "ass.h"
 #include "libavutil/opt.h"
-#include "libavutil/avstring.h"
 #include "libavutil/common.h"
 #include "libavutil/bprint.h"
 #include "libavutil/intreadwrite.h"
@@ -554,7 +553,7 @@ static int mov_text_decode_frame(AVCodecContext *avctx,
     } else
         text_to_ass(&buf, ptr, end, avctx);
 
-    ret = ff_ass_add_rect(sub, buf.str, m->readorder++, 0, NULL, NULL);
+    ret = avpriv_ass_add_rect(sub, buf.str, m->readorder++, 0, NULL, NULL);
     av_bprint_finalize(&buf, NULL);
     if (ret < 0)
         return ret;
diff --git a/libavcodec/movtextenc.c b/libavcodec/movtextenc.c
index 221cd76fea..d506ed5c37 100644
--- a/libavcodec/movtextenc.c
+++ b/libavcodec/movtextenc.c
@@ -26,8 +26,8 @@
 #include "libavutil/intreadwrite.h"
 #include "libavutil/mem.h"
 #include "libavutil/common.h"
-#include "ass_split.h"
-#include "ass.h"
+#include "libavutil/ass_split_internal.h"
+#include "libavutil/ass_internal.h"
 #include "bytestream.h"
 #include "internal.h"
 
@@ -167,7 +167,7 @@ static int mov_text_encode_close(AVCodecContext *avctx)
 {
     MovTextContext *s = avctx->priv_data;
 
-    ff_ass_split_free(s->ass_ctx);
+    avpriv_ass_split_free(s->ass_ctx);
     av_freep(&s->style_attributes);
     av_freep(&s->fonts);
     av_bprint_finalize(&s->buffer, NULL);
@@ -222,7 +222,7 @@ static int encode_sample_description(AVCodecContext *avctx)
     else
         s->font_scale_factor = 1;
 
-    style = ff_ass_style_get(s->ass_ctx, "Default");
+    style = avpriv_ass_style_get(s->ass_ctx, "Default");
     if (!style && ass->styles_count) {
         style = &ass->styles[0];
     }
@@ -329,7 +329,7 @@ static av_cold int mov_text_encode_init(AVCodecContext *avctx)
 
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
 
-    s->ass_ctx = ff_ass_split(avctx->subtitle_header);
+    s->ass_ctx = avpriv_ass_split(avctx->subtitle_header);
     if (!s->ass_ctx)
         return AVERROR_INVALIDDATA;
     ret = encode_sample_description(avctx);
@@ -566,7 +566,7 @@ static void mov_text_ass_style_set(MovTextContext *s, ASSStyle *style)
 
 static void mov_text_dialog(MovTextContext *s, ASSDialog *dialog)
 {
-    ASSStyle *style = ff_ass_style_get(s->ass_ctx, dialog->style);
+    ASSStyle *style = avpriv_ass_style_get(s->ass_ctx, dialog->style);
 
     s->ass_dialog_style = style;
     mov_text_ass_style_set(s, style);
@@ -580,7 +580,7 @@ static void mov_text_cancel_overrides_cb(void *priv, const char *style_name)
     if (!style_name || !*style_name)
         style = s->ass_dialog_style;
     else
-        style= ff_ass_style_get(s->ass_ctx, style_name);
+        style= avpriv_ass_style_get(s->ass_ctx, style_name);
 
     mov_text_ass_style_set(s, style);
 }
@@ -652,12 +652,12 @@ static int mov_text_encode_frame(AVCodecContext *avctx, unsigned char *buf,
             return AVERROR(EINVAL);
         }
 
-        dialog = ff_ass_split_dialog(s->ass_ctx, ass);
+        dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
         if (!dialog)
             return AVERROR(ENOMEM);
         mov_text_dialog(s, dialog);
-        ff_ass_split_override_codes(&mov_text_callbacks, s, dialog->text);
-        ff_ass_free_dialog(&dialog);
+        avpriv_ass_split_override_codes(&mov_text_callbacks, s, dialog->text);
+        avpriv_ass_free_dialog(&dialog);
     }
 
     if (s->buffer.len > UINT16_MAX)
diff --git a/libavcodec/mpl2dec.c b/libavcodec/mpl2dec.c
index 61e47050ec..fe806b4927 100644
--- a/libavcodec/mpl2dec.c
+++ b/libavcodec/mpl2dec.c
@@ -74,7 +74,7 @@ static int mpl2_decode_frame(AVCodecContext *avctx, void *data,
 
     av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
     if (ptr && avpkt->size > 0 && *ptr && !mpl2_event_to_ass(&buf, ptr))
-        ret = ff_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
     av_bprint_finalize(&buf, NULL);
     if (ret < 0)
         return ret;
diff --git a/libavcodec/realtextdec.c b/libavcodec/realtextdec.c
index 11b586d493..acd548b6b5 100644
--- a/libavcodec/realtextdec.c
+++ b/libavcodec/realtextdec.c
@@ -67,7 +67,7 @@ static int realtext_decode_frame(AVCodecContext *avctx,
 
     av_bprint_init(&buf, 0, 4096);
     if (ptr && avpkt->size > 0 && !rt_event_to_ass(&buf, ptr))
-        ret = ff_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
     av_bprint_finalize(&buf, NULL);
     if (ret < 0)
         return ret;
diff --git a/libavcodec/samidec.c b/libavcodec/samidec.c
index 32d07447b4..1249b629d6 100644
--- a/libavcodec/samidec.c
+++ b/libavcodec/samidec.c
@@ -144,7 +144,7 @@ static int sami_decode_frame(AVCodecContext *avctx,
         if (ret < 0)
             return ret;
         // TODO: pass escaped sami->encoded_source.str as source
-        ret = ff_ass_add_rect(sub, sami->full.str, sami->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, sami->full.str, sami->readorder++, 0, NULL, NULL);
         if (ret < 0)
             return ret;
     }
diff --git a/libavcodec/srtdec.c b/libavcodec/srtdec.c
index 4f16226b83..847352eb37 100644
--- a/libavcodec/srtdec.c
+++ b/libavcodec/srtdec.c
@@ -78,7 +78,7 @@ static int srt_decode_frame(AVCodecContext *avctx,
 
     ret = srt_to_ass(avctx, &buffer, avpkt->data, x1, y1, x2, y2);
     if (ret >= 0)
-        ret = ff_ass_add_rect(sub, buffer.str, s->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, buffer.str, s->readorder++, 0, NULL, NULL);
     av_bprint_finalize(&buffer, NULL);
     if (ret < 0)
         return ret;
diff --git a/libavcodec/srtenc.c b/libavcodec/srtenc.c
index 2e3ac55770..a7c5fccefe 100644
--- a/libavcodec/srtenc.c
+++ b/libavcodec/srtenc.c
@@ -23,8 +23,8 @@
 #include "avcodec.h"
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
-#include "ass_split.h"
-#include "ass.h"
+#include "libavutil/ass_split_internal.h"
+#include "libavutil/ass_internal.h"
 #include "internal.h"
 
 
@@ -94,7 +94,7 @@ static void srt_stack_push_pop(SRTContext *s, const char c, int close)
 
 static void srt_style_apply(SRTContext *s, const char *style)
 {
-    ASSStyle *st = ff_ass_style_get(s->ass_ctx, style);
+    ASSStyle *st = avpriv_ass_style_get(s->ass_ctx, style);
     if (st) {
         int c = st->primary_color & 0xFFFFFF;
         if (st->font_name && strcmp(st->font_name, ASS_DEFAULT_FONT) ||
@@ -135,7 +135,7 @@ static av_cold int srt_encode_init(AVCodecContext *avctx)
 {
     SRTContext *s = avctx->priv_data;
     s->avctx = avctx;
-    s->ass_ctx = ff_ass_split(avctx->subtitle_header);
+    s->ass_ctx = avpriv_ass_split(avctx->subtitle_header);
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
     return s->ass_ctx ? 0 : AVERROR_INVALIDDATA;
 }
@@ -245,14 +245,14 @@ static int encode_frame(AVCodecContext *avctx,
             return AVERROR(EINVAL);
         }
 
-        dialog = ff_ass_split_dialog(s->ass_ctx, ass);
+        dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
         if (!dialog)
             return AVERROR(ENOMEM);
         s->alignment_applied = 0;
         if (avctx->codec_id == AV_CODEC_ID_SUBRIP)
             srt_style_apply(s, dialog->style);
-        ff_ass_split_override_codes(cb, s, dialog->text);
-        ff_ass_free_dialog(&dialog);
+        avpriv_ass_split_override_codes(cb, s, dialog->text);
+        avpriv_ass_free_dialog(&dialog);
     }
 
     if (!av_bprint_is_complete(&s->buffer))
@@ -284,7 +284,7 @@ static int text_encode_frame(AVCodecContext *avctx,
 static int srt_encode_close(AVCodecContext *avctx)
 {
     SRTContext *s = avctx->priv_data;
-    ff_ass_split_free(s->ass_ctx);
+    avpriv_ass_split_free(s->ass_ctx);
     av_bprint_finalize(&s->buffer, NULL);
     return 0;
 }
diff --git a/libavcodec/subviewerdec.c b/libavcodec/subviewerdec.c
index 5c650d0cde..04e9ae7c09 100644
--- a/libavcodec/subviewerdec.c
+++ b/libavcodec/subviewerdec.c
@@ -58,7 +58,7 @@ static int subviewer_decode_frame(AVCodecContext *avctx,
 
     av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
     if (ptr && avpkt->size > 0 && !subviewer_event_to_ass(&buf, ptr))
-        ret = ff_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
     av_bprint_finalize(&buf, NULL);
     if (ret < 0)
         return ret;
diff --git a/libavcodec/textdec.c b/libavcodec/textdec.c
index 308553660a..7556deefe8 100644
--- a/libavcodec/textdec.c
+++ b/libavcodec/textdec.c
@@ -54,8 +54,8 @@ static int text_decode_frame(AVCodecContext *avctx, void *data,
 
     av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
     if (ptr && avpkt->size > 0 && *ptr) {
-        ff_ass_bprint_text_event(&buf, ptr, avpkt->size, text->linebreaks, text->keep_ass_markup);
-        ret = ff_ass_add_rect(sub, buf.str, text->readorder++, 0, NULL, NULL);
+        avpriv_ass_bprint_text_event(&buf, ptr, avpkt->size, text->linebreaks, text->keep_ass_markup);
+        ret = avpriv_ass_add_rect(sub, buf.str, text->readorder++, 0, NULL, NULL);
     }
     av_bprint_finalize(&buf, NULL);
     if (ret < 0)
diff --git a/libavcodec/ttmlenc.c b/libavcodec/ttmlenc.c
index ad2eddfdd5..083f2dd67a 100644
--- a/libavcodec/ttmlenc.c
+++ b/libavcodec/ttmlenc.c
@@ -32,8 +32,7 @@
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
 #include "libavutil/internal.h"
-#include "ass_split.h"
-#include "ass.h"
+#include "libavutil/ass_split_internal.h"
 #include "ttmlenc.h"
 
 typedef struct {
@@ -95,7 +94,7 @@ static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf,
             return AVERROR(EINVAL);
         }
 
-        dialog = ff_ass_split_dialog(s->ass_ctx, ass);
+        dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
         if (!dialog)
             return AVERROR(ENOMEM);
 
@@ -107,7 +106,7 @@ static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf,
             av_bprintf(&s->buffer, "\">");
         }
 
-        ret = ff_ass_split_override_codes(&ttml_callbacks, s, dialog->text);
+        ret = avpriv_ass_split_override_codes(&ttml_callbacks, s, dialog->text);
         if (ret < 0) {
             int log_level = (ret != AVERROR_INVALIDDATA ||
                              avctx->err_recognition & AV_EF_EXPLODE) ?
@@ -118,7 +117,7 @@ static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf,
                    av_err2str(ret));
 
             if (log_level == AV_LOG_ERROR) {
-                ff_ass_free_dialog(&dialog);
+                avpriv_ass_free_dialog(&dialog);
                 return ret;
             }
         }
@@ -126,7 +125,7 @@ static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf,
         if (dialog->style)
             av_bprintf(&s->buffer, "</span>");
 
-        ff_ass_free_dialog(&dialog);
+        avpriv_ass_free_dialog(&dialog);
     }
 
     if (!av_bprint_is_complete(&s->buffer))
@@ -148,7 +147,7 @@ static av_cold int ttml_encode_close(AVCodecContext *avctx)
 {
     TTMLContext *s = avctx->priv_data;
 
-    ff_ass_split_free(s->ass_ctx);
+    avpriv_ass_split_free(s->ass_ctx);
 
     av_bprint_finalize(&s->buffer, NULL);
 
@@ -372,7 +371,7 @@ static av_cold int ttml_encode_init(AVCodecContext *avctx)
 
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
 
-    if (!(s->ass_ctx = ff_ass_split(avctx->subtitle_header))) {
+    if (!(s->ass_ctx = avpriv_ass_split(avctx->subtitle_header))) {
         return AVERROR_INVALIDDATA;
     }
 
diff --git a/libavcodec/webvttdec.c b/libavcodec/webvttdec.c
index 0093f328fa..d304edc705 100644
--- a/libavcodec/webvttdec.c
+++ b/libavcodec/webvttdec.c
@@ -91,7 +91,7 @@ static int webvtt_decode_frame(AVCodecContext *avctx,
 
     av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
     if (ptr && avpkt->size > 0 && !webvtt_event_to_ass(&buf, ptr))
-        ret = ff_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
     av_bprint_finalize(&buf, NULL);
     if (ret < 0)
         return ret;
diff --git a/libavcodec/webvttenc.c b/libavcodec/webvttenc.c
index 89b49e42bf..761099b69a 100644
--- a/libavcodec/webvttenc.c
+++ b/libavcodec/webvttenc.c
@@ -24,8 +24,8 @@
 #include "avcodec.h"
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
-#include "ass_split.h"
-#include "ass.h"
+#include "libavutil/ass_split_internal.h"
+#include "libavutil/ass_internal.h"
 #include "internal.h"
 
 #define WEBVTT_STACK_SIZE 64
@@ -93,7 +93,7 @@ static void webvtt_stack_push_pop(WebVTTContext *s, const char c, int close)
 
 static void webvtt_style_apply(WebVTTContext *s, const char *style)
 {
-    ASSStyle *st = ff_ass_style_get(s->ass_ctx, style);
+    ASSStyle *st = avpriv_ass_style_get(s->ass_ctx, style);
     if (st) {
         if (st->bold != ASS_DEFAULT_BOLD) {
             webvtt_print(s, "<b>");
@@ -172,12 +172,12 @@ static int webvtt_encode_frame(AVCodecContext *avctx,
             return AVERROR(EINVAL);
         }
 
-        dialog = ff_ass_split_dialog(s->ass_ctx, ass);
+        dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
         if (!dialog)
             return AVERROR(ENOMEM);
         webvtt_style_apply(s, dialog->style);
-        ff_ass_split_override_codes(&webvtt_callbacks, s, dialog->text);
-        ff_ass_free_dialog(&dialog);
+        avpriv_ass_split_override_codes(&webvtt_callbacks, s, dialog->text);
+        avpriv_ass_free_dialog(&dialog);
     }
 
     if (!av_bprint_is_complete(&s->buffer))
@@ -197,7 +197,7 @@ static int webvtt_encode_frame(AVCodecContext *avctx,
 static int webvtt_encode_close(AVCodecContext *avctx)
 {
     WebVTTContext *s = avctx->priv_data;
-    ff_ass_split_free(s->ass_ctx);
+    avpriv_ass_split_free(s->ass_ctx);
     av_bprint_finalize(&s->buffer, NULL);
     return 0;
 }
@@ -206,7 +206,7 @@ static av_cold int webvtt_encode_init(AVCodecContext *avctx)
 {
     WebVTTContext *s = avctx->priv_data;
     s->avctx = avctx;
-    s->ass_ctx = ff_ass_split(avctx->subtitle_header);
+    s->ass_ctx = avpriv_ass_split(avctx->subtitle_header);
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
     return s->ass_ctx ? 0 : AVERROR_INVALIDDATA;
 }
diff --git a/libavutil/Makefile b/libavutil/Makefile
index 8bc0a14942..7d4c4793b1 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -101,6 +101,8 @@ BUILT_HEADERS = avconfig.h                                              \
 OBJS = adler32.o                                                        \
        aes.o                                                            \
        aes_ctr.o                                                        \
+       ass.o                                                            \
+       ass_split.o                                                      \
        audio_fifo.o                                                     \
        avstring.o                                                       \
        avsscanf.o                                                       \
diff --git a/libavcodec/ass.c b/libavutil/ass.c
similarity index 65%
rename from libavcodec/ass.c
rename to libavutil/ass.c
index 725e4d42ba..9eeaa38ba9 100644
--- a/libavcodec/ass.c
+++ b/libavutil/ass.c
@@ -19,21 +19,22 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-#include "avcodec.h"
-#include "ass.h"
+#include "ass_internal.h"
+
+#include "subfmt.h"
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
 #include "libavutil/common.h"
 
-int ff_ass_subtitle_header_full(AVCodecContext *avctx,
-                                int play_res_x, int play_res_y,
-                                const char *font, int font_size,
-                                int primary_color, int secondary_color,
-                                int outline_color, int back_color,
-                                int bold, int italic, int underline,
-                                int border_style, int alignment)
+char* avpriv_ass_get_subtitle_header_full(int play_res_x, int play_res_y,
+                                    const char *font, int font_size,
+                                    int primary_color, int secondary_color,
+                                    int outline_color, int back_color,
+                                    int bold, int italic, int underline,
+                                    int border_style, int alignment,
+                                    int print_av_version)
 {
-    avctx->subtitle_header = av_asprintf(
+    char* header = av_asprintf(
              "[Script Info]\r\n"
              "; Script generated by FFmpeg/Lavc%s\r\n"
              "ScriptType: v4.00+\r\n"
@@ -68,34 +69,31 @@ int ff_ass_subtitle_header_full(AVCodecContext *avctx,
              "\r\n"
              "[Events]\r\n"
              "Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\r\n",
-             !(avctx->flags & AV_CODEC_FLAG_BITEXACT) ? AV_STRINGIFY(LIBAVCODEC_VERSION) : "",
+             print_av_version ? AV_STRINGIFY(LIBAVCODEC_VERSION) : "",
              play_res_x, play_res_y, font, font_size,
              primary_color, secondary_color, outline_color, back_color,
              -bold, -italic, -underline, border_style, alignment);
 
-    if (!avctx->subtitle_header)
-        return AVERROR(ENOMEM);
-    avctx->subtitle_header_size = strlen(avctx->subtitle_header);
-    return 0;
+    return header;
 }
 
-int ff_ass_subtitle_header(AVCodecContext *avctx,
-                           const char *font, int font_size,
+char* avpriv_ass_get_subtitle_header(const char *font, int font_size,
                            int color, int back_color,
                            int bold, int italic, int underline,
-                           int border_style, int alignment)
+                           int border_style, int alignment,
+                           int print_av_version)
 {
-    return ff_ass_subtitle_header_full(avctx,
-                               ASS_DEFAULT_PLAYRESX, ASS_DEFAULT_PLAYRESY,
-                               font, font_size, color, color,
-                               back_color, back_color,
-                               bold, italic, underline,
-                               border_style, alignment);
+    return avpriv_ass_get_subtitle_header_full(ASS_DEFAULT_PLAYRESX, ASS_DEFAULT_PLAYRESY,
+                                       font, font_size, color, color,
+                                       back_color, back_color,
+                                       bold, italic, underline,
+                                       border_style, alignment,
+                                       print_av_version);
 }
 
-int ff_ass_subtitle_header_default(AVCodecContext *avctx)
+char* avpriv_ass_get_subtitle_header_default(int print_av_version)
 {
-    return ff_ass_subtitle_header(avctx, ASS_DEFAULT_FONT,
+    return avpriv_ass_get_subtitle_header(ASS_DEFAULT_FONT,
                                ASS_DEFAULT_FONT_SIZE,
                                ASS_DEFAULT_COLOR,
                                ASS_DEFAULT_BACK_COLOR,
@@ -103,10 +101,11 @@ int ff_ass_subtitle_header_default(AVCodecContext *avctx)
                                ASS_DEFAULT_ITALIC,
                                ASS_DEFAULT_UNDERLINE,
                                ASS_DEFAULT_BORDERSTYLE,
-                               ASS_DEFAULT_ALIGNMENT);
+                               ASS_DEFAULT_ALIGNMENT,
+                               print_av_version);
 }
 
-char *ff_ass_get_dialog(int readorder, int layer, const char *style,
+char *avpriv_ass_get_dialog(int readorder, int layer, const char *style,
                         const char *speaker, const char *text)
 {
     return av_asprintf("%d,%d,%s,%s,0,0,0,,%s",
@@ -114,37 +113,17 @@ char *ff_ass_get_dialog(int readorder, int layer, const char *style,
                        speaker ? speaker : "", text);
 }
 
-int ff_ass_add_rect(AVSubtitle *sub, const char *dialog,
-                    int readorder, int layer, const char *style,
-                    const char *speaker)
+char *avpriv_ass_get_dialog_ex(int readorder, int layer, const char *style,
+                        const char *speaker, int margin_l, int margin_r,
+                        int margin_v, const char *text)
 {
-    AVSubtitleRect **rects, *rect;
-    char *ass_str;
-
-    rects = av_realloc_array(sub->rects, sub->num_rects+1, sizeof(*sub->rects));
-    if (!rects)
-        return AVERROR(ENOMEM);
-    sub->rects = rects;
-    rect       = av_mallocz(sizeof(*rect));
-    if (!rect)
-        return AVERROR(ENOMEM);
-    rects[sub->num_rects++] = rect;
-    rect->type = SUBTITLE_ASS;
-    ass_str = ff_ass_get_dialog(readorder, layer, style, speaker, dialog);
-    if (!ass_str)
-        return AVERROR(ENOMEM);
-    rect->ass = ass_str;
-    return 0;
-}
-
-void ff_ass_decoder_flush(AVCodecContext *avctx)
-{
-    FFASSDecoderContext *s = avctx->priv_data;
-    if (!(avctx->flags2 & AV_CODEC_FLAG2_RO_FLUSH_NOOP))
-        s->readorder = 0;
+    return av_asprintf("%d,%d,%s,%s,%d,%d,%d,,%s",
+                       readorder, layer, style ? style : "Default",
+                       speaker ? speaker : "", margin_l, margin_r,
+                       margin_v, text);
 }
 
-void ff_ass_bprint_text_event(AVBPrint *buf, const char *p, int size,
+void avpriv_ass_bprint_text_event(AVBPrint *buf, const char *p, int size,
                              const char *linebreaks, int keep_ass_markup)
 {
     const char *p_end = p + size;
diff --git a/libavutil/ass_internal.h b/libavutil/ass_internal.h
new file mode 100644
index 0000000000..cde5561cd3
--- /dev/null
+++ b/libavutil/ass_internal.h
@@ -0,0 +1,135 @@
+/*
+ * SSA/ASS common functions
+ * Copyright (c) 2010  Aurelien Jacobs <aurel@gnuage.org>
+ *
+ * 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
+ */
+
+#ifndef AVUTIL_ASS_INTERNAL_H
+#define AVUTIL_ASS_INTERNAL_H
+
+#include "subfmt.h"
+#include "libavutil/bprint.h"
+
+#define ASS_DEFAULT_PLAYRESX 384
+#define ASS_DEFAULT_PLAYRESY 288
+
+/**
+ * @name Default values for ASS style
+ * @{
+ */
+#define ASS_DEFAULT_FONT        "Arial"
+#define ASS_DEFAULT_FONT_SIZE   16
+#define ASS_DEFAULT_COLOR       0xffffff
+#define ASS_DEFAULT_BACK_COLOR  0
+#define ASS_DEFAULT_BOLD        0
+#define ASS_DEFAULT_ITALIC      0
+#define ASS_DEFAULT_UNDERLINE   0
+#define ASS_DEFAULT_ALIGNMENT   2
+#define ASS_DEFAULT_BORDERSTYLE 1
+/** @} */
+
+/**
+ * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS.
+ * Can specify all fields explicitly
+ *
+ * @param play_res_x subtitle frame width
+ * @param play_res_y subtitle frame height
+ * @param font name of the default font face to use
+ * @param font_size default font size to use
+ * @param primary_color default text color to use (ABGR)
+ * @param secondary_color default secondary text color to use (ABGR)
+ * @param outline_color default outline color to use (ABGR)
+ * @param back_color default background color to use (ABGR)
+ * @param bold 1 for bold text, 0 for normal text
+ * @param italic 1 for italic text, 0 for normal text
+ * @param underline 1 for underline text, 0 for normal text
+ * @param border_style 1 for outline, 3 for opaque box
+ * @param alignment position of the text (left, center, top...), defined after
+ *                  the layout of the numpad (1-3 sub, 4-6 mid, 7-9 top)
+ * @param print_av_version include library version in header
+ * @return a string containing the subtitle header that needs
+ *         to be released via av_free()
+ */
+char* avpriv_ass_get_subtitle_header_full(int play_res_x, int play_res_y,
+                                  const char *font, int font_size,
+                                  int primary_color, int secondary_color,
+                                  int outline_color, int back_color,
+                                  int bold, int italic, int underline,
+                                  int border_style, int alignment,
+                                  int print_av_version);
+
+/**
+ * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS.
+ *
+ * @param font name of the default font face to use
+ * @param font_size default font size to use
+ * @param color default text color to use (ABGR)
+ * @param back_color default background color to use (ABGR)
+ * @param bold 1 for bold text, 0 for normal text
+ * @param italic 1 for italic text, 0 for normal text
+ * @param underline 1 for underline text, 0 for normal text
+ * @param border_style 1 for outline, 3 for opaque box
+ * @param alignment position of the text (left, center, top...), defined after
+ *                  the layout of the numpad (1-3 sub, 4-6 mid, 7-9 top)
+ * @param print_av_version include library version in header
+ * @return a string containing the subtitle header that needs
+ *         to be released via av_free()
+ */
+char* avpriv_ass_get_subtitle_header(const char *font, int font_size,
+                                int color, int back_color,
+                                int bold, int italic, int underline,
+                                int border_style, int alignment,
+                                int print_av_version);
+
+/**
+ * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS
+ * with default style.
+ *
+ * @param print_av_version include library version in header
+ * @return a string containing the subtitle header that needs
+ *         to be released via av_free()
+ */
+char* avpriv_ass_get_subtitle_header_default(int print_av_version);
+
+/**
+ * Craft an ASS dialog string.
+ */
+char *avpriv_ass_get_dialog(int readorder, int layer, const char *style,
+                        const char *speaker, const char *text);
+
+/**
+ * Craft an ASS dialog string.
+ */
+char *avpriv_ass_get_dialog_ex(int readorder, int layer, const char *style,
+                        const char *speaker, int margin_l, int margin_r,
+                        int margin_v, const char *text);
+
+/**
+ * Escape a text subtitle using ASS syntax into an AVBPrint buffer.
+ * Newline characters will be escaped to \N.
+ *
+ * @param buf pointer to an initialized AVBPrint buffer
+ * @param p source text
+ * @param size size of the source text
+ * @param linebreaks additional newline chars, which will be escaped to \N
+ * @param keep_ass_markup braces and backslash will not be escaped if set
+ */
+void avpriv_ass_bprint_text_event(AVBPrint *buf, const char *p, int size,
+                             const char *linebreaks, int keep_ass_markup);
+
+#endif /* AVUTIL_ASS_INTERNAL_H */
diff --git a/libavcodec/ass_split.c b/libavutil/ass_split.c
similarity index 94%
rename from libavcodec/ass_split.c
rename to libavutil/ass_split.c
index 05c5453e53..c5963351fc 100644
--- a/libavcodec/ass_split.c
+++ b/libavutil/ass_split.c
@@ -22,7 +22,7 @@
 #include "libavutil/common.h"
 #include "libavutil/error.h"
 #include "libavutil/mem.h"
-#include "ass_split.h"
+#include "ass_split_internal.h"
 
 typedef enum {
     ASS_STR,
@@ -373,7 +373,7 @@ static int ass_split(ASSSplitContext *ctx, const char *buf)
     return buf ? 0 : AVERROR_INVALIDDATA;
 }
 
-ASSSplitContext *ff_ass_split(const char *buf)
+ASSSplitContext *avpriv_ass_split(const char *buf)
 {
     ASSSplitContext *ctx = av_mallocz(sizeof(*ctx));
     if (!ctx)
@@ -382,7 +382,7 @@ ASSSplitContext *ff_ass_split(const char *buf)
         buf += 3;
     ctx->current_section = -1;
     if (ass_split(ctx, buf) < 0) {
-        ff_ass_split_free(ctx);
+        avpriv_ass_split_free(ctx);
         return NULL;
     }
     return ctx;
@@ -412,7 +412,7 @@ static void free_section(ASSSplitContext *ctx, const ASSSection *section)
         av_freep((uint8_t *)&ctx->ass + section->offset);
 }
 
-void ff_ass_free_dialog(ASSDialog **dialogp)
+void avpriv_ass_free_dialog(ASSDialog **dialogp)
 {
     ASSDialog *dialog = *dialogp;
     if (!dialog)
@@ -424,7 +424,7 @@ void ff_ass_free_dialog(ASSDialog **dialogp)
     av_freep(dialogp);
 }
 
-ASSDialog *ff_ass_split_dialog(ASSSplitContext *ctx, const char *buf)
+ASSDialog *avpriv_ass_split_dialog(ASSSplitContext *ctx, const char *buf)
 {
     int i;
     static const ASSFields fields[] = {
@@ -451,7 +451,7 @@ ASSDialog *ff_ass_split_dialog(ASSSplitContext *ctx, const char *buf)
         buf = skip_space(buf);
         len = last ? strlen(buf) : strcspn(buf, ",");
         if (len >= INT_MAX) {
-            ff_ass_free_dialog(&dialog);
+            avpriv_ass_free_dialog(&dialog);
             return NULL;
         }
         convert_func[type](ptr, buf, len);
@@ -461,7 +461,7 @@ ASSDialog *ff_ass_split_dialog(ASSSplitContext *ctx, const char *buf)
     return dialog;
 }
 
-void ff_ass_split_free(ASSSplitContext *ctx)
+void avpriv_ass_split_free(ASSSplitContext *ctx)
 {
     if (ctx) {
         int i;
@@ -474,7 +474,7 @@ void ff_ass_split_free(ASSSplitContext *ctx)
 }
 
 
-int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
+int avpriv_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
                                 const char *buf)
 {
     const char *text = NULL;
@@ -497,8 +497,8 @@ int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
             while (*buf == '\\') {
                 char style[2], c[2], sep[2], c_num[2] = "0", tmp[128] = {0};
                 unsigned int color = 0xFFFFFFFF;
-                int len, size = -1, an = -1, alpha = -1;
-                int x1, y1, x2, y2, t1 = -1, t2 = -1;
+                int len, size = -1, an = -1, alpha = -1, scale = 0;
+                int x1, y1, x2, y2, t1 = -1, t2 = -1, accel = 1;
                 if (sscanf(buf, "\\%1[bisu]%1[01\\}]%n", style, c, &len) > 1) {
                     int close = c[0] == '0' ? 1 : c[0] == '1' ? 0 : -1;
                     len += close != -1;
@@ -546,6 +546,14 @@ int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
                 } else if (sscanf(buf, "\\org(%d,%d)%1[\\}]%n", &x1, &y1, sep, &len) > 2) {
                     if (callbacks->origin)
                         callbacks->origin(priv, x1, y1);
+                } else if (sscanf(buf, "\\t(%d,%d,%1[\\}]%n", &t1, &t2, sep, &len) > 2 ||
+                           sscanf(buf, "\\t(%d,%d,%d,%1[\\}]%n", &t1, &t2, &accel, sep, &len) > 3) {
+                    if (callbacks->animate)
+                        callbacks->animate(priv, t1, t2, accel, tmp);
+                } else if (sscanf(buf, "\\p%1[\\}]%n", sep, &len) > 0 ||
+                           sscanf(buf, "\\p%u%1[\\}]%n", &scale, sep, &len) > 1) {
+                    if (callbacks->drawing_mode)
+                        callbacks->drawing_mode(priv, scale);
                 } else {
                     len = strcspn(buf+1, "\\}") + 2;  /* skip unknown code */
                 }
@@ -569,7 +577,7 @@ int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
     return 0;
 }
 
-ASSStyle *ff_ass_style_get(ASSSplitContext *ctx, const char *style)
+ASSStyle *avpriv_ass_style_get(ASSSplitContext *ctx, const char *style)
 {
     ASS *ass = &ctx->ass;
     int i;
diff --git a/libavcodec/ass_split.h b/libavutil/ass_split_internal.h
similarity index 86%
rename from libavcodec/ass_split.h
rename to libavutil/ass_split_internal.h
index a45fb9b8a1..eee49ef0f5 100644
--- a/libavcodec/ass_split.h
+++ b/libavutil/ass_split_internal.h
@@ -19,8 +19,8 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-#ifndef AVCODEC_ASS_SPLIT_H
-#define AVCODEC_ASS_SPLIT_H
+#ifndef AVUTIL_ASS_SPLIT_INTERNAL_H
+#define AVUTIL_ASS_SPLIT_INTERNAL_H
 
 /**
  * fields extracted from the [Script Info] section
@@ -81,7 +81,7 @@ typedef struct {
     char *effect;
     char *text;     /**< actual text which will be displayed as a subtitle,
                          can include style override control codes (see
-                         ff_ass_split_override_codes()) */
+                         avpriv_ass_split_override_codes()) */
 } ASSDialog;
 
 /**
@@ -107,12 +107,12 @@ typedef struct ASSSplitContext ASSSplitContext;
  * @param buf String containing the ASS formatted data.
  * @return Newly allocated struct containing split data.
  */
-ASSSplitContext *ff_ass_split(const char *buf);
+ASSSplitContext *avpriv_ass_split(const char *buf);
 
 /**
- * Free a dialogue obtained from ff_ass_split_dialog().
+ * Free a dialogue obtained from avpriv_ass_split_dialog().
  */
-void ff_ass_free_dialog(ASSDialog **dialogp);
+void avpriv_ass_free_dialog(ASSDialog **dialogp);
 
 /**
  * Split one ASS Dialogue line from a string buffer.
@@ -121,14 +121,14 @@ void ff_ass_free_dialog(ASSDialog **dialogp);
  * @param buf String containing the ASS "Dialogue" line.
  * @return Pointer to the split ASSDialog. Must be freed with ff_ass_free_dialog()
  */
-ASSDialog *ff_ass_split_dialog(ASSSplitContext *ctx, const char *buf);
+ASSDialog *avpriv_ass_split_dialog(ASSSplitContext *ctx, const char *buf);
 
 /**
  * Free all the memory allocated for an ASSSplitContext.
  *
  * @param ctx Context previously initialized by ff_ass_split().
  */
-void ff_ass_split_free(ASSSplitContext *ctx);
+void avpriv_ass_split_free(ASSSplitContext *ctx);
 
 
 /**
@@ -141,6 +141,7 @@ typedef struct {
      * @{
      */
     void (*text)(void *priv, const char *text, int len);
+    void (*hard_space)(void *priv);
     void (*new_line)(void *priv, int forced);
     void (*style)(void *priv, char style, int close);
     void (*color)(void *priv, unsigned int /* color */, unsigned int color_id);
@@ -156,7 +157,16 @@ typedef struct {
      * @{
      */
     void (*move)(void *priv, int x1, int y1, int x2, int y2, int t1, int t2);
+    void (*animate)(void *priv, int t1, int t2, int accel, char *style);
     void (*origin)(void *priv, int x, int y);
+    void (*drawing_mode)(void *priv, int scale);
+    /** @} */
+
+    /**
+     * @defgroup ass_ext    ASS extensible parsing callback
+     * @{
+     */
+    void (*ext)(void *priv, int ext_id, const char *text, int p1, int p2);
     /** @} */
 
     /**
@@ -176,7 +186,7 @@ typedef struct {
  * @param buf The ASS "Dialogue" Text field to split.
  * @return >= 0 on success otherwise an error code <0
  */
-int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
+int avpriv_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
                                 const char *buf);
 
 /**
@@ -186,6 +196,6 @@ int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
  * @param style name of the style to search for.
  * @return the ASSStyle corresponding to style, or NULL if style can't be found
  */
-ASSStyle *ff_ass_style_get(ASSSplitContext *ctx, const char *style);
+ASSStyle *avpriv_ass_style_get(ASSSplitContext *ctx, const char *style);
 
-#endif /* AVCODEC_ASS_SPLIT_H */
+#endif /* AVUTIL_ASS_SPLIT_INTERNAL_H */
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH 06/24] avcodec/subtitles: Migrate subtitle encoders to frame-based API
  2022-01-14  1:13 [FFmpeg-devel] [PATCH 00/24] Subtitle Filtering 2022 ffmpegagent
                   ` (4 preceding siblings ...)
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 05/24] avcodec, avutil: Move ass helper functions to avutil as avpriv_ and extend ass dialog parsing ffmpegagent
@ 2022-01-14  1:13 ` ffmpegagent
  2022-01-14 17:22   ` Michael Niedermayer
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 07/24] avcodec/subtitles: Replace deprecated enum values ffmpegagent
                   ` (18 subsequent siblings)
  24 siblings, 1 reply; 217+ messages in thread
From: ffmpegagent @ 2022-01-14  1:13 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: softworkz

From: softworkz <softworkz@hotmail.com>

and provide a compatibility shim for the legacy api

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/assenc.c        | 189 ++++++++++++++++++++++++++++++-------
 libavcodec/avcodec.h       |   5 +-
 libavcodec/dvbsubenc.c     |  96 ++++++++++---------
 libavcodec/dvdsubenc.c     | 102 ++++++++++++--------
 libavcodec/encode.c        |  57 ++++++++++-
 libavcodec/movtextenc.c    | 114 ++++++++++++++++------
 libavcodec/srtenc.c        | 108 ++++++++++++++-------
 libavcodec/tests/avcodec.c |   2 -
 libavcodec/ttmlenc.c       | 101 +++++++++++++++-----
 libavcodec/webvttenc.c     |  86 ++++++++++++-----
 libavcodec/xsubenc.c       |  88 ++++++++++-------
 11 files changed, 685 insertions(+), 263 deletions(-)

diff --git a/libavcodec/assenc.c b/libavcodec/assenc.c
index b0e475834b..e1401b1ac5 100644
--- a/libavcodec/assenc.c
+++ b/libavcodec/assenc.c
@@ -22,70 +22,195 @@
 #include <string.h>
 
 #include "avcodec.h"
+#include "encode.h"
 #include "libavutil/ass_internal.h"
 #include "internal.h"
 #include "libavutil/avstring.h"
 #include "libavutil/internal.h"
 #include "libavutil/mem.h"
 
+typedef struct {
+    AVCodecContext *avctx;
+    AVFrame* current_frame;
+    int have_frame;
+    int current_area;
+} AssEncContext;
+
+static void check_write_header(AVCodecContext* avctx, const AVFrame* frame)
+{
+    if (avctx->extradata_size)
+        return;
+
+    if (frame->subtitle_header && frame->subtitle_header->size > 0) {
+        const char* subtitle_header = (char*)frame->subtitle_header->data;
+        avctx->extradata_size = strlen(subtitle_header);
+        avctx->extradata = av_mallocz(frame->subtitle_header->size + 1);
+        memcpy(avctx->extradata, subtitle_header, avctx->extradata_size);
+        avctx->extradata[avctx->extradata_size] = 0;
+    }
+
+    if (!avctx->extradata_size) {
+        const char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+        if (!subtitle_header)
+            return;
+
+        avctx->extradata_size = strlen(subtitle_header);
+        avctx->extradata = av_mallocz(avctx->extradata_size + 1);
+        memcpy(avctx->extradata, subtitle_header, avctx->extradata_size);
+        avctx->extradata[avctx->extradata_size] = 0;
+        av_freep(&subtitle_header);
+    }
+}
+
 static av_cold int ass_encode_init(AVCodecContext *avctx)
 {
-    avctx->extradata = av_malloc(avctx->subtitle_header_size + 1);
-    if (!avctx->extradata)
-        return AVERROR(ENOMEM);
-    memcpy(avctx->extradata, avctx->subtitle_header, avctx->subtitle_header_size);
-    avctx->extradata_size = avctx->subtitle_header_size;
-    avctx->extradata[avctx->extradata_size] = 0;
+    AssEncContext *s = avctx->priv_data;
+
+    if (avctx->subtitle_header_size) {
+        avctx->extradata = av_malloc(avctx->subtitle_header_size + 1);
+        if (!avctx->extradata)
+            return AVERROR(ENOMEM);
+        memcpy(avctx->extradata, avctx->subtitle_header, avctx->subtitle_header_size);
+        avctx->extradata_size                   = avctx->subtitle_header_size;
+        avctx->extradata[avctx->extradata_size] = 0;
+    }
+
+    s->current_frame = av_frame_alloc();
+    return 0;
+}
+
+static av_cold int ass_encode_close(AVCodecContext *avctx)
+{
+    AssEncContext *s = avctx->priv_data;
+    av_frame_free(&s->current_frame);
     return 0;
 }
 
-static int ass_encode_frame(AVCodecContext *avctx,
-                            unsigned char *buf, int bufsize,
-                            const AVSubtitle *sub)
+////static int ass_encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+////                            const AVFrame* frame, int* got_packet)
+////{
+////    int ret;
+////    size_t req_len = 0, total_len = 0;
+////
+////    check_write_header(avctx, frame);
+////
+////    for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+////        const char *ass = frame->subtitle_areas[i]->ass;
+////
+////        if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) {
+////            av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
+////            return AVERROR(EINVAL);
+////        }
+////
+////        if (ass)
+////            req_len += strlen(ass);
+////    }
+////
+////    ret = ff_get_encode_buffer(avctx, avpkt, req_len + 1, 0);
+////    if (ret < 0) {
+////        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+////        return ret;
+////    }
+////
+////    for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+////        const char *ass = frame->subtitle_areas[i]->ass;
+////
+////        if (ass) {
+////            size_t len = av_strlcpy((char *)avpkt->data + total_len, ass, avpkt->size - total_len);
+////            total_len += len;
+////        }
+////    }
+////
+////    avpkt->size = total_len;
+////    *got_packet = total_len > 0;
+////
+////    return 0;
+////}
+
+static int ass_receive_packet(AVCodecContext *avctx, AVPacket *avpkt)
 {
-    int i, len, total_len = 0;
+    AssEncContext *s = avctx->priv_data;
+    int ret;
+
+    if (!s->have_frame) {
+        s->current_area = 0;
+        ret = ff_encode_get_frame(avctx, s->current_frame);
+
+        if (ret < 0) {
+            av_frame_unref(s->current_frame);
+            return ret;
+        }
+
+        s->have_frame = 1;
+    }
 
-    for (i=0; i<sub->num_rects; i++) {
-        const char *ass = sub->rects[i]->ass;
+    check_write_header(avctx, s->current_frame);
 
-        if (sub->rects[i]->type != SUBTITLE_ASS) {
-            av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
+    if (s->current_frame->repeat_sub) {
+        av_frame_unref(s->current_frame);
+        s->have_frame = 0;
+        return AVERROR(EAGAIN);
+    }
+
+    if (s->current_area < s->current_frame->num_subtitle_areas) {
+        const AVSubtitleArea *area = s->current_frame->subtitle_areas[s->current_area];
+        const char *ass = area->ass;
+
+        if (area->type != AV_SUBTITLE_FMT_ASS) {
+            av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
             return AVERROR(EINVAL);
         }
 
-        len = av_strlcpy(buf+total_len, ass, bufsize-total_len);
+        if (ass) {
+            size_t len = strlen(ass);
+
+            ret = ff_get_encode_buffer(avctx, avpkt, len + 1, 0);
+            if (ret < 0) {
+                av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+                return ret;
+            }
+
+            len = av_strlcpy((char *)avpkt->data, ass, avpkt->size);
 
-        if (len > bufsize-total_len-1) {
-            av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n");
-            return AVERROR_BUFFER_TOO_SMALL;
+            avpkt->size = len;
         }
 
-        total_len += len;
+        s->current_area++;
     }
 
-    return total_len;
+    if (s->current_area < s->current_frame->num_subtitle_areas)
+        return 0;
+
+    av_frame_unref(s->current_frame);
+    s->have_frame = 0;
+
+    return 0;
 }
 
 #if CONFIG_SSA_ENCODER
 const AVCodec ff_ssa_encoder = {
-    .name         = "ssa",
-    .long_name    = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"),
-    .type         = AVMEDIA_TYPE_SUBTITLE,
-    .id           = AV_CODEC_ID_ASS,
-    .init         = ass_encode_init,
-    .encode_sub   = ass_encode_frame,
+    .name           = "ssa",
+    .long_name      = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"),
+    .type           = AVMEDIA_TYPE_SUBTITLE,
+    .id             = AV_CODEC_ID_ASS,
+    .priv_data_size = sizeof(AssEncContext),
+    .init           = ass_encode_init,
+    .close          = ass_encode_close,
+    .receive_packet = ass_receive_packet,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
 };
 #endif
 
 #if CONFIG_ASS_ENCODER
 const AVCodec ff_ass_encoder = {
-    .name         = "ass",
-    .long_name    = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"),
-    .type         = AVMEDIA_TYPE_SUBTITLE,
-    .id           = AV_CODEC_ID_ASS,
-    .init         = ass_encode_init,
-    .encode_sub   = ass_encode_frame,
+    .name           = "ass",
+    .long_name      = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"),
+    .type           = AVMEDIA_TYPE_SUBTITLE,
+    .priv_data_size = sizeof(AssEncContext),
+    .id             = AV_CODEC_ID_ASS,
+    .init           = ass_encode_init,
+    .close          = ass_encode_close,
+    .receive_packet = ass_receive_packet,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
 };
 #endif
diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index 9d59f6e840..93063dc6e9 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -2995,10 +2995,13 @@ void av_parser_close(AVCodecParserContext *s);
  * @{
  */
 
+ /**
+  * @deprecated Use @ref avcodec_encode_subtitle2() instead.
+  */
+attribute_deprecated
 int avcodec_encode_subtitle(AVCodecContext *avctx, uint8_t *buf, int buf_size,
                             const AVSubtitle *sub);
 
-
 /**
  * @}
  */
diff --git a/libavcodec/dvbsubenc.c b/libavcodec/dvbsubenc.c
index 322fc27cb4..3b5a76daa7 100644
--- a/libavcodec/dvbsubenc.c
+++ b/libavcodec/dvbsubenc.c
@@ -20,6 +20,7 @@
  */
 #include "avcodec.h"
 #include "bytestream.h"
+#include "encode.h"
 #include "libavutil/colorspace.h"
 
 typedef struct DVBSubtitleContext {
@@ -268,21 +269,29 @@ static int dvb_encode_rle8(uint8_t **pq, int buf_size,
     return len;
 }
 
-static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
-                         const AVSubtitle *h)
+static int dvbsub_encode(AVCodecContext* avctx, AVPacket* avpkt,
+                         const AVFrame* frame, int* got_packet)
 {
     DVBSubtitleContext *s = avctx->priv_data;
     uint8_t *q, *pseg_len;
     int page_id, region_id, clut_id, object_id, i, bpp_index, page_state;
-
-
-    q = outbuf;
+    size_t buf_size;
+    int ret;
 
     page_id = 1;
 
-    if (h->num_rects && !h->rects)
+    if (frame->num_subtitle_areas && !frame->subtitle_areas)
         return AVERROR(EINVAL);
 
+    ret = ff_get_encode_buffer(avctx, avpkt, 1024 * 1024, 0);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+        return ret;
+    }
+
+    buf_size = avpkt->size;
+    q = avpkt->data;
+
     if (avctx->width > 0 && avctx->height > 0) {
         if (buf_size < 11)
             return AVERROR_BUFFER_TOO_SMALL;
@@ -301,7 +310,7 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
 
     /* page composition segment */
 
-    if (buf_size < 8 + h->num_rects * 6)
+    if (buf_size < 8 + frame->num_subtitle_areas * 6)
         return AVERROR_BUFFER_TOO_SMALL;
     *q++ = 0x0f; /* sync_byte */
     *q++ = 0x10; /* segment_type */
@@ -313,30 +322,30 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
     /* page_version = 0 + page_state */
     *q++ = (s->object_version << 4) | (page_state << 2) | 3;
 
-    for (region_id = 0; region_id < h->num_rects; region_id++) {
+    for (region_id = 0; region_id < frame->num_subtitle_areas; region_id++) {
         *q++ = region_id;
         *q++ = 0xff; /* reserved */
-        bytestream_put_be16(&q, h->rects[region_id]->x); /* left pos */
-        bytestream_put_be16(&q, h->rects[region_id]->y); /* top pos */
+        bytestream_put_be16(&q, frame->subtitle_areas[region_id]->x); /* left pos */
+        bytestream_put_be16(&q, frame->subtitle_areas[region_id]->y); /* top pos */
     }
 
     bytestream_put_be16(&pseg_len, q - pseg_len - 2);
-    buf_size -= 8 + h->num_rects * 6;
+    buf_size -= 8 + frame->num_subtitle_areas * 6;
 
-    if (h->num_rects) {
-        for (clut_id = 0; clut_id < h->num_rects; clut_id++) {
-            if (buf_size < 6 + h->rects[clut_id]->nb_colors * 6)
+    if (frame->num_subtitle_areas) {
+        for (clut_id = 0; clut_id < frame->num_subtitle_areas; clut_id++) {
+            if (buf_size < 6 + frame->subtitle_areas[clut_id]->nb_colors * 6)
                 return AVERROR_BUFFER_TOO_SMALL;
 
             /* CLUT segment */
 
-            if (h->rects[clut_id]->nb_colors <= 4) {
+            if (frame->subtitle_areas[clut_id]->nb_colors <= 4) {
                 /* 2 bpp, some decoders do not support it correctly */
                 bpp_index = 0;
-            } else if (h->rects[clut_id]->nb_colors <= 16) {
+            } else if (frame->subtitle_areas[clut_id]->nb_colors <= 16) {
                 /* 4 bpp, standard encoding */
                 bpp_index = 1;
-            } else if (h->rects[clut_id]->nb_colors <= 256) {
+            } else if (frame->subtitle_areas[clut_id]->nb_colors <= 256) {
                 /* 8 bpp, standard encoding */
                 bpp_index = 2;
             } else {
@@ -353,12 +362,12 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
             *q++ = clut_id;
             *q++ = (0 << 4) | 0xf; /* version = 0 */
 
-            for(i = 0; i < h->rects[clut_id]->nb_colors; i++) {
+            for(i = 0; i < frame->subtitle_areas[clut_id]->nb_colors; i++) {
                 *q++ = i; /* clut_entry_id */
                 *q++ = (1 << (7 - bpp_index)) | (0xf << 1) | 1; /* 2 bits/pixel full range */
                 {
                     int a, r, g, b;
-                    uint32_t x= ((uint32_t*)h->rects[clut_id]->data[1])[i];
+                    uint32_t x= ((uint32_t*)frame->subtitle_areas[clut_id]->pal)[i];
                     a = (x >> 24) & 0xff;
                     r = (x >> 16) & 0xff;
                     g = (x >>  8) & 0xff;
@@ -372,22 +381,22 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
             }
 
             bytestream_put_be16(&pseg_len, q - pseg_len - 2);
-            buf_size -= 6 + h->rects[clut_id]->nb_colors * 6;
+            buf_size -= 6 + frame->subtitle_areas[clut_id]->nb_colors * 6;
         }
 
-        if (buf_size < h->num_rects * 22)
+        if (buf_size < frame->num_subtitle_areas * 22)
             return AVERROR_BUFFER_TOO_SMALL;
-        for (region_id = 0; region_id < h->num_rects; region_id++) {
+        for (region_id = 0; region_id < frame->num_subtitle_areas; region_id++) {
 
             /* region composition segment */
 
-            if (h->rects[region_id]->nb_colors <= 4) {
+            if (frame->subtitle_areas[region_id]->nb_colors <= 4) {
                 /* 2 bpp, some decoders do not support it correctly */
                 bpp_index = 0;
-            } else if (h->rects[region_id]->nb_colors <= 16) {
+            } else if (frame->subtitle_areas[region_id]->nb_colors <= 16) {
                 /* 4 bpp, standard encoding */
                 bpp_index = 1;
-            } else if (h->rects[region_id]->nb_colors <= 256) {
+            } else if (frame->subtitle_areas[region_id]->nb_colors <= 256) {
                 /* 8 bpp, standard encoding */
                 bpp_index = 2;
             } else {
@@ -401,8 +410,8 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
             q += 2; /* segment length */
             *q++ = region_id;
             *q++ = (s->object_version << 4) | (0 << 3) | 0x07; /* version , no fill */
-            bytestream_put_be16(&q, h->rects[region_id]->w); /* region width */
-            bytestream_put_be16(&q, h->rects[region_id]->h); /* region height */
+            bytestream_put_be16(&q, frame->subtitle_areas[region_id]->w); /* region width */
+            bytestream_put_be16(&q, frame->subtitle_areas[region_id]->h); /* region height */
             *q++ = ((1 + bpp_index) << 5) | ((1 + bpp_index) << 2) | 0x03;
             *q++ = region_id; /* clut_id == region_id */
             *q++ = 0; /* 8 bit fill colors */
@@ -416,9 +425,9 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
 
             bytestream_put_be16(&pseg_len, q - pseg_len - 2);
         }
-        buf_size -= h->num_rects * 22;
+        buf_size -= frame->num_subtitle_areas * 22;
 
-        for (object_id = 0; object_id < h->num_rects; object_id++) {
+        for (object_id = 0; object_id < frame->num_subtitle_areas; object_id++) {
             int (*dvb_encode_rle)(uint8_t **pq, int buf_size,
                                   const uint8_t *bitmap, int linesize,
                                   int w, int h);
@@ -427,13 +436,13 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
                 return AVERROR_BUFFER_TOO_SMALL;
 
             /* bpp_index maths */
-            if (h->rects[object_id]->nb_colors <= 4) {
+            if (frame->subtitle_areas[object_id]->nb_colors <= 4) {
                 /* 2 bpp, some decoders do not support it correctly */
                 dvb_encode_rle = dvb_encode_rle2;
-            } else if (h->rects[object_id]->nb_colors <= 16) {
+            } else if (frame->subtitle_areas[object_id]->nb_colors <= 16) {
                 /* 4 bpp, standard encoding */
                 dvb_encode_rle = dvb_encode_rle4;
-            } else if (h->rects[object_id]->nb_colors <= 256) {
+            } else if (frame->subtitle_areas[object_id]->nb_colors <= 256) {
                 /* 8 bpp, standard encoding */
                 dvb_encode_rle = dvb_encode_rle8;
             } else {
@@ -463,19 +472,19 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
 
                 top_ptr = q;
                 ret = dvb_encode_rle(&q, buf_size,
-                                     h->rects[object_id]->data[0],
-                                     h->rects[object_id]->w * 2,
-                                     h->rects[object_id]->w,
-                                     h->rects[object_id]->h >> 1);
+                                     frame->subtitle_areas[object_id]->buf[0]->data,
+                                     frame->subtitle_areas[object_id]->w * 2,
+                                     frame->subtitle_areas[object_id]->w,
+                                     frame->subtitle_areas[object_id]->h >> 1);
                 if (ret < 0)
                     return ret;
                 buf_size -= ret;
                 bottom_ptr = q;
                 ret = dvb_encode_rle(&q, buf_size,
-                                     h->rects[object_id]->data[0] + h->rects[object_id]->w,
-                                     h->rects[object_id]->w * 2,
-                                     h->rects[object_id]->w,
-                                     h->rects[object_id]->h >> 1);
+                                     frame->subtitle_areas[object_id]->buf[0]->data + frame->subtitle_areas[object_id]->w,
+                                     frame->subtitle_areas[object_id]->w * 2,
+                                     frame->subtitle_areas[object_id]->w,
+                                     frame->subtitle_areas[object_id]->h >> 1);
                 if (ret < 0)
                     return ret;
                 buf_size -= ret;
@@ -502,7 +511,10 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
     buf_size -= 6;
 
     s->object_version = (s->object_version + 1) & 0xf;
-    return q - outbuf;
+    avpkt->size = q - avpkt->data;
+    *got_packet = 1;
+
+    return 0;
 }
 
 const AVCodec ff_dvbsub_encoder = {
@@ -511,5 +523,5 @@ const AVCodec ff_dvbsub_encoder = {
     .type           = AVMEDIA_TYPE_SUBTITLE,
     .id             = AV_CODEC_ID_DVB_SUBTITLE,
     .priv_data_size = sizeof(DVBSubtitleContext),
-    .encode_sub     = dvbsub_encode,
+    .encode2        = dvbsub_encode,
 };
diff --git a/libavcodec/dvdsubenc.c b/libavcodec/dvdsubenc.c
index ff4fbed39d..84f2a544f0 100644
--- a/libavcodec/dvdsubenc.c
+++ b/libavcodec/dvdsubenc.c
@@ -20,6 +20,7 @@
  */
 #include "avcodec.h"
 #include "bytestream.h"
+#include "encode.h"
 #include "internal.h"
 #include "libavutil/avassert.h"
 #include "libavutil/bprint.h"
@@ -114,15 +115,14 @@ static int color_distance(uint32_t a, uint32_t b)
  * Count colors used in a rectangle, quantizing alpha and grouping by
  * nearest global palette entry.
  */
-static void count_colors(AVCodecContext *avctx, unsigned hits[33],
-                         const AVSubtitleRect *r)
+static void count_colors(const AVCodecContext *avctx, unsigned hits[33],
+                         const AVSubtitleArea *r)
 {
     DVDSubtitleContext *dvdc = avctx->priv_data;
     unsigned count[256] = { 0 };
-    uint32_t *palette = (uint32_t *)r->data[1];
     uint32_t color;
     int x, y, i, j, match, d, best_d, av_uninit(best_j);
-    uint8_t *p = r->data[0];
+    uint8_t *p = r->buf[0]->data;
 
     for (y = 0; y < r->h; y++) {
         for (x = 0; x < r->w; x++)
@@ -132,7 +132,7 @@ static void count_colors(AVCodecContext *avctx, unsigned hits[33],
     for (i = 0; i < 256; i++) {
         if (!count[i]) /* avoid useless search */
             continue;
-        color = palette[i];
+        color = r->pal[i];
         /* 0: transparent, 1-16: semi-transparent, 17-33 opaque */
         match = color < 0x33000000 ? 0 : color < 0xCC000000 ? 1 : 17;
         if (match) {
@@ -232,13 +232,13 @@ static void build_color_map(AVCodecContext *avctx, int cmap[],
     }
 }
 
-static void copy_rectangle(AVSubtitleRect *dst, AVSubtitleRect *src, int cmap[])
+static void copy_rectangle(AVSubtitleArea*dst, AVSubtitleArea *src, int cmap[])
 {
     int x, y;
     uint8_t *p, *q;
 
-    p = src->data[0];
-    q = dst->data[0] + (src->x - dst->x) +
+    p = src->buf[0]->data;
+    q = dst->buf[0]->data + (src->x - dst->x) +
                             (src->y - dst->y) * dst->linesize[0];
     for (y = 0; y < src->h; y++) {
         for (x = 0; x < src->w; x++)
@@ -248,51 +248,57 @@ static void copy_rectangle(AVSubtitleRect *dst, AVSubtitleRect *src, int cmap[])
     }
 }
 
-static int encode_dvd_subtitles(AVCodecContext *avctx,
-                                uint8_t *outbuf, int outbuf_size,
-                                const AVSubtitle *h)
+static int encode_dvd_subtitles(AVCodecContext* avctx, AVPacket* avpkt,
+                                const AVFrame* frame, int* got_packet)
 {
     DVDSubtitleContext *dvdc = avctx->priv_data;
     uint8_t *q, *qq;
     int offset1, offset2;
-    int i, rects = h->num_rects, ret;
+    int ret = 0;
+    unsigned i, rects = frame->num_subtitle_areas;
     unsigned global_palette_hits[33] = { 0 };
     int cmap[256];
     int out_palette[4];
     int out_alpha[4];
-    AVSubtitleRect vrect;
-    uint8_t *vrect_data = NULL;
+    AVSubtitleArea vrect;
+    uint8_t* vrect_data = NULL, *outbuf;
     int x2, y2;
     int forced = 0;
+    int outbuf_size;
+    int64_t duration_ms;
 
-    if (rects == 0 || !h->rects)
+    if (rects == 0)
+        return 0;
+
+    if (!frame->subtitle_areas)
         return AVERROR(EINVAL);
+
     for (i = 0; i < rects; i++)
-        if (h->rects[i]->type != SUBTITLE_BITMAP) {
+        if (frame->subtitle_areas[i]->type != SUBTITLE_BITMAP) {
             av_log(avctx, AV_LOG_ERROR, "Bitmap subtitle required\n");
             return AVERROR(EINVAL);
         }
     /* Mark this subtitle forced if any of the rectangles is forced. */
     for (i = 0; i < rects; i++)
-        if ((h->rects[i]->flags & AV_SUBTITLE_FLAG_FORCED) != 0) {
+        if ((frame->subtitle_areas[i]->flags & AV_SUBTITLE_FLAG_FORCED) != 0) {
             forced = 1;
             break;
         }
 
-    vrect = *h->rects[0];
+    vrect = *frame->subtitle_areas[0];
 
     if (rects > 1) {
         /* DVD subtitles can have only one rectangle: build a virtual
            rectangle containing all actual rectangles.
            The data of the rectangles will be copied later, when the palette
            is decided, because the rectangles may have different palettes. */
-        int xmin = h->rects[0]->x, xmax = xmin + h->rects[0]->w;
-        int ymin = h->rects[0]->y, ymax = ymin + h->rects[0]->h;
+        int xmin = frame->subtitle_areas[0]->x, xmax = xmin + frame->subtitle_areas[0]->w;
+        int ymin = frame->subtitle_areas[0]->y, ymax = ymin + frame->subtitle_areas[0]->h;
         for (i = 1; i < rects; i++) {
-            xmin = FFMIN(xmin, h->rects[i]->x);
-            ymin = FFMIN(ymin, h->rects[i]->y);
-            xmax = FFMAX(xmax, h->rects[i]->x + h->rects[i]->w);
-            ymax = FFMAX(ymax, h->rects[i]->y + h->rects[i]->h);
+            xmin = FFMIN(xmin, frame->subtitle_areas[i]->x);
+            ymin = FFMIN(ymin, frame->subtitle_areas[i]->y);
+            xmax = FFMAX(xmax, frame->subtitle_areas[i]->x + frame->subtitle_areas[i]->w);
+            ymax = FFMAX(ymax, frame->subtitle_areas[i]->y + frame->subtitle_areas[i]->h);
         }
         vrect.x = xmin;
         vrect.y = ymin;
@@ -304,27 +310,29 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
         /* Count pixels outside the virtual rectangle as transparent */
         global_palette_hits[0] = vrect.w * vrect.h;
         for (i = 0; i < rects; i++)
-            global_palette_hits[0] -= h->rects[i]->w * h->rects[i]->h;
+            global_palette_hits[0] -= frame->subtitle_areas[i]->w * frame->subtitle_areas[i]->h;
     }
 
     for (i = 0; i < rects; i++)
-        count_colors(avctx, global_palette_hits, h->rects[i]);
+        count_colors(avctx, global_palette_hits, frame->subtitle_areas[i]);
     select_palette(avctx, out_palette, out_alpha, global_palette_hits);
 
     if (rects > 1) {
-        if (!(vrect_data = av_calloc(vrect.w, vrect.h)))
+
+        vrect.buf[0] = av_buffer_allocz((size_t)vrect.w * vrect.h);
+        if (!vrect.buf[0])
             return AVERROR(ENOMEM);
-        vrect.data    [0] = vrect_data;
+
         vrect.linesize[0] = vrect.w;
         for (i = 0; i < rects; i++) {
-            build_color_map(avctx, cmap, (uint32_t *)h->rects[i]->data[1],
+            build_color_map(avctx, cmap, frame->subtitle_areas[i]->pal,
                             out_palette, out_alpha);
-            copy_rectangle(&vrect, h->rects[i], cmap);
+            copy_rectangle(&vrect, frame->subtitle_areas[i], cmap);
         }
         for (i = 0; i < 4; i++)
             cmap[i] = i;
     } else {
-        build_color_map(avctx, cmap, (uint32_t *)h->rects[0]->data[1],
+        build_color_map(avctx, cmap, frame->subtitle_areas[0]->pal,
                         out_palette, out_alpha);
     }
 
@@ -335,6 +343,16 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
                out_palette[i], out_alpha[i] >> 4);
     av_log(avctx, AV_LOG_DEBUG, "\n");
 
+
+    ret = ff_get_encode_buffer(avctx, avpkt, 4 + vrect.w * vrect.h / 2 + 17 + 21, 0);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+        return ret;
+    }
+
+    outbuf_size = avpkt->size;
+    outbuf = avpkt->data;
+
     // encode data block
     q = outbuf + 4;
     offset1 = q - outbuf;
@@ -344,10 +362,10 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
         ret = AVERROR_BUFFER_TOO_SMALL;
         goto fail;
     }
-    dvd_encode_rle(&q, vrect.data[0], vrect.w * 2,
+    dvd_encode_rle(&q, vrect.buf[0]->data, vrect.w * 2,
                    vrect.w, (vrect.h + 1) >> 1, cmap);
     offset2 = q - outbuf;
-    dvd_encode_rle(&q, vrect.data[0] + vrect.w, vrect.w * 2,
+    dvd_encode_rle(&q, vrect.buf[0]->data + vrect.w, vrect.w * 2,
                    vrect.w, vrect.h >> 1, cmap);
 
     if (dvdc->even_rows_fix && (vrect.h & 1)) {
@@ -362,7 +380,7 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
     bytestream_put_be16(&qq, q - outbuf);
 
     // send start display command
-    bytestream_put_be16(&q, (h->start_display_time*90) >> 10);
+    bytestream_put_be16(&q, 0);
     bytestream_put_be16(&q, (q - outbuf) /*- 2 */ + 8 + 12 + 2);
     *q++ = 0x03; // palette - 4 nibbles
     *q++ = (out_palette[3] << 4) | out_palette[2];
@@ -394,7 +412,8 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
     *q++ = 0xff; // terminating command
 
     // send stop display command last
-    bytestream_put_be16(&q, (h->end_display_time*90) >> 10);
+    duration_ms = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, (AVRational){ 1, 1000 });
+    bytestream_put_be16(&q, (duration_ms*90) >> 10);
     bytestream_put_be16(&q, (q - outbuf) - 2 /*+ 4*/);
     *q++ = 0x02; // set end
     *q++ = 0xff; // terminating command
@@ -403,7 +422,9 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
     bytestream_put_be16(&qq, q - outbuf);
 
     av_log(NULL, AV_LOG_DEBUG, "subtitle_packet size=%"PTRDIFF_SPECIFIER"\n", q - outbuf);
-    ret = q - outbuf;
+    avpkt->size = q - outbuf;
+    ret = 0;
+    *got_packet = 1;
 
 fail:
     av_free(vrect_data);
@@ -467,14 +488,13 @@ static int dvdsub_init(AVCodecContext *avctx)
     return 0;
 }
 
-static int dvdsub_encode(AVCodecContext *avctx,
-                         unsigned char *buf, int buf_size,
-                         const AVSubtitle *sub)
+static int dvdsub_encode(struct AVCodecContext* avctx, struct AVPacket* avpkt,
+                         const struct AVFrame* frame, int* got_packet)
 {
     //DVDSubtitleContext *s = avctx->priv_data;
     int ret;
 
-    ret = encode_dvd_subtitles(avctx, buf, buf_size, sub);
+    ret = encode_dvd_subtitles(avctx, avpkt, frame, got_packet);
     return ret;
 }
 
@@ -499,7 +519,7 @@ const AVCodec ff_dvdsub_encoder = {
     .type           = AVMEDIA_TYPE_SUBTITLE,
     .id             = AV_CODEC_ID_DVD_SUBTITLE,
     .init           = dvdsub_init,
-    .encode_sub     = dvdsub_encode,
+    .encode2        = dvdsub_encode,
     .priv_class     = &dvdsubenc_class,
     .priv_data_size = sizeof(DVDSubtitleContext),
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
diff --git a/libavcodec/encode.c b/libavcodec/encode.c
index 618be0573d..982ee2de08 100644
--- a/libavcodec/encode.c
+++ b/libavcodec/encode.c
@@ -140,17 +140,70 @@ fail:
     return ret;
 }
 
+/**
+ * \brief
+ * \param avctx
+ * \param buf q
+ * \param buf_size
+ * \param sub
+ * \return
+ */
 int avcodec_encode_subtitle(AVCodecContext *avctx, uint8_t *buf, int buf_size,
                             const AVSubtitle *sub)
 {
-    int ret;
+    int ret = 0, got_packet = 0;
+    AVFrame *frame = NULL;
+    AVPacket* avpkt = NULL;
+
     if (sub->start_display_time) {
         av_log(avctx, AV_LOG_ERROR, "start_display_time must be 0.\n");
         return -1;
     }
 
-    ret = avctx->codec->encode_sub(avctx, buf, buf_size, sub);
+    memset(buf, 0, buf_size);
+    // Create a temporary frame for calling the regular api:
+    frame = av_frame_alloc();
+    if (!frame) {
+        ret = AVERROR(ENOMEM);
+        goto exit;
+    }
+
+    frame->format = sub->format;
+    frame->type = AVMEDIA_TYPE_SUBTITLE;
+    ret = av_frame_get_buffer2(frame, 0);
+    if (ret < 0)
+        goto exit;
+
+    // Create a temporary packet
+    avpkt = av_packet_alloc();
+    if (!avpkt) {
+        ret = AVERROR(ENOMEM);
+        goto exit;
+    }
+
+    // Copy legacy subtitle data to temp frame
+    ret = ff_frame_put_subtitle(frame, sub);
+    if (ret < 0)
+        goto exit;
+
+    ret = avctx->codec->encode2(avctx, avpkt, frame, &got_packet);
+
     avctx->frame_number++;
+
+    if (got_packet) {
+        if (avpkt->size > buf_size) {
+            ret = AVERROR_BUFFER_TOO_SMALL;
+            goto exit;
+        }
+
+        memcpy(buf, avpkt->data, avpkt->size);
+        ret = avpkt->size;
+    }
+
+exit:
+
+    av_packet_free(&avpkt);
+    av_frame_free(&frame);
     return ret;
 }
 
diff --git a/libavcodec/movtextenc.c b/libavcodec/movtextenc.c
index d506ed5c37..6383d694a8 100644
--- a/libavcodec/movtextenc.c
+++ b/libavcodec/movtextenc.c
@@ -29,6 +29,7 @@
 #include "libavutil/ass_split_internal.h"
 #include "libavutil/ass_internal.h"
 #include "bytestream.h"
+#include "encode.h"
 #include "internal.h"
 
 #define STYLE_FLAG_BOLD         (1<<0)
@@ -73,6 +74,7 @@ typedef struct {
     AVCodecContext *avctx;
 
     ASSSplitContext *ass_ctx;
+    int is_default_ass_context;
     ASSStyle *ass_dialog_style;
     StyleBox *style_attributes;
     unsigned  count;
@@ -329,7 +331,7 @@ static av_cold int mov_text_encode_init(AVCodecContext *avctx)
 
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
 
-    s->ass_ctx = avpriv_ass_split(avctx->subtitle_header);
+    s->ass_ctx = avpriv_ass_split((char*)avctx->subtitle_header);
     if (!s->ass_ctx)
         return AVERROR_INVALIDDATA;
     ret = encode_sample_description(avctx);
@@ -633,56 +635,112 @@ static const ASSCodesCallbacks mov_text_callbacks = {
     .end              = mov_text_end_cb,
 };
 
-static int mov_text_encode_frame(AVCodecContext *avctx, unsigned char *buf,
-                                 int bufsize, const AVSubtitle *sub)
+static void ensure_ass_context(AVCodecContext* avctx, const AVFrame* frame)
+{
+    MovTextContext* s = avctx->priv_data;
+    int ret;
+
+    if (s->ass_ctx && !s->is_default_ass_context)
+        // We already have a (non-default context)
+        return;
+
+    if (!frame->num_subtitle_areas)
+        // Don't need ass context for processing empty subtitle frames
+        return;
+
+    // The frame has content, so we need to set up a context
+    if (frame->subtitle_header && frame->subtitle_header->size > 0) {
+        const char* subtitle_header = (char*)frame->subtitle_header->data;
+        avpriv_ass_split_free(s->ass_ctx);
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 0;
+    }
+    else if (!s->ass_ctx) {
+        char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+        if (!subtitle_header)
+            return;
+
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 1;
+        av_free(subtitle_header);
+    }
+
+    if (s->ass_ctx && !avctx->extradata_size) {
+        ret = encode_sample_description(avctx);
+        if (ret < 0)
+            av_log(avctx, AV_LOG_ERROR, "Error during encode_sample_description().\n");
+    }
+}
+
+static int mov_text_encode_frame(AVCodecContext *avctx, AVPacket *avpkt,
+                                 const AVFrame *frame, int *got_packet)
 {
     MovTextContext *s = avctx->priv_data;
     ASSDialog *dialog;
-    int i, length;
+    int i, ret = 0;
+    size_t j;
+    uint8_t* buf;
+
+    ensure_ass_context(avctx, frame);
 
     s->text_pos = 0;
     s->count = 0;
     s->box_flags = 0;
     av_bprint_clear(&s->buffer);
-    for (i = 0; i < sub->num_rects; i++) {
-        const char *ass = sub->rects[i]->ass;
+    for (i = 0; i < frame->num_subtitle_areas; i++) {
+        const char *ass = frame->subtitle_areas[i]->ass;
 
-        if (sub->rects[i]->type != SUBTITLE_ASS) {
-            av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
+        if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) {
+            av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
             return AVERROR(EINVAL);
         }
 
-        dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
-        if (!dialog)
-            return AVERROR(ENOMEM);
-        mov_text_dialog(s, dialog);
-        avpriv_ass_split_override_codes(&mov_text_callbacks, s, dialog->text);
-        avpriv_ass_free_dialog(&dialog);
+        if (ass) {
+
+            if (i > 0)
+                mov_text_new_line_cb(s, 0);
+
+            dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
+            if (!dialog)
+                return AVERROR(ENOMEM);
+            mov_text_dialog(s, dialog);
+            avpriv_ass_split_override_codes(&mov_text_callbacks, s, dialog->text);
+            avpriv_ass_free_dialog(&dialog);
+
+        }
     }
 
-    if (s->buffer.len > UINT16_MAX)
-        return AVERROR(ERANGE);
-    AV_WB16(buf, s->buffer.len);
-    buf += 2;
+    if (!av_bprint_is_complete(&s->buffer)) {
+        return AVERROR(ENOMEM);
+    }
 
-    for (size_t j = 0; j < box_count; j++)
+    for (j = 0; j < box_count; j++) {
         box_types[j].encode(s);
+    }
 
-    if (!av_bprint_is_complete(&s->buffer))
-        return AVERROR(ENOMEM);
+    ret = ff_get_encode_buffer(avctx, avpkt, s->buffer.len + 3, 0);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+        return ret;
+    }
 
-    if (!s->buffer.len)
-        return 0;
+    buf = avpkt->data;
 
-    if (s->buffer.len > bufsize - 3) {
+    AV_WB16(buf, s->buffer.len);
+    buf += 2;
+
+    if (s->buffer.len > avpkt->size - 3) {
         av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n");
-        return AVERROR_BUFFER_TOO_SMALL;
+        ret = AVERROR_BUFFER_TOO_SMALL;
+        goto exit;
     }
 
     memcpy(buf, s->buffer.str, s->buffer.len);
-    length = s->buffer.len + 2;
+    avpkt->size = s->buffer.len + 2;
+    *got_packet = 1;
 
-    return length;
+exit:
+    return ret;
 }
 
 #define OFFSET(x) offsetof(MovTextContext, x)
@@ -707,7 +765,7 @@ const AVCodec ff_movtext_encoder = {
     .priv_data_size = sizeof(MovTextContext),
     .priv_class     = &mov_text_encoder_class,
     .init           = mov_text_encode_init,
-    .encode_sub     = mov_text_encode_frame,
+    .encode2        = mov_text_encode_frame,
     .close          = mov_text_encode_close,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP,
 };
diff --git a/libavcodec/srtenc.c b/libavcodec/srtenc.c
index a7c5fccefe..ebe42ef817 100644
--- a/libavcodec/srtenc.c
+++ b/libavcodec/srtenc.c
@@ -21,6 +21,7 @@
 
 #include <stdarg.h>
 #include "avcodec.h"
+#include "encode.h"
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
 #include "libavutil/ass_split_internal.h"
@@ -33,6 +34,7 @@
 typedef struct {
     AVCodecContext *avctx;
     ASSSplitContext *ass_ctx;
+    int is_default_ass_context;
     AVBPrint buffer;
     char stack[SRT_STACK_SIZE];
     int stack_ptr;
@@ -130,14 +132,13 @@ static void srt_style_apply(SRTContext *s, const char *style)
     }
 }
 
-
 static av_cold int srt_encode_init(AVCodecContext *avctx)
 {
     SRTContext *s = avctx->priv_data;
     s->avctx = avctx;
-    s->ass_ctx = avpriv_ass_split(avctx->subtitle_header);
+    s->ass_ctx = avpriv_ass_split((char *)avctx->subtitle_header);
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
-    return s->ass_ctx ? 0 : AVERROR_INVALIDDATA;
+    return 0;
 }
 
 static void srt_text_cb(void *priv, const char *text, int len)
@@ -227,58 +228,95 @@ static const ASSCodesCallbacks text_callbacks = {
     .new_line         = srt_new_line_cb,
 };
 
-static int encode_frame(AVCodecContext *avctx,
-                        unsigned char *buf, int bufsize, const AVSubtitle *sub,
-                        const ASSCodesCallbacks *cb)
+static void ensure_ass_context(SRTContext* s, const AVFrame* frame)
+{
+    if (s->ass_ctx && !s->is_default_ass_context)
+        // We already have a (non-default context)
+        return;
+
+    if (!frame->num_subtitle_areas)
+        // Don't need ass context for processing empty subtitle frames
+        return;
+
+    // The frame has content, so we need to set up a context
+    if (frame->subtitle_header && frame->subtitle_header->size > 0) {
+        const char* subtitle_header = (char*)frame->subtitle_header->data;
+        avpriv_ass_split_free(s->ass_ctx);
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 0;
+    }
+    else if (!s->ass_ctx) {
+        char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+        if (!subtitle_header)
+            return;
+
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 1;
+        av_free(subtitle_header);
+    }
+}
+
+static int encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+                        const AVFrame* frame, int* got_packet, const ASSCodesCallbacks* cb)
 {
     SRTContext *s = avctx->priv_data;
     ASSDialog *dialog;
-    int i;
+    int i, ret;
+
+    ensure_ass_context(s, frame);
 
     av_bprint_clear(&s->buffer);
 
-    for (i=0; i<sub->num_rects; i++) {
-        const char *ass = sub->rects[i]->ass;
+    for (i=0; i< frame->num_subtitle_areas; i++) {
+        const char *ass = frame->subtitle_areas[i]->ass;
 
-        if (sub->rects[i]->type != SUBTITLE_ASS) {
-            av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
+        if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) {
+            av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
             return AVERROR(EINVAL);
         }
 
-        dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
-        if (!dialog)
-            return AVERROR(ENOMEM);
-        s->alignment_applied = 0;
-        if (avctx->codec_id == AV_CODEC_ID_SUBRIP)
-            srt_style_apply(s, dialog->style);
-        avpriv_ass_split_override_codes(cb, s, dialog->text);
-        avpriv_ass_free_dialog(&dialog);
+        if (ass) {
+
+            if (i > 0)
+                srt_new_line_cb(s, 0);
+
+            dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
+            if (!dialog)
+                return AVERROR(ENOMEM);
+            s->alignment_applied = 0;
+            if (avctx->codec_id == AV_CODEC_ID_SUBRIP)
+                srt_style_apply(s, dialog->style);
+            avpriv_ass_split_override_codes(cb, s, dialog->text);
+            avpriv_ass_free_dialog(&dialog);
+        }
     }
 
     if (!av_bprint_is_complete(&s->buffer))
         return AVERROR(ENOMEM);
-    if (!s->buffer.len)
-        return 0;
 
-    if (s->buffer.len > bufsize) {
-        av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n");
-        return AVERROR_BUFFER_TOO_SMALL;
+    ret = ff_get_encode_buffer(avctx, avpkt, s->buffer.len + 1, 0);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+        return ret;
     }
-    memcpy(buf, s->buffer.str, s->buffer.len);
 
-    return s->buffer.len;
+    memcpy(avpkt->data, s->buffer.str, s->buffer.len);
+    avpkt->size = s->buffer.len;
+    *got_packet = 1;
+
+    return 0;
 }
 
-static int srt_encode_frame(AVCodecContext *avctx,
-                               unsigned char *buf, int bufsize, const AVSubtitle *sub)
+static int srt_encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+                            const AVFrame* frame, int* got_packet)
 {
-    return encode_frame(avctx, buf, bufsize, sub, &srt_callbacks);
+    return encode_frame(avctx, avpkt, frame, got_packet, &srt_callbacks);
 }
 
-static int text_encode_frame(AVCodecContext *avctx,
-                             unsigned char *buf, int bufsize, const AVSubtitle *sub)
+static int text_encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+                             const AVFrame* frame, int* got_packet)
 {
-    return encode_frame(avctx, buf, bufsize, sub, &text_callbacks);
+    return encode_frame(avctx, avpkt, frame, got_packet, &text_callbacks);
 }
 
 static int srt_encode_close(AVCodecContext *avctx)
@@ -298,7 +336,7 @@ const AVCodec ff_srt_encoder = {
     .id             = AV_CODEC_ID_SUBRIP,
     .priv_data_size = sizeof(SRTContext),
     .init           = srt_encode_init,
-    .encode_sub     = srt_encode_frame,
+    .encode2        = srt_encode_frame,
     .close          = srt_encode_close,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
 };
@@ -312,7 +350,7 @@ const AVCodec ff_subrip_encoder = {
     .id             = AV_CODEC_ID_SUBRIP,
     .priv_data_size = sizeof(SRTContext),
     .init           = srt_encode_init,
-    .encode_sub     = srt_encode_frame,
+    .encode2        = srt_encode_frame,
     .close          = srt_encode_close,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
 };
@@ -326,7 +364,7 @@ const AVCodec ff_text_encoder = {
     .id             = AV_CODEC_ID_TEXT,
     .priv_data_size = sizeof(SRTContext),
     .init           = srt_encode_init,
-    .encode_sub     = text_encode_frame,
+    .encode2        = text_encode_frame,
     .close          = srt_encode_close,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
 };
diff --git a/libavcodec/tests/avcodec.c b/libavcodec/tests/avcodec.c
index 5d0ff9432c..bd979b2184 100644
--- a/libavcodec/tests/avcodec.c
+++ b/libavcodec/tests/avcodec.c
@@ -107,8 +107,6 @@ int main(void){
             continue;
         }
         if (is_encoder) {
-            if (codec->type == AVMEDIA_TYPE_SUBTITLE ^ !!codec->encode_sub)
-                ERR("Encoder %s is both subtitle encoder and not subtitle encoder.");
             if (!!codec->encode_sub + !!codec->encode2 + !!codec->receive_packet != 1)
                 ERR("Encoder %s does not implement exactly one encode API.\n");
             if (codec->update_thread_context || codec->update_thread_context_for_user || codec->bsfs)
diff --git a/libavcodec/ttmlenc.c b/libavcodec/ttmlenc.c
index 083f2dd67a..e6d8a01a73 100644
--- a/libavcodec/ttmlenc.c
+++ b/libavcodec/ttmlenc.c
@@ -33,11 +33,17 @@
 #include "libavutil/bprint.h"
 #include "libavutil/internal.h"
 #include "libavutil/ass_split_internal.h"
+#include "libavutil/ass_internal.h"
 #include "ttmlenc.h"
 
+#include "encode.h"
+
+
 typedef struct {
     AVCodecContext *avctx;
     ASSSplitContext *ass_ctx;
+    int is_default_ass_context;
+    int extradata_written;
     AVBPrint buffer;
 } TTMLContext;
 
@@ -76,28 +82,75 @@ static const ASSCodesCallbacks ttml_callbacks = {
     .new_line         = ttml_new_line_cb,
 };
 
-static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf,
-                             int bufsize, const AVSubtitle *sub)
+static int ttml_write_header_content(AVCodecContext* avctx);
+
+static void ensure_ass_context(AVCodecContext* avctx, const AVFrame* frame)
+{
+    TTMLContext* s = avctx->priv_data;
+    int ret;
+
+    if (s->ass_ctx && !s->is_default_ass_context)
+        // We already have a (non-default context)
+        return;
+
+    if (!frame->num_subtitle_areas)
+        // Don't need ass context for processing empty subtitle frames
+        return;
+
+    // The frame has content, so we need to set up a context
+    if (frame->subtitle_header && frame->subtitle_header->size > 0) {
+        const char* subtitle_header = (char*)frame->subtitle_header->data;
+        avpriv_ass_split_free(s->ass_ctx);
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 0;
+    }
+    else if (!s->ass_ctx) {
+        char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+        if (!subtitle_header)
+            return;
+
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 1;
+        av_free(subtitle_header);
+    }
+
+    if (s->ass_ctx && !s->extradata_written) {
+        s->extradata_written = 1;
+        if ((ret = ttml_write_header_content(avctx)) < 0) {
+            av_log(avctx, AV_LOG_ERROR, "Error writing header content.\n");
+        }
+    }
+}
+
+static int ttml_encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+                             const AVFrame* frame, int* got_packet)
 {
     TTMLContext *s = avctx->priv_data;
     ASSDialog *dialog;
-    int i;
+    int i, ret;
+
+    ensure_ass_context(avctx, frame);
 
     av_bprint_clear(&s->buffer);
 
-    for (i=0; i<sub->num_rects; i++) {
-        const char *ass = sub->rects[i]->ass;
-        int ret;
+    for (i=0; i< frame->num_subtitle_areas; i++) {
+        const char *ass = frame->subtitle_areas[i]->ass;
 
-        if (sub->rects[i]->type != SUBTITLE_ASS) {
-            av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
+        if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) {
+            av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
             return AVERROR(EINVAL);
         }
 
+        if (!ass)
+            continue;
+
         dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
         if (!dialog)
             return AVERROR(ENOMEM);
 
+        if (i > 0)
+            ttml_new_line_cb(s, 0);
+
         if (dialog->style) {
             av_bprintf(&s->buffer, "<span region=\"");
             av_bprint_escape(&s->buffer, dialog->style, NULL,
@@ -130,17 +183,19 @@ static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf,
 
     if (!av_bprint_is_complete(&s->buffer))
         return AVERROR(ENOMEM);
-    if (!s->buffer.len)
-        return 0;
-
-    // force null-termination, so in case our destination buffer is
-    // too small, the return value is larger than bufsize minus null.
-    if (av_strlcpy(buf, s->buffer.str, bufsize) > bufsize - 1) {
-        av_log(avctx, AV_LOG_ERROR, "Buffer too small for TTML event.\n");
-        return AVERROR_BUFFER_TOO_SMALL;
+
+    ret = ff_get_encode_buffer(avctx, avpkt, s->buffer.len + 3, 0);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+        return ret;
     }
 
-    return s->buffer.len;
+    av_strlcpy(avpkt->data, s->buffer.str, avpkt->size);
+
+    avpkt->size = s->buffer.len;
+    *got_packet = 1;
+
+    return 0;
 }
 
 static av_cold int ttml_encode_close(AVCodecContext *avctx)
@@ -370,13 +425,13 @@ static av_cold int ttml_encode_init(AVCodecContext *avctx)
     s->avctx   = avctx;
 
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
+    s->ass_ctx = avpriv_ass_split((char*)avctx->subtitle_header);
 
-    if (!(s->ass_ctx = avpriv_ass_split(avctx->subtitle_header))) {
-        return AVERROR_INVALIDDATA;
-    }
+    if (s->ass_ctx) {
+        if (ret = ttml_write_header_content(avctx) < 0)
+            return ret;
 
-    if ((ret = ttml_write_header_content(avctx)) < 0) {
-        return ret;
+        s->extradata_written = 1;
     }
 
     return 0;
@@ -389,7 +444,7 @@ const AVCodec ff_ttml_encoder = {
     .id             = AV_CODEC_ID_TTML,
     .priv_data_size = sizeof(TTMLContext),
     .init           = ttml_encode_init,
-    .encode_sub     = ttml_encode_frame,
+    .encode2        = ttml_encode_frame,
     .close          = ttml_encode_close,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP,
 };
diff --git a/libavcodec/webvttenc.c b/libavcodec/webvttenc.c
index 761099b69a..c0436f5739 100644
--- a/libavcodec/webvttenc.c
+++ b/libavcodec/webvttenc.c
@@ -22,6 +22,7 @@
 
 #include <stdarg.h>
 #include "avcodec.h"
+#include "encode.h"
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
 #include "libavutil/ass_split_internal.h"
@@ -32,6 +33,7 @@
 typedef struct {
     AVCodecContext *avctx;
     ASSSplitContext *ass_ctx;
+    int is_default_ass_context;
     AVBPrint buffer;
     unsigned timestamp_end;
     int count;
@@ -155,43 +157,81 @@ static const ASSCodesCallbacks webvtt_callbacks = {
     .end              = webvtt_end_cb,
 };
 
-static int webvtt_encode_frame(AVCodecContext *avctx,
-                               unsigned char *buf, int bufsize, const AVSubtitle *sub)
+static void ensure_ass_context(WebVTTContext* s, const AVFrame* frame)
+{
+    if (s->ass_ctx && !s->is_default_ass_context)
+        // We already have a (non-default context)
+        return;
+
+    if (!frame->num_subtitle_areas)
+        // Don't need ass context for processing empty subtitle frames
+        return;
+
+    // The frame has content, so we need to set up a context
+    if (frame->subtitle_header && frame->subtitle_header->size > 0) {
+        avpriv_ass_split_free(s->ass_ctx);
+        const char* subtitle_header = (char*)frame->subtitle_header->data;
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 0;
+    }
+    else if (!s->ass_ctx) {
+        char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+        if (!subtitle_header)
+            return;
+
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 1;
+        av_free(subtitle_header);
+    }
+}
+
+static int webvtt_encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+                               const AVFrame* frame, int* got_packet)
 {
     WebVTTContext *s = avctx->priv_data;
     ASSDialog *dialog;
-    int i;
+    int ret, i;
+
+    ensure_ass_context(s, frame);
 
     av_bprint_clear(&s->buffer);
 
-    for (i=0; i<sub->num_rects; i++) {
-        const char *ass = sub->rects[i]->ass;
+    for (i=0; i< frame->num_subtitle_areas; i++) {
+        const char *ass = frame->subtitle_areas[i]->ass;
 
-        if (sub->rects[i]->type != SUBTITLE_ASS) {
-            av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
+        if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) {
+            av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
             return AVERROR(EINVAL);
         }
 
-        dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
-        if (!dialog)
-            return AVERROR(ENOMEM);
-        webvtt_style_apply(s, dialog->style);
-        avpriv_ass_split_override_codes(&webvtt_callbacks, s, dialog->text);
-        avpriv_ass_free_dialog(&dialog);
+        if (ass) {
+
+            if (i > 0)
+                webvtt_new_line_cb(s, 0);
+
+            dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
+            if (!dialog)
+                return AVERROR(ENOMEM);
+            webvtt_style_apply(s, dialog->style);
+            avpriv_ass_split_override_codes(&webvtt_callbacks, s, dialog->text);
+            avpriv_ass_free_dialog(&dialog);
+        }
     }
 
     if (!av_bprint_is_complete(&s->buffer))
         return AVERROR(ENOMEM);
-    if (!s->buffer.len)
-        return 0;
 
-    if (s->buffer.len > bufsize) {
-        av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n");
-        return AVERROR_BUFFER_TOO_SMALL;
+    ret = ff_get_encode_buffer(avctx, avpkt, s->buffer.len + 1, 0);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+        return ret;
     }
-    memcpy(buf, s->buffer.str, s->buffer.len);
 
-    return s->buffer.len;
+    memcpy(avpkt->data, s->buffer.str, s->buffer.len);
+    avpkt->size = s->buffer.len;
+    *got_packet = s->buffer.len > 0;
+
+    return 0;
 }
 
 static int webvtt_encode_close(AVCodecContext *avctx)
@@ -206,9 +246,9 @@ static av_cold int webvtt_encode_init(AVCodecContext *avctx)
 {
     WebVTTContext *s = avctx->priv_data;
     s->avctx = avctx;
-    s->ass_ctx = avpriv_ass_split(avctx->subtitle_header);
+    s->ass_ctx = avpriv_ass_split((char*)avctx->subtitle_header);
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
-    return s->ass_ctx ? 0 : AVERROR_INVALIDDATA;
+    return 0;
 }
 
 const AVCodec ff_webvtt_encoder = {
@@ -218,7 +258,7 @@ const AVCodec ff_webvtt_encoder = {
     .id             = AV_CODEC_ID_WEBVTT,
     .priv_data_size = sizeof(WebVTTContext),
     .init           = webvtt_encode_init,
-    .encode_sub     = webvtt_encode_frame,
+    .encode2        = webvtt_encode_frame,
     .close          = webvtt_encode_close,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
 };
diff --git a/libavcodec/xsubenc.c b/libavcodec/xsubenc.c
index 03d0dc2d86..ef804f21f4 100644
--- a/libavcodec/xsubenc.c
+++ b/libavcodec/xsubenc.c
@@ -22,6 +22,7 @@
 
 #include "avcodec.h"
 #include "bytestream.h"
+#include "encode.h"
 #include "internal.h"
 #include "put_bits.h"
 
@@ -111,39 +112,56 @@ static int make_tc(uint64_t ms, int *tc)
     return ms > 99;
 }
 
-static int xsub_encode(AVCodecContext *avctx, unsigned char *buf,
-                       int bufsize, const AVSubtitle *h)
+static int xsub_encode(AVCodecContext* avctx, AVPacket* avpkt,
+                       const AVFrame* frame, int* got_packet)
 {
-    uint64_t startTime = h->pts / 1000; // FIXME: need better solution...
-    uint64_t endTime = startTime + h->end_display_time - h->start_display_time;
+    const int64_t duration_ms = (int64_t)((double)frame->subtitle_timing.duration * av_q2d(AV_TIME_BASE_Q) * 1000);
+    const uint64_t startTime = (int64_t)((double)frame->subtitle_timing.start_pts * av_q2d(AV_TIME_BASE_Q) * 1000);
+    const uint64_t endTime   = startTime + duration_ms;
     int start_tc[4], end_tc[4];
-    uint8_t *hdr = buf + 27; // Point behind the timestamp
+    uint8_t *hdr;
     uint8_t *rlelenptr;
     uint16_t width, height;
-    int i;
+    int i, ret;
     PutBitContext pb;
+    uint8_t* buf;
+    int64_t req_size;
 
-    if (bufsize < 27 + 7*2 + 4*3) {
-        av_log(avctx, AV_LOG_ERROR, "Buffer too small for XSUB header.\n");
-        return AVERROR_BUFFER_TOO_SMALL;
+    if (!frame->num_subtitle_areas) {
+        // Don't encode empty sub events
+        return 0;
     }
 
+    // Estimate size (timestamp 27, header 7*2 + 4*3, padding 10)
+    req_size = 27 + 7*2 + 4*3 + 10;
+    req_size += frame->subtitle_areas[0]->linesize[0] * frame->subtitle_areas[0]->h;
+    req_size += 256; // Palette
+
+    ret = ff_get_encode_buffer(avctx, avpkt, req_size, 0);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+        return ret;
+    }
+
+    buf = avpkt->data;
+    hdr = avpkt->data + 27; // Point behind the timestamp
+
     // TODO: support multiple rects
-    if (h->num_rects != 1)
-        av_log(avctx, AV_LOG_WARNING, "Only single rects supported (%d in subtitle.)\n", h->num_rects);
+    if (frame->num_subtitle_areas != 1)
+        av_log(avctx, AV_LOG_WARNING, "Only single rects supported (%d in subtitle.)\n", frame->num_subtitle_areas);
 
     // TODO: render text-based subtitles into bitmaps
-    if (!h->rects[0]->data[0] || !h->rects[0]->data[1]) {
+    if (!frame->subtitle_areas[0]->buf[0]->data || !frame->subtitle_areas[0]->pal) {
         av_log(avctx, AV_LOG_WARNING, "No subtitle bitmap available.\n");
         return AVERROR(EINVAL);
     }
 
     // TODO: color reduction, similar to dvdsub encoder
-    if (h->rects[0]->nb_colors > 4)
-        av_log(avctx, AV_LOG_WARNING, "No more than 4 subtitle colors supported (%d found.)\n", h->rects[0]->nb_colors);
+    if (frame->subtitle_areas[0]->nb_colors > 4)
+        av_log(avctx, AV_LOG_WARNING, "No more than 4 subtitle colors supported (%d found.)\n", frame->subtitle_areas[0]->nb_colors);
 
     // TODO: Palette swapping if color zero is not transparent
-    if (((uint32_t *)h->rects[0]->data[1])[0] & 0xff000000)
+    if (((uint32_t *)frame->subtitle_areas[0]->pal)[0] & 0xff000000)
         av_log(avctx, AV_LOG_WARNING, "Color index 0 is not transparent. Transparency will be messed up.\n");
 
     if (make_tc(startTime, start_tc) || make_tc(endTime, end_tc)) {
@@ -151,7 +169,7 @@ static int xsub_encode(AVCodecContext *avctx, unsigned char *buf,
         return AVERROR(EINVAL);
     }
 
-    snprintf(buf, 28,
+    snprintf((char *)avpkt->data, 28,
         "[%02d:%02d:%02d.%03d-%02d:%02d:%02d.%03d]",
         start_tc[3], start_tc[2], start_tc[1], start_tc[0],
         end_tc[3],   end_tc[2],   end_tc[1],   end_tc[0]);
@@ -160,45 +178,47 @@ static int xsub_encode(AVCodecContext *avctx, unsigned char *buf,
     // 2 pixels required on either side of subtitle.
     // Possibly due to limitations of hardware renderers.
     // TODO: check if the bitmap is already padded
-    width  = FFALIGN(h->rects[0]->w, 2) + PADDING * 2;
-    height = FFALIGN(h->rects[0]->h, 2);
+    width  = FFALIGN(frame->subtitle_areas[0]->w, 2) + PADDING * 2;
+    height = FFALIGN(frame->subtitle_areas[0]->h, 2);
 
     bytestream_put_le16(&hdr, width);
     bytestream_put_le16(&hdr, height);
-    bytestream_put_le16(&hdr, h->rects[0]->x);
-    bytestream_put_le16(&hdr, h->rects[0]->y);
-    bytestream_put_le16(&hdr, h->rects[0]->x + width -1);
-    bytestream_put_le16(&hdr, h->rects[0]->y + height -1);
+    bytestream_put_le16(&hdr, frame->subtitle_areas[0]->x);
+    bytestream_put_le16(&hdr, frame->subtitle_areas[0]->y);
+    bytestream_put_le16(&hdr, frame->subtitle_areas[0]->x + width -1);
+    bytestream_put_le16(&hdr, frame->subtitle_areas[0]->y + height -1);
 
     rlelenptr = hdr; // Will store length of first field here later.
     hdr+=2;
 
     // Palette
     for (i=0; i<4; i++)
-        bytestream_put_be24(&hdr, ((uint32_t *)h->rects[0]->data[1])[i]);
+        bytestream_put_be24(&hdr, ((uint32_t *)frame->subtitle_areas[0]->pal)[i]);
 
     // Bitmap
     // RLE buffer. Reserve 2 bytes for possible padding after the last row.
-    init_put_bits(&pb, hdr, bufsize - (hdr - buf) - 2);
-    if (xsub_encode_rle(&pb, h->rects[0]->data[0],
-                        h->rects[0]->linesize[0] * 2,
-                        h->rects[0]->w, (h->rects[0]->h + 1) >> 1))
+    init_put_bits(&pb, hdr, avpkt->size - (hdr - buf) - 2);
+    if (xsub_encode_rle(&pb, frame->subtitle_areas[0]->buf[0]->data,
+                        frame->subtitle_areas[0]->linesize[0] * 2,
+                        frame->subtitle_areas[0]->w, (frame->subtitle_areas[0]->h + 1) >> 1))
         return AVERROR_BUFFER_TOO_SMALL;
     bytestream_put_le16(&rlelenptr, put_bytes_count(&pb, 0)); // Length of first field
 
-    if (xsub_encode_rle(&pb, h->rects[0]->data[0] + h->rects[0]->linesize[0],
-                        h->rects[0]->linesize[0] * 2,
-                        h->rects[0]->w, h->rects[0]->h >> 1))
+    if (xsub_encode_rle(&pb, frame->subtitle_areas[0]->buf[0]->data + frame->subtitle_areas[0]->linesize[0],
+                        frame->subtitle_areas[0]->linesize[0] * 2,
+                        frame->subtitle_areas[0]->w, frame->subtitle_areas[0]->h >> 1))
         return AVERROR_BUFFER_TOO_SMALL;
 
     // Enforce total height to be a multiple of 2
-    if (h->rects[0]->h & 1) {
-        put_xsub_rle(&pb, h->rects[0]->w, PADDING_COLOR);
+    if (frame->subtitle_areas[0]->h & 1) {
+        put_xsub_rle(&pb, frame->subtitle_areas[0]->w, PADDING_COLOR);
     }
 
     flush_put_bits(&pb);
 
-    return hdr - buf + put_bytes_output(&pb);
+    avpkt->size = hdr - buf + put_bytes_output(&pb);
+    *got_packet = 1;
+    return 0;
 }
 
 static av_cold int xsub_encoder_init(AVCodecContext *avctx)
@@ -217,6 +237,6 @@ const AVCodec ff_xsub_encoder = {
     .type       = AVMEDIA_TYPE_SUBTITLE,
     .id         = AV_CODEC_ID_XSUB,
     .init       = xsub_encoder_init,
-    .encode_sub = xsub_encode,
+    .encode2    = xsub_encode,
     .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE,
 };
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH 07/24] avcodec/subtitles: Replace deprecated enum values
  2022-01-14  1:13 [FFmpeg-devel] [PATCH 00/24] Subtitle Filtering 2022 ffmpegagent
                   ` (5 preceding siblings ...)
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 06/24] avcodec/subtitles: Migrate subtitle encoders to frame-based API ffmpegagent
@ 2022-01-14  1:13 ` ffmpegagent
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 08/24] fftools/play, probe: Adjust for subtitle changes ffmpegagent
                   ` (17 subsequent siblings)
  24 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-14  1:13 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: softworkz

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/ass.h       | 2 +-
 libavcodec/assdec.c    | 2 +-
 libavcodec/dvbsubdec.c | 2 +-
 libavcodec/dvdsubdec.c | 2 +-
 libavcodec/dvdsubenc.c | 2 +-
 libavcodec/pgssubdec.c | 2 +-
 libavcodec/xsubdec.c   | 2 +-
 7 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/libavcodec/ass.h b/libavcodec/ass.h
index 8bc13d7ab8..43c5ad651a 100644
--- a/libavcodec/ass.h
+++ b/libavcodec/ass.h
@@ -83,7 +83,7 @@ static inline int avpriv_ass_add_rect(AVSubtitle *sub, const char *dialog,
     rects[sub->num_rects]       = av_mallocz(sizeof(*rects[0]));
     if (!rects[sub->num_rects])
         return AVERROR(ENOMEM);
-    rects[sub->num_rects]->type = SUBTITLE_ASS;
+    rects[sub->num_rects]->type = AV_SUBTITLE_FMT_ASS;
     ass_str = avpriv_ass_get_dialog(readorder, layer, style, speaker, dialog);
     if (!ass_str)
         return AVERROR(ENOMEM);
diff --git a/libavcodec/assdec.c b/libavcodec/assdec.c
index 7802a44e71..fd321e7004 100644
--- a/libavcodec/assdec.c
+++ b/libavcodec/assdec.c
@@ -54,7 +54,7 @@ static int ass_decode_frame(AVCodecContext *avctx, void *data, int *got_sub_ptr,
     if (!sub->rects[0])
         return AVERROR(ENOMEM);
     sub->num_rects = 1;
-    sub->rects[0]->type = SUBTITLE_ASS;
+    sub->rects[0]->type = AV_SUBTITLE_FMT_ASS;
     sub->rects[0]->ass  = av_strdup(avpkt->data);
     if (!sub->rects[0]->ass)
         return AVERROR(ENOMEM);
diff --git a/libavcodec/dvbsubdec.c b/libavcodec/dvbsubdec.c
index bc741a1de6..0d64c6e71c 100644
--- a/libavcodec/dvbsubdec.c
+++ b/libavcodec/dvbsubdec.c
@@ -795,7 +795,7 @@ static int save_subtitle_set(AVCodecContext *avctx, AVSubtitle *sub, int *got_ou
             rect->w = region->width;
             rect->h = region->height;
             rect->nb_colors = (1 << region->depth);
-            rect->type      = SUBTITLE_BITMAP;
+            rect->type      = AV_SUBTITLE_FMT_BITMAP;
             rect->linesize[0] = region->width;
 
             clut = get_clut(ctx, region->clut);
diff --git a/libavcodec/dvdsubdec.c b/libavcodec/dvdsubdec.c
index 52259f0730..b39b3d1838 100644
--- a/libavcodec/dvdsubdec.c
+++ b/libavcodec/dvdsubdec.c
@@ -406,7 +406,7 @@ static int decode_dvd_subtitles(DVDSubContext *ctx, AVSubtitle *sub_header,
                 sub_header->rects[0]->y = y1;
                 sub_header->rects[0]->w = w;
                 sub_header->rects[0]->h = h;
-                sub_header->rects[0]->type = SUBTITLE_BITMAP;
+                sub_header->rects[0]->type = AV_SUBTITLE_FMT_BITMAP;
                 sub_header->rects[0]->linesize[0] = w;
                 sub_header->rects[0]->flags = is_menu ? AV_SUBTITLE_FLAG_FORCED : 0;
             }
diff --git a/libavcodec/dvdsubenc.c b/libavcodec/dvdsubenc.c
index 84f2a544f0..ff21e75ef3 100644
--- a/libavcodec/dvdsubenc.c
+++ b/libavcodec/dvdsubenc.c
@@ -274,7 +274,7 @@ static int encode_dvd_subtitles(AVCodecContext* avctx, AVPacket* avpkt,
         return AVERROR(EINVAL);
 
     for (i = 0; i < rects; i++)
-        if (frame->subtitle_areas[i]->type != SUBTITLE_BITMAP) {
+        if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_BITMAP) {
             av_log(avctx, AV_LOG_ERROR, "Bitmap subtitle required\n");
             return AVERROR(EINVAL);
         }
diff --git a/libavcodec/pgssubdec.c b/libavcodec/pgssubdec.c
index bdd20c914b..22b6616f9b 100644
--- a/libavcodec/pgssubdec.c
+++ b/libavcodec/pgssubdec.c
@@ -535,7 +535,7 @@ static int display_end_segment(AVCodecContext *avctx, void *data,
         if (!rect)
             return AVERROR(ENOMEM);
         sub->rects[sub->num_rects++] = rect;
-        rect->type = SUBTITLE_BITMAP;
+        rect->type = AV_SUBTITLE_FMT_BITMAP;
 
         /* Process bitmap */
         object = find_object(ctx->presentation.objects[i].id, &ctx->objects);
diff --git a/libavcodec/xsubdec.c b/libavcodec/xsubdec.c
index 85cd7d1c20..a4be18a1d8 100644
--- a/libavcodec/xsubdec.c
+++ b/libavcodec/xsubdec.c
@@ -107,7 +107,7 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_sub_ptr,
     sub->num_rects = 1;
     rect->x = x; rect->y = y;
     rect->w = w; rect->h = h;
-    rect->type = SUBTITLE_BITMAP;
+    rect->type = AV_SUBTITLE_FMT_BITMAP;
     rect->linesize[0] = w;
     rect->data[0] = av_malloc(w * h);
     rect->nb_colors = 4;
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH 08/24] fftools/play, probe: Adjust for subtitle changes
  2022-01-14  1:13 [FFmpeg-devel] [PATCH 00/24] Subtitle Filtering 2022 ffmpegagent
                   ` (6 preceding siblings ...)
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 07/24] avcodec/subtitles: Replace deprecated enum values ffmpegagent
@ 2022-01-14  1:13 ` ffmpegagent
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 09/24] avfilter/subtitles: Add subtitles.c for subtitle frame allocation ffmpegagent
                   ` (16 subsequent siblings)
  24 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-14  1:13 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: softworkz

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 fftools/ffplay.c  | 102 +++++++++++++++++++++-------------------------
 fftools/ffprobe.c |  47 +++++++++++++--------
 2 files changed, 77 insertions(+), 72 deletions(-)

diff --git a/fftools/ffplay.c b/fftools/ffplay.c
index e7b20be76b..94286eb678 100644
--- a/fftools/ffplay.c
+++ b/fftools/ffplay.c
@@ -152,7 +152,6 @@ typedef struct Clock {
 /* Common struct for handling all types of decoded data and allocated render buffers. */
 typedef struct Frame {
     AVFrame *frame;
-    AVSubtitle sub;
     int serial;
     double pts;           /* presentation timestamp for the frame */
     double duration;      /* estimated duration of the frame */
@@ -586,7 +585,7 @@ static int decoder_init(Decoder *d, AVCodecContext *avctx, PacketQueue *queue, S
     return 0;
 }
 
-static int decoder_decode_frame(Decoder *d, AVFrame *frame, AVSubtitle *sub) {
+static int decoder_decode_frame(Decoder *d, AVFrame *frame) {
     int ret = AVERROR(EAGAIN);
 
     for (;;) {
@@ -620,6 +619,9 @@ static int decoder_decode_frame(Decoder *d, AVFrame *frame, AVSubtitle *sub) {
                             }
                         }
                         break;
+                    case AVMEDIA_TYPE_SUBTITLE:
+                        ret = avcodec_receive_frame(d->avctx, frame);
+                        break;
                 }
                 if (ret == AVERROR_EOF) {
                     d->finished = d->pkt_serial;
@@ -652,25 +654,11 @@ static int decoder_decode_frame(Decoder *d, AVFrame *frame, AVSubtitle *sub) {
             av_packet_unref(d->pkt);
         } while (1);
 
-        if (d->avctx->codec_type == AVMEDIA_TYPE_SUBTITLE) {
-            int got_frame = 0;
-            ret = avcodec_decode_subtitle2(d->avctx, sub, &got_frame, d->pkt);
-            if (ret < 0) {
-                ret = AVERROR(EAGAIN);
-            } else {
-                if (got_frame && !d->pkt->data) {
-                    d->packet_pending = 1;
-                }
-                ret = got_frame ? 0 : (d->pkt->data ? AVERROR(EAGAIN) : AVERROR_EOF);
-            }
-            av_packet_unref(d->pkt);
+        if (avcodec_send_packet(d->avctx, d->pkt) == AVERROR(EAGAIN)) {
+            av_log(d->avctx, AV_LOG_ERROR, "Receive_frame and send_packet both returned EAGAIN, which is an API violation.\n");
+            d->packet_pending = 1;
         } else {
-            if (avcodec_send_packet(d->avctx, d->pkt) == AVERROR(EAGAIN)) {
-                av_log(d->avctx, AV_LOG_ERROR, "Receive_frame and send_packet both returned EAGAIN, which is an API violation.\n");
-                d->packet_pending = 1;
-            } else {
-                av_packet_unref(d->pkt);
-            }
+            av_packet_unref(d->pkt);
         }
     }
 }
@@ -683,7 +671,6 @@ static void decoder_destroy(Decoder *d) {
 static void frame_queue_unref_item(Frame *vp)
 {
     av_frame_unref(vp->frame);
-    avsubtitle_free(&vp->sub);
 }
 
 static int frame_queue_init(FrameQueue *f, PacketQueue *pktq, int max_size, int keep_last)
@@ -981,7 +968,7 @@ static void video_image_display(VideoState *is)
         if (frame_queue_nb_remaining(&is->subpq) > 0) {
             sp = frame_queue_peek(&is->subpq);
 
-            if (vp->pts >= sp->pts + ((float) sp->sub.start_display_time / 1000)) {
+            if (vp->pts >= sp->pts) {
                 if (!sp->uploaded) {
                     uint8_t* pixels[4];
                     int pitch[4];
@@ -993,25 +980,27 @@ static void video_image_display(VideoState *is)
                     if (realloc_texture(&is->sub_texture, SDL_PIXELFORMAT_ARGB8888, sp->width, sp->height, SDL_BLENDMODE_BLEND, 1) < 0)
                         return;
 
-                    for (i = 0; i < sp->sub.num_rects; i++) {
-                        AVSubtitleRect *sub_rect = sp->sub.rects[i];
+                    for (i = 0; i < sp->frame->num_subtitle_areas; i++) {
+                        AVSubtitleArea *area = sp->frame->subtitle_areas[i];
+                        SDL_Rect sdl_rect = { .x = area->x, .y = area->y, .w = area->w, .h = area->h };
 
-                        sub_rect->x = av_clip(sub_rect->x, 0, sp->width );
-                        sub_rect->y = av_clip(sub_rect->y, 0, sp->height);
-                        sub_rect->w = av_clip(sub_rect->w, 0, sp->width  - sub_rect->x);
-                        sub_rect->h = av_clip(sub_rect->h, 0, sp->height - sub_rect->y);
+                        area->x = av_clip(area->x, 0, sp->width );
+                        area->y = av_clip(area->y, 0, sp->height);
+                        area->w = av_clip(area->w, 0, sp->width  - area->x);
+                        area->h = av_clip(area->h, 0, sp->height - area->y);
 
                         is->sub_convert_ctx = sws_getCachedContext(is->sub_convert_ctx,
-                            sub_rect->w, sub_rect->h, AV_PIX_FMT_PAL8,
-                            sub_rect->w, sub_rect->h, AV_PIX_FMT_BGRA,
+                            area->w, area->h, AV_PIX_FMT_PAL8,
+                            area->w, area->h, AV_PIX_FMT_BGRA,
                             0, NULL, NULL, NULL);
                         if (!is->sub_convert_ctx) {
                             av_log(NULL, AV_LOG_FATAL, "Cannot initialize the conversion context\n");
                             return;
                         }
-                        if (!SDL_LockTexture(is->sub_texture, (SDL_Rect *)sub_rect, (void **)pixels, pitch)) {
-                            sws_scale(is->sub_convert_ctx, (const uint8_t * const *)sub_rect->data, sub_rect->linesize,
-                                      0, sub_rect->h, pixels, pitch);
+                        if (!SDL_LockTexture(is->sub_texture, &sdl_rect, (void **)pixels, pitch)) {
+                            const uint8_t* data[2] = { area->buf[0]->data, (uint8_t *)&area->pal };
+                            sws_scale(is->sub_convert_ctx, data, area->linesize,
+                                      0, area->h, pixels, pitch);
                             SDL_UnlockTexture(is->sub_texture);
                         }
                     }
@@ -1038,16 +1027,18 @@ static void video_image_display(VideoState *is)
 #if USE_ONEPASS_SUBTITLE_RENDER
         SDL_RenderCopy(renderer, is->sub_texture, NULL, &rect);
 #else
-        int i;
+        unsigned i;
         double xratio = (double)rect.w / (double)sp->width;
         double yratio = (double)rect.h / (double)sp->height;
-        for (i = 0; i < sp->sub.num_rects; i++) {
-            SDL_Rect *sub_rect = (SDL_Rect*)sp->sub.rects[i];
-            SDL_Rect target = {.x = rect.x + sub_rect->x * xratio,
-                               .y = rect.y + sub_rect->y * yratio,
-                               .w = sub_rect->w * xratio,
-                               .h = sub_rect->h * yratio};
-            SDL_RenderCopy(renderer, is->sub_texture, sub_rect, &target);
+        for (i = 0; i < sp->frame->num_subtitle_areas; i++) {
+            AVSubtitleArea *area = sp->frame->subtitle_areas[i];
+            SDL_Rect sub_rect = { .x = area->x, .y = area->y,
+                                  .w = area->w, .h = area->h};
+            SDL_Rect target = {.x = rect.x + sub_rect.x * xratio,
+                               .y = rect.y + sub_rect.y * yratio,
+                               .w = sub_rect.w * xratio,
+                               .h = sub_rect.h * yratio};
+            SDL_RenderCopy(renderer, is->sub_texture, &sub_rect, &target);
         }
 #endif
     }
@@ -1651,19 +1642,20 @@ retry:
                         sp2 = NULL;
 
                     if (sp->serial != is->subtitleq.serial
-                            || (is->vidclk.pts > (sp->pts + ((float) sp->sub.end_display_time / 1000)))
-                            || (sp2 && is->vidclk.pts > (sp2->pts + ((float) sp2->sub.start_display_time / 1000))))
+                            || (is->vidclk.pts > (sp->pts + ((float) sp->frame->subtitle_timing.duration / AV_TIME_BASE)))
+                            || (sp2 && is->vidclk.pts > (sp2->pts)))
                     {
                         if (sp->uploaded) {
                             int i;
-                            for (i = 0; i < sp->sub.num_rects; i++) {
-                                AVSubtitleRect *sub_rect = sp->sub.rects[i];
+                            for (i = 0; i < sp->frame->num_subtitle_areas; i++) {
+                                AVSubtitleArea *area = sp->frame->subtitle_areas[i];
+                                SDL_Rect sdl_rect = { .x = area->x, .y = area->y, .w = area->w, .h = area->h };
                                 uint8_t *pixels;
                                 int pitch, j;
 
-                                if (!SDL_LockTexture(is->sub_texture, (SDL_Rect *)sub_rect, (void **)&pixels, &pitch)) {
-                                    for (j = 0; j < sub_rect->h; j++, pixels += pitch)
-                                        memset(pixels, 0, sub_rect->w << 2);
+                                if (!SDL_LockTexture(is->sub_texture, &sdl_rect, (void **)&pixels, &pitch)) {
+                                    for (j = 0; j < area->h; j++, pixels += pitch)
+                                        memset(pixels, 0, area->w << 2);
                                     SDL_UnlockTexture(is->sub_texture);
                                 }
                             }
@@ -1774,7 +1766,7 @@ static int get_video_frame(VideoState *is, AVFrame *frame)
 {
     int got_picture;
 
-    if ((got_picture = decoder_decode_frame(&is->viddec, frame, NULL)) < 0)
+    if ((got_picture = decoder_decode_frame(&is->viddec, frame)) < 0)
         return -1;
 
     if (got_picture) {
@@ -2048,7 +2040,7 @@ static int audio_thread(void *arg)
         return AVERROR(ENOMEM);
 
     do {
-        if ((got_frame = decoder_decode_frame(&is->auddec, frame, NULL)) < 0)
+        if ((got_frame = decoder_decode_frame(&is->auddec, frame)) < 0)
             goto the_end;
 
         if (got_frame) {
@@ -2246,14 +2238,14 @@ static int subtitle_thread(void *arg)
         if (!(sp = frame_queue_peek_writable(&is->subpq)))
             return 0;
 
-        if ((got_subtitle = decoder_decode_frame(&is->subdec, NULL, &sp->sub)) < 0)
+        if ((got_subtitle = decoder_decode_frame(&is->subdec, sp->frame)) < 0)
             break;
 
         pts = 0;
 
-        if (got_subtitle && sp->sub.format == 0) {
-            if (sp->sub.pts != AV_NOPTS_VALUE)
-                pts = sp->sub.pts / (double)AV_TIME_BASE;
+        if (got_subtitle && sp->frame->format == AV_SUBTITLE_FMT_BITMAP) {
+            if (sp->frame->subtitle_timing.start_pts != AV_NOPTS_VALUE)
+                pts = (double)sp->frame->subtitle_timing.start_pts / (double)AV_TIME_BASE;
             sp->pts = pts;
             sp->serial = is->subdec.pkt_serial;
             sp->width = is->subdec.avctx->width;
@@ -2263,7 +2255,7 @@ static int subtitle_thread(void *arg)
             /* now we can update the picture count */
             frame_queue_push(&is->subpq);
         } else if (got_subtitle) {
-            avsubtitle_free(&sp->sub);
+            av_frame_free(&sp->frame);
         }
     }
     return 0;
diff --git a/fftools/ffprobe.c b/fftools/ffprobe.c
index 20582ca7ac..a969faec1d 100644
--- a/fftools/ffprobe.c
+++ b/fftools/ffprobe.c
@@ -2375,22 +2375,42 @@ static void show_packet(WriterContext *w, InputFile *ifile, AVPacket *pkt, int p
     fflush(stdout);
 }
 
-static void show_subtitle(WriterContext *w, AVSubtitle *sub, AVStream *stream,
+static void show_subtitle(WriterContext *w, AVFrame *sub, AVStream *stream,
                           AVFormatContext *fmt_ctx)
 {
     AVBPrint pbuf;
+    const char *s;
 
     av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
 
     writer_print_section_header(w, SECTION_ID_SUBTITLE);
 
     print_str ("media_type",         "subtitle");
-    print_ts  ("pts",                 sub->pts);
-    print_time("pts_time",            sub->pts, &AV_TIME_BASE_Q);
-    print_int ("format",              sub->format);
-    print_int ("start_display_time",  sub->start_display_time);
-    print_int ("end_display_time",    sub->end_display_time);
-    print_int ("num_rects",           sub->num_rects);
+    print_ts  ("pts",                 sub->subtitle_timing.start_pts);
+    print_time("pts_time",            sub->subtitle_timing.start_pts, &AV_TIME_BASE_Q);
+    print_time("duration",            sub->subtitle_timing.duration, &AV_TIME_BASE_Q);
+
+    // Remain compatible with previous outputs
+    switch (sub->format) {
+    case AV_SUBTITLE_FMT_BITMAP:
+        print_int ("format",         0);
+        break;
+    case AV_SUBTITLE_FMT_TEXT:
+        print_int ("format",         1);
+        break;
+    case AV_SUBTITLE_FMT_ASS:
+        print_int ("format",         1);
+        break;
+    default:
+        print_int ("format",         -1);
+        break;
+    }
+
+    s = av_get_subtitle_fmt_name(sub->format);
+    if (s) print_str    ("format_str", s);
+    else   print_str_opt("format_str", "unknown");
+
+    print_int ("num_subtitle_rects",           sub->num_subtitle_areas);
 
     writer_print_section_footer(w);
 
@@ -2557,7 +2577,6 @@ static av_always_inline int process_frame(WriterContext *w,
     AVFormatContext *fmt_ctx = ifile->fmt_ctx;
     AVCodecContext *dec_ctx = ifile->streams[pkt->stream_index].dec_ctx;
     AVCodecParameters *par = ifile->streams[pkt->stream_index].st->codecpar;
-    AVSubtitle sub;
     int ret = 0, got_frame = 0;
 
     clear_log(1);
@@ -2565,6 +2584,7 @@ static av_always_inline int process_frame(WriterContext *w,
         switch (par->codec_type) {
         case AVMEDIA_TYPE_VIDEO:
         case AVMEDIA_TYPE_AUDIO:
+        case AVMEDIA_TYPE_SUBTITLE:
             if (*packet_new) {
                 ret = avcodec_send_packet(dec_ctx, pkt);
                 if (ret == AVERROR(EAGAIN)) {
@@ -2583,12 +2603,6 @@ static av_always_inline int process_frame(WriterContext *w,
                 }
             }
             break;
-
-        case AVMEDIA_TYPE_SUBTITLE:
-            if (*packet_new)
-                ret = avcodec_decode_subtitle2(dec_ctx, &sub, &got_frame, pkt);
-            *packet_new = 0;
-            break;
         default:
             *packet_new = 0;
         }
@@ -2603,12 +2617,11 @@ static av_always_inline int process_frame(WriterContext *w,
         nb_streams_frames[pkt->stream_index]++;
         if (do_show_frames)
             if (is_sub)
-                show_subtitle(w, &sub, ifile->streams[pkt->stream_index].st, fmt_ctx);
+                show_subtitle(w, frame, ifile->streams[pkt->stream_index].st, fmt_ctx);
             else
                 show_frame(w, frame, ifile->streams[pkt->stream_index].st, fmt_ctx);
-        if (is_sub)
-            avsubtitle_free(&sub);
     }
+
     return got_frame || *packet_new;
 }
 
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH 09/24] avfilter/subtitles: Add subtitles.c for subtitle frame allocation
  2022-01-14  1:13 [FFmpeg-devel] [PATCH 00/24] Subtitle Filtering 2022 ffmpegagent
                   ` (7 preceding siblings ...)
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 08/24] fftools/play, probe: Adjust for subtitle changes ffmpegagent
@ 2022-01-14  1:13 ` ffmpegagent
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 10/24] avfilter/avfilter: Handle subtitle frames ffmpegagent
                   ` (15 subsequent siblings)
  24 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-14  1:13 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: softworkz

From: softworkz <softworkz@hotmail.com>

Analog to avfilter/video.c and avfilter/audio.c

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavfilter/Makefile    |  1 +
 libavfilter/avfilter.c  |  4 +++
 libavfilter/internal.h  |  1 +
 libavfilter/subtitles.c | 63 +++++++++++++++++++++++++++++++++++++++++
 libavfilter/subtitles.h | 44 ++++++++++++++++++++++++++++
 5 files changed, 113 insertions(+)
 create mode 100644 libavfilter/subtitles.c
 create mode 100644 libavfilter/subtitles.h

diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 1adbea75bd..283dd436cd 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -19,6 +19,7 @@ OBJS = allfilters.o                                                     \
        framequeue.o                                                     \
        graphdump.o                                                      \
        graphparser.o                                                    \
+       subtitles.o                                                      \
        video.o                                                          \
 
 OBJS-$(HAVE_THREADS)                         += pthread.o
diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c
index 7362bcdab5..df5b8f483c 100644
--- a/libavfilter/avfilter.c
+++ b/libavfilter/avfilter.c
@@ -43,6 +43,7 @@
 #include "formats.h"
 #include "framepool.h"
 #include "internal.h"
+#include "subtitles.h"
 
 #include "libavutil/ffversion.h"
 const char av_filter_ffversion[] = "FFmpeg version " FFMPEG_VERSION;
@@ -1475,6 +1476,9 @@ int ff_inlink_make_frame_writable(AVFilterLink *link, AVFrame **rframe)
     case AVMEDIA_TYPE_AUDIO:
         out = ff_get_audio_buffer(link, frame->nb_samples);
         break;
+    case AVMEDIA_TYPE_SUBTITLE:
+        out = ff_get_subtitles_buffer(link, link->format);
+        break;
     default:
         return AVERROR(EINVAL);
     }
diff --git a/libavfilter/internal.h b/libavfilter/internal.h
index 1099b82b4b..fc09ef574c 100644
--- a/libavfilter/internal.h
+++ b/libavfilter/internal.h
@@ -90,6 +90,7 @@ struct AVFilterPad {
     union {
         AVFrame *(*video)(AVFilterLink *link, int w, int h);
         AVFrame *(*audio)(AVFilterLink *link, int nb_samples);
+        AVFrame *(*subtitle)(AVFilterLink *link, int format);
     } get_buffer;
 
     /**
diff --git a/libavfilter/subtitles.c b/libavfilter/subtitles.c
new file mode 100644
index 0000000000..951bfd612c
--- /dev/null
+++ b/libavfilter/subtitles.c
@@ -0,0 +1,63 @@
+/*
+ * 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
+ */
+
+#include "libavutil/common.h"
+
+#include "subtitles.h"
+#include "avfilter.h"
+#include "internal.h"
+
+
+AVFrame *ff_null_get_subtitles_buffer(AVFilterLink *link, int format)
+{
+    return ff_get_subtitles_buffer(link->dst->outputs[0], format);
+}
+
+AVFrame *ff_default_get_subtitles_buffer(AVFilterLink *link, int format)
+{
+    AVFrame *frame;
+
+    frame = av_frame_alloc();
+    if (!frame)
+        return NULL;
+
+    frame->format = format;
+    frame->type = AVMEDIA_TYPE_SUBTITLE;
+
+    if (av_frame_get_buffer2(frame, 0) < 0) {
+        av_frame_free(&frame);
+        return NULL;
+    }
+
+    return frame;
+}
+
+AVFrame *ff_get_subtitles_buffer(AVFilterLink *link, int format)
+{
+    AVFrame *ret = NULL;
+
+    if (link->dstpad->get_buffer.subtitle)
+        ret = link->dstpad->get_buffer.subtitle(link, format);
+
+    if (!ret)
+        ret = ff_default_get_subtitles_buffer(link, format);
+
+    return ret;
+}
diff --git a/libavfilter/subtitles.h b/libavfilter/subtitles.h
new file mode 100644
index 0000000000..4a9115126e
--- /dev/null
+++ b/libavfilter/subtitles.h
@@ -0,0 +1,44 @@
+/*
+ * 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
+ */
+
+#ifndef AVFILTER_SUBTITLES_H
+#define AVFILTER_SUBTITLES_H
+
+#include "avfilter.h"
+#include "internal.h"
+
+/** default handler for get_subtitles_buffer() for subtitle inputs */
+AVFrame *ff_default_get_subtitles_buffer(AVFilterLink *link, int format);
+
+/** get_subtitles_buffer() handler for filters which simply pass subtitles along */
+AVFrame *ff_null_get_subtitles_buffer(AVFilterLink *link, int format);
+
+/**
+ * Request a subtitles frame with a specific set of permissions.
+ *
+ * @param link           the output link to the filter from which the buffer will
+ *                       be requested
+ * @param format         The subtitles format.
+ * @return               A reference to the frame. This must be unreferenced with
+ *                       av_frame_free when you are finished with it.
+*/
+AVFrame *ff_get_subtitles_buffer(AVFilterLink *link, int format);
+
+#endif /* AVFILTER_SUBTITLES_H */
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH 10/24] avfilter/avfilter: Handle subtitle frames
  2022-01-14  1:13 [FFmpeg-devel] [PATCH 00/24] Subtitle Filtering 2022 ffmpegagent
                   ` (8 preceding siblings ...)
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 09/24] avfilter/subtitles: Add subtitles.c for subtitle frame allocation ffmpegagent
@ 2022-01-14  1:13 ` ffmpegagent
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 11/24] avfilter/avfilter: Fix hardcoded input index ffmpegagent
                   ` (14 subsequent siblings)
  24 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-14  1:13 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: softworkz

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavfilter/avfilter.c      |  8 +++++---
 libavfilter/avfilter.h      | 11 +++++++++++
 libavfilter/avfiltergraph.c |  5 +++++
 libavfilter/formats.c       | 22 ++++++++++++++++++++++
 libavfilter/formats.h       |  3 +++
 libavfilter/internal.h      | 18 +++++++++++++++---
 6 files changed, 61 insertions(+), 6 deletions(-)

diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c
index df5b8f483c..75d5e86539 100644
--- a/libavfilter/avfilter.c
+++ b/libavfilter/avfilter.c
@@ -56,7 +56,8 @@ static void tlog_ref(void *ctx, AVFrame *ref, int end)
             ref->linesize[0], ref->linesize[1], ref->linesize[2], ref->linesize[3],
             ref->pts, ref->pkt_pos);
 
-    if (ref->width) {
+    switch(ref->type) {
+    case AVMEDIA_TYPE_VIDEO:
         ff_tlog(ctx, " a:%d/%d s:%dx%d i:%c iskey:%d type:%c",
                 ref->sample_aspect_ratio.num, ref->sample_aspect_ratio.den,
                 ref->width, ref->height,
@@ -64,12 +65,13 @@ static void tlog_ref(void *ctx, AVFrame *ref, int end)
                 ref->top_field_first ? 'T' : 'B',    /* Top / Bottom */
                 ref->key_frame,
                 av_get_picture_type_char(ref->pict_type));
-    }
-    if (ref->nb_samples) {
+        break;
+    case AVMEDIA_TYPE_AUDIO:
         ff_tlog(ctx, " cl:%"PRId64"d n:%d r:%d",
                 ref->channel_layout,
                 ref->nb_samples,
                 ref->sample_rate);
+        break;
     }
 
     ff_tlog(ctx, "]%s", end ? "\n" : "");
diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h
index b105dc3159..9f917deb41 100644
--- a/libavfilter/avfilter.h
+++ b/libavfilter/avfilter.h
@@ -45,6 +45,7 @@
 #include "libavutil/log.h"
 #include "libavutil/samplefmt.h"
 #include "libavutil/pixfmt.h"
+#include "libavutil/subfmt.h"
 #include "libavutil/rational.h"
 
 #include "libavfilter/version.h"
@@ -343,6 +344,12 @@ typedef struct AVFilter {
          * and outputs use the same sample rate and channel count/layout.
          */
         const enum AVSampleFormat *samples_list;
+        /**
+         * Analogous to pixels, but delimited by AV_SUBTITLE_FMT_NONE
+         * and restricted to filters that only have AVMEDIA_TYPE_SUBTITLE
+         * inputs and outputs.
+         */
+        const enum AVSubtitleType *subs_list;
         /**
          * Equivalent to { pix_fmt, AV_PIX_FMT_NONE } as pixels_list.
          */
@@ -351,6 +358,10 @@ typedef struct AVFilter {
          * Equivalent to { sample_fmt, AV_SAMPLE_FMT_NONE } as samples_list.
          */
         enum AVSampleFormat sample_fmt;
+        /**
+         * Equivalent to { sub_fmt, AV_SUBTITLE_FMT_NONE } as subs_list.
+         */
+        enum AVSubtitleType sub_fmt;
     } formats;
 
     int priv_size;      ///< size of private data to allocate for the filter
diff --git a/libavfilter/avfiltergraph.c b/libavfilter/avfiltergraph.c
index b8b432e98b..f4987654af 100644
--- a/libavfilter/avfiltergraph.c
+++ b/libavfilter/avfiltergraph.c
@@ -311,6 +311,8 @@ static int filter_link_check_formats(void *log, AVFilterLink *link, AVFilterForm
             return ret;
         break;
 
+    case AVMEDIA_TYPE_SUBTITLE:
+        return 0;
     default:
         av_assert0(!"reached");
     }
@@ -441,6 +443,9 @@ static int query_formats(AVFilterGraph *graph, void *log_ctx)
             if (!link)
                 continue;
 
+            if (link->type == AVMEDIA_TYPE_SUBTITLE)
+                continue;
+
             neg = ff_filter_get_negotiation(link);
             av_assert0(neg);
             for (neg_step = 1; neg_step < neg->nb_mergers; neg_step++) {
diff --git a/libavfilter/formats.c b/libavfilter/formats.c
index ba62f73248..5c972bb183 100644
--- a/libavfilter/formats.c
+++ b/libavfilter/formats.c
@@ -19,6 +19,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
+#include "libavutil/subfmt.h"
 #include "libavutil/avassert.h"
 #include "libavutil/channel_layout.h"
 #include "libavutil/common.h"
@@ -431,6 +432,12 @@ int ff_add_channel_layout(AVFilterChannelLayouts **l, uint64_t channel_layout)
     return 0;
 }
 
+int ff_add_subtitle_type(AVFilterFormats **avff, int64_t fmt)
+{
+    ADD_FORMAT(avff, fmt, ff_formats_unref, int, formats, nb_formats);
+    return 0;
+}
+
 AVFilterFormats *ff_make_formats_list_singleton(int fmt)
 {
     int fmts[2] = { fmt, -1 };
@@ -450,6 +457,13 @@ AVFilterFormats *ff_all_formats(enum AVMediaType type)
                 return NULL;
             fmt++;
         }
+    } else if (type == AVMEDIA_TYPE_SUBTITLE) {
+        if (ff_add_subtitle_type(&ret, AV_SUBTITLE_FMT_BITMAP) < 0)
+            return NULL;
+        if (ff_add_subtitle_type(&ret, AV_SUBTITLE_FMT_ASS) < 0)
+            return NULL;
+        if (ff_add_subtitle_type(&ret, AV_SUBTITLE_FMT_TEXT) < 0)
+            return NULL;
     }
 
     return ret;
@@ -724,6 +738,10 @@ int ff_default_query_formats(AVFilterContext *ctx)
         type    = AVMEDIA_TYPE_AUDIO;
         formats = ff_make_format_list(f->formats.samples_list);
         break;
+    case FF_FILTER_FORMATS_SUBFMTS_LIST:
+        type    = AVMEDIA_TYPE_SUBTITLE;
+        formats = ff_make_format_list(f->formats.subs_list);
+        break;
     case FF_FILTER_FORMATS_SINGLE_PIXFMT:
         type    = AVMEDIA_TYPE_VIDEO;
         formats = ff_make_formats_list_singleton(f->formats.pix_fmt);
@@ -732,6 +750,10 @@ int ff_default_query_formats(AVFilterContext *ctx)
         type    = AVMEDIA_TYPE_AUDIO;
         formats = ff_make_formats_list_singleton(f->formats.sample_fmt);
         break;
+    case FF_FILTER_FORMATS_SINGLE_SUBFMT:
+        type    = AVMEDIA_TYPE_SUBTITLE;
+        formats = ff_make_formats_list_singleton(f->formats.sub_fmt);
+        break;
     default:
         av_assert2(!"Unreachable");
     /* Intended fallthrough */
diff --git a/libavfilter/formats.h b/libavfilter/formats.h
index a884d15213..94754ebd88 100644
--- a/libavfilter/formats.h
+++ b/libavfilter/formats.h
@@ -180,6 +180,9 @@ int ff_set_common_formats_from_list(AVFilterContext *ctx, const int *fmts);
 av_warn_unused_result
 int ff_add_channel_layout(AVFilterChannelLayouts **l, uint64_t channel_layout);
 
+av_warn_unused_result
+int ff_add_subtitle_type(AVFilterFormats **avff, int64_t fmt);
+
 /**
  * Add *ref as a new reference to f.
  */
diff --git a/libavfilter/internal.h b/libavfilter/internal.h
index fc09ef574c..192b0ae196 100644
--- a/libavfilter/internal.h
+++ b/libavfilter/internal.h
@@ -149,9 +149,11 @@ static av_always_inline int ff_filter_execute(AVFilterContext *ctx, avfilter_act
 
 enum FilterFormatsState {
     /**
-     * The default value meaning that this filter supports all formats
-     * and (for audio) sample rates and channel layouts/counts as long
-     * as these properties agree for all inputs and outputs.
+     * The default value meaning that this filter supports
+     * - For video:     all formats
+     * - For audio:     all sample rates and channel layouts/counts
+     * - For subtitles: all subtitle formats
+     * as long as these properties agree for all inputs and outputs.
      * This state is only allowed in case all inputs and outputs actually
      * have the same type.
      * The union is unused in this state.
@@ -162,8 +164,10 @@ enum FilterFormatsState {
     FF_FILTER_FORMATS_QUERY_FUNC,       ///< formats.query active.
     FF_FILTER_FORMATS_PIXFMT_LIST,      ///< formats.pixels_list active.
     FF_FILTER_FORMATS_SAMPLEFMTS_LIST,  ///< formats.samples_list active.
+    FF_FILTER_FORMATS_SUBFMTS_LIST,     ///< formats.subs_list active.
     FF_FILTER_FORMATS_SINGLE_PIXFMT,    ///< formats.pix_fmt active
     FF_FILTER_FORMATS_SINGLE_SAMPLEFMT, ///< formats.sample_fmt active.
+    FF_FILTER_FORMATS_SINGLE_SUBFMT,    ///< formats.sub_fmt active.
 };
 
 #define FILTER_QUERY_FUNC(func)        \
@@ -175,16 +179,24 @@ enum FilterFormatsState {
 #define FILTER_SAMPLEFMTS_ARRAY(array) \
         .formats.samples_list = array, \
         .formats_state        = FF_FILTER_FORMATS_SAMPLEFMTS_LIST
+#define FILTER_SUBFMTS_ARRAY(array) \
+        .formats.subs_list = array, \
+        .formats_state        = FF_FILTER_FORMATS_SUBFMTS_LIST
 #define FILTER_PIXFMTS(...)            \
     FILTER_PIXFMTS_ARRAY(((const enum AVPixelFormat []) { __VA_ARGS__, AV_PIX_FMT_NONE }))
 #define FILTER_SAMPLEFMTS(...)         \
     FILTER_SAMPLEFMTS_ARRAY(((const enum AVSampleFormat[]) { __VA_ARGS__, AV_SAMPLE_FMT_NONE }))
+#define FILTER_SUBFMTS(...)         \
+    FILTER_SUBFMTS_ARRAY(((const enum AVSubtitleType[]) { __VA_ARGS__, AV_SUBTITLE_FMT_NONE }))
 #define FILTER_SINGLE_PIXFMT(pix_fmt_)  \
         .formats.pix_fmt = pix_fmt_,    \
         .formats_state   = FF_FILTER_FORMATS_SINGLE_PIXFMT
 #define FILTER_SINGLE_SAMPLEFMT(sample_fmt_) \
         .formats.sample_fmt = sample_fmt_,   \
         .formats_state      = FF_FILTER_FORMATS_SINGLE_SAMPLEFMT
+#define FILTER_SINGLE_SUBFMT(sub_fmt_) \
+        .formats.sub_fmt = sub_fmt_,   \
+        .formats_state      = FF_FILTER_FORMATS_SINGLE_SUBFMT
 
 #define FILTER_INOUTPADS(inout, array) \
        .inout        = array, \
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH 11/24] avfilter/avfilter: Fix hardcoded input index
  2022-01-14  1:13 [FFmpeg-devel] [PATCH 00/24] Subtitle Filtering 2022 ffmpegagent
                   ` (9 preceding siblings ...)
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 10/24] avfilter/avfilter: Handle subtitle frames ffmpegagent
@ 2022-01-14  1:13 ` ffmpegagent
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 12/24] avfilter/sbuffer: Add sbuffersrc and sbuffersink filters ffmpegagent
                   ` (13 subsequent siblings)
  24 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-14  1:13 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: softworkz

From: softworkz <softworkz@hotmail.com>

This fix targets (rare) cases where multiple input pads have a
.filter_frame function. ff_request_frame_to_filter needs
to call ff_request_frame with the correct input pad
instead of the hardcoded first one.

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavfilter/avfilter.c | 18 +++++++++++++-----
 1 file changed, 13 insertions(+), 5 deletions(-)

diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c
index 75d5e86539..aa9aa71f53 100644
--- a/libavfilter/avfilter.c
+++ b/libavfilter/avfilter.c
@@ -463,7 +463,7 @@ static int64_t guess_status_pts(AVFilterContext *ctx, int status, AVRational lin
     return AV_NOPTS_VALUE;
 }
 
-static int ff_request_frame_to_filter(AVFilterLink *link)
+static int ff_request_frame_to_filter(AVFilterLink *link, int input_index)
 {
     int ret = -1;
 
@@ -472,8 +472,8 @@ static int ff_request_frame_to_filter(AVFilterLink *link)
     link->frame_blocked_in = 1;
     if (link->srcpad->request_frame)
         ret = link->srcpad->request_frame(link);
-    else if (link->src->inputs[0])
-        ret = ff_request_frame(link->src->inputs[0]);
+    else if (link->src->inputs[input_index])
+        ret = ff_request_frame(link->src->inputs[input_index]);
     if (ret < 0) {
         if (ret != AVERROR(EAGAIN) && ret != link->status_in)
             ff_avfilter_link_set_in_status(link, ret, guess_status_pts(link->src, ret, link->time_base));
@@ -1172,6 +1172,14 @@ static int forward_status_change(AVFilterContext *filter, AVFilterLink *in)
 {
     unsigned out = 0, progress = 0;
     int ret;
+    int input_index = 0;
+
+    for (int i = 0; i < in->dst->nb_inputs; i++) {
+        if (&in->dst->input_pads[i] == in->dstpad) {
+            input_index = i;
+            break;
+        }
+    }
 
     av_assert0(!in->status_out);
     if (!filter->nb_outputs) {
@@ -1181,7 +1189,7 @@ static int forward_status_change(AVFilterContext *filter, AVFilterLink *in)
     while (!in->status_out) {
         if (!filter->outputs[out]->status_in) {
             progress++;
-            ret = ff_request_frame_to_filter(filter->outputs[out]);
+            ret = ff_request_frame_to_filter(filter->outputs[out], input_index);
             if (ret < 0)
                 return ret;
         }
@@ -1218,7 +1226,7 @@ static int ff_filter_activate_default(AVFilterContext *filter)
     for (i = 0; i < filter->nb_outputs; i++) {
         if (filter->outputs[i]->frame_wanted_out &&
             !filter->outputs[i]->frame_blocked_in) {
-            return ff_request_frame_to_filter(filter->outputs[i]);
+            return ff_request_frame_to_filter(filter->outputs[i], 0);
         }
     }
     return FFERROR_NOT_READY;
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH 12/24] avfilter/sbuffer: Add sbuffersrc and sbuffersink filters
  2022-01-14  1:13 [FFmpeg-devel] [PATCH 00/24] Subtitle Filtering 2022 ffmpegagent
                   ` (10 preceding siblings ...)
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 11/24] avfilter/avfilter: Fix hardcoded input index ffmpegagent
@ 2022-01-14  1:13 ` ffmpegagent
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 13/24] avfilter/overlaygraphicsubs: Add overlaygraphicsubs and graphicsub2video filters ffmpegagent
                   ` (12 subsequent siblings)
  24 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-14  1:13 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: softworkz

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 configure                |  2 +-
 libavfilter/allfilters.c |  2 ++
 libavfilter/buffersink.c | 54 ++++++++++++++++++++++++++++++
 libavfilter/buffersink.h |  7 ++++
 libavfilter/buffersrc.c  | 72 ++++++++++++++++++++++++++++++++++++++++
 libavfilter/buffersrc.h  |  1 +
 6 files changed, 137 insertions(+), 1 deletion(-)

diff --git a/configure b/configure
index 1413122d87..7c2931bd50 100755
--- a/configure
+++ b/configure
@@ -7854,7 +7854,7 @@ print_enabled_components(){
         fi
     done
     if [ "$name" = "filter_list" ]; then
-        for c in asrc_abuffer vsrc_buffer asink_abuffer vsink_buffer; do
+        for c in asrc_abuffer vsrc_buffer ssrc_sbuffer asink_abuffer vsink_buffer ssink_sbuffer; do
             printf "    &ff_%s,\n" $c >> $TMPH
         done
     fi
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 4325a3e557..8a3bd03924 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -556,8 +556,10 @@ extern const AVFilter ff_avsrc_movie;
  * being the same while having different 'types'). */
 extern  const AVFilter ff_asrc_abuffer;
 extern  const AVFilter ff_vsrc_buffer;
+extern  const AVFilter ff_ssrc_sbuffer;
 extern  const AVFilter ff_asink_abuffer;
 extern  const AVFilter ff_vsink_buffer;
+extern  const AVFilter ff_ssink_sbuffer;
 extern const AVFilter ff_af_afifo;
 extern const AVFilter ff_vf_fifo;
 
diff --git a/libavfilter/buffersink.c b/libavfilter/buffersink.c
index c0215669e7..0b268c2fa4 100644
--- a/libavfilter/buffersink.c
+++ b/libavfilter/buffersink.c
@@ -29,6 +29,8 @@
 #include "libavutil/internal.h"
 #include "libavutil/opt.h"
 
+#include "libavcodec/avcodec.h"
+
 #define FF_INTERNAL_FIELDS 1
 #include "framequeue.h"
 
@@ -57,6 +59,10 @@ typedef struct BufferSinkContext {
     int *sample_rates;                  ///< list of accepted sample rates
     int sample_rates_size;
 
+    /* only used for subtitles */
+    enum AVSubtitleType *subtitle_types;     ///< list of accepted subtitle types, must be terminated with -1
+    int subtitle_types_size;
+
     AVFrame *peeked_frame;
 } BufferSinkContext;
 
@@ -305,6 +311,28 @@ static int asink_query_formats(AVFilterContext *ctx)
     return 0;
 }
 
+static int ssink_query_formats(AVFilterContext *ctx)
+{
+    BufferSinkContext *buf = ctx->priv;
+    AVFilterFormats *formats = NULL;
+    unsigned i;
+    int ret;
+
+    CHECK_LIST_SIZE(subtitle_types)
+    if (buf->subtitle_types_size) {
+        for (i = 0; i < NB_ITEMS(buf->subtitle_types); i++)
+            if ((ret = ff_add_subtitle_type(&formats, buf->subtitle_types[i])) < 0)
+                return ret;
+        if ((ret = ff_set_common_formats(ctx, formats)) < 0)
+            return ret;
+    } else {
+        if ((ret = ff_default_query_formats(ctx)) < 0)
+            return ret;
+    }
+
+    return 0;
+}
+
 #define OFFSET(x) offsetof(BufferSinkContext, x)
 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
 static const AVOption buffersink_options[] = {
@@ -322,9 +350,16 @@ static const AVOption abuffersink_options[] = {
     { NULL },
 };
 #undef FLAGS
+#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_SUBTITLE_PARAM
+static const AVOption sbuffersink_options[] = {
+    { "subtitle_types", "set the supported subtitle formats", OFFSET(subtitle_types), AV_OPT_TYPE_BINARY, .flags = FLAGS },
+    { NULL },
+};
+#undef FLAGS
 
 AVFILTER_DEFINE_CLASS(buffersink);
 AVFILTER_DEFINE_CLASS(abuffersink);
+AVFILTER_DEFINE_CLASS(sbuffersink);
 
 static const AVFilterPad avfilter_vsink_buffer_inputs[] = {
     {
@@ -363,3 +398,22 @@ const AVFilter ff_asink_abuffer = {
     .outputs       = NULL,
     FILTER_QUERY_FUNC(asink_query_formats),
 };
+
+static const AVFilterPad avfilter_ssink_sbuffer_inputs[] = {
+    {
+        .name = "default",
+        .type = AVMEDIA_TYPE_SUBTITLE,
+    },
+};
+
+const AVFilter ff_ssink_sbuffer = {
+    .name          = "sbuffersink",
+    .description   = NULL_IF_CONFIG_SMALL("Buffer subtitle frames, and make them available to the end of the filter graph."),
+    .priv_class    = &sbuffersink_class,
+    .priv_size     = sizeof(BufferSinkContext),
+    .init          = common_init,
+    .activate      = activate,
+    FILTER_INPUTS(avfilter_ssink_sbuffer_inputs),
+    .outputs       = NULL,
+    FILTER_QUERY_FUNC(ssink_query_formats),
+};
diff --git a/libavfilter/buffersink.h b/libavfilter/buffersink.h
index 69ed0f29a8..11905abdc5 100644
--- a/libavfilter/buffersink.h
+++ b/libavfilter/buffersink.h
@@ -129,6 +129,13 @@ typedef struct AVABufferSinkParams {
  */
 attribute_deprecated
 AVABufferSinkParams *av_abuffersink_params_alloc(void);
+
+/**
+ * Deprecated and unused struct to use for initializing an sbuffersink context.
+ */
+typedef struct AVSBufferSinkParams {
+    const int *subtitle_type;
+} AVSBufferSinkParams;
 #endif
 
 /**
diff --git a/libavfilter/buffersrc.c b/libavfilter/buffersrc.c
index b0611872f1..d2362999a2 100644
--- a/libavfilter/buffersrc.c
+++ b/libavfilter/buffersrc.c
@@ -39,6 +39,7 @@
 #include "formats.h"
 #include "internal.h"
 #include "video.h"
+#include "libavcodec/avcodec.h"
 
 typedef struct BufferSourceContext {
     const AVClass    *class;
@@ -63,6 +64,9 @@ typedef struct BufferSourceContext {
     uint64_t channel_layout;
     char    *channel_layout_str;
 
+    /* subtitle only */
+    enum AVSubtitleType subtitle_type;
+
     int eof;
 } BufferSourceContext;
 
@@ -130,6 +134,13 @@ int av_buffersrc_parameters_set(AVFilterContext *ctx, AVBufferSrcParameters *par
         if (param->channel_layout)
             s->channel_layout = param->channel_layout;
         break;
+    case AVMEDIA_TYPE_SUBTITLE:
+        s->subtitle_type = param->format;
+        if (param->width > 0)
+            s->w = param->width;
+        if (param->height > 0)
+            s->h = param->height;
+        break;
     default:
         return AVERROR_BUG;
     }
@@ -197,6 +208,8 @@ int attribute_align_arg av_buffersrc_add_frame_flags(AVFilterContext *ctx, AVFra
             CHECK_AUDIO_PARAM_CHANGE(ctx, s, frame->sample_rate, frame->channel_layout,
                                      frame->channels, frame->format, frame->pts);
             break;
+        case AVMEDIA_TYPE_SUBTITLE:
+            break;
         default:
             return AVERROR(EINVAL);
         }
@@ -269,6 +282,7 @@ unsigned av_buffersrc_get_nb_failed_requests(AVFilterContext *buffer_src)
 #define OFFSET(x) offsetof(BufferSourceContext, x)
 #define A AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_AUDIO_PARAM
 #define V AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
+#define S AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_SUBTITLE_PARAM
 
 static const AVOption buffer_options[] = {
     { "width",         NULL,                     OFFSET(w),                AV_OPT_TYPE_INT,      { .i64 = 0 }, 0, INT_MAX, V },
@@ -298,6 +312,16 @@ static const AVOption abuffer_options[] = {
 
 AVFILTER_DEFINE_CLASS(abuffer);
 
+static const AVOption sbuffer_options[] = {
+    { "time_base",     NULL, OFFSET(time_base),            AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, 0, INT_MAX, S },
+    { "subtitle_type", NULL, OFFSET(subtitle_type),        AV_OPT_TYPE_INT,      { .i64 = 0 }, 0, INT_MAX, S },
+    { "width",         NULL, OFFSET(w),                    AV_OPT_TYPE_INT,      { .i64 = 0 }, 0, INT_MAX, V },
+    { "height",        NULL, OFFSET(h),                    AV_OPT_TYPE_INT,      { .i64 = 0 }, 0, INT_MAX, V },
+    { NULL },
+};
+
+AVFILTER_DEFINE_CLASS(sbuffer);
+
 static av_cold int init_audio(AVFilterContext *ctx)
 {
     BufferSourceContext *s = ctx->priv;
@@ -347,6 +371,21 @@ static av_cold int init_audio(AVFilterContext *ctx)
     return ret;
 }
 
+static av_cold int init_subtitle(AVFilterContext *ctx)
+{
+    BufferSourceContext *c = ctx->priv;
+
+    if (c->subtitle_type == AV_SUBTITLE_FMT_BITMAP)
+        av_log(ctx, AV_LOG_VERBOSE, "graphical subtitles - w:%d h:%d tb:%d/%d\n",
+               c->w, c->h, c->time_base.num, c->time_base.den);
+    else
+        av_log(ctx, AV_LOG_VERBOSE, "text subtitles -  w:%d h:%d tb:%d/%d\n",
+               c->w, c->h, c->time_base.num, c->time_base.den);
+
+    return 0;
+}
+
+
 static av_cold void uninit(AVFilterContext *ctx)
 {
     BufferSourceContext *s = ctx->priv;
@@ -381,6 +420,11 @@ static int query_formats(AVFilterContext *ctx)
         if ((ret = ff_set_common_channel_layouts(ctx, channel_layouts)) < 0)
             return ret;
         break;
+    case AVMEDIA_TYPE_SUBTITLE:
+        if ((ret = ff_add_format         (&formats, c->subtitle_type)) < 0 ||
+            (ret = ff_set_common_formats (ctx     , formats   )) < 0)
+            return ret;
+        break;
     default:
         return AVERROR(EINVAL);
     }
@@ -408,6 +452,11 @@ static int config_props(AVFilterLink *link)
         if (!c->channel_layout)
             c->channel_layout = link->channel_layout;
         break;
+    case AVMEDIA_TYPE_SUBTITLE:
+        link->format = c->subtitle_type;
+        link->w = c->w;
+        link->h = c->h;
+        break;
     default:
         return AVERROR(EINVAL);
     }
@@ -472,3 +521,26 @@ const AVFilter ff_asrc_abuffer = {
     FILTER_QUERY_FUNC(query_formats),
     .priv_class = &abuffer_class,
 };
+
+static const AVFilterPad ssrc_sbuffer_outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_SUBTITLE,
+        .request_frame = request_frame,
+        .config_props  = config_props,
+    },
+};
+
+const AVFilter ff_ssrc_sbuffer = {
+    .name          = "sbuffer",
+    .description   = NULL_IF_CONFIG_SMALL("Buffer subtitle frames, and make them accessible to the filterchain."),
+    .priv_size     = sizeof(BufferSourceContext),
+
+    .init      = init_subtitle,
+    .uninit    = uninit,
+
+    .inputs    = NULL,
+    FILTER_OUTPUTS(ssrc_sbuffer_outputs),
+    FILTER_QUERY_FUNC(query_formats),
+    .priv_class = &sbuffer_class,
+};
diff --git a/libavfilter/buffersrc.h b/libavfilter/buffersrc.h
index 08fbd18a47..929a2fa249 100644
--- a/libavfilter/buffersrc.h
+++ b/libavfilter/buffersrc.h
@@ -74,6 +74,7 @@ typedef struct AVBufferSrcParameters {
     /**
      * video: the pixel format, value corresponds to enum AVPixelFormat
      * audio: the sample format, value corresponds to enum AVSampleFormat
+     * subtitles: the subtitle format, value corresponds to enum AVSubtitleType
      */
     int format;
     /**
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH 13/24] avfilter/overlaygraphicsubs: Add overlaygraphicsubs and graphicsub2video filters
  2022-01-14  1:13 [FFmpeg-devel] [PATCH 00/24] Subtitle Filtering 2022 ffmpegagent
                   ` (11 preceding siblings ...)
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 12/24] avfilter/sbuffer: Add sbuffersrc and sbuffersink filters ffmpegagent
@ 2022-01-14  1:13 ` ffmpegagent
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 14/24] avfilter/overlaytextsubs: Add overlaytextsubs and textsubs2video filters ffmpegagent
                   ` (11 subsequent siblings)
  24 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-14  1:13 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: softworkz

From: softworkz <softworkz@hotmail.com>

- overlaygraphicsubs (VS -> V)
  Overlay graphic subtitles onto a video stream

- graphicsub2video {S -> V)
  Converts graphic subtitles to video frames (with alpha)
  Gets auto-inserted for retaining compatibility with
  sub2video command lines

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 doc/filters.texi                    | 118 +++++
 libavfilter/Makefile                |   2 +
 libavfilter/allfilters.c            |   2 +
 libavfilter/vf_overlaygraphicsubs.c | 765 ++++++++++++++++++++++++++++
 4 files changed, 887 insertions(+)
 create mode 100644 libavfilter/vf_overlaygraphicsubs.c

diff --git a/doc/filters.texi b/doc/filters.texi
index 05d4b1a56e..4fc4a57dbb 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -25713,6 +25713,124 @@ tools.
 
 @c man end VIDEO SINKS
 
+@chapter Subtitle Filters
+@c man begin SUBTITLE FILTERS
+
+When you configure your FFmpeg build, you can disable any of the
+existing filters using @code{--disable-filters}.
+
+Below is a description of the currently available subtitle filters.
+
+@section graphicsub2video
+
+Renders graphic subtitles as video frames.
+
+This filter replaces the previous "sub2video" hack which did the conversion implicitly and up-front as subtitle filtering wasn't possible at that time.
+To retain compatibility with earlier sub2video command lines, this filter is being auto-inserted in those cases.
+
+For overlaying graphicsal subtitles it is recommended to use the 'overlay_graphicsubs' filter which is more efficient and takes less processing resources.
+
+This filter is still useful in cases where the overlay is done with hardware acceleration (e.g. overlay_qsv, overlay_vaapi, overlay_cuda) for preparing the overlay frames.
+
+Inputs:
+@itemize
+@item 0: Subtitles [BITMAP]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Video [RGB32]
+@end itemize
+
+
+It accepts the following parameters:
+
+@table @option
+@item size, s
+Set the size of the output video frame.
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Overlay PGS subtitles
+(not recommended - better use overlay_graphicsubs)
+@example
+ffmpeg -i "https://streams.videolan.org/samples/sub/PGS/Girl_With_The_Dragon_Tattoo_2%3A23%3A56.mkv" -filter_complex "[0:1]graphicsub2video[subs];[0:0][subs]overlay" output.mp4
+@end example
+
+@item
+Overlay PGS subtitles implicitly
+The graphicsub2video is inserted automatically for compatibility with legacy command lines.
+@example
+ffmpeg -i "https://streams.videolan.org/samples/sub/PGS/Girl_With_The_Dragon_Tattoo_2%3A23%3A56.mkv" -filter_complex "[0:0][0:1]overlay" output.mp4
+@end example
+@end itemize
+
+@section overlaygraphicsubs
+
+Overlay graphic subtitles onto a video stream.
+
+This filter can blend graphical subtitles on a video stream directly, i.e. without creating full-size alpha images first.
+The blending operation is limited to the area of the subtitle rectangles, which also means that no processing is done at times where no subtitles are to be displayed.
+
+Inputs:
+@itemize
+@item 0: Video [YUV420P, YUV422P, YUV444P, ARGB, RGBA, ABGR, BGRA, RGB24, BGR24]
+@item 1: Subtitles [BITMAP]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Video (same as input)
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+@item x
+@item y
+Set the expression for the x and y coordinates of the overlaid video
+on the main video. Default value is "0" for both expressions. In case
+the expression is invalid, it is set to a huge value (meaning that the
+overlay will not be displayed within the output visible area).
+
+@item eof_action
+See @ref{framesync}.
+
+@item eval
+Set when the expressions for @option{x}, and @option{y} are evaluated.
+
+It accepts the following values:
+@table @samp
+@item init
+only evaluate expressions once during the filter initialization or
+when a command is processed
+
+@item frame
+evaluate expressions for each incoming frame
+@end table
+
+Default value is @samp{frame}.
+
+@item shortest
+See @ref{framesync}.
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Overlay PGS subtitles
+@example
+ffmpeg -i "https://streams.videolan.org/samples/sub/PGS/Girl_With_The_Dragon_Tattoo_2%3A23%3A56.mkv" -filter_complex "[0:0][0:1]overlaygraphicsubs" output.mp4
+@end example
+@end itemize
+@c man end SUBTITLE FILTERS
+
 @chapter Multimedia Filters
 @c man begin MULTIMEDIA FILTERS
 
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 283dd436cd..a372effc12 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -298,6 +298,7 @@ OBJS-$(CONFIG_GBLUR_FILTER)                  += vf_gblur.o
 OBJS-$(CONFIG_GBLUR_VULKAN_FILTER)           += vf_gblur_vulkan.o vulkan.o vulkan_filter.o
 OBJS-$(CONFIG_GEQ_FILTER)                    += vf_geq.o
 OBJS-$(CONFIG_GRADFUN_FILTER)                += vf_gradfun.o
+OBJS-$(CONFIG_GRAPHICSUB2VIDEO_FILTER)       += vf_overlaygraphicsubs.o framesync.o
 OBJS-$(CONFIG_GRAPHMONITOR_FILTER)           += f_graphmonitor.o
 OBJS-$(CONFIG_GRAYWORLD_FILTER)              += vf_grayworld.o
 OBJS-$(CONFIG_GREYEDGE_FILTER)               += vf_colorconstancy.o
@@ -378,6 +379,7 @@ OBJS-$(CONFIG_OVERLAY_OPENCL_FILTER)         += vf_overlay_opencl.o opencl.o \
                                                 opencl/overlay.o framesync.o
 OBJS-$(CONFIG_OVERLAY_QSV_FILTER)            += vf_overlay_qsv.o framesync.o
 OBJS-$(CONFIG_OVERLAY_VULKAN_FILTER)         += vf_overlay_vulkan.o vulkan.o vulkan_filter.o
+OBJS-$(CONFIG_OVERLAYGRAPHICSUBS_FILTER)     += vf_overlaygraphicsubs.o framesync.o
 OBJS-$(CONFIG_OWDENOISE_FILTER)              += vf_owdenoise.o
 OBJS-$(CONFIG_PAD_FILTER)                    += vf_pad.o
 OBJS-$(CONFIG_PAD_OPENCL_FILTER)             += vf_pad_opencl.o opencl.o opencl/pad.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 8a3bd03924..eb4e30a270 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -358,6 +358,7 @@ extern const AVFilter ff_vf_oscilloscope;
 extern const AVFilter ff_vf_overlay;
 extern const AVFilter ff_vf_overlay_opencl;
 extern const AVFilter ff_vf_overlay_qsv;
+extern const AVFilter ff_vf_overlaygraphicsubs;
 extern const AVFilter ff_vf_overlay_vulkan;
 extern const AVFilter ff_vf_overlay_cuda;
 extern const AVFilter ff_vf_owdenoise;
@@ -545,6 +546,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_svf_graphicsub2video;
 
 /* multimedia sources */
 extern const AVFilter ff_avsrc_amovie;
diff --git a/libavfilter/vf_overlaygraphicsubs.c b/libavfilter/vf_overlaygraphicsubs.c
new file mode 100644
index 0000000000..7b26d8ef37
--- /dev/null
+++ b/libavfilter/vf_overlaygraphicsubs.c
@@ -0,0 +1,765 @@
+/*
+ * Copyright (c) 2021 softworkz (derived from vf_overlay)
+ * Copyright (c) 2010 Stefano Sabatini
+ * Copyright (c) 2010 Baptiste Coudurier
+ * Copyright (c) 2007 Bobby Bingham
+ *
+ * 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
+ * overlay graphical subtitles on top of a video frame
+ */
+
+#include "libavutil/eval.h"
+#include "libavutil/imgutils.h"
+#include "libavutil/opt.h"
+#include "internal.h"
+#include "drawutils.h"
+#include "framesync.h"
+
+enum var_name {
+    VAR_MAIN_W,    VAR_MW,
+    VAR_MAIN_H,    VAR_MH,
+    VAR_OVERLAY_W, VAR_OW,
+    VAR_OVERLAY_H, VAR_OH,
+    VAR_HSUB,
+    VAR_VSUB,
+    VAR_X,
+    VAR_Y,
+    VAR_N,
+    VAR_POS,
+    VAR_T,
+    VAR_VARS_NB
+};
+
+typedef struct OverlaySubsContext {
+    const AVClass *class;
+    int x, y;                   ///< position of overlaid picture
+    int w, h;
+    AVFrame *outpicref;
+
+    int main_is_packed_rgb;
+    uint8_t main_rgba_map[4];
+    int main_has_alpha;
+    uint8_t overlay_rgba_map[4];
+    int eval_mode;              ///< EvalMode
+    int use_caching;
+    AVFrame *cache_frame;
+
+    FFFrameSync fs;
+
+    int main_pix_step[4];       ///< steps per pixel for each plane of the main output
+    int hsub, vsub;             ///< chroma subsampling values
+    const AVPixFmtDescriptor *main_desc; ///< format descriptor for main input
+
+    double var_values[VAR_VARS_NB];
+    char *x_expr, *y_expr;
+
+    AVExpr *x_pexpr, *y_pexpr;
+
+    int pic_counter;
+} OverlaySubsContext;
+
+static const char *const var_names[] = {
+    "main_w",    "W", ///< width  of the main    video
+    "main_h",    "H", ///< height of the main    video
+    "overlay_w", "w", ///< width  of the overlay video
+    "overlay_h", "h", ///< height of the overlay video
+    "hsub",
+    "vsub",
+    "x",
+    "y",
+    "n",            ///< number of frame
+    "pos",          ///< position in the file
+    "t",            ///< timestamp expressed in seconds
+    NULL
+};
+
+#define MAIN    0
+#define OVERLAY 1
+
+#define R 0
+#define G 1
+#define B 2
+#define A 3
+
+#define Y 0
+#define U 1
+#define V 2
+
+enum EvalMode {
+    EVAL_MODE_INIT,
+    EVAL_MODE_FRAME,
+    EVAL_MODE_NB
+};
+
+static av_cold void overlay_graphicsubs_uninit(AVFilterContext *ctx)
+{
+    OverlaySubsContext *s = ctx->priv;
+
+    av_frame_free(&s->cache_frame);
+    ff_framesync_uninit(&s->fs);
+    av_expr_free(s->x_pexpr); s->x_pexpr = NULL;
+    av_expr_free(s->y_pexpr); s->y_pexpr = NULL;
+}
+
+static inline int normalize_xy(double d, int chroma_sub)
+{
+    if (isnan(d))
+        return INT_MAX;
+    return (int)d & ~((1 << chroma_sub) - 1);
+}
+
+static void eval_expr(AVFilterContext *ctx)
+{
+    OverlaySubsContext *s = ctx->priv;
+
+    s->var_values[VAR_X] = av_expr_eval(s->x_pexpr, s->var_values, NULL);
+    s->var_values[VAR_Y] = av_expr_eval(s->y_pexpr, s->var_values, NULL);
+    /* It is necessary if x is expressed from y  */
+    s->var_values[VAR_X] = av_expr_eval(s->x_pexpr, s->var_values, NULL);
+    s->x = normalize_xy(s->var_values[VAR_X], s->hsub);
+    s->y = normalize_xy(s->var_values[VAR_Y], s->vsub);
+}
+
+static int set_expr(AVExpr **pexpr, const char *expr, const char *option, void *log_ctx)
+{
+    int ret;
+    AVExpr *old = NULL;
+
+    if (*pexpr)
+        old = *pexpr;
+    ret = av_expr_parse(pexpr, expr, var_names,
+                        NULL, NULL, NULL, NULL, 0, log_ctx);
+    if (ret < 0) {
+        av_log(log_ctx, AV_LOG_ERROR,
+               "Error when evaluating the expression '%s' for %s\n",
+               expr, option);
+        *pexpr = old;
+        return ret;
+    }
+
+    av_expr_free(old);
+    return 0;
+}
+
+static int overlay_graphicsubs_query_formats(AVFilterContext *ctx)
+{
+    AVFilterFormats *formats;
+    AVFilterLink *inlink0 = ctx->inputs[0];
+    AVFilterLink *inlink1 = ctx->inputs[1];
+    AVFilterLink *outlink = ctx->outputs[0];
+    int ret;
+    static const enum AVSubtitleType subtitle_fmts[] = { AV_SUBTITLE_FMT_BITMAP, AV_SUBTITLE_FMT_NONE };
+    static const enum AVPixelFormat supported_pix_fmts[] = {
+        AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV444P,
+        AV_PIX_FMT_ARGB,  AV_PIX_FMT_RGBA,
+        AV_PIX_FMT_ABGR,  AV_PIX_FMT_BGRA,
+        AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24,
+        AV_PIX_FMT_NONE
+    };
+
+    /* set input0 video formats */
+    formats = ff_make_format_list(supported_pix_fmts);
+    if ((ret = ff_formats_ref(formats, &inlink0->outcfg.formats)) < 0)
+        return ret;
+
+    /* set output0 video formats */
+    if ((ret = ff_formats_ref(formats, &outlink->incfg.formats)) < 0)
+        return ret;
+
+    /* set input1 subtitle formats */
+    formats = ff_make_format_list(subtitle_fmts);
+    if ((ret = ff_formats_ref(formats, &inlink1->outcfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    OverlaySubsContext *s = ctx->priv;
+    int ret;
+
+    if ((ret = ff_framesync_init_dualinput(&s->fs, ctx)) < 0)
+        return ret;
+
+    outlink->w = ctx->inputs[MAIN]->w;
+    outlink->h = ctx->inputs[MAIN]->h;
+    outlink->time_base = ctx->inputs[MAIN]->time_base;
+    outlink->frame_rate = ctx->inputs[MAIN]->frame_rate;
+
+    return ff_framesync_configure(&s->fs);
+}
+
+// divide by 255 and round to nearest
+// apply a fast variant: (X+127)/255 = ((X+127)*257+257)>>16 = ((X+128)*257)>>16
+#define FAST_DIV255(x) ((((x) + 128) * 257) >> 16)
+
+// calculate the non-pre-multiplied alpha, applying the general equation:
+// alpha = alpha_overlay / ( (alpha_main + alpha_overlay) - (alpha_main * alpha_overlay) )
+// (((x) << 16) - ((x) << 9) + (x)) is a faster version of: 255 * 255 * x
+// ((((x) + (y)) << 8) - ((x) + (y)) - (y) * (x)) is a faster version of: 255 * (x + y)
+#define UNPREMULTIPLY_ALPHA(x, y) ((((x) << 16) - ((x) << 9) + (x)) / ((((x) + (y)) << 8) - ((x) + (y)) - (y) * (x)))
+
+/**
+ * Blend image in src to destination buffer dst at position (x, y).
+ */
+static av_always_inline void blend_packed_rgb(const AVFilterContext *ctx,
+    const AVFrame *dst, const AVSubtitleArea *src,
+    int x, int y,
+    int is_straight)
+{
+    OverlaySubsContext *s = ctx->priv;
+    int i, imax, j, jmax;
+    const int src_w = src->w;
+    const int src_h = src->h;
+    const int dst_w = dst->width;
+    const int dst_h = dst->height;
+    uint8_t alpha;          ///< the amount of overlay to blend on to main
+    const int dr = s->main_rgba_map[R];
+    const int dg = s->main_rgba_map[G];
+    const int db = s->main_rgba_map[B];
+    const int da = s->main_rgba_map[A];
+    const int dstep = s->main_pix_step[0];
+    const int sr = s->overlay_rgba_map[R];
+    const int sg = s->overlay_rgba_map[G];
+    const int sb = s->overlay_rgba_map[B];
+    const int sa = s->overlay_rgba_map[A];
+    int slice_start, slice_end;
+    uint8_t *S, *sp, *d, *dp;
+
+    i = FFMAX(-y, 0);
+    imax = FFMIN3(-y + dst_h, FFMIN(src_h, dst_h), y + src_h);
+
+    slice_start = i;
+    slice_end = i + imax;
+
+    sp = src->buf[0]->data + slice_start       * src->linesize[0];
+    dp = dst->data[0] + (slice_start + y) * dst->linesize[0];
+
+    for (i = slice_start; i < slice_end; i++) {
+        j = FFMAX(-x, 0);
+        S = sp + j;
+        d = dp + ((x + j) * dstep);
+
+        for (jmax = FFMIN(-x + dst_w, src_w); j < jmax; j++) {
+            uint32_t val = src->pal[*S];
+            const uint8_t *sval = (uint8_t *)&val;
+            alpha = sval[sa];
+
+            // if the main channel has an alpha channel, alpha has to be calculated
+            // to create an un-premultiplied (straight) alpha value
+            if (s->main_has_alpha && alpha != 0 && alpha != 255) {
+                const uint8_t alpha_d = d[da];
+                alpha = UNPREMULTIPLY_ALPHA(alpha, alpha_d);
+            }
+
+            switch (alpha) {
+            case 0:
+                break;
+            case 255:
+                d[dr] = sval[sr];
+                d[dg] = sval[sg];
+                d[db] = sval[sb];
+                break;
+            default:
+                // main_value = main_value * (1 - alpha) + overlay_value * alpha
+                // since alpha is in the range 0-255, the result must divided by 255
+                d[dr] = is_straight ? FAST_DIV255(d[dr] * (255 - alpha) + sval[sr] * alpha) :
+                        FFMIN(FAST_DIV255(d[dr] * (255 - alpha)) + sval[sr], 255);
+                d[dg] = is_straight ? FAST_DIV255(d[dg] * (255 - alpha) + sval[sg] * alpha) :
+                        FFMIN(FAST_DIV255(d[dg] * (255 - alpha)) + sval[sg], 255);
+                d[db] = is_straight ? FAST_DIV255(d[db] * (255 - alpha) + sval[sb] * alpha) :
+                        FFMIN(FAST_DIV255(d[db] * (255 - alpha)) + sval[sb], 255);
+            }
+
+            if (s->main_has_alpha) {
+                switch (alpha) {
+                case 0:
+                    break;
+                case 255:
+                    d[da] = sval[sa];
+                    break;
+                default:
+                    // apply alpha compositing: main_alpha += (1-main_alpha) * overlay_alpha
+                    d[da] += FAST_DIV255((255 - d[da]) * S[sa]);
+                }
+            }
+            d += dstep;
+            S += 1;
+        }
+        dp += dst->linesize[0];
+        sp += src->linesize[0];
+    }
+}
+
+static av_always_inline void blend_plane_8_8bits(const AVFilterContext *ctx, const AVFrame *dst, const AVSubtitleArea *area,
+    const uint32_t *yuv_pal, int src_w, int src_h, int dst_w, int dst_h, int plane, int hsub, int vsub,
+    int x, int y, int dst_plane, int dst_offset, int dst_step)
+{
+    const int src_wp = AV_CEIL_RSHIFT(src_w, hsub);
+    const int src_hp = AV_CEIL_RSHIFT(src_h, vsub);
+    const int dst_wp = AV_CEIL_RSHIFT(dst_w, hsub);
+    const int dst_hp = AV_CEIL_RSHIFT(dst_h, vsub);
+    const int yp = y >> vsub;
+    const int xp = x >> hsub;
+    uint8_t *s, *sp, *d, *dp, *dap;
+    int imax, i, j, jmax;
+    int slice_start, slice_end;
+
+    i = FFMAX(-yp, 0);                                                                                     \
+    imax = FFMIN3(-yp + dst_hp, FFMIN(src_hp, dst_hp), yp + src_hp);                                       \
+
+    slice_start = i;
+    slice_end = i + imax;
+
+    sp = area->buf[0]->data + (slice_start << vsub) * area->linesize[0];
+    dp = dst->data[dst_plane] + (yp + slice_start) * dst->linesize[dst_plane] + dst_offset;
+
+    dap = dst->data[3] + ((yp + slice_start) << vsub) * dst->linesize[3];
+
+    for (i = slice_start; i < slice_end; i++) {
+        j = FFMAX(-xp, 0);
+        d = dp + (xp + j) * dst_step;
+        s = sp + (j << hsub);
+        jmax = FFMIN(-xp + dst_wp, src_wp);
+
+        for (; j < jmax; j++) {
+            uint32_t val = yuv_pal[*s];
+            const uint8_t *sval = (uint8_t *)&val;
+            const int alpha = sval[3];
+            const int max = 255, mid = 128;
+            const int d_int = *d;
+            const int sval_int = sval[plane];
+
+            switch (alpha) {
+            case 0:
+                break;
+            case 255:
+                *d = sval[plane];
+                break;
+            default:
+                if (plane > 0)
+                    *d = av_clip(FAST_DIV255((d_int - mid) * (max - alpha) + (sval_int - mid) * alpha) , -mid, mid) + mid;
+                else
+                    *d = FAST_DIV255(d_int * (max - alpha) + sval_int * alpha);
+                break;
+            }
+
+            d += dst_step;
+            s += 1 << hsub;
+        }
+        dp += dst->linesize[dst_plane];
+        sp +=  (1 << vsub) * area->linesize[0];
+        dap += (1 << vsub) * dst->linesize[3];
+    }
+}
+
+#define RGB2Y(r, g, b) (uint8_t)(((66 * (r) + 129 * (g) +  25 * (b) + 128) >> 8) +  16)
+#define RGB2U(r, g, b) (uint8_t)(((-38 * (r) - 74 * (g) + 112 * (b) + 128) >> 8) + 128)
+#define RGB2V(r, g, b) (uint8_t)(((112 * (r) - 94 * (g) -  18 * (b) + 128) >> 8) + 128)
+/* Converts R8 G8 B8 color to YUV. */
+static av_always_inline void rgb_2_yuv(uint8_t r, uint8_t g, uint8_t b, uint8_t* y, uint8_t* u, uint8_t* v)
+{
+    *y = RGB2Y((int)r, (int)g, (int)b);
+    *u = RGB2U((int)r, (int)g, (int)b);
+    *v = RGB2V((int)r, (int)g, (int)b);
+}
+
+
+static av_always_inline void blend_yuv_8_8bits(AVFilterContext *ctx, AVFrame *dst, const AVSubtitleArea *area, int hsub, int vsub, int x, int y)
+{
+    OverlaySubsContext *s = ctx->priv;
+    const int src_w = area->w;
+    const int src_h = area->h;
+    const int dst_w = dst->width;
+    const int dst_h = dst->height;
+    const int sr = s->overlay_rgba_map[R];
+    const int sg = s->overlay_rgba_map[G];
+    const int sb = s->overlay_rgba_map[B];
+    const int sa = s->overlay_rgba_map[A];
+    uint32_t yuvpal[256];
+
+    for (int i = 0; i < 256; ++i) {
+        const uint8_t *rgba = (const uint8_t *)&area->pal[i];
+        uint8_t *yuva = (uint8_t *)&yuvpal[i];
+        rgb_2_yuv(rgba[sr], rgba[sg], rgba[sb], &yuva[Y], &yuva[U], &yuva[V]);
+        yuva[3] = rgba[sa];
+    }
+
+    blend_plane_8_8bits(ctx, dst, area, yuvpal, src_w, src_h, dst_w, dst_h, Y, 0,    0,    x, y, s->main_desc->comp[Y].plane, s->main_desc->comp[Y].offset, s->main_desc->comp[Y].step);
+    blend_plane_8_8bits(ctx, dst, area, yuvpal, src_w, src_h, dst_w, dst_h, U, hsub, vsub, x, y, s->main_desc->comp[U].plane, s->main_desc->comp[U].offset, s->main_desc->comp[U].step);
+    blend_plane_8_8bits(ctx, dst, area, yuvpal, src_w, src_h, dst_w, dst_h, V, hsub, vsub, x, y, s->main_desc->comp[V].plane, s->main_desc->comp[V].offset, s->main_desc->comp[V].step);
+}
+
+static int config_input_main(AVFilterLink *inlink)
+{
+    int ret;
+    AVFilterContext *ctx  = inlink->dst;
+    OverlaySubsContext *s = inlink->dst->priv;
+    const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(inlink->format);
+
+    av_image_fill_max_pixsteps(s->main_pix_step,    NULL, pix_desc);
+    ff_fill_rgba_map(s->overlay_rgba_map, AV_PIX_FMT_RGB32); // it's actually AV_PIX_FMT_PAL8);
+
+    s->hsub = pix_desc->log2_chroma_w;
+    s->vsub = pix_desc->log2_chroma_h;
+
+    s->main_desc = pix_desc;
+
+    s->main_is_packed_rgb = ff_fill_rgba_map(s->main_rgba_map, inlink->format) >= 0;
+    s->main_has_alpha = !!(pix_desc->flags & AV_PIX_FMT_FLAG_ALPHA);
+
+    /* Finish the configuration by evaluating the expressions
+       now when both inputs are configured. */
+    s->var_values[VAR_MAIN_W   ] = s->var_values[VAR_MW] = ctx->inputs[MAIN   ]->w;
+    s->var_values[VAR_MAIN_H   ] = s->var_values[VAR_MH] = ctx->inputs[MAIN   ]->h;
+    s->var_values[VAR_OVERLAY_W] = s->var_values[VAR_OW] = ctx->inputs[OVERLAY]->w;
+    s->var_values[VAR_OVERLAY_H] = s->var_values[VAR_OH] = ctx->inputs[OVERLAY]->h;
+    s->var_values[VAR_HSUB]  = 1<<pix_desc->log2_chroma_w;
+    s->var_values[VAR_VSUB]  = 1<<pix_desc->log2_chroma_h;
+    s->var_values[VAR_X]     = NAN;
+    s->var_values[VAR_Y]     = NAN;
+    s->var_values[VAR_N]     = 0;
+    s->var_values[VAR_T]     = NAN;
+    s->var_values[VAR_POS]   = NAN;
+
+    if ((ret = set_expr(&s->x_pexpr,      s->x_expr,      "x",      ctx)) < 0 ||
+        (ret = set_expr(&s->y_pexpr,      s->y_expr,      "y",      ctx)) < 0)
+        return ret;
+
+    if (s->eval_mode == EVAL_MODE_INIT) {
+        eval_expr(ctx);
+        av_log(ctx, AV_LOG_VERBOSE, "x:%f xi:%d y:%f yi:%d\n",
+               s->var_values[VAR_X], s->x,
+               s->var_values[VAR_Y], s->y);
+    }
+
+    av_log(ctx, AV_LOG_VERBOSE,
+           "main w:%d h:%d fmt:%s overlay w:%d h:%d fmt:%s\n",
+           ctx->inputs[MAIN]->w, ctx->inputs[MAIN]->h,
+           av_get_pix_fmt_name(ctx->inputs[MAIN]->format),
+           ctx->inputs[OVERLAY]->w, ctx->inputs[OVERLAY]->h,
+           av_get_pix_fmt_name(ctx->inputs[OVERLAY]->format));
+    return 0;
+}
+
+static int do_blend(FFFrameSync *fs)
+{
+    AVFilterContext *ctx = fs->parent;
+    AVFrame *mainpic, *second;
+    OverlaySubsContext *s = ctx->priv;
+    AVFilterLink *inlink = ctx->inputs[0];
+    unsigned i;
+    int ret;
+
+    ret = ff_framesync_dualinput_get_writable(fs, &mainpic, &second);
+    if (ret < 0)
+        return ret;
+    if (!second)
+        return ff_filter_frame(ctx->outputs[0], mainpic);
+
+    if (s->eval_mode == EVAL_MODE_FRAME) {
+        int64_t pos = mainpic->pkt_pos;
+
+        s->var_values[VAR_N] = (double)inlink->frame_count_out;
+        s->var_values[VAR_T] = mainpic->pts == AV_NOPTS_VALUE ?
+            NAN :(double)mainpic->pts * av_q2d(inlink->time_base);
+        s->var_values[VAR_POS] = pos == -1 ? NAN : (double)pos;
+
+        s->var_values[VAR_OVERLAY_W] = s->var_values[VAR_OW] = second->width;
+        s->var_values[VAR_OVERLAY_H] = s->var_values[VAR_OH] = second->height;
+        s->var_values[VAR_MAIN_W   ] = s->var_values[VAR_MW] = mainpic->width;
+        s->var_values[VAR_MAIN_H   ] = s->var_values[VAR_MH] = mainpic->height;
+
+        eval_expr(ctx);
+        av_log(ctx, AV_LOG_DEBUG, "n:%f t:%f pos:%f x:%f xi:%d y:%f yi:%d\n",
+               s->var_values[VAR_N], s->var_values[VAR_T], s->var_values[VAR_POS],
+               s->var_values[VAR_X], s->x,
+               s->var_values[VAR_Y], s->y);
+    }
+
+    for (i = 0; i < second->num_subtitle_areas; i++) {
+        const AVSubtitleArea *sub_area = second->subtitle_areas[i];
+
+        if (sub_area->type != AV_SUBTITLE_FMT_BITMAP) {
+            av_log(NULL, AV_LOG_WARNING, "overlay_graphicsubs: non-bitmap subtitle\n");
+            return AVERROR_INVALIDDATA;
+        }
+
+        switch (inlink->format) {
+        case AV_PIX_FMT_YUV420P:
+            blend_yuv_8_8bits(ctx, mainpic, sub_area, 1, 1, sub_area->x + s->x, sub_area->y + s->y);
+            break;
+        case AV_PIX_FMT_YUV422P:
+            blend_yuv_8_8bits(ctx, mainpic, sub_area, 1, 0, sub_area->x + s->x, sub_area->y + s->y);
+            break;
+        case AV_PIX_FMT_YUV444P:
+            blend_yuv_8_8bits(ctx, mainpic, sub_area, 0, 0, sub_area->x + s->x, sub_area->y + s->y);
+            break;
+        case AV_PIX_FMT_RGB24:
+        case AV_PIX_FMT_BGR24:
+        case AV_PIX_FMT_ARGB:
+        case AV_PIX_FMT_RGBA:
+        case AV_PIX_FMT_BGRA:
+        case AV_PIX_FMT_ABGR:
+            blend_packed_rgb(ctx, mainpic, sub_area, sub_area->x + s->x, sub_area->y + s->y, 1);
+            break;
+        default:
+            av_log(NULL, AV_LOG_ERROR, "Unsupported input pix fmt: %d\n", inlink->format);
+            return AVERROR(EINVAL);
+        }
+    }
+
+    return ff_filter_frame(ctx->outputs[0], mainpic);
+}
+
+static av_cold int overlay_graphicsubs_init(AVFilterContext *ctx)
+{
+    OverlaySubsContext *s = ctx->priv;
+
+    s->fs.on_event = do_blend;
+    return 0;
+}
+
+static int overlay_graphicsubs_activate(AVFilterContext *ctx)
+{
+    OverlaySubsContext *s = ctx->priv;
+    return ff_framesync_activate(&s->fs);
+}
+
+static int graphicsub2video_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_BITMAP, AV_SUBTITLE_FMT_NONE };
+    static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_RGB32, AV_PIX_FMT_NONE };
+    int ret;
+
+    /* set input subtitle formats */
+    formats = ff_make_format_list(subtitle_fmts);
+    if ((ret = ff_formats_ref(formats, &inlink->outcfg.formats)) < 0)
+        return ret;
+
+    /* set output video formats */
+    formats = ff_make_format_list(pix_fmts);
+    if ((ret = ff_formats_ref(formats, &outlink->incfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int graphicsub2video_config_input(AVFilterLink *inlink)
+{
+    AVFilterContext *ctx = inlink->dst;
+    OverlaySubsContext *s = ctx->priv;
+
+    if (s->w <= 0 || s->h <= 0) {
+        s->w = inlink->w;
+        s->h = inlink->h;
+    }
+    return 0;
+}
+
+static int graphicsub2video_config_output(AVFilterLink *outlink)
+{
+    const AVFilterContext *ctx  = outlink->src;
+    OverlaySubsContext *s = ctx->priv;
+    const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(outlink->format);
+
+    outlink->w = s->w;
+    outlink->h = s->h;
+
+    if (outlink->w == 0 && outlink->h == 0) {
+        outlink->w = 1;
+        outlink->h = 1;
+    }
+    outlink->sample_aspect_ratio = (AVRational){1,1};
+    outlink->time_base = ctx->inputs[0]->time_base;
+    outlink->frame_rate = ctx->inputs[0]->frame_rate;
+
+    av_image_fill_max_pixsteps(s->main_pix_step, NULL, pix_desc);
+    ff_fill_rgba_map(s->overlay_rgba_map, AV_PIX_FMT_RGB32);
+
+    s->hsub = pix_desc->log2_chroma_w;
+    s->vsub = pix_desc->log2_chroma_h;
+
+    s->main_desc = pix_desc;
+
+    s->main_is_packed_rgb = ff_fill_rgba_map(s->main_rgba_map, outlink->format) >= 0;
+    s->main_has_alpha = !!(pix_desc->flags & AV_PIX_FMT_FLAG_ALPHA);
+
+    return 0;
+}
+
+static int graphicsub2video_filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFilterLink *outlink = inlink->dst->outputs[0];
+    const AVFilterContext *ctx  = outlink->src;
+    OverlaySubsContext *s = ctx->priv;
+    AVFrame *out;
+    const unsigned num_rects = frame->num_subtitle_areas;
+    unsigned int i;
+    int ret;
+
+    if (s->use_caching && s->cache_frame && frame->repeat_sub
+        && s->cache_frame->subtitle_timing.start_pts == frame->subtitle_timing.start_pts) {
+        out = av_frame_clone(s->cache_frame);
+        if (!out)
+            return AVERROR(ENOMEM);
+
+        ret = av_frame_copy_props(out, frame);
+        if (ret < 0)
+            return ret;
+
+        av_log(inlink->dst, AV_LOG_DEBUG, "graphicsub2video CACHED - size %dx%d  pts: %"PRId64"  areas: %d\n", frame->width, frame->height, frame->subtitle_timing.start_pts, frame->num_subtitle_areas);
+        av_frame_free(&frame);
+        return ff_filter_frame(outlink, out);
+    }
+
+
+    out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
+    if (!out) {
+        av_frame_free(&frame);
+        return AVERROR(ENOMEM);
+    }
+
+    memset(out->data[0], 0, (size_t)out->linesize[0] * out->height);
+
+    out->pts = out->pkt_dts = out->best_effort_timestamp = frame->pts;
+    out->coded_picture_number = out->display_picture_number = s->pic_counter++;
+
+    for (i = 0; i < num_rects; i++) {
+        const AVSubtitleArea  *sub_rect = frame->subtitle_areas[i];
+
+        if (sub_rect->type != AV_SUBTITLE_FMT_BITMAP) {
+            av_log(NULL, AV_LOG_WARNING, "graphicsub2video: non-bitmap subtitle\n");
+            av_frame_free(&frame);
+            return AVERROR_INVALIDDATA;
+        }
+
+        blend_packed_rgb(inlink->dst, out, sub_rect, sub_rect->x, sub_rect->y, 1);
+    }
+
+    av_log(inlink->dst, AV_LOG_DEBUG, "graphicsub2video output - size %dx%d  pts: %"PRId64"  areas: %d\n", out->width, out->height, out->pts, frame->num_subtitle_areas);
+
+    if (s->use_caching) {
+        av_frame_free(&s->cache_frame);
+        s->cache_frame = av_frame_clone(out);
+    }
+
+    av_frame_free(&frame);
+    return ff_filter_frame(outlink, out);
+}
+
+#define OFFSET(x) offsetof(OverlaySubsContext, x)
+#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption overlaygraphicsubs_options[] = {
+    { "x", "set the x expression", OFFSET(x_expr), AV_OPT_TYPE_STRING, {.str = "0"}, 0, 0, .flags = FLAGS },
+    { "y", "set the y expression", OFFSET(y_expr), AV_OPT_TYPE_STRING, {.str = "0"}, 0, 0, .flags = FLAGS },
+    { "eof_action", "Action to take when encountering EOF from secondary input ",
+        OFFSET(fs.opt_eof_action), AV_OPT_TYPE_INT, { .i64 = EOF_ACTION_REPEAT },
+        EOF_ACTION_REPEAT, EOF_ACTION_PASS, .flags = FLAGS, "eof_action" },
+        { "repeat", "Repeat the previous frame.",   0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_REPEAT }, .flags = FLAGS, "eof_action" },
+        { "endall", "End both streams.",            0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_ENDALL }, .flags = FLAGS, "eof_action" },
+        { "pass",   "Pass through the main input.", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_PASS },   .flags = FLAGS, "eof_action" },
+    { "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, {.i64 = EVAL_MODE_FRAME}, 0, EVAL_MODE_NB-1, FLAGS, "eval" },
+         { "init",  "eval expressions once during initialization", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_INIT},  .flags = FLAGS, .unit = "eval" },
+         { "frame", "eval expressions per-frame",                  0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_FRAME}, .flags = FLAGS, .unit = "eval" },
+    { "shortest", "force termination when the shortest input terminates", OFFSET(fs.opt_shortest), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, .flags = FLAGS },
+    { "repeatlast", "repeat overlay of the last overlay frame", OFFSET(fs.opt_repeatlast), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, .flags = FLAGS },
+    { NULL }
+};
+
+static const AVOption graphicsub2video_options[] = {
+    { "size",        "set output frame size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, .flags = FLAGS },
+    { "s",           "set output frame size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, .flags = FLAGS },
+    { "use_caching", "Cache output frames",   OFFSET(use_caching), AV_OPT_TYPE_BOOL, { .i64 = 1}, 0, 1, .flags = FLAGS },
+    { NULL }
+};
+
+FRAMESYNC_DEFINE_CLASS(overlaygraphicsubs, OverlaySubsContext, fs);
+
+static const AVFilterPad overlaygraphicsubs_inputs[] = {
+    {
+        .name         = "main",
+        .type         = AVMEDIA_TYPE_VIDEO,
+        .config_props = config_input_main,
+        .flags        = AVFILTERPAD_FLAG_NEEDS_WRITABLE,
+    },
+    {
+        .name         = "overlay",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+    },
+};
+
+static const AVFilterPad overlaygraphicsubs_outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_VIDEO,
+        .config_props  = config_output,
+    },
+};
+
+const AVFilter ff_vf_overlaygraphicsubs = {
+    .name          = "overlaygraphicsubs",
+    .description   = NULL_IF_CONFIG_SMALL("Overlay graphical subtitles on top of the input."),
+    .preinit       = overlaygraphicsubs_framesync_preinit,
+    .init          = overlay_graphicsubs_init,
+    .uninit        = overlay_graphicsubs_uninit,
+    .priv_size     = sizeof(OverlaySubsContext),
+    .priv_class    = &overlaygraphicsubs_class,
+    .activate      = overlay_graphicsubs_activate,
+    FILTER_INPUTS(overlaygraphicsubs_inputs),
+    FILTER_OUTPUTS(overlaygraphicsubs_outputs),
+    FILTER_QUERY_FUNC(overlay_graphicsubs_query_formats),
+};
+
+AVFILTER_DEFINE_CLASS(graphicsub2video);
+
+static const AVFilterPad graphicsub2video_inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .filter_frame = graphicsub2video_filter_frame,
+        .config_props = graphicsub2video_config_input,
+    },
+};
+
+static const AVFilterPad graphicsub2video_outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_VIDEO,
+        .config_props  = graphicsub2video_config_output,
+    },
+};
+
+const AVFilter ff_svf_graphicsub2video = {
+    .name          = "graphicsub2video",
+    .description   = NULL_IF_CONFIG_SMALL("Convert graphical subtitles to video"),
+    .priv_size     = sizeof(OverlaySubsContext),
+    .priv_class    = &graphicsub2video_class,
+    FILTER_INPUTS(graphicsub2video_inputs),
+    FILTER_OUTPUTS(graphicsub2video_outputs),
+    FILTER_QUERY_FUNC(graphicsub2video_query_formats),
+};
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH 14/24] avfilter/overlaytextsubs: Add overlaytextsubs and textsubs2video filters
  2022-01-14  1:13 [FFmpeg-devel] [PATCH 00/24] Subtitle Filtering 2022 ffmpegagent
                   ` (12 preceding siblings ...)
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 13/24] avfilter/overlaygraphicsubs: Add overlaygraphicsubs and graphicsub2video filters ffmpegagent
@ 2022-01-14  1:13 ` ffmpegagent
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 15/24] avfilter/textmod: Add textmod, censor and show_speaker filters ffmpegagent
                   ` (10 subsequent siblings)
  24 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-14  1:13 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: softworkz

From: softworkz <softworkz@hotmail.com>

- overlaytextsubs {VS -> V)
  Overlay text subtitles onto a video stream.

- textsubs2video {S -> V)
  Converts text subtitles to video frames

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 configure                        |   2 +
 doc/filters.texi                 | 113 ++++++
 libavfilter/Makefile             |   2 +
 libavfilter/allfilters.c         |   4 +-
 libavfilter/vf_overlaytextsubs.c | 671 +++++++++++++++++++++++++++++++
 5 files changed, 791 insertions(+), 1 deletion(-)
 create mode 100644 libavfilter/vf_overlaytextsubs.c

diff --git a/configure b/configure
index 7c2931bd50..340a198fa6 100755
--- a/configure
+++ b/configure
@@ -3691,6 +3691,7 @@ overlay_opencl_filter_deps="opencl"
 overlay_qsv_filter_deps="libmfx"
 overlay_qsv_filter_select="qsvvpp"
 overlay_vulkan_filter_deps="vulkan spirv_compiler"
+overlaytextsubs_filter_deps="avcodec libass"
 owdenoise_filter_deps="gpl"
 pad_opencl_filter_deps="opencl"
 pan_filter_deps="swresample"
@@ -3735,6 +3736,7 @@ superequalizer_filter_deps="avcodec"
 superequalizer_filter_select="rdft"
 surround_filter_deps="avcodec"
 surround_filter_select="rdft"
+textsub2video_filter_deps="avcodec libass"
 tinterlace_filter_deps="gpl"
 tinterlace_merge_test_deps="tinterlace_filter"
 tinterlace_pad_test_deps="tinterlace_filter"
diff --git a/doc/filters.texi b/doc/filters.texi
index 4fc4a57dbb..51771f88e4 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -25829,6 +25829,119 @@ Overlay PGS subtitles
 ffmpeg -i "https://streams.videolan.org/samples/sub/PGS/Girl_With_The_Dragon_Tattoo_2%3A23%3A56.mkv" -filter_complex "[0:0][0:1]overlaygraphicsubs" output.mp4
 @end example
 @end itemize
+
+@section overlaytextsubs
+
+Overlay text subtitles onto a video stream.
+
+This filter supersedes the classic @ref{subtitles} filter opposed to which it does no longer require to open and access the source stream separately, which is often causing problems or doesn't even work for non-local or slow sources.
+
+Inputs:
+@itemize
+@item 0: Video [YUV420P, YUV422P, YUV444P, ARGB, RGBA, ABGR, BGRA, RGB24, BGR24]
+@item 1: Subtitles [TEXT]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Video (same as input)
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+
+@item alpha
+Process alpha channel, by default alpha channel is untouched.
+
+@item fonts_dir
+Set a directory path containing fonts that can be used by the filter.
+These fonts will be used in addition to whatever the font provider uses.
+
+@item default_font_path
+Path to a font file to be used as the default font.
+
+@item font_size
+Set the default font size.
+
+@item fontconfig_file
+Path to ASS fontconfig configuration file.
+
+@item force_style
+Override default style or script info parameters of the subtitles. It accepts a
+string containing ASS style format @code{KEY=VALUE} couples separated by ",".
+
+@item margin
+Set the rendering margin in pixels.
+
+@item render_latest_only
+For rendering, alway use the latest event only, which is covering the given point in time
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Overlay ASS subtitles with animations:
+@example
+ffmpeg -i "http://streams.videolan.org/samples/sub/SSA/subtitle_testing_complex.mkv" -filter_complex "[0:v]overlaytextsubs" -map 0 -y out.mkv
+@end example
+@end itemize
+
+@section textsub2video
+
+Converts text subtitles to video frames.
+
+For overlaying text subtitles onto video frames it is recommended to use the overlay_textsubs filter.
+The textsub2video is useful for for creating transparent text-frames when overlay is done via hw acceleration
+
+Inputs:
+@itemize
+@item 0: Subtitles [TEXT]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Video [RGB32]
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+
+@item rate, r
+Set the framerate for updating overlay frames.
+Normally, overlay frames will only be updated each time when the subtitles to display are changing.
+In cases where subtitles include advanced features (like animation), this parameter determines the frequency by which the overlay frames should be updated.
+
+@item size, s
+Set the output frame size.
+Allows to override the size of output video frames.
+
+@item fonts_dir
+Set a directory path containing fonts that can be used by the filter.
+These fonts will be used in addition to whatever the font provider uses.
+
+@item default_font_path
+Path to a font file to be used as the default font.
+
+@item font_size
+Set the default font size.
+
+@item fontconfig_file
+Path to ASS fontconfig configuration file.
+
+@item force_style
+Override default style or script info parameters of the subtitles. It accepts a
+string containing ASS style format @code{KEY=VALUE} couples separated by ",".
+
+@item margin
+Set the rendering margin in pixels.
+
+@item render_latest_only
+For rendering, alway use the latest event only, which is covering the given point in time.
+@end table
+
 @c man end SUBTITLE FILTERS
 
 @chapter Multimedia Filters
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index a372effc12..48c9182c55 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -380,6 +380,7 @@ OBJS-$(CONFIG_OVERLAY_OPENCL_FILTER)         += vf_overlay_opencl.o opencl.o \
 OBJS-$(CONFIG_OVERLAY_QSV_FILTER)            += vf_overlay_qsv.o framesync.o
 OBJS-$(CONFIG_OVERLAY_VULKAN_FILTER)         += vf_overlay_vulkan.o vulkan.o vulkan_filter.o
 OBJS-$(CONFIG_OVERLAYGRAPHICSUBS_FILTER)     += vf_overlaygraphicsubs.o framesync.o
+OBJS-$(CONFIG_OVERLAYTEXTSUBS_FILTER)        += vf_overlaytextsubs.o
 OBJS-$(CONFIG_OWDENOISE_FILTER)              += vf_owdenoise.o
 OBJS-$(CONFIG_PAD_FILTER)                    += vf_pad.o
 OBJS-$(CONFIG_PAD_OPENCL_FILTER)             += vf_pad_opencl.o opencl.o opencl/pad.o
@@ -469,6 +470,7 @@ OBJS-$(CONFIG_SWAPRECT_FILTER)               += vf_swaprect.o
 OBJS-$(CONFIG_SWAPUV_FILTER)                 += vf_swapuv.o
 OBJS-$(CONFIG_TBLEND_FILTER)                 += vf_blend.o framesync.o
 OBJS-$(CONFIG_TELECINE_FILTER)               += vf_telecine.o
+OBJS-$(CONFIG_TEXTSUB2VIDEO_FILTER)          += vf_overlaytextsubs.o
 OBJS-$(CONFIG_THISTOGRAM_FILTER)             += vf_histogram.o
 OBJS-$(CONFIG_THRESHOLD_FILTER)              += vf_threshold.o framesync.o
 OBJS-$(CONFIG_THUMBNAIL_FILTER)              += vf_thumbnail.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index eb4e30a270..9ba89db128 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -358,9 +358,10 @@ extern const AVFilter ff_vf_oscilloscope;
 extern const AVFilter ff_vf_overlay;
 extern const AVFilter ff_vf_overlay_opencl;
 extern const AVFilter ff_vf_overlay_qsv;
-extern const AVFilter ff_vf_overlaygraphicsubs;
 extern const AVFilter ff_vf_overlay_vulkan;
 extern const AVFilter ff_vf_overlay_cuda;
+extern const AVFilter ff_vf_overlaygraphicsubs;
+extern const AVFilter ff_vf_overlaytextsubs;
 extern const AVFilter ff_vf_owdenoise;
 extern const AVFilter ff_vf_pad;
 extern const AVFilter ff_vf_pad_opencl;
@@ -547,6 +548,7 @@ extern const AVFilter ff_avf_showwaves;
 extern const AVFilter ff_avf_showwavespic;
 extern const AVFilter ff_vaf_spectrumsynth;
 extern const AVFilter ff_svf_graphicsub2video;
+extern const AVFilter ff_svf_textsub2video;
 
 /* multimedia sources */
 extern const AVFilter ff_avsrc_amovie;
diff --git a/libavfilter/vf_overlaytextsubs.c b/libavfilter/vf_overlaytextsubs.c
new file mode 100644
index 0000000000..efe2684dac
--- /dev/null
+++ b/libavfilter/vf_overlaytextsubs.c
@@ -0,0 +1,671 @@
+/*
+ * 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
+ * overlay text subtitles on top of a video frame
+ */
+
+#include <ass/ass.h>
+#include "libavutil/ass_internal.h"
+#include "libavutil/thread.h"
+
+#include "drawutils.h"
+#include "filters.h"
+
+#include "libavutil/opt.h"
+#include "libavutil/pixdesc.h"
+
+typedef struct TextSubsContext {
+    const AVClass *class;
+    AVMutex mutex;
+    int is_mutex_initialized;
+
+    ASS_Library   *library;
+    ASS_Renderer  *renderer;
+    ASS_Track     *track;
+
+    char *default_font_path;
+    char *fonts_dir;
+    char *fc_file;
+    double font_size;
+    char *force_style;
+    char *language;
+    int margin;
+    int render_latest_only;
+
+    int alpha;
+    FFDrawContext draw;
+
+    int got_header;
+    int out_w, out_h;
+    AVRational frame_rate;
+    AVFrame *last_frame;
+    int need_frame;
+    int eof;
+} TextSubsContext;
+
+/* libass supports a log level ranging from 0 to 7 */
+static const int ass_libavfilter_log_level_map[] = {
+    AV_LOG_QUIET,               /* 0 */
+    AV_LOG_PANIC,               /* 1 */
+    AV_LOG_FATAL,               /* 2 */
+    AV_LOG_ERROR,               /* 3 */
+    AV_LOG_WARNING,             /* 4 */
+    AV_LOG_INFO,                /* 5 */
+    AV_LOG_VERBOSE,             /* 6 */
+    AV_LOG_DEBUG,               /* 7 */
+};
+
+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 void uninit(AVFilterContext *ctx)
+{
+    TextSubsContext *s = ctx->priv;
+
+    if (s->track)
+        ass_free_track(s->track);
+    if (s->renderer)
+        ass_renderer_done(s->renderer);
+    if (s->library)
+        ass_library_done(s->library);
+
+    s->track = NULL;
+    s->renderer = NULL;
+    s->library = NULL;
+
+    if (s->is_mutex_initialized) {
+        ff_mutex_destroy(&s->mutex);
+        s->is_mutex_initialized = 0;
+    }
+
+    av_frame_free(&s->last_frame);
+}
+
+static int overlay_textsubs_query_formats(AVFilterContext *ctx)
+{
+    AVFilterFormats *formats;
+    AVFilterLink *inlink0 = ctx->inputs[0];
+    AVFilterLink *inlink1 = ctx->inputs[1];
+    AVFilterLink *outlink = ctx->outputs[0];
+    static const enum AVSubtitleType subtitle_fmts[] = { AV_SUBTITLE_FMT_ASS, AV_SUBTITLE_FMT_NONE };
+    int ret;
+
+    /* set input0 and output0 video formats */
+    formats = ff_draw_supported_pixel_formats(0);
+    if ((ret = ff_formats_ref(formats, &inlink0->outcfg.formats)) < 0)
+        return ret;
+    if ((ret = ff_formats_ref(formats, &outlink->incfg.formats)) < 0)
+        return ret;
+
+    /* set input1 subtitle formats */
+    formats = ff_make_format_list(subtitle_fmts);
+    if ((ret = ff_formats_ref(formats, &inlink1->outcfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+
+    outlink->w = ctx->inputs[0]->w;
+    outlink->h = ctx->inputs[0]->h;
+    outlink->time_base = ctx->inputs[0]->time_base;
+    outlink->frame_rate = ctx->inputs[0]->frame_rate;
+
+    return 0;
+}
+
+static int config_input_main(AVFilterLink *inlink)
+{
+    AVFilterContext *ctx  = inlink->dst;
+    TextSubsContext *s = inlink->dst->priv;
+    int ret;
+
+    ret = ff_draw_init(&s->draw, inlink->format, s->alpha ? FF_DRAW_PROCESS_ALPHA : 0);
+    if (ret < 0) {
+        av_log(ctx, AV_LOG_ERROR, "Could not initialize ff_draw.\n");
+        return ret;
+    }
+
+    ass_set_frame_size  (s->renderer, inlink->w, inlink->h);
+    ass_set_pixel_aspect(s->renderer, av_q2d(inlink->sample_aspect_ratio));
+
+    av_log(ctx, AV_LOG_VERBOSE, "Subtitle screen: %dx%d\n\n\n\n", inlink->w, inlink->h);
+
+    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(TextSubsContext *s, 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(&s->draw, &color, rgba_color);
+        ff_blend_mask(&s->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 void process_header(AVFilterContext *link, AVFrame *frame)
+{
+    TextSubsContext *s = link->priv;
+    ASS_Track *track = s->track;
+    ASS_Style *style;
+    int sid = 0;
+
+    if (!track)
+        return;
+
+    if (frame && frame->subtitle_header) {
+        char *subtitle_header = (char *)frame->subtitle_header->data;
+        ass_process_codec_private(s->track, subtitle_header, strlen(subtitle_header));
+    }
+    else {
+        char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+        if (!subtitle_header)
+            return;
+
+        ass_process_codec_private(s->track, subtitle_header, strlen(subtitle_header));
+        av_free(subtitle_header);
+    }
+
+    if (s->language)
+        s->track->Language = av_strdup(s->language);
+
+    if (!s->track->event_format) {
+        s->track->event_format = av_strdup("ReadOrder, Layer, Style, Name, MarginL, MarginR, MarginV, Effect, Text");
+    }
+
+    if (s->track->n_styles == 0) {
+        sid = ass_alloc_style(track);
+        style = &s->track->styles[sid];
+        style->Name             = av_strdup("Default");
+        style->PrimaryColour    = 0xffffff00;
+        style->SecondaryColour  = 0x00ffff00;
+        style->OutlineColour    = 0x00000000;
+        style->BackColour       = 0x00000080;
+        style->Bold             = 200;
+        style->ScaleX           = 1.0;
+        style->ScaleY           = 1.0;
+        style->Spacing          = 0;
+        style->BorderStyle      = 1;
+        style->Outline          = 2;
+        style->Shadow           = 3;
+        style->Alignment        = 2;
+    }
+    else
+        style = &s->track->styles[sid];
+
+    style->FontSize         = s->font_size;
+    style->MarginL = style->MarginR = style->MarginV = s->margin;
+
+    track->default_style = sid;
+
+    s->got_header = 1;
+}
+
+static int filter_video_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFilterContext *ctx = inlink->dst;
+    TextSubsContext *s = ctx->priv;
+    int detect_change = 0;
+    ASS_Image *image;
+
+    ff_request_frame(ctx->inputs[1]);
+
+    int64_t time_ms = (int64_t)((double)frame->pts * av_q2d(inlink->time_base) * 1000);
+
+    ff_mutex_lock(&s->mutex);
+    image = ass_render_frame(s->renderer, s->track, time_ms, &detect_change);
+    ff_mutex_unlock(&s->mutex);
+
+    if (detect_change)
+        av_log(ctx, AV_LOG_DEBUG, "Change happened at time ms:%"PRId64"\n", time_ms);
+
+    overlay_ass_image(s, frame, image);
+
+    return ff_filter_frame(ctx->outputs[0], frame);
+}
+
+static int filter_subtitle_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFilterContext *ctx = inlink->dst;
+    TextSubsContext *s = ctx->priv;
+    const int64_t start_time = av_rescale_q(frame->subtitle_timing.start_pts, AV_TIME_BASE_Q, av_make_q(1, 1000));
+    const int64_t duration   = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, av_make_q(1, 1000));
+
+    // Postpone header processing until we receive a frame with content
+    if (!s->got_header && frame->num_subtitle_areas > 0)
+        process_header(ctx, frame);
+
+    if (frame->repeat_sub)
+        goto exit;
+
+    ff_mutex_lock(&s->mutex);
+
+    if (s->render_latest_only && s->track->n_events > 0) {
+        const int64_t previous_start_time = s->track->events[s->track->n_events - 1].Start;
+        const int64_t diff = start_time - previous_start_time;
+        for (int i = s->track->n_events - 1; i >= 0; i--) {
+            if (previous_start_time != s->track->events[i].Start)
+                break;
+
+            if (s->track->events[i].Duration > diff)
+                s->track->events[i].Duration = diff;
+
+        }
+    }
+
+    for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+        char *ass_line = frame->subtitle_areas[i]->ass;
+        if (!ass_line)
+            continue;
+
+        ass_process_chunk(s->track, ass_line, strlen(ass_line), start_time, duration);
+    }
+
+    ff_mutex_unlock(&s->mutex);
+
+exit:
+    av_frame_free(&frame);
+    return 0;
+}
+
+static av_cold int init(AVFilterContext *ctx)
+{
+    int ret;
+    TextSubsContext *s = ctx->priv;
+
+    s->library = ass_library_init();
+
+    if (!s->library) {
+        av_log(ctx, AV_LOG_ERROR, "Could not initialize libass.\n");
+        return AVERROR(EINVAL);
+    }
+
+    ass_set_message_cb(s->library, ass_log, ctx);
+
+    /* Initialize fonts */
+    if (s->fonts_dir)
+        ass_set_fonts_dir(s->library, s->fonts_dir);
+
+    ass_set_extract_fonts(s->library, 1);
+
+    s->renderer = ass_renderer_init(s->library);
+    if (!s->renderer) {
+        av_log(ctx, AV_LOG_ERROR, "Could not initialize libass renderer.\n");
+        return AVERROR(EINVAL);
+    }
+
+    s->track = ass_new_track(s->library);
+    if (!s->track) {
+        av_log(ctx, AV_LOG_ERROR, "ass_new_track() failed!\n");
+        return AVERROR(EINVAL);
+    }
+
+    ass_set_check_readorder(s->track, 0);
+
+    ass_set_fonts(s->renderer, s->default_font_path, NULL, 1, s->fc_file, 1);
+
+    if (s->force_style) {
+        char **list = NULL;
+        char *temp = NULL;
+        char *ptr = av_strtok(s->force_style, ",", &temp);
+        int i = 0;
+        while (ptr) {
+            av_dynarray_add(&list, &i, ptr);
+            if (!list) {
+                return AVERROR(ENOMEM);
+            }
+            ptr = av_strtok(NULL, ",", &temp);
+        }
+        av_dynarray_add(&list, &i, NULL);
+        if (!list) {
+            return AVERROR(ENOMEM);
+        }
+        ass_set_style_overrides(s->library, list);
+        av_free(list);
+    }
+
+    ret = ff_mutex_init(&s->mutex, NULL);
+    if (ret) {
+        av_log(ctx, AV_LOG_ERROR, "mutex initialiuzation failed! Error code: %d\n", ret);
+        return AVERROR(EINVAL);
+    }
+
+    s->is_mutex_initialized = 1;
+
+    return ret;
+}
+
+static int textsub2video_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 input0 subtitle format */
+    formats = ff_make_format_list(subtitle_fmts);
+    if ((ret = ff_formats_ref(formats, &inlink->outcfg.formats)) < 0)
+        return ret;
+
+    /* set output0 video format */
+    formats = ff_draw_supported_pixel_formats(AV_PIX_FMT_FLAG_ALPHA);
+    if ((ret = ff_formats_ref(formats, &outlink->incfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int textsub2video_config_input(AVFilterLink *inlink)
+{
+    AVFilterContext *ctx = inlink->dst;
+    TextSubsContext *s = ctx->priv;
+
+    if (s->out_w <= 0 || s->out_h <= 0) {
+        s->out_w = inlink->w;
+        s->out_h = inlink->h;
+    }
+
+    return 0;
+}
+
+static int textsub2video_config_output(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    TextSubsContext *s = ctx->priv;
+    int ret;
+
+    ret = ff_draw_init(&s->draw, outlink->format, FF_DRAW_PROCESS_ALPHA);
+    if (ret < 0) {
+        av_log(ctx, AV_LOG_ERROR, "Could not initialize ff_draw.\n");
+        return ret;
+    }
+
+    if (s->out_w <= 0 || s->out_h <= 0) {
+        av_log(ctx, AV_LOG_ERROR, "No output image size set.\n");
+        return AVERROR(EINVAL);
+    }
+
+    ass_set_frame_size  (s->renderer, s->out_w, s->out_h);
+
+    outlink->w = s->out_w;
+    outlink->h = s->out_h;
+    outlink->sample_aspect_ratio = (AVRational){1,1};
+    outlink->frame_rate = s->frame_rate;
+
+    return 0;
+}
+
+static int textsub2video_request_frame(AVFilterLink *outlink)
+{
+    TextSubsContext *s = outlink->src->priv;
+    AVFilterLink *inlink = outlink->src->inputs[0];
+    int64_t last_pts = outlink->current_pts;
+    int64_t next_pts, time_ms;
+    int i, detect_change = 0, status;
+    AVFrame *out;
+    ASS_Image *image;
+
+    status = ff_outlink_get_status(inlink);
+    if (status == AVERROR_EOF)
+        return AVERROR_EOF;
+
+    if (s->eof)
+        return AVERROR_EOF;
+
+    if (inlink->current_pts == AV_NOPTS_VALUE) { // || outlink->current_pts > inlink->current_pts) {
+        int ret = ff_request_frame(inlink);
+        if (ret == AVERROR_EOF) {
+            s->eof = 1;
+        }
+
+        if (ret != 0)
+            av_log(outlink->src, AV_LOG_DEBUG, "ff_request_frame returned: %d\n", ret);
+
+        s->need_frame = 1;
+        return 0;
+    }
+
+    if (last_pts == AV_NOPTS_VALUE)
+        next_pts = last_pts = inlink->current_pts * av_q2d(inlink->time_base) / av_q2d(outlink->time_base);
+    else
+        next_pts = last_pts + (int64_t)(1.0 / av_q2d(outlink->frame_rate) / av_q2d(outlink->time_base));
+
+    time_ms = (int64_t)((double)next_pts * av_q2d(outlink->time_base) * 1000);
+
+    ff_mutex_lock(&s->mutex);
+    image = ass_render_frame(s->renderer, s->track, time_ms, &detect_change);
+    ff_mutex_unlock(&s->mutex);
+
+    if (detect_change)
+        av_log(outlink->src, AV_LOG_VERBOSE, "Change happened at time ms:%"PRId64" pts:%"PRId64"\n", time_ms, next_pts);
+    else if (s->last_frame) {
+        out = av_frame_clone(s->last_frame);
+        if (!out)
+            return AVERROR(ENOMEM);
+
+        out->pts = out->pkt_dts = out->best_effort_timestamp = next_pts;
+        return ff_filter_frame(outlink, out);
+    }
+
+    out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
+    if (!out)
+        return AVERROR(ENOMEM);
+
+    for (i = 0; i < AV_NUM_DATA_POINTERS; i++) {
+        if (out->buf[i] && i != 1)
+            memset(out->buf[i]->data, 0, out->buf[i]->size);
+    }
+
+    out->pts = out->pkt_dts = out->best_effort_timestamp = next_pts;
+
+    if (image)
+        overlay_ass_image(s, out, image);
+
+    av_frame_free(&s->last_frame);
+
+    s->last_frame = av_frame_clone(out);
+
+    return ff_filter_frame(outlink, out);
+}
+
+static int textsub2video_filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFilterContext *ctx = inlink->dst;
+    TextSubsContext *s = ctx->priv;
+    const int64_t start_time = av_rescale_q(frame->subtitle_timing.start_pts, AV_TIME_BASE_Q, av_make_q(1, 1000));
+    const int64_t duration   = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, av_make_q(1, 1000));
+
+    av_log(ctx, AV_LOG_VERBOSE, "textsub2video_filter_frame num_subtitle_rects: %d, start_time_ms: %"PRId64"\n", frame->num_subtitle_areas, start_time);
+
+    if (!s->got_header && frame->num_subtitle_areas > 0)
+        process_header(ctx, frame);
+
+    if (frame->repeat_sub)
+        goto exit;
+
+    ff_mutex_lock(&s->mutex);
+
+    if (s->render_latest_only && s->track->n_events > 0) {
+        const int64_t previous_start_time = s->track->events[s->track->n_events - 1].Start;
+        const int64_t diff = start_time - previous_start_time;
+        for (int i = s->track->n_events - 1; i >= 0; i--) {
+            if (previous_start_time != s->track->events[i].Start)
+                break;
+
+            if (s->track->events[i].Duration > diff)
+                s->track->events[i].Duration = diff;
+
+        }
+    }
+
+    for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+        char *ass_line = frame->subtitle_areas[i]->ass;
+        if (!ass_line)
+            continue;
+
+        ass_process_chunk(s->track, ass_line, strlen(ass_line), start_time, duration);
+    }
+
+
+    ff_mutex_unlock(&s->mutex);
+
+exit:
+    av_frame_free(&frame);
+
+    if (s->need_frame) {
+        s->need_frame = 0;
+        return textsub2video_request_frame(ctx->outputs[0]);
+    }
+
+    return 0;
+}
+
+#define OFFSET(x) offsetof(TextSubsContext, x)
+#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption overlaytextsubs_options[] = {
+    {"alpha",              "enable processing of alpha channel", OFFSET(alpha),              AV_OPT_TYPE_BOOL,   {.i64 = 0   }, 0,         1,        .flags =  FLAGS},
+    {"font_size",          "default font size",                  OFFSET(font_size),          AV_OPT_TYPE_DOUBLE, {.dbl = 18.0}, 0.0,       100.0,    .flags =  FLAGS},
+    {"force_style",        "force subtitle style",               OFFSET(force_style),        AV_OPT_TYPE_STRING, {.str = NULL}, 0,         0,        .flags =  FLAGS},
+    {"margin",             "default margin",                     OFFSET(margin),             AV_OPT_TYPE_INT,    {.i64 = 20  }, 0,         INT_MAX,  .flags =  FLAGS},
+    {"default_font_path",  "path to default font",               OFFSET(default_font_path),  AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"fonts_dir",          "directory to scan for fonts",        OFFSET(fonts_dir),          AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"fontsdir",           "directory to scan for fonts",        OFFSET(fonts_dir),          AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"fontconfig_file",    "fontconfig file to load",            OFFSET(fc_file),            AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"language",           "default language",                   OFFSET(language),           AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"render_latest_only", "newest sub event for each time",     OFFSET(render_latest_only), AV_OPT_TYPE_BOOL,   {.i64 = 0   }, 0,         1,        .flags =  FLAGS},
+    { .name = NULL }
+};
+
+static const AVOption textsub2video_options[] = {
+    {"rate",               "set frame rate",                   OFFSET(frame_rate),         AV_OPT_TYPE_VIDEO_RATE, {.str="8"},   0,         INT_MAX,   .flags =  FLAGS},
+    {"r",                  "set frame rate",                   OFFSET(frame_rate),         AV_OPT_TYPE_VIDEO_RATE, {.str="8"},   0,         INT_MAX,   .flags =  FLAGS},
+    {"size",               "set video size",                   OFFSET(out_w),              AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0,         0,        .flags =  FLAGS},
+    {"s",                  "set video size",                   OFFSET(out_w),              AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0,         0,        .flags =  FLAGS},
+    {"font_size",          "default font size",                OFFSET(font_size),          AV_OPT_TYPE_DOUBLE,     {.dbl = 18.0}, 0.0,       100.0,    .flags =  FLAGS},
+    {"force_style",        "force subtitle style",             OFFSET(force_style),        AV_OPT_TYPE_STRING,     {.str = NULL}, 0,         0,        .flags =  FLAGS},
+    {"margin",             "default margin",                   OFFSET(margin),             AV_OPT_TYPE_INT,        {.i64 = 20  }, 0,         INT_MAX,  .flags =  FLAGS},
+    {"default_font_path",  "path to default font",             OFFSET(default_font_path),  AV_OPT_TYPE_STRING,     {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"fonts_dir",          "directory to scan for fonts",      OFFSET(fonts_dir),          AV_OPT_TYPE_STRING,     {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"fontsdir",           "directory to scan for fonts",      OFFSET(fonts_dir),          AV_OPT_TYPE_STRING,     {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"fontconfig_file",    "fontconfig file to load",          OFFSET(fc_file),            AV_OPT_TYPE_STRING,     {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"language",           "default language",                 OFFSET(language),           AV_OPT_TYPE_STRING,     {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"render_latest_only", "newest sub event for each time",   OFFSET(render_latest_only), AV_OPT_TYPE_BOOL,       {.i64 = 0   }, 0,         1,        .flags =  FLAGS},
+    { .name = NULL }
+};
+
+#if CONFIG_OVERLAYTEXTSUBS_FILTER
+
+AVFILTER_DEFINE_CLASS(overlaytextsubs);
+
+static const AVFilterPad overlaytextsubs_inputs[] = {
+    {
+        .name         = "main",
+        .type         = AVMEDIA_TYPE_VIDEO,
+        .config_props = config_input_main,
+        .flags        = AVFILTERPAD_FLAG_NEEDS_WRITABLE,
+        .filter_frame = filter_video_frame,
+    },
+    {
+        .name         = "overlay",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .filter_frame = filter_subtitle_frame,
+    },
+};
+
+static const AVFilterPad overlaytextsubs_outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_VIDEO,
+        .config_props  = config_output,
+    },
+};
+
+const AVFilter ff_vf_overlaytextsubs = {
+    .name          = "overlaytextsubs",
+    .description   = NULL_IF_CONFIG_SMALL("Overlay textual subtitles on top of the input."),
+    .init          = init,
+    .uninit        = uninit,
+    .priv_size     = sizeof(TextSubsContext),
+    .priv_class    = &overlaytextsubs_class,
+    FILTER_INPUTS(overlaytextsubs_inputs),
+    FILTER_OUTPUTS(overlaytextsubs_outputs),
+    FILTER_QUERY_FUNC(overlay_textsubs_query_formats),
+};
+#endif
+
+#if CONFIG_TEXTSUB2VIDEO_FILTER
+
+AVFILTER_DEFINE_CLASS(textsub2video);
+
+static const AVFilterPad textsub2video_inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .config_props = textsub2video_config_input,
+        .filter_frame = textsub2video_filter_frame,
+    },
+};
+
+static const AVFilterPad textsub2video_outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_VIDEO,
+        .config_props  = textsub2video_config_output,
+        .request_frame = textsub2video_request_frame,
+    },
+};
+
+const AVFilter ff_svf_textsub2video = {
+    .name          = "textsub2video",
+    .description   = NULL_IF_CONFIG_SMALL("Convert textual subtitles to video frames"),
+    .init          = init,
+    .uninit        = uninit,
+    .priv_size     = sizeof(TextSubsContext),
+    .priv_class    = &textsub2video_class,
+    FILTER_INPUTS(textsub2video_inputs),
+    FILTER_OUTPUTS(textsub2video_outputs),
+    FILTER_QUERY_FUNC(textsub2video_query_formats),
+};
+#endif
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH 15/24] avfilter/textmod: Add textmod, censor and show_speaker filters
  2022-01-14  1:13 [FFmpeg-devel] [PATCH 00/24] Subtitle Filtering 2022 ffmpegagent
                   ` (13 preceding siblings ...)
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 14/24] avfilter/overlaytextsubs: Add overlaytextsubs and textsubs2video filters ffmpegagent
@ 2022-01-14  1:13 ` ffmpegagent
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 16/24] avfilter/stripstyles: Add stripstyles filter ffmpegagent
                   ` (9 subsequent siblings)
  24 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-14  1:13 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: softworkz

From: softworkz <softworkz@hotmail.com>

- textmod {S -> S)
  Modify subtitle text in a number of ways

- censor {S -> S)
  Censor subtitles using a word list

- show_speaker {S -> S)
  Prepend speaker names from ASS subtitles to the visible text lines

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 doc/filters.texi         | 206 ++++++++++++
 libavfilter/Makefile     |   5 +
 libavfilter/allfilters.c |   3 +
 libavfilter/sf_textmod.c | 710 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 924 insertions(+)
 create mode 100644 libavfilter/sf_textmod.c

diff --git a/doc/filters.texi b/doc/filters.texi
index 51771f88e4..494ee6f062 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -25721,6 +25721,145 @@ existing filters using @code{--disable-filters}.
 
 Below is a description of the currently available subtitle filters.
 
+
+@section censor
+
+Censor selected words in text subtitles.
+
+Inputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+@item mode
+The censoring mode to apply.
+
+Supported censoring modes are:
+
+@table @var
+@item 0, keep_first_last
+Replace all characters with the 'censor_char' except the first and the last character of a word.
+For words with less than 4 characters, the last character will be replaced as well.
+For words with less than 3 characters, the first character will be replaced as well.
+@item 1, keep_first
+Replace all characters with the 'censor_char' except the first character of a word.
+For words with less than 3 characters, the first character will be replaced as well.
+@item 2, all
+Replace all characters with the 'censor_char'.
+@end table
+
+@item words
+A list of words to censor, separated by 'separator'.
+
+@item words_file
+Specify a file from which to load the contents for the 'words' parameter.
+
+@item censor_char
+Single character used as replacement for censoring.
+
+@item separator
+Delimiter character for words. Used with replace_words and remove_words- Must be a single character.
+The default is '.'.
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Censor a few given words with a pound character.
+@example
+ffmpeg -i "http://streams.videolan.org/samples/sub/SSA/subtitle_testing_complex.mkv" -filter_complex "[0:1]censor=words='diss,louder,hope,beam,word':censor_char='#'" -map 0 -y output.mkv
+@end example
+@end itemize
+
+
+@section textmod
+
+Modify subtitle text in a number of ways.
+
+Inputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+@item mode
+The kind of text modification to apply
+
+Supported operation modes are:
+
+@table @var
+@item 0, leet
+Convert subtitle text to 'leet speak'. It's primarily useful for testing as the modification will be visible with almost all text lines.
+@item 1, to_upper
+Change all text to upper case. Might improve readability.
+@item 2, to_lower
+Change all text to lower case.
+@item 3, replace_chars
+Replace one or more characters. Requires the find and replace parameters to be specified.
+Both need to be equal in length.
+The first char in find is replaced by the first char in replace, same for all subsequent chars.
+@item 4, remove_chars
+Remove certain characters. Requires the find parameter to be specified.
+All chars in the find parameter string will be removed from all subtitle text.
+@item 5, replace_words
+Replace one or more words. Requires the find and replace parameters to be specified. Multiple words must be separated by the delimiter char specified vie the separator parameter (default: ',').
+The number of words in the find and replace parameters needs to be equal.
+The first word in find is replaced by the first word in replace, same for all subsequent words
+@item 6, remove_words
+Remove certain words. Requires the find parameter to be specified. Multiple words must be separated by the delimiter char specified vie the separator parameter (default: ',').
+All words in the find parameter string will be removed from all subtitle text.
+@end table
+
+@item find
+Required for replace_chars, remove_chars, replace_words and remove_words.
+
+@item find_file
+Specify a file from which to load the contents for the 'find' parameter.
+
+@item replace
+Required for replace_chars and replace_words.
+
+@item replace_file
+Specify a file from which to load the contents for the 'replace' parameter.
+
+@item separator
+Delimiter character for words. Used with replace_words and remove_words- Must be a single character.
+The default is '.'.
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Change all characters to upper case while keeping all styles and animations:
+@example
+ffmpeg -i "https://streams.videolan.org/ffmpeg/mkv_subtitles.mkv" -filter_complex "[0:s]textmod=mode=to_upper" -map 0 -y out.mkv
+@end example
+@item
+Remove a set of symbol characters for am improved and smoother visual apperance:
+@example
+ffmpeg -i "https://streams.videolan.org/ffmpeg/mkv_subtitles.mkv" -filter_complex "[0:s]textmod=mode=remove_chars:find='$&#@*§'" -map 0 -y out.mkv
+@end example
+@end itemize
+
 @section graphicsub2video
 
 Renders graphic subtitles as video frames.
@@ -25888,6 +26027,73 @@ ffmpeg -i "http://streams.videolan.org/samples/sub/SSA/subtitle_testing_complex.
 @end example
 @end itemize
 
+@section showspeaker
+
+Prepend speaker names to subtitle lines (when available).
+
+Subtitles in ASS/SSA format are often including the names of the persons
+or character for each subtitle line. The showspeaker filter adds those names
+to the actual subtitle text to make it visible on playback.
+
+Inputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+@item format
+The format for prepending speaker names. Default is 'square_brackets'.
+
+Supported operation modes are:
+
+@table @var
+@item 0, square_brackets
+Enclose the speaker name in square brackets, followed by space ('[speaker] text').
+@item 1, round_brackets
+Enclose the speaker name in round brackets, followed by space ('(speaker) text').
+@item 2, colon
+Separate the speaker name with a colon and space ('speaker: text').
+@item 3, plain
+Separate the speaker name with a space only ('speaker text').
+@end table
+
+@item line_break
+Set thís parameter to insert a line break between speaker name and text instead of the space character.
+
+@item style
+Allows to set a specific style for the speaker name text.
+
+This can be either a named style that exists in the ass subtitle header script (e.g. 'Default') or an ass style override code.
+Example:  @{\\c&HDD0000&\\be1\\i1\\bord10@}
+This sets the color to blue, enables edge blurring, italic font and a border of size 10.
+
+The behavior is as follows:
+
+- When the style parameter is not provided, the filter will find the first position in the event string that is actual text.
+  The speaker name will be inserted at this position. This allows to have the speaker name shown in the same style like the
+  regular text, in case the string would start with a sequence of style codes.
+- When the style parameter is provided, everything will be prepended to the original text:
+  Style Code or Style name >> Speaker Name >> Style Reset Code >> Original Text
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Prepend speaker names with blue text, smooth edges and blend/overlay that onto the video.
+@example
+ffmpeg -i INPUT -filter_complex "showspeaker=format=colon:style='@{\\c&HDD0000&\\be1@}',[0:v]overlay_textsubs"
+@end example
+@end itemize
+
 @section textsub2video
 
 Converts text subtitles to video frames.
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 48c9182c55..1af4f4b9bc 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -561,6 +561,11 @@ OBJS-$(CONFIG_YUVTESTSRC_FILTER)             += vsrc_testsrc.o
 
 OBJS-$(CONFIG_NULLSINK_FILTER)               += vsink_nullsink.o
 
+# subtitle filters
+OBJS-$(CONFIG_CENSOR_FILTER)                 += sf_textmod.o
+OBJS-$(CONFIG_SHOW_SPEAKER_FILTER)           += sf_textmod.o
+OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
+
 # multimedia filters
 OBJS-$(CONFIG_ABITSCOPE_FILTER)              += avf_abitscope.o
 OBJS-$(CONFIG_ADRAWGRAPH_FILTER)             += f_drawgraph.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 9ba89db128..ac7d71547b 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -547,6 +547,9 @@ 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_censor;
+extern const AVFilter ff_sf_showspeaker;
+extern const AVFilter ff_sf_textmod;
 extern const AVFilter ff_svf_graphicsub2video;
 extern const AVFilter ff_svf_textsub2video;
 
diff --git a/libavfilter/sf_textmod.c b/libavfilter/sf_textmod.c
new file mode 100644
index 0000000000..c571edc1d2
--- /dev/null
+++ b/libavfilter/sf_textmod.c
@@ -0,0 +1,710 @@
+/*
+ * 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 allows to modify subtitle text in several ways
+ */
+
+#include <libavutil/ass_internal.h>
+
+#include "libavutil/opt.h"
+#include "internal.h"
+#include "libavutil/ass_split_internal.h"
+#include "libavutil/bprint.h"
+#include "libavutil/file.h"
+
+static const char* leet_src = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+static const char* leet_dst = "abcd3f6#1jklmn0pq257uvwxyzAB(D3F6#1JKLMN0PQ257UVWXYZ";
+
+enum TextModFilterType {
+    TM_TEXTMOD,
+    TM_CENSOR,
+    TM_SHOW_SPEAKER,
+};
+
+enum TextModOperation {
+    OP_LEET,
+    OP_TO_UPPER,
+    OP_TO_LOWER,
+    OP_REPLACE_CHARS,
+    OP_REMOVE_CHARS,
+    OP_REPLACE_WORDS,
+    OP_REMOVE_WORDS,
+    NB_OPS,
+};
+
+enum CensorMode {
+    CM_KEEP_FIRST_LAST,
+    CM_KEEP_FIRST,
+    CM_ALL,
+};
+
+enum ShowSpeakerMode {
+    SM_SQUARE_BRACKETS,
+    SM_ROUND_BRACKETS,
+    SM_COLON,
+    SM_PLAIN,
+};
+
+typedef struct TextModContext {
+    const AVClass *class;
+    enum AVSubtitleType format;
+    enum TextModFilterType filter_type;
+    enum TextModOperation operation;
+    enum CensorMode censor_mode;
+    enum ShowSpeakerMode speaker_mode;
+    char *find;
+    char *find_file;
+    char *style;
+    char *replace;
+    char *replace_file;
+    char *separator;
+    char *censor_char;
+    char **find_list;
+    int  line_break;
+    int  nb_find_list;
+    char **replace_list;
+    int  nb_replace_list;
+} TextModContext;
+
+static char **split_string(char *source, int *nb_elems, const char *delim)
+{
+    char **list = NULL;
+    char *temp = NULL;
+    char *ptr = av_strtok(source, delim, &temp);
+
+    while (ptr) {
+        if (strlen(ptr)) {
+            av_dynarray_add(&list, nb_elems, ptr);
+            if (!list)
+                return NULL;
+        }
+
+        ptr = av_strtok(NULL, delim, &temp);
+    }
+
+    if (!list)
+        return NULL;
+
+    for (int i = 0; i < *nb_elems; i++) {
+        list[i] = av_strdup(list[i]);
+        if (!list[i]) {
+            for (int n = 0; n < i; n++)
+                av_free(list[n]);
+            av_free(list);
+            return NULL;
+        }
+    }
+
+    return list;
+}
+
+static int load_text_from_file(AVFilterContext *ctx, const char *file_name, char **text, char separator)
+{
+    int err;
+    uint8_t *textbuf;
+    char *tmp;
+    size_t textbuf_size;
+    int offset = 0;
+
+    if ((err = av_file_map(file_name, &textbuf, &textbuf_size, 0, ctx)) < 0) {
+        av_log(ctx, AV_LOG_ERROR, "The text file '%s' could not be read or is empty\n", file_name);
+        return err;
+    }
+
+    if (textbuf_size > 1 &&
+        (textbuf[0] == 0xFF && textbuf[1] == 0xFE
+        || textbuf[0] == 0xFE && textbuf[1] == 0xFF)) {
+        av_log(ctx, AV_LOG_ERROR, "UTF text files are not supported. File: %s\n", file_name);
+        return AVERROR(EINVAL);
+    }
+
+    if (textbuf_size > 2 && textbuf[0] == 0xEF && textbuf[1] == 0xBB && textbuf[2] == 0xBF)
+        offset = 3; // UTF-8
+
+    if (textbuf_size > SIZE_MAX - 1 || !((tmp = av_strndup((char *)textbuf + offset, textbuf_size - offset)))) {
+        av_file_unmap(textbuf, textbuf_size);
+        return AVERROR(ENOMEM);
+    }
+
+    av_file_unmap(textbuf, textbuf_size);
+
+    for (size_t i = 0; i < strlen(tmp); i++) {
+        switch (tmp[i]) {
+        case '\n':
+        case '\r':
+        case '\f':
+        case '\v':
+            tmp[i] = separator;
+        }
+    }
+
+    *text = tmp;
+
+    return 0;
+}
+
+static int load_files(AVFilterContext *ctx)
+{
+    TextModContext *s = ctx->priv;
+    int ret;
+
+    if (!s->separator || strlen(s->separator) != 1) {
+        av_log(ctx, AV_LOG_ERROR, "A single character needs to be specified for the separator parameter.\n");
+        return AVERROR(EINVAL);
+    }
+
+    if (s->find_file && strlen(s->find_file)) {
+        ret = load_text_from_file(ctx, s->find_file, &s->find, s->separator[0]);
+        if (ret < 0 )
+            return ret;
+    }
+
+    if (s->replace_file && strlen(s->replace_file)) {
+        ret = load_text_from_file(ctx, s->replace_file, &s->replace, s->separator[0]);
+        if (ret < 0 )
+            return ret;
+    }
+
+    return 0;
+}
+
+static int init_censor(AVFilterContext *ctx)
+{
+    TextModContext *s = ctx->priv;
+    int ret;
+
+    s->filter_type = TM_CENSOR;
+    s->operation = OP_REPLACE_WORDS;
+
+    ret = load_files(ctx);
+    if (ret < 0 )
+        return ret;
+
+    if (!s->find || !strlen(s->find)) {
+        av_log(ctx, AV_LOG_ERROR, "Either the 'words' or the 'words_file' parameter needs to be specified\n");
+        return AVERROR(EINVAL);
+    }
+
+    if (!s->censor_char || strlen(s->censor_char) != 1) {
+        av_log(ctx, AV_LOG_ERROR, "A single character needs to be specified for the censor_char parameter\n");
+        return AVERROR(EINVAL);
+    }
+
+    s->find_list = split_string(s->find, &s->nb_find_list, s->separator);
+    if (!s->find_list)
+        return AVERROR(ENOMEM);
+
+    s->replace_list = av_calloc(s->nb_find_list, sizeof(char *));
+    if (!s->replace_list)
+        return AVERROR(ENOMEM);
+
+    for (int i = 0; i < s->nb_find_list; i++) {
+        size_t len, start = 0, end;
+        char *item = av_strdup(s->find_list[i]);
+        if (!item)
+            return AVERROR(ENOMEM);
+
+        len = end = strlen(item);
+
+        switch (s->censor_mode) {
+        case CM_KEEP_FIRST_LAST:
+
+            if (len > 2)
+                start = 1;
+            if (len > 3)
+                end--;
+
+            break;
+        case CM_KEEP_FIRST:
+
+            if (len > 2)
+                start = 1;
+
+            break;
+        }
+
+        for (size_t n = start; n < end; n++)
+            item[n] = s->censor_char[0];
+
+        s->replace_list[i] = item;
+    }
+
+    return 0;
+}
+
+static int init_showspeaker(AVFilterContext *ctx)
+{
+    TextModContext *s = ctx->priv;
+    s->filter_type = TM_SHOW_SPEAKER;
+
+    return 0;
+}
+
+static int init(AVFilterContext *ctx)
+{
+    TextModContext *s = ctx->priv;
+    int ret;
+
+    ret = load_files(ctx);
+    if (ret < 0 )
+        return ret;
+
+    switch (s->operation) {
+    case OP_REPLACE_CHARS:
+    case OP_REMOVE_CHARS:
+    case OP_REPLACE_WORDS:
+    case OP_REMOVE_WORDS:
+        if (!s->find || !strlen(s->find)) {
+            av_log(ctx, AV_LOG_ERROR, "Selected mode requires the 'find' parameter to be specified\n");
+            return AVERROR(EINVAL);
+        }
+        break;
+    }
+
+    switch (s->operation) {
+    case OP_REPLACE_CHARS:
+    case OP_REPLACE_WORDS:
+        if (!s->replace || !strlen(s->replace)) {
+            av_log(ctx, AV_LOG_ERROR, "Selected mode requires the 'replace' parameter to be specified\n");
+            return AVERROR(EINVAL);
+        }
+        break;
+    }
+
+    if (s->operation == OP_REPLACE_CHARS && strlen(s->find) != strlen(s->replace)) {
+        av_log(ctx, AV_LOG_ERROR, "Selected mode requires the 'find' and 'replace' parameters to have the same length\n");
+        return AVERROR(EINVAL);
+    }
+
+    if (s->operation == OP_REPLACE_WORDS || s->operation == OP_REMOVE_WORDS) {
+        if (!s->separator || strlen(s->separator) != 1) {
+            av_log(ctx, AV_LOG_ERROR, "Selected mode requires a single separator char to be specified\n");
+            return AVERROR(EINVAL);
+        }
+
+        s->find_list = split_string(s->find, &s->nb_find_list, s->separator);
+        if (!s->find_list)
+            return AVERROR(ENOMEM);
+
+        if (s->operation == OP_REPLACE_WORDS) {
+
+            s->replace_list = split_string(s->replace, &s->nb_replace_list, s->separator);
+            if (!s->replace_list)
+                return AVERROR(ENOMEM);
+
+            if (s->nb_find_list != s->nb_replace_list) {
+                av_log(ctx, AV_LOG_ERROR, "The number of words in 'find' and 'replace' needs to be equal\n");
+                return AVERROR(EINVAL);
+            }
+        }
+    }
+
+    return 0;
+}
+
+static void uninit(AVFilterContext *ctx)
+{
+    TextModContext *s = ctx->priv;
+
+    for (int i = 0; i < s->nb_find_list; i++)
+        av_freep(&s->find_list[i]);
+
+    s->nb_find_list = 0;
+    av_freep(&s->find_list);
+
+    for (int i = 0; i < s->nb_replace_list; i++)
+        av_freep(&s->replace_list[i]);
+
+    s->nb_replace_list = 0;
+    av_freep(&s->replace_list);
+}
+
+static char *process_text(TextModContext *s, char *text)
+{
+    const char *char_src = s->find;
+    const char *char_dst = s->replace;
+    char *result         = NULL;
+    int escape_level     = 0, k = 0;
+
+    switch (s->operation) {
+    case OP_LEET:
+    case OP_REPLACE_CHARS:
+
+        if (s->operation == OP_LEET) {
+            char_src = leet_src;
+            char_dst = leet_dst;
+        }
+
+        result = av_strdup(text);
+        if (!result)
+            return NULL;
+
+        for (size_t n = 0; n < strlen(result); n++) {
+            if (result[n] == '{')
+                escape_level++;
+
+            if (!escape_level) {
+                size_t len = strlen(char_src);
+                for (size_t t = 0; t < len; t++) {
+                    if (result[n] == char_src[t]) {
+                        result[n] = char_dst[t];
+                        break;
+                    }
+                }
+            }
+
+            if (result[n] == '}')
+                escape_level--;
+        }
+
+        break;
+    case OP_TO_UPPER:
+    case OP_TO_LOWER:
+
+        result = av_strdup(text);
+        if (!result)
+            return NULL;
+
+        for (size_t n = 0; n < strlen(result); n++) {
+            if (result[n] == '{')
+                escape_level++;
+            if (!escape_level)
+                result[n] = s->operation == OP_TO_LOWER ? av_tolower(result[n]) : av_toupper(result[n]);
+            if (result[n] == '}')
+                escape_level--;
+        }
+
+        break;
+    case OP_REMOVE_CHARS:
+
+        result = av_strdup(text);
+        if (!result)
+            return NULL;
+
+        for (size_t n = 0; n < strlen(result); n++) {
+            int skip_char = 0;
+
+            if (result[n] == '{')
+                escape_level++;
+
+            if (!escape_level) {
+                size_t len = strlen(char_src);
+                for (size_t t = 0; t < len; t++) {
+                    if (result[n] == char_src[t]) {
+                        skip_char = 1;
+                        break;
+                    }
+                }
+            }
+
+            if (!skip_char)
+                result[k++] = result[n];
+
+            if (result[n] == '}')
+                escape_level--;
+        }
+
+        result[k] = 0;
+
+        break;
+    case OP_REPLACE_WORDS:
+    case OP_REMOVE_WORDS:
+
+        result = av_strdup(text);
+        if (!result)
+            return NULL;
+
+        for (int n = 0; n < s->nb_find_list; n++) {
+            char *tmp           = result;
+            const char *replace = (s->operation == OP_REPLACE_WORDS) ? s->replace_list[n] : "";
+
+            result = av_strireplace(result, s->find_list[n], replace);
+            if (!result)
+                return NULL;
+
+            av_free(tmp);
+        }
+
+        break;
+    }
+
+    return result;
+}
+
+static char *process_dialog_show_speaker(TextModContext *s, char *ass_line)
+{
+    ASSDialog *dialog = avpriv_ass_split_dialog(NULL, ass_line);
+    int escape_level = 0;
+    unsigned pos = 0, len;
+    char *result, *text;
+    AVBPrint pbuf;
+
+    if (!dialog)
+        return NULL;
+
+    text = process_text(s, dialog->text);
+    if (!text)
+        return NULL;
+
+    if (!dialog->name || !strlen(dialog->name) || !dialog->text || !strlen(dialog->text))
+        return av_strdup(ass_line);
+
+    // Find insertion point in case the line starts with style codes
+    len = (unsigned)strlen(dialog->text);
+    for (unsigned i = 0; i < len; i++) {
+
+        if (dialog->text[i] == '{')
+            escape_level++;
+
+        if (dialog->text[i] == '}')
+            escape_level--;
+
+        if (escape_level == 0) {
+            pos = i;
+            break;
+        }
+    }
+
+    if (s->style && strlen(s->style))
+        // When a style is specified reset the insertion point
+        // (always add speaker plus style at the start in that case)
+        pos = 0;
+
+    if (pos >= len - 1)
+        return av_strdup(ass_line);
+
+    av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
+
+    if (pos > 0) {
+        av_bprint_append_data(&pbuf, dialog->text, pos);
+    }
+
+    if (s->style && strlen(s->style)) {
+        if (s->style[0] == '{')
+            // Assume complete and valid style code, e.g. {\c&HFF0000&}
+            av_bprintf(&pbuf, "%s", s->style);
+        else
+            // Otherwise it must be a style name
+            av_bprintf(&pbuf, "{\\r%s}", s->style);
+    }
+
+    switch (s->speaker_mode) {
+    case SM_SQUARE_BRACKETS:
+        av_bprintf(&pbuf, "[%s]", dialog->name);
+        break;
+    case SM_ROUND_BRACKETS:
+        av_bprintf(&pbuf, "(%s)", dialog->name);
+        break;
+    case SM_COLON:
+        av_bprintf(&pbuf, "%s:", dialog->name);
+        break;
+    case SM_PLAIN:
+        av_bprintf(&pbuf, "%s", dialog->name);
+        break;
+    }
+
+    if (s->style && strlen(s->style)) {
+        // Reset line style
+        if (dialog->style && strlen(dialog->style) && !av_strcasecmp(dialog->style, "default"))
+            av_bprintf(&pbuf, "{\\r%s}", dialog->style);
+        else
+            av_bprintf(&pbuf, "{\\r}");
+    }
+
+    if (s->line_break)
+        av_bprintf(&pbuf, "\\N");
+    else
+        av_bprintf(&pbuf, " ");
+
+    av_bprint_append_data(&pbuf, dialog->text + pos, len - pos);
+
+    av_bprint_finalize(&pbuf, &text);
+
+    result = avpriv_ass_get_dialog(dialog->readorder, dialog->layer, dialog->style, dialog->name, text);
+
+    av_free(text);
+    avpriv_ass_free_dialog(&dialog);
+    return result;
+}
+
+static char *process_dialog(TextModContext *s, char *ass_line)
+{
+    ASSDialog *dialog;
+    char *result, *text;
+
+    if (s->filter_type == TM_SHOW_SPEAKER)
+        return process_dialog_show_speaker(s, ass_line);
+
+    dialog = avpriv_ass_split_dialog(NULL, ass_line);
+    if (!dialog)
+        return NULL;
+
+    text = process_text(s, dialog->text);
+    if (!text)
+        return NULL;
+
+    result = avpriv_ass_get_dialog(dialog->readorder, dialog->layer, dialog->style, dialog->name, text);
+
+    av_free(text);
+    avpriv_ass_free_dialog(&dialog);
+    return result;
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    AVFilterLink *inlink = outlink->src->inputs[0];
+
+    outlink->w = inlink->w;
+    outlink->h = inlink->h;
+    outlink->time_base = inlink->time_base;
+    outlink->frame_rate = inlink->frame_rate;
+
+    return 0;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    TextModContext *s = inlink->dst->priv;
+    AVFilterLink *outlink = inlink->dst->outputs[0];
+    int ret;
+
+    outlink->format = inlink->format;
+
+    ret = av_frame_make_writable(frame);
+    if (ret < 0)
+        return ret;
+
+    for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+
+        AVSubtitleArea *area = frame->subtitle_areas[i];
+
+        if (area->ass) {
+            char *tmp = area->ass;
+            area->ass = process_dialog(s, area->ass);
+            av_free(tmp);
+            if (!area->ass)
+                return AVERROR(ENOMEM);
+        }
+    }
+
+    return ff_filter_frame(outlink, frame);
+}
+
+#define OFFSET(x) offsetof(TextModContext, x)
+#define FLAGS (AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption textmod_options[] = {
+    { "mode",             "set operation mode",              OFFSET(operation),    AV_OPT_TYPE_INT,    {.i64=OP_LEET},          OP_LEET, NB_OPS-1, FLAGS, "mode" },
+    {   "leet",           "convert text to 'leet speak'",    0,                    AV_OPT_TYPE_CONST,  {.i64=OP_LEET},          0,       0,        FLAGS, "mode" },
+    {   "to_upper",       "change to upper case",            0,                    AV_OPT_TYPE_CONST,  {.i64=OP_TO_UPPER},      0,       0,        FLAGS, "mode" },
+    {   "to_lower",       "change to lower case",            0,                    AV_OPT_TYPE_CONST,  {.i64=OP_TO_LOWER},      0,       0,        FLAGS, "mode" },
+    {   "replace_chars",  "replace characters",              0,                    AV_OPT_TYPE_CONST,  {.i64=OP_REPLACE_CHARS}, 0,       0,        FLAGS, "mode" },
+    {   "remove_chars",   "remove characters",               0,                    AV_OPT_TYPE_CONST,  {.i64=OP_REMOVE_CHARS},  0,       0,        FLAGS, "mode" },
+    {   "replace_words",  "replace words",                   0,                    AV_OPT_TYPE_CONST,  {.i64=OP_REPLACE_WORDS}, 0,       0,        FLAGS, "mode" },
+    {   "remove_words",   "remove words",                    0,                    AV_OPT_TYPE_CONST,  {.i64=OP_REMOVE_WORDS},  0,       0,        FLAGS, "mode" },
+    { "find",             "chars/words to find or remove",   OFFSET(find),         AV_OPT_TYPE_STRING, {.str = NULL},           0,       0,        FLAGS, NULL   },
+    { "find_file",        "load find param from file",       OFFSET(find_file),    AV_OPT_TYPE_STRING, {.str = NULL},           0,       0,        FLAGS, NULL   },
+    { "replace",          "chars/words to replace",          OFFSET(replace),      AV_OPT_TYPE_STRING, {.str = NULL},           0,       0,        FLAGS, NULL   },
+    { "replace_file",     "load replace param from file",    OFFSET(replace_file), AV_OPT_TYPE_STRING, {.str = NULL},           0,       0,        FLAGS, NULL   },
+    { "separator",        "word separator",                  OFFSET(separator),    AV_OPT_TYPE_STRING, {.str = ","},            0,       0,        FLAGS, NULL   },
+    { NULL },
+};
+
+
+static const AVOption censor_options[] = {
+    { "mode",               "set censoring mode",        OFFSET(censor_mode), AV_OPT_TYPE_INT,    {.i64=CM_KEEP_FIRST_LAST}, 0, 2, FLAGS, "mode" },
+    {   "keep_first_last",  "censor inner chars",        0,                   AV_OPT_TYPE_CONST,  {.i64=CM_KEEP_FIRST_LAST}, 0, 0, FLAGS, "mode" },
+    {   "keep_first",       "censor all but first char", 0,                   AV_OPT_TYPE_CONST,  {.i64=CM_KEEP_FIRST},      0, 0, FLAGS, "mode" },
+    {   "all",              "censor all chars",          0,                   AV_OPT_TYPE_CONST,  {.i64=CM_ALL},             0, 0, FLAGS, "mode" },
+    { "words",              "list of words to censor",   OFFSET(find),        AV_OPT_TYPE_STRING, {.str = NULL},             0, 0, FLAGS, NULL   },
+    { "words_file",         "path to word list file",    OFFSET(find_file),   AV_OPT_TYPE_STRING, {.str = NULL},             0, 0, FLAGS, NULL   },
+    { "separator",          "word separator",            OFFSET(separator),   AV_OPT_TYPE_STRING, {.str = ","},              0, 0, FLAGS, NULL   },
+    { "censor_char",        "replacement character",     OFFSET(censor_char), AV_OPT_TYPE_STRING, {.str = "*"},              0, 0, FLAGS, NULL   },
+    { NULL },
+};
+
+static const AVOption showspeaker_options[] = {
+    { "format",             "speaker name formatting",        OFFSET(speaker_mode), AV_OPT_TYPE_INT,    {.i64=SM_SQUARE_BRACKETS}, 0, 2, FLAGS, "format" },
+    {   "square_brackets",  "[speaker] text",                 0,                    AV_OPT_TYPE_CONST,  {.i64=SM_SQUARE_BRACKETS}, 0, 0, FLAGS, "format" },
+    {   "round_brackets",   "(speaker) text",                 0,                    AV_OPT_TYPE_CONST,  {.i64=SM_ROUND_BRACKETS},  0, 0, FLAGS, "format" },
+    {   "colon",            "speaker: text",                  0,                    AV_OPT_TYPE_CONST,  {.i64=SM_COLON},           0, 0, FLAGS, "format" },
+    {   "plain",            "speaker text",                   0,                    AV_OPT_TYPE_CONST,  {.i64=SM_PLAIN},           0, 0, FLAGS, "format" },
+    { "line_break",         "insert line break",              OFFSET(line_break),   AV_OPT_TYPE_BOOL,   {.i64=0},                  0, 1, FLAGS, NULL     },
+    { "style",              "ass type name or style code",    OFFSET(style),        AV_OPT_TYPE_STRING, {.str = NULL},             0, 0, FLAGS, NULL     },
+    { NULL },
+};
+
+AVFILTER_DEFINE_CLASS(textmod);
+AVFILTER_DEFINE_CLASS(censor);
+AVFILTER_DEFINE_CLASS(showspeaker);
+
+static const AVFilterPad inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .filter_frame = filter_frame,
+    },
+};
+
+static const AVFilterPad outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_SUBTITLE,
+        .config_props  = config_output,
+    },
+};
+
+const AVFilter ff_sf_textmod = {
+    .name          = "textmod",
+    .description   = NULL_IF_CONFIG_SMALL("Modify subtitle text in several ways"),
+    .init          = init,
+    .uninit        = uninit,
+    .priv_size     = sizeof(TextModContext),
+    .priv_class    = &textmod_class,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_SINGLE_SUBFMT(AV_SUBTITLE_FMT_ASS),
+};
+
+const AVFilter ff_sf_censor = {
+    .name          = "censor",
+    .description   = NULL_IF_CONFIG_SMALL("Censor words in subtitle text"),
+    .init          = init_censor,
+    .uninit        = uninit,
+    .priv_size     = sizeof(TextModContext),
+    .priv_class    = &censor_class,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_SINGLE_SUBFMT(AV_SUBTITLE_FMT_ASS),
+};
+
+const AVFilter ff_sf_showspeaker = {
+    .name          = "showspeaker",
+    .description   = NULL_IF_CONFIG_SMALL("Prepend speaker names to text subtitles (when available)"),
+    .init          = init_showspeaker,
+    .uninit        = uninit,
+    .priv_size     = sizeof(TextModContext),
+    .priv_class    = &showspeaker_class,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_SINGLE_SUBFMT(AV_SUBTITLE_FMT_ASS),
+};
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH 16/24] avfilter/stripstyles: Add stripstyles filter
  2022-01-14  1:13 [FFmpeg-devel] [PATCH 00/24] Subtitle Filtering 2022 ffmpegagent
                   ` (14 preceding siblings ...)
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 15/24] avfilter/textmod: Add textmod, censor and show_speaker filters ffmpegagent
@ 2022-01-14  1:13 ` ffmpegagent
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 17/24] avfilter/splitcc: Add splitcc filter for closed caption handling ffmpegagent
                   ` (8 subsequent siblings)
  24 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-14  1:13 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: softworkz

From: softworkz <softworkz@hotmail.com>

- stripstyles {S -> S)
  Remove all inline styles from subtitle events

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 doc/filters.texi             |  37 +++++++
 libavfilter/Makefile         |   1 +
 libavfilter/allfilters.c     |   1 +
 libavfilter/sf_stripstyles.c | 209 +++++++++++++++++++++++++++++++++++
 4 files changed, 248 insertions(+)
 create mode 100644 libavfilter/sf_stripstyles.c

diff --git a/doc/filters.texi b/doc/filters.texi
index 494ee6f062..c0f0fe13e7 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -25781,6 +25781,43 @@ ffmpeg -i "http://streams.videolan.org/samples/sub/SSA/subtitle_testing_complex.
 @end example
 @end itemize
 
+@section stripstyles
+
+Remove all inline styles from subtitle events.
+
+Inputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+@item remove_animated
+Also remove text which is subject to animation (default: true)
+Usually, animated text elements are used used in addition to static subtitle lines for creating effects, so in most cases it is safe to remove the animation content.
+If subtitle text is missing, try setting this to false.
+
+@item select_layer
+Process only ASS subtitle events from a specific layer. This allows to filter out certain effects where an ASS author duplicates the text onto multiple layers.
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Remove styles and animations from ASS subtitles and output events from ass layer 0 only. Then convert asn save as SRT stream:
+@example
+ffmpeg -i "https://streams.videolan.org/samples/sub/SSA/subtitle_testing_complex.mkv" -filter_complex "[0:1]stripstyles=select_layer=0" -map 0 -c:s srt output.mkv
+@end example
+@end itemize
+
 
 @section textmod
 
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 1af4f4b9bc..d330020f67 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -565,6 +565,7 @@ OBJS-$(CONFIG_NULLSINK_FILTER)               += vsink_nullsink.o
 OBJS-$(CONFIG_CENSOR_FILTER)                 += sf_textmod.o
 OBJS-$(CONFIG_SHOW_SPEAKER_FILTER)           += sf_textmod.o
 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 ac7d71547b..b0c12595af 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -549,6 +549,7 @@ extern const AVFilter ff_avf_showwavespic;
 extern const AVFilter ff_vaf_spectrumsynth;
 extern const AVFilter ff_sf_censor;
 extern const AVFilter ff_sf_showspeaker;
+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..bbd731cc03
--- /dev/null
+++ b/libavfilter/sf_stripstyles.c
@@ -0,0 +1,209 @@
+/*
+ * 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 "libavutil/opt.h"
+#include "internal.h"
+#include "libavutil/ass_split_internal.h"
+#include "libavutil/bprint.h"
+
+typedef struct StripStylesContext {
+    const AVClass *class;
+    enum AVSubtitleType format;
+    int remove_animated;
+    int select_layer;
+} StripStylesContext;
+
+typedef struct DialogContext {
+    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;
+
+    av_log(s->ss_ctx, AV_LOG_DEBUG, "dialog_text_cb: %s\n", text);
+
+    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 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)
+{
+    DialogContext dlg_ctx = { .ss_ctx = s };
+    ASSDialog *dialog = avpriv_ass_split_dialog(NULL, ass_line);
+    char *result = NULL;
+
+    if (!dialog)
+        return NULL;
+
+    if (s->select_layer >= 0 && dialog->layer != s->select_layer)
+        return NULL;
+
+    dlg_ctx.ss_ctx = s;
+
+    av_bprint_init(&dlg_ctx.buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
+
+    avpriv_ass_split_override_codes(&dialog_callbacks, &dlg_ctx, dialog->text);
+
+    if (av_bprint_is_complete(&dlg_ctx.buffer)
+        && dlg_ctx.buffer.len > 0)
+        result = ass_get_line(dialog->readorder, dialog->layer, dialog->style, dialog->name, dialog->effect, dlg_ctx.buffer.str);
+
+    av_bprint_finalize(&dlg_ctx.buffer, NULL);
+    avpriv_ass_free_dialog(&dialog);
+
+    return result;
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    AVFilterLink *inlink = outlink->src->inputs[0];
+
+    outlink->w = inlink->w;
+    outlink->h = inlink->h;
+    outlink->time_base = inlink->time_base;
+    outlink->frame_rate = inlink->frame_rate;
+
+    return 0;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    StripStylesContext *s = inlink->dst->priv;
+    AVFilterLink *outlink = inlink->dst->outputs[0];
+    int ret;
+
+    outlink->format = inlink->format;
+
+    ret = av_frame_make_writable(frame);
+    if (ret <0 ) {
+        av_frame_free(&frame);
+        return AVERROR(ENOMEM);
+    }
+
+    for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+
+        AVSubtitleArea *area = frame->subtitle_areas[i];
+
+        if (area->ass) {
+            char *tmp = area->ass;
+            area->ass = process_dialog(s, area->ass);
+
+            if (area->ass) {
+                av_log(inlink->dst, AV_LOG_INFO, "original: %d %s\n", i, tmp);
+                av_log(inlink->dst, AV_LOG_INFO, "stripped: %d %s\n", i, area->ass);
+            }
+            else
+                area->ass = NULL;
+
+            av_free(tmp);
+        }
+    }
+
+    return ff_filter_frame(outlink, frame);
+}
+
+#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 },
+    { "select_layer", "process a specific ass layer only",   OFFSET(remove_animated),  AV_OPT_TYPE_INT, {.i64 = -1 }, -1, INT_MAX, 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,
+        .config_props  = config_output,
+    },
+};
+
+const AVFilter ff_sf_stripstyles = {
+    .name          = "stripstyles",
+    .description   = NULL_IF_CONFIG_SMALL("Strip subtitle inline styles"),
+    .priv_size     = sizeof(StripStylesContext),
+    .priv_class    = &stripstyles_class,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_SINGLE_SUBFMT(AV_SUBTITLE_FMT_ASS),
+};
+
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH 17/24] avfilter/splitcc: Add splitcc filter for closed caption handling
  2022-01-14  1:13 [FFmpeg-devel] [PATCH 00/24] Subtitle Filtering 2022 ffmpegagent
                   ` (15 preceding siblings ...)
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 16/24] avfilter/stripstyles: Add stripstyles filter ffmpegagent
@ 2022-01-14  1:13 ` ffmpegagent
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 18/24] avfilter/graphicsub2text: Add new graphicsub2text filter (OCR) ffmpegagent
                   ` (7 subsequent siblings)
  24 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-14  1:13 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: softworkz

From: softworkz <softworkz@hotmail.com>

- splitcc {V -> VS)
  Extract closed-caption (A53) data from video
  frames as subtitle Frames

ffmpeg -y -loglevel verbose -i "https://streams.videolan.org/streams
/ts/CC/NewsStream-608-ac3.ts" -filter_complex "[0:v]splitcc[vid1],
textmod=mode=remove_chars:find='@',[vid1]overlay_textsubs" output.mkv

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 configure                |   1 +
 doc/filters.texi         |  63 +++++++
 libavfilter/Makefile     |   1 +
 libavfilter/allfilters.c |   1 +
 libavfilter/sf_splitcc.c | 385 +++++++++++++++++++++++++++++++++++++++
 5 files changed, 451 insertions(+)
 create mode 100644 libavfilter/sf_splitcc.c

diff --git a/configure b/configure
index 340a198fa6..c1d2bc41c2 100755
--- a/configure
+++ b/configure
@@ -3729,6 +3729,7 @@ spp_filter_select="fft idctdsp fdctdsp me_cmp pixblockdsp"
 sr_filter_deps="avformat swscale"
 sr_filter_select="dnn"
 stereo3d_filter_deps="gpl"
+splitcc_filter_deps="avcodec"
 subtitles_filter_deps="avformat avcodec libass"
 super2xsai_filter_deps="gpl"
 pixfmts_super2xsai_test_deps="super2xsai_filter"
diff --git a/doc/filters.texi b/doc/filters.texi
index c0f0fe13e7..265a267e9d 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -26131,6 +26131,69 @@ ffmpeg -i INPUT -filter_complex "showspeaker=format=colon:style='@{\\c&HDD0000&\
 @end example
 @end itemize
 
+
+@section splitcc
+
+Split-out closed-caption/A53 subtitles from video frame side data.
+
+This filter provides an input and an output for video frames, which are just passed through without modification.
+The second out provides subtitle frames which are extracted from video frame side data.
+
+Inputs:
+@itemize
+@item 0: Video [ALL]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Video (same as input)
+@item 1: Subtitles [TEXT]
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+
+@item use_cc_styles
+Emit closed caption style header.
+This will make closed captions appear in white font with a black rectangle background.
+
+@item real_time
+Emit subtitle events as they are decoded for real-time display.
+
+@item real_time_latency_msec
+Minimum elapsed time between emitting real-time subtitle events.
+Only applies to real_time mode.
+
+@item data_field
+Select data field. Possible values:
+
+@table @samp
+@item auto
+Pick first one that appears.
+@item first
+@item second
+@end table
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Extract closed captions as text subtitle stream and overlay it onto the video in cc style (black bar background):
+@example
+ffmpeg -i "https://streams.videolan.org/streams/ts/CC/NewsStream-608-ac3.ts" -filter_complex  "[0:v:0]splitcc=use_cc_styles=1[vid1][sub1];[vid1][sub1]overlaytextsubs" output.mkv
+@end example
+
+@item
+A nicer variant, using realtime output from cc_dec and rendering it with the render_latest_only parameter from overlaytextsubs to avoid ghosting by timely overlap.
+@example
+ffmpeg -i "https://streams.videolan.org/streams/ts/CC/NewsStream-608-ac3.ts" -filter_complex  "[0:v:0]splitcc=real_time=1:real_time_latency_msec=200[vid1][sub1];[vid1][sub1]overlaytextsubs=render_latest_only=1" output.mkv
+@end example
+@end itemize
+
+
 @section textsub2video
 
 Converts text subtitles to video frames.
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index d330020f67..c6a4a4f5ae 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -565,6 +565,7 @@ OBJS-$(CONFIG_NULLSINK_FILTER)               += vsink_nullsink.o
 OBJS-$(CONFIG_CENSOR_FILTER)                 += sf_textmod.o
 OBJS-$(CONFIG_SHOW_SPEAKER_FILTER)           += sf_textmod.o
 OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
+OBJS-$(CONFIG_SPLITCC_FILTER)                += sf_splitcc.o
 OBJS-$(CONFIG_STRIPSTYLES_FILTER)            += sf_stripstyles.o
 
 # multimedia filters
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index b0c12595af..50498e8ec4 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -549,6 +549,7 @@ extern const AVFilter ff_avf_showwavespic;
 extern const AVFilter ff_vaf_spectrumsynth;
 extern const AVFilter ff_sf_censor;
 extern const AVFilter ff_sf_showspeaker;
+extern const AVFilter ff_sf_splitcc;
 extern const AVFilter ff_sf_stripstyles;
 extern const AVFilter ff_sf_textmod;
 extern const AVFilter ff_svf_graphicsub2video;
diff --git a/libavfilter/sf_splitcc.c b/libavfilter/sf_splitcc.c
new file mode 100644
index 0000000000..084f5f6969
--- /dev/null
+++ b/libavfilter/sf_splitcc.c
@@ -0,0 +1,385 @@
+/*
+ * 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
+ * subtitle filter for splitting out closed-caption/A53 subtitles from video frame side data
+ */
+
+#include "filters.h"
+#include "libavutil/opt.h"
+#include "subtitles.h"
+#include "libavcodec/avcodec.h"
+
+static const AVRational ms_tb = {1, 1000};
+
+typedef struct SplitCaptionsContext {
+    const AVClass *class;
+    enum AVSubtitleType format;
+    AVCodecContext *cc_dec;
+    int eof;
+    AVFrame *next_sub_frame;
+    AVFrame *empty_sub_frame;
+    int new_frame;
+    int64_t next_repetition_pts;
+    AVBufferRef *subtitle_header;
+    int use_cc_styles;
+    int real_time;
+    int real_time_latency_msec;
+    int data_field;
+    int scatter_realtime_output;
+} SplitCaptionsContext;
+
+static int64_t ms_to_avtb(int64_t ms)
+{
+    return av_rescale_q(ms, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q);
+}
+
+static int init(AVFilterContext *ctx)
+{
+    SplitCaptionsContext *s = ctx->priv;
+    AVDictionary *options = NULL;
+
+    int ret;
+    const AVCodec *codec = avcodec_find_decoder(AV_CODEC_ID_EIA_608);
+    if (!codec) {
+        av_log(ctx, AV_LOG_ERROR, "failed to find EIA-608/708 decoder\n");
+        return AVERROR_DECODER_NOT_FOUND;
+    }
+
+    if (!((s->cc_dec = avcodec_alloc_context3(codec)))) {
+        av_log(ctx, AV_LOG_ERROR, "failed to allocate EIA-608/708 decoder\n");
+        return AVERROR(ENOMEM);
+    }
+
+    av_dict_set_int(&options, "real_time", s->real_time, 0);
+    av_dict_set_int(&options, "real_time_latency_msec", s->real_time_latency_msec, 0);
+    av_dict_set_int(&options, "data_field", s->data_field, 0);
+
+    if ((ret = avcodec_open2(s->cc_dec, codec, &options)) < 0) {
+        av_log(ctx, AV_LOG_ERROR, "failed to open EIA-608/708 decoder: %i\n", ret);
+        return ret;
+    }
+
+    if (s->use_cc_styles && s->cc_dec->subtitle_header && s->cc_dec->subtitle_header[0] != 0) {
+        char* subtitle_header =  av_strdup((char *)s->cc_dec->subtitle_header);
+        if (!subtitle_header)
+            return AVERROR(ENOMEM);
+        s->subtitle_header = av_buffer_create((uint8_t *)subtitle_header, strlen(subtitle_header) + 1, NULL, NULL, 0);
+        if (!s->subtitle_header) {
+            av_free(subtitle_header);
+            return AVERROR(ENOMEM);
+        }
+    }
+
+    return 0;
+}
+
+static void uninit(AVFilterContext *ctx)
+{
+    SplitCaptionsContext *s = ctx->priv;
+    av_frame_free(&s->next_sub_frame);
+    av_frame_free(&s->empty_sub_frame);
+    av_buffer_unref(&s->subtitle_header);
+}
+
+static int config_input(AVFilterLink *link)
+{
+    const SplitCaptionsContext *context = link->dst->priv;
+
+    if (context->cc_dec)
+        context->cc_dec->pkt_timebase = link->time_base;
+
+    return 0;
+}
+
+static int query_formats(AVFilterContext *ctx)
+{
+    AVFilterFormats *formats;
+    AVFilterLink *inlink0 = ctx->inputs[0];
+    AVFilterLink *outlink0 = ctx->outputs[0];
+    AVFilterLink *outlink1 = ctx->outputs[1];
+    static const enum AVSubtitleType subtitle_fmts[] = { AV_SUBTITLE_FMT_ASS, AV_SUBTITLE_FMT_NB };
+    int ret;
+
+    /* set input0 video formats */
+    formats = ff_all_formats(AVMEDIA_TYPE_VIDEO);
+    if ((ret = ff_formats_ref(formats, &inlink0->outcfg.formats)) < 0)
+        return ret;
+
+    /* set output0 video formats */
+    if ((ret = ff_formats_ref(formats, &outlink0->incfg.formats)) < 0)
+        return ret;
+
+    /* set output1 subtitle formats */
+    formats = ff_make_format_list(subtitle_fmts);
+    if ((ret = ff_formats_ref(formats, &outlink1->incfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int config_video_output(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    const AVFilterLink *inlink = outlink->src->inputs[0];
+
+    outlink->w = ctx->inputs[0]->w;
+    outlink->h = ctx->inputs[0]->h;
+    outlink->frame_rate = ctx->inputs[0]->frame_rate;
+    outlink->time_base = ctx->inputs[0]->time_base;
+    outlink->sample_aspect_ratio = ctx->inputs[0]->sample_aspect_ratio;
+
+    if (inlink->hw_frames_ctx)
+        outlink->hw_frames_ctx = av_buffer_ref(inlink->hw_frames_ctx);
+
+    return 0;
+}
+
+static int config_sub_output(AVFilterLink *outlink)
+{
+    SplitCaptionsContext *s = outlink->src->priv;
+    const AVFilterLink *inlink = outlink->src->inputs[0];
+
+    outlink->time_base = inlink->time_base;
+    outlink->format = AV_SUBTITLE_FMT_ASS;
+    outlink->frame_rate = (AVRational){1000, s->real_time_latency_msec};
+
+    return 0;
+}
+
+static int request_sub_frame(AVFilterLink *outlink)
+{
+    SplitCaptionsContext *s = outlink->src->priv;
+    int status;
+    int64_t pts;
+
+    if (!s->empty_sub_frame) {
+        s->empty_sub_frame = ff_get_subtitles_buffer(outlink, outlink->format);
+        if (!s->empty_sub_frame)
+            return AVERROR(ENOMEM);
+    }
+
+    if (!s->eof && ff_inlink_acknowledge_status(outlink->src->inputs[0], &status, &pts)) {
+        if (status == AVERROR_EOF)
+            s->eof = 1;
+    }
+
+    if (s->eof)
+        return AVERROR_EOF;
+
+    if (s->next_sub_frame) {
+
+        AVFrame *out = NULL;
+        s->next_sub_frame->pts++;
+
+        if (s->new_frame)
+            out = av_frame_clone(s->next_sub_frame);
+        else if (s->empty_sub_frame) {
+            s->empty_sub_frame->pts = s->next_sub_frame->pts;
+            out = av_frame_clone(s->empty_sub_frame);
+            av_frame_copy_props(out, s->next_sub_frame);
+            out->repeat_sub = 1;
+        }
+
+        if (!out)
+            return AVERROR(ENOMEM);
+
+        out->subtitle_timing.start_pts = av_rescale_q(s->next_sub_frame->pts, outlink->time_base, AV_TIME_BASE_Q);
+        s->new_frame = 0;
+
+        return ff_filter_frame(outlink, out);
+    }
+
+    return 0;
+}
+
+static int decode(AVCodecContext *avctx, AVFrame *frame, int *got_frame, AVPacket *pkt)
+{
+    int ret;
+
+    *got_frame = 0;
+
+    if (pkt) {
+        ret = avcodec_send_packet(avctx, pkt);
+        // In particular, we don't expect AVERROR(EAGAIN), because we read all
+        // decoded frames with avcodec_receive_frame() until done.
+        if (ret < 0 && ret != AVERROR_EOF)
+            return ret;
+    }
+
+    ret = avcodec_receive_frame(avctx, frame);
+    if (ret < 0 && ret != AVERROR(EAGAIN))
+        return ret;
+    if (ret >= 0)
+        *got_frame = 1;
+
+    return 0;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFrameSideData *sd;
+    SplitCaptionsContext *s = inlink->dst->priv;
+    AVFilterLink *outlink0 = inlink->dst->outputs[0];
+    AVFilterLink *outlink1 = inlink->dst->outputs[1];
+    AVPacket *pkt = NULL;
+    AVFrame *sub_out = NULL;
+
+    int ret;
+
+    outlink0->format = inlink->format;
+
+    sd = av_frame_get_side_data(frame, AV_FRAME_DATA_A53_CC);
+
+    if (sd) {
+        int got_output = 0;
+
+        pkt = av_packet_alloc();
+        pkt->buf = av_buffer_ref(sd->buf);
+        if (!pkt->buf) {
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
+
+        pkt->data = sd->data;
+        pkt->size = (int)sd->size;
+        pkt->pts  = frame->pts;
+
+        sub_out = ff_get_subtitles_buffer(outlink1, AV_SUBTITLE_FMT_ASS);
+        if (!sub_out) {
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
+
+        if ((ret = av_buffer_replace(&sub_out->subtitle_header, s->subtitle_header)) < 0)
+            goto fail;
+
+        ret = decode(s->cc_dec, sub_out, &got_output, pkt);
+
+        if (ret < 0)
+            goto fail;
+
+        if (got_output) {
+            sub_out->pts = frame->pts;
+            av_frame_free(&s->next_sub_frame);
+            s->next_sub_frame = sub_out;
+            sub_out = NULL;
+            s->new_frame = 1;
+            s->next_sub_frame->pts = frame->pts;
+
+            if ((ret = av_buffer_replace(&s->next_sub_frame->subtitle_header, s->subtitle_header)) < 0)
+                goto fail;
+
+            if (s->real_time && s->scatter_realtime_output) {
+                if (s->next_repetition_pts)
+                    s->next_sub_frame->pts = s->next_repetition_pts;
+
+                s->next_sub_frame->subtitle_timing.duration = ms_to_avtb(s->real_time_latency_msec);
+                s->next_repetition_pts = s->next_sub_frame->pts + av_rescale_q(s->real_time_latency_msec, ms_tb, inlink->time_base);
+            }
+
+            ret = request_sub_frame(outlink1);
+            if (ret < 0)
+                goto fail;
+        }
+    }
+
+    if (s->real_time && s->scatter_realtime_output && !s->new_frame && s->next_repetition_pts > 0 && frame->pts > s->next_repetition_pts) {
+        s->new_frame = 1;
+        s->next_sub_frame->pts = s->next_repetition_pts;
+        s->next_repetition_pts = s->next_sub_frame->pts + av_rescale_q(s->real_time_latency_msec, ms_tb, inlink->time_base);
+    }
+
+    if (!s->next_sub_frame) {
+        s->next_sub_frame = ff_get_subtitles_buffer(outlink1, outlink1->format);
+        if (!s->next_sub_frame) {
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
+
+        s->next_sub_frame->subtitle_timing.duration = ms_to_avtb(s->real_time_latency_msec);
+        s->next_sub_frame->pts = frame->pts;
+        s->new_frame = 1;
+
+        if ((ret = av_buffer_replace(&s->next_sub_frame->subtitle_header, s->subtitle_header)) < 0)
+            goto fail;
+    }
+
+    ret = ff_filter_frame(outlink0, frame);
+
+fail:
+    av_packet_free(&pkt);
+    av_frame_free(&sub_out);
+    return ret;
+}
+
+#define OFFSET(x) offsetof(SplitCaptionsContext, x)
+#define FLAGS (AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption split_cc_options[] = {
+    { "use_cc_styles",    "Emit closed caption style header", OFFSET(use_cc_styles),  AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, NULL },
+    { "real_time", "emit subtitle events as they are decoded for real-time display", OFFSET(real_time), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS },
+    { "real_time_latency_msec", "minimum elapsed time between emitting real-time subtitle events", OFFSET(real_time_latency_msec), AV_OPT_TYPE_INT, { .i64 = 200 }, 0, 500, FLAGS },
+    { "scatter_realtime_output", "scatter output events to a duration of real_time_latency_msec", OFFSET(scatter_realtime_output), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS },
+    { "data_field", "select data field", OFFSET(data_field), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1, FLAGS, "data_field" },
+    { "auto",   "pick first one that appears", 0, AV_OPT_TYPE_CONST, { .i64 =-1 }, 0, 0, FLAGS, "data_field" },
+    { "first",  NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, FLAGS, "data_field" },
+    { "second", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, FLAGS, "data_field" },
+    { NULL },
+};
+
+AVFILTER_DEFINE_CLASS(split_cc);
+
+static const AVFilterPad inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_VIDEO,
+        .filter_frame = filter_frame,
+        .config_props = config_input,
+    },
+};
+
+static const AVFilterPad outputs[] = {
+    {
+        .name          = "video_passthrough",
+        .type          = AVMEDIA_TYPE_VIDEO,
+        .config_props  = config_video_output,
+    },
+    {
+        .name          = "subtitles",
+        .type          = AVMEDIA_TYPE_SUBTITLE,
+        .request_frame = request_sub_frame,
+        .config_props  = config_sub_output,
+    },
+};
+
+const AVFilter ff_sf_splitcc = {
+    .name           = "splitcc",
+    .description    = NULL_IF_CONFIG_SMALL("Extract closed-caption (A53) data from video as subtitle stream."),
+    .init           = init,
+    .uninit         = uninit,
+    .priv_size      = sizeof(SplitCaptionsContext),
+    .priv_class     = &split_cc_class,
+    .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_QUERY_FUNC(query_formats),
+};
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH 18/24] avfilter/graphicsub2text: Add new graphicsub2text filter (OCR)
  2022-01-14  1:13 [FFmpeg-devel] [PATCH 00/24] Subtitle Filtering 2022 ffmpegagent
                   ` (16 preceding siblings ...)
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 17/24] avfilter/splitcc: Add splitcc filter for closed caption handling ffmpegagent
@ 2022-01-14  1:13 ` ffmpegagent
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 19/24] avfilter/subscale: Add filter for scaling and/or re-arranging graphical subtitles ffmpegagent
                   ` (6 subsequent siblings)
  24 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-14  1:13 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: softworkz

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 configure                        |    1 +
 doc/filters.texi                 |   55 ++
 libavfilter/Makefile             |    2 +
 libavfilter/allfilters.c         |    1 +
 libavfilter/sf_graphicsub2text.c | 1132 ++++++++++++++++++++++++++++++
 5 files changed, 1191 insertions(+)
 create mode 100644 libavfilter/sf_graphicsub2text.c

diff --git a/configure b/configure
index c1d2bc41c2..ee7afffb05 100755
--- a/configure
+++ b/configure
@@ -3665,6 +3665,7 @@ frei0r_filter_deps="frei0r"
 frei0r_src_filter_deps="frei0r"
 fspp_filter_deps="gpl"
 gblur_vulkan_filter_deps="vulkan spirv_compiler"
+graphicsub2text_filter_deps="libtesseract"
 hflip_vulkan_filter_deps="vulkan spirv_compiler"
 histeq_filter_deps="gpl"
 hqdn3d_filter_deps="gpl"
diff --git a/doc/filters.texi b/doc/filters.texi
index 265a267e9d..7bc02b9415 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -25897,6 +25897,61 @@ ffmpeg -i "https://streams.videolan.org/ffmpeg/mkv_subtitles.mkv" -filter_comple
 @end example
 @end itemize
 
+@section graphicsub2text
+
+Converts graphic subtitles to text subtitles by performing OCR.
+
+For this filter to be available, ffmpeg needs to be compiled with libtesseract (see https://github.com/tesseract-ocr/tesseract).
+Language models need to be downloaded from https://github.com/tesseract-ocr/tessdata and put into as subfolder named 'tessdata' or into a folder specified via the environment variable 'TESSDATA_PREFIX'.
+The path can also be specified via filter option (see below).
+
+Note: These models are including the data for both OCR modes.
+
+Inputs:
+- 0: Subtitles [bitmap]
+
+Outputs:
+- 0: Subtitles [text]
+
+It accepts the following parameters:
+
+@table @option
+@item ocr_mode
+The character recognition mode to use.
+
+Supported OCR modes are:
+
+@table @var
+@item 0, tesseract
+This is the classic libtesseract operation mode. It is fast but less accurate than LSTM.
+@item 1, lstm
+Newer OCR implementation based on ML models. Provides usually better results, requires more processing resources.
+@item 2, both
+Use a combination of both modes.
+@end table
+
+@item tessdata_path
+The path to a folder containing the language models to be used.
+
+@item language
+The recognition language. It needs to match the first three characters of a  language model file in the tessdata path.
+
+@end table
+
+
+@subsection Examples
+
+@itemize
+@item
+Convert DVB graphic subtitles to ASS (text) subtitles
+
+Note: For this to work, you need to have the data file 'eng.traineddata' in a 'tessdata' subfolder (see above).
+@example
+ffmpeg ffmpeg -loglevel verbose -i "https://streams.videolan.org/streams/ts/video_subs_ttxt%2Bdvbsub.ts" -filter_complex "[0:13]graphicsub2text=ocr_mode=both" -c:s ass -y output.mkv
+@end example
+@end itemize
+
+
 @section graphicsub2video
 
 Renders graphic subtitles as video frames.
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index c6a4a4f5ae..ead3e38507 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -299,6 +299,8 @@ OBJS-$(CONFIG_GBLUR_VULKAN_FILTER)           += vf_gblur_vulkan.o vulkan.o vulka
 OBJS-$(CONFIG_GEQ_FILTER)                    += vf_geq.o
 OBJS-$(CONFIG_GRADFUN_FILTER)                += vf_gradfun.o
 OBJS-$(CONFIG_GRAPHICSUB2VIDEO_FILTER)       += vf_overlaygraphicsubs.o framesync.o
+OBJS-$(CONFIG_GRAPHICSUB2TEXT_FILTER)        += sf_graphicsub2text.o
+OBJS-$(CONFIG_GRAPHICSUB2VIDEO_FILTER)       += vf_overlaygraphicsubs.o framesync.o
 OBJS-$(CONFIG_GRAPHMONITOR_FILTER)           += f_graphmonitor.o
 OBJS-$(CONFIG_GRAYWORLD_FILTER)              += vf_grayworld.o
 OBJS-$(CONFIG_GREYEDGE_FILTER)               += vf_colorconstancy.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 50498e8ec4..34576016ce 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -548,6 +548,7 @@ extern const AVFilter ff_avf_showwaves;
 extern const AVFilter ff_avf_showwavespic;
 extern const AVFilter ff_vaf_spectrumsynth;
 extern const AVFilter ff_sf_censor;
+extern const AVFilter ff_sf_graphicsub2text;
 extern const AVFilter ff_sf_showspeaker;
 extern const AVFilter ff_sf_splitcc;
 extern const AVFilter ff_sf_stripstyles;
diff --git a/libavfilter/sf_graphicsub2text.c b/libavfilter/sf_graphicsub2text.c
new file mode 100644
index 0000000000..fcabb19892
--- /dev/null
+++ b/libavfilter/sf_graphicsub2text.c
@@ -0,0 +1,1132 @@
+/*
+ * 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
+ * subtitle filter to convert graphical subs to text subs via OCR
+ */
+
+#include <tesseract/capi.h>
+#include <libavutil/ass_internal.h>
+
+#include "drawutils.h"
+#include "libavutil/opt.h"
+#include "subtitles.h"
+
+#include "libavcodec/elbg.h"
+
+enum {
+    RFLAGS_NONE         = 0,
+    RFLAGS_HALIGN       = 1 << 0,
+    RFLAGS_VALIGN       = 1 << 1,
+    RFLAGS_FBOLD        = 1 << 2,
+    RFLAGS_FITALIC      = 1 << 3,
+    RFLAGS_FUNDERLINE   = 1 << 4,
+    RFLAGS_FONT         = 1 << 5,
+    RFLAGS_FONTSIZE     = 1 << 6,
+    RFLAGS_COLOR        = 1 << 7,
+    RFLAGS_OUTLINECOLOR = 1 << 8,
+    RFLAGS_ALL = RFLAGS_HALIGN | RFLAGS_VALIGN | RFLAGS_FBOLD | RFLAGS_FITALIC | RFLAGS_FUNDERLINE |
+                RFLAGS_FONT | RFLAGS_FONTSIZE | RFLAGS_COLOR | RFLAGS_OUTLINECOLOR,
+};
+
+typedef struct SubOcrContext {
+    const AVClass *class;
+    int w, h;
+
+    TessBaseAPI *tapi;
+    TessOcrEngineMode ocr_mode;
+    char *tessdata_path;
+    char *language;
+    int preprocess_images;
+    int dump_bitmaps;
+    int delay_when_no_duration;
+    int recognize;
+    double font_size_factor;
+
+    int readorder_counter;
+
+    AVFrame *pending_frame;
+    AVBufferRef *subtitle_header;
+    AVBPrint buffer;
+
+    // Color Quantization Fields
+    struct ELBGContext *ctx;
+    AVLFG lfg;
+    int *codeword;
+    int *codeword_closest_codebook_idxs;
+    int *codebook;
+    int r_idx, g_idx, b_idx, a_idx;
+    int64_t last_subtitle_pts;
+} SubOcrContext;
+
+typedef struct OcrImageProps {
+    int background_color_index;
+    int fill_color_index;
+
+} OcrImageProps;
+
+static int64_t ms_to_avtb(int64_t ms)
+{
+    return av_rescale_q(ms, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q);
+}
+
+static int create_ass_header(AVFilterContext* ctx)
+{
+    SubOcrContext* s = ctx->priv;
+
+    if (!(s->w && s->h)) {
+        av_log(ctx, AV_LOG_WARNING, "create_ass_header: no width and height specified!\n");
+        s->w = ASS_DEFAULT_PLAYRESX;
+        s->h = ASS_DEFAULT_PLAYRESY;
+    }
+
+    char* subtitle_header_text = avpriv_ass_get_subtitle_header_full(s->w, s->h, ASS_DEFAULT_FONT, ASS_DEFAULT_FONT_SIZE,
+        ASS_DEFAULT_COLOR, ASS_DEFAULT_COLOR, ASS_DEFAULT_BACK_COLOR, ASS_DEFAULT_BACK_COLOR, ASS_DEFAULT_BOLD,
+        ASS_DEFAULT_ITALIC, ASS_DEFAULT_UNDERLINE, ASS_DEFAULT_BORDERSTYLE, ASS_DEFAULT_ALIGNMENT, 0);
+
+    if (!subtitle_header_text)
+        return AVERROR(ENOMEM);
+
+    s->subtitle_header = av_buffer_create((uint8_t*)subtitle_header_text, strlen(subtitle_header_text) + 1, NULL, NULL, 0);
+
+    if (!s->subtitle_header)
+        return AVERROR(ENOMEM);
+
+    return 0;
+}
+
+static int init(AVFilterContext *ctx)
+{
+    SubOcrContext *s = ctx->priv;
+    const char* tver = TessVersion();
+    uint8_t rgba_map[4];
+    int ret;
+
+    s->tapi = TessBaseAPICreate();
+
+    if (!s->tapi || !tver || !strlen(tver)) {
+        av_log(ctx, AV_LOG_ERROR, "Failed to access libtesseract\n");
+        return AVERROR(ENOSYS);
+    }
+
+    av_log(ctx, AV_LOG_VERBOSE, "Initializing libtesseract, version: %s\n", tver);
+
+    ret = TessBaseAPIInit4(s->tapi, s->tessdata_path, s->language, s->ocr_mode, NULL, 0, NULL, NULL, 0, 1);
+    if (ret < 0 ) {
+        av_log(ctx, AV_LOG_ERROR, "Failed to initialize libtesseract. Error: %d\n", ret);
+        return AVERROR(ENOSYS);
+    }
+
+    ret = TessBaseAPISetVariable(s->tapi, "tessedit_char_blacklist", "|");
+    if (ret < 0 ) {
+        av_log(ctx, AV_LOG_ERROR, "Failed to set 'tessedit_char_blacklist'. Error: %d\n", ret);
+        return AVERROR(EINVAL);
+    }
+
+    av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
+
+    ff_fill_rgba_map(&rgba_map[0], AV_PIX_FMT_RGB32);
+
+    s->r_idx = rgba_map[0]; // R
+    s->g_idx = rgba_map[1]; // G
+    s->b_idx = rgba_map[2]; // B
+    s->a_idx = rgba_map[3]; // A
+
+    av_lfg_init(&s->lfg, 123456789);
+
+    return 0;
+}
+
+static void uninit(AVFilterContext *ctx)
+{
+    SubOcrContext *s = ctx->priv;
+
+    av_buffer_unref(&s->subtitle_header);
+    av_bprint_finalize(&s->buffer, NULL);
+
+    if (s->tapi) {
+        TessBaseAPIEnd(s->tapi);
+        TessBaseAPIDelete(s->tapi);
+    }
+
+    avpriv_elbg_free(&s->ctx);
+}
+
+static int query_formats(AVFilterContext *ctx)
+{
+    AVFilterFormats *formats, *formats2;
+    AVFilterLink *inlink = ctx->inputs[0];
+    AVFilterLink *outlink = ctx->outputs[0];
+    static const enum AVSubtitleType in_fmts[] = { AV_SUBTITLE_FMT_BITMAP, AV_SUBTITLE_FMT_NONE };
+    static const enum AVSubtitleType out_fmts[] = { AV_SUBTITLE_FMT_ASS, AV_SUBTITLE_FMT_NONE };
+    int ret;
+
+    /* set input format */
+    formats = ff_make_format_list(in_fmts);
+    if ((ret = ff_formats_ref(formats, &inlink->outcfg.formats)) < 0)
+        return ret;
+
+    /* set output format */
+    formats2 = ff_make_format_list(out_fmts);
+    if ((ret = ff_formats_ref(formats2, &outlink->incfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int config_input(AVFilterLink *inlink)
+{
+    AVFilterContext *ctx = inlink->dst;
+    SubOcrContext *s = ctx->priv;
+
+    if (s->w <= 0 || s->h <= 0) {
+        s->w = inlink->w;
+        s->h = inlink->h;
+    }
+
+    return create_ass_header(ctx);
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    AVFilterLink *inlink = outlink->src->inputs[0];
+    const AVFilterContext *ctx  = outlink->src;
+    SubOcrContext *s = ctx->priv;
+
+    outlink->format = AV_SUBTITLE_FMT_ASS;
+    outlink->w = s->w;
+    outlink->h = s->h;
+    outlink->time_base = inlink->time_base;
+    outlink->frame_rate = inlink->frame_rate;
+
+    return 0;
+}
+
+static void free_subtitle_area(AVSubtitleArea *area)
+{
+    for (unsigned n = 0; n < FF_ARRAY_ELEMS(area->buf); n++)
+        av_buffer_unref(&area->buf[n]);
+
+    av_freep(&area->text);
+    av_freep(&area->ass);
+    av_free(area);
+
+}
+
+static AVSubtitleArea *copy_subtitle_area(const AVSubtitleArea *src)
+{
+    AVSubtitleArea *dst = av_mallocz(sizeof(AVSubtitleArea));
+
+    if (!dst)
+        return NULL;
+
+    dst->x         =  src->x;
+    dst->y         =  src->y;
+    dst->w         =  src->w;
+    dst->h         =  src->h;
+    dst->nb_colors =  src->nb_colors;
+    dst->type      =  src->type;
+    dst->flags     =  src->flags;
+
+    for (unsigned i = 0; i < AV_NUM_BUFFER_POINTERS; i++) {
+        if (src->h > 0 && src->w > 0 && src->buf[i]) {
+            dst->buf[0] = av_buffer_ref(src->buf[i]);
+            if (!dst->buf[i])
+                return NULL;
+
+            const int ret = av_buffer_make_writable(&dst->buf[i]);
+            if (ret < 0)
+                return NULL;
+
+            dst->linesize[i] = src->linesize[i];
+        }
+    }
+
+    memcpy(&dst->pal[0], &src->pal[0], sizeof(src->pal[0]) * 256);
+
+
+    return dst;
+}
+
+static int quantize_image_colors(SubOcrContext *const s, AVSubtitleArea *subtitle_area)
+{
+    const int num_quantized_colors = 3;
+    int k, ret;
+    const int codeword_length = subtitle_area->w * subtitle_area->h;
+    uint8_t *src_data = subtitle_area->buf[0]->data;
+
+    if (subtitle_area->nb_colors <= num_quantized_colors) {
+        av_log(s, AV_LOG_DEBUG, "No need to quantize colors. Color count: %d\n", subtitle_area->nb_colors);
+        return 0;
+    }
+
+    // Convert palette to grayscale
+    for (int i = 0; i < subtitle_area->nb_colors; i++) {
+        uint8_t *color        = (uint8_t *)&subtitle_area->pal[i];
+        const uint8_t average = (uint8_t)(((int)color[s->r_idx] + color[s->g_idx] + color[s->b_idx]) / 3);
+        color[s->b_idx]       = average;
+        color[s->g_idx]       = average;
+        color[s->r_idx]       = average;
+    }
+
+    /* Re-Initialize */
+    s->codeword = av_realloc_f(s->codeword, codeword_length, 4 * sizeof(*s->codeword));
+    if (!s->codeword)
+        return AVERROR(ENOMEM);
+
+    s->codeword_closest_codebook_idxs = av_realloc_f(s->codeword_closest_codebook_idxs,
+        codeword_length, sizeof(*s->codeword_closest_codebook_idxs));
+    if (!s->codeword_closest_codebook_idxs)
+        return AVERROR(ENOMEM);
+
+    s->codebook = av_realloc_f(s->codebook, num_quantized_colors, 4 * sizeof(*s->codebook));
+    if (!s->codebook)
+        return AVERROR(ENOMEM);
+
+    /* build the codeword */
+    k = 0;
+    for (int i = 0; i < subtitle_area->h; i++) {
+        uint8_t *p = src_data;
+        for (int j = 0; j < subtitle_area->w; j++) {
+            const uint8_t *color = (uint8_t *)&subtitle_area->pal[*p];
+            s->codeword[k++] = color[s->b_idx];
+            s->codeword[k++] = color[s->g_idx];
+            s->codeword[k++] = color[s->r_idx];
+            s->codeword[k++] = color[s->a_idx];
+            p++;
+        }
+        src_data += subtitle_area->linesize[0];
+    }
+
+    /* compute the codebook */
+    ret = avpriv_elbg_do(&s->ctx, s->codeword, 4, codeword_length, s->codebook,
+        num_quantized_colors, 1, s->codeword_closest_codebook_idxs, &s->lfg, 0);
+    if (ret < 0)
+        return ret;
+
+    /* Write Palette */
+    for (int i = 0; i < num_quantized_colors; i++) {
+        subtitle_area->pal[i] = s->codebook[i*4+3] << 24  |
+                    (s->codebook[i*4+2] << 16) |
+                    (s->codebook[i*4+1] <<  8) |
+                    (s->codebook[i*4  ] <<  0);
+    }
+
+
+    av_log(s, AV_LOG_DEBUG, "Quantized colors from %d to %d\n", subtitle_area->nb_colors, num_quantized_colors);
+
+    subtitle_area->nb_colors = num_quantized_colors;
+    src_data = subtitle_area->buf[0]->data;
+
+    /* Write Image */
+    k = 0;
+    for (int i = 0; i < subtitle_area->h; i++) {
+        uint8_t *p = src_data;
+        for (int j = 0; j < subtitle_area->w; j++, p++) {
+            p[0] = (uint8_t)s->codeword_closest_codebook_idxs[k++];
+        }
+
+        src_data += subtitle_area->linesize[0];
+    }
+
+    return ret;
+}
+
+#define MEASURE_LINE_COUNT 6
+
+static uint8_t get_background_color_index(SubOcrContext *const s, const AVSubtitleArea *subtitle_area)
+{
+    const int linesize = subtitle_area->linesize[0];
+    int index_counts[256] = {0};
+    const unsigned int line_offsets[MEASURE_LINE_COUNT] = {
+        0,
+        linesize,
+        2 * linesize,
+        (subtitle_area->h - 3) * linesize,
+        (subtitle_area->h - 2) * linesize,
+        (subtitle_area->h - 1) * linesize
+    };
+
+    const uint8_t *src_data = subtitle_area->buf[0]->data;
+    const uint8_t tl = src_data[0];
+    const uint8_t tr = src_data[subtitle_area->w - 1];
+    const uint8_t bl = src_data[(subtitle_area->h - 1) * linesize + 0];
+    const uint8_t br = src_data[(subtitle_area->h - 1) * linesize + subtitle_area->w - 1];
+    uint8_t max_index = 0;
+    int max_count;
+
+    // When all corner pixels are equal, assume that as background color
+    if (tl == tr == bl == br || subtitle_area->h < 6)
+        return tl;
+
+    for (unsigned int i = 0; i < MEASURE_LINE_COUNT; i++) {
+        uint8_t *p = subtitle_area->buf[0]->data + line_offsets[i];
+        for (int k = 0; k < subtitle_area->w; k++)
+            index_counts[p[k]]++;
+    }
+
+    max_count = index_counts[0];
+
+    for (uint8_t i = 1; i < subtitle_area->nb_colors; i++) {
+        if (index_counts[i] > max_count) {
+            max_count = index_counts[i];
+            max_index = i;
+        }
+    }
+
+    return max_index;
+}
+
+static uint8_t get_text_color_index(SubOcrContext *const s, const AVSubtitleArea *subtitle_area, const uint8_t bg_color_index, uint8_t *outline_color_index)
+{
+    const int linesize = subtitle_area->linesize[0];
+    int index_counts[256] = {0};
+    uint8_t last_index = bg_color_index;
+    int max_count, min_req_count;
+    uint8_t max_index = 0;
+
+    for (int i = 3; i < subtitle_area->h - 3; i += 5) {
+        const uint8_t *p = subtitle_area->buf[0]->data + ((ptrdiff_t)linesize * i);
+        for (int k = 0; k < subtitle_area->w; k++) {
+            const uint8_t cur_index = p[k];
+
+            // When color hasn't changed, continue
+            if (cur_index == last_index)
+                continue;
+
+            if (cur_index != bg_color_index)
+                index_counts[cur_index]++;
+
+            last_index = cur_index;
+        }
+    }
+
+    max_count = index_counts[0];
+
+    for (uint8_t i = 1; i < subtitle_area->nb_colors; i++) {
+        if (index_counts[i] > max_count) {
+            max_count = index_counts[i];
+            max_index = i;
+        }
+    }
+
+    min_req_count = max_count / 3;
+
+    for (uint8_t i = 1; i < subtitle_area->nb_colors; i++) {
+        if (index_counts[i] < min_req_count)
+            index_counts[i] = 0;
+    }
+
+    *outline_color_index = max_index;
+
+    index_counts[max_index] = 0;
+    max_count = 0;
+
+    for (uint8_t i = 0; i < subtitle_area->nb_colors; i++) {
+        if (index_counts[i] > max_count) {
+            max_count = index_counts[i];
+            max_index = i;
+        }
+    }
+
+    if (*outline_color_index == max_index)
+        *outline_color_index = 255;
+
+    return max_index;
+}
+
+static void make_image_binary(SubOcrContext *const s, AVSubtitleArea *subtitle_area, const uint8_t text_color_index)
+{
+    for (int i = 0; i < subtitle_area->nb_colors; i++) {
+
+        if (i != text_color_index)
+            subtitle_area->pal[i] = 0xffffffff;
+        else
+            subtitle_area->pal[i] = 0xff000000;
+    }
+}
+
+static int get_crop_region(SubOcrContext *const s, const AVSubtitleArea *subtitle_area, uint8_t text_color_index, int *x, int *y, int *w, int *h)
+{
+    const int linesize = subtitle_area->linesize[0];
+    int max_y = 0, max_x = 0;
+    int min_y = subtitle_area->h - 1, min_x = subtitle_area->w - 1;
+
+    for (int i = 0; i < subtitle_area->h; i += 3) {
+        const uint8_t *p = subtitle_area->buf[0]->data + ((ptrdiff_t)linesize * i);
+        for (int k = 0; k < subtitle_area->w; k += 2) {
+            if (p[k] == text_color_index) {
+                min_y = FFMIN(min_y, i);
+                min_x = FFMIN(min_x, k);
+                max_y = FFMAX(max_y, i);
+                max_x = FFMAX(max_x, k);
+            }
+        }
+    }
+
+    if (max_y <= min_y || max_x <= min_x) {
+        av_log(s, AV_LOG_WARNING, "Unable to detect crop region\n");
+        *x = 0;
+        *y = 0;
+        *w = subtitle_area->w;
+        *h = subtitle_area->h;
+    }    else {
+        *x = FFMAX(min_x - 10, 0);
+        *y = FFMAX(min_y - 10, 0);
+        *w = FFMIN(max_x + 10 - *x, (subtitle_area->w - *x));
+        *h = FFMIN(max_y + 10 - *y, (subtitle_area->h - *y));
+    }
+
+    return 0;
+}
+
+static int crop_area_bitmap(SubOcrContext *const s, AVSubtitleArea *subtitle_area, int x, int y, int w, int h)
+{
+    const int linesize = subtitle_area->linesize[0];
+    AVBufferRef *dst = av_buffer_allocz(h * w);
+    uint8_t *d;
+
+    if (!dst)
+        return AVERROR(ENOMEM);
+
+    d = dst->data;
+
+    for (int i = y; i < y + h; i++) {
+        const uint8_t *p = subtitle_area->buf[0]->data + ((ptrdiff_t)linesize * i);
+        for (int k = x; k < x + w; k++) {
+            *d = p[k];
+            d++;
+        }
+    }
+
+    subtitle_area->w = w;
+    subtitle_area->h = h;
+    subtitle_area->x += x;
+    subtitle_area->y += y;
+    subtitle_area->linesize[0] = w;
+    av_buffer_replace(&subtitle_area->buf[0], dst);
+
+    av_buffer_unref(&dst);
+    return 0;
+}
+
+#define R 0
+#define G 1
+#define B 2
+#define A 3
+
+static int print_code(AVBPrint *buf, int in_code, const char *fmt, ...)
+{
+    va_list vl;
+
+    if (!in_code)
+        av_bprint_chars(buf, '{', 1);
+
+    va_start(vl, fmt);
+    av_vbprintf(buf, fmt, vl);
+    va_end(vl);
+
+    return 1;
+}
+
+static int end_code(AVBPrint *buf, int in_code)
+{
+    if (in_code)
+        av_bprint_chars(buf, '}', 1);
+    return 0;
+}
+
+static uint8_t* create_grayscale_image(AVFilterContext *ctx, AVSubtitleArea *area, int invert)
+{
+    uint8_t gray_pal[256];
+    const size_t img_size = area->buf[0]->size;
+    const uint8_t* img    = area->buf[0]->data;
+    uint8_t* gs_img       = av_malloc(img_size);
+
+    if (!gs_img)
+        return NULL;
+
+    for (unsigned i = 0; i < 256; i++) {
+        const uint8_t *col = (uint8_t*)&area->pal[i];
+        const int val      = (int)col[3] * FFMAX3(col[0], col[1], col[2]);
+        gray_pal[i]        = (uint8_t)(val >> 8);
+    }
+
+    if (invert)
+        for (unsigned i = 0; i < img_size; i++)
+            gs_img[i]   = 255 - gray_pal[img[i]];
+    else
+        for (unsigned i = 0; i < img_size; i++)
+            gs_img[i]   = gray_pal[img[i]];
+
+    return gs_img;
+}
+
+static uint8_t* create_bitmap_image(AVFilterContext *ctx, AVSubtitleArea *area, const uint8_t text_color_index)
+{
+    const size_t img_size = area->buf[0]->size;
+    const uint8_t* img    = area->buf[0]->data;
+    uint8_t* gs_img       = av_malloc(img_size);
+
+    if (!gs_img)
+        return NULL;
+
+    for (unsigned i = 0; i < img_size; i++) {
+        if (img[i] == text_color_index)
+            gs_img[i]   = 0;
+        else
+            gs_img[i]   = 255;
+    }
+
+    return gs_img;
+}
+
+static void png_save(AVFilterContext *ctx, const char *filename, AVSubtitleArea *area)
+{
+    int x, y;
+    int v;
+    FILE *f;
+    char fname[40];
+    const uint8_t *data = area->buf[0]->data;
+
+    snprintf(fname, sizeof(fname), "%s.ppm", filename);
+
+    f = fopen(fname, "wb");
+    if (!f) {
+        perror(fname);
+        return;
+    }
+    fprintf(f, "P6\n"
+            "%d %d\n"
+            "%d\n",
+            area->w, area->h, 255);
+    for(y = 0; y < area->h; y++) {
+        for(x = 0; x < area->w; x++) {
+            const uint8_t index = data[y * area->linesize[0] + x];
+            v = (int)area->pal[index];
+            putc(v >> 16 & 0xff, f);
+            putc(v >> 8 & 0xff, f);
+            putc(v >> 0 & 0xff, f);
+        }
+    }
+
+    fclose(f);
+}
+
+static int get_max_index(int score[256])
+{
+    int max_val = 0, max_index = 0;
+
+    for (int i = 0; i < 256; i++) {
+        if (score[i] > max_val) {
+            max_val = score[i];
+            max_index = i;
+        }
+    }
+
+    return max_index;
+}
+
+static int get_word_colors(AVFilterContext *ctx, TessResultIterator* ri, const AVSubtitleArea* area, const AVSubtitleArea* original_area,
+                           uint8_t bg_color_index, uint8_t text_color_index, uint8_t outline_color_index,
+                           uint32_t* bg_color, uint32_t* text_color, uint32_t* outline_color)
+{
+    int left = 0, top = 0, right = 0, bottom = 0, ret;
+    int bg_score[256] = {0}, text_score[256] = {0}, outline_score[256] = {0};
+    int max_index;
+
+    ret = TessPageIteratorBoundingBox((TessPageIterator*)ri, RIL_WORD, &left, &top, &right, &bottom);
+    if (ret < 0) {
+        av_log(ctx, AV_LOG_WARNING, "get_word_colors: IteratorBoundingBox failed: %d\n", ret);
+        return  ret;
+    }
+
+    if (left >= area->w || right >= area->w || top >= area->h || bottom >= area->h) {
+        av_log(ctx, AV_LOG_WARNING, "get_word_colors: word bounding box (l: %d, t: %d r: %d, b: %d) out of image bounds (%dx%d)\n", left,top, right, bottom, area->w, area->h);
+        return  AVERROR(EINVAL);
+    }
+
+    for (int y = top; y < bottom; y += 3) {
+        uint8_t *p = area->buf[0]->data + (y * area->linesize[0]) + left;
+        uint8_t *porig = original_area->buf[0]->data + (y * original_area->linesize[0]) + left;
+
+        for (int x = left; x < right; x++, p++, porig++) {
+            if (*p == bg_color_index)
+                bg_score[*porig]++;
+            if (*p == text_color_index)
+                text_score[*porig]++;
+            if (*p == outline_color_index)
+                outline_score[*porig]++;
+        }
+    }
+
+    max_index = get_max_index(bg_score);
+    if (bg_score[max_index] > 0)
+        *bg_color = original_area->pal[max_index];
+
+    max_index = get_max_index(text_score);
+    if (text_score[max_index] > 0)
+        *text_color = original_area->pal[max_index];
+
+    max_index = get_max_index(outline_score);
+    if (outline_score[max_index] > 0)
+        *outline_color = original_area->pal[max_index];
+
+    return 0;
+}
+
+static int convert_area(AVFilterContext *ctx, AVSubtitleArea *area, const AVFrame *frame, const unsigned area_index, int *margin_v)
+{
+    SubOcrContext *s = ctx->priv;
+    char *ocr_text = NULL;
+    int ret = 0;
+    uint8_t *gs_img;
+    uint8_t bg_color_index;
+    uint8_t text_color_index = 255;
+    uint8_t outline_color_index = 255;
+    char filename[32];
+    AVSubtitleArea *original_area = copy_subtitle_area(area);
+
+    if (!original_area)
+        return AVERROR(ENOMEM);
+
+    if (area->w < 6 || area->h < 6) {
+        area->ass = NULL;
+        goto exit;
+    }
+
+    if (s->dump_bitmaps) {
+        snprintf(filename, sizeof(filename), "graphicsub2text_%"PRId64"_%d_original", frame->subtitle_timing.start_pts, area_index);
+        png_save(ctx, filename, area);
+    }
+
+    if (s->preprocess_images) {
+        ret = quantize_image_colors(s, area);
+        if (ret < 0)
+            goto exit;
+        if (s->dump_bitmaps && original_area->nb_colors != area->nb_colors) {
+            snprintf(filename, sizeof(filename), "graphicsub2text_%"PRId64"_%d_quantized", frame->subtitle_timing.start_pts, area_index);
+            png_save(ctx, filename, area);
+        }
+    }
+
+    bg_color_index = get_background_color_index(s, area);
+
+    if (s->preprocess_images) {
+        int x, y, w, h;
+
+        for (int i = 0; i < area->nb_colors; ++i) {
+            av_log(s, AV_LOG_DEBUG, "Color #%d: %0.8X\n", i, area->pal[i]);
+        }
+
+        text_color_index = get_text_color_index(s, area, bg_color_index, &outline_color_index);
+
+        get_crop_region(s, area, text_color_index, &x, &y, &w, &h);
+
+        if ((ret = crop_area_bitmap(s, area, x, y, w, h) < 0))
+            goto exit;
+
+        if ((ret = crop_area_bitmap(s, original_area, x, y, w, h) < 0))
+            goto exit;
+
+        make_image_binary(s, area, text_color_index);
+
+        if (s->dump_bitmaps) {
+            snprintf(filename, sizeof(filename), "graphicsub2text_%"PRId64"_%d_preprocessed", frame->subtitle_timing.start_pts, area_index);
+            png_save(ctx, filename, area);
+        }
+
+        gs_img = create_bitmap_image(ctx, area, text_color_index);
+    } else
+        gs_img = create_grayscale_image(ctx, area, 1);
+
+    if (!gs_img) {
+        ret = AVERROR(ENOMEM);
+        goto exit;
+    }
+
+    area->type = AV_SUBTITLE_FMT_ASS;
+    TessBaseAPISetImage(s->tapi, gs_img, area->w, area->h, 1, area->linesize[0]);
+
+    TessBaseAPISetSourceResolution(s->tapi, 72);
+
+    ret = TessBaseAPIRecognize(s->tapi, NULL);
+    if (ret == 0)
+        ocr_text = TessBaseAPIGetUTF8Text(s->tapi);
+
+    if (!ocr_text || !strlen(ocr_text)) {
+        av_log(ctx, AV_LOG_WARNING, "OCR didn't return a text. ret=%d\n", ret);
+        area->ass = NULL;
+
+        goto exit;
+    }
+
+    const size_t len = strlen(ocr_text);
+    if (len > 0 && ocr_text[len - 1] == '\n')
+        ocr_text[len - 1] = 0;
+
+    av_log(ctx, AV_LOG_VERBOSE, "OCR Result: %s\n", ocr_text);
+
+    area->ass = av_strdup(ocr_text);
+    TessDeleteText(ocr_text);
+
+    // End of simple recognition
+
+    if (s->recognize != RFLAGS_NONE) {
+        TessResultIterator* ri = 0;
+        const TessPageIteratorLevel level = RIL_WORD;
+        int cur_is_bold = 0, cur_is_italic = 0, cur_is_underlined = 0, cur_pointsize = 0;
+        uint32_t cur_text_color = 0, cur_bg_color = 0, cur_outline_color = 0;
+
+        char *cur_font_name = NULL;
+        int valign = 0; // 0: bottom, 4: top, 8 middle
+        int halign = 2; // 1: left, 2: center, 3: right
+        int in_code = 0;
+        double font_factor = (0.000666 * (s->h - 480) + 1) * s->font_size_factor;
+
+        av_freep(&area->ass);
+        av_bprint_clear(&s->buffer);
+
+        ri = TessBaseAPIGetIterator(s->tapi);
+
+        // Horizontal Alignment
+        if (s->w && s->recognize & RFLAGS_HALIGN) {
+            int left_margin = area->x;
+            int right_margin = s->w - area->x - area->w;
+            double relative_diff = ((double)left_margin - right_margin) / s->w;
+
+            if (FFABS(relative_diff) < 0.1)
+                halign = 2; // center
+            else if (relative_diff > 0)
+                halign = 3; // right
+            else
+                halign = 1; // left
+        }
+
+        // Vertical Alignment
+        if (s->h && frame->height && s->recognize & RFLAGS_VALIGN) {
+            int left = 0, top = 0, right = 0, bottom = 0;
+
+            TessPageIteratorBoundingBox((TessPageIterator*)ri, RIL_TEXTLINE, &left, &top, &right, &bottom);
+            av_log(s, AV_LOG_DEBUG, "RIL_TEXTLINE - TOP: %d  BOTTOM: %d HEIGHT: %d\n", top, bottom, bottom - top);
+
+            TessPageIteratorBoundingBox((TessPageIterator*)ri, RIL_BLOCK, &left, &top, &right, &bottom);
+
+            const int vertical_pos = area->y + area->h / 2;
+            if (vertical_pos < s->h / 3) {
+                *margin_v = area->y + top;
+                valign = 4;
+            }
+            else if (vertical_pos < s->h / 3 * 2) {
+                *margin_v = 0;
+                valign = 8;
+            } else {
+                *margin_v = frame->height - area->y - area->h;
+                valign = 0;
+            }
+        }
+
+        if (*margin_v < 0)
+            *margin_v = 0;
+
+        // Set alignment when not default (2)
+        if ((valign | halign) != 2)
+            in_code = print_code(&s->buffer, in_code, "\\a%d", valign | halign);
+
+        do {
+            int is_bold, is_italic, is_underlined, is_monospace, is_serif, is_smallcaps, pointsize, font_id;
+            char* word;
+            const char *font_name = TessResultIteratorWordFontAttributes(ri, &is_bold, &is_italic, &is_underlined, &is_monospace, &is_serif, &is_smallcaps, &pointsize, &font_id);
+            uint32_t text_color = 0, bg_color = 0, outline_color = 0;
+
+            if (cur_is_underlined && !is_underlined && s->recognize & RFLAGS_FUNDERLINE)
+                in_code = print_code(&s->buffer, in_code, "\\u0");
+
+            if (cur_is_bold && !is_bold && s->recognize & RFLAGS_FBOLD)
+                in_code = print_code(&s->buffer, in_code, "\\b0");
+
+            if (cur_is_italic && !is_italic && s->recognize & RFLAGS_FITALIC)
+                in_code = print_code(&s->buffer, in_code, "\\i0");
+
+
+            if (TessPageIteratorIsAtBeginningOf((TessPageIterator*)ri, RIL_TEXTLINE) && !TessPageIteratorIsAtBeginningOf((TessPageIterator*)ri, RIL_BLOCK)) {
+                in_code = end_code(&s->buffer, in_code);
+                av_bprintf(&s->buffer, "\\N");
+            }
+
+            if (get_word_colors(ctx, ri, area, original_area, bg_color_index, text_color_index, outline_color_index, &bg_color, &text_color, &outline_color) == 0) {
+
+                if (text_color > 0 && cur_text_color != text_color && s->recognize & RFLAGS_COLOR) {
+                    const uint8_t* tval = (uint8_t*)&text_color;
+                    const int color = (int)tval[R] << 16 | (int)tval[G] << 8 | tval[B];
+
+                    in_code = print_code(&s->buffer, in_code, "\\1c&H%0.6X&", color);
+                    if (tval[A] != 255)
+                        in_code = print_code(&s->buffer, in_code, "\\1a&H%0.2X&", 255 - tval[A]);
+                }
+
+                if (outline_color > 0 && cur_outline_color != outline_color && s->recognize & RFLAGS_OUTLINECOLOR) {
+                    const uint8_t* tval = (uint8_t*)&outline_color;
+                    const int color = (int)tval[R] << 16 | (int)tval[G] << 8 | tval[B];
+
+                    in_code = print_code(&s->buffer, in_code, "\\3c&H%0.6X&\\bord2", color);
+                    in_code = print_code(&s->buffer, in_code, "\\3a&H%0.2X&", FFMIN(255 - tval[A], 30));
+                }
+
+                cur_text_color = text_color;
+                cur_outline_color = outline_color;
+            }
+
+            if (font_name && strlen(font_name) && s->recognize & RFLAGS_FONT) {
+                if (!cur_font_name || !strlen(cur_font_name) || strcmp(cur_font_name, font_name) != 0) {
+                    char *sanitized_font_name = av_strireplace(font_name, "_", " ");
+                    if (!sanitized_font_name) {
+                        ret = AVERROR(ENOMEM);
+                        goto exit;
+                    }
+
+                    in_code = print_code(&s->buffer, in_code, "\\fn%s", sanitized_font_name);
+                    av_freep(&sanitized_font_name);
+
+                    if (cur_font_name)
+                        av_freep(&cur_font_name);
+                    cur_font_name = av_strdup(font_name);
+                    if (!cur_font_name) {
+                        ret = AVERROR(ENOMEM);
+                        goto exit;
+                    }
+                }
+            }
+
+            if (pointsize != cur_pointsize && s->recognize & RFLAGS_FONTSIZE) {
+                av_log(s, AV_LOG_DEBUG, "pointsize - pointsize: %d\n", pointsize);
+                in_code = print_code(&s->buffer, in_code, "\\fs%d", (int)(pointsize * font_factor));
+                cur_pointsize = pointsize;
+            }
+
+            if (is_italic && !cur_is_italic && s->recognize & RFLAGS_FITALIC)
+                in_code = print_code(&s->buffer, in_code, "\\i1");
+
+            if (is_bold && !cur_is_bold && s->recognize & RFLAGS_FBOLD)
+                in_code = print_code(&s->buffer, in_code, "\\b1");
+
+            if (is_underlined && !cur_is_underlined && s->recognize & RFLAGS_FUNDERLINE)
+                in_code = print_code(&s->buffer, in_code, "\\u1");
+
+            in_code = end_code(&s->buffer, in_code);
+
+            cur_is_underlined = is_underlined;
+            cur_is_bold = is_bold;
+            cur_is_italic = is_italic;
+
+            if (!TessPageIteratorIsAtBeginningOf((TessPageIterator*)ri, RIL_TEXTLINE))
+                av_bprint_chars(&s->buffer, ' ', 1);
+
+            word = TessResultIteratorGetUTF8Text(ri, level);
+            av_bprint_append_data(&s->buffer, word, strlen(word));
+            TessDeleteText(word);
+
+        } while (TessResultIteratorNext(ri, level));
+
+        if (!av_bprint_is_complete(&s->buffer))
+            ret = AVERROR(ENOMEM);
+        else {
+            av_log(ctx, AV_LOG_VERBOSE, "ASS Result: %s\n", s->buffer.str);
+            area->ass = av_strdup(s->buffer.str);
+        }
+
+        TessResultIteratorDelete(ri);
+        av_freep(&cur_font_name);
+    }
+
+exit:
+    free_subtitle_area(original_area);
+    av_freep(&gs_img);
+    av_buffer_unref(&area->buf[0]);
+    area->type = AV_SUBTITLE_FMT_ASS;
+
+    return ret;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFilterContext *ctx = inlink->dst;
+    SubOcrContext *s = ctx->priv;
+    AVFilterLink *outlink = inlink->dst->outputs[0];
+    int ret, frame_sent = 0;
+
+    if (s->pending_frame && !frame->repeat_sub) {
+        const int64_t pts_diff = frame->subtitle_timing.start_pts - s->pending_frame->subtitle_timing.start_pts;
+
+        if (pts_diff == 0) {
+            // This is just a repetition of the previous frame, ignore it
+            av_frame_free(&frame);
+            return 0;
+        }
+
+        s->pending_frame->subtitle_timing.duration = pts_diff;
+
+        if ((ret = av_buffer_replace(&s->pending_frame->subtitle_header, s->subtitle_header)) < 0)
+            return ret;
+
+        ret = ff_filter_frame(outlink, s->pending_frame);
+        s->pending_frame = NULL;
+        if (ret < 0)
+            return  ret;
+
+        frame_sent = 1;
+        s->last_subtitle_pts = frame->subtitle_timing.start_pts;
+
+        if (frame->num_subtitle_areas == 0) {
+            // No need to forward this empty frame
+            av_frame_free(&frame);
+            return 0;
+        }
+    }
+
+    if (frame->repeat_sub && frame->subtitle_timing.start_pts == s->last_subtitle_pts) {
+        // Ignore repeated frame
+        av_frame_free(&frame);
+        return 0;
+    }
+
+    s->last_subtitle_pts = frame->subtitle_timing.start_pts;
+
+    ret = av_frame_make_writable(frame);
+
+    if (ret < 0) {
+        av_frame_free(&frame);
+        return ret;
+    }
+
+    frame->format = AV_SUBTITLE_FMT_ASS;
+
+    av_log(ctx, AV_LOG_VERBOSE, "filter_frame sub_pts: %"PRIu64", duration: %"PRIu64", num_areas: %d\n",
+        frame->subtitle_timing.start_pts, frame->subtitle_timing.duration, frame->num_subtitle_areas);
+
+    if (frame->num_subtitle_areas > 1 &&
+        frame->subtitle_areas[0]->y > frame->subtitle_areas[frame->num_subtitle_areas - 1]->y) {
+
+        for (unsigned i = 0; i < frame->num_subtitle_areas / 2; i++)
+            FFSWAP(AVSubtitleArea*, frame->subtitle_areas[i], frame->subtitle_areas[frame->num_subtitle_areas - i - 1]);
+    }
+
+    for (int i = 0; i < frame->num_subtitle_areas; i++) {
+        AVSubtitleArea *area = frame->subtitle_areas[i];
+        int margin_v = 0;
+
+        ret = convert_area(ctx, area, frame, i, &margin_v);
+        if (ret < 0)
+            return ret;
+
+        if (area->ass && area->ass[0] != '\0') {
+
+            const int layer = s->recognize ? i : 0;
+            char *tmp = area->ass;
+            area->ass = avpriv_ass_get_dialog_ex(s->readorder_counter++, layer, "Default", NULL, 0, 0, margin_v, tmp);
+            av_free(tmp);
+        }
+    }
+
+    // When decoders can't determine the end time, they are setting it either to UINT32_NAX
+    // or 30s (dvbsub).
+    if (s->delay_when_no_duration && frame->num_subtitle_areas > 0 && frame->subtitle_timing.duration >= ms_to_avtb(29000)) {
+        // Can't send it without end time, wait for the next frame to determine the end_display time
+        s->pending_frame = frame;
+
+        if (frame_sent)
+            return 0;
+
+        // To keep all going, send an empty frame instead
+        frame = ff_get_subtitles_buffer(outlink, AV_SUBTITLE_FMT_ASS);
+        if (!frame)
+            return AVERROR(ENOMEM);
+
+        av_frame_copy_props(frame, s->pending_frame);
+        frame->subtitle_timing.start_pts = 0;
+        frame->subtitle_timing.duration = 1;
+        frame->repeat_sub = 1;
+    }
+
+    if ((ret = av_buffer_replace(&frame->subtitle_header, s->subtitle_header)) < 0)
+        return ret;
+
+    return ff_filter_frame(outlink, frame);
+}
+
+#define OFFSET(x) offsetof(SubOcrContext, x)
+#define FLAGS (AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption graphicsub2text_options[] = {
+    { "delay_when_no_duration", "delay output when duration is unknown", OFFSET(delay_when_no_duration), AV_OPT_TYPE_BOOL,   { .i64 = 0 },                         0,                  1,       FLAGS, NULL },
+    { "dump_bitmaps",           "save processed bitmaps as .ppm",        OFFSET(dump_bitmaps),           AV_OPT_TYPE_BOOL,   { .i64 = 0 },                         0,                  1,       FLAGS, NULL },
+    { "font_size_factor",       "font size adjustment factor",           OFFSET(font_size_factor),       AV_OPT_TYPE_DOUBLE, { .dbl = 1.0 },                       0.2,                5,       FLAGS, NULL },
+    { "language",               "ocr language",                          OFFSET(language),               AV_OPT_TYPE_STRING, { .str = "eng" },                     0,                  0,       FLAGS, NULL },
+    { "ocr_mode",               "set ocr mode",                          OFFSET(ocr_mode),               AV_OPT_TYPE_INT,    { .i64=OEM_TESSERACT_ONLY },          OEM_TESSERACT_ONLY, 2,       FLAGS, "ocr_mode" },
+    {   "tesseract",            "classic tesseract ocr",                 0,                              AV_OPT_TYPE_CONST,  { .i64=OEM_TESSERACT_ONLY },          0,                  0,       FLAGS, "ocr_mode" },
+    {   "lstm",                 "lstm (ML based)",                       0,                              AV_OPT_TYPE_CONST,  { .i64=OEM_LSTM_ONLY},                0,                  0,       FLAGS, "ocr_mode" },
+    {   "both",                 "use both models combined",              0,                              AV_OPT_TYPE_CONST,  { .i64=OEM_TESSERACT_LSTM_COMBINED }, 0,                  0,       FLAGS, "ocr_mode" },
+    { "preprocess_images",      "reduce colors, remove outlines",        OFFSET(preprocess_images),      AV_OPT_TYPE_BOOL,   { .i64 = 1 },                         0,                  1,       FLAGS, NULL },
+    { "recognize",              "detect fonts, styles and colors",       OFFSET(recognize),              AV_OPT_TYPE_FLAGS,  { .i64 = RFLAGS_ALL},                  0,                  INT_MAX, FLAGS, "reco_flags" },
+        { "none",         "no format detection",  0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_NONE         }, 0, 0, FLAGS, "reco_flags" },
+        { "halign",       "horizontal alignment", 0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_HALIGN       }, 0, 0, FLAGS, "reco_flags" },
+        { "valign",       "vertical alignment",   0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_VALIGN       }, 0, 0, FLAGS, "reco_flags" },
+        { "bold",         "font bold",            0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_FBOLD        }, 0, 0, FLAGS, "reco_flags" },
+        { "italic",       "font italic",          0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_FITALIC      }, 0, 0, FLAGS, "reco_flags" },
+        { "underline",    "font underline",       0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_FUNDERLINE   }, 0, 0, FLAGS, "reco_flags" },
+        { "font",         "font name",            0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_FONT         }, 0, 0, FLAGS, "reco_flags" },
+        { "fontsize",     "font size",            0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_FONTSIZE     }, 0, 0, FLAGS, "reco_flags" },
+        { "color",        "font color",           0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_COLOR        }, 0, 0, FLAGS, "reco_flags" },
+        { "outlinecolor", "outline color",        0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_OUTLINECOLOR }, 0, 0, FLAGS, "reco_flags" },
+    { "tessdata_path",          "path to tesseract data",                OFFSET(tessdata_path),          AV_OPT_TYPE_STRING, { .str = NULL },                      0,                  0,       FLAGS, NULL },
+    { NULL },
+};
+
+AVFILTER_DEFINE_CLASS(graphicsub2text);
+
+static const AVFilterPad inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .filter_frame = filter_frame,
+        .config_props = config_input,
+    },
+};
+
+static const AVFilterPad outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_SUBTITLE,
+        .config_props  = config_output,
+    },
+};
+
+const AVFilter ff_sf_graphicsub2text = {
+    .name          = "graphicsub2text",
+    .description   = NULL_IF_CONFIG_SMALL("Convert graphical subtitles to text subtitles via OCR"),
+    .init          = init,
+    .uninit        = uninit,
+    .priv_size     = sizeof(SubOcrContext),
+    .priv_class    = &graphicsub2text_class,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_QUERY_FUNC(query_formats),
+};
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH 19/24] avfilter/subscale: Add filter for scaling and/or re-arranging graphical subtitles
  2022-01-14  1:13 [FFmpeg-devel] [PATCH 00/24] Subtitle Filtering 2022 ffmpegagent
                   ` (17 preceding siblings ...)
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 18/24] avfilter/graphicsub2text: Add new graphicsub2text filter (OCR) ffmpegagent
@ 2022-01-14  1:13 ` ffmpegagent
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 20/24] avfilter/subfeed: add subtitle feed filter ffmpegagent
                   ` (5 subsequent siblings)
  24 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-14  1:13 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: softworkz

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 configure                 |   1 +
 doc/filters.texi          | 164 +++++++
 libavfilter/Makefile      |   1 +
 libavfilter/allfilters.c  |   1 +
 libavfilter/sf_subscale.c | 884 ++++++++++++++++++++++++++++++++++++++
 5 files changed, 1051 insertions(+)
 create mode 100644 libavfilter/sf_subscale.c

diff --git a/configure b/configure
index ee7afffb05..a8b7ce8a26 100755
--- a/configure
+++ b/configure
@@ -3731,6 +3731,7 @@ sr_filter_deps="avformat swscale"
 sr_filter_select="dnn"
 stereo3d_filter_deps="gpl"
 splitcc_filter_deps="avcodec"
+subscale_filter_deps="swscale avcodec"
 subtitles_filter_deps="avformat avcodec libass"
 super2xsai_filter_deps="gpl"
 pixfmts_super2xsai_test_deps="super2xsai_filter"
diff --git a/doc/filters.texi b/doc/filters.texi
index 7bc02b9415..ee6e184882 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -26303,6 +26303,170 @@ Set the rendering margin in pixels.
 For rendering, alway use the latest event only, which is covering the given point in time.
 @end table
 
+@section subscale
+
+Provides high-quality scaling and rearranging functionality for graphical subtitles.
+
+The subscale filter provides multiple approaches for manipulating
+the size and position of graphical subtitle rectangles wich can
+be combined or used separately.
+Scaling is performed by converting the palettized subtitle bitmaps
+to RGBA and re-quantization to palette colors afterwards via elbg algorithm.
+
+The two major operations are 'scale' and 're-arrange' with the
+latter being separated as 'arrange_h' and 'arrange_v'.
+
+
+Inputs:
+- 0: Subtitles [bitmap]
+
+Outputs:
+- 0: Subtitles [bitmap]
+
+It accepts the following parameters:
+
+@table @option
+
+@item w, width
+Set the width of the output.
+Width and height in case of graphical subtitles are just indicating
+a virtual size for which the output (consisting of 0-n bitmap rectangles)
+is intended to be displayed on.
+
+@item h, height
+Set the height of the output.
+
+@item margin_h
+Sets a horizontal margin to be preserverved when using any
+of the arrange modes.
+
+@item margin_v
+Sets a vertical margin to be preserverved when using any
+of the arrange modes.
+
+@item force_original_aspect_ratio
+Enable decreasing or increasing output video width or height if necessary to
+keep the original aspect ratio. Possible values:
+
+@table @samp
+@item disable
+Scale the video as specified and disable this feature.
+
+@item decrease
+The output video dimensions will automatically be decreased if needed.
+
+@item increase
+The output video dimensions will automatically be increased if needed.
+
+@end table
+
+
+@item scale_mode
+Specifies how subtitle bitmaps should be scaled.
+The scale factor is determined by the the factor between input
+and output size.
+
+@table @samp
+@item none
+Do not apply any common scaling.
+
+@item uniform
+Uniformly scale all subtitle bitmaps including their positions.
+
+@item uniform_no_reposition
+Uniformly scale all subtitle bitmaps without changing positions.
+
+@end table
+
+
+@item arrange_h
+Specifies how subtitle bitmaps should be arranged horizontally.
+
+@item arrange_v
+Specifies how subtitle bitmaps should be arranged vertically.
+
+
+@table @samp
+@item none
+Do not rearrange subtitle bitmaps.
+
+@item margin_no_scale
+Move subtitle bitmaps to be positioned inside the specified
+margin (margin_h or margin_v) when possible and without scaling.
+
+@item margin_and_scale
+Move subtitle bitmaps to be positioned inside the specified
+margin (margin_h or margin_v) and scale in case it doesn't fit.
+
+@item snapalign_no_scale
+Categorize subtitle bitmap positions as one of left/center/right
+or top/bottom/middle based on original positioning and apply
+these alignments for the target positioning.
+No scaling will be applied.
+
+@item snapalign_and_scale
+Categorize subtitle bitmap positions as one of left/center/right
+or top/bottom/middle based on original positioning and apply
+these alignments for the target positioning.
+Bitmaps that do not fit inside the margins borders are
+scaled to fit.
+@end table
+
+@item eval
+Set evaluation mode for the expressions (@option{width}, @option{height}).
+
+It accepts the following values:
+@table @samp
+@item init
+Evaluate expressions only once during the filter initialization.
+
+@item frame
+Evaluate expressions for each incoming frame. This is way slower than the
+@samp{init} mode since it requires all the scalers to be re-computed, but it
+allows advanced dynamic expressions.
+@end table
+
+Default value is @samp{init}.
+
+
+@item num_colors
+Set the number of palette colors for output images.
+Choose the maximum (256) when further processing is done (e.g.
+overlaying on a video).
+When subtitles will be encoded as bitmap subtitles (e.g. dvbsub),
+a smaller number of palette colors (e.g. 4-16) might need to be used, depending
+on the target format and codec.
+
+@item bitmap_width_align
+@item bitmap_height_align
+Make sure that subtitle bitmap sizes are a multiple of this
+value. Default is 2.
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Uniformly scale down video and bitmap subtitles and encode
+subtitles as dvbsub.
+@example
+ffmpeg -y -loglevel verbose -ss 32 -i "https://streams.videolan.org/samples/sub/largeres_vobsub.mkv" -filter_complex "[0:0]scale=480x270[vid1];[0:10]subscale=w=480:h=270:num_colors=16[sub1]" -map [vid1] -map [sub1] -c:s dvbsub -c:v libx265 output.ts
+@end example
+@item
+Squeeze video vertically and arrange subtitle bitmaps
+inside the video area without scaling, then overlay
+subtitles onto the video.
+@example
+ffmpeg -y -loglevel verbose -ss 32 -i "https://streams.videolan.org/samples/sub/largeres_vobsub.mkv" -filter_complex "[0:0]scale=1920x400,setsar=1[vid1];[0:10]subscale=w=1920:h=400:scale_mode=none:margin_h=50:arrange_h=margin_no_scale:arrange_v=margin_no_scale:margin_v=50:num_colors=256[sub1];[vid1][sub1]overlay_graphicsubs" -c:v libx265 output.ts
+@end example
+@item
+Scale both, a video and its embedded VOB subs, then encode them as separate streams into an MKV.
+@example
+ffmpeg -y -y -loglevel verbose -i "https://streams.videolan.org/samples/sub/largeres_vobsub.mkv" -filter_complex "[0:0]scale=720x576[vid1];[0:7]subscale=w=720:h=576:num_colors=16[sub1]" -map [vid1] -map [sub1] -c:s dvdsub out.mkv
+@end example
+@end itemize
+
 @c man end SUBTITLE FILTERS
 
 @chapter Multimedia Filters
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index ead3e38507..0e3e48613e 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -569,6 +569,7 @@ OBJS-$(CONFIG_SHOW_SPEAKER_FILTER)           += sf_textmod.o
 OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
 OBJS-$(CONFIG_SPLITCC_FILTER)                += sf_splitcc.o
 OBJS-$(CONFIG_STRIPSTYLES_FILTER)            += sf_stripstyles.o
+OBJS-$(CONFIG_SUBSCALE_FILTER)               += sf_subscale.o
 
 # multimedia filters
 OBJS-$(CONFIG_ABITSCOPE_FILTER)              += avf_abitscope.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 34576016ce..8e4f2feca3 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -552,6 +552,7 @@ extern const AVFilter ff_sf_graphicsub2text;
 extern const AVFilter ff_sf_showspeaker;
 extern const AVFilter ff_sf_splitcc;
 extern const AVFilter ff_sf_stripstyles;
+extern const AVFilter ff_sf_subscale;
 extern const AVFilter ff_sf_textmod;
 extern const AVFilter ff_svf_graphicsub2video;
 extern const AVFilter ff_svf_textsub2video;
diff --git a/libavfilter/sf_subscale.c b/libavfilter/sf_subscale.c
new file mode 100644
index 0000000000..04f0f16c27
--- /dev/null
+++ b/libavfilter/sf_subscale.c
@@ -0,0 +1,884 @@
+/*
+ * 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
+ * scale graphical subtitles filter
+ */
+
+#include <string.h>
+
+#include "drawutils.h"
+#include "internal.h"
+#include "scale_eval.h"
+#include "libavutil/eval.h"
+#include "libavutil/opt.h"
+#include "libswscale/swscale.h"
+
+#include "libavcodec/elbg.h"
+
+static const char *const var_names[] = {
+    "in_w",   "iw",
+    "in_h",   "ih",
+    "out_w",  "ow",
+    "out_h",  "oh",
+    "a",
+    "sar",
+    "dar",
+    "margin_h",
+    "margin_v",
+    NULL
+};
+
+enum var_name {
+    VAR_IN_W,   VAR_IW,
+    VAR_IN_H,   VAR_IH,
+    VAR_OUT_W,  VAR_OW,
+    VAR_OUT_H,  VAR_OH,
+    VAR_A,
+    VAR_SAR,
+    VAR_DAR,
+    VARS_B_H,
+    VARS_B_V,
+    VARS_NB
+};
+
+enum EvalMode {
+    EVAL_MODE_INIT,
+    EVAL_MODE_FRAME,
+    EVAL_MODE_NB
+};
+
+enum SubScaleMode {
+    SSM_NONE,
+    SSM_UNIFORM,
+    SSM_UNIFORM_NO_REPOSITION,
+};
+
+enum SubArrangeMode {
+    SAM_NONE,
+    SAM_ENSUREMARGIN_NO_SCALE,
+    SAM_ENSUREMARGIN_AND_SCALE,
+    SAM_SNAPALIGNMENT_NO_SCALE,
+    SAM_SNAPALIGNMENT_AND_SCALE,
+};
+
+typedef struct SubScaleContext {
+    const AVClass *class;
+    struct SwsContext *sws;
+    AVDictionary *opts;
+
+    int w, h;
+
+    char *w_expr;               ///< width  expression string
+    char *h_expr;               ///< height expression string
+    AVExpr *w_pexpr;
+    AVExpr *h_pexpr;
+    double var_values[VARS_NB];
+
+    int force_original_aspect_ratio;
+    int eval_mode;               ///< expression evaluation mode
+
+    int use_caching;
+
+    // Scale Options
+    enum SubScaleMode scale_mode;
+
+    // Arrange Options
+    enum SubArrangeMode arrange_mode_h;
+    enum SubArrangeMode arrange_mode_v;
+    int margin_h;
+    int margin_v;
+    char *margin_h_expr;
+    char *margin_v_expr;
+    AVExpr *margin_h_pexpr;
+    AVExpr *margin_v_pexpr;
+
+    // Bitmap Options
+    int num_output_colors;
+    int bitmap_width_align;
+    int bitmap_height_align;
+
+    // Color Quantization Fields
+    struct ELBGContext *ctx;
+    AVLFG lfg;
+    int *codeword;
+    int *codeword_closest_codebook_idxs;
+    int *codebook;
+    int r_idx, g_idx, b_idx, a_idx;
+    AVFrame *cache_frame;
+} SubScaleContext;
+
+
+static int config_output(AVFilterLink *outlink);
+
+static int check_exprs(AVFilterContext *ctx)
+{
+    const SubScaleContext *s = ctx->priv;
+    unsigned vars_w[VARS_NB] = { 0 }, vars_h[VARS_NB] = { 0 };
+
+    if (!s->w_pexpr && !s->h_pexpr)
+        return AVERROR(EINVAL);
+
+    if (s->w_pexpr)
+        av_expr_count_vars(s->w_pexpr, vars_w, VARS_NB);
+    if (s->h_pexpr)
+        av_expr_count_vars(s->h_pexpr, vars_h, VARS_NB);
+
+    if (vars_w[VAR_OUT_W] || vars_w[VAR_OW]) {
+        av_log(ctx, AV_LOG_ERROR, "Width expression cannot be self-referencing: '%s'.\n", s->w_expr);
+        return AVERROR(EINVAL);
+    }
+
+    if (vars_h[VAR_OUT_H] || vars_h[VAR_OH]) {
+        av_log(ctx, AV_LOG_ERROR, "Height expression cannot be self-referencing: '%s'.\n", s->h_expr);
+        return AVERROR(EINVAL);
+    }
+
+    if ((vars_w[VAR_OUT_H] || vars_w[VAR_OH]) &&
+        (vars_h[VAR_OUT_W] || vars_h[VAR_OW])) {
+        av_log(ctx, AV_LOG_WARNING, "Circular references detected for width '%s' and height '%s' - possibly invalid.\n", s->w_expr, s->h_expr);
+    }
+
+    if (s->margin_h_pexpr)
+        av_expr_count_vars(s->margin_h_pexpr, vars_w, VARS_NB);
+    if (s->margin_v_pexpr)
+        av_expr_count_vars(s->margin_v_pexpr, vars_h, VARS_NB);
+
+    return 0;
+}
+
+static int scale_parse_expr(AVFilterContext *ctx, char *str_expr, AVExpr **pexpr_ptr, const char *var, const char *args)
+{
+    SubScaleContext *s = ctx->priv;
+    int ret, is_inited = 0;
+    char *old_str_expr = NULL;
+    AVExpr *old_pexpr = NULL;
+
+    if (str_expr) {
+        old_str_expr = av_strdup(str_expr);
+        if (!old_str_expr)
+            return AVERROR(ENOMEM);
+        av_opt_set(s, var, args, 0);
+    }
+
+    if (*pexpr_ptr) {
+        old_pexpr = *pexpr_ptr;
+        *pexpr_ptr = NULL;
+        is_inited = 1;
+    }
+
+    ret = av_expr_parse(pexpr_ptr, args, var_names,
+                        NULL, NULL, NULL, NULL, 0, ctx);
+    if (ret < 0) {
+        av_log(ctx, AV_LOG_ERROR, "Cannot parse expression for %s: '%s'\n", var, args);
+        goto revert;
+    }
+
+    ret = check_exprs(ctx);
+    if (ret < 0)
+        goto revert;
+
+    if (is_inited && (ret = config_output(ctx->outputs[0])) < 0)
+        goto revert;
+
+    av_expr_free(old_pexpr);
+    av_freep(&old_str_expr);
+
+    return 0;
+
+revert:
+    av_expr_free(*pexpr_ptr);
+    *pexpr_ptr = NULL;
+    if (old_str_expr) {
+        av_opt_set(s, var, old_str_expr, 0);
+        av_free(old_str_expr);
+    }
+    if (old_pexpr)
+        *pexpr_ptr = old_pexpr;
+
+    return ret;
+}
+
+static av_cold int init_dict(AVFilterContext *ctx, AVDictionary **opts)
+{
+    SubScaleContext *s = ctx->priv;
+    uint8_t rgba_map[4];
+    int ret;
+
+    if (!s->w_expr)
+        av_opt_set(s, "w", "iw", 0);
+    if (!s->h_expr)
+        av_opt_set(s, "h", "ih", 0);
+
+    ret = scale_parse_expr(ctx, NULL, &s->w_pexpr, "width", s->w_expr);
+    if (ret < 0)
+        return ret;
+
+    ret = scale_parse_expr(ctx, NULL, &s->h_pexpr, "height", s->h_expr);
+    if (ret < 0)
+        return ret;
+
+    av_log(ctx, AV_LOG_VERBOSE, "w:%s h:%s\n",
+           s->w_expr, s->h_expr);
+
+    if (!s->margin_h_expr)
+        av_opt_set(s, "margin_h", "0", 0);
+    if (!s->margin_v_expr)
+        av_opt_set(s, "margin_v", "0", 0);
+
+    ret = scale_parse_expr(ctx, NULL, &s->margin_h_pexpr, "margin_h", s->margin_h_expr);
+    if (ret < 0)
+        return ret;
+
+    ret = scale_parse_expr(ctx, NULL, &s->margin_v_pexpr, "margin_v", s->margin_v_expr);
+    if (ret < 0)
+        return ret;
+
+    s->opts = *opts;
+    *opts = NULL;
+
+    ff_fill_rgba_map(&rgba_map[0], AV_PIX_FMT_RGB32);
+
+    s->r_idx = rgba_map[0]; // R
+    s->g_idx = rgba_map[1]; // G
+    s->b_idx = rgba_map[2]; // B
+    s->a_idx = rgba_map[3]; // A
+
+    av_lfg_init(&s->lfg, 123456789);
+
+
+    return 0;
+}
+
+static av_cold void uninit(AVFilterContext *ctx)
+{
+    SubScaleContext *s = ctx->priv;
+
+    av_frame_free(&s->cache_frame);
+
+    av_expr_free(s->w_pexpr);
+    av_expr_free(s->h_pexpr);
+    s->w_pexpr = s->h_pexpr = NULL;
+
+    av_expr_free(s->margin_h_pexpr);
+    av_expr_free(s->margin_v_pexpr);
+    s->margin_h_pexpr = s->margin_v_pexpr = NULL;
+
+    sws_freeContext(s->sws);
+    s->sws = NULL;
+    av_dict_free(&s->opts);
+
+    avpriv_elbg_free(&s->ctx);
+
+    av_freep(&s->codebook);
+    av_freep(&s->codeword);
+    av_freep(&s->codeword_closest_codebook_idxs);
+}
+
+static int config_input(AVFilterLink *inlink)
+{
+    ////const AVFilterContext *ctx = inlink->dst;
+    ////SubScaleContext *s = ctx->priv;
+
+    ////if (s->w <= 0 || s->h <= 0) {
+    ////    s->w = inlink->w;
+    ////    s->h = inlink->h;
+    ////}
+    return 0;
+}
+
+static int scale_eval_dimensions(AVFilterContext *ctx)
+{
+    SubScaleContext *s = ctx->priv;
+    const AVFilterLink *inlink = ctx->inputs[0];
+    char *expr;
+    int eval_w, eval_h, margin_h, margin_v;
+    int ret;
+    double res;
+
+    s->var_values[VAR_IN_W]  = s->var_values[VAR_IW] = inlink->w;
+    s->var_values[VAR_IN_H]  = s->var_values[VAR_IH] = inlink->h;
+    s->var_values[VAR_OUT_W] = s->var_values[VAR_OW] = NAN;
+    s->var_values[VAR_OUT_H] = s->var_values[VAR_OH] = NAN;
+    s->var_values[VARS_B_H]  = s->var_values[VARS_B_V] = 0;
+    s->var_values[VAR_A]     = (double) inlink->w / inlink->h;
+    s->var_values[VAR_SAR]   = inlink->sample_aspect_ratio.num ?
+        (double) inlink->sample_aspect_ratio.num / inlink->sample_aspect_ratio.den : 1;
+    s->var_values[VAR_DAR]   = s->var_values[VAR_A] * s->var_values[VAR_SAR];
+
+    res = av_expr_eval(s->w_pexpr, s->var_values, NULL);
+    s->var_values[VAR_OUT_W] = s->var_values[VAR_OW] = (int) res == 0 ? inlink->w : (int) res;
+
+    res = av_expr_eval(s->h_pexpr, s->var_values, NULL);
+    if (isnan(res)) {
+        expr = s->h_expr;
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+    eval_h = s->var_values[VAR_OUT_H] = s->var_values[VAR_OH] = (int) res == 0 ? inlink->h : (int) res;
+
+    res = av_expr_eval(s->w_pexpr, s->var_values, NULL);
+    if (isnan(res)) {
+        expr = s->w_expr;
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+    eval_w = s->var_values[VAR_OUT_W] = s->var_values[VAR_OW] = (int) res == 0 ? inlink->w : (int) res;
+
+    s->w = eval_w;
+    s->h = eval_h;
+
+    res = av_expr_eval(s->margin_h_pexpr, s->var_values, NULL);
+    s->var_values[VARS_B_H] = (int)res;
+
+    res = av_expr_eval(s->margin_v_pexpr, s->var_values, NULL);
+    if (isnan(res)) {
+        expr = s->margin_v_expr;
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+    margin_v = s->var_values[VARS_B_V] = (int)res;
+
+    res = av_expr_eval(s->margin_h_pexpr, s->var_values, NULL);
+    if (isnan(res)) {
+        expr = s->margin_h_expr;
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+    margin_h = s->var_values[VARS_B_H] = (int)res;
+
+    s->margin_h = margin_h;
+    s->margin_v = margin_v;
+
+    return 0;
+
+fail:
+    av_log(ctx, AV_LOG_ERROR,
+           "Error when evaluating the expression '%s'.\n", expr);
+    return ret;
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    AVFilterLink *inlink  = outlink->src->inputs[0];
+    SubScaleContext *s = ctx->priv;
+    int ret;
+
+    outlink->format = AV_SUBTITLE_FMT_BITMAP;
+    outlink->time_base = ctx->inputs[0]->time_base;
+    outlink->frame_rate = ctx->inputs[0]->frame_rate;
+
+    if ((ret = scale_eval_dimensions(ctx)) < 0)
+        goto fail;
+
+    ff_scale_adjust_dimensions(inlink, &s->w, &s->h,
+                               s->force_original_aspect_ratio, 2);
+
+    if (s->w > INT_MAX ||
+        s->h > INT_MAX ||
+        (s->h * inlink->w) > INT_MAX ||
+        (s->w * inlink->h) > INT_MAX)
+        av_log(ctx, AV_LOG_ERROR, "Rescaled value for width or height is too big.\n");
+
+    outlink->w = s->w;
+    outlink->h = s->h;
+
+    if (s->sws)
+        sws_freeContext(s->sws);
+
+    s->sws = sws_alloc_context();
+    if (!s->sws)
+        return AVERROR(ENOMEM);
+
+    av_opt_set_pixel_fmt(s->sws, "src_format", AV_PIX_FMT_PAL8, 0);
+    av_opt_set_int(s->sws, "dst_format", AV_PIX_FMT_RGB32, 0);
+    av_opt_set_int(s->sws, "threads", ff_filter_get_nb_threads(ctx), 0);
+
+    if (s->opts) {
+        const AVDictionaryEntry *e = NULL;
+        while ((e = av_dict_get(s->opts, "", e, AV_DICT_IGNORE_SUFFIX))) {
+            if ((ret = av_opt_set(s->sws, e->key, e->value, 0)) < 0)
+                return ret;
+        }
+    }
+
+    if ((ret = sws_init_context(s->sws, NULL, NULL)) < 0)
+        return ret;
+
+    if (inlink->sample_aspect_ratio.num){
+        outlink->sample_aspect_ratio = av_mul_q((AVRational){outlink->h * inlink->w, outlink->w * inlink->h}, inlink->sample_aspect_ratio);
+    } else
+        outlink->sample_aspect_ratio = inlink->sample_aspect_ratio;
+
+    av_log(ctx, AV_LOG_VERBOSE, "Output size set to %dx%d.\n", outlink->w, outlink->h);
+
+    return 0;
+fail:
+    return ret;
+}
+
+static int palettize_image(SubScaleContext *const s, const int w, const int h, const int src_linesize, uint8_t *src_data,
+                          int dst_linesize, uint8_t *dst_data, uint32_t *dst_pal)
+{
+    int k, ret;
+    const int codeword_length = w * h;
+
+    /* Re-Initialize */
+    s->codeword = av_realloc_f(s->codeword, codeword_length, 4 * sizeof(*s->codeword));
+    if (!s->codeword)
+        return AVERROR(ENOMEM);
+
+    s->codeword_closest_codebook_idxs =
+        av_realloc_f(s->codeword_closest_codebook_idxs, codeword_length,
+                     sizeof(*s->codeword_closest_codebook_idxs));
+    if (!s->codeword_closest_codebook_idxs)
+        return AVERROR(ENOMEM);
+
+    s->codebook = av_realloc_f(s->codebook, s->num_output_colors, 4 * sizeof(*s->codebook));
+    if (!s->codebook)
+        return AVERROR(ENOMEM);
+
+    /* build the codeword */
+    k = 0;
+    for (int i = 0; i < h; i++) {
+        uint8_t *p = src_data;
+        for (int j = 0; j < w; j++) {
+            s->codeword[k++] = p[s->b_idx];
+            s->codeword[k++] = p[s->g_idx];
+            s->codeword[k++] = p[s->r_idx];
+            s->codeword[k++] = p[s->a_idx];
+            p += 4;
+        }
+        src_data += src_linesize;
+    }
+
+    /* compute the codebook */
+    ret = avpriv_elbg_do(&s->ctx, s->codeword, 4,
+        codeword_length, s->codebook,
+        s->num_output_colors, 1,
+        s->codeword_closest_codebook_idxs, &s->lfg, 0);
+
+    if (ret < 0)
+        return ret;
+
+    /* Write Palette */
+    for (int i = 0; i < s->num_output_colors; i++) {
+        dst_pal[i] = s->codebook[i*4+3] << 24  |
+                    (s->codebook[i*4+2] << 16) |
+                    (s->codebook[i*4+1] <<  8) |
+                     s->codebook[i*4  ];
+    }
+
+    /* Write Image */
+    k = 0;
+    for (int i = 0; i < h; i++) {
+        uint8_t *p = dst_data;
+        for (int j = 0; j < w; j++, p++) {
+            p[0] = s->codeword_closest_codebook_idxs[k++];
+        }
+
+        dst_data += dst_linesize;
+    }
+
+    return ret;
+}
+
+static int rescale_size(int64_t a, AVRational factor)
+{
+    const int64_t res = av_rescale_rnd(a, factor.num, factor.den, AV_ROUND_NEAR_INF);
+    if (res > INT32_MAX || res < 0)
+        return 0;
+
+    return (int)res;
+}
+
+
+static int scale_area(AVFilterLink *link, AVSubtitleArea *area, const int target_width, const int target_height)
+{
+    const AVFilterContext *ctx = link->dst;
+    SubScaleContext *s = ctx->priv;
+    int ret;
+
+    AVBufferRef *dst_buffer;
+    const uint8_t* data[2]    = { area->buf[0]->data, (uint8_t *)&area->pal };
+    const int dstW            = FFALIGN(target_width, s->bitmap_width_align);
+    const int dstH            = FFALIGN(target_height, s->bitmap_height_align);
+    const int tmp_linesize[2] = { FFALIGN(dstW * 4, 32), 0 };
+    const int dst_linesize[2] = { dstW, 0 };
+    uint8_t* tmp[2] = { 0, 0 };
+
+    AVBufferRef *tmp_buffer = av_buffer_allocz(tmp_linesize[0] * dstH);
+    if (!tmp_buffer)
+        return AVERROR(ENOMEM);
+
+    if (!s->sws)
+        return 0;
+
+    tmp[0] = tmp_buffer->data;
+
+    s->sws = sws_getCachedContext(s->sws, area->w, area->h, AV_PIX_FMT_PAL8,
+        dstW, dstH, AV_PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL);
+    if (!s->sws) {
+        av_log(NULL, AV_LOG_FATAL, "Cannot initialize the conversion context. dstW=%d dstH=%d\n", dstW, dstH);
+        return AVERROR(EINVAL);
+    }
+
+    // Rescale to ARGB
+    ret = sws_scale(s->sws, data, area->linesize, 0, area->h, tmp, tmp_linesize);
+    if (ret < 0) {
+        av_buffer_unref(&tmp_buffer);
+        return ret;
+    }
+
+    // Alloc output buffer
+    dst_buffer = av_buffer_allocz(dst_linesize[0] * dstH);
+    if (!dst_buffer) {
+        av_buffer_unref(&tmp_buffer);
+        return AVERROR(ENOMEM);
+    }
+
+    // Quantize to palettized image
+    ret = palettize_image(s, dstW, dstH, tmp_linesize[0], tmp[0], dst_linesize[0], dst_buffer->data, area->pal);
+    av_buffer_unref(&tmp_buffer);
+
+    if (ret < 0) {
+        av_buffer_unref(&dst_buffer);
+        return ret;
+    }
+
+    av_buffer_unref(&area->buf[0]);
+    ret = av_buffer_replace(&area->buf[0], dst_buffer);
+    if (ret < 0) {
+        av_buffer_unref(&dst_buffer);
+        return ret;
+    }
+
+    area->w = dstW;
+    area->h = dstH;
+    area->linesize[0] = dst_linesize[0];
+    area->nb_colors = s->num_output_colors;
+
+    return ret;
+}
+
+static int process_area(AVFilterLink *inlink, AVSubtitleArea *area, AVRational x_factor, AVRational y_factor)
+{
+    AVFilterContext *ctx     = inlink->dst;
+    const SubScaleContext *s = ctx->priv;
+    int target_w, target_h, target_x, target_y;
+    const int border_l = s->margin_h;
+    const int border_r = s->w - s->margin_h;
+    const int border_t = s->margin_v;
+    const int border_b = s->h - s->margin_v;
+
+    av_log(ctx, AV_LOG_DEBUG, "process_area -  start: x/y: (%d:%d) size: %dx%d scale_mode: %d x-factor: %d:%d y-factor: %d:%d\n",
+        area->x, area->y, area->w, area->h, s->scale_mode, x_factor.num, x_factor.den, y_factor.num, y_factor.den);
+
+    switch (s->scale_mode) {
+    case SSM_NONE:
+        target_w = area->w;
+        target_h = area->h;
+        target_x = area->x;
+        target_y = area->y;
+        break;
+    case SSM_UNIFORM:
+        target_w = rescale_size(area->w, x_factor);
+        target_h = rescale_size(area->h, y_factor);
+        target_x = rescale_size(area->x, x_factor);
+        target_y = rescale_size(area->y, y_factor);
+        break;
+    case SSM_UNIFORM_NO_REPOSITION:
+        target_w = rescale_size(area->w, x_factor);
+        target_h = rescale_size(area->h, y_factor);
+        target_x = area->x;
+        target_y = area->y;
+        break;
+    default:
+        av_log(ctx, AV_LOG_ERROR, "Invalid scale_mode: %d\n", s->scale_mode);
+        return AVERROR(EINVAL);
+    }
+
+    av_log(ctx, AV_LOG_DEBUG, "process_area - scaled: x/y: (%d:%d) size: %dx%d.\n", target_x, target_y, target_w, target_h);
+
+
+    switch (s->arrange_mode_h) {
+    case SAM_ENSUREMARGIN_AND_SCALE:
+    case SAM_SNAPALIGNMENT_AND_SCALE:
+        {
+            // IF it doesn't fit - scale it
+            int max_width = s->w - 2 * s->margin_h;
+
+            if (max_width < 2)
+                max_width = 2;
+
+            if (target_w > max_width) {
+                target_h = (int)av_rescale(target_h, max_width, target_w);
+                target_w = max_width;
+                target_x = s->margin_h;
+            }
+        }
+        break;
+    }
+
+    switch (s->arrange_mode_h) {
+    case SAM_NONE:
+        break;
+    case SAM_ENSUREMARGIN_NO_SCALE:
+    case SAM_ENSUREMARGIN_AND_SCALE:
+        // Left border
+        if (target_x < border_l)
+            target_x = border_l;
+
+        // Right border
+        if (target_x + target_w > border_r)
+            target_x = border_r - target_w;
+
+        break;
+    case SAM_SNAPALIGNMENT_NO_SCALE:
+    case SAM_SNAPALIGNMENT_AND_SCALE:
+        {
+            // Use original values to detect alignment
+            const int left_margin          = area->x;
+            const int right_margin         = inlink->w - area->x - area->w;
+            const AVRational diff_factor_r = { left_margin - right_margin, area->w };
+            const float diff_factor        = (float)av_q2d(diff_factor_r);
+
+            if (diff_factor > 0.2f) {
+                // Right aligned
+                target_x = border_r - target_w;
+            } else if (diff_factor < -0.2f) {
+                // Left aligned
+                target_x = border_l;
+            } else {
+                // Centered
+                target_x = (inlink->w - area->w) / 2;
+            }
+        }
+
+        break;
+    default:
+        av_log(ctx, AV_LOG_ERROR, "Invalid arrange_mode_h: %d\n", s->arrange_mode_h);
+        return AVERROR(EINVAL);
+    }
+
+    av_log(ctx, AV_LOG_DEBUG, "process_area -  arr_h: x/y: (%d:%d) size: %dx%d.\n", target_x, target_y, target_w, target_h);
+
+
+    switch (s->arrange_mode_v) {
+    case SAM_ENSUREMARGIN_AND_SCALE:
+    case SAM_SNAPALIGNMENT_AND_SCALE:
+        {
+            // IF it doesn't fit - scale it
+            int max_height = s->h - 2 * s->margin_v;
+
+            if (max_height < 2)
+                max_height = 2;
+
+            if (target_h > max_height) {
+                target_w = (int)av_rescale(target_w, max_height, target_h);
+                target_h = max_height;
+                target_y = s->margin_v;
+            }
+        }
+        break;
+    }
+
+    switch (s->arrange_mode_v) {
+    case SAM_NONE:
+        break;
+    case SAM_ENSUREMARGIN_NO_SCALE:
+    case SAM_ENSUREMARGIN_AND_SCALE:
+        // Top border
+        if (target_y < border_t)
+            target_y = border_t;
+
+        // Bottom border
+        if (target_y + target_h > border_b)
+            target_y = border_b - target_h;
+
+        break;
+    case SAM_SNAPALIGNMENT_NO_SCALE:
+    case SAM_SNAPALIGNMENT_AND_SCALE:
+        {
+            // Use original values to detect alignment
+            const int top_margin           = area->y;
+            const int bottom_margin        = inlink->h - area->y - area->h;
+            const AVRational diff_factor_r = { top_margin - bottom_margin, area->h };
+            const float diff_factor        = (float)av_q2d(diff_factor_r);
+
+            if (diff_factor > 0.2f) {
+                // Bottom aligned
+                target_y = border_b - target_h;
+            } else if (diff_factor < -0.2f) {
+                // Top aligned
+                target_y = border_t;
+            } else {
+                // Centered
+                target_y = (inlink->h - area->h) / 2;
+            }
+        }
+
+        break;
+    default:
+        av_log(ctx, AV_LOG_ERROR, "Invalid arrange_mode_v: %d\n", s->arrange_mode_v);
+        return AVERROR(EINVAL);
+    }
+
+    av_log(ctx, AV_LOG_VERBOSE, "process_area -  arr_v: x/y: (%d:%d) size: %dx%d.\n", target_x, target_y, target_w, target_h);
+
+    area->x = target_x;
+    area->y = target_y;
+
+    if (area->w != target_w || area->h != target_h)
+        return scale_area(inlink, area, target_w, target_h);
+
+    return 0;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    const AVFilterContext *ctx = inlink->dst;
+    SubScaleContext *s = ctx->priv;
+    AVFilterLink *outlink = ctx->outputs[0];
+    int ret;
+
+    // just forward empty frames
+    if (frame->num_subtitle_areas == 0) {
+        av_frame_free(&s->cache_frame);
+        return ff_filter_frame(outlink, frame);
+    }
+
+    if (s->use_caching && s->cache_frame && frame->repeat_sub
+        && s->cache_frame->subtitle_timing.start_pts == frame->subtitle_timing.start_pts) {
+        AVFrame *out = av_frame_clone(s->cache_frame);
+        if (!out)
+            return AVERROR(ENOMEM);
+
+        ret = av_frame_copy_props(out, frame);
+        if (ret < 0)
+            return ret;
+
+        av_log(inlink->dst, AV_LOG_DEBUG, "subscale CACHED - size %dx%d  pts: %"PRId64"  areas: %d\n", frame->width, frame->height, frame->subtitle_timing.start_pts, frame->num_subtitle_areas);
+        av_frame_free(&frame);
+        return ff_filter_frame(outlink, out);
+    }
+
+    ret = av_frame_make_writable(frame);
+    if (ret >= 0) {
+        const AVRational x_factor = { .num = outlink->w, .den = inlink->w} ;
+        const AVRational y_factor = { .num = outlink->h, .den = inlink->h} ;
+
+        for (unsigned i = 0; i < frame->num_subtitle_areas; ++i) {
+            AVSubtitleArea *area = frame->subtitle_areas[i];
+
+            ret = process_area(inlink, area, x_factor, y_factor);
+            if (ret < 0)
+                return ret;
+        }
+
+        av_log(inlink->dst, AV_LOG_DEBUG, "subscale output - size %dx%d  pts: %"PRId64"  areas: %d\n", frame->width, frame->height, frame->subtitle_timing.start_pts, frame->num_subtitle_areas);
+
+        if (s->use_caching) {
+            av_frame_free(&s->cache_frame);
+            s->cache_frame = av_frame_clone(frame);
+        }
+
+        return ff_filter_frame(outlink, frame);
+    }
+
+    return ret;
+}
+
+#define OFFSET(x) offsetof(SubScaleContext, x)
+#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption subscale_options[] = {
+    { "margin_h", "horizontal border",        OFFSET(margin_h_expr),    AV_OPT_TYPE_STRING,        .flags = FLAGS },
+    { "margin_v", "vertical border",          OFFSET(margin_v_expr),    AV_OPT_TYPE_STRING,        .flags = FLAGS },
+    { "w",     "Output video width",          OFFSET(w_expr),    AV_OPT_TYPE_STRING,        .flags = FLAGS },
+    { "width", "Output video width",          OFFSET(w_expr),    AV_OPT_TYPE_STRING,        .flags = FLAGS },
+    { "h",     "Output video height",         OFFSET(h_expr),    AV_OPT_TYPE_STRING,        .flags = FLAGS },
+    { "height","Output video height",         OFFSET(h_expr),    AV_OPT_TYPE_STRING,        .flags = FLAGS },
+    { "force_original_aspect_ratio", "decrease or increase w/h if necessary to keep the original AR", OFFSET(force_original_aspect_ratio), AV_OPT_TYPE_INT, { .i64 = 0}, 0, 2, FLAGS, "force_oar" },
+    { "disable",  NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, 0, 0, FLAGS, "force_oar" },
+    { "decrease", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 1 }, 0, 0, FLAGS, "force_oar" },
+    { "increase", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 2 }, 0, 0, FLAGS, "force_oar" },
+    { "scale_mode", "specify how to scale subtitles", OFFSET(scale_mode), AV_OPT_TYPE_INT, {.i64 = SSM_UNIFORM}, 0, SSM_UNIFORM_NO_REPOSITION, FLAGS, "scale_mode" },
+         { "none",  "no common scaling", 0, AV_OPT_TYPE_CONST, {.i64=SSM_NONE},  .flags = FLAGS, .unit = "scale_mode" },
+         { "uniform",  "uniformly scale and reposition", 0, AV_OPT_TYPE_CONST, {.i64=SSM_UNIFORM},  .flags = FLAGS, .unit = "scale_mode" },
+         { "uniform_no_reposition",  "uniformly scale but keep positions", 0, AV_OPT_TYPE_CONST, {.i64=SSM_UNIFORM_NO_REPOSITION},  .flags = FLAGS, .unit = "scale_mode" },
+    { "use_caching", "Cache output frames",   OFFSET(use_caching), AV_OPT_TYPE_BOOL, { .i64 = 1}, 0, 1, .flags = FLAGS },
+
+    { "arrange_h", "specify how to arrange subtitles horizontally", OFFSET(arrange_mode_h), AV_OPT_TYPE_INT, {.i64 = SAM_NONE}, 0, SAM_SNAPALIGNMENT_AND_SCALE, FLAGS, "arrange" },
+    { "arrange_v", "specify how to arrange subtitles vertically", OFFSET(arrange_mode_v), AV_OPT_TYPE_INT, {.i64 = SAM_NONE}, 0, SAM_SNAPALIGNMENT_AND_SCALE, FLAGS, "arrange" },
+         { "none",  "no repositioning", 0, AV_OPT_TYPE_CONST, {.i64=SAM_NONE},  .flags = FLAGS, .unit = "arrange" },
+         { "margin_no_scale",  "move subs inside border when possible", 0, AV_OPT_TYPE_CONST, {.i64=SAM_ENSUREMARGIN_NO_SCALE,},  .flags = FLAGS, .unit = "arrange" },
+         { "margin_and_scale",  "move subs inside border and scale as needed", 0, AV_OPT_TYPE_CONST, {.i64=SAM_ENSUREMARGIN_AND_SCALE,},  .flags = FLAGS, .unit = "arrange" },
+         { "snapalign_no_scale",  "snap subs to near/far/center when possible", 0, AV_OPT_TYPE_CONST, {.i64=SAM_SNAPALIGNMENT_NO_SCALE,},  .flags = FLAGS, .unit = "arrange" },
+         { "snapalign_and_scale", "snap subs to near/far/center and scale as needed", 0, AV_OPT_TYPE_CONST, {.i64=SAM_SNAPALIGNMENT_AND_SCALE,}, .flags = FLAGS, .unit = "arrange" },
+
+    { "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, {.i64 = EVAL_MODE_INIT}, 0, EVAL_MODE_NB-1, FLAGS, "eval" },
+         { "init",  "eval expressions once during initialization", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_INIT},  .flags = FLAGS, .unit = "eval" },
+         { "frame", "eval expressions during initialization and per-frame", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_FRAME}, .flags = FLAGS, .unit = "eval" },
+    { "num_colors",     "number of palette colors in output", OFFSET(num_output_colors),  AV_OPT_TYPE_INT, {.i64 = 256 }, 2, 256, .flags = FLAGS },
+    { "bitmap_width_align",     "Bitmap width alignment", OFFSET(bitmap_width_align),  AV_OPT_TYPE_INT, {.i64 = 2 }, 1, 256, .flags = FLAGS },
+    { "bitmap_height_align",     "Bitmap height alignment", OFFSET(bitmap_height_align),  AV_OPT_TYPE_INT, {.i64 = 2 }, 1, 256, .flags = FLAGS },
+    { .name =  NULL }
+};
+
+static const AVClass subscale_class = {
+    .class_name       = "subscale",
+    .item_name        = av_default_item_name,
+    .option           = subscale_options,
+    .version          = LIBAVUTIL_VERSION_INT,
+    .category         = AV_CLASS_CATEGORY_FILTER,
+};
+
+static const AVFilterPad inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .filter_frame = filter_frame,
+        .config_props = config_input,
+    },
+};
+
+static const AVFilterPad outputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .config_props = config_output,
+    },
+};
+
+const AVFilter ff_sf_subscale = {
+    .name            = "subscale",
+    .description     = NULL_IF_CONFIG_SMALL("Scale graphical subtitles."),
+    .init_dict       = init_dict,
+    .uninit          = uninit,
+    .priv_size       = sizeof(SubScaleContext),
+    .priv_class      = &subscale_class,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_SINGLE_SUBFMT(AV_SUBTITLE_FMT_BITMAP),
+};
+
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH 20/24] avfilter/subfeed: add subtitle feed filter
  2022-01-14  1:13 [FFmpeg-devel] [PATCH 00/24] Subtitle Filtering 2022 ffmpegagent
                   ` (18 preceding siblings ...)
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 19/24] avfilter/subscale: Add filter for scaling and/or re-arranging graphical subtitles ffmpegagent
@ 2022-01-14  1:13 ` ffmpegagent
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 21/24] fftools/ffmpeg: Introduce subtitle filtering new frame-based subtitle encoding ffmpegagent
                   ` (4 subsequent siblings)
  24 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-14  1:13 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: softworkz

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavfilter/Makefile     |   1 +
 libavfilter/allfilters.c |   1 +
 libavfilter/sf_subfeed.c | 366 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 368 insertions(+)
 create mode 100644 libavfilter/sf_subfeed.c

diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 0e3e48613e..5711c7770b 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -570,6 +570,7 @@ OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
 OBJS-$(CONFIG_SPLITCC_FILTER)                += sf_splitcc.o
 OBJS-$(CONFIG_STRIPSTYLES_FILTER)            += sf_stripstyles.o
 OBJS-$(CONFIG_SUBSCALE_FILTER)               += sf_subscale.o
+OBJS-$(CONFIG_SUBFEED_FILTER)                += sf_subfeed.o
 
 # multimedia filters
 OBJS-$(CONFIG_ABITSCOPE_FILTER)              += avf_abitscope.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 8e4f2feca3..10b14b7b8e 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -553,6 +553,7 @@ extern const AVFilter ff_sf_showspeaker;
 extern const AVFilter ff_sf_splitcc;
 extern const AVFilter ff_sf_stripstyles;
 extern const AVFilter ff_sf_subscale;
+extern const AVFilter ff_sf_subfeed;
 extern const AVFilter ff_sf_textmod;
 extern const AVFilter ff_svf_graphicsub2video;
 extern const AVFilter ff_svf_textsub2video;
diff --git a/libavfilter/sf_subfeed.c b/libavfilter/sf_subfeed.c
new file mode 100644
index 0000000000..8a7ac5528b
--- /dev/null
+++ b/libavfilter/sf_subfeed.c
@@ -0,0 +1,366 @@
+/*
+ * 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
+ * subtitle filter for feeding subtitle frames into a filtergraph in a contiguous way
+ *
+ *
+ * also supports
+ *   - duration fixup
+ *     delaying a subtitle event with unknown duration and infer duration from the
+ *     start time of the subsequent subtitle
+ *   - scattering
+ *     splitting a subtitle event with unknown duration into multiple ones with
+ *     a short and fixed duration
+ *
+ */
+
+#include "filters.h"
+#include "libavutil/opt.h"
+#include "subtitles.h"
+#include "libavutil/avassert.h"
+
+enum SubFeedMode {
+    FM_REPEAT,
+    FM_SCATTER,
+    FM_FORWARD,
+};
+
+typedef struct SubFeedContext {
+    const AVClass *class;
+    enum AVSubtitleType format;
+    enum SubFeedMode mode;
+
+    AVRational frame_rate;
+    int fix_durations;
+    int fix_overlap;
+
+    int current_frame_isnew;
+    int eof;
+    int got_first_input;
+    int need_frame;
+    int64_t next_pts_offset;
+    int64_t recent_subtitle_pts;
+
+    int64_t counter;
+
+    /**
+     * Queue of frames waiting to be filtered.
+     */
+    FFFrameQueue fifo;
+
+} SubFeedContext;
+
+static int64_t ms_to_avtb(int64_t ms)
+{
+    return av_rescale_q(ms, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q);
+}
+
+static int64_t avtb_to_ms(int64_t avtb)
+{
+    return av_rescale_q(avtb, AV_TIME_BASE_Q, (AVRational){ 1, 1000 });
+}
+
+static int init(AVFilterContext *ctx)
+{
+    SubFeedContext *s = ctx->priv;
+
+    ff_framequeue_init(&s->fifo, NULL);
+
+    return 0;
+}
+
+static void uninit(AVFilterContext *ctx)
+{
+    SubFeedContext *s = ctx->priv;
+    ff_framequeue_free(&s->fifo);
+}
+
+static int config_input(AVFilterLink *link)
+{
+    ////const subfeedContext *context = link->dst->priv;
+
+    return 0;
+}
+
+static int query_formats(AVFilterContext *ctx)
+{
+    AVFilterFormats *formats;
+    AVFilterLink *inlink0 = ctx->inputs[0];
+    AVFilterLink *outlink0 = ctx->outputs[0];
+    static const enum AVSubtitleType subtitle_fmts[] = { AV_SUBTITLE_FMT_BITMAP, AV_SUBTITLE_FMT_ASS, AV_SUBTITLE_FMT_NB };
+    int ret;
+
+    formats = ff_make_format_list(subtitle_fmts);
+
+    if ((ret = ff_formats_ref(formats, &inlink0->outcfg.formats)) < 0)
+        return ret;
+
+    if ((ret = ff_formats_ref(formats, &outlink0->incfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    SubFeedContext *s = outlink->src->priv;
+    const AVFilterLink *inlink = outlink->src->inputs[0];
+
+    outlink->time_base = AV_TIME_BASE_Q;
+    outlink->format = inlink->format;
+    outlink->w = inlink->w;
+    outlink->h = inlink->h;
+
+    if (s->mode == FM_FORWARD)
+        outlink->frame_rate = (AVRational) { 1, 0 };
+    else
+        outlink->frame_rate = s->frame_rate;
+
+    return 0;
+}
+
+static int request_frame(AVFilterLink *outlink)
+{
+    SubFeedContext *s = outlink->src->priv;
+    AVFilterLink *inlink = outlink->src->inputs[0];
+    int64_t last_pts = outlink->current_pts;
+    int64_t next_pts;
+    int64_t interval = ms_to_avtb((int64_t)(av_q2d(av_inv_q(outlink->frame_rate)) * 1000));
+    AVFrame *out;
+    int status;
+
+    if (s->mode == FM_FORWARD)
+        return ff_request_frame(inlink);
+
+    s->counter++;
+    if (interval == 0)
+        interval =  ms_to_avtb(200);
+
+    status = ff_outlink_get_status(inlink);
+    if (status == AVERROR_EOF)
+        s->eof = 1;
+
+    if (s->eof)
+        return AVERROR_EOF;
+
+    if (!s->got_first_input && inlink->current_pts != AV_NOPTS_VALUE) {
+
+        s->got_first_input = 1;
+        next_pts = av_rescale_q(inlink->current_pts, inlink->time_base, AV_TIME_BASE_Q);
+        if (next_pts < last_pts)
+            next_pts = last_pts + interval;
+
+    } else if (last_pts == AV_NOPTS_VALUE)
+        next_pts = av_rescale_q(inlink->current_pts, inlink->time_base, AV_TIME_BASE_Q);
+    else
+        next_pts = last_pts + interval;
+
+    if (next_pts == AV_NOPTS_VALUE)
+        next_pts = 0;
+
+    if (s->next_pts_offset) {
+        next_pts -= s->next_pts_offset;
+        s->next_pts_offset = 0;
+    }
+
+retry:
+    if (ff_framequeue_queued_frames(&s->fifo) && !s->current_frame_isnew) {
+
+        const AVFrame *current_frame = ff_framequeue_peek(&s->fifo, 0);
+        const int64_t sub_end_time   = current_frame->subtitle_timing.start_pts + current_frame->subtitle_timing.duration;
+
+        if (next_pts > sub_end_time) {
+            AVFrame *remove_frame = ff_framequeue_take(&s->fifo);
+            av_frame_free(&remove_frame);
+
+            if (ff_framequeue_queued_frames(&s->fifo)) {
+                s->current_frame_isnew = 1;
+                goto retry;
+            }
+        }
+    }
+
+    if (ff_framequeue_queued_frames(&s->fifo)) {
+        AVFrame *current_frame = ff_framequeue_peek(&s->fifo, 0);
+
+        if (current_frame && current_frame->subtitle_timing.start_pts <= next_pts + interval) {
+            if (!s->current_frame_isnew)
+                current_frame->repeat_sub++;
+
+            out = av_frame_clone(current_frame);
+
+            if (!out)
+                return AVERROR(ENOMEM);
+
+            if (!s->current_frame_isnew) {
+                out->pts = next_pts;
+            } else {
+                out->pts = out->subtitle_timing.start_pts;
+
+                if (out->pts < next_pts)
+                    out->pts = next_pts;
+
+                s->next_pts_offset = (out->pts - next_pts) % interval;
+            }
+
+            if (s->mode == FM_SCATTER) {
+                const int64_t sub_end_time  = current_frame->subtitle_timing.start_pts + current_frame->subtitle_timing.duration;
+
+                if (s->current_frame_isnew == 1 && current_frame->subtitle_timing.start_pts < out->pts) {
+                    const int64_t diff = out->pts - current_frame->subtitle_timing.start_pts;
+                    current_frame->subtitle_timing.duration -= diff;
+                }
+
+                out->repeat_sub = 0;
+                out->subtitle_timing.start_pts = out->pts;
+                out->subtitle_timing.duration = interval;
+                av_assert1(out->pts >= next_pts);
+                av_assert1(out->pts < next_pts + interval);
+                av_assert1(out->pts < sub_end_time);
+
+                if (out->pts > next_pts)
+                    out->subtitle_timing.duration -= out->pts - next_pts;
+
+                if (sub_end_time < next_pts + interval) {
+                    const int64_t diff = next_pts + interval - sub_end_time;
+                    av_assert1(diff <= out->subtitle_timing.duration);
+                    out->subtitle_timing.duration -= diff;
+                }
+            }
+
+            s->current_frame_isnew = 0;
+            s->recent_subtitle_pts = out->subtitle_timing.start_pts;
+
+            return ff_filter_frame(outlink, out);
+        }
+    }
+
+    if (ff_framequeue_queued_frames(&s->fifo) == 0) {
+        status = ff_request_frame(inlink);
+        if (status == AVERROR_EOF) {
+            s->eof = 1;
+            return status;
+        }
+
+        if (s->counter > 1 && s->counter % 2)
+            return 0;
+    }
+
+    out = ff_get_subtitles_buffer(outlink, outlink->format);
+    out->pts = next_pts;
+    out->repeat_sub = 1;
+    out->subtitle_timing.start_pts = s->recent_subtitle_pts;
+
+    return ff_filter_frame(outlink, out);
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFilterContext *ctx        = inlink->dst;
+    SubFeedContext *s           = inlink->dst->priv;
+    AVFilterLink *outlink       = inlink->dst->outputs[0];
+    const int64_t index         = (int64_t)ff_framequeue_queued_frames(&s->fifo) - 1;
+    int ret = 0;
+
+    frame->pts = av_rescale_q(frame->pts, inlink->time_base, AV_TIME_BASE_Q);
+
+    if (index < 0) {
+        s->current_frame_isnew = 1;
+    } else if (s->fix_durations || s->fix_overlap) {
+        AVFrame *previous_frame = ff_framequeue_peek(&s->fifo, index);
+        const int64_t pts_diff = frame->subtitle_timing.start_pts - previous_frame->subtitle_timing.start_pts;
+
+        if (s->fix_durations && pts_diff > 0 && previous_frame->subtitle_timing.duration > ms_to_avtb(29000))
+                    previous_frame->subtitle_timing.duration = frame->subtitle_timing.start_pts - previous_frame->subtitle_timing.start_pts;
+
+        if (s->fix_overlap && pts_diff > 0 && previous_frame->subtitle_timing.duration > pts_diff)
+                    previous_frame->subtitle_timing.duration = pts_diff;
+    }
+
+    ff_framequeue_add(&s->fifo, frame);
+
+    if (index > 3)
+        av_log(ctx, AV_LOG_WARNING, "frame queue count: %d\n", (int)index);
+
+    if (s->mode == FM_FORWARD && ff_framequeue_queued_frames(&s->fifo)) {
+
+        AVFrame *first_frame = ff_framequeue_peek(&s->fifo, 0);
+
+        if (s->fix_overlap && ff_framequeue_queued_frames(&s->fifo) < 2)
+            return 0;
+
+        if (s->fix_durations && first_frame->subtitle_timing.duration > ms_to_avtb(29000))
+            return 0;
+
+        first_frame = ff_framequeue_take(&s->fifo);
+        return ff_filter_frame(outlink, first_frame);
+    }
+
+    return ret;
+}
+
+#define OFFSET(x) offsetof(SubFeedContext, x)
+#define FLAGS (AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption subfeed_options[] = {
+    { "fix_durations", "delay output and determine duration from next frame", OFFSET(fix_durations),   AV_OPT_TYPE_BOOL,   { .i64=1 },                  0, 1, FLAGS, NULL     },
+    { "fix_overlap", "delay output and adjust durations to prevent overlap", OFFSET(fix_overlap),   AV_OPT_TYPE_BOOL,   { .i64=0 },                  0, 1, FLAGS, NULL     },
+    { "mode",       "set feed mode",         OFFSET(mode),      AV_OPT_TYPE_INT,                 { .i64=FM_REPEAT },  FM_REPEAT, FM_FORWARD,  FLAGS, "mode" },
+    {   "repeat",     "repeat recent while valid, send empty otherwise",   0, AV_OPT_TYPE_CONST, { .i64=FM_REPEAT },  0,                  0,  FLAGS, "mode" },
+    {   "scatter",    "subdivide subtitles into 1/framerate segments",     0, AV_OPT_TYPE_CONST, { .i64=FM_SCATTER }, 0,                  0,  FLAGS, "mode" },
+    {   "forward",    "forward only (clears output framerate)",            0, AV_OPT_TYPE_CONST, { .i64=FM_FORWARD }, 0,                  0,  FLAGS, "mode" },
+    { "rate",       "output frame rate",     OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE,         { .str = "5"},       0,         INT_MAX,     FLAGS, NULL },\
+    { "r",          "output frame rate",     OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE,         { .str = "5"},       0,         INT_MAX,     FLAGS, NULL },\
+    { NULL },
+};
+
+AVFILTER_DEFINE_CLASS(subfeed);
+
+static const AVFilterPad inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .filter_frame = filter_frame,
+        .config_props = config_input,
+    },
+};
+
+static const AVFilterPad outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_SUBTITLE,
+        .request_frame = request_frame,
+        .config_props  = config_output,
+    },
+};
+
+const AVFilter ff_sf_subfeed = {
+    .name           = "subfeed",
+    .description    = NULL_IF_CONFIG_SMALL("Scatter subtitle events by duration"),
+    .init           = init,
+    .uninit         = uninit,
+    .priv_size      = sizeof(SubFeedContext),
+    .priv_class     = &subfeed_class,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_QUERY_FUNC(query_formats),
+};
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH 21/24] fftools/ffmpeg: Introduce subtitle filtering new frame-based subtitle encoding
  2022-01-14  1:13 [FFmpeg-devel] [PATCH 00/24] Subtitle Filtering 2022 ffmpegagent
                   ` (19 preceding siblings ...)
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 20/24] avfilter/subfeed: add subtitle feed filter ffmpegagent
@ 2022-01-14  1:13 ` ffmpegagent
  2022-01-14 16:52   ` Michael Niedermayer
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 22/24] avutil/ass_split: Add parsing of hard-space tags (\h) ffmpegagent
                   ` (3 subsequent siblings)
  24 siblings, 1 reply; 217+ messages in thread
From: ffmpegagent @ 2022-01-14  1:13 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: softworkz

From: softworkz <softworkz@hotmail.com>

This commit actually enables subtitle filtering in ffmpeg by
sending and receiving subtitle frames to and from a filtergraph.

The heartbeat functionality from the previous sub2video implementation
is removed and now provided by the 'subfeed' filter.
The other part of sub2video functionality is retained by
auto-insertion of the new graphicsub2video filter.

Justification for changed test refs:

- sub2video
  The new results are identical excepting the last frame which
  is due to the implementation changes

- sub2video_basic
  The previous results had some incorrect output because multiple
  frames had the same dts
  The non-empty content frames are visually identical, the different
  CRC is due to the different blending algorithm that is being used.

- sub2video_time_limited
  The third frame in the previous ref was a repetition, which doesn't
  happen anymore with the new subtitle filtering.

- sub-dvb
  Running ffprobe -show_frames on the source file shows that there
  are 7 subtitle frames with 0 rects in the source at the start
  and 2 at the end. This translates to the 14 and 4 additional
  entries in the new test results.

- filter-overlay-dvdsub-2397
  Overlay results have slightly different CRCs due to different
  blending implementation

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 fftools/ffmpeg.c                          |  493 ++++----
 fftools/ffmpeg.h                          |   13 +-
 fftools/ffmpeg_filter.c                   |  235 ++--
 fftools/ffmpeg_hw.c                       |    2 +-
 fftools/ffmpeg_opt.c                      |    3 +-
 tests/ref/fate/filter-overlay-dvdsub-2397 |  182 +--
 tests/ref/fate/sub-dvb                    |  162 +--
 tests/ref/fate/sub2video                  | 1091 +++++++++++++++++-
 tests/ref/fate/sub2video_basic            | 1239 +++++++++++++++++++--
 tests/ref/fate/sub2video_time_limited     |   78 +-
 10 files changed, 2829 insertions(+), 669 deletions(-)

diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c
index 5d134b025f..d088a3cd2b 100644
--- a/fftools/ffmpeg.c
+++ b/fftools/ffmpeg.c
@@ -143,8 +143,6 @@ static int want_sdp = 1;
 static BenchmarkTimeStamps current_time;
 AVIOContext *progress_avio = NULL;
 
-static uint8_t *subtitle_out;
-
 InputStream **input_streams = NULL;
 int        nb_input_streams = 0;
 InputFile   **input_files   = NULL;
@@ -169,163 +167,6 @@ static int restore_tty;
 static void free_input_threads(void);
 #endif
 
-/* sub2video hack:
-   Convert subtitles to video with alpha to insert them in filter graphs.
-   This is a temporary solution until libavfilter gets real subtitles support.
- */
-
-static int sub2video_get_blank_frame(InputStream *ist)
-{
-    int ret;
-    AVFrame *frame = ist->sub2video.frame;
-
-    av_frame_unref(frame);
-    ist->sub2video.frame->width  = ist->dec_ctx->width  ? ist->dec_ctx->width  : ist->sub2video.w;
-    ist->sub2video.frame->height = ist->dec_ctx->height ? ist->dec_ctx->height : ist->sub2video.h;
-    ist->sub2video.frame->format = AV_PIX_FMT_RGB32;
-    if ((ret = av_frame_get_buffer(frame, 0)) < 0)
-        return ret;
-    memset(frame->data[0], 0, frame->height * frame->linesize[0]);
-    return 0;
-}
-
-static void sub2video_copy_rect(uint8_t *dst, int dst_linesize, int w, int h,
-                                AVSubtitleRect *r)
-{
-    uint32_t *pal, *dst2;
-    uint8_t *src, *src2;
-    int x, y;
-
-    if (r->type != SUBTITLE_BITMAP) {
-        av_log(NULL, AV_LOG_WARNING, "sub2video: non-bitmap subtitle\n");
-        return;
-    }
-    if (r->x < 0 || r->x + r->w > w || r->y < 0 || r->y + r->h > h) {
-        av_log(NULL, AV_LOG_WARNING, "sub2video: rectangle (%d %d %d %d) overflowing %d %d\n",
-            r->x, r->y, r->w, r->h, w, h
-        );
-        return;
-    }
-
-    dst += r->y * dst_linesize + r->x * 4;
-    src = r->data[0];
-    pal = (uint32_t *)r->data[1];
-    for (y = 0; y < r->h; y++) {
-        dst2 = (uint32_t *)dst;
-        src2 = src;
-        for (x = 0; x < r->w; x++)
-            *(dst2++) = pal[*(src2++)];
-        dst += dst_linesize;
-        src += r->linesize[0];
-    }
-}
-
-static void sub2video_push_ref(InputStream *ist, int64_t pts)
-{
-    AVFrame *frame = ist->sub2video.frame;
-    int i;
-    int ret;
-
-    av_assert1(frame->data[0]);
-    ist->sub2video.last_pts = frame->pts = pts;
-    for (i = 0; i < ist->nb_filters; i++) {
-        ret = av_buffersrc_add_frame_flags(ist->filters[i]->filter, frame,
-                                           AV_BUFFERSRC_FLAG_KEEP_REF |
-                                           AV_BUFFERSRC_FLAG_PUSH);
-        if (ret != AVERROR_EOF && ret < 0)
-            av_log(NULL, AV_LOG_WARNING, "Error while add the frame to buffer source(%s).\n",
-                   av_err2str(ret));
-    }
-}
-
-void sub2video_update(InputStream *ist, int64_t heartbeat_pts, AVSubtitle *sub)
-{
-    AVFrame *frame = ist->sub2video.frame;
-    int8_t *dst;
-    int     dst_linesize;
-    int num_rects, i;
-    int64_t pts, end_pts;
-
-    if (!frame)
-        return;
-    if (sub) {
-        pts       = av_rescale_q(sub->pts + sub->start_display_time * 1000LL,
-                                 AV_TIME_BASE_Q, ist->st->time_base);
-        end_pts   = av_rescale_q(sub->pts + sub->end_display_time   * 1000LL,
-                                 AV_TIME_BASE_Q, ist->st->time_base);
-        num_rects = sub->num_rects;
-    } else {
-        /* If we are initializing the system, utilize current heartbeat
-           PTS as the start time, and show until the following subpicture
-           is received. Otherwise, utilize the previous subpicture's end time
-           as the fall-back value. */
-        pts       = ist->sub2video.initialize ?
-                    heartbeat_pts : ist->sub2video.end_pts;
-        end_pts   = INT64_MAX;
-        num_rects = 0;
-    }
-    if (sub2video_get_blank_frame(ist) < 0) {
-        av_log(ist->dec_ctx, AV_LOG_ERROR,
-               "Impossible to get a blank canvas.\n");
-        return;
-    }
-    dst          = frame->data    [0];
-    dst_linesize = frame->linesize[0];
-    for (i = 0; i < num_rects; i++)
-        sub2video_copy_rect(dst, dst_linesize, frame->width, frame->height, sub->rects[i]);
-    sub2video_push_ref(ist, pts);
-    ist->sub2video.end_pts = end_pts;
-    ist->sub2video.initialize = 0;
-}
-
-static void sub2video_heartbeat(InputStream *ist, int64_t pts)
-{
-    InputFile *infile = input_files[ist->file_index];
-    int i, j, nb_reqs;
-    int64_t pts2;
-
-    /* When a frame is read from a file, examine all sub2video streams in
-       the same file and send the sub2video frame again. Otherwise, decoded
-       video frames could be accumulating in the filter graph while a filter
-       (possibly overlay) is desperately waiting for a subtitle frame. */
-    for (i = 0; i < infile->nb_streams; i++) {
-        InputStream *ist2 = input_streams[infile->ist_index + i];
-        if (!ist2->sub2video.frame)
-            continue;
-        /* subtitles seem to be usually muxed ahead of other streams;
-           if not, subtracting a larger time here is necessary */
-        pts2 = av_rescale_q(pts, ist->st->time_base, ist2->st->time_base) - 1;
-        /* do not send the heartbeat frame if the subtitle is already ahead */
-        if (pts2 <= ist2->sub2video.last_pts)
-            continue;
-        if (pts2 >= ist2->sub2video.end_pts || ist2->sub2video.initialize)
-            /* if we have hit the end of the current displayed subpicture,
-               or if we need to initialize the system, update the
-               overlayed subpicture and its start/end times */
-            sub2video_update(ist2, pts2 + 1, NULL);
-        for (j = 0, nb_reqs = 0; j < ist2->nb_filters; j++)
-            nb_reqs += av_buffersrc_get_nb_failed_requests(ist2->filters[j]->filter);
-        if (nb_reqs)
-            sub2video_push_ref(ist2, pts2);
-    }
-}
-
-static void sub2video_flush(InputStream *ist)
-{
-    int i;
-    int ret;
-
-    if (ist->sub2video.end_pts < INT64_MAX)
-        sub2video_update(ist, INT64_MAX, NULL);
-    for (i = 0; i < ist->nb_filters; i++) {
-        ret = av_buffersrc_add_frame(ist->filters[i]->filter, NULL);
-        if (ret != AVERROR_EOF && ret < 0)
-            av_log(NULL, AV_LOG_WARNING, "Flush the frame error.\n");
-    }
-}
-
-/* end of sub2video hack */
-
 static void term_exit_sigsafe(void)
 {
 #if HAVE_TERMIOS_H
@@ -526,7 +367,6 @@ static void ffmpeg_cleanup(int ret)
         avfilter_graph_free(&fg->graph);
         for (j = 0; j < fg->nb_inputs; j++) {
             InputFilter *ifilter = fg->inputs[j];
-            struct InputStream *ist = ifilter->ist;
 
             while (av_fifo_size(ifilter->frame_queue)) {
                 AVFrame *frame;
@@ -536,15 +376,6 @@ static void ffmpeg_cleanup(int ret)
             }
             av_fifo_freep(&ifilter->frame_queue);
             av_freep(&ifilter->displaymatrix);
-            if (ist->sub2video.sub_queue) {
-                while (av_fifo_size(ist->sub2video.sub_queue)) {
-                    AVSubtitle sub;
-                    av_fifo_generic_read(ist->sub2video.sub_queue,
-                                         &sub, sizeof(sub), NULL);
-                    avsubtitle_free(&sub);
-                }
-                av_fifo_freep(&ist->sub2video.sub_queue);
-            }
             av_buffer_unref(&ifilter->hw_frames_ctx);
             av_freep(&ifilter->name);
             av_freep(&fg->inputs[j]);
@@ -564,7 +395,7 @@ static void ffmpeg_cleanup(int ret)
     }
     av_freep(&filtergraphs);
 
-    av_freep(&subtitle_out);
+    ////av_freep(&subtitle_out);
 
     /* close files */
     for (i = 0; i < nb_output_files; i++) {
@@ -632,12 +463,12 @@ static void ffmpeg_cleanup(int ret)
         av_frame_free(&ist->decoded_frame);
         av_packet_free(&ist->pkt);
         av_dict_free(&ist->decoder_opts);
-        avsubtitle_free(&ist->prev_sub.subtitle);
-        av_frame_free(&ist->sub2video.frame);
+        av_frame_free(&ist->prev_sub.subtitle);
         av_freep(&ist->filters);
         av_freep(&ist->hwaccel_device);
         av_freep(&ist->dts_buffer);
 
+        av_buffer_unref(&ist->subtitle_header);
         avcodec_free_context(&ist->dec_ctx);
 
         av_freep(&input_streams[i]);
@@ -1055,33 +886,76 @@ error:
     exit_program(1);
 }
 
-static void do_subtitle_out(OutputFile *of,
-                            OutputStream *ost,
-                            AVSubtitle *sub)
+static void encode_subtitle_frame(OutputFile *of, OutputStream *ost, AVFrame *frame, AVPacket *pkt, int64_t pts_offset)
 {
-    int subtitle_out_max_size = 1024 * 1024;
-    int subtitle_out_size, nb, i;
+    AVCodecContext *enc = ost->enc_ctx;
+    int ret;
+
+        ost->frames_encoded++;
+
+        ret = avcodec_send_frame(enc, frame);
+        if (ret < 0)
+            goto error;
+
+        while (1) {
+            ret = avcodec_receive_packet(enc, pkt);
+            update_benchmark("encode_subtitles %d.%d", ost->file_index, ost->index);
+            if (ret == AVERROR(EAGAIN))
+                break;
+            if (ret < 0)
+                goto error;
+
+            if (debug_ts) {
+                av_log(NULL, AV_LOG_INFO, "encoder -> type:subtitles "
+                       "pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s\n",
+                       av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &enc->time_base),
+                       av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &enc->time_base));
+            }
+
+            pkt->pts  = av_rescale_q(frame->subtitle_timing.start_pts, AV_TIME_BASE_Q, ost->mux_timebase);
+            pkt->duration = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, ost->mux_timebase);
+            pkt->pts += pts_offset;
+
+            pkt->dts = pkt->pts;
+            output_packet(of, pkt, ost, 0);
+        }
+        ost->sync_opts++;
+        ost->frame_number++;
+
+    return;
+error:
+    av_log(NULL, AV_LOG_FATAL, "Subtitle encoding failed - Error code: %d\n", ret);
+    exit_program(1);
+}
+
+static void do_subtitle_out(OutputFile *of, OutputStream *ost, AVFrame *frame)
+{
+    int nb, i;
     AVCodecContext *enc;
     AVPacket *pkt = ost->pkt;
     int64_t pts;
 
-    if (sub->pts == AV_NOPTS_VALUE) {
+    if (!frame)
+        return;
+
+    av_log(NULL, AV_LOG_DEBUG, "do_subtitle_out: sub->pts: %"PRId64"  frame->pts: %"PRId64"\n", frame->subtitle_timing.start_pts, frame->pts);
+
+    if (frame->subtitle_timing.start_pts == AV_NOPTS_VALUE) {
         av_log(NULL, AV_LOG_ERROR, "Subtitle packets must have a pts\n");
         if (exit_on_error)
             exit_program(1);
         return;
     }
 
-    enc = ost->enc_ctx;
-
-    if (!subtitle_out) {
-        subtitle_out = av_malloc(subtitle_out_max_size);
-        if (!subtitle_out) {
-            av_log(NULL, AV_LOG_FATAL, "Failed to allocate subtitle_out\n");
-            exit_program(1);
-        }
+    if (frame->repeat_sub) {
+        av_log(NULL, AV_LOG_WARNING, "Ignoring repeated subtitle frame\n");
+        return;
     }
 
+    init_output_stream_wrapper(ost, frame, 1);
+
+    enc = ost->enc_ctx;
+
     /* Note: DVB subtitle need one packet to draw them and one other
        packet to clear them */
     /* XXX: signal it in the codec context ? */
@@ -1091,50 +965,38 @@ static void do_subtitle_out(OutputFile *of,
         nb = 1;
 
     /* shift timestamp to honor -ss and make check_recording_time() work with -t */
-    pts = sub->pts;
+    pts = frame->subtitle_timing.start_pts;
     if (output_files[ost->file_index]->start_time != AV_NOPTS_VALUE)
         pts -= output_files[ost->file_index]->start_time;
-    for (i = 0; i < nb; i++) {
-        unsigned save_num_rects = sub->num_rects;
 
-        ost->sync_opts = av_rescale_q(pts, AV_TIME_BASE_Q, enc->time_base);
-        if (!check_recording_time(ost))
-            return;
+    ost->sync_opts = av_rescale_q(pts, AV_TIME_BASE_Q, enc->time_base);
+    if (!check_recording_time(ost))
+        return;
 
-        sub->pts = pts;
-        // start_display_time is required to be 0
-        sub->pts               += av_rescale_q(sub->start_display_time, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q);
-        sub->end_display_time  -= sub->start_display_time;
-        sub->start_display_time = 0;
-        if (i == 1)
-            sub->num_rects = 0;
+    frame->subtitle_timing.start_pts = pts;
+
+    for (i = 0; i < nb; i++) {
+        const unsigned save_num_rects = frame->num_subtitle_areas;
+        int64_t pts_offset = 0;
 
         ost->frames_encoded++;
 
-        subtitle_out_size = avcodec_encode_subtitle(enc, subtitle_out,
-                                                    subtitle_out_max_size, sub);
         if (i == 1)
-            sub->num_rects = save_num_rects;
-        if (subtitle_out_size < 0) {
-            av_log(NULL, AV_LOG_FATAL, "Subtitle encoding failed\n");
-            exit_program(1);
-        }
+            frame->num_subtitle_areas = 0;
 
-        av_packet_unref(pkt);
-        pkt->data = subtitle_out;
-        pkt->size = subtitle_out_size;
-        pkt->pts  = av_rescale_q(sub->pts, AV_TIME_BASE_Q, ost->mux_timebase);
-        pkt->duration = av_rescale_q(sub->end_display_time, (AVRational){ 1, 1000 }, ost->mux_timebase);
         if (enc->codec_id == AV_CODEC_ID_DVB_SUBTITLE) {
             /* XXX: the pts correction is handled here. Maybe handling
                it in the codec would be better */
             if (i == 0)
-                pkt->pts += av_rescale_q(sub->start_display_time, (AVRational){ 1, 1000 }, ost->mux_timebase);
+                pts_offset = 0;
             else
-                pkt->pts += av_rescale_q(sub->end_display_time, (AVRational){ 1, 1000 }, ost->mux_timebase);
+                pts_offset = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, ost->mux_timebase);
         }
-        pkt->dts = pkt->pts;
-        output_packet(of, pkt, ost, 0);
+
+        encode_subtitle_frame(of, ost, frame, pkt, pts_offset);
+
+        if (i == 1)
+            frame->num_subtitle_areas = save_num_rects;
     }
 }
 
@@ -1544,8 +1406,26 @@ static int reap_filters(int flush)
                 }
                 do_audio_out(of, ost, filtered_frame);
                 break;
+            case AVMEDIA_TYPE_SUBTITLE:
+
+                if (filtered_frame->format == AV_SUBTITLE_FMT_ASS && !enc->subtitle_header
+                    && filtered_frame->subtitle_header) {
+                    const char *subtitle_header = (char *)filtered_frame->subtitle_header->data;
+                    enc->subtitle_header = (uint8_t *)av_strdup(subtitle_header);
+                    if (!enc->subtitle_header)
+                        return AVERROR(ENOMEM);
+                    enc->subtitle_header_size = strlen(subtitle_header);
+                }
+
+                if ((ost->enc_ctx->width == 0 || ost->enc_ctx->height == 0)
+                    && filter->inputs[0]->w > 0 && filter->inputs[0]->h > 0 ) {
+                    ost->enc_ctx->width = filter->inputs[0]->w;
+                    ost->enc_ctx->height = filter->inputs[0]->h;
+                }
+
+                do_subtitle_out(of, ost, filtered_frame);
+                break;
             default:
-                // TODO support subtitle filters
                 av_assert0(0);
             }
 
@@ -2138,7 +2018,8 @@ static int ifilter_has_all_input_formats(FilterGraph *fg)
     int i;
     for (i = 0; i < fg->nb_inputs; i++) {
         if (fg->inputs[i]->format < 0 && (fg->inputs[i]->type == AVMEDIA_TYPE_AUDIO ||
-                                          fg->inputs[i]->type == AVMEDIA_TYPE_VIDEO))
+                                          fg->inputs[i]->type == AVMEDIA_TYPE_VIDEO ||
+                                          fg->inputs[i]->type == AVMEDIA_TYPE_SUBTITLE))
             return 0;
     }
     return 1;
@@ -2164,8 +2045,12 @@ static int ifilter_send_frame(InputFilter *ifilter, AVFrame *frame, int keep_ref
                        ifilter->channel_layout != frame->channel_layout;
         break;
     case AVMEDIA_TYPE_VIDEO:
+    case AVMEDIA_TYPE_SUBTITLE:
         need_reinit |= ifilter->width  != frame->width ||
                        ifilter->height != frame->height;
+
+        if (need_reinit)
+            need_reinit = 1;
         break;
     }
 
@@ -2243,7 +2128,7 @@ static int ifilter_send_eof(InputFilter *ifilter, int64_t pts)
         // the filtergraph was never configured
         if (ifilter->format < 0)
             ifilter_parameters_from_codecpar(ifilter, ifilter->ist->st->codecpar);
-        if (ifilter->format < 0 && (ifilter->type == AVMEDIA_TYPE_AUDIO || ifilter->type == AVMEDIA_TYPE_VIDEO)) {
+        if (ifilter->format < 0 && (ifilter->type == AVMEDIA_TYPE_AUDIO || ifilter->type == AVMEDIA_TYPE_VIDEO || ifilter->type == AVMEDIA_TYPE_SUBTITLE)) {
             av_log(NULL, AV_LOG_ERROR, "Cannot determine format of input stream %d:%d after EOF\n", ifilter->ist->file_index, ifilter->ist->st->index);
             return AVERROR_INVALIDDATA;
         }
@@ -2281,7 +2166,7 @@ static int decode(AVCodecContext *avctx, AVFrame *frame, int *got_frame, AVPacke
 
 static int send_frame_to_filters(InputStream *ist, AVFrame *decoded_frame)
 {
-    int i, ret;
+    int i, ret = 0;
 
     av_assert1(ist->nb_filters > 0); /* ensure ret is initialized */
     for (i = 0; i < ist->nb_filters; i++) {
@@ -2482,81 +2367,114 @@ fail:
     return err < 0 ? err : ret;
 }
 
-static int transcode_subtitles(InputStream *ist, AVPacket *pkt, int *got_output,
+static InputStream *get_input_stream(OutputStream *ost)
+{
+    if (ost->source_index >= 0)
+        return input_streams[ost->source_index];
+    return NULL;
+}
+
+static int decode_subtitles(InputStream *ist, AVPacket *pkt, int *got_output,
                                int *decode_failed)
 {
-    AVSubtitle subtitle;
-    int free_sub = 1;
-    int i, ret = avcodec_decode_subtitle2(ist->dec_ctx,
-                                          &subtitle, got_output, pkt);
+    AVFrame *decoded_frame;
+    AVCodecContext *avctx = ist->dec_ctx;
+    int i = 0, ret = 0, err = 0;
 
-    check_decode_result(NULL, got_output, ret);
+    if (!ist->decoded_frame && !(ist->decoded_frame = av_frame_alloc()))
+        return AVERROR(ENOMEM);
+    decoded_frame = ist->decoded_frame;
+    decoded_frame->type = AVMEDIA_TYPE_SUBTITLE;
+    decoded_frame->format = avcodec_descriptor_get_subtitle_format(avctx->codec_descriptor);
+
+    if (!ist->subtitle_header && avctx->subtitle_header && avctx->subtitle_header_size > 0) {
+        ist->subtitle_header = av_buffer_allocz(avctx->subtitle_header_size + 1);
+        if (!ist->subtitle_header)
+            return AVERROR(ENOMEM);
+        memcpy(ist->subtitle_header->data, avctx->subtitle_header, avctx->subtitle_header_size);
+    }
+
+    ret = decode(avctx, decoded_frame, got_output, pkt);
+
+    if (ret != AVERROR_EOF)
+        check_decode_result(NULL, got_output, ret);
 
     if (ret < 0 || !*got_output) {
         *decode_failed = 1;
-        if (!pkt->size)
-            sub2video_flush(ist);
+        if (!pkt->size) {
+            // Flush
+            for (i = 0; i < ist->nb_filters; i++) {
+                ret = av_buffersrc_add_frame(ist->filters[i]->filter, NULL);
+                if (ret != AVERROR_EOF && ret < 0)
+                    av_log(NULL, AV_LOG_WARNING, "Flush the frame error.\n");
+            }
+        }
         return ret;
     }
 
     if (ist->fix_sub_duration) {
-        int end = 1;
-        if (ist->prev_sub.got_output) {
-            end = av_rescale(subtitle.pts - ist->prev_sub.subtitle.pts,
-                             1000, AV_TIME_BASE);
-            if (end < ist->prev_sub.subtitle.end_display_time) {
-                av_log(ist->dec_ctx, AV_LOG_DEBUG,
-                       "Subtitle duration reduced from %"PRId32" to %d%s\n",
-                       ist->prev_sub.subtitle.end_display_time, end,
-                       end <= 0 ? ", dropping it" : "");
-                ist->prev_sub.subtitle.end_display_time = end;
+        int64_t end = 1;
+        if (ist->prev_sub.got_output && ist->prev_sub.subtitle) {
+
+            const int64_t duration = ist->prev_sub.subtitle->subtitle_timing.duration;
+            end = decoded_frame->subtitle_timing.start_pts - ist->prev_sub.subtitle->subtitle_timing.start_pts;
+
+            if (end < duration) {
+                av_log(avctx, AV_LOG_DEBUG, "Subtitle duration reduced from %"PRId64" to %"PRId64"%s\n",
+                    duration, end, end <= 0 ? ", dropping it" : "");
+                ist->prev_sub.subtitle->subtitle_timing.duration = end;
             }
         }
-        FFSWAP(int,        *got_output, ist->prev_sub.got_output);
-        FFSWAP(int,        ret,         ist->prev_sub.ret);
-        FFSWAP(AVSubtitle, subtitle,    ist->prev_sub.subtitle);
+        FFSWAP(int,        *got_output,   ist->prev_sub.got_output);
+        FFSWAP(int,        ret,           ist->prev_sub.ret);
+        FFSWAP(AVFrame*,   decoded_frame, ist->prev_sub.subtitle);
         if (end <= 0)
-            goto out;
+            return (int)end;
     }
 
-    if (!*got_output)
+    if (!*got_output || !decoded_frame)
         return ret;
 
-    if (ist->sub2video.frame) {
-        sub2video_update(ist, INT64_MIN, &subtitle);
-    } else if (ist->nb_filters) {
-        if (!ist->sub2video.sub_queue)
-            ist->sub2video.sub_queue = av_fifo_alloc(8 * sizeof(AVSubtitle));
-        if (!ist->sub2video.sub_queue)
-            exit_program(1);
-        if (!av_fifo_space(ist->sub2video.sub_queue)) {
-            ret = av_fifo_realloc2(ist->sub2video.sub_queue, 2 * av_fifo_size(ist->sub2video.sub_queue));
-            if (ret < 0)
-                exit_program(1);
+    decoded_frame->type = AVMEDIA_TYPE_SUBTITLE;
+    decoded_frame->format = avcodec_descriptor_get_subtitle_format(avctx->codec_descriptor);
+
+    if ((ret = av_buffer_replace(&decoded_frame->subtitle_header, ist->subtitle_header)) < 0)
+        return ret;
+
+    decoded_frame->pts = av_rescale_q(decoded_frame->subtitle_timing.start_pts, AV_TIME_BASE_Q, ist->st->time_base);
+
+    if (ist->nb_filters > 0) {
+        AVFrame *filter_frame = av_frame_clone(decoded_frame);
+        if (!filter_frame) {
+            err = AVERROR(ENOMEM);
+            goto end;
         }
-        av_fifo_generic_write(ist->sub2video.sub_queue, &subtitle, sizeof(subtitle), NULL);
-        free_sub = 0;
-    }
 
-    if (!subtitle.num_rects)
-        goto out;
+        err = send_frame_to_filters(ist, filter_frame);
+        av_frame_free(&filter_frame);
+    }
 
-    ist->frames_decoded++;
+    if (err >= 0) {
+        for (i = 0; i < nb_output_streams; i++) {
+            OutputStream *ost = output_streams[i];
+            InputStream *ist_src = get_input_stream(ost);
 
-    for (i = 0; i < nb_output_streams; i++) {
-        OutputStream *ost = output_streams[i];
+            if (!ist_src || !check_output_constraints(ist, ost)
+                || ist_src != ist
+                || !ost->encoding_needed
+                || ost->enc->type != AVMEDIA_TYPE_SUBTITLE)
+                continue;
 
-        if (!check_output_constraints(ist, ost) || !ost->encoding_needed
-            || ost->enc->type != AVMEDIA_TYPE_SUBTITLE)
-            continue;
+            if (ost->filter && ost->filter->filter->nb_inputs > 0)
+                continue;
 
-        do_subtitle_out(output_files[ost->file_index], ost, &subtitle);
+            do_subtitle_out(output_files[ost->file_index], ost, decoded_frame);
+        }
     }
 
-out:
-    if (free_sub)
-        avsubtitle_free(&subtitle);
-    return ret;
+end:
+    av_frame_unref(decoded_frame);
+    return err < 0 ? err : ret;
 }
 
 static int send_filter_eof(InputStream *ist)
@@ -2660,7 +2578,7 @@ static int process_input_packet(InputStream *ist, const AVPacket *pkt, int no_eo
         case AVMEDIA_TYPE_SUBTITLE:
             if (repeating)
                 break;
-            ret = transcode_subtitles(ist, avpkt, &got_output, &decode_failed);
+            ret = decode_subtitles(ist, avpkt, &got_output, &decode_failed);
             if (!pkt && ret >= 0)
                 ret = AVERROR_EOF;
             av_packet_unref(avpkt);
@@ -2928,13 +2846,6 @@ FF_ENABLE_DEPRECATION_WARNINGS
     return 0;
 }
 
-static InputStream *get_input_stream(OutputStream *ost)
-{
-    if (ost->source_index >= 0)
-        return input_streams[ost->source_index];
-    return NULL;
-}
-
 static int compare_int64(const void *a, const void *b)
 {
     return FFDIFFSIGN(*(const int64_t *)a, *(const int64_t *)b);
@@ -3411,7 +3322,7 @@ static int init_output_stream_encode(OutputStream *ost, AVFrame *frame)
         break;
     case AVMEDIA_TYPE_SUBTITLE:
         enc_ctx->time_base = AV_TIME_BASE_Q;
-        if (!enc_ctx->width) {
+        if (!enc_ctx->width && ost->source_index >= 0) {
             enc_ctx->width     = input_streams[ost->source_index]->st->codecpar->width;
             enc_ctx->height    = input_streams[ost->source_index]->st->codecpar->height;
         }
@@ -3464,19 +3375,14 @@ static int init_output_stream(OutputStream *ost, AVFrame *frame,
         }
 
         if (ist && ist->dec->type == AVMEDIA_TYPE_SUBTITLE && ost->enc->type == AVMEDIA_TYPE_SUBTITLE) {
-            int input_props = 0, output_props = 0;
-            AVCodecDescriptor const *input_descriptor =
-                avcodec_descriptor_get(dec->codec_id);
-            AVCodecDescriptor const *output_descriptor =
-                avcodec_descriptor_get(ost->enc_ctx->codec_id);
-            if (input_descriptor)
-                input_props = input_descriptor->props & (AV_CODEC_PROP_TEXT_SUB | AV_CODEC_PROP_BITMAP_SUB);
-            if (output_descriptor)
-                output_props = output_descriptor->props & (AV_CODEC_PROP_TEXT_SUB | AV_CODEC_PROP_BITMAP_SUB);
-            if (input_props && output_props && input_props != output_props) {
-                snprintf(error, error_len,
-                         "Subtitle encoding currently only possible from text to text "
-                         "or bitmap to bitmap");
+            AVCodecDescriptor const *input_descriptor     = avcodec_descriptor_get(dec->codec_id);
+            AVCodecDescriptor const *output_descriptor    = avcodec_descriptor_get(ost->enc_ctx->codec_id);
+            const enum AVSubtitleType in_subtitle_format  = output_descriptor ? avcodec_descriptor_get_subtitle_format(input_descriptor) : AV_SUBTITLE_FMT_UNKNOWN;
+            const enum AVSubtitleType out_subtitle_format = output_descriptor ? avcodec_descriptor_get_subtitle_format(output_descriptor) : AV_SUBTITLE_FMT_UNKNOWN;
+
+            if (ist->nb_filters == 0 && in_subtitle_format != AV_SUBTITLE_FMT_UNKNOWN && out_subtitle_format != AV_SUBTITLE_FMT_UNKNOWN
+                && in_subtitle_format != out_subtitle_format) {
+                snprintf(error, error_len, "Subtitle encoding is only possible from text to text or bitmap to bitmap");
                 return AVERROR_INVALIDDATA;
             }
         }
@@ -3640,7 +3546,8 @@ static int transcode_init(void)
     for (i = 0; i < nb_output_streams; i++) {
         if (!output_streams[i]->stream_copy &&
             (output_streams[i]->enc_ctx->codec_type == AVMEDIA_TYPE_VIDEO ||
-             output_streams[i]->enc_ctx->codec_type == AVMEDIA_TYPE_AUDIO))
+             output_streams[i]->enc_ctx->codec_type == AVMEDIA_TYPE_AUDIO ||
+             output_streams[i]->enc_ctx->codec_type == AVMEDIA_TYPE_SUBTITLE))
             continue;
 
         ret = init_output_stream_wrapper(output_streams[i], NULL, 0);
@@ -4477,8 +4384,6 @@ static int process_input(int file_index)
                av_ts2timestr(input_files[ist->file_index]->ts_offset, &AV_TIME_BASE_Q));
     }
 
-    sub2video_heartbeat(ist, pkt->pts);
-
     process_input_packet(ist, pkt, 0);
 
 discard_packet:
diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h
index 9b200b806a..5ebd01c14e 100644
--- a/fftools/ffmpeg.h
+++ b/fftools/ffmpeg.h
@@ -349,17 +349,10 @@ typedef struct InputStream {
     struct { /* previous decoded subtitle and related variables */
         int got_output;
         int ret;
-        AVSubtitle subtitle;
+        AVFrame *subtitle;
     } prev_sub;
 
-    struct sub2video {
-        int64_t last_pts;
-        int64_t end_pts;
-        AVFifoBuffer *sub_queue;    ///< queue of AVSubtitle* before filter init
-        AVFrame *frame;
-        int w, h;
-        unsigned int initialize; ///< marks if sub2video_update should force an initialization
-    } sub2video;
+    AVBufferRef *subtitle_header;
 
     /* decoded data from this stream goes into all those filters
      * currently video and audio only */
@@ -659,8 +652,6 @@ int filtergraph_is_simple(FilterGraph *fg);
 int init_simple_filtergraph(InputStream *ist, OutputStream *ost);
 int init_complex_filtergraph(FilterGraph *fg);
 
-void sub2video_update(InputStream *ist, int64_t heartbeat_pts, AVSubtitle *sub);
-
 int ifilter_parameters_from_frame(InputFilter *ifilter, const AVFrame *frame);
 
 int ffmpeg_parse_options(int argc, char **argv);
diff --git a/fftools/ffmpeg_filter.c b/fftools/ffmpeg_filter.c
index 1f6cba2c04..17ce79cfaa 100644
--- a/fftools/ffmpeg_filter.c
+++ b/fftools/ffmpeg_filter.c
@@ -22,6 +22,8 @@
 
 #include "ffmpeg.h"
 
+#include "libavutil/ass_split_internal.h"
+
 #include "libavfilter/avfilter.h"
 #include "libavfilter/buffersink.h"
 #include "libavfilter/buffersrc.h"
@@ -30,11 +32,9 @@
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
 #include "libavutil/channel_layout.h"
-#include "libavutil/display.h"
 #include "libavutil/opt.h"
 #include "libavutil/pixdesc.h"
 #include "libavutil/pixfmt.h"
-#include "libavutil/imgutils.h"
 #include "libavutil/samplefmt.h"
 
 // FIXME: YUV420P etc. are actually supported with full color range,
@@ -215,9 +215,8 @@ static void init_input_filter(FilterGraph *fg, AVFilterInOut *in)
     InputFilter *ifilter;
     int i;
 
-    // TODO: support other filter types
-    if (type != AVMEDIA_TYPE_VIDEO && type != AVMEDIA_TYPE_AUDIO) {
-        av_log(NULL, AV_LOG_FATAL, "Only video and audio filters supported "
+    if (type != AVMEDIA_TYPE_VIDEO && type != AVMEDIA_TYPE_AUDIO && type != AVMEDIA_TYPE_SUBTITLE) {
+        av_log(NULL, AV_LOG_FATAL, "Only video, audio and subtitle filters supported "
                "currently.\n");
         exit_program(1);
     }
@@ -238,8 +237,9 @@ static void init_input_filter(FilterGraph *fg, AVFilterInOut *in)
         for (i = 0; i < s->nb_streams; i++) {
             enum AVMediaType stream_type = s->streams[i]->codecpar->codec_type;
             if (stream_type != type &&
-                !(stream_type == AVMEDIA_TYPE_SUBTITLE &&
-                  type == AVMEDIA_TYPE_VIDEO /* sub2video hack */))
+                // in the followng case we auto-insert the graphicsub2video conversion filter
+                // for retaining compatibility with the previous sub2video hack
+                !(stream_type == AVMEDIA_TYPE_SUBTITLE && type == AVMEDIA_TYPE_VIDEO))
                 continue;
             if (check_stream_specifier(s, s->streams[i], *p == ':' ? p + 1 : p) == 1) {
                 st = s->streams[i];
@@ -286,6 +286,17 @@ static void init_input_filter(FilterGraph *fg, AVFilterInOut *in)
     ifilter->type   = ist->st->codecpar->codec_type;
     ifilter->name   = describe_filter_link(fg, in, 1);
 
+    if (ist->st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE && ist->dec_ctx) {
+        const AVCodecDescriptor *codec_descriptor = ist->dec_ctx->codec_descriptor;
+        if (!codec_descriptor)
+            codec_descriptor = avcodec_descriptor_get(ist->dec_ctx->codec_id);
+
+        // For subtitles, we need to set the format here. Would we leave the format
+        // at -1, it would delay processing (ifilter_has_all_input_formats()) until
+        // the first subtile frame arrives, which could never happen in the worst case
+        fg->inputs[fg->nb_inputs - 1]->format = avcodec_descriptor_get_subtitle_format(codec_descriptor);
+    }
+
     ifilter->frame_queue = av_fifo_alloc(8 * sizeof(AVFrame*));
     if (!ifilter->frame_queue)
         exit_program(1);
@@ -405,6 +416,39 @@ static int insert_filter(AVFilterContext **last_filter, int *pad_idx,
     return 0;
 }
 
+static int configure_output_subtitle_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out)
+{
+    OutputStream *ost = ofilter->ost;
+    AVFilterContext *last_filter = out->filter_ctx;
+    int pad_idx = out->pad_idx;
+    int ret;
+    char name[255];
+
+    snprintf(name, sizeof(name), "out_%d_%d", ost->file_index, ost->index);
+    ret = avfilter_graph_create_filter(&ofilter->filter,
+                                       avfilter_get_by_name("sbuffersink"),
+                                       name, NULL, NULL, fg->graph);
+
+    if (ret < 0) {
+        av_log(NULL, AV_LOG_ERROR, "Unable to create filter sbuffersink\n");
+        return ret;
+    }
+
+    ////snprintf(name, sizeof(name), "trim_out_%d_%d",
+    ////         ost->file_index, ost->index);
+    ////ret = insert_trim(of->start_time, of->recording_time,
+    ////                  &last_filter, &pad_idx, name);
+    ////if (ret < 0)
+    ////    return ret;
+
+    ////ost->st->codecpar->codec_tag = MKTAG('a', 's', 's', 's');
+
+    if ((ret = avfilter_link(last_filter, pad_idx, ofilter->filter, 0)) < 0)
+        return ret;
+
+    return 0;
+}
+
 static int configure_output_video_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out)
 {
     OutputStream *ost = ofilter->ost;
@@ -585,7 +629,8 @@ static int configure_output_audio_filter(FilterGraph *fg, OutputFilter *ofilter,
         int i;
 
         for (i=0; i<of->ctx->nb_streams; i++)
-            if (of->ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
+            if (of->ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ||
+                of->ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)
                 break;
 
         if (i<of->ctx->nb_streams) {
@@ -619,6 +664,7 @@ static int configure_output_filter(FilterGraph *fg, OutputFilter *ofilter,
     switch (avfilter_pad_get_type(out->filter_ctx->output_pads, out->pad_idx)) {
     case AVMEDIA_TYPE_VIDEO: return configure_output_video_filter(fg, ofilter, out);
     case AVMEDIA_TYPE_AUDIO: return configure_output_audio_filter(fg, ofilter, out);
+    case AVMEDIA_TYPE_SUBTITLE: return configure_output_subtitle_filter(fg, ofilter, out);
     default: av_assert0(0); return 0;
     }
 }
@@ -638,51 +684,126 @@ void check_filter_outputs(void)
     }
 }
 
-static int sub2video_prepare(InputStream *ist, InputFilter *ifilter)
+static int configure_input_subtitle_filter(FilterGraph *fg, InputFilter *ifilter,
+                                        AVFilterInOut *in)
 {
-    AVFormatContext *avf = input_files[ist->file_index]->ctx;
-    int i, w, h;
+    AVFilterContext *last_filter;
+    const AVFilter *buffer_filt = avfilter_get_by_name("sbuffer");
+    InputStream *ist = ifilter->ist;
+    AVBPrint args;
+    char name[255];
+    int ret, pad_idx = 0;
+    int w, h;
+    AVBufferSrcParameters *par = av_buffersrc_parameters_alloc();
+    enum AVMediaType media_type;
+
+    if (!par)
+        return AVERROR(ENOMEM);
+
+    par->format = AV_PIX_FMT_NONE;
+
+    if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) {
+        av_log(NULL, AV_LOG_ERROR, "Cannot connect subtitle filter to audio input\n");
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+
+    if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO) {
+        av_log(NULL, AV_LOG_ERROR, "Cannot connect subtitle filter to video input\n");
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
 
-    /* Compute the size of the canvas for the subtitles stream.
-       If the subtitles codecpar has set a size, use it. Otherwise use the
-       maximum dimensions of the video streams in the same file. */
     w = ifilter->width;
     h = ifilter->height;
+
     if (!(w && h)) {
-        for (i = 0; i < avf->nb_streams; i++) {
-            if (avf->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
-                w = FFMAX(w, avf->streams[i]->codecpar->width);
-                h = FFMAX(h, avf->streams[i]->codecpar->height);
-            }
-        }
-        if (!(w && h)) {
-            w = FFMAX(w, 720);
-            h = FFMAX(h, 576);
-        }
-        av_log(avf, AV_LOG_INFO, "sub2video: using %dx%d canvas\n", w, h);
+        w = ist->dec_ctx->width;
+        h = ist->dec_ctx->height;
     }
-    ist->sub2video.w = ifilter->width  = w;
-    ist->sub2video.h = ifilter->height = h;
 
-    ifilter->width  = ist->dec_ctx->width  ? ist->dec_ctx->width  : ist->sub2video.w;
-    ifilter->height = ist->dec_ctx->height ? ist->dec_ctx->height : ist->sub2video.h;
+    if (!(w && h) && ist->dec_ctx->subtitle_header) {
+        ASSSplitContext *ass_ctx = avpriv_ass_split((char *)ist->dec_ctx->subtitle_header);
+        ASS *ass = (ASS *)ass_ctx;
+        w = ass->script_info.play_res_x;
+        h = ass->script_info.play_res_y;
+        avpriv_ass_split_free(ass_ctx);
+    }
 
-    /* rectangles are AV_PIX_FMT_PAL8, but we have no guarantee that the
-       palettes for all rectangles are identical or compatible */
-    ifilter->format = AV_PIX_FMT_RGB32;
+    ifilter->width = w;
+    ifilter->height = h;
+    ist->dec_ctx->width = w;
+    ist->dec_ctx->height = h;
 
-    ist->sub2video.frame = av_frame_alloc();
-    if (!ist->sub2video.frame)
-        return AVERROR(ENOMEM);
-    ist->sub2video.last_pts = INT64_MIN;
-    ist->sub2video.end_pts  = INT64_MIN;
+    snprintf(name, sizeof(name), "graph %d subtitle input from stream %d:%d", fg->index,
+             ist->file_index, ist->st->index);
+
+
+    av_bprint_init(&args, 0, AV_BPRINT_SIZE_AUTOMATIC);
+    av_bprintf(&args,
+             "subtitle_type=%d:width=%d:height=%d:time_base=%d/%d:",
+             ifilter->format, ifilter->width, ifilter->height,
+             ist->st->time_base.num, ist->st->time_base.den);
+    if ((ret = avfilter_graph_create_filter(&ifilter->filter, buffer_filt, name,
+                                            args.str, NULL, fg->graph)) < 0)
+        goto fail;
+
+    par->hw_frames_ctx = ifilter->hw_frames_ctx;
+    par->format = ifilter->format;
+    par->width = ifilter->width;
+    par->height = ifilter->height;
+
+    ret = av_buffersrc_parameters_set(ifilter->filter, par);
+    if (ret < 0)
+        goto fail;
+    av_freep(&par);
+    last_filter = ifilter->filter;
+
+    // This is for sub2video compatibility
+    media_type = avfilter_pad_get_type(in->filter_ctx->input_pads, in->pad_idx);
+    if (media_type == AVMEDIA_TYPE_VIDEO) {
+        int subscale_w = w, subscale_h = h;
+
+        av_log(NULL, AV_LOG_INFO, "Auto-inserting subfeed filter\n");
+        ret = insert_filter(&last_filter, &pad_idx, "subfeed", NULL);
+        if (ret < 0)
+            return ret;
+
+        if (!(subscale_w && subscale_h)) {
+            // If the subtitle frame size is unknown, try to find a video input
+            // and use its size for adding a subscale filter
+            for (int i = 0; i < fg->nb_inputs; i++) {
+                InputFilter *input = fg->inputs[i];
+                if (input->type == AVMEDIA_TYPE_VIDEO && input->width && input->height) {
+                    subscale_w = input->width;
+                    subscale_h = input->height;
+                    break;
+                }
+            }
+        }
+
+        if (subscale_w && subscale_h) {
+            char subscale_params[64];
+            snprintf(subscale_params, sizeof(subscale_params), "w=%d:h=%d", subscale_w, subscale_h);
+            ret = insert_filter(&last_filter, &pad_idx, "subscale", subscale_params);
+            if (ret < 0)
+                return ret;
+        }
 
-    /* sub2video structure has been (re-)initialized.
-       Mark it as such so that the system will be
-       initialized with the first received heartbeat. */
-    ist->sub2video.initialize = 1;
+        av_log(NULL, AV_LOG_INFO, "Auto-inserting graphicsub2video filter\n");
+        ret = insert_filter(&last_filter, &pad_idx, "graphicsub2video", NULL);
+        if (ret < 0)
+            return ret;
+    }
+
+    if ((ret = avfilter_link(last_filter, 0, in->filter_ctx, in->pad_idx)) < 0)
+        return ret;
 
     return 0;
+fail:
+    av_freep(&par);
+
+    return ret;
 }
 
 static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter,
@@ -701,8 +822,15 @@ static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter,
     char name[255];
     int ret, pad_idx = 0;
     int64_t tsoffset = 0;
-    AVBufferSrcParameters *par = av_buffersrc_parameters_alloc();
+    AVBufferSrcParameters *par;
 
+    if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_SUBTITLE) {
+        // Automatically insert conversion filter to retain compatibility
+        // with sub2video command lines
+        return configure_input_subtitle_filter(fg, ifilter, in);
+    }
+
+    par = av_buffersrc_parameters_alloc();
     if (!par)
         return AVERROR(ENOMEM);
     memset(par, 0, sizeof(*par));
@@ -717,12 +845,6 @@ static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter,
     if (!fr.num)
         fr = av_guess_frame_rate(input_files[ist->file_index]->ctx, ist->st, NULL);
 
-    if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_SUBTITLE) {
-        ret = sub2video_prepare(ist, ifilter);
-        if (ret < 0)
-            goto fail;
-    }
-
     sar = ifilter->sample_aspect_ratio;
     if(!sar.den)
         sar = (AVRational){0,1};
@@ -734,7 +856,7 @@ static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter,
              tb.num, tb.den, sar.num, sar.den);
     if (fr.num && fr.den)
         av_bprintf(&args, ":frame_rate=%d/%d", fr.num, fr.den);
-    snprintf(name, sizeof(name), "graph %d input from stream %d:%d", fg->index,
+    snprintf(name, sizeof(name), "graph %d video input from stream %d:%d", fg->index,
              ist->file_index, ist->st->index);
 
 
@@ -932,6 +1054,7 @@ static int configure_input_filter(FilterGraph *fg, InputFilter *ifilter,
     switch (avfilter_pad_get_type(in->filter_ctx->input_pads, in->pad_idx)) {
     case AVMEDIA_TYPE_VIDEO: return configure_input_video_filter(fg, ifilter, in);
     case AVMEDIA_TYPE_AUDIO: return configure_input_audio_filter(fg, ifilter, in);
+    case AVMEDIA_TYPE_SUBTITLE: return configure_input_subtitle_filter(fg, ifilter, in);
     default: av_assert0(0); return 0;
     }
 }
@@ -1125,19 +1248,6 @@ int configure_filtergraph(FilterGraph *fg)
         }
     }
 
-    /* process queued up subtitle packets */
-    for (i = 0; i < fg->nb_inputs; i++) {
-        InputStream *ist = fg->inputs[i]->ist;
-        if (ist->sub2video.sub_queue && ist->sub2video.frame) {
-            while (av_fifo_size(ist->sub2video.sub_queue)) {
-                AVSubtitle tmp;
-                av_fifo_generic_read(ist->sub2video.sub_queue, &tmp, sizeof(tmp), NULL);
-                sub2video_update(ist, INT64_MIN, &tmp);
-                avsubtitle_free(&tmp);
-            }
-        }
-    }
-
     return 0;
 
 fail:
@@ -1160,6 +1270,7 @@ int ifilter_parameters_from_frame(InputFilter *ifilter, const AVFrame *frame)
     ifilter->sample_rate         = frame->sample_rate;
     ifilter->channels            = frame->channels;
     ifilter->channel_layout      = frame->channel_layout;
+    ifilter->type                = frame->type;
 
     av_freep(&ifilter->displaymatrix);
     sd = av_frame_get_side_data(frame, AV_FRAME_DATA_DISPLAYMATRIX);
diff --git a/fftools/ffmpeg_hw.c b/fftools/ffmpeg_hw.c
index 14e702bd92..be69d54aaf 100644
--- a/fftools/ffmpeg_hw.c
+++ b/fftools/ffmpeg_hw.c
@@ -449,7 +449,7 @@ int hw_device_setup_for_encode(OutputStream *ost)
     AVBufferRef *frames_ref = NULL;
     int i;
 
-    if (ost->filter) {
+    if (ost->filter && ost->filter->filter) {
         frames_ref = av_buffersink_get_hw_frames_ctx(ost->filter->filter);
         if (frames_ref &&
             ((AVHWFramesContext*)frames_ref->data)->format ==
diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c
index 9c820ab73f..476ef628e4 100644
--- a/fftools/ffmpeg_opt.c
+++ b/fftools/ffmpeg_opt.c
@@ -2211,8 +2211,9 @@ static void init_output_filter(OutputFilter *ofilter, OptionsContext *o,
     switch (ofilter->type) {
     case AVMEDIA_TYPE_VIDEO: ost = new_video_stream(o, oc, -1); break;
     case AVMEDIA_TYPE_AUDIO: ost = new_audio_stream(o, oc, -1); break;
+    case AVMEDIA_TYPE_SUBTITLE: ost = new_subtitle_stream(o, oc, -1); break;
     default:
-        av_log(NULL, AV_LOG_FATAL, "Only video and audio filters are supported "
+        av_log(NULL, AV_LOG_FATAL, "Only video, audio and subtitle filters are supported "
                "currently.\n");
         exit_program(1);
     }
diff --git a/tests/ref/fate/filter-overlay-dvdsub-2397 b/tests/ref/fate/filter-overlay-dvdsub-2397
index 483e5fa4e0..42042b967c 100644
--- a/tests/ref/fate/filter-overlay-dvdsub-2397
+++ b/tests/ref/fate/filter-overlay-dvdsub-2397
@@ -490,368 +490,368 @@
 1,       3877,       3877,       10,     2013, 0x95a39f9c
 1,       3887,       3887,       10,     2013, 0x4f7ea123
 1,       3897,       3897,       10,     2013, 0x9efb9ba1
-0,        117,        117,        1,   518400, 0xbf8523da
+0,        117,        117,        1,   518400, 0xc44e1d4c
 1,       3907,       3907,       10,     2013, 0xf395b2cd
 1,       3917,       3917,       10,     2013, 0x261a881e
 1,       3927,       3927,       10,     2013, 0x7f2d9f72
 1,       3937,       3937,       10,     2013, 0x0105b38d
-0,        118,        118,        1,   518400, 0x41890ed6
+0,        118,        118,        1,   518400, 0xad1a084c
 1,       3952,       3952,       10,     2013, 0x0e5db67e
 1,       3962,       3962,       10,     2013, 0xfc9baf97
-0,        119,        119,        1,   518400, 0x588534fc
+0,        119,        119,        1,   518400, 0x52d52e73
 1,       3972,       3972,       10,     2013, 0x8e02a1b1
 1,       3982,       3982,       10,     2013, 0x6eecaac8
 1,       3992,       3992,       10,     2013, 0xf5558f0c
 1,       4002,       4002,       10,     2013, 0x512ba99b
-0,        120,        120,        1,   518400, 0x2145ebc1
+0,        120,        120,        1,   518400, 0xc732e546
 1,       4012,       4012,       10,     2013, 0x932b9932
 1,       4022,       4022,       10,     2013, 0xc01ea987
-0,        121,        121,        1,   518400, 0x28bca595
+0,        121,        121,        1,   518400, 0xc36f9f14
 1,       4038,       4038,       10,     2013, 0x10879cf7
 1,       4048,       4048,       10,     2013, 0x90679338
 1,       4058,       4058,       10,     2013, 0x077d8a9e
 1,       4068,       4068,       10,     2013, 0x969fa57c
-0,        122,        122,        1,   518400, 0x77dc951e
+0,        122,        122,        1,   518400, 0x78428e8b
 1,       4078,       4078,       10,     2013, 0xe049ab07
 1,       4088,       4088,       10,     2013, 0xf535b3b3
 1,       4098,       4098,       10,     2013, 0xfe76bd37
-0,        123,        123,        1,   518400, 0xe8924c17
+0,        123,        123,        1,   518400, 0xf0f8458d
 1,       4108,       4108,       10,     2013, 0xde79ad8c
 1,       4123,       4123,       10,     2013, 0xe89b9c47
 1,       4133,       4133,       10,     2013, 0xc570b0f0
-0,        124,        124,        1,   518400, 0xadb4cccc
+0,        124,        124,        1,   518400, 0x7083c653
 1,       4143,       4143,       10,     2013, 0xee709cd9
 1,       4153,       4153,       10,     2013, 0xcfe5afab
 1,       4163,       4163,       10,     2013, 0x98ff8ce4
-0,        125,        125,        1,   518400, 0x1d7b56ac
+0,        125,        125,        1,   518400, 0xa105502c
 1,       4173,       4173,       10,     2013, 0x9d19b44c
 1,       4183,       4183,       10,     2013, 0x4349917a
 1,       4193,       4193,       10,     2013, 0xbf54a59a
-0,        126,        126,        1,   518400, 0xad5739a4
+0,        126,        126,        1,   518400, 0xd411331a
 1,       4208,       4208,       10,     2013, 0xc4a399e0
 1,       4218,       4218,       10,     2013, 0x1bf58ff0
 1,       4228,       4228,       10,     2013, 0x3518ac56
-0,        127,        127,        1,   518400, 0x2733d35a
+0,        127,        127,        1,   518400, 0x83b0ccdb
 1,       4238,       4238,       10,     2013, 0xcd38c1de
 1,       4248,       4248,       10,     2013, 0xbe7d9c4d
 1,       4258,       4258,       10,     2013, 0xe113a306
 1,       4268,       4268,       10,     2013, 0x083197ea
-0,        128,        128,        1,   518400, 0x78e76da2
+0,        128,        128,        1,   518400, 0xa9be671a
 1,       4278,       4278,       10,     2013, 0x1929b1eb
 1,       4294,       4294,       10,     2013, 0x5d6ea5af
 1,       4304,       4304,       10,     2013, 0x05519d53
-0,        129,        129,        1,   518400, 0x6c076013
+0,        129,        129,        1,   518400, 0xaeb75983
 1,       4314,       4314,       10,     2013, 0x5773b380
 1,       4324,       4324,       10,     2013, 0xaa70a8f5
 1,       4334,       4334,       10,     2013, 0x990db0ec
-0,        130,        130,        1,   518400, 0x7854f2b1
+0,        130,        130,        1,   518400, 0x81f8ec13
 1,       4344,       4344,       10,     2013, 0x91d3a623
 1,       4354,       4354,       10,     2013, 0xc91f9824
 1,       4364,       4364,       10,     2013, 0x1d058abf
-0,        131,        131,        1,   518400, 0xd2ae1ecd
+0,        131,        131,        1,   518400, 0x8aaa1839
 1,       4379,       4379,       10,     2013, 0x8de1b8d5
 1,       4389,       4389,       10,     2013, 0x7872b06b
 1,       4399,       4399,       10,     2013, 0xa084c203
-0,        132,        132,        1,   518400, 0xf5eab38d
+0,        132,        132,        1,   518400, 0xc98bacf5
 1,       4409,       4409,       10,     2013, 0xff90ae8d
 1,       4419,       4419,       10,     2013, 0x61dead8e
 1,       4429,       4429,       10,     2013, 0xee76b284
-0,        133,        133,        1,   518400, 0x994d3e9c
+0,        133,        133,        1,   518400, 0x31083804
 1,       4439,       4439,       10,     2013, 0xe888af7f
 1,       4449,       4449,       10,     2013, 0x5d57b115
 1,       4464,       4464,       10,     2013, 0xcdbfb1d0
-0,        134,        134,        1,   518400, 0x95ab705a
+0,        134,        134,        1,   518400, 0x540a69dc
 1,       4474,       4474,       10,     2013, 0x2e28a952
 1,       4484,       4484,       10,     2013, 0x4795a994
 1,       4494,       4494,       10,     2013, 0x7e7ea304
 1,       4504,       4504,       10,     2013, 0x9502c1e1
-0,        135,        135,        1,   518400, 0x3c83c5ce
+0,        135,        135,        1,   518400, 0x80d3bf46
 1,       4514,       4514,       10,     2013, 0xf7c78ab2
 1,       4524,       4524,       10,     2013, 0x24049816
 1,       4534,       4534,       10,     2013, 0x52089dcf
-0,        136,        136,        1,   518400, 0xfa22c508
+0,        136,        136,        1,   518400, 0x2967be7f
 1,       4550,       4550,       10,     2013, 0x2150a0b1
 1,       4560,       4560,       10,     2013, 0x3c2e9b93
 1,       4570,       4570,       10,     2013, 0x491f932b
-0,        137,        137,        1,   518400, 0xddda1712
+0,        137,        137,        1,   518400, 0x5a3b1092
 1,       4580,       4580,       10,     2013, 0x31359cf8
 1,       4590,       4590,       10,     2013, 0x1b00ac3f
 1,       4600,       4600,       10,     2013, 0x8d7ab3cb
-0,        138,        138,        1,   518400, 0x985a3b93
+0,        138,        138,        1,   518400, 0x8741350b
 1,       4610,       4610,       10,     2013, 0xb2c2a4de
 1,       4620,       4620,       10,     2013, 0x80a4abf2
 1,       4635,       4635,       10,     2013, 0x0701a4ee
-0,        139,        139,        1,   518400, 0xea63c5e7
+0,        139,        139,        1,   518400, 0xd5a9bf60
 1,       4645,       4645,       10,     2013, 0xdc1ba5bc
 1,       4655,       4655,       10,     2013, 0x6083a8a4
 1,       4665,       4665,       10,     2013, 0x6226ad45
-0,        140,        140,        1,   518400, 0xef64983d
+0,        140,        140,        1,   518400, 0xc05f91ba
 1,       4675,       4675,       10,     2013, 0x2732a205
 1,       4685,       4685,       10,     2013, 0x0f62a0d3
 1,       4695,       4695,       10,     2013, 0xc1799249
-0,        141,        141,        1,   518400, 0x747bb193
+0,        141,        141,        1,   518400, 0x3fdaab0b
 1,       4705,       4705,       10,     2013, 0xbccfa9c8
 1,       4720,       4720,       10,     2013, 0xded096e7
 1,       4730,       4730,       10,     2013, 0x7f0daf43
-0,        142,        142,        1,   518400, 0xb8748862
+0,        142,        142,        1,   518400, 0xab7281d9
 1,       4740,       4740,       10,     2013, 0xc47ea682
 1,       4750,       4750,       10,     2013, 0x5a72b07a
 1,       4760,       4760,       10,     2013, 0x386faa8c
 1,       4770,       4770,       10,     2013, 0xf9919a91
-0,        143,        143,        1,   518400, 0xaab55a5f
+0,        143,        143,        1,   518400, 0xc80053d6
 1,       4780,       4780,       10,     2013, 0x4908897e
 1,       4790,       4790,       10,     2013, 0x4882b594
-0,        144,        144,        1,   518400, 0x7b468add
+0,        144,        144,        1,   518400, 0x6526845c
 1,       4806,       4806,       10,     2013, 0x113e98d1
 1,       4816,       4816,       10,     2013, 0x5098b30d
 1,       4826,       4826,       10,     2013, 0x0ef7b857
 1,       4836,       4836,       10,     2013, 0x216ea176
-0,        145,        145,        1,   518400, 0xf2078707
+0,        145,        145,        1,   518400, 0x1b788089
 1,       4846,       4846,       10,     2013, 0xf906944a
 1,       4856,       4856,       10,     2013, 0xee9b92fb
 1,       4866,       4866,       10,     2013, 0xd6029209
-0,        146,        146,        1,   518400, 0x6a2d931e
+0,        146,        146,        1,   518400, 0xfa8e8ca9
 1,       4876,       4876,       10,     2013, 0x2256a12e
 1,       4891,       4891,       10,     2013, 0x89de8e4a
 1,       4901,       4901,       10,     2013, 0x0bf0a584
-0,        147,        147,        1,   518400, 0xbbe3c417
+0,        147,        147,        1,   518400, 0xb278bda1
 1,       4911,       4911,       10,     2013, 0x6a5ebd58
 1,       4921,       4921,       10,     2013, 0x3edd9aa4
 1,       4931,       4931,       10,     2013, 0xbd66ac26
-0,        148,        148,        1,   518400, 0x6294e449
+0,        148,        148,        1,   518400, 0xb0c3ddca
 1,       4941,       4941,       10,     2013, 0x313896ea
 1,       4951,       4951,       10,     2013, 0x6b83a6a0
 1,       4961,       4961,       10,     2013, 0x9aafb109
-0,        149,        149,        1,   518400, 0xa05721e7
+0,        149,        149,        1,   518400, 0x10351b53
 1,       4976,       4976,       10,     2013, 0x5192a85a
 1,       4986,       4986,       10,     2013, 0x1f919f79
 1,       4996,       4996,       10,     2013, 0xc0799c40
-0,        150,        150,        1,   518400, 0x37749183
+0,        150,        150,        1,   518400, 0xc1408aee
 1,       5006,       5006,       10,     2013, 0x2988bcd8
 1,       5016,       5016,       10,     2013, 0x1482913a
 1,       5026,       5026,       10,     2013, 0x74da9a94
 1,       5036,       5036,       10,     2013, 0x763eb709
-0,        151,        151,        1,   518400, 0xf9d9dca0
+0,        151,        151,        1,   518400, 0xf016d615
 1,       5046,       5046,       10,     2013, 0x1285b405
 1,       5062,       5062,       10,     2013, 0xb6ab9dfc
-0,        152,        152,        1,   518400, 0x5f8ccf08
+0,        152,        152,        1,   518400, 0xa768c892
 1,       5072,       5072,       10,     2013, 0xe4c8bf19
 1,       5082,       5082,       10,     2013, 0xabbbade8
 1,       5092,       5092,       10,     2013, 0xf8b69d89
 1,       5102,       5102,       10,     2013, 0xce04a866
-0,        153,        153,        1,   518400, 0x7303f77b
+0,        153,        153,        1,   518400, 0x11c3f11e
 1,       5112,       5112,       10,     2013, 0x07528abf
 1,       5122,       5122,       10,     2013, 0x74fb98bf
 1,       5132,       5132,       10,     2013, 0x579fb1c9
-0,        154,        154,        1,   518400, 0x22b0513f
+0,        154,        154,        1,   518400, 0xcd9a4ac4
 1,       5147,       5147,       10,     2013, 0x7ddea2ed
 1,       5157,       5157,       10,     2013, 0x296caa2c
 1,       5167,       5167,       10,     2013, 0x346d9c4f
-0,        155,        155,        1,   518400, 0x330485d2
+0,        155,        155,        1,   518400, 0x4ade7f5e
 1,       5177,       5177,       10,     2013, 0x3e1fba15
 1,       5187,       5187,       10,     2013, 0x48a2908f
 1,       5197,       5197,       10,     2013, 0xc1938d09
-0,        156,        156,        1,   518400, 0x7f83daea
+0,        156,        156,        1,   518400, 0x655dd46b
 1,       5207,       5207,       10,     2013, 0x0e96a060
 1,       5217,       5217,       10,     2013, 0x7b6a9e06
 1,       5232,       5232,       10,     2013, 0x5b779d28
-0,        157,        157,        1,   518400, 0xee19f2df
+0,        157,        157,        1,   518400, 0x5ab5ec61
 1,       5242,       5242,       10,     2013, 0xf600aca1
 1,       5252,       5252,       10,     2013, 0x3a6c9e68
 1,       5262,       5262,       10,     2013, 0x0c8dc1b0
-0,        158,        158,        1,   518400, 0xb71b1c77
+0,        158,        158,        1,   518400, 0x45dc15e6
 1,       5272,       5272,       10,     2013, 0x26beb245
 1,       5282,       5282,       10,     2013, 0x2bc09557
 1,       5292,       5292,       10,     2013, 0x27fc8845
 1,       5302,       5302,       10,     2013, 0x1025aa47
-0,        159,        159,        1,   518400, 0xbffc1856
+0,        159,        159,        1,   518400, 0x201911d3
 1,       5318,       5318,       10,     2013, 0xc2e69baa
 1,       5328,       5328,       10,     2013, 0xdb249b92
 1,       5338,       5338,       10,     2013, 0x6ccda29e
-0,        160,        160,        1,   518400, 0xabc125aa
+0,        160,        160,        1,   518400, 0x0fbc1f46
 1,       5348,       5348,       10,     2013, 0xeaf6a1cf
 1,       5358,       5358,       10,     2013, 0x509ba397
 1,       5368,       5368,       10,     2013, 0xfaf8a2df
-0,        161,        161,        1,   518400, 0x5ee467f8
+0,        161,        161,        1,   518400, 0x7e316179
 1,       5378,       5378,       10,     2013, 0x41388f28
 1,       5388,       5388,       10,     2013, 0xfe5eab39
 1,       5403,       5403,       10,     2013, 0xd5ffa066
-0,        162,        162,        1,   518400, 0x6c2cf168
+0,        162,        162,        1,   518400, 0x73bbeaed
 1,       5413,       5413,       10,     2013, 0x6813a30a
 1,       5423,       5423,       10,     2013, 0x9be89718
 1,       5433,       5433,       10,     2013, 0xaec3a27b
-0,        163,        163,        1,   518400, 0x63996b26
+0,        163,        163,        1,   518400, 0x3a7c648a
 1,       5446,       5446,       10,     2013, 0x579a983e
 1,       5456,       5456,       10,     2013, 0x98cea21f
 1,       5466,       5466,       10,     2013, 0xca77a58a
-0,        164,        164,        1,   518400, 0xb34d789a
+0,        164,        164,        1,   518400, 0x9f707209
 1,       5476,       5476,       10,     2013, 0xcbc3b1ee
 1,       5486,       5486,       10,     2013, 0xf3bb8f07
 1,       5496,       5496,       10,     2013, 0x6aeebd92
-0,        165,        165,        1,   518400, 0xf49c030f
+0,        165,        165,        1,   518400, 0x9f25fc5c
 1,       5506,       5506,       10,     2013, 0xe955a449
 1,       5516,       5516,       10,     2013, 0x9436aa5b
 1,       5531,       5531,       10,     2013, 0x4f0a8f9f
-0,        166,        166,        1,   518400, 0x092dc41a
+0,        166,        166,        1,   518400, 0x2ed8bd75
 1,       5541,       5541,       10,     2013, 0x3551b22d
 1,       5551,       5551,       10,     2013, 0x0959a3d4
 1,       5561,       5561,       10,     2013, 0x2ed5a11b
 1,       5571,       5571,       10,     2013, 0x8f52a5c3
-0,        167,        167,        1,   518400, 0x4134c577
+0,        167,        167,        1,   518400, 0xb493becb
 1,       5581,       5581,       10,     2013, 0x6552978d
 1,       5591,       5591,       10,     2013, 0x7dcca0c1
 1,       5601,       5601,       10,     2013, 0xbcd4a3c9
-0,        168,        168,        1,   518400, 0x261de1ed
+0,        168,        168,        1,   518400, 0x7df6db57
 1,       5616,       5616,       10,     2013, 0xfe41a8d8
 1,       5626,       5626,       10,     2013, 0xc85aae14
 1,       5636,       5636,       10,     2013, 0x1185b346
-0,        169,        169,        1,   518400, 0xcbc8566a
+0,        169,        169,        1,   518400, 0x1cb94fca
 1,       5646,       5646,       10,     2013, 0xf7429a0d
 1,       5656,       5656,       10,     2013, 0x48c2a160
 1,       5666,       5666,       10,     2013, 0x9d85a85d
-0,        170,        170,        1,   518400, 0x407a5c76
+0,        170,        170,        1,   518400, 0x70db55d8
 1,       5676,       5676,       10,     2013, 0xbbe89fe9
 1,       5686,       5686,       10,     2013, 0xea429fe2
 1,       5702,       5702,       10,     2013, 0x221ca1d4
-0,        171,        171,        1,   518400, 0x1ed73bb2
+0,        171,        171,        1,   518400, 0xc1d9351b
 1,       5712,       5712,       10,     2013, 0x394b925b
 1,       5722,       5722,       10,     2013, 0x556dc26f
 1,       5732,       5732,       10,     2013, 0xce21a5e1
-0,        172,        172,        1,   518400, 0x8467ddb5
+0,        172,        172,        1,   518400, 0xa4b0d717
 1,       5742,       5742,       10,     2013, 0xbc87c0a8
 1,       5752,       5752,       10,     2013, 0xbac4ac07
 1,       5762,       5762,       10,     2013, 0xdeefa4aa
 1,       5772,       5772,       10,     2013, 0x1f15b362
-0,        173,        173,        1,   518400, 0x0523dc73
+0,        173,        173,        1,   518400, 0x3730d5e9
 1,       5787,       5787,       10,     2013, 0x6406b7b2
 1,       5797,       5797,       10,     2013, 0x8030a03d
-0,        174,        174,        1,   518400, 0x81f5e895
+0,        174,        174,        1,   518400, 0x9673e1ec
 1,       5807,       5807,       10,     2013, 0x0373a5b1
 1,       5817,       5817,       10,     2013, 0x34ef93da
 1,       5827,       5827,       10,     2013, 0x94c198fe
 1,       5837,       5837,       10,     2013, 0xfefcabad
-0,        175,        175,        1,   518400, 0xfc74608d
+0,        175,        175,        1,   518400, 0x877959d5
 1,       5847,       5847,       10,     2013, 0x8755b3ec
 1,       5857,       5857,       10,     2013, 0xe436a6fd
 1,       5872,       5872,       10,     2013, 0x9cf5a11e
-0,        176,        176,        1,   518400, 0xc4e0dae0
+0,        176,        176,        1,   518400, 0x04f3d421
 1,       5882,       5882,       10,     2013, 0x03b8a98c
 1,       5892,       5892,       10,     2013, 0x6216a138
 1,       5902,       5902,       10,     2013, 0xd87b9f12
-0,        177,        177,        1,   518400, 0x98367f5b
+0,        177,        177,        1,   518400, 0x4f3078bc
 1,       5912,       5912,       10,     2013, 0x4ce99653
 1,       5922,       5922,       10,     2013, 0x6c2ea9e2
 1,       5932,       5932,       10,     2013, 0x918cae4c
-0,        178,        178,        1,   518400, 0x0f1a869d
+0,        178,        178,        1,   518400, 0x8a127ff8
 1,       5942,       5942,       10,     2013, 0xd19fa5f2
 1,       5958,       5958,       10,     2013, 0x0bdda7c6
 1,       5968,       5968,       10,     2013, 0x0f9ab0ca
-0,        179,        179,        1,   518400, 0x45b6ccf2
+0,        179,        179,        1,   518400, 0x5864c64f
 1,       5978,       5978,       10,     2013, 0x410a92b1
 1,       5988,       5988,       10,     2013, 0xcfbe9d1c
 1,       5998,       5998,       10,     2013, 0x59ed9d15
-0,        180,        180,        1,   518400, 0x5f9ccb77
+0,        180,        180,        1,   518400, 0xdaccc4c0
 1,       6008,       6008,       10,     2013, 0x4e129e27
 1,       6018,       6018,       10,     2013, 0x7bb9ac0a
 1,       6028,       6028,       10,     2013, 0x826ca82b
-0,        181,        181,        1,   518400, 0x5f15ea31
+0,        181,        181,        1,   518400, 0xd999e376
 1,       6043,       6043,       10,     2013, 0x9ad5a74b
 1,       6053,       6053,       10,     2013, 0x6c5f969a
 1,       6063,       6063,       10,     2013, 0x8479a0e5
-0,        182,        182,        1,   518400, 0x86369f27
+0,        182,        182,        1,   518400, 0x8af39876
 1,       6073,       6073,       10,     2013, 0x165298ef
 1,       6083,       6083,       10,     2013, 0xdcadb4a1
 1,       6093,       6093,       10,     2013, 0xa90e987c
 1,       6103,       6103,       10,     2013, 0x1ac5b510
-0,        183,        183,        1,   518400, 0x2e27f9fa
+0,        183,        183,        1,   518400, 0x5e72f33d
 1,       6113,       6113,       10,     2013, 0x66728d85
 1,       6128,       6128,       10,     2013, 0xe4859fc5
 1,       6138,       6138,       10,     2013, 0x9901786e
-0,        184,        184,        1,   518400, 0xc029a44d
+0,        184,        184,        1,   518400, 0x14af9d92
 1,       6148,       6148,       10,     2013, 0x6aebb406
 1,       6158,       6158,       10,     2013, 0x7d13a2cc
 1,       6168,       6168,       10,     2013, 0x99b7a8cc
-0,        185,        185,        1,   518400, 0xebee33b0
+0,        185,        185,        1,   518400, 0x50b82d10
 1,       6178,       6178,       10,     2013, 0x80b8a624
 1,       6188,       6188,       10,     2013, 0xbb6aa271
 1,       6198,       6198,       10,     2013, 0x17af9e4a
-0,        186,        186,        1,   518400, 0x19e5494f
+0,        186,        186,        1,   518400, 0xc068429c
 1,       6214,       6214,       10,     2013, 0xfaf0a8f1
 1,       6224,       6224,       10,     2013, 0xd6849b93
 1,       6234,       6234,       10,     2013, 0xe9829669
-0,        187,        187,        1,   518400, 0xf697bd7c
+0,        187,        187,        1,   518400, 0x8934b6d1
 1,       6244,       6244,       10,     2013, 0x7ec98944
 1,       6254,       6254,       10,     2013, 0x2b2099a4
 1,       6264,       6264,       10,     2013, 0x1033a82f
-0,        188,        188,        1,   518400, 0x82569002
+0,        188,        188,        1,   518400, 0x11d08947
 1,       6274,       6274,       10,     2013, 0x5ec88990
 1,       6284,       6284,       10,     2013, 0xd2a19b3d
 1,       6299,       6299,       10,     2013, 0xa377b268
-0,        189,        189,        1,   518400, 0xfcb6d707
+0,        189,        189,        1,   518400, 0x8a27d041
 1,       6309,       6309,       10,     2013, 0xfa859901
 1,       6319,       6319,       10,     2013, 0x1713955a
 1,       6329,       6329,       10,     2013, 0x70aab0da
 1,       6339,       6339,       10,     2013, 0xcdaea422
-0,        190,        190,        1,   518400, 0x82a9662b
+0,        190,        190,        1,   518400, 0xab265f7d
 1,       6349,       6349,       10,     2013, 0x65c3bf80
 1,       6359,       6359,       10,     2013, 0x1d75a55f
 1,       6369,       6369,       10,     2013, 0xa5bea4de
-0,        191,        191,        1,   518400, 0x212e16ee
+0,        191,        191,        1,   518400, 0xff491040
 1,       6384,       6384,       10,     2013, 0x184db71c
 1,       6394,       6394,       10,     2013, 0x99858ec8
 1,       6404,       6404,       10,     2013, 0xb8f2aee5
-0,        192,        192,        1,   518400, 0x2ca34dca
+0,        192,        192,        1,   518400, 0x822b4704
 1,       6414,       6414,       10,     2013, 0x4435b2ef
 1,       6424,       6424,       10,     2013, 0x8acfa6c7
 1,       6434,       6434,       10,     2013, 0x42b4c01f
-0,        193,        193,        1,   518400, 0xe9ebe0a5
+0,        193,        193,        1,   518400, 0x4523d9f4
 1,       6444,       6444,       10,     2013, 0x6e308c13
 1,       6454,       6454,       10,     2013, 0x8227a0f6
 1,       6470,       6470,       10,     2013, 0x6f12a7a2
-0,        194,        194,        1,   518400, 0x4e6b6917
+0,        194,        194,        1,   518400, 0xfc3c626e
 1,       6480,       6480,       10,     2013, 0x785392be
 1,       6490,       6490,       10,     2013, 0x81849c2b
 1,       6500,       6500,       10,     2013, 0x5cf2af65
-0,        195,        195,        1,   518400, 0x7dcf20ab
+0,        195,        195,        1,   518400, 0x237319e5
 1,       6510,       6510,       10,     2013, 0x0c6ca6b4
 1,       6520,       6520,       10,     2013, 0x412fab9f
 1,       6530,       6530,       10,     2013, 0x08e792b4
-0,        196,        196,        1,   518400, 0xf30fac97
+0,        196,        196,        1,   518400, 0x892ca5d8
 1,       6540,       6540,       10,     2013, 0x407aace3
 1,       6555,       6555,       10,     2013, 0xd26bac16
 1,       6565,       6565,       10,     2013, 0xac8bb295
-0,        197,        197,        1,   518400, 0xcb9fc692
+0,        197,        197,        1,   518400, 0xc4c0bfc7
 1,       6575,       6575,       10,     2013, 0xddd1949c
 1,       6585,       6585,       10,     2013, 0x6b26b868
 1,       6595,       6595,       10,     2013, 0x5eaba587
 1,       6605,       6605,       10,     2013, 0xef0793b9
-0,        198,        198,        1,   518400, 0x5d05601e
+0,        198,        198,        1,   518400, 0x57c85956
 1,       6615,       6615,       10,     2013, 0xdef19bd6
 1,       6625,       6625,       10,     2013, 0xca98a635
-0,        199,        199,        1,   518400, 0x456c1417
+0,        199,        199,        1,   518400, 0xd6300d46
 1,       6640,       6640,       10,     2013, 0x06269a5a
 1,       6650,       6650,       10,     2013, 0x32cb9952
 1,       6660,       6660,       10,     2013, 0xf01fa95a
 1,       6670,       6670,       10,     2013, 0xefab9e55
-0,        200,        200,        1,   518400, 0x9a0fd1ad
+0,        200,        200,        1,   518400, 0xd3dacaec
 1,       6680,       6680,       10,     2013, 0x55a3b63a
 1,       6690,       6690,       10,     2013, 0xcd36a553
 1,       6700,       6700,       10,     2013, 0x2ec19877
-0,        201,        201,        1,   518400, 0x55db9716
+0,        201,        201,        1,   518400, 0x65429052
 1,       6710,       6710,       10,     2013, 0xc18b924c
 1,       6726,       6726,       10,     2013, 0xf132b04c
 1,       6736,       6736,       10,     2013, 0x7975a44d
-0,        202,        202,        1,   518400, 0x1f0d40d6
+0,        202,        202,        1,   518400, 0xec803a15
 1,       6746,       6746,       10,     2013, 0x2aaf94cb
 1,       6756,       6756,       10,     2013, 0x58cfa60f
 1,       6766,       6766,       10,     2013, 0x9757a658
-0,        203,        203,        1,   518400, 0x73695c82
+0,        203,        203,        1,   518400, 0x7a9a55c9
 1,       6776,       6776,       10,     2013, 0x67ebc0d5
 1,       6786,       6786,       10,     2013, 0x3c50a70e
 1,       6796,       6796,       10,     2013, 0x9c5799c6
-0,        204,        204,        1,   518400, 0xb0f10812
+0,        204,        204,        1,   518400, 0xcac30160
 1,       6811,       6811,       10,     2013, 0x018d85b2
 1,       6821,       6821,       10,     2013, 0x5367a956
-0,        205,        205,        1,   518400, 0xdec18505
-0,        208,        208,        1,   518400, 0xb147b947
-0,        240,        240,        1,   518400, 0x9d2e3977
+0,        205,        205,        1,   518400, 0x7e187e4f
+0,        208,        208,        1,   518400, 0x0be0b2a2
+0,        213,        213,        1,   518400, 0x0be0b2a2
diff --git a/tests/ref/fate/sub-dvb b/tests/ref/fate/sub-dvb
index cbd1801d64..8f33c75d70 100644
--- a/tests/ref/fate/sub-dvb
+++ b/tests/ref/fate/sub-dvb
@@ -1,75 +1,93 @@
 #tb 0: 1/1000000
 #media_type 0: subtitle
 #codec_id 0: dvb_subtitle
-0,   15600000,   15600000,   159000,     1168, 0xd0f89d82
-0,   15759000,   15759000,   159000,       14, 0x064900eb
-0,   15760000,   15760000,   239000,     1544, 0xe60f1751
-0,   15999000,   15999000,   239000,       14, 0x0729010b
-0,   16000000,   16000000,   339000,     1658, 0xbe343093
-0,   16339000,   16339000,   339000,       14, 0x0809012b
-0,   16340000,   16340000,   599000,     2343, 0xc68f07ef
-0,   16939000,   16939000,   599000,       14, 0x08e9014b
-0,   16940000,   16940000,   459000,     2568, 0x0ee657b1
-0,   17399000,   17399000,   459000,       14, 0x09c9016b
-0,   17400000,   17400000,   359000,     3422, 0xba5b63ce
-0,   17759000,   17759000,   359000,       14, 0x0aa9018b
-0,   17760000,   17760000,   219000,     5078, 0x95b19902
-0,   17979000,   17979000,   219000,       14, 0x0b8901ab
-0,   17980000,   17980000,   959000,     5808, 0xc9717b89
-0,   18939000,   18939000,   959000,       14, 0x0c6901cb
-0,   18940000,   18940000,   219000,     6015, 0x0becbfac
-0,   19159000,   19159000,   219000,       14, 0x064900eb
-0,   19160000,   19160000,   259000,     6519, 0xfcd24d26
-0,   19419000,   19419000,   259000,       14, 0x0729010b
-0,   19420000,   19420000,    99000,     7061, 0xf0320408
-0,   19519000,   19519000,    99000,       14, 0x0809012b
-0,   19520000,   19520000,   219000,     4773, 0x66c93074
-0,   19739000,   19739000,   219000,       14, 0x08e9014b
-0,   19740000,   19740000,   219000,     5546, 0x06052c81
-0,   19959000,   19959000,   219000,       14, 0x09c9016b
-0,   19960000,   19960000,   239000,     5754, 0x904f7325
-0,   20199000,   20199000,   239000,       14, 0x0aa9018b
-0,   20200000,   20200000,   139000,     6099, 0xe30cde07
-0,   20339000,   20339000,   139000,       14, 0x0b8901ab
-0,   20340000,   20340000,   799000,     6839, 0x770fcb6c
-0,   21139000,   21139000,   799000,       14, 0x0c6901cb
-0,   21140000,   21140000,   239000,     4744, 0xa91e1b41
-0,   21379000,   21379000,   239000,       14, 0x064900eb
-0,   21380000,   21380000,   339000,     5824, 0xcf6d782b
-0,   21719000,   21719000,   339000,       14, 0x0729010b
-0,   21720000,   21720000,  1439000,     6212, 0xabf8f7cf
-0,   23159000,   23159000,  1439000,       14, 0x0809012b
-0,   23160000,   23160000,  1319000,     7082, 0xd7ca10f2
-0,   24479000,   24479000,  1319000,       14, 0x08e9014b
-0,   24480000,   24480000,   219000,     5345, 0x12b2cae0
-0,   24699000,   24699000,   219000,       14, 0x09c9016b
-0,   24700000,   24700000,   219000,     5765, 0xc7d46192
-0,   24919000,   24919000,   219000,       14, 0x0aa9018b
-0,   24920000,   24920000,   599000,     6557, 0xcb995d30
-0,   25519000,   25519000,   599000,       14, 0x0b8901ab
-0,   25520000,   25520000,   219000,     7091, 0xe6ea0559
-0,   25739000,   25739000,   219000,       14, 0x0c6901cb
-0,   25740000,   25740000,   239000,     7305, 0xb66c404e
-0,   25979000,   25979000,   239000,       14, 0x064900eb
-0,   25980000,   25980000,   359000,     7590, 0x0cc2a481
-0,   26339000,   26339000,   359000,       14, 0x0729010b
-0,   26340000,   26340000,   219000,     4629, 0xe18cfea8
-0,   26559000,   26559000,   219000,       14, 0x0809012b
-0,   26560000,   26560000,   719000,     4785, 0x82043fc0
-0,   27279000,   27279000,   719000,       14, 0x08e9014b
-0,   27280000,   27280000,   459000,     6061, 0xbde7d245
-0,   27739000,   27739000,   459000,       14, 0x09c9016b
-0,   27740000,   27740000,   239000,     6301, 0x92d01a51
-0,   27979000,   27979000,   239000,       14, 0x0aa9018b
-0,   27980000,   27980000,    99000,     6736, 0xbd25a134
-0,   28079000,   28079000,    99000,       14, 0x0b8901ab
-0,   28080000,   28080000,   219000,     7214, 0x7ef93c13
-0,   28299000,   28299000,   219000,       14, 0x0c6901cb
-0,   28300000,   28300000,   239000,     7366, 0x5bed7fcd
-0,   28539000,   28539000,   239000,       14, 0x064900eb
-0,   28540000,   28540000,   599000,     4564, 0x7f4c014b
-0,   29139000,   29139000,   599000,       14, 0x0729010b
-0,   29140000,   29140000,   219000,     4637, 0x682626b7
-0,   29359000,   29359000,   219000,       14, 0x0809012b
-0,   29360000,   29360000,  1679000,     5358, 0x29e30c48
-0,   31039000,   31039000,  1679000,       14, 0x08e9014b
+0,          0,          0,   279000,       14, 0x05d900db
+0,     279000,     279000,   279000,       14, 0x064900eb
+0,     280000,     280000,  4999000,       14, 0x06b900fb
+0,    5279000,    5279000,  4999000,       14, 0x0729010b
+0,    5280000,    5280000,  5019000,       14, 0x0799011b
+0,   10299000,   10299000,  5019000,       14, 0x0809012b
+0,   10300000,   10300000,  3599000,       14, 0x0879013b
+0,   13899000,   13899000,  3599000,       14, 0x08e9014b
+0,   13900000,   13900000,   219000,       14, 0x0959015b
+0,   14119000,   14119000,   219000,       14, 0x09c9016b
+0,   14120000,   14120000,  1439000,       14, 0x0a39017b
+0,   15559000,   15559000,  1439000,       14, 0x0aa9018b
+0,   15560000,   15560000,    39000,       14, 0x0b19019b
+0,   15599000,   15599000,    39000,       14, 0x0b8901ab
+0,   15600000,   15600000,   159000,     1168, 0xd69da022
+0,   15759000,   15759000,   159000,       14, 0x0c6901cb
+0,   15760000,   15760000,   239000,     1544, 0xc5f116f1
+0,   15999000,   15999000,   239000,       14, 0x064900eb
+0,   16000000,   16000000,   339000,     1658, 0x73563033
+0,   16339000,   16339000,   339000,       14, 0x0729010b
+0,   16340000,   16340000,   599000,     2343, 0x7ac2078f
+0,   16939000,   16939000,   599000,       14, 0x0809012b
+0,   16940000,   16940000,   459000,     2568, 0x6eaa5751
+0,   17399000,   17399000,   459000,       14, 0x08e9014b
+0,   17400000,   17400000,   359000,     3422, 0xd9d0636e
+0,   17759000,   17759000,   359000,       14, 0x09c9016b
+0,   17760000,   17760000,   219000,     5078, 0x722c9862
+0,   17979000,   17979000,   219000,       14, 0x0aa9018b
+0,   17980000,   17980000,   959000,     5808, 0x38dd7ae9
+0,   18939000,   18939000,   959000,       14, 0x0b8901ab
+0,   18940000,   18940000,   219000,     6015, 0xd4d2c40c
+0,   19159000,   19159000,   219000,       14, 0x0c6901cb
+0,   19160000,   19160000,   259000,     6519, 0x08af4c86
+0,   19419000,   19419000,   259000,       14, 0x064900eb
+0,   19420000,   19420000,    99000,     7061, 0xecf10368
+0,   19519000,   19519000,    99000,       14, 0x0729010b
+0,   19520000,   19520000,   219000,     4773, 0xbee42fd4
+0,   19739000,   19739000,   219000,       14, 0x0809012b
+0,   19740000,   19740000,   219000,     5546, 0xdb822be1
+0,   19959000,   19959000,   219000,       14, 0x08e9014b
+0,   19960000,   19960000,   239000,     5754, 0xfdcc7285
+0,   20199000,   20199000,   239000,       14, 0x09c9016b
+0,   20200000,   20200000,   139000,     6099, 0xa409dd67
+0,   20339000,   20339000,   139000,       14, 0x0aa9018b
+0,   20340000,   20340000,   799000,     6839, 0xc5eecacc
+0,   21139000,   21139000,   799000,       14, 0x0b8901ab
+0,   21140000,   21140000,   239000,     4744, 0x4e451fa1
+0,   21379000,   21379000,   239000,       14, 0x0c6901cb
+0,   21380000,   21380000,   339000,     5824, 0x5299778b
+0,   21719000,   21719000,   339000,       14, 0x064900eb
+0,   21720000,   21720000,  1439000,     6212, 0x6d15f72f
+0,   23159000,   23159000,  1439000,       14, 0x0729010b
+0,   23160000,   23160000,  1319000,     7082, 0xe5c91052
+0,   24479000,   24479000,  1319000,       14, 0x0809012b
+0,   24480000,   24480000,   219000,     5345, 0x2e5eca40
+0,   24699000,   24699000,   219000,       14, 0x08e9014b
+0,   24700000,   24700000,   219000,     5765, 0x118060f2
+0,   24919000,   24919000,   219000,       14, 0x09c9016b
+0,   24920000,   24920000,   599000,     6557, 0x89275c90
+0,   25519000,   25519000,   599000,       14, 0x0aa9018b
+0,   25520000,   25520000,   219000,     7091, 0x996904b9
+0,   25739000,   25739000,   219000,       14, 0x0b8901ab
+0,   25740000,   25740000,   239000,     7305, 0xc23e44ae
+0,   25979000,   25979000,   239000,       14, 0x0c6901cb
+0,   25980000,   25980000,   359000,     7590, 0xc5a3a3e1
+0,   26339000,   26339000,   359000,       14, 0x064900eb
+0,   26340000,   26340000,   219000,     4629, 0x7ad6fe08
+0,   26559000,   26559000,   219000,       14, 0x0729010b
+0,   26560000,   26560000,   719000,     4785, 0xcd3f3f20
+0,   27279000,   27279000,   719000,       14, 0x0809012b
+0,   27280000,   27280000,   459000,     6061, 0x8b04d1a5
+0,   27739000,   27739000,   459000,       14, 0x08e9014b
+0,   27740000,   27740000,   239000,     6301, 0xe7de19b1
+0,   27979000,   27979000,   239000,       14, 0x09c9016b
+0,   27980000,   27980000,    99000,     6736, 0x38b3a094
+0,   28079000,   28079000,    99000,       14, 0x0aa9018b
+0,   28080000,   28080000,   219000,     7214, 0x0b783b73
+0,   28299000,   28299000,   219000,       14, 0x0b8901ab
+0,   28300000,   28300000,   239000,     7366, 0x98bf842d
+0,   28539000,   28539000,   239000,       14, 0x0c6901cb
+0,   28540000,   28540000,   599000,     4564, 0x3d9600ab
+0,   29139000,   29139000,   599000,       14, 0x064900eb
+0,   29140000,   29140000,   219000,     4637, 0x01f02617
+0,   29359000,   29359000,   219000,       14, 0x0729010b
+0,   29360000,   29360000,  1679000,     5358, 0x5b0f0ba8
+0,   31039000,   31039000,  1679000,       14, 0x0809012b
+0,   31040000,   31040000,   359000,       14, 0x0879013b
+0,   31399000,   31399000,   359000,       14, 0x08e9014b
+0,   31400000,   31400000,   479000,       14, 0x0959015b
+0,   31879000,   31879000,   479000,       14, 0x09c9016b
diff --git a/tests/ref/fate/sub2video b/tests/ref/fate/sub2video
index 80abe9c905..6f9f92b8e0 100644
--- a/tests/ref/fate/sub2video
+++ b/tests/ref/fate/sub2video
@@ -58,129 +58,1136 @@
 0,         47,         47,        1,   518400, 0xde69683f
 0,         48,         48,        1,   518400, 0x7df08fba
 0,         49,         49,        1,   518400, 0xbab197ea
+0,         50,         50,        1,   518400, 0xbab197ea
+0,         51,         51,        1,   518400, 0xbab197ea
+0,         52,         52,        1,   518400, 0xbab197ea
+0,         53,         53,        1,   518400, 0xbab197ea
+0,         54,         54,        1,   518400, 0xbab197ea
+0,         55,         55,        1,   518400, 0xbab197ea
+0,         56,         56,        1,   518400, 0xbab197ea
+0,         57,         57,        1,   518400, 0xbab197ea
+0,         58,         58,        1,   518400, 0xbab197ea
+0,         59,         59,        1,   518400, 0xbab197ea
+0,         60,         60,        1,   518400, 0xbab197ea
+0,         61,         61,        1,   518400, 0xbab197ea
+0,         62,         62,        1,   518400, 0xbab197ea
+0,         63,         63,        1,   518400, 0xbab197ea
+0,         64,         64,        1,   518400, 0xbab197ea
+0,         65,         65,        1,   518400, 0xbab197ea
+0,         66,         66,        1,   518400, 0xbab197ea
+0,         67,         67,        1,   518400, 0xbab197ea
+0,         68,         68,        1,   518400, 0xbab197ea
+0,         69,         69,        1,   518400, 0xbab197ea
+0,         70,         70,        1,   518400, 0xbab197ea
+0,         71,         71,        1,   518400, 0xbab197ea
+0,         72,         72,        1,   518400, 0xbab197ea
+0,         73,         73,        1,   518400, 0xbab197ea
+0,         74,         74,        1,   518400, 0xbab197ea
+0,         75,         75,        1,   518400, 0xbab197ea
+0,         76,         76,        1,   518400, 0xbab197ea
 1,   15355000,   15355000,  4733000,     2094, 0x3c171425
 0,         77,         77,        1,   518400, 0x902285d9
-0,        100,        100,        1,   518400, 0xbab197ea
+0,         78,         78,        1,   518400, 0x902285d9
+0,         79,         79,        1,   518400, 0x902285d9
+0,         80,         80,        1,   518400, 0x902285d9
+0,         81,         81,        1,   518400, 0x902285d9
+0,         82,         82,        1,   518400, 0x902285d9
+0,         83,         83,        1,   518400, 0x902285d9
+0,         84,         84,        1,   518400, 0x902285d9
+0,         85,         85,        1,   518400, 0x902285d9
+0,         86,         86,        1,   518400, 0x902285d9
+0,         87,         87,        1,   518400, 0x902285d9
+0,         88,         88,        1,   518400, 0x902285d9
+0,         89,         89,        1,   518400, 0x902285d9
+0,         90,         90,        1,   518400, 0x902285d9
+0,         91,         91,        1,   518400, 0x902285d9
+0,         92,         92,        1,   518400, 0x902285d9
+0,         93,         93,        1,   518400, 0x902285d9
+0,         94,         94,        1,   518400, 0x902285d9
+0,         95,         95,        1,   518400, 0x902285d9
+0,         96,         96,        1,   518400, 0x902285d9
+0,         97,         97,        1,   518400, 0x902285d9
+0,         98,         98,        1,   518400, 0x902285d9
+0,         99,         99,        1,   518400, 0x902285d9
+0,        100,        100,        1,   518400, 0x902285d9
+0,        101,        101,        1,   518400, 0xbab197ea
+0,        102,        102,        1,   518400, 0xbab197ea
+0,        103,        103,        1,   518400, 0xbab197ea
+0,        104,        104,        1,   518400, 0xbab197ea
+0,        105,        105,        1,   518400, 0xbab197ea
+0,        106,        106,        1,   518400, 0xbab197ea
+0,        107,        107,        1,   518400, 0xbab197ea
+0,        108,        108,        1,   518400, 0xbab197ea
+0,        109,        109,        1,   518400, 0xbab197ea
+0,        110,        110,        1,   518400, 0xbab197ea
+0,        111,        111,        1,   518400, 0xbab197ea
+0,        112,        112,        1,   518400, 0xbab197ea
+0,        113,        113,        1,   518400, 0xbab197ea
+0,        114,        114,        1,   518400, 0xbab197ea
+0,        115,        115,        1,   518400, 0xbab197ea
+0,        116,        116,        1,   518400, 0xbab197ea
+0,        117,        117,        1,   518400, 0xbab197ea
+0,        118,        118,        1,   518400, 0xbab197ea
+0,        119,        119,        1,   518400, 0xbab197ea
+0,        120,        120,        1,   518400, 0xbab197ea
+0,        121,        121,        1,   518400, 0xbab197ea
+0,        122,        122,        1,   518400, 0xbab197ea
+0,        123,        123,        1,   518400, 0xbab197ea
+0,        124,        124,        1,   518400, 0xbab197ea
+0,        125,        125,        1,   518400, 0xbab197ea
+0,        126,        126,        1,   518400, 0xbab197ea
+0,        127,        127,        1,   518400, 0xbab197ea
+0,        128,        128,        1,   518400, 0xbab197ea
+0,        129,        129,        1,   518400, 0xbab197ea
+0,        130,        130,        1,   518400, 0xbab197ea
+0,        131,        131,        1,   518400, 0xbab197ea
+0,        132,        132,        1,   518400, 0xbab197ea
+0,        133,        133,        1,   518400, 0xbab197ea
+0,        134,        134,        1,   518400, 0xbab197ea
+0,        135,        135,        1,   518400, 0xbab197ea
+0,        136,        136,        1,   518400, 0xbab197ea
+0,        137,        137,        1,   518400, 0xbab197ea
+0,        138,        138,        1,   518400, 0xbab197ea
+0,        139,        139,        1,   518400, 0xbab197ea
+0,        140,        140,        1,   518400, 0xbab197ea
+0,        141,        141,        1,   518400, 0xbab197ea
+0,        142,        142,        1,   518400, 0xbab197ea
+0,        143,        143,        1,   518400, 0xbab197ea
+0,        144,        144,        1,   518400, 0xbab197ea
+0,        145,        145,        1,   518400, 0xbab197ea
+0,        146,        146,        1,   518400, 0xbab197ea
+0,        147,        147,        1,   518400, 0xbab197ea
+0,        148,        148,        1,   518400, 0xbab197ea
+0,        149,        149,        1,   518400, 0xbab197ea
+0,        150,        150,        1,   518400, 0xbab197ea
+0,        151,        151,        1,   518400, 0xbab197ea
+0,        152,        152,        1,   518400, 0xbab197ea
+0,        153,        153,        1,   518400, 0xbab197ea
+0,        154,        154,        1,   518400, 0xbab197ea
+0,        155,        155,        1,   518400, 0xbab197ea
+0,        156,        156,        1,   518400, 0xbab197ea
+0,        157,        157,        1,   518400, 0xbab197ea
+0,        158,        158,        1,   518400, 0xbab197ea
+0,        159,        159,        1,   518400, 0xbab197ea
+0,        160,        160,        1,   518400, 0xbab197ea
+0,        161,        161,        1,   518400, 0xbab197ea
+0,        162,        162,        1,   518400, 0xbab197ea
+0,        163,        163,        1,   518400, 0xbab197ea
+0,        164,        164,        1,   518400, 0xbab197ea
+0,        165,        165,        1,   518400, 0xbab197ea
+0,        166,        166,        1,   518400, 0xbab197ea
+0,        167,        167,        1,   518400, 0xbab197ea
+0,        168,        168,        1,   518400, 0xbab197ea
+0,        169,        169,        1,   518400, 0xbab197ea
+0,        170,        170,        1,   518400, 0xbab197ea
+0,        171,        171,        1,   518400, 0xbab197ea
+0,        172,        172,        1,   518400, 0xbab197ea
+0,        173,        173,        1,   518400, 0xbab197ea
+0,        174,        174,        1,   518400, 0xbab197ea
+0,        175,        175,        1,   518400, 0xbab197ea
+0,        176,        176,        1,   518400, 0xbab197ea
+0,        177,        177,        1,   518400, 0xbab197ea
+0,        178,        178,        1,   518400, 0xbab197ea
+0,        179,        179,        1,   518400, 0xbab197ea
+0,        180,        180,        1,   518400, 0xbab197ea
+0,        181,        181,        1,   518400, 0xbab197ea
+0,        182,        182,        1,   518400, 0xbab197ea
+0,        183,        183,        1,   518400, 0xbab197ea
+0,        184,        184,        1,   518400, 0xbab197ea
+0,        185,        185,        1,   518400, 0xbab197ea
+0,        186,        186,        1,   518400, 0xbab197ea
+0,        187,        187,        1,   518400, 0xbab197ea
+0,        188,        188,        1,   518400, 0xbab197ea
+0,        189,        189,        1,   518400, 0xbab197ea
+0,        190,        190,        1,   518400, 0xbab197ea
+0,        191,        191,        1,   518400, 0xbab197ea
+0,        192,        192,        1,   518400, 0xbab197ea
+0,        193,        193,        1,   518400, 0xbab197ea
+0,        194,        194,        1,   518400, 0xbab197ea
+0,        195,        195,        1,   518400, 0xbab197ea
+0,        196,        196,        1,   518400, 0xbab197ea
+0,        197,        197,        1,   518400, 0xbab197ea
+0,        198,        198,        1,   518400, 0xbab197ea
+0,        199,        199,        1,   518400, 0xbab197ea
+0,        200,        200,        1,   518400, 0xbab197ea
+0,        201,        201,        1,   518400, 0xbab197ea
+0,        202,        202,        1,   518400, 0xbab197ea
+0,        203,        203,        1,   518400, 0xbab197ea
+0,        204,        204,        1,   518400, 0xbab197ea
+0,        205,        205,        1,   518400, 0xbab197ea
+0,        206,        206,        1,   518400, 0xbab197ea
+0,        207,        207,        1,   518400, 0xbab197ea
+0,        208,        208,        1,   518400, 0xbab197ea
+0,        209,        209,        1,   518400, 0xbab197ea
+0,        210,        210,        1,   518400, 0xbab197ea
+0,        211,        211,        1,   518400, 0xbab197ea
+0,        212,        212,        1,   518400, 0xbab197ea
+0,        213,        213,        1,   518400, 0xbab197ea
+0,        214,        214,        1,   518400, 0xbab197ea
+0,        215,        215,        1,   518400, 0xbab197ea
+0,        216,        216,        1,   518400, 0xbab197ea
+0,        217,        217,        1,   518400, 0xbab197ea
+0,        218,        218,        1,   518400, 0xbab197ea
+0,        219,        219,        1,   518400, 0xbab197ea
+0,        220,        220,        1,   518400, 0xbab197ea
+0,        221,        221,        1,   518400, 0xbab197ea
+0,        222,        222,        1,   518400, 0xbab197ea
+0,        223,        223,        1,   518400, 0xbab197ea
+0,        224,        224,        1,   518400, 0xbab197ea
+0,        225,        225,        1,   518400, 0xbab197ea
+0,        226,        226,        1,   518400, 0xbab197ea
+0,        227,        227,        1,   518400, 0xbab197ea
+0,        228,        228,        1,   518400, 0xbab197ea
+0,        229,        229,        1,   518400, 0xbab197ea
+0,        230,        230,        1,   518400, 0xbab197ea
+0,        231,        231,        1,   518400, 0xbab197ea
+0,        232,        232,        1,   518400, 0xbab197ea
+0,        233,        233,        1,   518400, 0xbab197ea
+0,        234,        234,        1,   518400, 0xbab197ea
+0,        235,        235,        1,   518400, 0xbab197ea
+0,        236,        236,        1,   518400, 0xbab197ea
+0,        237,        237,        1,   518400, 0xbab197ea
+0,        238,        238,        1,   518400, 0xbab197ea
+0,        239,        239,        1,   518400, 0xbab197ea
+0,        240,        240,        1,   518400, 0xbab197ea
+0,        241,        241,        1,   518400, 0xbab197ea
+0,        242,        242,        1,   518400, 0xbab197ea
+0,        243,        243,        1,   518400, 0xbab197ea
 1,   48797000,   48797000,  2560000,     2480, 0x7c0edf21
 0,        244,        244,        1,   518400, 0x7a11c812
-0,        257,        257,        1,   518400, 0xbab197ea
+0,        245,        245,        1,   518400, 0x7a11c812
+0,        246,        246,        1,   518400, 0x7a11c812
+0,        247,        247,        1,   518400, 0x7a11c812
+0,        248,        248,        1,   518400, 0x7a11c812
+0,        249,        249,        1,   518400, 0x7a11c812
+0,        250,        250,        1,   518400, 0x7a11c812
+0,        251,        251,        1,   518400, 0x7a11c812
+0,        252,        252,        1,   518400, 0x7a11c812
+0,        253,        253,        1,   518400, 0x7a11c812
+0,        254,        254,        1,   518400, 0x7a11c812
+0,        255,        255,        1,   518400, 0x7a11c812
+0,        256,        256,        1,   518400, 0x7a11c812
+0,        257,        257,        1,   518400, 0x7a11c812
 1,   51433000,   51433000,  2366000,     3059, 0xc95b8a05
 0,        258,        258,        1,   518400, 0x34cdddee
-0,        269,        269,        1,   518400, 0xbab197ea
+0,        259,        259,        1,   518400, 0x34cdddee
+0,        260,        260,        1,   518400, 0x34cdddee
+0,        261,        261,        1,   518400, 0x34cdddee
+0,        262,        262,        1,   518400, 0x34cdddee
+0,        263,        263,        1,   518400, 0x34cdddee
+0,        264,        264,        1,   518400, 0x34cdddee
+0,        265,        265,        1,   518400, 0x34cdddee
+0,        266,        266,        1,   518400, 0x34cdddee
+0,        267,        267,        1,   518400, 0x34cdddee
+0,        268,        268,        1,   518400, 0x34cdddee
+0,        269,        269,        1,   518400, 0x34cdddee
 1,   53910000,   53910000,  2696000,     2095, 0x61bb15ed
 0,        270,        270,        1,   518400, 0x4db4ce51
-0,        283,        283,        1,   518400, 0xbab197ea
+0,        271,        271,        1,   518400, 0x4db4ce51
+0,        272,        272,        1,   518400, 0x4db4ce51
+0,        273,        273,        1,   518400, 0x4db4ce51
+0,        274,        274,        1,   518400, 0x4db4ce51
+0,        275,        275,        1,   518400, 0x4db4ce51
+0,        276,        276,        1,   518400, 0x4db4ce51
+0,        277,        277,        1,   518400, 0x4db4ce51
+0,        278,        278,        1,   518400, 0x4db4ce51
+0,        279,        279,        1,   518400, 0x4db4ce51
+0,        280,        280,        1,   518400, 0x4db4ce51
+0,        281,        281,        1,   518400, 0x4db4ce51
+0,        282,        282,        1,   518400, 0x4db4ce51
+0,        283,        283,        1,   518400, 0x4db4ce51
 1,   56663000,   56663000,  1262000,     1013, 0xc9ae89b7
 0,        284,        284,        1,   518400, 0xe6bc0ea9
-0,        290,        290,        1,   518400, 0xbab197ea
+0,        285,        285,        1,   518400, 0xe6bc0ea9
+0,        286,        286,        1,   518400, 0xe6bc0ea9
+0,        287,        287,        1,   518400, 0xe6bc0ea9
+0,        288,        288,        1,   518400, 0xe6bc0ea9
+0,        289,        289,        1,   518400, 0xe6bc0ea9
+0,        290,        290,        1,   518400, 0xe6bc0ea9
 1,   58014000,   58014000,  1661000,      969, 0xe01878f0
 0,        291,        291,        1,   518400, 0xa8643af7
-0,        298,        298,        1,   518400, 0xbab197ea
+0,        292,        292,        1,   518400, 0xa8643af7
+0,        293,        293,        1,   518400, 0xa8643af7
+0,        294,        294,        1,   518400, 0xa8643af7
+0,        295,        295,        1,   518400, 0xa8643af7
+0,        296,        296,        1,   518400, 0xa8643af7
+0,        297,        297,        1,   518400, 0xa8643af7
+0,        298,        298,        1,   518400, 0xa8643af7
+0,        299,        299,        1,   518400, 0xbab197ea
+0,        300,        300,        1,   518400, 0xbab197ea
+0,        301,        301,        1,   518400, 0xbab197ea
+0,        302,        302,        1,   518400, 0xbab197ea
+0,        303,        303,        1,   518400, 0xbab197ea
+0,        304,        304,        1,   518400, 0xbab197ea
+0,        305,        305,        1,   518400, 0xbab197ea
+0,        306,        306,        1,   518400, 0xbab197ea
+0,        307,        307,        1,   518400, 0xbab197ea
+0,        308,        308,        1,   518400, 0xbab197ea
+0,        309,        309,        1,   518400, 0xbab197ea
+0,        310,        310,        1,   518400, 0xbab197ea
+0,        311,        311,        1,   518400, 0xbab197ea
+0,        312,        312,        1,   518400, 0xbab197ea
+0,        313,        313,        1,   518400, 0xbab197ea
+0,        314,        314,        1,   518400, 0xbab197ea
+0,        315,        315,        1,   518400, 0xbab197ea
+0,        316,        316,        1,   518400, 0xbab197ea
+0,        317,        317,        1,   518400, 0xbab197ea
+0,        318,        318,        1,   518400, 0xbab197ea
+0,        319,        319,        1,   518400, 0xbab197ea
+0,        320,        320,        1,   518400, 0xbab197ea
+0,        321,        321,        1,   518400, 0xbab197ea
+0,        322,        322,        1,   518400, 0xbab197ea
+0,        323,        323,        1,   518400, 0xbab197ea
+0,        324,        324,        1,   518400, 0xbab197ea
+0,        325,        325,        1,   518400, 0xbab197ea
+0,        326,        326,        1,   518400, 0xbab197ea
+0,        327,        327,        1,   518400, 0xbab197ea
+0,        328,        328,        1,   518400, 0xbab197ea
+0,        329,        329,        1,   518400, 0xbab197ea
+0,        330,        330,        1,   518400, 0xbab197ea
+0,        331,        331,        1,   518400, 0xbab197ea
+0,        332,        332,        1,   518400, 0xbab197ea
+0,        333,        333,        1,   518400, 0xbab197ea
+0,        334,        334,        1,   518400, 0xbab197ea
+0,        335,        335,        1,   518400, 0xbab197ea
+0,        336,        336,        1,   518400, 0xbab197ea
+0,        337,        337,        1,   518400, 0xbab197ea
+0,        338,        338,        1,   518400, 0xbab197ea
 1,   67724000,   67724000,  1365000,      844, 0xe7db4fc1
 0,        339,        339,        1,   518400, 0xb1885c67
-0,        345,        345,        1,   518400, 0xbab197ea
+0,        340,        340,        1,   518400, 0xb1885c67
+0,        341,        341,        1,   518400, 0xb1885c67
+0,        342,        342,        1,   518400, 0xb1885c67
+0,        343,        343,        1,   518400, 0xb1885c67
+0,        344,        344,        1,   518400, 0xb1885c67
+0,        345,        345,        1,   518400, 0xb1885c67
 1,   69175000,   69175000,  1558000,      802, 0xf48531ba
 0,        346,        346,        1,   518400, 0x378e3fd0
-0,        354,        354,        1,   518400, 0xbab197ea
+0,        347,        347,        1,   518400, 0x378e3fd0
+0,        348,        348,        1,   518400, 0x378e3fd0
+0,        349,        349,        1,   518400, 0x378e3fd0
+0,        350,        350,        1,   518400, 0x378e3fd0
+0,        351,        351,        1,   518400, 0x378e3fd0
+0,        352,        352,        1,   518400, 0x378e3fd0
+0,        353,        353,        1,   518400, 0x378e3fd0
+0,        354,        354,        1,   518400, 0x378e3fd0
 1,   70819000,   70819000,  1865000,     1709, 0xb4d5a1bd
 0,        355,        355,        1,   518400, 0xa3782469
-0,        363,        363,        1,   518400, 0xbab197ea
+0,        356,        356,        1,   518400, 0xa3782469
+0,        357,        357,        1,   518400, 0xa3782469
+0,        358,        358,        1,   518400, 0xa3782469
+0,        359,        359,        1,   518400, 0xa3782469
+0,        360,        360,        1,   518400, 0xa3782469
+0,        361,        361,        1,   518400, 0xa3782469
+0,        362,        362,        1,   518400, 0xa3782469
+0,        363,        363,        1,   518400, 0xa3782469
 1,   72762000,   72762000,  1968000,     2438, 0x99d7bc82
 0,        364,        364,        1,   518400, 0xba23a0d5
-0,        374,        374,        1,   518400, 0xbab197ea
+0,        365,        365,        1,   518400, 0xba23a0d5
+0,        366,        366,        1,   518400, 0xba23a0d5
+0,        367,        367,        1,   518400, 0xba23a0d5
+0,        368,        368,        1,   518400, 0xba23a0d5
+0,        369,        369,        1,   518400, 0xba23a0d5
+0,        370,        370,        1,   518400, 0xba23a0d5
+0,        371,        371,        1,   518400, 0xba23a0d5
+0,        372,        372,        1,   518400, 0xba23a0d5
+0,        373,        373,        1,   518400, 0xba23a0d5
+0,        374,        374,        1,   518400, 0xba23a0d5
 1,   74806000,   74806000,  1831000,     2116, 0x96514097
 0,        375,        375,        1,   518400, 0x129de2f8
-0,        383,        383,        1,   518400, 0xbab197ea
+0,        376,        376,        1,   518400, 0x129de2f8
+0,        377,        377,        1,   518400, 0x129de2f8
+0,        378,        378,        1,   518400, 0x129de2f8
+0,        379,        379,        1,   518400, 0x129de2f8
+0,        380,        380,        1,   518400, 0x129de2f8
+0,        381,        381,        1,   518400, 0x129de2f8
+0,        382,        382,        1,   518400, 0x129de2f8
+0,        383,        383,        1,   518400, 0x129de2f8
 1,   76716000,   76716000,  1262000,     1822, 0xefccc72e
 0,        384,        384,        1,   518400, 0x19772f0f
-0,        390,        390,        1,   518400, 0xbab197ea
+0,        385,        385,        1,   518400, 0x19772f0f
+0,        386,        386,        1,   518400, 0x19772f0f
+0,        387,        387,        1,   518400, 0x19772f0f
+0,        388,        388,        1,   518400, 0x19772f0f
+0,        389,        389,        1,   518400, 0x19772f0f
+0,        390,        390,        1,   518400, 0x19772f0f
 1,   78051000,   78051000,  1524000,      987, 0x7b927a27
 0,        391,        391,        1,   518400, 0x56f54e73
-0,        398,        398,        1,   518400, 0xbab197ea
+0,        392,        392,        1,   518400, 0x56f54e73
+0,        393,        393,        1,   518400, 0x56f54e73
+0,        394,        394,        1,   518400, 0x56f54e73
+0,        395,        395,        1,   518400, 0x56f54e73
+0,        396,        396,        1,   518400, 0x56f54e73
+0,        397,        397,        1,   518400, 0x56f54e73
+0,        398,        398,        1,   518400, 0x56f54e73
 1,   79644000,   79644000,  2662000,     2956, 0x190778f7
 0,        399,        399,        1,   518400, 0x300b5247
+0,        400,        400,        1,   518400, 0x300b5247
+0,        401,        401,        1,   518400, 0x300b5247
+0,        402,        402,        1,   518400, 0x300b5247
+0,        403,        403,        1,   518400, 0x300b5247
+0,        404,        404,        1,   518400, 0x300b5247
+0,        405,        405,        1,   518400, 0x300b5247
+0,        406,        406,        1,   518400, 0x300b5247
+0,        407,        407,        1,   518400, 0x300b5247
+0,        408,        408,        1,   518400, 0x300b5247
+0,        409,        409,        1,   518400, 0x300b5247
+0,        410,        410,        1,   518400, 0x300b5247
+0,        411,        411,        1,   518400, 0x300b5247
 1,   82380000,   82380000,  2764000,     3094, 0xc021b7d3
-0,        412,        412,        1,   518400, 0xbab197ea
+0,        412,        412,        1,   518400, 0x300b5247
 0,        413,        413,        1,   518400, 0x6fd028fa
-0,        426,        426,        1,   518400, 0xbab197ea
+0,        414,        414,        1,   518400, 0x6fd028fa
+0,        415,        415,        1,   518400, 0x6fd028fa
+0,        416,        416,        1,   518400, 0x6fd028fa
+0,        417,        417,        1,   518400, 0x6fd028fa
+0,        418,        418,        1,   518400, 0x6fd028fa
+0,        419,        419,        1,   518400, 0x6fd028fa
+0,        420,        420,        1,   518400, 0x6fd028fa
+0,        421,        421,        1,   518400, 0x6fd028fa
+0,        422,        422,        1,   518400, 0x6fd028fa
+0,        423,        423,        1,   518400, 0x6fd028fa
+0,        424,        424,        1,   518400, 0x6fd028fa
+0,        425,        425,        1,   518400, 0x6fd028fa
+0,        426,        426,        1,   518400, 0x6fd028fa
 1,   85225000,   85225000,  2366000,     2585, 0x74d0048f
 0,        427,        427,        1,   518400, 0x01f80e9d
-0,        438,        438,        1,   518400, 0xbab197ea
+0,        428,        428,        1,   518400, 0x01f80e9d
+0,        429,        429,        1,   518400, 0x01f80e9d
+0,        430,        430,        1,   518400, 0x01f80e9d
+0,        431,        431,        1,   518400, 0x01f80e9d
+0,        432,        432,        1,   518400, 0x01f80e9d
+0,        433,        433,        1,   518400, 0x01f80e9d
+0,        434,        434,        1,   518400, 0x01f80e9d
+0,        435,        435,        1,   518400, 0x01f80e9d
+0,        436,        436,        1,   518400, 0x01f80e9d
+0,        437,        437,        1,   518400, 0x01f80e9d
+0,        438,        438,        1,   518400, 0x01f80e9d
 1,   87652000,   87652000,  1831000,      634, 0x8832fda1
 0,        439,        439,        1,   518400, 0xb48d90c0
-0,        447,        447,        1,   518400, 0xbab197ea
+0,        440,        440,        1,   518400, 0xb48d90c0
+0,        441,        441,        1,   518400, 0xb48d90c0
+0,        442,        442,        1,   518400, 0xb48d90c0
+0,        443,        443,        1,   518400, 0xb48d90c0
+0,        444,        444,        1,   518400, 0xb48d90c0
+0,        445,        445,        1,   518400, 0xb48d90c0
+0,        446,        446,        1,   518400, 0xb48d90c0
+0,        447,        447,        1,   518400, 0xb48d90c0
+0,        448,        448,        1,   518400, 0xbab197ea
+0,        449,        449,        1,   518400, 0xbab197ea
+0,        450,        450,        1,   518400, 0xbab197ea
+0,        451,        451,        1,   518400, 0xbab197ea
+0,        452,        452,        1,   518400, 0xbab197ea
+0,        453,        453,        1,   518400, 0xbab197ea
+0,        454,        454,        1,   518400, 0xbab197ea
+0,        455,        455,        1,   518400, 0xbab197ea
+0,        456,        456,        1,   518400, 0xbab197ea
+0,        457,        457,        1,   518400, 0xbab197ea
 1,   91531000,   91531000,  2332000,     2080, 0x97a1146f
 0,        458,        458,        1,   518400, 0xcb5a0173
-0,        469,        469,        1,   518400, 0xbab197ea
+0,        459,        459,        1,   518400, 0xcb5a0173
+0,        460,        460,        1,   518400, 0xcb5a0173
+0,        461,        461,        1,   518400, 0xcb5a0173
+0,        462,        462,        1,   518400, 0xcb5a0173
+0,        463,        463,        1,   518400, 0xcb5a0173
+0,        464,        464,        1,   518400, 0xcb5a0173
+0,        465,        465,        1,   518400, 0xcb5a0173
+0,        466,        466,        1,   518400, 0xcb5a0173
+0,        467,        467,        1,   518400, 0xcb5a0173
+0,        468,        468,        1,   518400, 0xcb5a0173
+0,        469,        469,        1,   518400, 0xcb5a0173
+0,        470,        470,        1,   518400, 0xbab197ea
+0,        471,        471,        1,   518400, 0xbab197ea
+0,        472,        472,        1,   518400, 0xbab197ea
+0,        473,        473,        1,   518400, 0xbab197ea
+0,        474,        474,        1,   518400, 0xbab197ea
+0,        475,        475,        1,   518400, 0xbab197ea
+0,        476,        476,        1,   518400, 0xbab197ea
+0,        477,        477,        1,   518400, 0xbab197ea
 1,   95510000,   95510000,  3299000,     2964, 0x8b8f6684
 0,        478,        478,        1,   518400, 0xb8a323e4
-0,        494,        494,        1,   518400, 0xbab197ea
+0,        479,        479,        1,   518400, 0xb8a323e4
+0,        480,        480,        1,   518400, 0xb8a323e4
+0,        481,        481,        1,   518400, 0xb8a323e4
+0,        482,        482,        1,   518400, 0xb8a323e4
+0,        483,        483,        1,   518400, 0xb8a323e4
+0,        484,        484,        1,   518400, 0xb8a323e4
+0,        485,        485,        1,   518400, 0xb8a323e4
+0,        486,        486,        1,   518400, 0xb8a323e4
+0,        487,        487,        1,   518400, 0xb8a323e4
+0,        488,        488,        1,   518400, 0xb8a323e4
+0,        489,        489,        1,   518400, 0xb8a323e4
+0,        490,        490,        1,   518400, 0xb8a323e4
+0,        491,        491,        1,   518400, 0xb8a323e4
+0,        492,        492,        1,   518400, 0xb8a323e4
+0,        493,        493,        1,   518400, 0xb8a323e4
+0,        494,        494,        1,   518400, 0xb8a323e4
 1,   98872000,   98872000,  2161000,     1875, 0x9002ef71
 0,        495,        495,        1,   518400, 0xc43518ba
-0,        505,        505,        1,   518400, 0xbab197ea
+0,        496,        496,        1,   518400, 0xc43518ba
+0,        497,        497,        1,   518400, 0xc43518ba
+0,        498,        498,        1,   518400, 0xc43518ba
+0,        499,        499,        1,   518400, 0xc43518ba
+0,        500,        500,        1,   518400, 0xc43518ba
+0,        501,        501,        1,   518400, 0xc43518ba
+0,        502,        502,        1,   518400, 0xc43518ba
+0,        503,        503,        1,   518400, 0xc43518ba
+0,        504,        504,        1,   518400, 0xc43518ba
+0,        505,        505,        1,   518400, 0xc43518ba
 1,  101124000,  101124000,  4096000,     3872, 0x20c6ed9c
 0,        506,        506,        1,   518400, 0x04e38692
-0,        526,        526,        1,   518400, 0xbab197ea
+0,        507,        507,        1,   518400, 0x04e38692
+0,        508,        508,        1,   518400, 0x04e38692
+0,        509,        509,        1,   518400, 0x04e38692
+0,        510,        510,        1,   518400, 0x04e38692
+0,        511,        511,        1,   518400, 0x04e38692
+0,        512,        512,        1,   518400, 0x04e38692
+0,        513,        513,        1,   518400, 0x04e38692
+0,        514,        514,        1,   518400, 0x04e38692
+0,        515,        515,        1,   518400, 0x04e38692
+0,        516,        516,        1,   518400, 0x04e38692
+0,        517,        517,        1,   518400, 0x04e38692
+0,        518,        518,        1,   518400, 0x04e38692
+0,        519,        519,        1,   518400, 0x04e38692
+0,        520,        520,        1,   518400, 0x04e38692
+0,        521,        521,        1,   518400, 0x04e38692
+0,        522,        522,        1,   518400, 0x04e38692
+0,        523,        523,        1,   518400, 0x04e38692
+0,        524,        524,        1,   518400, 0x04e38692
+0,        525,        525,        1,   518400, 0x04e38692
+0,        526,        526,        1,   518400, 0x04e38692
 1,  105303000,  105303000,  2730000,     3094, 0xf203a663
 0,        527,        527,        1,   518400, 0x856b0ee5
-0,        540,        540,        1,   518400, 0xbab197ea
+0,        528,        528,        1,   518400, 0x856b0ee5
+0,        529,        529,        1,   518400, 0x856b0ee5
+0,        530,        530,        1,   518400, 0x856b0ee5
+0,        531,        531,        1,   518400, 0x856b0ee5
+0,        532,        532,        1,   518400, 0x856b0ee5
+0,        533,        533,        1,   518400, 0x856b0ee5
+0,        534,        534,        1,   518400, 0x856b0ee5
+0,        535,        535,        1,   518400, 0x856b0ee5
+0,        536,        536,        1,   518400, 0x856b0ee5
+0,        537,        537,        1,   518400, 0x856b0ee5
+0,        538,        538,        1,   518400, 0x856b0ee5
+0,        539,        539,        1,   518400, 0x856b0ee5
+0,        540,        540,        1,   518400, 0x856b0ee5
 1,  108106000,  108106000,  2059000,     2404, 0x41a7b429
 0,        541,        541,        1,   518400, 0x3e5beee2
-0,        551,        551,        1,   518400, 0xbab197ea
+0,        542,        542,        1,   518400, 0x3e5beee2
+0,        543,        543,        1,   518400, 0x3e5beee2
+0,        544,        544,        1,   518400, 0x3e5beee2
+0,        545,        545,        1,   518400, 0x3e5beee2
+0,        546,        546,        1,   518400, 0x3e5beee2
+0,        547,        547,        1,   518400, 0x3e5beee2
+0,        548,        548,        1,   518400, 0x3e5beee2
+0,        549,        549,        1,   518400, 0x3e5beee2
+0,        550,        550,        1,   518400, 0x3e5beee2
+0,        551,        551,        1,   518400, 0x3e5beee2
+0,        552,        552,        1,   518400, 0xbab197ea
+0,        553,        553,        1,   518400, 0xbab197ea
+0,        554,        554,        1,   518400, 0xbab197ea
+0,        555,        555,        1,   518400, 0xbab197ea
+0,        556,        556,        1,   518400, 0xbab197ea
+0,        557,        557,        1,   518400, 0xbab197ea
+0,        558,        558,        1,   518400, 0xbab197ea
+0,        559,        559,        1,   518400, 0xbab197ea
+0,        560,        560,        1,   518400, 0xbab197ea
+0,        561,        561,        1,   518400, 0xbab197ea
+0,        562,        562,        1,   518400, 0xbab197ea
+0,        563,        563,        1,   518400, 0xbab197ea
+0,        564,        564,        1,   518400, 0xbab197ea
+0,        565,        565,        1,   518400, 0xbab197ea
+0,        566,        566,        1,   518400, 0xbab197ea
+0,        567,        567,        1,   518400, 0xbab197ea
+0,        568,        568,        1,   518400, 0xbab197ea
+0,        569,        569,        1,   518400, 0xbab197ea
+0,        570,        570,        1,   518400, 0xbab197ea
+0,        571,        571,        1,   518400, 0xbab197ea
+0,        572,        572,        1,   518400, 0xbab197ea
+0,        573,        573,        1,   518400, 0xbab197ea
+0,        574,        574,        1,   518400, 0xbab197ea
+0,        575,        575,        1,   518400, 0xbab197ea
+0,        576,        576,        1,   518400, 0xbab197ea
+0,        577,        577,        1,   518400, 0xbab197ea
+0,        578,        578,        1,   518400, 0xbab197ea
+0,        579,        579,        1,   518400, 0xbab197ea
+0,        580,        580,        1,   518400, 0xbab197ea
+0,        581,        581,        1,   518400, 0xbab197ea
+0,        582,        582,        1,   518400, 0xbab197ea
+0,        583,        583,        1,   518400, 0xbab197ea
+0,        584,        584,        1,   518400, 0xbab197ea
+0,        585,        585,        1,   518400, 0xbab197ea
+0,        586,        586,        1,   518400, 0xbab197ea
+0,        587,        587,        1,   518400, 0xbab197ea
+0,        588,        588,        1,   518400, 0xbab197ea
+0,        589,        589,        1,   518400, 0xbab197ea
+0,        590,        590,        1,   518400, 0xbab197ea
+0,        591,        591,        1,   518400, 0xbab197ea
+0,        592,        592,        1,   518400, 0xbab197ea
+0,        593,        593,        1,   518400, 0xbab197ea
+0,        594,        594,        1,   518400, 0xbab197ea
+0,        595,        595,        1,   518400, 0xbab197ea
+0,        596,        596,        1,   518400, 0xbab197ea
+0,        597,        597,        1,   518400, 0xbab197ea
+0,        598,        598,        1,   518400, 0xbab197ea
+0,        599,        599,        1,   518400, 0xbab197ea
+0,        600,        600,        1,   518400, 0xbab197ea
+0,        601,        601,        1,   518400, 0xbab197ea
+0,        602,        602,        1,   518400, 0xbab197ea
+0,        603,        603,        1,   518400, 0xbab197ea
+0,        604,        604,        1,   518400, 0xbab197ea
+0,        605,        605,        1,   518400, 0xbab197ea
+0,        606,        606,        1,   518400, 0xbab197ea
+0,        607,        607,        1,   518400, 0xbab197ea
+0,        608,        608,        1,   518400, 0xbab197ea
+0,        609,        609,        1,   518400, 0xbab197ea
+0,        610,        610,        1,   518400, 0xbab197ea
+0,        611,        611,        1,   518400, 0xbab197ea
+0,        612,        612,        1,   518400, 0xbab197ea
+0,        613,        613,        1,   518400, 0xbab197ea
+0,        614,        614,        1,   518400, 0xbab197ea
+0,        615,        615,        1,   518400, 0xbab197ea
+0,        616,        616,        1,   518400, 0xbab197ea
+0,        617,        617,        1,   518400, 0xbab197ea
+0,        618,        618,        1,   518400, 0xbab197ea
+0,        619,        619,        1,   518400, 0xbab197ea
+0,        620,        620,        1,   518400, 0xbab197ea
+0,        621,        621,        1,   518400, 0xbab197ea
+0,        622,        622,        1,   518400, 0xbab197ea
+0,        623,        623,        1,   518400, 0xbab197ea
+0,        624,        624,        1,   518400, 0xbab197ea
+0,        625,        625,        1,   518400, 0xbab197ea
+0,        626,        626,        1,   518400, 0xbab197ea
+0,        627,        627,        1,   518400, 0xbab197ea
+0,        628,        628,        1,   518400, 0xbab197ea
+0,        629,        629,        1,   518400, 0xbab197ea
+0,        630,        630,        1,   518400, 0xbab197ea
+0,        631,        631,        1,   518400, 0xbab197ea
+0,        632,        632,        1,   518400, 0xbab197ea
+0,        633,        633,        1,   518400, 0xbab197ea
+0,        634,        634,        1,   518400, 0xbab197ea
+0,        635,        635,        1,   518400, 0xbab197ea
+0,        636,        636,        1,   518400, 0xbab197ea
+0,        637,        637,        1,   518400, 0xbab197ea
+0,        638,        638,        1,   518400, 0xbab197ea
+0,        639,        639,        1,   518400, 0xbab197ea
+0,        640,        640,        1,   518400, 0xbab197ea
+0,        641,        641,        1,   518400, 0xbab197ea
+0,        642,        642,        1,   518400, 0xbab197ea
+0,        643,        643,        1,   518400, 0xbab197ea
+0,        644,        644,        1,   518400, 0xbab197ea
+0,        645,        645,        1,   518400, 0xbab197ea
+0,        646,        646,        1,   518400, 0xbab197ea
+0,        647,        647,        1,   518400, 0xbab197ea
+0,        648,        648,        1,   518400, 0xbab197ea
+0,        649,        649,        1,   518400, 0xbab197ea
+0,        650,        650,        1,   518400, 0xbab197ea
+0,        651,        651,        1,   518400, 0xbab197ea
+0,        652,        652,        1,   518400, 0xbab197ea
+0,        653,        653,        1,   518400, 0xbab197ea
+0,        654,        654,        1,   518400, 0xbab197ea
+0,        655,        655,        1,   518400, 0xbab197ea
+0,        656,        656,        1,   518400, 0xbab197ea
+0,        657,        657,        1,   518400, 0xbab197ea
+0,        658,        658,        1,   518400, 0xbab197ea
+0,        659,        659,        1,   518400, 0xbab197ea
+0,        660,        660,        1,   518400, 0xbab197ea
+0,        661,        661,        1,   518400, 0xbab197ea
+0,        662,        662,        1,   518400, 0xbab197ea
+0,        663,        663,        1,   518400, 0xbab197ea
+0,        664,        664,        1,   518400, 0xbab197ea
+0,        665,        665,        1,   518400, 0xbab197ea
+0,        666,        666,        1,   518400, 0xbab197ea
+0,        667,        667,        1,   518400, 0xbab197ea
+0,        668,        668,        1,   518400, 0xbab197ea
+0,        669,        669,        1,   518400, 0xbab197ea
+0,        670,        670,        1,   518400, 0xbab197ea
+0,        671,        671,        1,   518400, 0xbab197ea
+0,        672,        672,        1,   518400, 0xbab197ea
+0,        673,        673,        1,   518400, 0xbab197ea
+0,        674,        674,        1,   518400, 0xbab197ea
+0,        675,        675,        1,   518400, 0xbab197ea
+0,        676,        676,        1,   518400, 0xbab197ea
+0,        677,        677,        1,   518400, 0xbab197ea
+0,        678,        678,        1,   518400, 0xbab197ea
+0,        679,        679,        1,   518400, 0xbab197ea
+0,        680,        680,        1,   518400, 0xbab197ea
+0,        681,        681,        1,   518400, 0xbab197ea
+0,        682,        682,        1,   518400, 0xbab197ea
+0,        683,        683,        1,   518400, 0xbab197ea
+0,        684,        684,        1,   518400, 0xbab197ea
+0,        685,        685,        1,   518400, 0xbab197ea
+0,        686,        686,        1,   518400, 0xbab197ea
+0,        687,        687,        1,   518400, 0xbab197ea
+0,        688,        688,        1,   518400, 0xbab197ea
+0,        689,        689,        1,   518400, 0xbab197ea
+0,        690,        690,        1,   518400, 0xbab197ea
+0,        691,        691,        1,   518400, 0xbab197ea
+0,        692,        692,        1,   518400, 0xbab197ea
+0,        693,        693,        1,   518400, 0xbab197ea
+0,        694,        694,        1,   518400, 0xbab197ea
+0,        695,        695,        1,   518400, 0xbab197ea
+0,        696,        696,        1,   518400, 0xbab197ea
+0,        697,        697,        1,   518400, 0xbab197ea
+0,        698,        698,        1,   518400, 0xbab197ea
+0,        699,        699,        1,   518400, 0xbab197ea
+0,        700,        700,        1,   518400, 0xbab197ea
+0,        701,        701,        1,   518400, 0xbab197ea
+0,        702,        702,        1,   518400, 0xbab197ea
+0,        703,        703,        1,   518400, 0xbab197ea
+0,        704,        704,        1,   518400, 0xbab197ea
+0,        705,        705,        1,   518400, 0xbab197ea
+0,        706,        706,        1,   518400, 0xbab197ea
+0,        707,        707,        1,   518400, 0xbab197ea
 1,  141556000,  141556000,  1661000,     1088, 0xde20aa20
 0,        708,        708,        1,   518400, 0xb8bc1365
-0,        716,        716,        1,   518400, 0xbab197ea
+0,        709,        709,        1,   518400, 0xb8bc1365
+0,        710,        710,        1,   518400, 0xb8bc1365
+0,        711,        711,        1,   518400, 0xb8bc1365
+0,        712,        712,        1,   518400, 0xb8bc1365
+0,        713,        713,        1,   518400, 0xb8bc1365
+0,        714,        714,        1,   518400, 0xb8bc1365
+0,        715,        715,        1,   518400, 0xb8bc1365
+0,        716,        716,        1,   518400, 0xb8bc1365
+0,        717,        717,        1,   518400, 0xbab197ea
+0,        718,        718,        1,   518400, 0xbab197ea
+0,        719,        719,        1,   518400, 0xbab197ea
+0,        720,        720,        1,   518400, 0xbab197ea
+0,        721,        721,        1,   518400, 0xbab197ea
+0,        722,        722,        1,   518400, 0xbab197ea
+0,        723,        723,        1,   518400, 0xbab197ea
+0,        724,        724,        1,   518400, 0xbab197ea
+0,        725,        725,        1,   518400, 0xbab197ea
+0,        726,        726,        1,   518400, 0xbab197ea
+0,        727,        727,        1,   518400, 0xbab197ea
+0,        728,        728,        1,   518400, 0xbab197ea
+0,        729,        729,        1,   518400, 0xbab197ea
+0,        730,        730,        1,   518400, 0xbab197ea
+0,        731,        731,        1,   518400, 0xbab197ea
+0,        732,        732,        1,   518400, 0xbab197ea
+0,        733,        733,        1,   518400, 0xbab197ea
+0,        734,        734,        1,   518400, 0xbab197ea
+0,        735,        735,        1,   518400, 0xbab197ea
+0,        736,        736,        1,   518400, 0xbab197ea
+0,        737,        737,        1,   518400, 0xbab197ea
+0,        738,        738,        1,   518400, 0xbab197ea
+0,        739,        739,        1,   518400, 0xbab197ea
+0,        740,        740,        1,   518400, 0xbab197ea
+0,        741,        741,        1,   518400, 0xbab197ea
+0,        742,        742,        1,   518400, 0xbab197ea
+0,        743,        743,        1,   518400, 0xbab197ea
+0,        744,        744,        1,   518400, 0xbab197ea
+0,        745,        745,        1,   518400, 0xbab197ea
+0,        746,        746,        1,   518400, 0xbab197ea
+0,        747,        747,        1,   518400, 0xbab197ea
+0,        748,        748,        1,   518400, 0xbab197ea
+0,        749,        749,        1,   518400, 0xbab197ea
+0,        750,        750,        1,   518400, 0xbab197ea
+0,        751,        751,        1,   518400, 0xbab197ea
+0,        752,        752,        1,   518400, 0xbab197ea
+0,        753,        753,        1,   518400, 0xbab197ea
+0,        754,        754,        1,   518400, 0xbab197ea
+0,        755,        755,        1,   518400, 0xbab197ea
+0,        756,        756,        1,   518400, 0xbab197ea
+0,        757,        757,        1,   518400, 0xbab197ea
+0,        758,        758,        1,   518400, 0xbab197ea
+0,        759,        759,        1,   518400, 0xbab197ea
+0,        760,        760,        1,   518400, 0xbab197ea
+0,        761,        761,        1,   518400, 0xbab197ea
+0,        762,        762,        1,   518400, 0xbab197ea
+0,        763,        763,        1,   518400, 0xbab197ea
+0,        764,        764,        1,   518400, 0xbab197ea
+0,        765,        765,        1,   518400, 0xbab197ea
+0,        766,        766,        1,   518400, 0xbab197ea
+0,        767,        767,        1,   518400, 0xbab197ea
+0,        768,        768,        1,   518400, 0xbab197ea
+0,        769,        769,        1,   518400, 0xbab197ea
+0,        770,        770,        1,   518400, 0xbab197ea
+0,        771,        771,        1,   518400, 0xbab197ea
+0,        772,        772,        1,   518400, 0xbab197ea
+0,        773,        773,        1,   518400, 0xbab197ea
+0,        774,        774,        1,   518400, 0xbab197ea
+0,        775,        775,        1,   518400, 0xbab197ea
+0,        776,        776,        1,   518400, 0xbab197ea
+0,        777,        777,        1,   518400, 0xbab197ea
+0,        778,        778,        1,   518400, 0xbab197ea
+0,        779,        779,        1,   518400, 0xbab197ea
+0,        780,        780,        1,   518400, 0xbab197ea
+0,        781,        781,        1,   518400, 0xbab197ea
+0,        782,        782,        1,   518400, 0xbab197ea
+0,        783,        783,        1,   518400, 0xbab197ea
+0,        784,        784,        1,   518400, 0xbab197ea
+0,        785,        785,        1,   518400, 0xbab197ea
+0,        786,        786,        1,   518400, 0xbab197ea
+0,        787,        787,        1,   518400, 0xbab197ea
+0,        788,        788,        1,   518400, 0xbab197ea
+0,        789,        789,        1,   518400, 0xbab197ea
+0,        790,        790,        1,   518400, 0xbab197ea
+0,        791,        791,        1,   518400, 0xbab197ea
+0,        792,        792,        1,   518400, 0xbab197ea
+0,        793,        793,        1,   518400, 0xbab197ea
+0,        794,        794,        1,   518400, 0xbab197ea
+0,        795,        795,        1,   518400, 0xbab197ea
+0,        796,        796,        1,   518400, 0xbab197ea
+0,        797,        797,        1,   518400, 0xbab197ea
+0,        798,        798,        1,   518400, 0xbab197ea
+0,        799,        799,        1,   518400, 0xbab197ea
+0,        800,        800,        1,   518400, 0xbab197ea
+0,        801,        801,        1,   518400, 0xbab197ea
+0,        802,        802,        1,   518400, 0xbab197ea
+0,        803,        803,        1,   518400, 0xbab197ea
+0,        804,        804,        1,   518400, 0xbab197ea
+0,        805,        805,        1,   518400, 0xbab197ea
+0,        806,        806,        1,   518400, 0xbab197ea
+0,        807,        807,        1,   518400, 0xbab197ea
+0,        808,        808,        1,   518400, 0xbab197ea
+0,        809,        809,        1,   518400, 0xbab197ea
+0,        810,        810,        1,   518400, 0xbab197ea
+0,        811,        811,        1,   518400, 0xbab197ea
+0,        812,        812,        1,   518400, 0xbab197ea
+0,        813,        813,        1,   518400, 0xbab197ea
+0,        814,        814,        1,   518400, 0xbab197ea
+0,        815,        815,        1,   518400, 0xbab197ea
+0,        816,        816,        1,   518400, 0xbab197ea
 0,        817,        817,        1,   518400, 0x83efa32d
 1,  163445000,  163445000,  1331000,      339, 0x8bd186ef
-0,        824,        824,        1,   518400, 0xbab197ea
+0,        818,        818,        1,   518400, 0x83efa32d
+0,        819,        819,        1,   518400, 0x83efa32d
+0,        820,        820,        1,   518400, 0x83efa32d
+0,        821,        821,        1,   518400, 0x83efa32d
+0,        822,        822,        1,   518400, 0x83efa32d
+0,        823,        823,        1,   518400, 0x83efa32d
+0,        824,        824,        1,   518400, 0x83efa32d
+0,        825,        825,        1,   518400, 0xbab197ea
+0,        826,        826,        1,   518400, 0xbab197ea
+0,        827,        827,        1,   518400, 0xbab197ea
+0,        828,        828,        1,   518400, 0xbab197ea
+0,        829,        829,        1,   518400, 0xbab197ea
+0,        830,        830,        1,   518400, 0xbab197ea
+0,        831,        831,        1,   518400, 0xbab197ea
+0,        832,        832,        1,   518400, 0xbab197ea
+0,        833,        833,        1,   518400, 0xbab197ea
+0,        834,        834,        1,   518400, 0xbab197ea
+0,        835,        835,        1,   518400, 0xbab197ea
+0,        836,        836,        1,   518400, 0xbab197ea
+0,        837,        837,        1,   518400, 0xbab197ea
+0,        838,        838,        1,   518400, 0xbab197ea
+0,        839,        839,        1,   518400, 0xbab197ea
 0,        840,        840,        1,   518400, 0x03ea0e90
 1,  168049000,  168049000,  1900000,     1312, 0x0bf20e8d
-0,        850,        850,        1,   518400, 0xbab197ea
+0,        841,        841,        1,   518400, 0x03ea0e90
+0,        842,        842,        1,   518400, 0x03ea0e90
+0,        843,        843,        1,   518400, 0x03ea0e90
+0,        844,        844,        1,   518400, 0x03ea0e90
+0,        845,        845,        1,   518400, 0x03ea0e90
+0,        846,        846,        1,   518400, 0x03ea0e90
+0,        847,        847,        1,   518400, 0x03ea0e90
+0,        848,        848,        1,   518400, 0x03ea0e90
+0,        849,        849,        1,   518400, 0x03ea0e90
+0,        850,        850,        1,   518400, 0x03ea0e90
 1,  170035000,  170035000,  1524000,     1279, 0xb6c2dafe
 0,        851,        851,        1,   518400, 0x8780239e
-0,        858,        858,        1,   518400, 0xbab197ea
+0,        852,        852,        1,   518400, 0x8780239e
+0,        853,        853,        1,   518400, 0x8780239e
+0,        854,        854,        1,   518400, 0x8780239e
+0,        855,        855,        1,   518400, 0x8780239e
+0,        856,        856,        1,   518400, 0x8780239e
+0,        857,        857,        1,   518400, 0x8780239e
+0,        858,        858,        1,   518400, 0x8780239e
+0,        859,        859,        1,   518400, 0xbab197ea
+0,        860,        860,        1,   518400, 0xbab197ea
 0,        861,        861,        1,   518400, 0x6eb72347
 1,  172203000,  172203000,  1695000,     1826, 0x9a1ac769
-0,        869,        869,        1,   518400, 0xbab197ea
+0,        862,        862,        1,   518400, 0x6eb72347
+0,        863,        863,        1,   518400, 0x6eb72347
+0,        864,        864,        1,   518400, 0x6eb72347
+0,        865,        865,        1,   518400, 0x6eb72347
+0,        866,        866,        1,   518400, 0x6eb72347
+0,        867,        867,        1,   518400, 0x6eb72347
+0,        868,        868,        1,   518400, 0x6eb72347
+0,        869,        869,        1,   518400, 0x6eb72347
 1,  173947000,  173947000,  1934000,     1474, 0xa9b03cdc
 0,        870,        870,        1,   518400, 0x9c4a3a3d
-0,        879,        879,        1,   518400, 0xbab197ea
+0,        871,        871,        1,   518400, 0x9c4a3a3d
+0,        872,        872,        1,   518400, 0x9c4a3a3d
+0,        873,        873,        1,   518400, 0x9c4a3a3d
+0,        874,        874,        1,   518400, 0x9c4a3a3d
+0,        875,        875,        1,   518400, 0x9c4a3a3d
+0,        876,        876,        1,   518400, 0x9c4a3a3d
+0,        877,        877,        1,   518400, 0x9c4a3a3d
+0,        878,        878,        1,   518400, 0x9c4a3a3d
+0,        879,        879,        1,   518400, 0x9c4a3a3d
 1,  175957000,  175957000,  1763000,     1019, 0x20409355
 0,        880,        880,        1,   518400, 0xc9ebfa89
-0,        889,        889,        1,   518400, 0xbab197ea
+0,        881,        881,        1,   518400, 0xc9ebfa89
+0,        882,        882,        1,   518400, 0xc9ebfa89
+0,        883,        883,        1,   518400, 0xc9ebfa89
+0,        884,        884,        1,   518400, 0xc9ebfa89
+0,        885,        885,        1,   518400, 0xc9ebfa89
+0,        886,        886,        1,   518400, 0xc9ebfa89
+0,        887,        887,        1,   518400, 0xc9ebfa89
+0,        888,        888,        1,   518400, 0xc9ebfa89
+0,        889,        889,        1,   518400, 0xc9ebfa89
+0,        890,        890,        1,   518400, 0xbab197ea
+0,        891,        891,        1,   518400, 0xbab197ea
+0,        892,        892,        1,   518400, 0xbab197ea
+0,        893,        893,        1,   518400, 0xbab197ea
+0,        894,        894,        1,   518400, 0xbab197ea
+0,        895,        895,        1,   518400, 0xbab197ea
+0,        896,        896,        1,   518400, 0xbab197ea
+0,        897,        897,        1,   518400, 0xbab197ea
+0,        898,        898,        1,   518400, 0xbab197ea
+0,        899,        899,        1,   518400, 0xbab197ea
+0,        900,        900,        1,   518400, 0xbab197ea
+0,        901,        901,        1,   518400, 0xbab197ea
+0,        902,        902,        1,   518400, 0xbab197ea
+0,        903,        903,        1,   518400, 0xbab197ea
+0,        904,        904,        1,   518400, 0xbab197ea
+0,        905,        905,        1,   518400, 0xbab197ea
+0,        906,        906,        1,   518400, 0xbab197ea
+0,        907,        907,        1,   518400, 0xbab197ea
+0,        908,        908,        1,   518400, 0xbab197ea
+0,        909,        909,        1,   518400, 0xbab197ea
+0,        910,        910,        1,   518400, 0xbab197ea
+0,        911,        911,        1,   518400, 0xbab197ea
+0,        912,        912,        1,   518400, 0xbab197ea
+0,        913,        913,        1,   518400, 0xbab197ea
+0,        914,        914,        1,   518400, 0xbab197ea
+0,        915,        915,        1,   518400, 0xbab197ea
+0,        916,        916,        1,   518400, 0xbab197ea
+0,        917,        917,        1,   518400, 0xbab197ea
+0,        918,        918,        1,   518400, 0xbab197ea
+0,        919,        919,        1,   518400, 0xbab197ea
+0,        920,        920,        1,   518400, 0xbab197ea
+0,        921,        921,        1,   518400, 0xbab197ea
+0,        922,        922,        1,   518400, 0xbab197ea
+0,        923,        923,        1,   518400, 0xbab197ea
+0,        924,        924,        1,   518400, 0xbab197ea
+0,        925,        925,        1,   518400, 0xbab197ea
+0,        926,        926,        1,   518400, 0xbab197ea
+0,        927,        927,        1,   518400, 0xbab197ea
+0,        928,        928,        1,   518400, 0xbab197ea
+0,        929,        929,        1,   518400, 0xbab197ea
+0,        930,        930,        1,   518400, 0xbab197ea
+0,        931,        931,        1,   518400, 0xbab197ea
+0,        932,        932,        1,   518400, 0xbab197ea
+0,        933,        933,        1,   518400, 0xbab197ea
+0,        934,        934,        1,   518400, 0xbab197ea
+0,        935,        935,        1,   518400, 0xbab197ea
+0,        936,        936,        1,   518400, 0xbab197ea
+0,        937,        937,        1,   518400, 0xbab197ea
+0,        938,        938,        1,   518400, 0xbab197ea
+0,        939,        939,        1,   518400, 0xbab197ea
+0,        940,        940,        1,   518400, 0xbab197ea
+0,        941,        941,        1,   518400, 0xbab197ea
+0,        942,        942,        1,   518400, 0xbab197ea
+0,        943,        943,        1,   518400, 0xbab197ea
+0,        944,        944,        1,   518400, 0xbab197ea
+0,        945,        945,        1,   518400, 0xbab197ea
 0,        946,        946,        1,   518400, 0xbaf801ef
 1,  189295000,  189295000,  1968000,     1596, 0x408c726e
-0,        956,        956,        1,   518400, 0xbab197ea
+0,        947,        947,        1,   518400, 0xbaf801ef
+0,        948,        948,        1,   518400, 0xbaf801ef
+0,        949,        949,        1,   518400, 0xbaf801ef
+0,        950,        950,        1,   518400, 0xbaf801ef
+0,        951,        951,        1,   518400, 0xbaf801ef
+0,        952,        952,        1,   518400, 0xbaf801ef
+0,        953,        953,        1,   518400, 0xbaf801ef
+0,        954,        954,        1,   518400, 0xbaf801ef
+0,        955,        955,        1,   518400, 0xbaf801ef
+0,        956,        956,        1,   518400, 0xbaf801ef
 1,  191356000,  191356000,  1228000,     1517, 0xae8c5c2b
 0,        957,        957,        1,   518400, 0x59f4e72f
-0,        963,        963,        1,   518400, 0xbab197ea
+0,        958,        958,        1,   518400, 0x59f4e72f
+0,        959,        959,        1,   518400, 0x59f4e72f
+0,        960,        960,        1,   518400, 0x59f4e72f
+0,        961,        961,        1,   518400, 0x59f4e72f
+0,        962,        962,        1,   518400, 0x59f4e72f
+0,        963,        963,        1,   518400, 0x59f4e72f
 1,  192640000,  192640000,  1763000,     2506, 0xa458d6d4
 0,        964,        964,        1,   518400, 0x9d5b9d69
-0,        972,        972,        1,   518400, 0xbab197ea
+0,        965,        965,        1,   518400, 0x9d5b9d69
+0,        966,        966,        1,   518400, 0x9d5b9d69
+0,        967,        967,        1,   518400, 0x9d5b9d69
+0,        968,        968,        1,   518400, 0x9d5b9d69
+0,        969,        969,        1,   518400, 0x9d5b9d69
+0,        970,        970,        1,   518400, 0x9d5b9d69
+0,        971,        971,        1,   518400, 0x9d5b9d69
+0,        972,        972,        1,   518400, 0x9d5b9d69
+0,        973,        973,        1,   518400, 0xbab197ea
+0,        974,        974,        1,   518400, 0xbab197ea
+0,        975,        975,        1,   518400, 0xbab197ea
 1,  195193000,  195193000,  1092000,     1074, 0x397ba9a8
 0,        976,        976,        1,   518400, 0x923d1ce7
-0,        981,        981,        1,   518400, 0xbab197ea
+0,        977,        977,        1,   518400, 0x923d1ce7
+0,        978,        978,        1,   518400, 0x923d1ce7
+0,        979,        979,        1,   518400, 0x923d1ce7
+0,        980,        980,        1,   518400, 0x923d1ce7
+0,        981,        981,        1,   518400, 0x923d1ce7
 1,  196361000,  196361000,  1524000,     1715, 0x695ca41e
 0,        982,        982,        1,   518400, 0x6e652cd2
-0,        989,        989,        1,   518400, 0xbab197ea
+0,        983,        983,        1,   518400, 0x6e652cd2
+0,        984,        984,        1,   518400, 0x6e652cd2
+0,        985,        985,        1,   518400, 0x6e652cd2
+0,        986,        986,        1,   518400, 0x6e652cd2
+0,        987,        987,        1,   518400, 0x6e652cd2
+0,        988,        988,        1,   518400, 0x6e652cd2
+0,        989,        989,        1,   518400, 0x6e652cd2
 1,  197946000,  197946000,  1160000,      789, 0xc63a189e
 0,        990,        990,        1,   518400, 0x25113966
-0,        996,        996,        1,   518400, 0xbab197ea
+0,        991,        991,        1,   518400, 0x25113966
+0,        992,        992,        1,   518400, 0x25113966
+0,        993,        993,        1,   518400, 0x25113966
+0,        994,        994,        1,   518400, 0x25113966
+0,        995,        995,        1,   518400, 0x25113966
+0,        996,        996,        1,   518400, 0x25113966
 1,  199230000,  199230000,  1627000,     1846, 0xeea8c599
 0,        997,        997,        1,   518400, 0x2dc83609
-0,       1004,       1004,        1,   518400, 0xbab197ea
+0,        998,        998,        1,   518400, 0x2dc83609
+0,        999,        999,        1,   518400, 0x2dc83609
+0,       1000,       1000,        1,   518400, 0x2dc83609
+0,       1001,       1001,        1,   518400, 0x2dc83609
+0,       1002,       1002,        1,   518400, 0x2dc83609
+0,       1003,       1003,        1,   518400, 0x2dc83609
+0,       1004,       1004,        1,   518400, 0x2dc83609
 1,  200924000,  200924000,  1763000,      922, 0xd4a87222
 0,       1005,       1005,        1,   518400, 0x90483bc6
-0,       1013,       1013,        1,   518400, 0xbab197ea
+0,       1006,       1006,        1,   518400, 0x90483bc6
+0,       1007,       1007,        1,   518400, 0x90483bc6
+0,       1008,       1008,        1,   518400, 0x90483bc6
+0,       1009,       1009,        1,   518400, 0x90483bc6
+0,       1010,       1010,        1,   518400, 0x90483bc6
+0,       1011,       1011,        1,   518400, 0x90483bc6
+0,       1012,       1012,        1,   518400, 0x90483bc6
+0,       1013,       1013,        1,   518400, 0x90483bc6
+0,       1014,       1014,        1,   518400, 0xbab197ea
+0,       1015,       1015,        1,   518400, 0xbab197ea
+0,       1016,       1016,        1,   518400, 0xbab197ea
+0,       1017,       1017,        1,   518400, 0xbab197ea
+0,       1018,       1018,        1,   518400, 0xbab197ea
+0,       1019,       1019,        1,   518400, 0xbab197ea
+0,       1020,       1020,        1,   518400, 0xbab197ea
+0,       1021,       1021,        1,   518400, 0xbab197ea
+0,       1022,       1022,        1,   518400, 0xbab197ea
+0,       1023,       1023,        1,   518400, 0xbab197ea
+0,       1024,       1024,        1,   518400, 0xbab197ea
+0,       1025,       1025,        1,   518400, 0xbab197ea
+0,       1026,       1026,        1,   518400, 0xbab197ea
+0,       1027,       1027,        1,   518400, 0xbab197ea
+0,       1028,       1028,        1,   518400, 0xbab197ea
+0,       1029,       1029,        1,   518400, 0xbab197ea
+0,       1030,       1030,        1,   518400, 0xbab197ea
+0,       1031,       1031,        1,   518400, 0xbab197ea
+0,       1032,       1032,        1,   518400, 0xbab197ea
+0,       1033,       1033,        1,   518400, 0xbab197ea
+0,       1034,       1034,        1,   518400, 0xbab197ea
+0,       1035,       1035,        1,   518400, 0xbab197ea
+0,       1036,       1036,        1,   518400, 0xbab197ea
+0,       1037,       1037,        1,   518400, 0xbab197ea
+0,       1038,       1038,        1,   518400, 0xbab197ea
+0,       1039,       1039,        1,   518400, 0xbab197ea
+0,       1040,       1040,        1,   518400, 0xbab197ea
+0,       1041,       1041,        1,   518400, 0xbab197ea
+0,       1042,       1042,        1,   518400, 0xbab197ea
+0,       1043,       1043,        1,   518400, 0xbab197ea
+0,       1044,       1044,        1,   518400, 0xbab197ea
+0,       1045,       1045,        1,   518400, 0xbab197ea
+0,       1046,       1046,        1,   518400, 0xbab197ea
+0,       1047,       1047,        1,   518400, 0xbab197ea
+0,       1048,       1048,        1,   518400, 0xbab197ea
+0,       1049,       1049,        1,   518400, 0xbab197ea
+0,       1050,       1050,        1,   518400, 0xbab197ea
+0,       1051,       1051,        1,   518400, 0xbab197ea
+0,       1052,       1052,        1,   518400, 0xbab197ea
 0,       1053,       1053,        1,   518400, 0x3de86ab7
 1,  210600000,  210600000,  1831000,      665, 0x55580135
-0,       1062,       1062,        1,   518400, 0xbab197ea
+0,       1054,       1054,        1,   518400, 0x3de86ab7
+0,       1055,       1055,        1,   518400, 0x3de86ab7
+0,       1056,       1056,        1,   518400, 0x3de86ab7
+0,       1057,       1057,        1,   518400, 0x3de86ab7
+0,       1058,       1058,        1,   518400, 0x3de86ab7
+0,       1059,       1059,        1,   518400, 0x3de86ab7
+0,       1060,       1060,        1,   518400, 0x3de86ab7
+0,       1061,       1061,        1,   518400, 0x3de86ab7
+0,       1062,       1062,        1,   518400, 0x3de86ab7
+0,       1063,       1063,        1,   518400, 0xbab197ea
+0,       1064,       1064,        1,   518400, 0xbab197ea
+0,       1065,       1065,        1,   518400, 0xbab197ea
+0,       1066,       1066,        1,   518400, 0xbab197ea
+0,       1067,       1067,        1,   518400, 0xbab197ea
+0,       1068,       1068,        1,   518400, 0xbab197ea
+0,       1069,       1069,        1,   518400, 0xbab197ea
+0,       1070,       1070,        1,   518400, 0xbab197ea
+0,       1071,       1071,        1,   518400, 0xbab197ea
+0,       1072,       1072,        1,   518400, 0xbab197ea
+0,       1073,       1073,        1,   518400, 0xbab197ea
 1,  214771000,  214771000,  1558000,     1216, 0x50d1f6c5
 0,       1074,       1074,        1,   518400, 0x8c320e68
-0,       1082,       1082,        1,   518400, 0xbab197ea
+0,       1075,       1075,        1,   518400, 0x8c320e68
+0,       1076,       1076,        1,   518400, 0x8c320e68
+0,       1077,       1077,        1,   518400, 0x8c320e68
+0,       1078,       1078,        1,   518400, 0x8c320e68
+0,       1079,       1079,        1,   518400, 0x8c320e68
+0,       1080,       1080,        1,   518400, 0x8c320e68
+0,       1081,       1081,        1,   518400, 0x8c320e68
+0,       1082,       1082,        1,   518400, 0x8c320e68
+0,       1083,       1083,        1,   518400, 0xbab197ea
+0,       1084,       1084,        1,   518400, 0xbab197ea
+0,       1085,       1085,        1,   518400, 0xbab197ea
+0,       1086,       1086,        1,   518400, 0xbab197ea
+0,       1087,       1087,        1,   518400, 0xbab197ea
+0,       1088,       1088,        1,   518400, 0xbab197ea
+0,       1089,       1089,        1,   518400, 0xbab197ea
+0,       1090,       1090,        1,   518400, 0xbab197ea
+0,       1091,       1091,        1,   518400, 0xbab197ea
+0,       1092,       1092,        1,   518400, 0xbab197ea
+0,       1093,       1093,        1,   518400, 0xbab197ea
+0,       1094,       1094,        1,   518400, 0xbab197ea
+0,       1095,       1095,        1,   518400, 0xbab197ea
+0,       1096,       1096,        1,   518400, 0xbab197ea
+0,       1097,       1097,        1,   518400, 0xbab197ea
+0,       1098,       1098,        1,   518400, 0xbab197ea
+0,       1099,       1099,        1,   518400, 0xbab197ea
+0,       1100,       1100,        1,   518400, 0xbab197ea
+0,       1101,       1101,        1,   518400, 0xbab197ea
+0,       1102,       1102,        1,   518400, 0xbab197ea
+0,       1103,       1103,        1,   518400, 0xbab197ea
+0,       1104,       1104,        1,   518400, 0xbab197ea
+0,       1105,       1105,        1,   518400, 0xbab197ea
+0,       1106,       1106,        1,   518400, 0xbab197ea
+0,       1107,       1107,        1,   518400, 0xbab197ea
+0,       1108,       1108,        1,   518400, 0xbab197ea
+0,       1109,       1109,        1,   518400, 0xbab197ea
+0,       1110,       1110,        1,   518400, 0xbab197ea
+0,       1111,       1111,        1,   518400, 0xbab197ea
+0,       1112,       1112,        1,   518400, 0xbab197ea
+0,       1113,       1113,        1,   518400, 0xbab197ea
+0,       1114,       1114,        1,   518400, 0xbab197ea
+0,       1115,       1115,        1,   518400, 0xbab197ea
+0,       1116,       1116,        1,   518400, 0xbab197ea
+0,       1117,       1117,        1,   518400, 0xbab197ea
+0,       1118,       1118,        1,   518400, 0xbab197ea
+0,       1119,       1119,        1,   518400, 0xbab197ea
+0,       1120,       1120,        1,   518400, 0xbab197ea
+0,       1121,       1121,        1,   518400, 0xbab197ea
+0,       1122,       1122,        1,   518400, 0xbab197ea
+0,       1123,       1123,        1,   518400, 0xbab197ea
+0,       1124,       1124,        1,   518400, 0xbab197ea
+0,       1125,       1125,        1,   518400, 0xbab197ea
+0,       1126,       1126,        1,   518400, 0xbab197ea
+0,       1127,       1127,        1,   518400, 0xbab197ea
 0,       1128,       1128,        1,   518400, 0x81e977b2
 1,  225640000,  225640000,  2127000,     2133, 0x670c11a5
-0,       1139,       1139,        1,   518400, 0xbab197ea
+0,       1129,       1129,        1,   518400, 0x81e977b2
+0,       1130,       1130,        1,   518400, 0x81e977b2
+0,       1131,       1131,        1,   518400, 0x81e977b2
+0,       1132,       1132,        1,   518400, 0x81e977b2
+0,       1133,       1133,        1,   518400, 0x81e977b2
+0,       1134,       1134,        1,   518400, 0x81e977b2
+0,       1135,       1135,        1,   518400, 0x81e977b2
+0,       1136,       1136,        1,   518400, 0x81e977b2
+0,       1137,       1137,        1,   518400, 0x81e977b2
+0,       1138,       1138,        1,   518400, 0x81e977b2
+0,       1139,       1139,        1,   518400, 0x81e977b2
 1,  227834000,  227834000,  1262000,     1264, 0xc1d9fc57
 0,       1140,       1140,        1,   518400, 0xb046dd30
-0,       1145,       1145,        1,   518400, 0xbab197ea
diff --git a/tests/ref/fate/sub2video_basic b/tests/ref/fate/sub2video_basic
index 5f72e292c9..3c9fc71b5c 100644
--- a/tests/ref/fate/sub2video_basic
+++ b/tests/ref/fate/sub2video_basic
@@ -1,95 +1,1150 @@
-#tb 0: 1/25
+#tb 0: 1/5
 #media_type 0: video
 #codec_id 0: rawvideo
 #dimensions 0: 720x480
-#sar 0: 0/1
-0,       3312,       3312,        1,  1382400, 0x00000000
-0,       3312,       3312,        1,  1382400, 0x8c93c2ba
-0,       3436,       3436,        1,  1382400, 0x00000000
-0,       3684,       3684,        1,  1382400, 0xb02e32ca
-0,       3802,       3802,        1,  1382400, 0x00000000
-0,       4520,       4520,        1,  1382400, 0x83b71116
-0,       4584,       4584,        1,  1382400, 0x00000000
-0,       4586,       4586,        1,  1382400, 0x85547fd1
-0,       4645,       4645,        1,  1382400, 0x00000000
-0,       4648,       4648,        1,  1382400, 0x00000000
-0,       4648,       4648,        1,  1382400, 0xb6a8f181
-0,       4715,       4715,        1,  1382400, 0x00000000
-0,       4717,       4717,        1,  1382400, 0xb64d1a2c
-0,       4748,       4748,        1,  1382400, 0x00000000
-0,       4750,       4750,        1,  1382400, 0x7b37ecf3
-0,       4792,       4792,        1,  1382400, 0x00000000
-0,       4993,       4993,        1,  1382400, 0xdc025bd1
-0,       5027,       5027,        1,  1382400, 0x00000000
-0,       5029,       5029,        1,  1382400, 0x688b294d
-0,       5068,       5068,        1,  1382400, 0x00000000
-0,       5070,       5070,        1,  1382400, 0xa2b33d1b
-0,       5117,       5117,        1,  1382400, 0x00000000
-0,       5119,       5119,        1,  1382400, 0xb3e525e3
-0,       5168,       5168,        1,  1382400, 0x00000000
-0,       5170,       5170,        1,  1382400, 0xaa8fbdd7
-0,       5216,       5216,        1,  1382400, 0x00000000
-0,       5218,       5218,        1,  1382400, 0x7b7f26dd
-0,       5249,       5249,        1,  1382400, 0x00000000
-0,       5251,       5251,        1,  1382400, 0x15e2f836
-0,       5289,       5289,        1,  1382400, 0x00000000
-0,       5291,       5291,        1,  1382400, 0x0fee9b0c
-0,       5358,       5358,        1,  1382400, 0x00000000
-0,       5360,       5360,        1,  1382400, 0x89d62791
-0,       5429,       5429,        1,  1382400, 0x00000000
-0,       5431,       5431,        1,  1382400, 0xa6a9fd74
-0,       5490,       5490,        1,  1382400, 0x00000000
-0,       5491,       5491,        1,  1382400, 0x7896178d
-0,       5537,       5537,        1,  1382400, 0x00000000
-0,       5588,       5588,        1,  1382400, 0x01751a52
-0,       5647,       5647,        1,  1382400, 0x00000000
-0,       5688,       5688,        1,  1382400, 0xa3959c6f
-0,       5770,       5770,        1,  1382400, 0x00000000
-0,       5772,       5772,        1,  1382400, 0x3d3ea47b
-0,       5826,       5826,        1,  1382400, 0x00000000
-0,       5828,       5828,        1,  1382400, 0x593f8b24
-0,       5931,       5931,        1,  1382400, 0x00000000
-0,       5933,       5933,        1,  1382400, 0x171f05ba
-0,       6001,       6001,        1,  1382400, 0x00000000
-0,       6003,       6003,        1,  1382400, 0xb014cdf1
-0,       6054,       6054,        1,  1382400, 0x00000000
-0,       6839,       6839,        1,  1382400, 0xd918e667
-0,       6880,       6880,        1,  1382400, 0x00000000
-0,       7386,       7386,        1,  1382400, 0xc9406331
-0,       7419,       7419,        1,  1382400, 0x00000000
-0,       7501,       7501,        1,  1382400, 0xaf08b10d
-0,       7549,       7549,        1,  1382400, 0x00000000
-0,       7551,       7551,        1,  1382400, 0x00000000
-0,       7551,       7551,        1,  1382400, 0x853a9d93
-0,       7589,       7589,        1,  1382400, 0x00000000
-0,       7605,       7605,        1,  1382400, 0x7491a87d
-0,       7647,       7647,        1,  1382400, 0x00000000
-0,       7649,       7649,        1,  1382400, 0xf7383c58
-0,       7697,       7697,        1,  1382400, 0x00000000
-0,       7699,       7699,        1,  1382400, 0xe66be411
-0,       7743,       7743,        1,  1382400, 0x00000000
-0,       8032,       8032,        1,  1382400, 0xd6850362
-0,       8082,       8082,        1,  1382400, 0x00000000
-0,       8084,       8084,        1,  1382400, 0x3e1ed109
-0,       8115,       8115,        1,  1382400, 0x00000000
-0,       8116,       8116,        1,  1382400, 0x39c1b7bd
-0,       8160,       8160,        1,  1382400, 0x00000000
-0,       8180,       8180,        1,  1382400, 0x35b85f2e
-0,       8207,       8207,        1,  1382400, 0x00000000
-0,       8209,       8209,        1,  1382400, 0x00000000
-0,       8209,       8209,        1,  1382400, 0x83f103e5
-0,       8247,       8247,        1,  1382400, 0x00000000
-0,       8249,       8249,        1,  1382400, 0xbc1ca9b3
-0,       8278,       8278,        1,  1382400, 0x00000000
-0,       8281,       8281,        1,  1382400, 0x94d4a51e
-0,       8321,       8321,        1,  1382400, 0x00000000
-0,       8323,       8323,        1,  1382400, 0xf88cdfde
-0,       8367,       8367,        1,  1382400, 0x00000000
-0,       8565,       8565,        1,  1382400, 0xdd51423b
-0,       8611,       8611,        1,  1382400, 0x00000000
-0,       8669,       8669,        1,  1382400, 0x08259fa4
-0,       8708,       8708,        1,  1382400, 0x00000000
-0,       8941,       8941,        1,  1382400, 0x1663fa34
-0,       8994,       8994,        1,  1382400, 0x00000000
-0,       8996,       8996,        1,  1382400, 0xda2ceb55
-0,       9027,       9027,        1,  1382400, 0x00000000
+#sar 0: 1/1
+0,          0,          0,        1,  1382400, 0x00000000
+0,        662,        662,        1,  1382400, 0xc637b893
+0,        663,        663,        1,  1382400, 0xc637b893
+0,        664,        664,        1,  1382400, 0xc637b893
+0,        665,        665,        1,  1382400, 0xc637b893
+0,        666,        666,        1,  1382400, 0xc637b893
+0,        667,        667,        1,  1382400, 0xc637b893
+0,        668,        668,        1,  1382400, 0xc637b893
+0,        669,        669,        1,  1382400, 0xc637b893
+0,        670,        670,        1,  1382400, 0xc637b893
+0,        671,        671,        1,  1382400, 0xc637b893
+0,        672,        672,        1,  1382400, 0xc637b893
+0,        673,        673,        1,  1382400, 0xc637b893
+0,        674,        674,        1,  1382400, 0xc637b893
+0,        675,        675,        1,  1382400, 0xc637b893
+0,        676,        676,        1,  1382400, 0xc637b893
+0,        677,        677,        1,  1382400, 0xc637b893
+0,        678,        678,        1,  1382400, 0xc637b893
+0,        679,        679,        1,  1382400, 0xc637b893
+0,        680,        680,        1,  1382400, 0xc637b893
+0,        681,        681,        1,  1382400, 0xc637b893
+0,        682,        682,        1,  1382400, 0xc637b893
+0,        683,        683,        1,  1382400, 0xc637b893
+0,        684,        684,        1,  1382400, 0xc637b893
+0,        685,        685,        1,  1382400, 0xc637b893
+0,        686,        686,        1,  1382400, 0xc637b893
+0,        687,        687,        1,  1382400, 0x00000000
+0,        688,        688,        1,  1382400, 0x00000000
+0,        689,        689,        1,  1382400, 0x00000000
+0,        690,        690,        1,  1382400, 0x00000000
+0,        691,        691,        1,  1382400, 0x00000000
+0,        692,        692,        1,  1382400, 0x00000000
+0,        693,        693,        1,  1382400, 0x00000000
+0,        694,        694,        1,  1382400, 0x00000000
+0,        695,        695,        1,  1382400, 0x00000000
+0,        696,        696,        1,  1382400, 0x00000000
+0,        697,        697,        1,  1382400, 0x00000000
+0,        698,        698,        1,  1382400, 0x00000000
+0,        699,        699,        1,  1382400, 0x00000000
+0,        700,        700,        1,  1382400, 0x00000000
+0,        701,        701,        1,  1382400, 0x00000000
+0,        702,        702,        1,  1382400, 0x00000000
+0,        703,        703,        1,  1382400, 0x00000000
+0,        704,        704,        1,  1382400, 0x00000000
+0,        705,        705,        1,  1382400, 0x00000000
+0,        706,        706,        1,  1382400, 0x00000000
+0,        707,        707,        1,  1382400, 0x00000000
+0,        708,        708,        1,  1382400, 0x00000000
+0,        709,        709,        1,  1382400, 0x00000000
+0,        710,        710,        1,  1382400, 0x00000000
+0,        711,        711,        1,  1382400, 0x00000000
+0,        712,        712,        1,  1382400, 0x00000000
+0,        713,        713,        1,  1382400, 0x00000000
+0,        714,        714,        1,  1382400, 0x00000000
+0,        715,        715,        1,  1382400, 0x00000000
+0,        716,        716,        1,  1382400, 0x00000000
+0,        717,        717,        1,  1382400, 0x00000000
+0,        718,        718,        1,  1382400, 0x00000000
+0,        719,        719,        1,  1382400, 0x00000000
+0,        720,        720,        1,  1382400, 0x00000000
+0,        721,        721,        1,  1382400, 0x00000000
+0,        722,        722,        1,  1382400, 0x00000000
+0,        723,        723,        1,  1382400, 0x00000000
+0,        724,        724,        1,  1382400, 0x00000000
+0,        725,        725,        1,  1382400, 0x00000000
+0,        726,        726,        1,  1382400, 0x00000000
+0,        727,        727,        1,  1382400, 0x00000000
+0,        728,        728,        1,  1382400, 0x00000000
+0,        729,        729,        1,  1382400, 0x00000000
+0,        730,        730,        1,  1382400, 0x00000000
+0,        731,        731,        1,  1382400, 0x00000000
+0,        732,        732,        1,  1382400, 0x00000000
+0,        733,        733,        1,  1382400, 0x00000000
+0,        734,        734,        1,  1382400, 0x00000000
+0,        735,        735,        1,  1382400, 0x00000000
+0,        737,        737,        1,  1382400, 0x4c2960ca
+0,        737,        737,        1,  1382400, 0x4c2960ca
+0,        738,        738,        1,  1382400, 0x4c2960ca
+0,        739,        739,        1,  1382400, 0x4c2960ca
+0,        740,        740,        1,  1382400, 0x4c2960ca
+0,        741,        741,        1,  1382400, 0x4c2960ca
+0,        742,        742,        1,  1382400, 0x4c2960ca
+0,        743,        743,        1,  1382400, 0x4c2960ca
+0,        744,        744,        1,  1382400, 0x4c2960ca
+0,        745,        745,        1,  1382400, 0x4c2960ca
+0,        746,        746,        1,  1382400, 0x4c2960ca
+0,        747,        747,        1,  1382400, 0x4c2960ca
+0,        748,        748,        1,  1382400, 0x4c2960ca
+0,        749,        749,        1,  1382400, 0x4c2960ca
+0,        750,        750,        1,  1382400, 0x4c2960ca
+0,        751,        751,        1,  1382400, 0x4c2960ca
+0,        752,        752,        1,  1382400, 0x4c2960ca
+0,        753,        753,        1,  1382400, 0x4c2960ca
+0,        754,        754,        1,  1382400, 0x4c2960ca
+0,        755,        755,        1,  1382400, 0x4c2960ca
+0,        756,        756,        1,  1382400, 0x4c2960ca
+0,        757,        757,        1,  1382400, 0x4c2960ca
+0,        758,        758,        1,  1382400, 0x4c2960ca
+0,        759,        759,        1,  1382400, 0x4c2960ca
+0,        760,        760,        1,  1382400, 0x00000000
+0,        761,        761,        1,  1382400, 0x00000000
+0,        762,        762,        1,  1382400, 0x00000000
+0,        763,        763,        1,  1382400, 0x00000000
+0,        764,        764,        1,  1382400, 0x00000000
+0,        765,        765,        1,  1382400, 0x00000000
+0,        766,        766,        1,  1382400, 0x00000000
+0,        767,        767,        1,  1382400, 0x00000000
+0,        768,        768,        1,  1382400, 0x00000000
+0,        769,        769,        1,  1382400, 0x00000000
+0,        770,        770,        1,  1382400, 0x00000000
+0,        771,        771,        1,  1382400, 0x00000000
+0,        772,        772,        1,  1382400, 0x00000000
+0,        773,        773,        1,  1382400, 0x00000000
+0,        774,        774,        1,  1382400, 0x00000000
+0,        775,        775,        1,  1382400, 0x00000000
+0,        776,        776,        1,  1382400, 0x00000000
+0,        777,        777,        1,  1382400, 0x00000000
+0,        778,        778,        1,  1382400, 0x00000000
+0,        779,        779,        1,  1382400, 0x00000000
+0,        780,        780,        1,  1382400, 0x00000000
+0,        781,        781,        1,  1382400, 0x00000000
+0,        782,        782,        1,  1382400, 0x00000000
+0,        783,        783,        1,  1382400, 0x00000000
+0,        784,        784,        1,  1382400, 0x00000000
+0,        785,        785,        1,  1382400, 0x00000000
+0,        786,        786,        1,  1382400, 0x00000000
+0,        787,        787,        1,  1382400, 0x00000000
+0,        788,        788,        1,  1382400, 0x00000000
+0,        789,        789,        1,  1382400, 0x00000000
+0,        790,        790,        1,  1382400, 0x00000000
+0,        791,        791,        1,  1382400, 0x00000000
+0,        792,        792,        1,  1382400, 0x00000000
+0,        793,        793,        1,  1382400, 0x00000000
+0,        794,        794,        1,  1382400, 0x00000000
+0,        795,        795,        1,  1382400, 0x00000000
+0,        796,        796,        1,  1382400, 0x00000000
+0,        797,        797,        1,  1382400, 0x00000000
+0,        798,        798,        1,  1382400, 0x00000000
+0,        799,        799,        1,  1382400, 0x00000000
+0,        800,        800,        1,  1382400, 0x00000000
+0,        801,        801,        1,  1382400, 0x00000000
+0,        802,        802,        1,  1382400, 0x00000000
+0,        803,        803,        1,  1382400, 0x00000000
+0,        804,        804,        1,  1382400, 0x00000000
+0,        805,        805,        1,  1382400, 0x00000000
+0,        806,        806,        1,  1382400, 0x00000000
+0,        807,        807,        1,  1382400, 0x00000000
+0,        808,        808,        1,  1382400, 0x00000000
+0,        809,        809,        1,  1382400, 0x00000000
+0,        810,        810,        1,  1382400, 0x00000000
+0,        811,        811,        1,  1382400, 0x00000000
+0,        812,        812,        1,  1382400, 0x00000000
+0,        813,        813,        1,  1382400, 0x00000000
+0,        814,        814,        1,  1382400, 0x00000000
+0,        815,        815,        1,  1382400, 0x00000000
+0,        816,        816,        1,  1382400, 0x00000000
+0,        817,        817,        1,  1382400, 0x00000000
+0,        818,        818,        1,  1382400, 0x00000000
+0,        819,        819,        1,  1382400, 0x00000000
+0,        820,        820,        1,  1382400, 0x00000000
+0,        821,        821,        1,  1382400, 0x00000000
+0,        822,        822,        1,  1382400, 0x00000000
+0,        823,        823,        1,  1382400, 0x00000000
+0,        824,        824,        1,  1382400, 0x00000000
+0,        825,        825,        1,  1382400, 0x00000000
+0,        826,        826,        1,  1382400, 0x00000000
+0,        827,        827,        1,  1382400, 0x00000000
+0,        828,        828,        1,  1382400, 0x00000000
+0,        829,        829,        1,  1382400, 0x00000000
+0,        830,        830,        1,  1382400, 0x00000000
+0,        831,        831,        1,  1382400, 0x00000000
+0,        832,        832,        1,  1382400, 0x00000000
+0,        833,        833,        1,  1382400, 0x00000000
+0,        834,        834,        1,  1382400, 0x00000000
+0,        835,        835,        1,  1382400, 0x00000000
+0,        836,        836,        1,  1382400, 0x00000000
+0,        837,        837,        1,  1382400, 0x00000000
+0,        838,        838,        1,  1382400, 0x00000000
+0,        839,        839,        1,  1382400, 0x00000000
+0,        840,        840,        1,  1382400, 0x00000000
+0,        841,        841,        1,  1382400, 0x00000000
+0,        842,        842,        1,  1382400, 0x00000000
+0,        843,        843,        1,  1382400, 0x00000000
+0,        844,        844,        1,  1382400, 0x00000000
+0,        845,        845,        1,  1382400, 0x00000000
+0,        846,        846,        1,  1382400, 0x00000000
+0,        847,        847,        1,  1382400, 0x00000000
+0,        848,        848,        1,  1382400, 0x00000000
+0,        849,        849,        1,  1382400, 0x00000000
+0,        850,        850,        1,  1382400, 0x00000000
+0,        851,        851,        1,  1382400, 0x00000000
+0,        852,        852,        1,  1382400, 0x00000000
+0,        853,        853,        1,  1382400, 0x00000000
+0,        854,        854,        1,  1382400, 0x00000000
+0,        855,        855,        1,  1382400, 0x00000000
+0,        856,        856,        1,  1382400, 0x00000000
+0,        857,        857,        1,  1382400, 0x00000000
+0,        858,        858,        1,  1382400, 0x00000000
+0,        859,        859,        1,  1382400, 0x00000000
+0,        860,        860,        1,  1382400, 0x00000000
+0,        861,        861,        1,  1382400, 0x00000000
+0,        862,        862,        1,  1382400, 0x00000000
+0,        863,        863,        1,  1382400, 0x00000000
+0,        864,        864,        1,  1382400, 0x00000000
+0,        865,        865,        1,  1382400, 0x00000000
+0,        866,        866,        1,  1382400, 0x00000000
+0,        867,        867,        1,  1382400, 0x00000000
+0,        868,        868,        1,  1382400, 0x00000000
+0,        869,        869,        1,  1382400, 0x00000000
+0,        870,        870,        1,  1382400, 0x00000000
+0,        871,        871,        1,  1382400, 0x00000000
+0,        872,        872,        1,  1382400, 0x00000000
+0,        873,        873,        1,  1382400, 0x00000000
+0,        874,        874,        1,  1382400, 0x00000000
+0,        875,        875,        1,  1382400, 0x00000000
+0,        876,        876,        1,  1382400, 0x00000000
+0,        877,        877,        1,  1382400, 0x00000000
+0,        878,        878,        1,  1382400, 0x00000000
+0,        879,        879,        1,  1382400, 0x00000000
+0,        880,        880,        1,  1382400, 0x00000000
+0,        881,        881,        1,  1382400, 0x00000000
+0,        882,        882,        1,  1382400, 0x00000000
+0,        883,        883,        1,  1382400, 0x00000000
+0,        884,        884,        1,  1382400, 0x00000000
+0,        885,        885,        1,  1382400, 0x00000000
+0,        886,        886,        1,  1382400, 0x00000000
+0,        887,        887,        1,  1382400, 0x00000000
+0,        888,        888,        1,  1382400, 0x00000000
+0,        889,        889,        1,  1382400, 0x00000000
+0,        890,        890,        1,  1382400, 0x00000000
+0,        891,        891,        1,  1382400, 0x00000000
+0,        892,        892,        1,  1382400, 0x00000000
+0,        893,        893,        1,  1382400, 0x00000000
+0,        894,        894,        1,  1382400, 0x00000000
+0,        895,        895,        1,  1382400, 0x00000000
+0,        896,        896,        1,  1382400, 0x00000000
+0,        897,        897,        1,  1382400, 0x00000000
+0,        898,        898,        1,  1382400, 0x00000000
+0,        899,        899,        1,  1382400, 0x00000000
+0,        900,        900,        1,  1382400, 0x00000000
+0,        901,        901,        1,  1382400, 0x00000000
+0,        902,        902,        1,  1382400, 0x00000000
+0,        904,        904,        1,  1382400, 0x5fa18966
+0,        904,        904,        1,  1382400, 0x5fa18966
+0,        905,        905,        1,  1382400, 0x5fa18966
+0,        906,        906,        1,  1382400, 0x5fa18966
+0,        907,        907,        1,  1382400, 0x5fa18966
+0,        908,        908,        1,  1382400, 0x5fa18966
+0,        909,        909,        1,  1382400, 0x5fa18966
+0,        910,        910,        1,  1382400, 0x5fa18966
+0,        911,        911,        1,  1382400, 0x5fa18966
+0,        912,        912,        1,  1382400, 0x5fa18966
+0,        913,        913,        1,  1382400, 0x5fa18966
+0,        914,        914,        1,  1382400, 0x5fa18966
+0,        915,        915,        1,  1382400, 0x5fa18966
+0,        916,        916,        1,  1382400, 0x5fa18966
+0,        917,        917,        1,  1382400, 0x55f4b7b1
+0,        918,        918,        1,  1382400, 0x55f4b7b1
+0,        919,        919,        1,  1382400, 0x55f4b7b1
+0,        920,        920,        1,  1382400, 0x55f4b7b1
+0,        921,        921,        1,  1382400, 0x55f4b7b1
+0,        922,        922,        1,  1382400, 0x55f4b7b1
+0,        923,        923,        1,  1382400, 0x55f4b7b1
+0,        924,        924,        1,  1382400, 0x55f4b7b1
+0,        925,        925,        1,  1382400, 0x55f4b7b1
+0,        926,        926,        1,  1382400, 0x55f4b7b1
+0,        927,        927,        1,  1382400, 0x55f4b7b1
+0,        928,        928,        1,  1382400, 0x55f4b7b1
+0,        929,        929,        1,  1382400, 0x00000000
+0,        930,        930,        1,  1382400, 0xdfa4cf32
+0,        931,        931,        1,  1382400, 0xdfa4cf32
+0,        932,        932,        1,  1382400, 0xdfa4cf32
+0,        933,        933,        1,  1382400, 0xdfa4cf32
+0,        934,        934,        1,  1382400, 0xdfa4cf32
+0,        935,        935,        1,  1382400, 0xdfa4cf32
+0,        936,        936,        1,  1382400, 0xdfa4cf32
+0,        937,        937,        1,  1382400, 0xdfa4cf32
+0,        938,        938,        1,  1382400, 0xdfa4cf32
+0,        939,        939,        1,  1382400, 0xdfa4cf32
+0,        940,        940,        1,  1382400, 0xdfa4cf32
+0,        941,        941,        1,  1382400, 0xdfa4cf32
+0,        942,        942,        1,  1382400, 0xdfa4cf32
+0,        943,        943,        1,  1382400, 0x35023df8
+0,        944,        944,        1,  1382400, 0x35023df8
+0,        945,        945,        1,  1382400, 0x35023df8
+0,        946,        946,        1,  1382400, 0x35023df8
+0,        947,        947,        1,  1382400, 0x35023df8
+0,        948,        948,        1,  1382400, 0x35023df8
+0,        949,        949,        1,  1382400, 0x35023df8
+0,        950,        950,        1,  1382400, 0xed933219
+0,        951,        951,        1,  1382400, 0xed933219
+0,        952,        952,        1,  1382400, 0xed933219
+0,        953,        953,        1,  1382400, 0xed933219
+0,        954,        954,        1,  1382400, 0xed933219
+0,        955,        955,        1,  1382400, 0xed933219
+0,        956,        956,        1,  1382400, 0xed933219
+0,        957,        957,        1,  1382400, 0xed933219
+0,        958,        958,        1,  1382400, 0x00000000
+0,        959,        959,        1,  1382400, 0x00000000
+0,        960,        960,        1,  1382400, 0x00000000
+0,        961,        961,        1,  1382400, 0x00000000
+0,        962,        962,        1,  1382400, 0x00000000
+0,        963,        963,        1,  1382400, 0x00000000
+0,        964,        964,        1,  1382400, 0x00000000
+0,        965,        965,        1,  1382400, 0x00000000
+0,        966,        966,        1,  1382400, 0x00000000
+0,        967,        967,        1,  1382400, 0x00000000
+0,        968,        968,        1,  1382400, 0x00000000
+0,        969,        969,        1,  1382400, 0x00000000
+0,        970,        970,        1,  1382400, 0x00000000
+0,        971,        971,        1,  1382400, 0x00000000
+0,        972,        972,        1,  1382400, 0x00000000
+0,        973,        973,        1,  1382400, 0x00000000
+0,        974,        974,        1,  1382400, 0x00000000
+0,        975,        975,        1,  1382400, 0x00000000
+0,        976,        976,        1,  1382400, 0x00000000
+0,        977,        977,        1,  1382400, 0x00000000
+0,        978,        978,        1,  1382400, 0x00000000
+0,        979,        979,        1,  1382400, 0x00000000
+0,        980,        980,        1,  1382400, 0x00000000
+0,        981,        981,        1,  1382400, 0x00000000
+0,        982,        982,        1,  1382400, 0x00000000
+0,        983,        983,        1,  1382400, 0x00000000
+0,        984,        984,        1,  1382400, 0x00000000
+0,        985,        985,        1,  1382400, 0x00000000
+0,        986,        986,        1,  1382400, 0x00000000
+0,        987,        987,        1,  1382400, 0x00000000
+0,        988,        988,        1,  1382400, 0x00000000
+0,        989,        989,        1,  1382400, 0x00000000
+0,        990,        990,        1,  1382400, 0x00000000
+0,        991,        991,        1,  1382400, 0x00000000
+0,        992,        992,        1,  1382400, 0x00000000
+0,        993,        993,        1,  1382400, 0x00000000
+0,        994,        994,        1,  1382400, 0x00000000
+0,        995,        995,        1,  1382400, 0x00000000
+0,        996,        996,        1,  1382400, 0x00000000
+0,        997,        997,        1,  1382400, 0x00000000
+0,        999,        999,        1,  1382400, 0x1b26389a
+0,        999,        999,        1,  1382400, 0x1b26389a
+0,       1000,       1000,        1,  1382400, 0x1b26389a
+0,       1001,       1001,        1,  1382400, 0x1b26389a
+0,       1002,       1002,        1,  1382400, 0x1b26389a
+0,       1003,       1003,        1,  1382400, 0x1b26389a
+0,       1004,       1004,        1,  1382400, 0x1b26389a
+0,       1006,       1006,        1,  1382400, 0xf0c7028b
+0,       1006,       1006,        1,  1382400, 0xf0c7028b
+0,       1007,       1007,        1,  1382400, 0xf0c7028b
+0,       1008,       1008,        1,  1382400, 0xf0c7028b
+0,       1009,       1009,        1,  1382400, 0xf0c7028b
+0,       1010,       1010,        1,  1382400, 0xf0c7028b
+0,       1011,       1011,        1,  1382400, 0xf0c7028b
+0,       1012,       1012,        1,  1382400, 0xf0c7028b
+0,       1013,       1013,        1,  1382400, 0xf0c7028b
+0,       1014,       1014,        1,  1382400, 0x395f521d
+0,       1015,       1015,        1,  1382400, 0x395f521d
+0,       1016,       1016,        1,  1382400, 0x395f521d
+0,       1017,       1017,        1,  1382400, 0x395f521d
+0,       1018,       1018,        1,  1382400, 0x395f521d
+0,       1019,       1019,        1,  1382400, 0x395f521d
+0,       1020,       1020,        1,  1382400, 0x395f521d
+0,       1021,       1021,        1,  1382400, 0x395f521d
+0,       1022,       1022,        1,  1382400, 0x395f521d
+0,       1024,       1024,        1,  1382400, 0x1ea87415
+0,       1024,       1024,        1,  1382400, 0x1ea87415
+0,       1025,       1025,        1,  1382400, 0x1ea87415
+0,       1026,       1026,        1,  1382400, 0x1ea87415
+0,       1027,       1027,        1,  1382400, 0x1ea87415
+0,       1028,       1028,        1,  1382400, 0x1ea87415
+0,       1029,       1029,        1,  1382400, 0x1ea87415
+0,       1030,       1030,        1,  1382400, 0x1ea87415
+0,       1031,       1031,        1,  1382400, 0x1ea87415
+0,       1032,       1032,        1,  1382400, 0x1ea87415
+0,       1033,       1033,        1,  1382400, 0x1ea87415
+0,       1034,       1034,        1,  1382400, 0xc6effdc1
+0,       1035,       1035,        1,  1382400, 0xc6effdc1
+0,       1036,       1036,        1,  1382400, 0xc6effdc1
+0,       1037,       1037,        1,  1382400, 0xc6effdc1
+0,       1038,       1038,        1,  1382400, 0xc6effdc1
+0,       1039,       1039,        1,  1382400, 0xc6effdc1
+0,       1040,       1040,        1,  1382400, 0xc6effdc1
+0,       1041,       1041,        1,  1382400, 0xc6effdc1
+0,       1042,       1042,        1,  1382400, 0xc6effdc1
+0,       1044,       1044,        1,  1382400, 0xba6846f8
+0,       1044,       1044,        1,  1382400, 0xba6846f8
+0,       1045,       1045,        1,  1382400, 0xba6846f8
+0,       1046,       1046,        1,  1382400, 0xba6846f8
+0,       1047,       1047,        1,  1382400, 0xba6846f8
+0,       1048,       1048,        1,  1382400, 0xba6846f8
+0,       1049,       1049,        1,  1382400, 0xba6846f8
+0,       1050,       1050,        1,  1382400, 0x033c5d5b
+0,       1051,       1051,        1,  1382400, 0x033c5d5b
+0,       1052,       1052,        1,  1382400, 0x033c5d5b
+0,       1053,       1053,        1,  1382400, 0x033c5d5b
+0,       1054,       1054,        1,  1382400, 0x033c5d5b
+0,       1055,       1055,        1,  1382400, 0x033c5d5b
+0,       1056,       1056,        1,  1382400, 0x033c5d5b
+0,       1057,       1057,        1,  1382400, 0x033c5d5b
+0,       1058,       1058,        1,  1382400, 0x00000000
+0,       1059,       1059,        1,  1382400, 0xef5abf66
+0,       1060,       1060,        1,  1382400, 0xef5abf66
+0,       1061,       1061,        1,  1382400, 0xef5abf66
+0,       1062,       1062,        1,  1382400, 0xef5abf66
+0,       1063,       1063,        1,  1382400, 0xef5abf66
+0,       1064,       1064,        1,  1382400, 0xef5abf66
+0,       1065,       1065,        1,  1382400, 0xef5abf66
+0,       1066,       1066,        1,  1382400, 0xef5abf66
+0,       1067,       1067,        1,  1382400, 0xef5abf66
+0,       1068,       1068,        1,  1382400, 0xef5abf66
+0,       1069,       1069,        1,  1382400, 0xef5abf66
+0,       1070,       1070,        1,  1382400, 0xef5abf66
+0,       1071,       1071,        1,  1382400, 0xef5abf66
+0,       1072,       1072,        1,  1382400, 0x00000000
+0,       1073,       1073,        1,  1382400, 0xec747954
+0,       1074,       1074,        1,  1382400, 0xec747954
+0,       1075,       1075,        1,  1382400, 0xec747954
+0,       1076,       1076,        1,  1382400, 0xec747954
+0,       1077,       1077,        1,  1382400, 0xec747954
+0,       1078,       1078,        1,  1382400, 0xec747954
+0,       1079,       1079,        1,  1382400, 0xec747954
+0,       1080,       1080,        1,  1382400, 0xec747954
+0,       1081,       1081,        1,  1382400, 0xec747954
+0,       1082,       1082,        1,  1382400, 0xec747954
+0,       1083,       1083,        1,  1382400, 0xec747954
+0,       1084,       1084,        1,  1382400, 0xec747954
+0,       1085,       1085,        1,  1382400, 0xec747954
+0,       1086,       1086,        1,  1382400, 0x00000000
+0,       1087,       1087,        1,  1382400, 0xfa34bcaf
+0,       1088,       1088,        1,  1382400, 0xfa34bcaf
+0,       1089,       1089,        1,  1382400, 0xfa34bcaf
+0,       1090,       1090,        1,  1382400, 0xfa34bcaf
+0,       1091,       1091,        1,  1382400, 0xfa34bcaf
+0,       1092,       1092,        1,  1382400, 0xfa34bcaf
+0,       1093,       1093,        1,  1382400, 0xfa34bcaf
+0,       1094,       1094,        1,  1382400, 0xfa34bcaf
+0,       1095,       1095,        1,  1382400, 0xfa34bcaf
+0,       1096,       1096,        1,  1382400, 0xfa34bcaf
+0,       1097,       1097,        1,  1382400, 0xfa34bcaf
+0,       1098,       1098,        1,  1382400, 0x00000000
+0,       1099,       1099,        1,  1382400, 0x8b7a709b
+0,       1100,       1100,        1,  1382400, 0x8b7a709b
+0,       1101,       1101,        1,  1382400, 0x8b7a709b
+0,       1102,       1102,        1,  1382400, 0x8b7a709b
+0,       1103,       1103,        1,  1382400, 0x8b7a709b
+0,       1104,       1104,        1,  1382400, 0x8b7a709b
+0,       1105,       1105,        1,  1382400, 0x8b7a709b
+0,       1106,       1106,        1,  1382400, 0x8b7a709b
+0,       1107,       1107,        1,  1382400, 0x00000000
+0,       1108,       1108,        1,  1382400, 0x00000000
+0,       1109,       1109,        1,  1382400, 0x00000000
+0,       1110,       1110,        1,  1382400, 0x00000000
+0,       1111,       1111,        1,  1382400, 0x00000000
+0,       1112,       1112,        1,  1382400, 0x00000000
+0,       1113,       1113,        1,  1382400, 0x00000000
+0,       1114,       1114,        1,  1382400, 0x00000000
+0,       1115,       1115,        1,  1382400, 0x00000000
+0,       1116,       1116,        1,  1382400, 0x00000000
+0,       1118,       1118,        1,  1382400, 0xc333382f
+0,       1118,       1118,        1,  1382400, 0xc333382f
+0,       1119,       1119,        1,  1382400, 0xc333382f
+0,       1120,       1120,        1,  1382400, 0xc333382f
+0,       1121,       1121,        1,  1382400, 0xc333382f
+0,       1122,       1122,        1,  1382400, 0xc333382f
+0,       1123,       1123,        1,  1382400, 0xc333382f
+0,       1124,       1124,        1,  1382400, 0xc333382f
+0,       1125,       1125,        1,  1382400, 0xc333382f
+0,       1126,       1126,        1,  1382400, 0xc333382f
+0,       1127,       1127,        1,  1382400, 0xc333382f
+0,       1128,       1128,        1,  1382400, 0xc333382f
+0,       1129,       1129,        1,  1382400, 0x00000000
+0,       1130,       1130,        1,  1382400, 0x00000000
+0,       1131,       1131,        1,  1382400, 0x00000000
+0,       1132,       1132,        1,  1382400, 0x00000000
+0,       1133,       1133,        1,  1382400, 0x00000000
+0,       1134,       1134,        1,  1382400, 0x00000000
+0,       1135,       1135,        1,  1382400, 0x00000000
+0,       1136,       1136,        1,  1382400, 0x00000000
+0,       1138,       1138,        1,  1382400, 0xabe5dfcf
+0,       1138,       1138,        1,  1382400, 0xabe5dfcf
+0,       1139,       1139,        1,  1382400, 0xabe5dfcf
+0,       1140,       1140,        1,  1382400, 0xabe5dfcf
+0,       1141,       1141,        1,  1382400, 0xabe5dfcf
+0,       1142,       1142,        1,  1382400, 0xabe5dfcf
+0,       1143,       1143,        1,  1382400, 0xabe5dfcf
+0,       1144,       1144,        1,  1382400, 0xabe5dfcf
+0,       1145,       1145,        1,  1382400, 0xabe5dfcf
+0,       1146,       1146,        1,  1382400, 0xabe5dfcf
+0,       1147,       1147,        1,  1382400, 0xabe5dfcf
+0,       1148,       1148,        1,  1382400, 0xabe5dfcf
+0,       1149,       1149,        1,  1382400, 0xabe5dfcf
+0,       1150,       1150,        1,  1382400, 0xabe5dfcf
+0,       1151,       1151,        1,  1382400, 0xabe5dfcf
+0,       1152,       1152,        1,  1382400, 0xabe5dfcf
+0,       1153,       1153,        1,  1382400, 0xabe5dfcf
+0,       1154,       1154,        1,  1382400, 0x56948101
+0,       1155,       1155,        1,  1382400, 0x56948101
+0,       1156,       1156,        1,  1382400, 0x56948101
+0,       1157,       1157,        1,  1382400, 0x56948101
+0,       1158,       1158,        1,  1382400, 0x56948101
+0,       1159,       1159,        1,  1382400, 0x56948101
+0,       1160,       1160,        1,  1382400, 0x56948101
+0,       1161,       1161,        1,  1382400, 0x56948101
+0,       1162,       1162,        1,  1382400, 0x56948101
+0,       1163,       1163,        1,  1382400, 0x56948101
+0,       1164,       1164,        1,  1382400, 0x56948101
+0,       1166,       1166,        1,  1382400, 0xb747834a
+0,       1166,       1166,        1,  1382400, 0xb747834a
+0,       1167,       1167,        1,  1382400, 0xb747834a
+0,       1168,       1168,        1,  1382400, 0xb747834a
+0,       1169,       1169,        1,  1382400, 0xb747834a
+0,       1170,       1170,        1,  1382400, 0xb747834a
+0,       1171,       1171,        1,  1382400, 0xb747834a
+0,       1172,       1172,        1,  1382400, 0xb747834a
+0,       1173,       1173,        1,  1382400, 0xb747834a
+0,       1174,       1174,        1,  1382400, 0xb747834a
+0,       1175,       1175,        1,  1382400, 0xb747834a
+0,       1176,       1176,        1,  1382400, 0xb747834a
+0,       1177,       1177,        1,  1382400, 0xb747834a
+0,       1178,       1178,        1,  1382400, 0xb747834a
+0,       1179,       1179,        1,  1382400, 0xb747834a
+0,       1180,       1180,        1,  1382400, 0xb747834a
+0,       1181,       1181,        1,  1382400, 0xb747834a
+0,       1182,       1182,        1,  1382400, 0xb747834a
+0,       1183,       1183,        1,  1382400, 0xb747834a
+0,       1184,       1184,        1,  1382400, 0xb747834a
+0,       1185,       1185,        1,  1382400, 0xb747834a
+0,       1187,       1187,        1,  1382400, 0x3448baad
+0,       1187,       1187,        1,  1382400, 0x3448baad
+0,       1188,       1188,        1,  1382400, 0x3448baad
+0,       1189,       1189,        1,  1382400, 0x3448baad
+0,       1190,       1190,        1,  1382400, 0x3448baad
+0,       1191,       1191,        1,  1382400, 0x3448baad
+0,       1192,       1192,        1,  1382400, 0x3448baad
+0,       1193,       1193,        1,  1382400, 0x3448baad
+0,       1194,       1194,        1,  1382400, 0x3448baad
+0,       1195,       1195,        1,  1382400, 0x3448baad
+0,       1196,       1196,        1,  1382400, 0x3448baad
+0,       1197,       1197,        1,  1382400, 0x3448baad
+0,       1198,       1198,        1,  1382400, 0x3448baad
+0,       1199,       1199,        1,  1382400, 0x3448baad
+0,       1200,       1200,        1,  1382400, 0x00000000
+0,       1201,       1201,        1,  1382400, 0xaabe4f37
+0,       1202,       1202,        1,  1382400, 0xaabe4f37
+0,       1203,       1203,        1,  1382400, 0xaabe4f37
+0,       1204,       1204,        1,  1382400, 0xaabe4f37
+0,       1205,       1205,        1,  1382400, 0xaabe4f37
+0,       1206,       1206,        1,  1382400, 0xaabe4f37
+0,       1207,       1207,        1,  1382400, 0xaabe4f37
+0,       1208,       1208,        1,  1382400, 0xaabe4f37
+0,       1209,       1209,        1,  1382400, 0xaabe4f37
+0,       1210,       1210,        1,  1382400, 0xaabe4f37
+0,       1211,       1211,        1,  1382400, 0x00000000
+0,       1212,       1212,        1,  1382400, 0x00000000
+0,       1213,       1213,        1,  1382400, 0x00000000
+0,       1214,       1214,        1,  1382400, 0x00000000
+0,       1215,       1215,        1,  1382400, 0x00000000
+0,       1216,       1216,        1,  1382400, 0x00000000
+0,       1217,       1217,        1,  1382400, 0x00000000
+0,       1218,       1218,        1,  1382400, 0x00000000
+0,       1219,       1219,        1,  1382400, 0x00000000
+0,       1220,       1220,        1,  1382400, 0x00000000
+0,       1221,       1221,        1,  1382400, 0x00000000
+0,       1222,       1222,        1,  1382400, 0x00000000
+0,       1223,       1223,        1,  1382400, 0x00000000
+0,       1224,       1224,        1,  1382400, 0x00000000
+0,       1225,       1225,        1,  1382400, 0x00000000
+0,       1226,       1226,        1,  1382400, 0x00000000
+0,       1227,       1227,        1,  1382400, 0x00000000
+0,       1228,       1228,        1,  1382400, 0x00000000
+0,       1229,       1229,        1,  1382400, 0x00000000
+0,       1230,       1230,        1,  1382400, 0x00000000
+0,       1231,       1231,        1,  1382400, 0x00000000
+0,       1232,       1232,        1,  1382400, 0x00000000
+0,       1233,       1233,        1,  1382400, 0x00000000
+0,       1234,       1234,        1,  1382400, 0x00000000
+0,       1235,       1235,        1,  1382400, 0x00000000
+0,       1236,       1236,        1,  1382400, 0x00000000
+0,       1237,       1237,        1,  1382400, 0x00000000
+0,       1238,       1238,        1,  1382400, 0x00000000
+0,       1239,       1239,        1,  1382400, 0x00000000
+0,       1240,       1240,        1,  1382400, 0x00000000
+0,       1241,       1241,        1,  1382400, 0x00000000
+0,       1242,       1242,        1,  1382400, 0x00000000
+0,       1243,       1243,        1,  1382400, 0x00000000
+0,       1244,       1244,        1,  1382400, 0x00000000
+0,       1245,       1245,        1,  1382400, 0x00000000
+0,       1246,       1246,        1,  1382400, 0x00000000
+0,       1247,       1247,        1,  1382400, 0x00000000
+0,       1248,       1248,        1,  1382400, 0x00000000
+0,       1249,       1249,        1,  1382400, 0x00000000
+0,       1250,       1250,        1,  1382400, 0x00000000
+0,       1251,       1251,        1,  1382400, 0x00000000
+0,       1252,       1252,        1,  1382400, 0x00000000
+0,       1253,       1253,        1,  1382400, 0x00000000
+0,       1254,       1254,        1,  1382400, 0x00000000
+0,       1255,       1255,        1,  1382400, 0x00000000
+0,       1256,       1256,        1,  1382400, 0x00000000
+0,       1257,       1257,        1,  1382400, 0x00000000
+0,       1258,       1258,        1,  1382400, 0x00000000
+0,       1259,       1259,        1,  1382400, 0x00000000
+0,       1260,       1260,        1,  1382400, 0x00000000
+0,       1261,       1261,        1,  1382400, 0x00000000
+0,       1262,       1262,        1,  1382400, 0x00000000
+0,       1263,       1263,        1,  1382400, 0x00000000
+0,       1264,       1264,        1,  1382400, 0x00000000
+0,       1265,       1265,        1,  1382400, 0x00000000
+0,       1266,       1266,        1,  1382400, 0x00000000
+0,       1267,       1267,        1,  1382400, 0x00000000
+0,       1268,       1268,        1,  1382400, 0x00000000
+0,       1269,       1269,        1,  1382400, 0x00000000
+0,       1270,       1270,        1,  1382400, 0x00000000
+0,       1271,       1271,        1,  1382400, 0x00000000
+0,       1272,       1272,        1,  1382400, 0x00000000
+0,       1273,       1273,        1,  1382400, 0x00000000
+0,       1274,       1274,        1,  1382400, 0x00000000
+0,       1275,       1275,        1,  1382400, 0x00000000
+0,       1276,       1276,        1,  1382400, 0x00000000
+0,       1277,       1277,        1,  1382400, 0x00000000
+0,       1278,       1278,        1,  1382400, 0x00000000
+0,       1279,       1279,        1,  1382400, 0x00000000
+0,       1280,       1280,        1,  1382400, 0x00000000
+0,       1281,       1281,        1,  1382400, 0x00000000
+0,       1282,       1282,        1,  1382400, 0x00000000
+0,       1283,       1283,        1,  1382400, 0x00000000
+0,       1284,       1284,        1,  1382400, 0x00000000
+0,       1285,       1285,        1,  1382400, 0x00000000
+0,       1286,       1286,        1,  1382400, 0x00000000
+0,       1287,       1287,        1,  1382400, 0x00000000
+0,       1288,       1288,        1,  1382400, 0x00000000
+0,       1289,       1289,        1,  1382400, 0x00000000
+0,       1290,       1290,        1,  1382400, 0x00000000
+0,       1291,       1291,        1,  1382400, 0x00000000
+0,       1292,       1292,        1,  1382400, 0x00000000
+0,       1293,       1293,        1,  1382400, 0x00000000
+0,       1294,       1294,        1,  1382400, 0x00000000
+0,       1295,       1295,        1,  1382400, 0x00000000
+0,       1296,       1296,        1,  1382400, 0x00000000
+0,       1297,       1297,        1,  1382400, 0x00000000
+0,       1298,       1298,        1,  1382400, 0x00000000
+0,       1299,       1299,        1,  1382400, 0x00000000
+0,       1300,       1300,        1,  1382400, 0x00000000
+0,       1301,       1301,        1,  1382400, 0x00000000
+0,       1302,       1302,        1,  1382400, 0x00000000
+0,       1303,       1303,        1,  1382400, 0x00000000
+0,       1304,       1304,        1,  1382400, 0x00000000
+0,       1305,       1305,        1,  1382400, 0x00000000
+0,       1306,       1306,        1,  1382400, 0x00000000
+0,       1307,       1307,        1,  1382400, 0x00000000
+0,       1308,       1308,        1,  1382400, 0x00000000
+0,       1309,       1309,        1,  1382400, 0x00000000
+0,       1310,       1310,        1,  1382400, 0x00000000
+0,       1311,       1311,        1,  1382400, 0x00000000
+0,       1312,       1312,        1,  1382400, 0x00000000
+0,       1313,       1313,        1,  1382400, 0x00000000
+0,       1314,       1314,        1,  1382400, 0x00000000
+0,       1315,       1315,        1,  1382400, 0x00000000
+0,       1316,       1316,        1,  1382400, 0x00000000
+0,       1317,       1317,        1,  1382400, 0x00000000
+0,       1318,       1318,        1,  1382400, 0x00000000
+0,       1319,       1319,        1,  1382400, 0x00000000
+0,       1320,       1320,        1,  1382400, 0x00000000
+0,       1321,       1321,        1,  1382400, 0x00000000
+0,       1322,       1322,        1,  1382400, 0x00000000
+0,       1323,       1323,        1,  1382400, 0x00000000
+0,       1324,       1324,        1,  1382400, 0x00000000
+0,       1325,       1325,        1,  1382400, 0x00000000
+0,       1326,       1326,        1,  1382400, 0x00000000
+0,       1327,       1327,        1,  1382400, 0x00000000
+0,       1328,       1328,        1,  1382400, 0x00000000
+0,       1329,       1329,        1,  1382400, 0x00000000
+0,       1330,       1330,        1,  1382400, 0x00000000
+0,       1331,       1331,        1,  1382400, 0x00000000
+0,       1332,       1332,        1,  1382400, 0x00000000
+0,       1333,       1333,        1,  1382400, 0x00000000
+0,       1334,       1334,        1,  1382400, 0x00000000
+0,       1335,       1335,        1,  1382400, 0x00000000
+0,       1336,       1336,        1,  1382400, 0x00000000
+0,       1337,       1337,        1,  1382400, 0x00000000
+0,       1338,       1338,        1,  1382400, 0x00000000
+0,       1339,       1339,        1,  1382400, 0x00000000
+0,       1340,       1340,        1,  1382400, 0x00000000
+0,       1341,       1341,        1,  1382400, 0x00000000
+0,       1342,       1342,        1,  1382400, 0x00000000
+0,       1343,       1343,        1,  1382400, 0x00000000
+0,       1344,       1344,        1,  1382400, 0x00000000
+0,       1345,       1345,        1,  1382400, 0x00000000
+0,       1346,       1346,        1,  1382400, 0x00000000
+0,       1347,       1347,        1,  1382400, 0x00000000
+0,       1348,       1348,        1,  1382400, 0x00000000
+0,       1349,       1349,        1,  1382400, 0x00000000
+0,       1350,       1350,        1,  1382400, 0x00000000
+0,       1351,       1351,        1,  1382400, 0x00000000
+0,       1352,       1352,        1,  1382400, 0x00000000
+0,       1353,       1353,        1,  1382400, 0x00000000
+0,       1354,       1354,        1,  1382400, 0x00000000
+0,       1355,       1355,        1,  1382400, 0x00000000
+0,       1356,       1356,        1,  1382400, 0x00000000
+0,       1357,       1357,        1,  1382400, 0x00000000
+0,       1358,       1358,        1,  1382400, 0x00000000
+0,       1359,       1359,        1,  1382400, 0x00000000
+0,       1360,       1360,        1,  1382400, 0x00000000
+0,       1361,       1361,        1,  1382400, 0x00000000
+0,       1362,       1362,        1,  1382400, 0x00000000
+0,       1363,       1363,        1,  1382400, 0x00000000
+0,       1364,       1364,        1,  1382400, 0x00000000
+0,       1365,       1365,        1,  1382400, 0x00000000
+0,       1366,       1366,        1,  1382400, 0x00000000
+0,       1368,       1368,        1,  1382400, 0x8a48cd6f
+0,       1368,       1368,        1,  1382400, 0x8a48cd6f
+0,       1369,       1369,        1,  1382400, 0x8a48cd6f
+0,       1370,       1370,        1,  1382400, 0x8a48cd6f
+0,       1371,       1371,        1,  1382400, 0x8a48cd6f
+0,       1372,       1372,        1,  1382400, 0x8a48cd6f
+0,       1373,       1373,        1,  1382400, 0x8a48cd6f
+0,       1374,       1374,        1,  1382400, 0x8a48cd6f
+0,       1375,       1375,        1,  1382400, 0x8a48cd6f
+0,       1376,       1376,        1,  1382400, 0x00000000
+0,       1377,       1377,        1,  1382400, 0x00000000
+0,       1378,       1378,        1,  1382400, 0x00000000
+0,       1379,       1379,        1,  1382400, 0x00000000
+0,       1380,       1380,        1,  1382400, 0x00000000
+0,       1381,       1381,        1,  1382400, 0x00000000
+0,       1382,       1382,        1,  1382400, 0x00000000
+0,       1383,       1383,        1,  1382400, 0x00000000
+0,       1384,       1384,        1,  1382400, 0x00000000
+0,       1385,       1385,        1,  1382400, 0x00000000
+0,       1386,       1386,        1,  1382400, 0x00000000
+0,       1387,       1387,        1,  1382400, 0x00000000
+0,       1388,       1388,        1,  1382400, 0x00000000
+0,       1389,       1389,        1,  1382400, 0x00000000
+0,       1390,       1390,        1,  1382400, 0x00000000
+0,       1391,       1391,        1,  1382400, 0x00000000
+0,       1392,       1392,        1,  1382400, 0x00000000
+0,       1393,       1393,        1,  1382400, 0x00000000
+0,       1394,       1394,        1,  1382400, 0x00000000
+0,       1395,       1395,        1,  1382400, 0x00000000
+0,       1396,       1396,        1,  1382400, 0x00000000
+0,       1397,       1397,        1,  1382400, 0x00000000
+0,       1398,       1398,        1,  1382400, 0x00000000
+0,       1399,       1399,        1,  1382400, 0x00000000
+0,       1400,       1400,        1,  1382400, 0x00000000
+0,       1401,       1401,        1,  1382400, 0x00000000
+0,       1402,       1402,        1,  1382400, 0x00000000
+0,       1403,       1403,        1,  1382400, 0x00000000
+0,       1404,       1404,        1,  1382400, 0x00000000
+0,       1405,       1405,        1,  1382400, 0x00000000
+0,       1406,       1406,        1,  1382400, 0x00000000
+0,       1407,       1407,        1,  1382400, 0x00000000
+0,       1408,       1408,        1,  1382400, 0x00000000
+0,       1409,       1409,        1,  1382400, 0x00000000
+0,       1410,       1410,        1,  1382400, 0x00000000
+0,       1411,       1411,        1,  1382400, 0x00000000
+0,       1412,       1412,        1,  1382400, 0x00000000
+0,       1413,       1413,        1,  1382400, 0x00000000
+0,       1414,       1414,        1,  1382400, 0x00000000
+0,       1415,       1415,        1,  1382400, 0x00000000
+0,       1416,       1416,        1,  1382400, 0x00000000
+0,       1417,       1417,        1,  1382400, 0x00000000
+0,       1418,       1418,        1,  1382400, 0x00000000
+0,       1419,       1419,        1,  1382400, 0x00000000
+0,       1420,       1420,        1,  1382400, 0x00000000
+0,       1421,       1421,        1,  1382400, 0x00000000
+0,       1422,       1422,        1,  1382400, 0x00000000
+0,       1423,       1423,        1,  1382400, 0x00000000
+0,       1424,       1424,        1,  1382400, 0x00000000
+0,       1425,       1425,        1,  1382400, 0x00000000
+0,       1426,       1426,        1,  1382400, 0x00000000
+0,       1427,       1427,        1,  1382400, 0x00000000
+0,       1428,       1428,        1,  1382400, 0x00000000
+0,       1429,       1429,        1,  1382400, 0x00000000
+0,       1430,       1430,        1,  1382400, 0x00000000
+0,       1431,       1431,        1,  1382400, 0x00000000
+0,       1432,       1432,        1,  1382400, 0x00000000
+0,       1433,       1433,        1,  1382400, 0x00000000
+0,       1434,       1434,        1,  1382400, 0x00000000
+0,       1435,       1435,        1,  1382400, 0x00000000
+0,       1436,       1436,        1,  1382400, 0x00000000
+0,       1437,       1437,        1,  1382400, 0x00000000
+0,       1438,       1438,        1,  1382400, 0x00000000
+0,       1439,       1439,        1,  1382400, 0x00000000
+0,       1440,       1440,        1,  1382400, 0x00000000
+0,       1441,       1441,        1,  1382400, 0x00000000
+0,       1442,       1442,        1,  1382400, 0x00000000
+0,       1443,       1443,        1,  1382400, 0x00000000
+0,       1444,       1444,        1,  1382400, 0x00000000
+0,       1445,       1445,        1,  1382400, 0x00000000
+0,       1446,       1446,        1,  1382400, 0x00000000
+0,       1447,       1447,        1,  1382400, 0x00000000
+0,       1448,       1448,        1,  1382400, 0x00000000
+0,       1449,       1449,        1,  1382400, 0x00000000
+0,       1450,       1450,        1,  1382400, 0x00000000
+0,       1451,       1451,        1,  1382400, 0x00000000
+0,       1452,       1452,        1,  1382400, 0x00000000
+0,       1453,       1453,        1,  1382400, 0x00000000
+0,       1454,       1454,        1,  1382400, 0x00000000
+0,       1455,       1455,        1,  1382400, 0x00000000
+0,       1456,       1456,        1,  1382400, 0x00000000
+0,       1457,       1457,        1,  1382400, 0x00000000
+0,       1458,       1458,        1,  1382400, 0x00000000
+0,       1459,       1459,        1,  1382400, 0x00000000
+0,       1460,       1460,        1,  1382400, 0x00000000
+0,       1461,       1461,        1,  1382400, 0x00000000
+0,       1462,       1462,        1,  1382400, 0x00000000
+0,       1463,       1463,        1,  1382400, 0x00000000
+0,       1464,       1464,        1,  1382400, 0x00000000
+0,       1465,       1465,        1,  1382400, 0x00000000
+0,       1466,       1466,        1,  1382400, 0x00000000
+0,       1467,       1467,        1,  1382400, 0x00000000
+0,       1468,       1468,        1,  1382400, 0x00000000
+0,       1469,       1469,        1,  1382400, 0x00000000
+0,       1470,       1470,        1,  1382400, 0x00000000
+0,       1471,       1471,        1,  1382400, 0x00000000
+0,       1472,       1472,        1,  1382400, 0x00000000
+0,       1473,       1473,        1,  1382400, 0x00000000
+0,       1474,       1474,        1,  1382400, 0x00000000
+0,       1475,       1475,        1,  1382400, 0x00000000
+0,       1477,       1477,        1,  1382400, 0x49518c43
+0,       1477,       1477,        1,  1382400, 0x49518c43
+0,       1478,       1478,        1,  1382400, 0x49518c43
+0,       1479,       1479,        1,  1382400, 0x49518c43
+0,       1480,       1480,        1,  1382400, 0x49518c43
+0,       1481,       1481,        1,  1382400, 0x49518c43
+0,       1482,       1482,        1,  1382400, 0x49518c43
+0,       1483,       1483,        1,  1382400, 0x49518c43
+0,       1484,       1484,        1,  1382400, 0x00000000
+0,       1485,       1485,        1,  1382400, 0x00000000
+0,       1486,       1486,        1,  1382400, 0x00000000
+0,       1487,       1487,        1,  1382400, 0x00000000
+0,       1488,       1488,        1,  1382400, 0x00000000
+0,       1489,       1489,        1,  1382400, 0x00000000
+0,       1490,       1490,        1,  1382400, 0x00000000
+0,       1491,       1491,        1,  1382400, 0x00000000
+0,       1492,       1492,        1,  1382400, 0x00000000
+0,       1493,       1493,        1,  1382400, 0x00000000
+0,       1494,       1494,        1,  1382400, 0x00000000
+0,       1495,       1495,        1,  1382400, 0x00000000
+0,       1496,       1496,        1,  1382400, 0x00000000
+0,       1497,       1497,        1,  1382400, 0x00000000
+0,       1498,       1498,        1,  1382400, 0x00000000
+0,       1500,       1500,        1,  1382400, 0x4a72fa21
+0,       1500,       1500,        1,  1382400, 0x4a72fa21
+0,       1501,       1501,        1,  1382400, 0x4a72fa21
+0,       1502,       1502,        1,  1382400, 0x4a72fa21
+0,       1503,       1503,        1,  1382400, 0x4a72fa21
+0,       1504,       1504,        1,  1382400, 0x4a72fa21
+0,       1505,       1505,        1,  1382400, 0x4a72fa21
+0,       1506,       1506,        1,  1382400, 0x4a72fa21
+0,       1507,       1507,        1,  1382400, 0x4a72fa21
+0,       1508,       1508,        1,  1382400, 0x4a72fa21
+0,       1509,       1509,        1,  1382400, 0x4a72fa21
+0,       1510,       1510,        1,  1382400, 0x00000000
+0,       1511,       1511,        1,  1382400, 0xa82f7de8
+0,       1512,       1512,        1,  1382400, 0xa82f7de8
+0,       1513,       1513,        1,  1382400, 0xa82f7de8
+0,       1514,       1514,        1,  1382400, 0xa82f7de8
+0,       1515,       1515,        1,  1382400, 0xa82f7de8
+0,       1516,       1516,        1,  1382400, 0xa82f7de8
+0,       1517,       1517,        1,  1382400, 0xa82f7de8
+0,       1518,       1518,        1,  1382400, 0x00000000
+0,       1519,       1519,        1,  1382400, 0x00000000
+0,       1521,       1521,        1,  1382400, 0xeba0b5f3
+0,       1521,       1521,        1,  1382400, 0xeba0b5f3
+0,       1522,       1522,        1,  1382400, 0xeba0b5f3
+0,       1523,       1523,        1,  1382400, 0xeba0b5f3
+0,       1524,       1524,        1,  1382400, 0xeba0b5f3
+0,       1525,       1525,        1,  1382400, 0xeba0b5f3
+0,       1526,       1526,        1,  1382400, 0xeba0b5f3
+0,       1527,       1527,        1,  1382400, 0xeba0b5f3
+0,       1528,       1528,        1,  1382400, 0xeba0b5f3
+0,       1530,       1530,        1,  1382400, 0xd6a91770
+0,       1530,       1530,        1,  1382400, 0xd6a91770
+0,       1531,       1531,        1,  1382400, 0xd6a91770
+0,       1532,       1532,        1,  1382400, 0xd6a91770
+0,       1533,       1533,        1,  1382400, 0xd6a91770
+0,       1534,       1534,        1,  1382400, 0xd6a91770
+0,       1535,       1535,        1,  1382400, 0xd6a91770
+0,       1536,       1536,        1,  1382400, 0xd6a91770
+0,       1537,       1537,        1,  1382400, 0xd6a91770
+0,       1538,       1538,        1,  1382400, 0xd6a91770
+0,       1539,       1539,        1,  1382400, 0x00000000
+0,       1540,       1540,        1,  1382400, 0x222f827c
+0,       1541,       1541,        1,  1382400, 0x222f827c
+0,       1542,       1542,        1,  1382400, 0x222f827c
+0,       1543,       1543,        1,  1382400, 0x222f827c
+0,       1544,       1544,        1,  1382400, 0x222f827c
+0,       1545,       1545,        1,  1382400, 0x222f827c
+0,       1546,       1546,        1,  1382400, 0x222f827c
+0,       1547,       1547,        1,  1382400, 0x222f827c
+0,       1548,       1548,        1,  1382400, 0x222f827c
+0,       1549,       1549,        1,  1382400, 0x00000000
+0,       1550,       1550,        1,  1382400, 0x00000000
+0,       1551,       1551,        1,  1382400, 0x00000000
+0,       1552,       1552,        1,  1382400, 0x00000000
+0,       1553,       1553,        1,  1382400, 0x00000000
+0,       1554,       1554,        1,  1382400, 0x00000000
+0,       1555,       1555,        1,  1382400, 0x00000000
+0,       1556,       1556,        1,  1382400, 0x00000000
+0,       1557,       1557,        1,  1382400, 0x00000000
+0,       1558,       1558,        1,  1382400, 0x00000000
+0,       1559,       1559,        1,  1382400, 0x00000000
+0,       1560,       1560,        1,  1382400, 0x00000000
+0,       1561,       1561,        1,  1382400, 0x00000000
+0,       1562,       1562,        1,  1382400, 0x00000000
+0,       1563,       1563,        1,  1382400, 0x00000000
+0,       1564,       1564,        1,  1382400, 0x00000000
+0,       1565,       1565,        1,  1382400, 0x00000000
+0,       1566,       1566,        1,  1382400, 0x00000000
+0,       1567,       1567,        1,  1382400, 0x00000000
+0,       1568,       1568,        1,  1382400, 0x00000000
+0,       1569,       1569,        1,  1382400, 0x00000000
+0,       1570,       1570,        1,  1382400, 0x00000000
+0,       1571,       1571,        1,  1382400, 0x00000000
+0,       1572,       1572,        1,  1382400, 0x00000000
+0,       1573,       1573,        1,  1382400, 0x00000000
+0,       1574,       1574,        1,  1382400, 0x00000000
+0,       1575,       1575,        1,  1382400, 0x00000000
+0,       1576,       1576,        1,  1382400, 0x00000000
+0,       1577,       1577,        1,  1382400, 0x00000000
+0,       1578,       1578,        1,  1382400, 0x00000000
+0,       1579,       1579,        1,  1382400, 0x00000000
+0,       1580,       1580,        1,  1382400, 0x00000000
+0,       1581,       1581,        1,  1382400, 0x00000000
+0,       1582,       1582,        1,  1382400, 0x00000000
+0,       1583,       1583,        1,  1382400, 0x00000000
+0,       1584,       1584,        1,  1382400, 0x00000000
+0,       1585,       1585,        1,  1382400, 0x00000000
+0,       1586,       1586,        1,  1382400, 0x00000000
+0,       1587,       1587,        1,  1382400, 0x00000000
+0,       1588,       1588,        1,  1382400, 0x00000000
+0,       1589,       1589,        1,  1382400, 0x00000000
+0,       1590,       1590,        1,  1382400, 0x00000000
+0,       1591,       1591,        1,  1382400, 0x00000000
+0,       1592,       1592,        1,  1382400, 0x00000000
+0,       1593,       1593,        1,  1382400, 0x00000000
+0,       1594,       1594,        1,  1382400, 0x00000000
+0,       1595,       1595,        1,  1382400, 0x00000000
+0,       1596,       1596,        1,  1382400, 0x00000000
+0,       1597,       1597,        1,  1382400, 0x00000000
+0,       1598,       1598,        1,  1382400, 0x00000000
+0,       1599,       1599,        1,  1382400, 0x00000000
+0,       1600,       1600,        1,  1382400, 0x00000000
+0,       1601,       1601,        1,  1382400, 0x00000000
+0,       1602,       1602,        1,  1382400, 0x00000000
+0,       1603,       1603,        1,  1382400, 0x00000000
+0,       1604,       1604,        1,  1382400, 0x00000000
+0,       1606,       1606,        1,  1382400, 0x3270f4ff
+0,       1606,       1606,        1,  1382400, 0x3270f4ff
+0,       1607,       1607,        1,  1382400, 0x3270f4ff
+0,       1608,       1608,        1,  1382400, 0x3270f4ff
+0,       1609,       1609,        1,  1382400, 0x3270f4ff
+0,       1610,       1610,        1,  1382400, 0x3270f4ff
+0,       1611,       1611,        1,  1382400, 0x3270f4ff
+0,       1612,       1612,        1,  1382400, 0x3270f4ff
+0,       1613,       1613,        1,  1382400, 0x3270f4ff
+0,       1614,       1614,        1,  1382400, 0x3270f4ff
+0,       1615,       1615,        1,  1382400, 0x3270f4ff
+0,       1617,       1617,        1,  1382400, 0x40813cb3
+0,       1617,       1617,        1,  1382400, 0x40813cb3
+0,       1618,       1618,        1,  1382400, 0x40813cb3
+0,       1619,       1619,        1,  1382400, 0x40813cb3
+0,       1620,       1620,        1,  1382400, 0x40813cb3
+0,       1621,       1621,        1,  1382400, 0x40813cb3
+0,       1622,       1622,        1,  1382400, 0x40813cb3
+0,       1623,       1623,        1,  1382400, 0x9d8fde41
+0,       1624,       1624,        1,  1382400, 0x9d8fde41
+0,       1625,       1625,        1,  1382400, 0x9d8fde41
+0,       1626,       1626,        1,  1382400, 0x9d8fde41
+0,       1627,       1627,        1,  1382400, 0x9d8fde41
+0,       1628,       1628,        1,  1382400, 0x9d8fde41
+0,       1629,       1629,        1,  1382400, 0x9d8fde41
+0,       1630,       1630,        1,  1382400, 0x9d8fde41
+0,       1631,       1631,        1,  1382400, 0x9d8fde41
+0,       1632,       1632,        1,  1382400, 0x00000000
+0,       1633,       1633,        1,  1382400, 0x00000000
+0,       1634,       1634,        1,  1382400, 0x00000000
+0,       1636,       1636,        1,  1382400, 0xc6d7a701
+0,       1636,       1636,        1,  1382400, 0xc6d7a701
+0,       1637,       1637,        1,  1382400, 0xc6d7a701
+0,       1638,       1638,        1,  1382400, 0xc6d7a701
+0,       1639,       1639,        1,  1382400, 0xc6d7a701
+0,       1640,       1640,        1,  1382400, 0xc6d7a701
+0,       1642,       1642,        1,  1382400, 0x9d45f2dc
+0,       1642,       1642,        1,  1382400, 0x9d45f2dc
+0,       1643,       1643,        1,  1382400, 0x9d45f2dc
+0,       1644,       1644,        1,  1382400, 0x9d45f2dc
+0,       1645,       1645,        1,  1382400, 0x9d45f2dc
+0,       1646,       1646,        1,  1382400, 0x9d45f2dc
+0,       1647,       1647,        1,  1382400, 0x9d45f2dc
+0,       1648,       1648,        1,  1382400, 0x9d45f2dc
+0,       1649,       1649,        1,  1382400, 0x00000000
+0,       1650,       1650,        1,  1382400, 0x8525ee40
+0,       1651,       1651,        1,  1382400, 0x8525ee40
+0,       1652,       1652,        1,  1382400, 0x8525ee40
+0,       1653,       1653,        1,  1382400, 0x8525ee40
+0,       1654,       1654,        1,  1382400, 0x8525ee40
+0,       1655,       1655,        1,  1382400, 0x8525ee40
+0,       1656,       1656,        1,  1382400, 0x5b26b98b
+0,       1657,       1657,        1,  1382400, 0x5b26b98b
+0,       1658,       1658,        1,  1382400, 0x5b26b98b
+0,       1659,       1659,        1,  1382400, 0x5b26b98b
+0,       1660,       1660,        1,  1382400, 0x5b26b98b
+0,       1661,       1661,        1,  1382400, 0x5b26b98b
+0,       1662,       1662,        1,  1382400, 0x5b26b98b
+0,       1663,       1663,        1,  1382400, 0x5b26b98b
+0,       1664,       1664,        1,  1382400, 0x00000000
+0,       1665,       1665,        1,  1382400, 0x51be311f
+0,       1666,       1666,        1,  1382400, 0x51be311f
+0,       1667,       1667,        1,  1382400, 0x51be311f
+0,       1668,       1668,        1,  1382400, 0x51be311f
+0,       1669,       1669,        1,  1382400, 0x51be311f
+0,       1670,       1670,        1,  1382400, 0x51be311f
+0,       1671,       1671,        1,  1382400, 0x51be311f
+0,       1672,       1672,        1,  1382400, 0x51be311f
+0,       1673,       1673,        1,  1382400, 0x00000000
+0,       1674,       1674,        1,  1382400, 0x00000000
+0,       1675,       1675,        1,  1382400, 0x00000000
+0,       1676,       1676,        1,  1382400, 0x00000000
+0,       1677,       1677,        1,  1382400, 0x00000000
+0,       1678,       1678,        1,  1382400, 0x00000000
+0,       1679,       1679,        1,  1382400, 0x00000000
+0,       1680,       1680,        1,  1382400, 0x00000000
+0,       1681,       1681,        1,  1382400, 0x00000000
+0,       1682,       1682,        1,  1382400, 0x00000000
+0,       1683,       1683,        1,  1382400, 0x00000000
+0,       1684,       1684,        1,  1382400, 0x00000000
+0,       1685,       1685,        1,  1382400, 0x00000000
+0,       1686,       1686,        1,  1382400, 0x00000000
+0,       1687,       1687,        1,  1382400, 0x00000000
+0,       1688,       1688,        1,  1382400, 0x00000000
+0,       1689,       1689,        1,  1382400, 0x00000000
+0,       1690,       1690,        1,  1382400, 0x00000000
+0,       1691,       1691,        1,  1382400, 0x00000000
+0,       1692,       1692,        1,  1382400, 0x00000000
+0,       1693,       1693,        1,  1382400, 0x00000000
+0,       1694,       1694,        1,  1382400, 0x00000000
+0,       1695,       1695,        1,  1382400, 0x00000000
+0,       1696,       1696,        1,  1382400, 0x00000000
+0,       1697,       1697,        1,  1382400, 0x00000000
+0,       1698,       1698,        1,  1382400, 0x00000000
+0,       1699,       1699,        1,  1382400, 0x00000000
+0,       1700,       1700,        1,  1382400, 0x00000000
+0,       1701,       1701,        1,  1382400, 0x00000000
+0,       1702,       1702,        1,  1382400, 0x00000000
+0,       1703,       1703,        1,  1382400, 0x00000000
+0,       1704,       1704,        1,  1382400, 0x00000000
+0,       1705,       1705,        1,  1382400, 0x00000000
+0,       1706,       1706,        1,  1382400, 0x00000000
+0,       1707,       1707,        1,  1382400, 0x00000000
+0,       1708,       1708,        1,  1382400, 0x00000000
+0,       1709,       1709,        1,  1382400, 0x00000000
+0,       1710,       1710,        1,  1382400, 0x00000000
+0,       1711,       1711,        1,  1382400, 0x00000000
+0,       1713,       1713,        1,  1382400, 0x00a4f2a3
+0,       1713,       1713,        1,  1382400, 0x00a4f2a3
+0,       1714,       1714,        1,  1382400, 0x00a4f2a3
+0,       1715,       1715,        1,  1382400, 0x00a4f2a3
+0,       1716,       1716,        1,  1382400, 0x00a4f2a3
+0,       1717,       1717,        1,  1382400, 0x00a4f2a3
+0,       1718,       1718,        1,  1382400, 0x00a4f2a3
+0,       1719,       1719,        1,  1382400, 0x00a4f2a3
+0,       1720,       1720,        1,  1382400, 0x00a4f2a3
+0,       1721,       1721,        1,  1382400, 0x00a4f2a3
+0,       1722,       1722,        1,  1382400, 0x00000000
+0,       1723,       1723,        1,  1382400, 0x00000000
+0,       1724,       1724,        1,  1382400, 0x00000000
+0,       1725,       1725,        1,  1382400, 0x00000000
+0,       1726,       1726,        1,  1382400, 0x00000000
+0,       1727,       1727,        1,  1382400, 0x00000000
+0,       1728,       1728,        1,  1382400, 0x00000000
+0,       1729,       1729,        1,  1382400, 0x00000000
+0,       1730,       1730,        1,  1382400, 0x00000000
+0,       1731,       1731,        1,  1382400, 0x00000000
+0,       1732,       1732,        1,  1382400, 0x00000000
+0,       1734,       1734,        1,  1382400, 0x40a445e8
+0,       1734,       1734,        1,  1382400, 0x40a445e8
+0,       1735,       1735,        1,  1382400, 0x40a445e8
+0,       1736,       1736,        1,  1382400, 0x40a445e8
+0,       1737,       1737,        1,  1382400, 0x40a445e8
+0,       1738,       1738,        1,  1382400, 0x40a445e8
+0,       1739,       1739,        1,  1382400, 0x40a445e8
+0,       1740,       1740,        1,  1382400, 0x40a445e8
+0,       1741,       1741,        1,  1382400, 0x40a445e8
+0,       1742,       1742,        1,  1382400, 0x00000000
+0,       1743,       1743,        1,  1382400, 0x00000000
+0,       1744,       1744,        1,  1382400, 0x00000000
+0,       1745,       1745,        1,  1382400, 0x00000000
+0,       1746,       1746,        1,  1382400, 0x00000000
+0,       1747,       1747,        1,  1382400, 0x00000000
+0,       1748,       1748,        1,  1382400, 0x00000000
+0,       1749,       1749,        1,  1382400, 0x00000000
+0,       1750,       1750,        1,  1382400, 0x00000000
+0,       1751,       1751,        1,  1382400, 0x00000000
+0,       1752,       1752,        1,  1382400, 0x00000000
+0,       1753,       1753,        1,  1382400, 0x00000000
+0,       1754,       1754,        1,  1382400, 0x00000000
+0,       1755,       1755,        1,  1382400, 0x00000000
+0,       1756,       1756,        1,  1382400, 0x00000000
+0,       1757,       1757,        1,  1382400, 0x00000000
+0,       1758,       1758,        1,  1382400, 0x00000000
+0,       1759,       1759,        1,  1382400, 0x00000000
+0,       1760,       1760,        1,  1382400, 0x00000000
+0,       1761,       1761,        1,  1382400, 0x00000000
+0,       1762,       1762,        1,  1382400, 0x00000000
+0,       1763,       1763,        1,  1382400, 0x00000000
+0,       1764,       1764,        1,  1382400, 0x00000000
+0,       1765,       1765,        1,  1382400, 0x00000000
+0,       1766,       1766,        1,  1382400, 0x00000000
+0,       1767,       1767,        1,  1382400, 0x00000000
+0,       1768,       1768,        1,  1382400, 0x00000000
+0,       1769,       1769,        1,  1382400, 0x00000000
+0,       1770,       1770,        1,  1382400, 0x00000000
+0,       1771,       1771,        1,  1382400, 0x00000000
+0,       1772,       1772,        1,  1382400, 0x00000000
+0,       1773,       1773,        1,  1382400, 0x00000000
+0,       1774,       1774,        1,  1382400, 0x00000000
+0,       1775,       1775,        1,  1382400, 0x00000000
+0,       1776,       1776,        1,  1382400, 0x00000000
+0,       1777,       1777,        1,  1382400, 0x00000000
+0,       1778,       1778,        1,  1382400, 0x00000000
+0,       1779,       1779,        1,  1382400, 0x00000000
+0,       1780,       1780,        1,  1382400, 0x00000000
+0,       1781,       1781,        1,  1382400, 0x00000000
+0,       1782,       1782,        1,  1382400, 0x00000000
+0,       1783,       1783,        1,  1382400, 0x00000000
+0,       1784,       1784,        1,  1382400, 0x00000000
+0,       1785,       1785,        1,  1382400, 0x00000000
+0,       1786,       1786,        1,  1382400, 0x00000000
+0,       1788,       1788,        1,  1382400, 0x43ef5128
+0,       1788,       1788,        1,  1382400, 0x43ef5128
+0,       1789,       1789,        1,  1382400, 0x43ef5128
+0,       1790,       1790,        1,  1382400, 0x43ef5128
+0,       1791,       1791,        1,  1382400, 0x43ef5128
+0,       1792,       1792,        1,  1382400, 0x43ef5128
+0,       1793,       1793,        1,  1382400, 0x43ef5128
+0,       1794,       1794,        1,  1382400, 0x43ef5128
+0,       1795,       1795,        1,  1382400, 0x43ef5128
+0,       1796,       1796,        1,  1382400, 0x43ef5128
+0,       1797,       1797,        1,  1382400, 0x43ef5128
+0,       1798,       1798,        1,  1382400, 0x43ef5128
+0,       1799,       1799,        1,  1382400, 0x3c3e3819
+0,       1800,       1800,        1,  1382400, 0x3c3e3819
+0,       1801,       1801,        1,  1382400, 0x3c3e3819
+0,       1802,       1802,        1,  1382400, 0x3c3e3819
+0,       1803,       1803,        1,  1382400, 0x3c3e3819
+0,       1804,       1804,        1,  1382400, 0x3c3e3819
+0,       1805,       1805,        1,  1382400, 0x00000000
diff --git a/tests/ref/fate/sub2video_time_limited b/tests/ref/fate/sub2video_time_limited
index 9fb6fb06f9..3a7cca384a 100644
--- a/tests/ref/fate/sub2video_time_limited
+++ b/tests/ref/fate/sub2video_time_limited
@@ -1,8 +1,80 @@
-#tb 0: 1/25
+#tb 0: 1/5
 #media_type 0: video
 #codec_id 0: rawvideo
 #dimensions 0: 1920x1080
-#sar 0: 0/1
-0,          2,          2,        1,  8294400, 0x00000000
+#sar 0: 1/1
+0,          0,          0,        1,  8294400, 0x00000000
+0,          1,          1,        1,  8294400, 0x00000000
 0,          2,          2,        1,  8294400, 0xa87c518f
+0,          3,          3,        1,  8294400, 0xa87c518f
+0,          4,          4,        1,  8294400, 0xa87c518f
+0,          5,          5,        1,  8294400, 0xa87c518f
+0,          6,          6,        1,  8294400, 0xa87c518f
+0,          7,          7,        1,  8294400, 0xa87c518f
+0,          8,          8,        1,  8294400, 0xa87c518f
+0,          9,          9,        1,  8294400, 0xa87c518f
 0,         10,         10,        1,  8294400, 0xa87c518f
+0,         11,         11,        1,  8294400, 0xa87c518f
+0,         12,         12,        1,  8294400, 0xa87c518f
+0,         13,         13,        1,  8294400, 0xa87c518f
+0,         14,         14,        1,  8294400, 0xa87c518f
+0,         15,         15,        1,  8294400, 0xa87c518f
+0,         16,         16,        1,  8294400, 0xa87c518f
+0,         17,         17,        1,  8294400, 0xa87c518f
+0,         18,         18,        1,  8294400, 0xa87c518f
+0,         19,         19,        1,  8294400, 0xa87c518f
+0,         20,         20,        1,  8294400, 0xa87c518f
+0,         21,         21,        1,  8294400, 0xa87c518f
+0,         22,         22,        1,  8294400, 0xa87c518f
+0,         23,         23,        1,  8294400, 0xa87c518f
+0,         24,         24,        1,  8294400, 0xa87c518f
+0,         25,         25,        1,  8294400, 0xa87c518f
+0,         26,         26,        1,  8294400, 0xa87c518f
+0,         27,         27,        1,  8294400, 0xa87c518f
+0,         28,         28,        1,  8294400, 0xa87c518f
+0,         29,         29,        1,  8294400, 0xa87c518f
+0,         30,         30,        1,  8294400, 0xa87c518f
+0,         31,         31,        1,  8294400, 0xa87c518f
+0,         32,         32,        1,  8294400, 0xa87c518f
+0,         33,         33,        1,  8294400, 0xa87c518f
+0,         34,         34,        1,  8294400, 0xa87c518f
+0,         35,         35,        1,  8294400, 0xa87c518f
+0,         36,         36,        1,  8294400, 0xa87c518f
+0,         37,         37,        1,  8294400, 0xa87c518f
+0,         38,         38,        1,  8294400, 0xa87c518f
+0,         39,         39,        1,  8294400, 0xa87c518f
+0,         40,         40,        1,  8294400, 0xa87c518f
+0,         41,         41,        1,  8294400, 0xa87c518f
+0,         42,         42,        1,  8294400, 0xa87c518f
+0,         43,         43,        1,  8294400, 0xa87c518f
+0,         44,         44,        1,  8294400, 0xa87c518f
+0,         45,         45,        1,  8294400, 0xa87c518f
+0,         46,         46,        1,  8294400, 0xa87c518f
+0,         47,         47,        1,  8294400, 0xa87c518f
+0,         48,         48,        1,  8294400, 0xa87c518f
+0,         49,         49,        1,  8294400, 0xa87c518f
+0,         50,         50,        1,  8294400, 0xa87c518f
+0,         51,         51,        1,  8294400, 0xa87c518f
+0,         52,         52,        1,  8294400, 0xa87c518f
+0,         53,         53,        1,  8294400, 0xa87c518f
+0,         54,         54,        1,  8294400, 0xa87c518f
+0,         55,         55,        1,  8294400, 0xa87c518f
+0,         56,         56,        1,  8294400, 0xa87c518f
+0,         57,         57,        1,  8294400, 0xa87c518f
+0,         58,         58,        1,  8294400, 0xa87c518f
+0,         59,         59,        1,  8294400, 0xa87c518f
+0,         60,         60,        1,  8294400, 0xa87c518f
+0,         61,         61,        1,  8294400, 0xa87c518f
+0,         62,         62,        1,  8294400, 0xa87c518f
+0,         63,         63,        1,  8294400, 0xa87c518f
+0,         64,         64,        1,  8294400, 0xa87c518f
+0,         65,         65,        1,  8294400, 0xa87c518f
+0,         66,         66,        1,  8294400, 0xa87c518f
+0,         67,         67,        1,  8294400, 0xa87c518f
+0,         68,         68,        1,  8294400, 0xa87c518f
+0,         69,         69,        1,  8294400, 0xa87c518f
+0,         70,         70,        1,  8294400, 0xa87c518f
+0,         71,         71,        1,  8294400, 0xa87c518f
+0,         72,         72,        1,  8294400, 0xa87c518f
+0,         73,         73,        1,  8294400, 0xa87c518f
+0,         74,         74,        1,  8294400, 0xa87c518f
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH 22/24] avutil/ass_split: Add parsing of hard-space tags (\h)
  2022-01-14  1:13 [FFmpeg-devel] [PATCH 00/24] Subtitle Filtering 2022 ffmpegagent
                   ` (20 preceding siblings ...)
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 21/24] fftools/ffmpeg: Introduce subtitle filtering new frame-based subtitle encoding ffmpegagent
@ 2022-01-14  1:13 ` ffmpegagent
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 23/24] avcodec/webvttenc: convert hard-space tags to &nbsp; ffmpegagent
                   ` (2 subsequent siblings)
  24 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-14  1:13 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: softworkz

From: softworkz <softworkz@hotmail.com>

The \h tag in ASS/SSA is indicating a non-breaking space. See
https://github.com/Aegisub/aegisite/blob/master/source/docs/3.2/
ASS_Tags.html.md

The ass_split implementation is used by almost all text subtitle
encoders and it didn't handle this tag. Interestingly, several
tests are testing for \h parsing and had incorrect reference data
for those tests.

The \h tag is specific to ASS and doesn't have any meaning outside
of ASS.
Still, the reference data for ttmlenc, textenc and webvttenc were
full of \h tags even though this tag doesn't have a meaning there.

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavutil/ass_split.c            |  7 +++++++
 tests/ref/fate/mov-mp4-ttml-dfxp |  8 ++++----
 tests/ref/fate/mov-mp4-ttml-stpp |  8 ++++----
 tests/ref/fate/sub-textenc       | 10 +++++-----
 tests/ref/fate/sub-ttmlenc       |  8 ++++----
 tests/ref/fate/sub-webvttenc     | 10 +++++-----
 6 files changed, 29 insertions(+), 22 deletions(-)

diff --git a/libavutil/ass_split.c b/libavutil/ass_split.c
index c5963351fc..30512dfc74 100644
--- a/libavutil/ass_split.c
+++ b/libavutil/ass_split.c
@@ -484,6 +484,7 @@ int avpriv_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *pr
     while (buf && *buf) {
         if (text && callbacks->text &&
             (sscanf(buf, "\\%1[nN]", new_line) == 1 ||
+             sscanf(buf, "\\%1[hH]", new_line) == 1 ||
              !strncmp(buf, "{\\", 2))) {
             callbacks->text(priv, text, text_len);
             text = NULL;
@@ -492,6 +493,12 @@ int avpriv_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *pr
             if (callbacks->new_line)
                 callbacks->new_line(priv, new_line[0] == 'N');
             buf += 2;
+        } else if (sscanf(buf, "\\%1[hH]", new_line) == 1) {
+            if (callbacks->hard_space)
+                callbacks->hard_space(priv);
+            else if (callbacks->text)
+                callbacks->text(priv, " ", 1);
+            buf += 2;
         } else if (!strncmp(buf, "{\\", 2)) {
             buf++;
             while (*buf == '\\') {
diff --git a/tests/ref/fate/mov-mp4-ttml-dfxp b/tests/ref/fate/mov-mp4-ttml-dfxp
index e24b5d618b..e565ffa1f6 100644
--- a/tests/ref/fate/mov-mp4-ttml-dfxp
+++ b/tests/ref/fate/mov-mp4-ttml-dfxp
@@ -1,9 +1,9 @@
-2e7e01c821c111466e7a2844826b7f6d *tests/data/fate/mov-mp4-ttml-dfxp.mp4
-8519 tests/data/fate/mov-mp4-ttml-dfxp.mp4
+658884e1b789e75c454b25bdf71283c9 *tests/data/fate/mov-mp4-ttml-dfxp.mp4
+8486 tests/data/fate/mov-mp4-ttml-dfxp.mp4
 #tb 0: 1/1000
 #media_type 0: data
 #codec_id 0: none
-0,          0,          0,    68500,     7866, 0x456c36b7
+0,          0,          0,    68500,     7833, 0x31b22193
 {
     "packets": [
         {
@@ -15,7 +15,7 @@
             "dts_time": "0.000000",
             "duration": 68500,
             "duration_time": "68.500000",
-            "size": "7866",
+            "size": "7833",
             "pos": "44",
             "flags": "K_"
         }
diff --git a/tests/ref/fate/mov-mp4-ttml-stpp b/tests/ref/fate/mov-mp4-ttml-stpp
index 77bd23b7bf..f25b5b2d28 100644
--- a/tests/ref/fate/mov-mp4-ttml-stpp
+++ b/tests/ref/fate/mov-mp4-ttml-stpp
@@ -1,9 +1,9 @@
-cbd2c7ff864a663b0d893deac5a0caec *tests/data/fate/mov-mp4-ttml-stpp.mp4
-8547 tests/data/fate/mov-mp4-ttml-stpp.mp4
+c9570de0ccebc858b0c662a7e449582c *tests/data/fate/mov-mp4-ttml-stpp.mp4
+8514 tests/data/fate/mov-mp4-ttml-stpp.mp4
 #tb 0: 1/1000
 #media_type 0: data
 #codec_id 0: none
-0,          0,          0,    68500,     7866, 0x456c36b7
+0,          0,          0,    68500,     7833, 0x31b22193
 {
     "packets": [
         {
@@ -15,7 +15,7 @@ cbd2c7ff864a663b0d893deac5a0caec *tests/data/fate/mov-mp4-ttml-stpp.mp4
             "dts_time": "0.000000",
             "duration": 68500,
             "duration_time": "68.500000",
-            "size": "7866",
+            "size": "7833",
             "pos": "44",
             "flags": "K_"
         }
diff --git a/tests/ref/fate/sub-textenc b/tests/ref/fate/sub-textenc
index 3ea56b38f0..910ca3d6e3 100644
--- a/tests/ref/fate/sub-textenc
+++ b/tests/ref/fate/sub-textenc
@@ -160,18 +160,18 @@ but show this: {normal text}
 \ N is a forced line break
 \ h is a hard space
 Normal spaces at the start and at the end of the line are trimmed while hard spaces are not trimmed.
-The\hline\hwill\hnever\hbreak\hautomatically\hright\hbefore\hor\hafter\ha\hhard\hspace.\h:-D
+The line will never break automatically right before or after a hard space. :-D
 
 31
 00:00:54,501 --> 00:00:56,500
 
-\h\h\h\h\hA (05 hard spaces followed by a letter)
+     A (05 hard spaces followed by a letter)
 A (Normal  spaces followed by a letter)
 A (No hard spaces followed by a letter)
 
 32
 00:00:56,501 --> 00:00:58,500
-\h\h\h\h\hA (05 hard spaces followed by a letter)
+     A (05 hard spaces followed by a letter)
 A (Normal  spaces followed by a letter)
 A (No hard spaces followed by a letter)
 Show this: \TEST and this: \-)
@@ -179,10 +179,10 @@ Show this: \TEST and this: \-)
 33
 00:00:58,501 --> 00:01:00,500
 
-A letter followed by 05 hard spaces: A\h\h\h\h\h
+A letter followed by 05 hard spaces: A     
 A letter followed by normal  spaces: A
 A letter followed by no hard spaces: A
-05 hard  spaces between letters: A\h\h\h\h\hA
+05 hard  spaces between letters: A     A
 5 normal spaces between letters: A     A
 
 ^--Forced line break
diff --git a/tests/ref/fate/sub-ttmlenc b/tests/ref/fate/sub-ttmlenc
index 4df8f8796f..aea09bb31e 100644
--- a/tests/ref/fate/sub-ttmlenc
+++ b/tests/ref/fate/sub-ttmlenc
@@ -109,16 +109,16 @@
         end="00:00:54.500"><span region="Default">Hide these tags:<br/>also hide these tags:<br/>but show this: {normal text}</span></p>
       <p
         begin="00:00:54.501"
-        end="00:01:00.500"><span region="Default"><br/>\ N is a forced line break<br/>\ h is a hard space<br/>Normal spaces at the start and at the end of the line are trimmed while hard spaces are not trimmed.<br/>The\hline\hwill\hnever\hbreak\hautomatically\hright\hbefore\hor\hafter\ha\hhard\hspace.\h:-D</span></p>
+        end="00:01:00.500"><span region="Default"><br/>\ N is a forced line break<br/>\ h is a hard space<br/>Normal spaces at the start and at the end of the line are trimmed while hard spaces are not trimmed.<br/>The line will never break automatically right before or after a hard space. :-D</span></p>
       <p
         begin="00:00:54.501"
-        end="00:00:56.500"><span region="Default"><br/>\h\h\h\h\hA (05 hard spaces followed by a letter)<br/>A (Normal  spaces followed by a letter)<br/>A (No hard spaces followed by a letter)</span></p>
+        end="00:00:56.500"><span region="Default"><br/>     A (05 hard spaces followed by a letter)<br/>A (Normal  spaces followed by a letter)<br/>A (No hard spaces followed by a letter)</span></p>
       <p
         begin="00:00:56.501"
-        end="00:00:58.500"><span region="Default">\h\h\h\h\hA (05 hard spaces followed by a letter)<br/>A (Normal  spaces followed by a letter)<br/>A (No hard spaces followed by a letter)<br/>Show this: \TEST and this: \-)</span></p>
+        end="00:00:58.500"><span region="Default">     A (05 hard spaces followed by a letter)<br/>A (Normal  spaces followed by a letter)<br/>A (No hard spaces followed by a letter)<br/>Show this: \TEST and this: \-)</span></p>
       <p
         begin="00:00:58.501"
-        end="00:01:00.500"><span region="Default"><br/>A letter followed by 05 hard spaces: A\h\h\h\h\h<br/>A letter followed by normal  spaces: A<br/>A letter followed by no hard spaces: A<br/>05 hard  spaces between letters: A\h\h\h\h\hA<br/>5 normal spaces between letters: A     A<br/><br/>^--Forced line break</span></p>
+        end="00:01:00.500"><span region="Default"><br/>A letter followed by 05 hard spaces: A     <br/>A letter followed by normal  spaces: A<br/>A letter followed by no hard spaces: A<br/>05 hard  spaces between letters: A     A<br/>5 normal spaces between letters: A     A<br/><br/>^--Forced line break</span></p>
       <p
         begin="00:01:00.501"
         end="00:01:02.500"><span region="Default">Both line should be strikethrough,<br/>yes.<br/>Correctly closed tags<br/>should be hidden.</span></p>
diff --git a/tests/ref/fate/sub-webvttenc b/tests/ref/fate/sub-webvttenc
index 45ae0b6131..f4172dcc84 100644
--- a/tests/ref/fate/sub-webvttenc
+++ b/tests/ref/fate/sub-webvttenc
@@ -132,26 +132,26 @@ but show this: {normal text}
 \ N is a forced line break
 \ h is a hard space
 Normal spaces at the start and at the end of the line are trimmed while hard spaces are not trimmed.
-The\hline\hwill\hnever\hbreak\hautomatically\hright\hbefore\hor\hafter\ha\hhard\hspace.\h:-D
+The line will never break automatically right before or after a hard space. :-D
 
 00:54.501 --> 00:56.500
 
-\h\h\h\h\hA (05 hard spaces followed by a letter)
+     A (05 hard spaces followed by a letter)
 A (Normal  spaces followed by a letter)
 A (No hard spaces followed by a letter)
 
 00:56.501 --> 00:58.500
-\h\h\h\h\hA (05 hard spaces followed by a letter)
+     A (05 hard spaces followed by a letter)
 A (Normal  spaces followed by a letter)
 A (No hard spaces followed by a letter)
 Show this: \TEST and this: \-)
 
 00:58.501 --> 01:00.500
 
-A letter followed by 05 hard spaces: A\h\h\h\h\h
+A letter followed by 05 hard spaces: A     
 A letter followed by normal  spaces: A
 A letter followed by no hard spaces: A
-05 hard  spaces between letters: A\h\h\h\h\hA
+05 hard  spaces between letters: A     A
 5 normal spaces between letters: A     A
 
 ^--Forced line break
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH 23/24] avcodec/webvttenc: convert hard-space tags to &nbsp;
  2022-01-14  1:13 [FFmpeg-devel] [PATCH 00/24] Subtitle Filtering 2022 ffmpegagent
                   ` (21 preceding siblings ...)
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 22/24] avutil/ass_split: Add parsing of hard-space tags (\h) ffmpegagent
@ 2022-01-14  1:13 ` ffmpegagent
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 24/24] doc/APIchanges: update for subtitle filtering changes ffmpegagent
  2022-01-20  2:48 ` [FFmpeg-devel] [PATCH v2 00/26] Subtitle Filtering 2022 ffmpegagent
  24 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-14  1:13 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: softworkz

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/webvttenc.c       |  6 ++++++
 tests/ref/fate/sub-webvttenc | 10 +++++-----
 2 files changed, 11 insertions(+), 5 deletions(-)

diff --git a/libavcodec/webvttenc.c b/libavcodec/webvttenc.c
index c0436f5739..48945dcb8e 100644
--- a/libavcodec/webvttenc.c
+++ b/libavcodec/webvttenc.c
@@ -123,6 +123,11 @@ static void webvtt_new_line_cb(void *priv, int forced)
     webvtt_print(priv, "\n");
 }
 
+static void webvtt_hard_space_cb(void *priv)
+{
+    webvtt_print(priv, "&nbsp;");
+}
+
 static void webvtt_style_cb(void *priv, char style, int close)
 {
     if (style == 's') // strikethrough unsupported
@@ -147,6 +152,7 @@ static void webvtt_end_cb(void *priv)
 static const ASSCodesCallbacks webvtt_callbacks = {
     .text             = webvtt_text_cb,
     .new_line         = webvtt_new_line_cb,
+    .hard_space       = webvtt_hard_space_cb,
     .style            = webvtt_style_cb,
     .color            = NULL,
     .font_name        = NULL,
diff --git a/tests/ref/fate/sub-webvttenc b/tests/ref/fate/sub-webvttenc
index f4172dcc84..ee9de2859e 100644
--- a/tests/ref/fate/sub-webvttenc
+++ b/tests/ref/fate/sub-webvttenc
@@ -132,26 +132,26 @@ but show this: {normal text}
 \ N is a forced line break
 \ h is a hard space
 Normal spaces at the start and at the end of the line are trimmed while hard spaces are not trimmed.
-The line will never break automatically right before or after a hard space. :-D
+The&nbsp;line&nbsp;will&nbsp;never&nbsp;break&nbsp;automatically&nbsp;right&nbsp;before&nbsp;or&nbsp;after&nbsp;a&nbsp;hard&nbsp;space.&nbsp;:-D
 
 00:54.501 --> 00:56.500
 
-     A (05 hard spaces followed by a letter)
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;A (05 hard spaces followed by a letter)
 A (Normal  spaces followed by a letter)
 A (No hard spaces followed by a letter)
 
 00:56.501 --> 00:58.500
-     A (05 hard spaces followed by a letter)
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;A (05 hard spaces followed by a letter)
 A (Normal  spaces followed by a letter)
 A (No hard spaces followed by a letter)
 Show this: \TEST and this: \-)
 
 00:58.501 --> 01:00.500
 
-A letter followed by 05 hard spaces: A     
+A letter followed by 05 hard spaces: A&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 A letter followed by normal  spaces: A
 A letter followed by no hard spaces: A
-05 hard  spaces between letters: A     A
+05 hard  spaces between letters: A&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;A
 5 normal spaces between letters: A     A
 
 ^--Forced line break
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH 24/24] doc/APIchanges: update for subtitle filtering changes
  2022-01-14  1:13 [FFmpeg-devel] [PATCH 00/24] Subtitle Filtering 2022 ffmpegagent
                   ` (22 preceding siblings ...)
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 23/24] avcodec/webvttenc: convert hard-space tags to &nbsp; ffmpegagent
@ 2022-01-14  1:13 ` ffmpegagent
  2022-01-20  2:48 ` [FFmpeg-devel] [PATCH v2 00/26] Subtitle Filtering 2022 ffmpegagent
  24 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-14  1:13 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: softworkz

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 doc/APIchanges       | 24 ++++++++++++++++++++++++
 libavcodec/version.h |  2 +-
 libavutil/version.h  |  2 +-
 3 files changed, 26 insertions(+), 2 deletions(-)

diff --git a/doc/APIchanges b/doc/APIchanges
index 8df0364e4c..c8238fb008 100644
--- a/doc/APIchanges
+++ b/doc/APIchanges
@@ -14,6 +14,30 @@ libavutil:     2021-04-27
 
 API changes, most recent first:
 
+2021-12-05 - xxxxxxxxxx - lavc 59.15.100 - avcodec.h
+  Deprecate avcodec_encode_subtitle(), use regular encode api now
+
+2021-12-05 - xxxxxxxxxx - lavc 59.15.100 - codec_desc.h
+  Add avcodec_descriptor_get_subtitle_format()
+
+2021-12-05 - xxxxxxxxxx - lavc 59.15.100 - avcodec.h
+  Deprecate avsubtitle_free()
+  Deprecate avcodec_decode_subtitle2(), use regular decode api now
+
+2021-12-05 - xxxxxxxxxx - lavu 57.11.100 - frame.h
+  Add AVMediaType field to AVFrame
+  Add Fields for carrying subtitle data to AVFrame
+  (subtitle_areas, subtitle_header, subtitle_pts, start/end time, etc.)
+  Add av_frame_get_buffer2() and deprecate av_frame_get_buffer()
+
+2021-12-05 - xxxxxxxxxx - lavu 57.11.100 - subfmt.h
+  Add struct AVSubtitleArea (replaces AVSubtitle)
+  Add av_get_subtitle_fmt_name() and av_get_subtitle_fmt()
+
+2021-12-05 - xxxxxxxxxx - lavu 57.11.100 - subfmt.h
+  Add enum AVSubtitleType (moved from lavc), add new values, deprecate existing
+
+2021-11-xx - xxxxxxxxxx - lavfi 8.19.100 - avfilter.h
 2022-01-04 - 78dc21b123e - lavu 57.16.100 - frame.h
   Add AV_FRAME_DATA_DOVI_METADATA.
 
diff --git a/libavcodec/version.h b/libavcodec/version.h
index a46fb05f1a..b5867ad041 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -28,7 +28,7 @@
 #include "libavutil/version.h"
 
 #define LIBAVCODEC_VERSION_MAJOR  59
-#define LIBAVCODEC_VERSION_MINOR  20
+#define LIBAVCODEC_VERSION_MINOR  21
 #define LIBAVCODEC_VERSION_MICRO 100
 
 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
diff --git a/libavutil/version.h b/libavutil/version.h
index 5bf48f6304..168e24f410 100644
--- a/libavutil/version.h
+++ b/libavutil/version.h
@@ -79,7 +79,7 @@
  */
 
 #define LIBAVUTIL_VERSION_MAJOR  57
-#define LIBAVUTIL_VERSION_MINOR  18
+#define LIBAVUTIL_VERSION_MINOR  19
 #define LIBAVUTIL_VERSION_MICRO 100
 
 #define LIBAVUTIL_VERSION_INT   AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \
-- 
ffmpeg-codebot
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH 21/24] fftools/ffmpeg: Introduce subtitle filtering new frame-based subtitle encoding
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 21/24] fftools/ffmpeg: Introduce subtitle filtering new frame-based subtitle encoding ffmpegagent
@ 2022-01-14 16:52   ` Michael Niedermayer
  2022-01-15  8:36     ` Soft Works
  0 siblings, 1 reply; 217+ messages in thread
From: Michael Niedermayer @ 2022-01-14 16:52 UTC (permalink / raw)
  To: FFmpeg development discussions and patches


[-- Attachment #1.1: Type: text/plain, Size: 2663 bytes --]

On Fri, Jan 14, 2022 at 01:13:30AM +0000, ffmpegagent wrote:
> From: softworkz <softworkz@hotmail.com>
> 
> This commit actually enables subtitle filtering in ffmpeg by
> sending and receiving subtitle frames to and from a filtergraph.
> 
> The heartbeat functionality from the previous sub2video implementation
> is removed and now provided by the 'subfeed' filter.
> The other part of sub2video functionality is retained by
> auto-insertion of the new graphicsub2video filter.
> 
> Justification for changed test refs:
> 
> - sub2video
>   The new results are identical excepting the last frame which
>   is due to the implementation changes
> 
> - sub2video_basic
>   The previous results had some incorrect output because multiple
>   frames had the same dts
>   The non-empty content frames are visually identical, the different
>   CRC is due to the different blending algorithm that is being used.
> 
> - sub2video_time_limited
>   The third frame in the previous ref was a repetition, which doesn't
>   happen anymore with the new subtitle filtering.
> 
> - sub-dvb
>   Running ffprobe -show_frames on the source file shows that there
>   are 7 subtitle frames with 0 rects in the source at the start
>   and 2 at the end. This translates to the 14 and 4 additional
>   entries in the new test results.
> 
> - filter-overlay-dvdsub-2397
>   Overlay results have slightly different CRCs due to different
>   blending implementation
> 
> Signed-off-by: softworkz <softworkz@hotmail.com>
> ---
>  fftools/ffmpeg.c                          |  493 ++++----
>  fftools/ffmpeg.h                          |   13 +-
>  fftools/ffmpeg_filter.c                   |  235 ++--
>  fftools/ffmpeg_hw.c                       |    2 +-
>  fftools/ffmpeg_opt.c                      |    3 +-
>  tests/ref/fate/filter-overlay-dvdsub-2397 |  182 +--
>  tests/ref/fate/sub-dvb                    |  162 +--
>  tests/ref/fate/sub2video                  | 1091 +++++++++++++++++-
>  tests/ref/fate/sub2video_basic            | 1239 +++++++++++++++++++--
>  tests/ref/fate/sub2video_time_limited     |   78 +-
>  10 files changed, 2829 insertions(+), 669 deletions(-)

./ffmpeg -i ~/tickets/153/bbc_small.ts -filter_complex '[0:v][0:s]overlay' -qscale 2  -t 3 f.avi

Press [q] to stop, [?] for help
Auto-inserting subfeed filter
Auto-inserting graphicsub2video filter
Assertion c > 0 failed at libavutil/mathematics.c:60
Aborted (core dumped)


[...]
-- 
Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB

Everything should be made as simple as possible, but not simpler.
-- Albert Einstein

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 195 bytes --]

[-- Attachment #2: Type: text/plain, Size: 251 bytes --]

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH 06/24] avcodec/subtitles: Migrate subtitle encoders to frame-based API
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 06/24] avcodec/subtitles: Migrate subtitle encoders to frame-based API ffmpegagent
@ 2022-01-14 17:22   ` Michael Niedermayer
  2022-01-15  8:03     ` Soft Works
  0 siblings, 1 reply; 217+ messages in thread
From: Michael Niedermayer @ 2022-01-14 17:22 UTC (permalink / raw)
  To: FFmpeg development discussions and patches


[-- Attachment #1.1: Type: text/plain, Size: 3193 bytes --]

On Fri, Jan 14, 2022 at 01:13:15AM +0000, ffmpegagent wrote:
> From: softworkz <softworkz@hotmail.com>
> 
> and provide a compatibility shim for the legacy api
> 
> Signed-off-by: softworkz <softworkz@hotmail.com>
> ---
>  libavcodec/assenc.c        | 189 ++++++++++++++++++++++++++++++-------
>  libavcodec/avcodec.h       |   5 +-
>  libavcodec/dvbsubenc.c     |  96 ++++++++++---------
>  libavcodec/dvdsubenc.c     | 102 ++++++++++++--------
>  libavcodec/encode.c        |  57 ++++++++++-
>  libavcodec/movtextenc.c    | 114 ++++++++++++++++------
>  libavcodec/srtenc.c        | 108 ++++++++++++++-------
>  libavcodec/tests/avcodec.c |   2 -
>  libavcodec/ttmlenc.c       | 101 +++++++++++++++-----
>  libavcodec/webvttenc.c     |  86 ++++++++++++-----
>  libavcodec/xsubenc.c       |  88 ++++++++++-------
>  11 files changed, 685 insertions(+), 263 deletions(-)

is this supposed to pass make fate without the later patches ?

it seems not here:

--- ./tests/ref/fate/sub-webvtt2	2022-01-13 23:24:22.195685643 +0100
+++ tests/data/fate/sub-webvtt2	2022-01-14 18:04:09.528302263 +0100
@@ -11,15 +11,3 @@
 
 [Events]
 Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
-Dialogue: 0,0:00:00.00,0:00:20.00,Default,,0,0,0,,Hi, my name is Fred
-Dialogue: 0,0:00:02.50,0:00:22.50,Default,,0,0,0,,Hi, I’m Bill
-Dialogue: 0,0:00:05.00,0:00:25.00,Default,,0,0,0,,Would you like to get a coffee?
-Dialogue: 0,0:00:07.50,0:00:27.50,Default,,0,0,0,,Sure! I’ve only had one today.
-Dialogue: 0,0:00:10.00,0:00:30.00,Default,,0,0,0,,This is my fourth!
-Dialogue: 0,0:00:12.50,0:00:32.50,Default,,0,0,0,,OK, let’s go.
-Dialogue: 0,0:00:38.00,0:00:43.00,Default,,0,0,0,,I want to 愛あい love you\NThat's not proper English!
-Dialogue: 0,0:00:43.00,0:00:46.00,Default,,0,0,0,,{\i1}キツネ{\i0}じゃない キツネじゃない\N乙女おとめは
-Dialogue: 0,0:00:50.00,0:00:55.00,Default,,0,0,0,,Some time ago in a rather distant place....
-Dialogue: 0,0:00:55.00,0:01:00.00,Default,,0,0,0,,Descending: 123456\NAscending: 123456
-Dialogue: 0,0:01:00.00,0:01:05.00,Default,,0,0,0,,>> Never gonna give you up Never gonna let you down\NNever\hgonna\hrun\haround & desert\hyou
-Dialogue: 0,0:55:00.00,1:00:00.00,Default,,0,0,0,,Transcrit par Célestes™
Test sub-webvtt2 failed. Look at tests/data/fate/sub-webvtt2.err for details.
tests/Makefile:256: recipe for target 'fate-sub-webvtt2' failed
make: *** [fate-sub-webvtt2] Error 139

if you cannot reproduce then say so and ill get e better backtrace

Program received signal SIGSEGV, Segmentation fault.
0x0000000000000000 in ?? ()
(gdb) bt
#0  0x0000000000000000 in  ()
#1  0x0000555555c7d2cf in avcodec_encode_subtitle ()
#2  0x0000555555744daa in transcode_subtitles ()
#3  0x000055555574b9eb in process_input_packet ()
#4  0x000055555574d64d in transcode ()
#5  0x0000555555725ebc in main ()



[...]
-- 
Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB

Take away the freedom of one citizen and you will be jailed, take away
the freedom of all citizens and you will be congratulated by your peers
in Parliament.

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 195 bytes --]

[-- Attachment #2: Type: text/plain, Size: 251 bytes --]

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH 03/24] avcodec/subtitles: Introduce new frame-based subtitle decoding API
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 03/24] avcodec/subtitles: Introduce new frame-based subtitle decoding API ffmpegagent
@ 2022-01-14 17:53   ` Andreas Rheinhardt
  2022-01-15  7:59     ` Soft Works
  0 siblings, 1 reply; 217+ messages in thread
From: Andreas Rheinhardt @ 2022-01-14 17:53 UTC (permalink / raw)
  To: ffmpeg-devel

ffmpegagent:
> From: softworkz <softworkz@hotmail.com>
> 
> - Add avcodec_decode_subtitle3 which takes subtitle frames,
>   serving as compatibility shim to legacy subtitle decoding
> - Add additional methods for conversion between old and new API

This commit message is completely wrong.

> 
> Signed-off-by: softworkz <softworkz@hotmail.com>
> ---
>  libavcodec/avcodec.h    |   8 +-
>  libavcodec/codec_desc.c |  11 +++
>  libavcodec/codec_desc.h |   8 ++
>  libavcodec/decode.c     |  56 ++++++++++--
>  libavcodec/internal.h   |  22 +++++
>  libavcodec/utils.c      | 184 ++++++++++++++++++++++++++++++++++++++++
>  6 files changed, 280 insertions(+), 9 deletions(-)
> 
> diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
> index fe5a83cf85..9d59f6e840 100644
> --- a/libavcodec/avcodec.h
> +++ b/libavcodec/avcodec.h
> @@ -1675,7 +1675,7 @@ typedef struct AVCodecContext {
>  
>      /**
>       * Header containing style information for text subtitles.
> -     * For SUBTITLE_ASS subtitle type, it should contain the whole ASS
> +     * For AV_SUBTITLE_FMT_ASS subtitle type, it should contain the whole ASS
>       * [Script Info] and [V4+ Styles] section, plus the [Events] line and
>       * the Format line following. It shouldn't include any Dialogue line.
>       * - encoding: Set/allocated/freed by user (before avcodec_open2())
> @@ -2415,7 +2415,10 @@ int avcodec_close(AVCodecContext *avctx);
>   * Free all allocated data in the given subtitle struct.
>   *
>   * @param sub AVSubtitle to free.
> + *
> + * @deprecated Use the regular frame based encode and decode APIs instead.
>   */
> +attribute_deprecated
>  void avsubtitle_free(AVSubtitle *sub);
>  
>  /**
> @@ -2508,7 +2511,10 @@ enum AVChromaLocation avcodec_chroma_pos_to_enum(int xpos, int ypos);
>   *                 must be freed with avsubtitle_free if *got_sub_ptr is set.
>   * @param[in,out] got_sub_ptr Zero if no subtitle could be decompressed, otherwise, it is nonzero.
>   * @param[in] avpkt The input AVPacket containing the input buffer.
> + *
> + * @deprecated Use the new decode API (avcodec_send_packet, avcodec_receive_frame) instead.
>   */
> +attribute_deprecated
>  int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub,
>                              int *got_sub_ptr,
>                              AVPacket *avpkt);
> diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
> index 0974ee03de..e48e4532ba 100644
> --- a/libavcodec/codec_desc.c
> +++ b/libavcodec/codec_desc.c
> @@ -3548,3 +3548,14 @@ enum AVMediaType avcodec_get_type(enum AVCodecID codec_id)
>      const AVCodecDescriptor *desc = avcodec_descriptor_get(codec_id);
>      return desc ? desc->type : AVMEDIA_TYPE_UNKNOWN;
>  }
> +
> +enum AVSubtitleType avcodec_descriptor_get_subtitle_format(const AVCodecDescriptor *codec_descriptor)
> +{
> +    if(codec_descriptor->props & AV_CODEC_PROP_BITMAP_SUB)
> +        return AV_SUBTITLE_FMT_BITMAP;
> +
> +    if(codec_descriptor->props & AV_CODEC_PROP_TEXT_SUB)
> +        return AV_SUBTITLE_FMT_ASS;
> +
> +    return AV_SUBTITLE_FMT_UNKNOWN;
> +}
> diff --git a/libavcodec/codec_desc.h b/libavcodec/codec_desc.h
> index 126b52df47..ba68d24e0e 100644
> --- a/libavcodec/codec_desc.h
> +++ b/libavcodec/codec_desc.h
> @@ -121,6 +121,14 @@ const AVCodecDescriptor *avcodec_descriptor_next(const AVCodecDescriptor *prev);
>   */
>  const AVCodecDescriptor *avcodec_descriptor_get_by_name(const char *name);
>  
> +/**
> + * Return subtitle format from a codec descriptor
> + *
> + * @param codec_descriptor codec descriptor
> + * @return                 the subtitle type (e.g. bitmap, text)
> + */
> +enum AVSubtitleType avcodec_descriptor_get_subtitle_format(const AVCodecDescriptor *codec_descriptor);
> +
>  /**
>   * @}
>   */
> diff --git a/libavcodec/decode.c b/libavcodec/decode.c
> index 0912f86a14..ab8a6ea6ff 100644
> --- a/libavcodec/decode.c
> +++ b/libavcodec/decode.c
> @@ -576,6 +576,39 @@ static int decode_receive_frame_internal(AVCodecContext *avctx, AVFrame *frame)
>      return ret;
>  }
>  
> +static int decode_subtitle2_priv(AVCodecContext *avctx, AVSubtitle *sub,
> +                                 int *got_sub_ptr, AVPacket *avpkt);
> +
> +static int decode_subtitle_shim(AVCodecContext *avctx, AVFrame *frame, AVPacket *avpkt)
> +{
> +    int ret, got_sub_ptr = 0;
> +    AVSubtitle subtitle = { 0 };
> +
> +    if (frame->buf[0])
> +        return AVERROR(EAGAIN);
> +
> +    av_frame_unref(frame);
> +
> +    ret = decode_subtitle2_priv(avctx, &subtitle, &got_sub_ptr, avpkt);
> +
> +    if (ret >= 0 && got_sub_ptr) {
> +        frame->type = AVMEDIA_TYPE_SUBTITLE;
> +        frame->format = subtitle.format;
> +        ret = av_frame_get_buffer2(frame, 0);
> +
> +        if (ret >= 0)
> +            ret = ff_frame_put_subtitle(frame, &subtitle);
> +
> +        frame->width = avctx->width;
> +        frame->height = avctx->height;
> +        frame->pkt_dts = avpkt->dts;
> +    }
> +
> +    avsubtitle_free(&subtitle);
> +
> +    return ret;
> +}
> +
>  int attribute_align_arg avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt)
>  {
>      AVCodecInternal *avci = avctx->internal;
> @@ -590,6 +623,9 @@ int attribute_align_arg avcodec_send_packet(AVCodecContext *avctx, const AVPacke
>      if (avpkt && !avpkt->size && avpkt->data)
>          return AVERROR(EINVAL);
>  
> +    if (avctx->codec_type == AVMEDIA_TYPE_SUBTITLE)
> +        return decode_subtitle_shim(avctx, avci->buffer_frame, avpkt);

With the avcodec_send_packet/avcodec_receive_frame API the user is
supposed to send the packet only once (unless we have the EAGAIN case).
Libavcodec takes care to buffer the packet in case this is necessary
even if the packet leads to multiple returned frames. In contrast to
this, the old API required the user to send the packet again, but with
data offsetted and size decremented to reflect this.
Some decoders (at least pgssubdec*) seem to be designed to only consume
partial packets; if that is the case, then your approach above won't
work and you will need to buffer the packet. In other words: The
subtitle decoding codepath will become more like the codepaths for audio
and video.
The old API is btw even more broken, in particular in case the subtitles
are recoded: Typically the size of the recoded UTF-8 is returned which
is an API abuse on part of lavc. See
https://github.com/mkver/FFmpeg/commit/ba1564c532654888015d67b70bf73d117c2d9f75
(part of https://github.com/mkver/FFmpeg/commits/subs) for more.
*: It seems that libzvbi currently always fully consumes the AVPacket;
yet looking at the history I don't know whether this is actually
intended and correct.

> +
>      av_packet_unref(avci->buffer_pkt);
>      if (avpkt && (avpkt->data || avpkt->side_data_elems)) {
>          ret = av_packet_ref(avci->buffer_pkt, avpkt);
> @@ -651,7 +687,9 @@ int attribute_align_arg avcodec_receive_frame(AVCodecContext *avctx, AVFrame *fr
>  
>      if (avci->buffer_frame->buf[0]) {
>          av_frame_move_ref(frame, avci->buffer_frame);
> -    } else {
> +    } else if (avctx->codec_type == AVMEDIA_TYPE_SUBTITLE)
> +        return AVERROR(EAGAIN);
> +    else {
>          ret = decode_receive_frame_internal(avctx, frame);
>          if (ret < 0)
>              return ret;
> @@ -802,9 +840,8 @@ static int utf8_check(const uint8_t *str)
>      return 1;
>  }
>  
> -int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub,
> -                             int *got_sub_ptr,
> -                             AVPacket *avpkt)
> +static int decode_subtitle2_priv(AVCodecContext *avctx, AVSubtitle *sub,
> +                             int *got_sub_ptr, AVPacket *avpkt)
>  {
>      int ret = 0;
>  
> @@ -850,10 +887,7 @@ int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub,
>                                                   avctx->pkt_timebase, ms);
>          }
>  
> -        if (avctx->codec_descriptor->props & AV_CODEC_PROP_BITMAP_SUB)
> -            sub->format = 0;
> -        else if (avctx->codec_descriptor->props & AV_CODEC_PROP_TEXT_SUB)
> -            sub->format = 1;
> +        sub->format = avcodec_descriptor_get_subtitle_format(avctx->codec_descriptor);
>  
>          for (unsigned i = 0; i < sub->num_rects; i++) {
>              if (avctx->sub_charenc_mode != FF_SUB_CHARENC_MODE_IGNORE &&
> @@ -874,6 +908,12 @@ int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub,
>      return ret;
>  }
>  
> +int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub,
> +                             int *got_sub_ptr, AVPacket *avpkt)
> +{
> +    return decode_subtitle2_priv(avctx, sub, got_sub_ptr, avpkt);
> +}
> +
>  enum AVPixelFormat avcodec_default_get_format(struct AVCodecContext *avctx,
>                                                const enum AVPixelFormat *fmt)
>  {
> diff --git a/libavcodec/internal.h b/libavcodec/internal.h
> index 72ca1553f6..ff63974f7f 100644
> --- a/libavcodec/internal.h
> +++ b/libavcodec/internal.h
> @@ -363,4 +363,26 @@ int ff_int_from_list_or_default(void *ctx, const char * val_name, int val,
>  
>  void ff_dvdsub_parse_palette(uint32_t *palette, const char *p);
>  
> +/**
> + * Copies subtitle data from AVSubtitle to AVFrame.
> + *
> + * @deprecated This is a compatibility method for interoperability with
> + * the legacy subtitle API.
> + */
> +int ff_frame_put_subtitle(AVFrame* frame, const AVSubtitle* sub);
> +
> +/**
> + * Copies subtitle data from AVFrame to AVSubtitle.
> + *
> + * @deprecated This is a compatibility method for interoperability with
> + * the legacy subtitle API.
> + */
> +int ff_frame_get_subtitle(AVSubtitle* sub, AVFrame* frame);
> +
> +#if defined(_WIN32) && CONFIG_SHARED && !defined(BUILDING_avcodec)
> +#    define av_export_avcodec __declspec(dllimport)
> +#else
> +#    define av_export_avcodec
> +#endif

This seems like a rebase problem.

> +
>  #endif /* AVCODEC_INTERNAL_H */

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH 03/24] avcodec/subtitles: Introduce new frame-based subtitle decoding API
  2022-01-14 17:53   ` Andreas Rheinhardt
@ 2022-01-15  7:59     ` Soft Works
  0 siblings, 0 replies; 217+ messages in thread
From: Soft Works @ 2022-01-15  7:59 UTC (permalink / raw)
  To: FFmpeg development discussions and patches



> -----Original Message-----
> From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of Andreas
> Rheinhardt
> Sent: Friday, January 14, 2022 6:54 PM
> To: ffmpeg-devel@ffmpeg.org
> Subject: Re: [FFmpeg-devel] [PATCH 03/24] avcodec/subtitles: Introduce new
> frame-based subtitle decoding API
> 
> ffmpegagent:
> > From: softworkz <softworkz@hotmail.com>
> >
> > - Add avcodec_decode_subtitle3 which takes subtitle frames,
> >   serving as compatibility shim to legacy subtitle decoding
> > - Add additional methods for conversion between old and new API
> 
> This commit message is completely wrong.
> 
> >
> > Signed-off-by: softworkz <softworkz@hotmail.com>
> > ---
> >  libavcodec/avcodec.h    |   8 +-
> >  libavcodec/codec_desc.c |  11 +++
> >  libavcodec/codec_desc.h |   8 ++
> >  libavcodec/decode.c     |  56 ++++++++++--
> >  libavcodec/internal.h   |  22 +++++
> >  libavcodec/utils.c      | 184 ++++++++++++++++++++++++++++++++++++++++
> >  6 files changed, 280 insertions(+), 9 deletions(-)
> >
> > diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
> > index fe5a83cf85..9d59f6e840 100644
> > --- a/libavcodec/avcodec.h
> > +++ b/libavcodec/avcodec.h
> > @@ -1675,7 +1675,7 @@ typedef struct AVCodecContext {
> >
> >      /**
> >       * Header containing style information for text subtitles.
> > -     * For SUBTITLE_ASS subtitle type, it should contain the whole ASS
> > +     * For AV_SUBTITLE_FMT_ASS subtitle type, it should contain the whole
> ASS
> >       * [Script Info] and [V4+ Styles] section, plus the [Events] line and
> >       * the Format line following. It shouldn't include any Dialogue line.
> >       * - encoding: Set/allocated/freed by user (before avcodec_open2())
> > @@ -2415,7 +2415,10 @@ int avcodec_close(AVCodecContext *avctx);
> >   * Free all allocated data in the given subtitle struct.
> >   *
> >   * @param sub AVSubtitle to free.
> > + *
> > + * @deprecated Use the regular frame based encode and decode APIs instead.
> >   */
> > +attribute_deprecated
> >  void avsubtitle_free(AVSubtitle *sub);
> >
> >  /**
> > @@ -2508,7 +2511,10 @@ enum AVChromaLocation avcodec_chroma_pos_to_enum(int
> xpos, int ypos);
> >   *                 must be freed with avsubtitle_free if *got_sub_ptr is
> set.
> >   * @param[in,out] got_sub_ptr Zero if no subtitle could be decompressed,
> otherwise, it is nonzero.
> >   * @param[in] avpkt The input AVPacket containing the input buffer.
> > + *
> > + * @deprecated Use the new decode API (avcodec_send_packet,
> avcodec_receive_frame) instead.
> >   */
> > +attribute_deprecated
> >  int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub,
> >                              int *got_sub_ptr,
> >                              AVPacket *avpkt);
> > diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
> > index 0974ee03de..e48e4532ba 100644
> > --- a/libavcodec/codec_desc.c
> > +++ b/libavcodec/codec_desc.c
> > @@ -3548,3 +3548,14 @@ enum AVMediaType avcodec_get_type(enum AVCodecID
> codec_id)
> >      const AVCodecDescriptor *desc = avcodec_descriptor_get(codec_id);
> >      return desc ? desc->type : AVMEDIA_TYPE_UNKNOWN;
> >  }
> > +
> > +enum AVSubtitleType avcodec_descriptor_get_subtitle_format(const
> AVCodecDescriptor *codec_descriptor)
> > +{
> > +    if(codec_descriptor->props & AV_CODEC_PROP_BITMAP_SUB)
> > +        return AV_SUBTITLE_FMT_BITMAP;
> > +
> > +    if(codec_descriptor->props & AV_CODEC_PROP_TEXT_SUB)
> > +        return AV_SUBTITLE_FMT_ASS;
> > +
> > +    return AV_SUBTITLE_FMT_UNKNOWN;
> > +}
> > diff --git a/libavcodec/codec_desc.h b/libavcodec/codec_desc.h
> > index 126b52df47..ba68d24e0e 100644
> > --- a/libavcodec/codec_desc.h
> > +++ b/libavcodec/codec_desc.h
> > @@ -121,6 +121,14 @@ const AVCodecDescriptor *avcodec_descriptor_next(const
> AVCodecDescriptor *prev);
> >   */
> >  const AVCodecDescriptor *avcodec_descriptor_get_by_name(const char *name);
> >
> > +/**
> > + * Return subtitle format from a codec descriptor
> > + *
> > + * @param codec_descriptor codec descriptor
> > + * @return                 the subtitle type (e.g. bitmap, text)
> > + */
> > +enum AVSubtitleType avcodec_descriptor_get_subtitle_format(const
> AVCodecDescriptor *codec_descriptor);
> > +
> >  /**
> >   * @}
> >   */
> > diff --git a/libavcodec/decode.c b/libavcodec/decode.c
> > index 0912f86a14..ab8a6ea6ff 100644
> > --- a/libavcodec/decode.c
> > +++ b/libavcodec/decode.c
> > @@ -576,6 +576,39 @@ static int
> decode_receive_frame_internal(AVCodecContext *avctx, AVFrame *frame)
> >      return ret;
> >  }
> >
> > +static int decode_subtitle2_priv(AVCodecContext *avctx, AVSubtitle *sub,
> > +                                 int *got_sub_ptr, AVPacket *avpkt);
> > +
> > +static int decode_subtitle_shim(AVCodecContext *avctx, AVFrame *frame,
> AVPacket *avpkt)
> > +{
> > +    int ret, got_sub_ptr = 0;
> > +    AVSubtitle subtitle = { 0 };
> > +
> > +    if (frame->buf[0])
> > +        return AVERROR(EAGAIN);
> > +
> > +    av_frame_unref(frame);
> > +
> > +    ret = decode_subtitle2_priv(avctx, &subtitle, &got_sub_ptr, avpkt);
> > +
> > +    if (ret >= 0 && got_sub_ptr) {
> > +        frame->type = AVMEDIA_TYPE_SUBTITLE;
> > +        frame->format = subtitle.format;
> > +        ret = av_frame_get_buffer2(frame, 0);
> > +
> > +        if (ret >= 0)
> > +            ret = ff_frame_put_subtitle(frame, &subtitle);
> > +
> > +        frame->width = avctx->width;
> > +        frame->height = avctx->height;
> > +        frame->pkt_dts = avpkt->dts;
> > +    }
> > +
> > +    avsubtitle_free(&subtitle);
> > +
> > +    return ret;
> > +}
> > +
> >  int attribute_align_arg avcodec_send_packet(AVCodecContext *avctx, const
> AVPacket *avpkt)
> >  {
> >      AVCodecInternal *avci = avctx->internal;
> > @@ -590,6 +623,9 @@ int attribute_align_arg
> avcodec_send_packet(AVCodecContext *avctx, const AVPacke
> >      if (avpkt && !avpkt->size && avpkt->data)
> >          return AVERROR(EINVAL);
> >
> > +    if (avctx->codec_type == AVMEDIA_TYPE_SUBTITLE)
> > +        return decode_subtitle_shim(avctx, avci->buffer_frame, avpkt);
> 
> With the avcodec_send_packet/avcodec_receive_frame API the user is
> supposed to send the packet only once (unless we have the EAGAIN case).
> Libavcodec takes care to buffer the packet in case this is necessary
> even if the packet leads to multiple returned frames. In contrast to
> this, the old API required the user to send the packet again, but with
> data offsetted and size decremented to reflect this.

This is only meant to be an interim solution until the decoders are
migrated to the frame based api.

> Some decoders (at least pgssubdec*) seem to be designed to only consume
> partial packets; if that is the case, then your approach above won't
> work and you will need to buffer the packet. In other words: The
> subtitle decoding codepath will become more like the codepaths for audio
> and video.

I (and many others) have run dozens of files with PGS subs with this code.
So, your assumption in the referenced commit (should not adversely affect the
decoder) is most likely right. 

> The old API is btw even more broken, in particular in case the subtitles
> are recoded: Typically the size of the recoded UTF-8 is returned which
> is an API abuse on part of lavc. See
> https://github.com/mkver/FFmpeg/commit/ba1564c532654888015d67b70bf73d117c2d9f
> 75
> (part of https://github.com/mkver/FFmpeg/commits/subs) for more.
> *: It seems that libzvbi currently always fully consumes the AVPacket;
> yet looking at the history I don't know whether this is actually
> intended and correct.

I would suggest we focus on the future implementation (after migration) and
try to get that right.



> 
> > +
> >      av_packet_unref(avci->buffer_pkt);
> >      if (avpkt && (avpkt->data || avpkt->side_data_elems)) {
> >          ret = av_packet_ref(avci->buffer_pkt, avpkt);
> > @@ -651,7 +687,9 @@ int attribute_align_arg
> avcodec_receive_frame(AVCodecContext *avctx, AVFrame *fr
> >
> >      if (avci->buffer_frame->buf[0]) {
> >          av_frame_move_ref(frame, avci->buffer_frame);
> > -    } else {
> > +    } else if (avctx->codec_type == AVMEDIA_TYPE_SUBTITLE)
> > +        return AVERROR(EAGAIN);
> > +    else {
> >          ret = decode_receive_frame_internal(avctx, frame);
> >          if (ret < 0)
> >              return ret;
> > @@ -802,9 +840,8 @@ static int utf8_check(const uint8_t *str)
> >      return 1;
> >  }
> >
> > -int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub,
> > -                             int *got_sub_ptr,
> > -                             AVPacket *avpkt)
> > +static int decode_subtitle2_priv(AVCodecContext *avctx, AVSubtitle *sub,
> > +                             int *got_sub_ptr, AVPacket *avpkt)
> >  {
> >      int ret = 0;
> >
> > @@ -850,10 +887,7 @@ int avcodec_decode_subtitle2(AVCodecContext *avctx,
> AVSubtitle *sub,
> >                                                   avctx->pkt_timebase, ms);
> >          }
> >
> > -        if (avctx->codec_descriptor->props & AV_CODEC_PROP_BITMAP_SUB)
> > -            sub->format = 0;
> > -        else if (avctx->codec_descriptor->props & AV_CODEC_PROP_TEXT_SUB)
> > -            sub->format = 1;
> > +        sub->format = avcodec_descriptor_get_subtitle_format(avctx-
> >codec_descriptor);
> >
> >          for (unsigned i = 0; i < sub->num_rects; i++) {
> >              if (avctx->sub_charenc_mode != FF_SUB_CHARENC_MODE_IGNORE &&
> > @@ -874,6 +908,12 @@ int avcodec_decode_subtitle2(AVCodecContext *avctx,
> AVSubtitle *sub,
> >      return ret;
> >  }
> >
> > +int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub,
> > +                             int *got_sub_ptr, AVPacket *avpkt)
> > +{
> > +    return decode_subtitle2_priv(avctx, sub, got_sub_ptr, avpkt);
> > +}
> > +
> >  enum AVPixelFormat avcodec_default_get_format(struct AVCodecContext
> *avctx,
> >                                                const enum AVPixelFormat
> *fmt)
> >  {
> > diff --git a/libavcodec/internal.h b/libavcodec/internal.h
> > index 72ca1553f6..ff63974f7f 100644
> > --- a/libavcodec/internal.h
> > +++ b/libavcodec/internal.h
> > @@ -363,4 +363,26 @@ int ff_int_from_list_or_default(void *ctx, const char
> * val_name, int val,
> >
> >  void ff_dvdsub_parse_palette(uint32_t *palette, const char *p);
> >
> > +/**
> > + * Copies subtitle data from AVSubtitle to AVFrame.
> > + *
> > + * @deprecated This is a compatibility method for interoperability with
> > + * the legacy subtitle API.
> > + */
> > +int ff_frame_put_subtitle(AVFrame* frame, const AVSubtitle* sub);
> > +
> > +/**
> > + * Copies subtitle data from AVFrame to AVSubtitle.
> > + *
> > + * @deprecated This is a compatibility method for interoperability with
> > + * the legacy subtitle API.
> > + */
> > +int ff_frame_get_subtitle(AVSubtitle* sub, AVFrame* frame);
> > +
> > +#if defined(_WIN32) && CONFIG_SHARED && !defined(BUILDING_avcodec)
> > +#    define av_export_avcodec __declspec(dllimport)
> > +#else
> > +#    define av_export_avcodec
> > +#endif
> 
> This seems like a rebase problem.
> 
> > +
> >  #endif /* AVCODEC_INTERNAL_H */
> 

Yup, seems so - thanks.

sw
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH 06/24] avcodec/subtitles: Migrate subtitle encoders to frame-based API
  2022-01-14 17:22   ` Michael Niedermayer
@ 2022-01-15  8:03     ` Soft Works
  0 siblings, 0 replies; 217+ messages in thread
From: Soft Works @ 2022-01-15  8:03 UTC (permalink / raw)
  To: FFmpeg development discussions and patches



> -----Original Message-----
> From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of Michael
> Niedermayer
> Sent: Friday, January 14, 2022 6:22 PM
> To: FFmpeg development discussions and patches <ffmpeg-devel@ffmpeg.org>
> Subject: Re: [FFmpeg-devel] [PATCH 06/24] avcodec/subtitles: Migrate subtitle
> encoders to frame-based API
> 
> On Fri, Jan 14, 2022 at 01:13:15AM +0000, ffmpegagent wrote:
> > From: softworkz <softworkz@hotmail.com>
> >
> > and provide a compatibility shim for the legacy api
> >
> > Signed-off-by: softworkz <softworkz@hotmail.com>
> > ---
> >  libavcodec/assenc.c        | 189 ++++++++++++++++++++++++++++++-------
> >  libavcodec/avcodec.h       |   5 +-
> >  libavcodec/dvbsubenc.c     |  96 ++++++++++---------
> >  libavcodec/dvdsubenc.c     | 102 ++++++++++++--------
> >  libavcodec/encode.c        |  57 ++++++++++-
> >  libavcodec/movtextenc.c    | 114 ++++++++++++++++------
> >  libavcodec/srtenc.c        | 108 ++++++++++++++-------
> >  libavcodec/tests/avcodec.c |   2 -
> >  libavcodec/ttmlenc.c       | 101 +++++++++++++++-----
> >  libavcodec/webvttenc.c     |  86 ++++++++++++-----
> >  libavcodec/xsubenc.c       |  88 ++++++++++-------
> >  11 files changed, 685 insertions(+), 263 deletions(-)
> 
> is this supposed to pass make fate without the later patches ?

Yea, I've seen it on Patchwork. I forgot to check the individual 
commits. They need some reordering.

Thanks,
softworkz
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH 21/24] fftools/ffmpeg: Introduce subtitle filtering new frame-based subtitle encoding
  2022-01-14 16:52   ` Michael Niedermayer
@ 2022-01-15  8:36     ` Soft Works
  0 siblings, 0 replies; 217+ messages in thread
From: Soft Works @ 2022-01-15  8:36 UTC (permalink / raw)
  To: FFmpeg development discussions and patches



> -----Original Message-----
> From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of Michael
> Niedermayer
> Sent: Friday, January 14, 2022 5:53 PM
> To: FFmpeg development discussions and patches <ffmpeg-devel@ffmpeg.org>
> Subject: Re: [FFmpeg-devel] [PATCH 21/24] fftools/ffmpeg: Introduce subtitle
> filtering new frame-based subtitle encoding
> 
> On Fri, Jan 14, 2022 at 01:13:30AM +0000, ffmpegagent wrote:
> > From: softworkz <softworkz@hotmail.com>
> >
> > This commit actually enables subtitle filtering in ffmpeg by
> > sending and receiving subtitle frames to and from a filtergraph.
> >
> > The heartbeat functionality from the previous sub2video implementation
> > is removed and now provided by the 'subfeed' filter.
> > The other part of sub2video functionality is retained by
> > auto-insertion of the new graphicsub2video filter.
> >
> > Justification for changed test refs:
> >
> > - sub2video
> >   The new results are identical excepting the last frame which
> >   is due to the implementation changes
> >
> > - sub2video_basic
> >   The previous results had some incorrect output because multiple
> >   frames had the same dts
> >   The non-empty content frames are visually identical, the different
> >   CRC is due to the different blending algorithm that is being used.
> >
> > - sub2video_time_limited
> >   The third frame in the previous ref was a repetition, which doesn't
> >   happen anymore with the new subtitle filtering.
> >
> > - sub-dvb
> >   Running ffprobe -show_frames on the source file shows that there
> >   are 7 subtitle frames with 0 rects in the source at the start
> >   and 2 at the end. This translates to the 14 and 4 additional
> >   entries in the new test results.
> >
> > - filter-overlay-dvdsub-2397
> >   Overlay results have slightly different CRCs due to different
> >   blending implementation
> >
> > Signed-off-by: softworkz <softworkz@hotmail.com>
> > ---
> >  fftools/ffmpeg.c                          |  493 ++++----
> >  fftools/ffmpeg.h                          |   13 +-
> >  fftools/ffmpeg_filter.c                   |  235 ++--
> >  fftools/ffmpeg_hw.c                       |    2 +-
> >  fftools/ffmpeg_opt.c                      |    3 +-
> >  tests/ref/fate/filter-overlay-dvdsub-2397 |  182 +--
> >  tests/ref/fate/sub-dvb                    |  162 +--
> >  tests/ref/fate/sub2video                  | 1091 +++++++++++++++++-
> >  tests/ref/fate/sub2video_basic            | 1239 +++++++++++++++++++--
> >  tests/ref/fate/sub2video_time_limited     |   78 +-
> >  10 files changed, 2829 insertions(+), 669 deletions(-)
> 
> ./ffmpeg -i ~/tickets/153/bbc_small.ts -filter_complex '[0:v][0:s]overlay' -
> qscale 2  -t 3 f.avi
> 
> Press [q] to stop, [?] for help
> Auto-inserting subfeed filter
> Auto-inserting graphicsub2video filter
> Assertion c > 0 failed at libavutil/mathematics.c:60
> Aborted (core dumped)

Thanks for testing.

With the new update also incorporating the final step (complete removal
of heartbeat code), it is unfortunately invalidating the previous test
results. The new version is providing the subfeed filter as a replacement
for the heartbeat functionality, which is working somewhat differently,
and even more tests (of those you are running) won't have bitexact results.
In those cases a visual comparison will be required.

Anyway, the previous results were erroneous in several ways, so it cannot 
be seen as a straight reference. The individual results need to be evaluated
individually in case there are significant differences.


Thanks,
softworkz





_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v2 00/26] Subtitle Filtering 2022
  2022-01-14  1:13 [FFmpeg-devel] [PATCH 00/24] Subtitle Filtering 2022 ffmpegagent
                   ` (23 preceding siblings ...)
  2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 24/24] doc/APIchanges: update for subtitle filtering changes ffmpegagent
@ 2022-01-20  2:48 ` ffmpegagent
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 01/26] avcodec, avutil: Move enum AVSubtitleType to avutil, add new and deprecate old values ffmpegagent
                     ` (26 more replies)
  24 siblings, 27 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  2:48 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt


Subtitle Filtering 2022
=======================

This is a substantial update to the earlier subtitle filtering patch series.
A primary goal has been to address others' concerns as much as possible on
one side and to provide more clarity and control over the way things are
working. Clarity is is specifically important to allow for a better
understanding of the need for a subtitle start pts value that can be
different from the frame's pts value. This is done by refactoring the
subtitle timing fields in AVFrame, adding a frame field to indicate repeated
subtitle frames, and finally the full removal of the heartbeat
functionality, replaced by a new 'subfeed' filter that provides different
modes for arbitrating subtitle frames in a filter graph. Finally, each
subtitle filter's documentation has been amended by a section describing the
filter's timeline behavior (in v3 update).

The update also includes major improvements to graphicsub2text and lots of
other details.

Versioning is restarting at v1 due to the new submission procedure.


Changes in v2
=============

 * added .gitattributes file to enforce binary diffs for the test refs that
   cannot be applied when being sent via e-mail
 * perform filter graph re-init due to subtitle "frame size" change only
   when the size was unknown before and not set via -canvas_size
 * overlaytextsubs: Make sure to request frames on the subtitle input
 * avfilter/splitcc: Start parsing cc data on key frames only
 * avcodec/webvttenc: Don't encode ass drawing codes and empty lines
 * stripstyles: fix mem leak
 * gs2t: improve color detection
 * gs2t: empty frames must not be skipped
 * subfeed: fix name
 * textmod: preserve margins
 * added .gitattributes file to enforce binary diffs for the test refs that
   cannot be applied when being sent via e-mail
 * perform filter graph re-init due to subtitle "frame size" change only
   when the size was unknown before and not set via -canvas_size
 * avcodec/dvbsubdec: Fix conditions for fallback to default resolution
 * Made changes suggested by Andreas
 * Fixed failing command line reported by Michael

Changes from previous version v24:


AVFrame
=======

 * Removed sub_start_time The start time is now added to the subtitle
   start_pts during decoding The sub_end_time field is adjusted accordingly
 * Renamed sub_end_time to duration which it is effectively after removing
   the start_time
 * Added a sub-struct 'subtitle_timing' to av frame Contains subtitle_pts
   renamed to 'subtitle_timing.start_pts' and 'subtitle_timing.duration'
 * Change both fields to (fixed) time_base AV_TIMEBASE
 * add repeat_sub field provides a clear indication whether a subtitle frame
   is an actual subtitle event or a repeated subtitle frame in a filter
   graph


Heartbeat Removal
=================

 * completely removed the earlier heartbeat implementation
 * filtering arbitration is now implemented in a new filter: 'subfeed'
 * subfeed will be auto-inserted for compatiblity with sub2video command
   lines
 * the new behavior is not exactly identical to the earlier behavior, but it
   basically allows to achieve the same results


New 'subfeed' Filter
====================

 * a versatile filter for solving all kinds of problems with subtile frame
   flow in filter graphs
 * Can be inserted at any position in a graph
 * Auto-inserted for sub2video command lines (in repeat-mode)
 * Allows duration fixup delay input frames with unknown duration and infer
   duration from start of subsequent frame
 * Provides multiple modes of operation:
   * repeat mode (default) Queues input frames Outputs frames at a fixed
     (configurable) rate Either sends a matching input frame (repeatedly) or
     empty frames otherwise
   * scatter mode similar to repeat mode, but splits input frames by
     duration into small segments with same content
   * forward mode No fixed output rate Useful in combination with duration
     fixup or overlap fixup


ffmpeg Tool Changes
===================

 * delay subtitle output stream initialization (like for audio and video)
   This is needed for example when a format header depends on having
   received an initial frame to derive certain header values from
 * decoding: set subtitle frame size from decoding context
 * re-init graph when subtitle size changes
 * always insert subscale filter for sub2video command lines (to ensure
   correct scaling)


Subtitle Encoding
=================

 * ignore repeated frames for encoding based on repeat_sub field in AVFrame
 * support multi-area encoding for text subtitles Subtitle OCR can create
   multiple areas at different positions. Previously, the texts were always
   squashed into a single area ('subtitle rect'), which was not ideal.
   Multiple text areas are now generally supported:
   * ASS Encoder Changed to use the 'receive_packet' encoding API A single
     frame with multiple text areas will create multiple packets now
   * All other text subtitle encoders A newline is inserted between the text
     from multiple areas


graphicsub2text (OCR)
=====================

 * enhanced preprocessing
   * using elbg algorithm for color quantization
   * detection and removal of text outlines
   * map-based identification of colors per word (text, outline, background)
 * add option for duration fixup
 * add option to dump preprocessing bitmaps
 * Recognize formatting and apply as ASS inline styles
   * per word(!)
   * paragraph alignment
   * positioning
   * font names
   * font size
   * font style (italic, underline, bold)
   * text color, outline color


Other Filter Changes
====================

 * all: Make sure to forward all link properties (time base, frame rate, w,
   h) where appropriate
 * overlaytextsubs: request frames on the subtitle input
 * overlaytextsubs: disable read-order checking
 * overlaytextsubs: improve implementation of render_latest_only
 * overlaytextsubs: ensure equal in/out video formats
 * splitcc: derive framerate from realtime_latency
 * graphicsub2video: implement caching of converted frames
 * graphicsub2video: use 1x1 output frame size as long as subtitle size is
   unknown (0x0)

Plus a dozen of things I forgot..

softworkz (26):
  avcodec,avutil: Move enum AVSubtitleType to avutil, add new and
    deprecate old values
  avutil/frame: Prepare AVFrame for subtitle handling
  avcodec/subtitles: Introduce new frame-based subtitle decoding API
  avfilter/subtitles: Update vf_subtitles to use new decoding api
  avcodec,avutil: Move ass helper functions to avutil as avpriv_ and
    extend ass dialog parsing
  avcodec/subtitles: Replace deprecated enum values
  fftools/play,probe: Adjust for subtitle changes
  avfilter/subtitles: Add subtitles.c for subtitle frame allocation
  avfilter/avfilter: Handle subtitle frames
  avfilter/avfilter: Fix hardcoded input index
  avfilter/sbuffer: Add sbuffersrc and sbuffersink filters
  avfilter/overlaygraphicsubs: Add overlaygraphicsubs and
    graphicsub2video filters
  avfilter/overlaytextsubs: Add overlaytextsubs and textsubs2video
    filters
  avfilter/textmod: Add textmod, censor and show_speaker filters
  avfilter/stripstyles: Add stripstyles filter
  avfilter/splitcc: Add splitcc filter for closed caption handling
  avfilter/graphicsub2text: Add new graphicsub2text filter (OCR)
  avfilter/subscale: Add filter for scaling and/or re-arranging
    graphical subtitles
  avfilter/subfeed: add subtitle feed filter
  avcodec/subtitles: Migrate subtitle encoders to frame-based API
  fftools/ffmpeg: Introduce subtitle filtering and new frame-based
    subtitle encoding
  avutil/ass_split: Add parsing of hard-space tags (\h)
  avcodec/webvttenc: convert hard-space tags to &nbsp;
  doc/APIchanges: update for subtitle filtering changes
  avcodec/webvttenc: Don't encode drawing codes and empty lines
  avcodec/dvbsubdec: Fix conditions for fallback to default resolution

 configure                                     |    7 +-
 doc/APIchanges                                |   24 +
 doc/filters.texi                              |  756 ++++++++++
 fftools/ffmpeg.c                              |  501 +++----
 fftools/ffmpeg.h                              |   13 +-
 fftools/ffmpeg_filter.c                       |  235 +++-
 fftools/ffmpeg_hw.c                           |    2 +-
 fftools/ffmpeg_opt.c                          |    3 +-
 fftools/ffplay.c                              |  102 +-
 fftools/ffprobe.c                             |   47 +-
 libavcodec/Makefile                           |   56 +-
 libavcodec/ass.h                              |  144 +-
 libavcodec/assdec.c                           |    4 +-
 libavcodec/assenc.c                           |  191 ++-
 libavcodec/avcodec.h                          |   32 +-
 libavcodec/ccaption_dec.c                     |   19 +-
 libavcodec/codec_desc.c                       |   11 +
 libavcodec/codec_desc.h                       |    8 +
 libavcodec/decode.c                           |   60 +-
 libavcodec/dvbsubdec.c                        |   53 +-
 libavcodec/dvbsubenc.c                        |   96 +-
 libavcodec/dvdsubdec.c                        |    2 +-
 libavcodec/dvdsubenc.c                        |  102 +-
 libavcodec/encode.c                           |   61 +-
 libavcodec/internal.h                         |   16 +
 libavcodec/jacosubdec.c                       |    2 +-
 libavcodec/libaribb24.c                       |    2 +-
 libavcodec/libzvbi-teletextdec.c              |   14 +-
 libavcodec/microdvddec.c                      |    7 +-
 libavcodec/movtextdec.c                       |    3 +-
 libavcodec/movtextenc.c                       |  126 +-
 libavcodec/mpl2dec.c                          |    2 +-
 libavcodec/pgssubdec.c                        |    2 +-
 libavcodec/realtextdec.c                      |    2 +-
 libavcodec/samidec.c                          |    2 +-
 libavcodec/srtdec.c                           |    2 +-
 libavcodec/srtenc.c                           |  116 +-
 libavcodec/subviewerdec.c                     |    2 +-
 libavcodec/tests/avcodec.c                    |    2 -
 libavcodec/textdec.c                          |    4 +-
 libavcodec/ttmlenc.c                          |  114 +-
 libavcodec/utils.c                            |  184 +++
 libavcodec/version.h                          |    2 +-
 libavcodec/webvttdec.c                        |    2 +-
 libavcodec/webvttenc.c                        |  127 +-
 libavcodec/xsubdec.c                          |    2 +-
 libavcodec/xsubenc.c                          |   88 +-
 libavfilter/Makefile                          |   16 +
 libavfilter/allfilters.c                      |   14 +
 libavfilter/avfilter.c                        |   30 +-
 libavfilter/avfilter.h                        |   11 +
 libavfilter/avfiltergraph.c                   |    5 +
 libavfilter/buffersink.c                      |   54 +
 libavfilter/buffersink.h                      |    7 +
 libavfilter/buffersrc.c                       |   72 +
 libavfilter/buffersrc.h                       |    1 +
 libavfilter/formats.c                         |   22 +
 libavfilter/formats.h                         |    3 +
 libavfilter/internal.h                        |   19 +-
 libavfilter/sf_graphicsub2text.c              | 1132 +++++++++++++++
 libavfilter/sf_splitcc.c                      |  395 ++++++
 libavfilter/sf_stripstyles.c                  |  211 +++
 libavfilter/sf_subfeed.c                      |  366 +++++
 libavfilter/sf_subscale.c                     |  884 ++++++++++++
 libavfilter/sf_textmod.c                      |  710 ++++++++++
 libavfilter/subtitles.c                       |   63 +
 libavfilter/subtitles.h                       |   44 +
 libavfilter/vf_overlaygraphicsubs.c           |  765 ++++++++++
 libavfilter/vf_overlaytextsubs.c              |  678 +++++++++
 libavfilter/vf_subtitles.c                    |   56 +-
 libavutil/Makefile                            |    4 +
 {libavcodec => libavutil}/ass.c               |   91 +-
 libavutil/ass_internal.h                      |  135 ++
 {libavcodec => libavutil}/ass_split.c         |   37 +-
 .../ass_split_internal.h                      |   32 +-
 libavutil/frame.c                             |  211 ++-
 libavutil/frame.h                             |   85 +-
 libavutil/subfmt.c                            |   45 +
 libavutil/subfmt.h                            |  115 ++
 libavutil/version.h                           |    3 +-
 tests/ref/fate/.gitattributes                 |    3 +
 tests/ref/fate/filter-overlay-dvdsub-2397     |  182 +--
 tests/ref/fate/mov-mp4-ttml-dfxp              |    8 +-
 tests/ref/fate/mov-mp4-ttml-stpp              |    8 +-
 tests/ref/fate/sub-dvb                        |  162 ++-
 tests/ref/fate/sub-textenc                    |   10 +-
 tests/ref/fate/sub-ttmlenc                    |    8 +-
 tests/ref/fate/sub-webvttenc                  |   10 +-
 tests/ref/fate/sub2video                      | 1091 ++++++++++++++-
 tests/ref/fate/sub2video_basic                | 1239 +++++++++++++++--
 tests/ref/fate/sub2video_time_limited         |   78 +-
 91 files changed, 11076 insertions(+), 1391 deletions(-)
 create mode 100644 libavfilter/sf_graphicsub2text.c
 create mode 100644 libavfilter/sf_splitcc.c
 create mode 100644 libavfilter/sf_stripstyles.c
 create mode 100644 libavfilter/sf_subfeed.c
 create mode 100644 libavfilter/sf_subscale.c
 create mode 100644 libavfilter/sf_textmod.c
 create mode 100644 libavfilter/subtitles.c
 create mode 100644 libavfilter/subtitles.h
 create mode 100644 libavfilter/vf_overlaygraphicsubs.c
 create mode 100644 libavfilter/vf_overlaytextsubs.c
 rename {libavcodec => libavutil}/ass.c (65%)
 create mode 100644 libavutil/ass_internal.h
 rename {libavcodec => libavutil}/ass_split.c (93%)
 rename libavcodec/ass_split.h => libavutil/ass_split_internal.h (86%)
 create mode 100644 libavutil/subfmt.c
 create mode 100644 libavutil/subfmt.h
 create mode 100644 tests/ref/fate/.gitattributes


base-commit: c936c319bd54f097cc1d75b1ee1c407d53215d71
Published-As: https://github.com/ffstaging/FFmpeg/releases/tag/pr-ffstaging-18%2Fsoftworkz%2Fsubmit_subfiltering-v2
Fetch-It-Via: git fetch https://github.com/ffstaging/FFmpeg pr-ffstaging-18/softworkz/submit_subfiltering-v2
Pull-Request: https://github.com/ffstaging/FFmpeg/pull/18

Range-diff vs v1:

  1:  13b9a26b25 =  1:  13b9a26b25 avcodec,avutil: Move enum AVSubtitleType to avutil, add new and deprecate old values
  2:  54ed1290d2 =  2:  54ed1290d2 avutil/frame: Prepare AVFrame for subtitle handling
  3:  c894aefe33 !  3:  840683a6e1 avcodec/subtitles: Introduce new frame-based subtitle decoding API
     @@ Metadata
       ## Commit message ##
          avcodec/subtitles: Introduce new frame-based subtitle decoding API
      
     -    - Add avcodec_decode_subtitle3 which takes subtitle frames,
     -      serving as compatibility shim to legacy subtitle decoding
     +    - Modify avcodec_send_packet() to support subtitles via the regular
     +      frame based decoding API
     +    - Add decode_subtitle_shim() which takes subtitle frames,
     +      and serves as a compatibility shim to the legacy subtitle decoding
     +      API until all subtitle decoders are migrated to the frame-based API
          - Add additional methods for conversion between old and new API
      
          Signed-off-by: softworkz <softworkz@hotmail.com>
     @@ libavcodec/decode.c: int attribute_align_arg avcodec_send_packet(AVCodecContext
               return AVERROR(EINVAL);
       
      +    if (avctx->codec_type == AVMEDIA_TYPE_SUBTITLE)
     ++		// this does not exactly implement the avcodec_send_packet/avcodec_receive_frame API 
     ++	    // but we know that no subtitle decoder produces multiple AVSubtitles per packet through
     ++		// the legacy API, and this will be changed when migrating the subtitle decoders
     ++		// to the frame based decoding api
      +        return decode_subtitle_shim(avctx, avci->buffer_frame, avpkt);
      +
           av_packet_unref(avci->buffer_pkt);
     @@ libavcodec/internal.h: int ff_int_from_list_or_default(void *ctx, const char * v
      + * the legacy subtitle API.
      + */
      +int ff_frame_get_subtitle(AVSubtitle* sub, AVFrame* frame);
     -+
     -+#if defined(_WIN32) && CONFIG_SHARED && !defined(BUILDING_avcodec)
     -+#    define av_export_avcodec __declspec(dllimport)
     -+#else
     -+#    define av_export_avcodec
     -+#endif
      +
       #endif /* AVCODEC_INTERNAL_H */
      
  4:  24d5ed19fe =  4:  e326e96492 avfilter/subtitles: Update vf_subtitles to use new decoding api
  5:  baf6ca3dbc =  5:  20dc27a6c6 avcodec,avutil: Move ass helper functions to avutil as avpriv_ and extend ass dialog parsing
  7:  34be31a7eb !  6:  3de0be7980 avcodec/subtitles: Replace deprecated enum values
     @@ libavcodec/dvdsubdec.c: static int decode_dvd_subtitles(DVDSubContext *ctx, AVSu
                   }
      
       ## libavcodec/dvdsubenc.c ##
     -@@ libavcodec/dvdsubenc.c: static int encode_dvd_subtitles(AVCodecContext* avctx, AVPacket* avpkt,
     +@@ libavcodec/dvdsubenc.c: static int encode_dvd_subtitles(AVCodecContext *avctx,
     +     if (rects == 0 || !h->rects)
               return AVERROR(EINVAL);
     - 
           for (i = 0; i < rects; i++)
     --        if (frame->subtitle_areas[i]->type != SUBTITLE_BITMAP) {
     -+        if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_BITMAP) {
     +-        if (h->rects[i]->type != SUBTITLE_BITMAP) {
     ++        if (h->rects[i]->type != AV_SUBTITLE_FMT_BITMAP) {
                   av_log(avctx, AV_LOG_ERROR, "Bitmap subtitle required\n");
                   return AVERROR(EINVAL);
               }
  8:  a13ddfc7b5 =  7:  05e3f2a6fe fftools/play,probe: Adjust for subtitle changes
  9:  b9501d32eb =  8:  a86457f868 avfilter/subtitles: Add subtitles.c for subtitle frame allocation
 10:  f7df7f8d06 =  9:  9fec875c51 avfilter/avfilter: Handle subtitle frames
 11:  76a2364048 = 10:  50bf6c36be avfilter/avfilter: Fix hardcoded input index
 12:  fdc41f4474 = 11:  d1ef5004aa avfilter/sbuffer: Add sbuffersrc and sbuffersink filters
 13:  b47404812f = 12:  f4af184a7b avfilter/overlaygraphicsubs: Add overlaygraphicsubs and graphicsub2video filters
 14:  453bef0c34 ! 13:  47b045ad74 avfilter/overlaytextsubs: Add overlaytextsubs and textsubs2video filters
     @@ libavfilter/vf_overlaytextsubs.c (new)
      +    int detect_change = 0;
      +    ASS_Image *image;
      +
     -+    ff_request_frame(ctx->inputs[1]);
      +
     -+    int64_t time_ms = (int64_t)((double)frame->pts * av_q2d(inlink->time_base) * 1000);
     ++    int64_t time_ms  = (int64_t)((double)frame->pts * av_q2d(inlink->time_base) * 1000);
     ++    int64_t time_ms1 = (int64_t)((double)ctx->inputs[1]->current_pts * av_q2d(ctx->inputs[1]->time_base) * 1000);
     ++
     ++    if (time_ms1 < time_ms + 1000)
     ++        ff_request_frame(ctx->inputs[1]);
     ++
     ++    av_log(ctx, AV_LOG_DEBUG, "filter_video_frame - video: %"PRId64"ms  sub: %"PRId64"ms  rel %d\n", time_ms, time_ms1, (time_ms1 < time_ms));
      +
      +    ff_mutex_lock(&s->mutex);
      +    image = ass_render_frame(s->renderer, s->track, time_ms, &detect_change);
     @@ libavfilter/vf_overlaytextsubs.c (new)
      +    TextSubsContext *s = ctx->priv;
      +    const int64_t start_time = av_rescale_q(frame->subtitle_timing.start_pts, AV_TIME_BASE_Q, av_make_q(1, 1000));
      +    const int64_t duration   = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, av_make_q(1, 1000));
     ++    const int64_t frame_time = (int64_t)((double)frame->pts * av_q2d(inlink->time_base) * 1000);
      +
      +    // Postpone header processing until we receive a frame with content
      +    if (!s->got_header && frame->num_subtitle_areas > 0)
      +        process_header(ctx, frame);
      +
     ++    av_log(ctx, AV_LOG_DEBUG, "filter_subtitle_frame dur: %"PRId64"ms frame: %"PRId64"ms  sub: %"PRId64"ms  repeat_sub %d\n", duration, frame_time, start_time, frame->repeat_sub);
     ++
      +    if (frame->repeat_sub)
      +        goto exit;
      +
     @@ libavfilter/vf_overlaytextsubs.c (new)
      +
      +            if (s->track->events[i].Duration > diff)
      +                s->track->events[i].Duration = diff;
     -+
      +        }
      +    }
      +
 15:  efdaddc322 ! 14:  6f200be0c3 avfilter/textmod: Add textmod, censor and show_speaker filters
     @@ libavfilter/sf_textmod.c (new)
      +
      +    av_bprint_finalize(&pbuf, &text);
      +
     -+    result = avpriv_ass_get_dialog(dialog->readorder, dialog->layer, dialog->style, dialog->name, text);
     ++    result = avpriv_ass_get_dialog_ex(dialog->readorder, dialog->layer, dialog->style, dialog->name, dialog->margin_l, dialog->margin_r, dialog->margin_v, text);
      +
      +    av_free(text);
      +    avpriv_ass_free_dialog(&dialog);
     @@ libavfilter/sf_textmod.c (new)
      +    if (!text)
      +        return NULL;
      +
     -+    result = avpriv_ass_get_dialog(dialog->readorder, dialog->layer, dialog->style, dialog->name, text);
     ++    result = avpriv_ass_get_dialog_ex(dialog->readorder, dialog->layer, dialog->style, dialog->name, dialog->margin_l, dialog->margin_r, dialog->margin_v, text);
      +
      +    av_free(text);
      +    avpriv_ass_free_dialog(&dialog);
 16:  54af3f8d07 ! 15:  ab8736ac43 avfilter/stripstyles: Add stripstyles filter
     @@ libavfilter/sf_stripstyles.c (new)
      +    if (!dialog)
      +        return NULL;
      +
     -+    if (s->select_layer >= 0 && dialog->layer != s->select_layer)
     ++    if (s->select_layer >= 0 && dialog->layer != s->select_layer) {
     ++        avpriv_ass_free_dialog(&dialog);
      +        return NULL;
     ++    }
      +
      +    dlg_ctx.ss_ctx = s;
      +
 17:  e18f10394c ! 16:  5369aca080 avfilter/splitcc: Add splitcc filter for closed caption handling
     @@ libavfilter/sf_splitcc.c (new)
      +    AVFrame *empty_sub_frame;
      +    int new_frame;
      +    int64_t next_repetition_pts;
     ++    int had_keyframe;
      +    AVBufferRef *subtitle_header;
      +    int use_cc_styles;
      +    int real_time;
     @@ libavfilter/sf_splitcc.c (new)
      +    AVFilterLink *inlink0 = ctx->inputs[0];
      +    AVFilterLink *outlink0 = ctx->outputs[0];
      +    AVFilterLink *outlink1 = ctx->outputs[1];
     -+    static const enum AVSubtitleType subtitle_fmts[] = { AV_SUBTITLE_FMT_ASS, AV_SUBTITLE_FMT_NB };
     ++    static const enum AVSubtitleType subtitle_fmts[] = { AV_SUBTITLE_FMT_ASS, AV_SUBTITLE_FMT_NONE };
      +    int ret;
      +
      +    /* set input0 video formats */
     @@ libavfilter/sf_splitcc.c (new)
      +
      +    sd = av_frame_get_side_data(frame, AV_FRAME_DATA_A53_CC);
      +
     -+    if (sd) {
     ++    if (sd && (s->had_keyframe || frame->key_frame)) {
      +        int got_output = 0;
      +
     ++        s->had_keyframe = 1;
      +        pkt = av_packet_alloc();
      +        pkt->buf = av_buffer_ref(sd->buf);
      +        if (!pkt->buf) {
     @@ libavfilter/sf_splitcc.c (new)
      +            goto fail;
      +        }
      +
     -+        pkt->data = sd->data;
     -+        pkt->size = (int)sd->size;
     -+        pkt->pts  = frame->pts;
     ++        pkt->data = pkt->buf->data;
     ++        pkt->size = pkt->buf->size;
     ++        pkt->pts  = av_rescale_q(frame->pts, inlink->time_base, AV_TIME_BASE_Q);
      +
      +        sub_out = ff_get_subtitles_buffer(outlink1, AV_SUBTITLE_FMT_ASS);
      +        if (!sub_out) {
     @@ libavfilter/sf_splitcc.c (new)
      +
      +        ret = decode(s->cc_dec, sub_out, &got_output, pkt);
      +
     -+        if (ret < 0)
     ++        ////if (got_output) {
     ++        ////    av_log(inlink->dst, AV_LOG_INFO, "CC Packet PTS: %"PRId64" got_output: %d  out_frame_pts: %"PRId64"  out_sub_pts: %"PRId64"\n", pkt->pts, got_output, sub_out->pts, sub_out->subtitle_pts);
     ++        ////}
     ++
     ++        av_packet_free(&pkt);
     ++
     ++        if (ret < 0) {
     ++            av_log(inlink->dst, AV_LOG_ERROR, "Decode error: %d \n", ret);
      +            goto fail;
     ++        }
      +
      +        if (got_output) {
      +            sub_out->pts = frame->pts;
 18:  2cf4d42167 ! 17:  43a20d1024 avfilter/graphicsub2text: Add new graphicsub2text filter (OCR)
     @@ doc/filters.texi: ffmpeg -i "https://streams.videolan.org/ffmpeg/mkv_subtitles.m
      +
      +Note: For this to work, you need to have the data file 'eng.traineddata' in a 'tessdata' subfolder (see above).
      +@example
     -+ffmpeg ffmpeg -loglevel verbose -i "https://streams.videolan.org/streams/ts/video_subs_ttxt%2Bdvbsub.ts" -filter_complex "[0:13]graphicsub2text=ocr_mode=both" -c:s ass -y output.mkv
     ++ffmpeg -loglevel verbose -i "https://streams.videolan.org/streams/ts/video_subs_ttxt%2Bdvbsub.ts" -filter_complex "[0:13]graphicsub2text=delay_when_no_duration=1" -c:s ass -y output.mkv
      +@end example
      +@end itemize
      +
     @@ libavfilter/sf_graphicsub2text.c (new)
      +    for (int y = top; y < bottom; y += 3) {
      +        uint8_t *p = area->buf[0]->data + (y * area->linesize[0]) + left;
      +        uint8_t *porig = original_area->buf[0]->data + (y * original_area->linesize[0]) + left;
     ++        uint8_t current_index = 255;
      +
      +        for (int x = left; x < right; x++, p++, porig++) {
     -+            if (*p == bg_color_index)
     -+                bg_score[*porig]++;
     -+            if (*p == text_color_index)
     -+                text_score[*porig]++;
     -+            if (*p == outline_color_index)
     -+                outline_score[*porig]++;
     ++
     ++            if (*p == current_index) {
     ++                if (*p == bg_color_index)
     ++                    bg_score[*porig]++;
     ++                if (*p == text_color_index)
     ++                    text_score[*porig]++;
     ++                if (*p == outline_color_index)
     ++                    outline_score[*porig]++;
     ++            }
     ++
     ++            current_index = *p;
      +        }
      +    }
      +
     @@ libavfilter/sf_graphicsub2text.c (new)
      +
      +        frame_sent = 1;
      +        s->last_subtitle_pts = frame->subtitle_timing.start_pts;
     -+
     -+        if (frame->num_subtitle_areas == 0) {
     -+            // No need to forward this empty frame
     -+            av_frame_free(&frame);
     -+            return 0;
     -+        }
      +    }
      +
     -+    if (frame->repeat_sub && frame->subtitle_timing.start_pts == s->last_subtitle_pts) {
     ++    if (frame->repeat_sub) {
      +        // Ignore repeated frame
      +        av_frame_free(&frame);
      +        return 0;
     @@ libavfilter/sf_graphicsub2text.c (new)
      +
      +    // When decoders can't determine the end time, they are setting it either to UINT32_NAX
      +    // or 30s (dvbsub).
     -+    if (s->delay_when_no_duration && frame->num_subtitle_areas > 0 && frame->subtitle_timing.duration >= ms_to_avtb(29000)) {
     ++    if (s->delay_when_no_duration && frame->subtitle_timing.duration >= ms_to_avtb(29000)) {
      +        // Can't send it without end time, wait for the next frame to determine the end_display time
      +        s->pending_frame = frame;
      +
 19:  c51d6a77da = 18:  0d3c46a68f avfilter/subscale: Add filter for scaling and/or re-arranging graphical subtitles
 20:  ad07dbf7c2 ! 19:  2c20886389 avfilter/subfeed: add subtitle feed filter
     @@ libavfilter/sf_subfeed.c (new)
      +
      +const AVFilter ff_sf_subfeed = {
      +    .name           = "subfeed",
     -+    .description    = NULL_IF_CONFIG_SMALL("Scatter subtitle events by duration"),
     ++    .description    = NULL_IF_CONFIG_SMALL("Control subtitle frame timing and flow in a filtergraph"),
      +    .init           = init,
      +    .uninit         = uninit,
      +    .priv_size      = sizeof(SubFeedContext),
  6:  808f589488 ! 20:  64ce976c19 avcodec/subtitles: Migrate subtitle encoders to frame-based API
     @@ libavcodec/dvdsubenc.c: static void copy_rectangle(AVSubtitleRect *dst, AVSubtit
               return AVERROR(EINVAL);
      +
           for (i = 0; i < rects; i++)
     --        if (h->rects[i]->type != SUBTITLE_BITMAP) {
     -+        if (frame->subtitle_areas[i]->type != SUBTITLE_BITMAP) {
     +-        if (h->rects[i]->type != AV_SUBTITLE_FMT_BITMAP) {
     ++        if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_BITMAP) {
                   av_log(avctx, AV_LOG_ERROR, "Bitmap subtitle required\n");
                   return AVERROR(EINVAL);
               }
     @@ libavcodec/encode.c: fail:
           return ret;
       }
       
     -+/**
     -+ * \brief
     -+ * \param avctx
     -+ * \param buf q
     -+ * \param buf_size
     -+ * \param sub
     -+ * \return
     -+ */
     - int avcodec_encode_subtitle(AVCodecContext *avctx, uint8_t *buf, int buf_size,
     -                             const AVSubtitle *sub)
     +-int avcodec_encode_subtitle(AVCodecContext *avctx, uint8_t *buf, int buf_size,
     +-                            const AVSubtitle *sub)
     ++int avcodec_encode_subtitle(AVCodecContext *avctx, uint8_t *buf, int buf_size, const AVSubtitle *sub)
       {
      -    int ret;
      +    int ret = 0, got_packet = 0;
     @@ libavcodec/encode.c: fail:
      +    if (ret < 0)
      +        goto exit;
      +
     -+    ret = avctx->codec->encode2(avctx, avpkt, frame, &got_packet);
     ++    ret = avcodec_send_frame(avctx, frame);
     ++    if (ret < 0)
     ++        goto exit;
     ++
     ++    ret = avcodec_receive_packet(avctx, avpkt);
     ++
     ++    if (ret < 0 && ret != AVERROR(EAGAIN))
     ++        goto exit;
     ++
     ++    //ret = avctx->codec->encode2(avctx, avpkt, frame, &got_packet);
      +
           avctx->frame_number++;
      +
     -+    if (got_packet) {
     ++    if (avpkt->size) {
      +        if (avpkt->size > buf_size) {
      +            ret = AVERROR_BUFFER_TOO_SMALL;
      +            goto exit;
 21:  00643582cf ! 21:  56a162b3a4 fftools/ffmpeg: Introduce subtitle filtering new frame-based subtitle encoding
     @@ Metadata
      Author: softworkz <softworkz@hotmail.com>
      
       ## Commit message ##
     -    fftools/ffmpeg: Introduce subtitle filtering new frame-based subtitle encoding
     +    fftools/ffmpeg: Introduce subtitle filtering and new frame-based subtitle encoding
      
          This commit actually enables subtitle filtering in ffmpeg by
          sending and receiving subtitle frames to and from a filtergraph.
     @@ fftools/ffmpeg.c: static int ifilter_has_all_input_formats(FilterGraph *fg)
           }
           return 1;
      @@ fftools/ffmpeg.c: static int ifilter_send_frame(InputFilter *ifilter, AVFrame *frame, int keep_ref
     -                        ifilter->channel_layout != frame->channel_layout;
     -         break;
           case AVMEDIA_TYPE_VIDEO:
     -+    case AVMEDIA_TYPE_SUBTITLE:
               need_reinit |= ifilter->width  != frame->width ||
                              ifilter->height != frame->height;
      +
      +        if (need_reinit)
     ++            need_reinit = 1;
     ++        break;
     ++    case AVMEDIA_TYPE_SUBTITLE:
     ++        need_reinit |= ifilter->width  != frame->width ||
     ++                       ifilter->height != frame->height;
     ++
     ++        need_reinit &= (ifilter->width == 0 || ifilter->height == 0);
     ++
     ++        if (need_reinit)
      +            need_reinit = 1;
               break;
           }
     @@ fftools/ffmpeg.c: fail:
      +    AVFrame *decoded_frame;
      +    AVCodecContext *avctx = ist->dec_ctx;
      +    int i = 0, ret = 0, err = 0;
     - 
     --    check_decode_result(NULL, got_output, ret);
     ++
      +    if (!ist->decoded_frame && !(ist->decoded_frame = av_frame_alloc()))
      +        return AVERROR(ENOMEM);
      +    decoded_frame = ist->decoded_frame;
      +    decoded_frame->type = AVMEDIA_TYPE_SUBTITLE;
      +    decoded_frame->format = avcodec_descriptor_get_subtitle_format(avctx->codec_descriptor);
     -+
     + 
     +-    check_decode_result(NULL, got_output, ret);
      +    if (!ist->subtitle_header && avctx->subtitle_header && avctx->subtitle_header_size > 0) {
      +        ist->subtitle_header = av_buffer_allocz(avctx->subtitle_header_size + 1);
      +        if (!ist->subtitle_header)
 22:  b48816fb4c ! 22:  1a0c6e01f3 avutil/ass_split: Add parsing of hard-space tags (\h)
     @@ libavutil/ass_split.c: int avpriv_ass_split_override_codes(const ASSCodesCallbac
                   buf++;
                   while (*buf == '\\') {
      
     + ## tests/ref/fate/.gitattributes (new) ##
     +@@
     ++sub-textenc -diff
     ++sub-ttmlenc -diff
     ++sub-webvttenc -diff
     +
       ## tests/ref/fate/mov-mp4-ttml-dfxp ##
      @@
      -2e7e01c821c111466e7a2844826b7f6d *tests/data/fate/mov-mp4-ttml-dfxp.mp4
 23:  10c1404272 = 23:  44b4e203d8 avcodec/webvttenc: convert hard-space tags to &nbsp;
 24:  c0011334ce = 24:  5773f2a1ff doc/APIchanges: update for subtitle filtering changes
  -:  ---------- > 25:  7ab6dedf80 avcodec/webvttenc: Don't encode drawing codes and empty lines
  -:  ---------- > 26:  217d96c39d avcodec/dvbsubdec: Fix conditions for fallback to default resolution

-- 
ffmpeg-codebot
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v2 01/26] avcodec, avutil: Move enum AVSubtitleType to avutil, add new and deprecate old values
  2022-01-20  2:48 ` [FFmpeg-devel] [PATCH v2 00/26] Subtitle Filtering 2022 ffmpegagent
@ 2022-01-20  2:48   ` ffmpegagent
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 02/26] avutil/frame: Prepare AVFrame for subtitle handling ffmpegagent
                     ` (25 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  2:48 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/avcodec.h | 19 +------------
 libavutil/Makefile   |  1 +
 libavutil/subfmt.h   | 68 ++++++++++++++++++++++++++++++++++++++++++++
 libavutil/version.h  |  1 +
 4 files changed, 71 insertions(+), 18 deletions(-)
 create mode 100644 libavutil/subfmt.h

diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index ec1a0566a4..fe5a83cf85 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -35,6 +35,7 @@
 #include "libavutil/frame.h"
 #include "libavutil/log.h"
 #include "libavutil/pixfmt.h"
+#include "libavutil/subfmt.h"
 #include "libavutil/rational.h"
 
 #include "codec.h"
@@ -2238,24 +2239,6 @@ typedef struct AVHWAccel {
  * @}
  */
 
-enum AVSubtitleType {
-    SUBTITLE_NONE,
-
-    SUBTITLE_BITMAP,                ///< A bitmap, pict will be set
-
-    /**
-     * Plain text, the text field must be set by the decoder and is
-     * authoritative. ass and pict fields may contain approximations.
-     */
-    SUBTITLE_TEXT,
-
-    /**
-     * Formatted text, the ass field must be set by the decoder and is
-     * authoritative. pict and text fields may contain approximations.
-     */
-    SUBTITLE_ASS,
-};
-
 #define AV_SUBTITLE_FLAG_FORCED 0x00000001
 
 typedef struct AVSubtitleRect {
diff --git a/libavutil/Makefile b/libavutil/Makefile
index d17876df1a..ce644f4d48 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -74,6 +74,7 @@ HEADERS = adler32.h                                                     \
           sha512.h                                                      \
           spherical.h                                                   \
           stereo3d.h                                                    \
+          subfmt.h                                                      \
           threadmessage.h                                               \
           time.h                                                        \
           timecode.h                                                    \
diff --git a/libavutil/subfmt.h b/libavutil/subfmt.h
new file mode 100644
index 0000000000..791b45519f
--- /dev/null
+++ b/libavutil/subfmt.h
@@ -0,0 +1,68 @@
+/*
+ * 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
+ */
+
+#ifndef AVUTIL_SUBFMT_H
+#define AVUTIL_SUBFMT_H
+
+#include "version.h"
+
+enum AVSubtitleType {
+
+    /**
+     * Subtitle format unknown.
+     */
+    AV_SUBTITLE_FMT_NONE = -1,
+
+    /**
+     * Subtitle format unknown.
+     */
+    AV_SUBTITLE_FMT_UNKNOWN = 0,
+#if FF_API_OLD_SUBTITLES
+    SUBTITLE_NONE = 0,          ///< Deprecated, use AV_SUBTITLE_FMT_NONE instead.
+#endif
+
+    /**
+     * Bitmap area in AVSubtitleRect.data, pixfmt AV_PIX_FMT_PAL8.
+     */
+    AV_SUBTITLE_FMT_BITMAP = 1,
+#if FF_API_OLD_SUBTITLES
+    SUBTITLE_BITMAP = 1,        ///< Deprecated, use AV_SUBTITLE_FMT_BITMAP instead.
+#endif
+
+    /**
+     * Plain text in AVSubtitleRect.text.
+     */
+    AV_SUBTITLE_FMT_TEXT = 2,
+#if FF_API_OLD_SUBTITLES
+    SUBTITLE_TEXT = 2,          ///< Deprecated, use AV_SUBTITLE_FMT_TEXT instead.
+#endif
+
+    /**
+     * Text Formatted as per ASS specification, contained AVSubtitleRect.ass.
+     */
+    AV_SUBTITLE_FMT_ASS = 3,
+#if FF_API_OLD_SUBTITLES
+    SUBTITLE_ASS = 3,           ///< Deprecated, use AV_SUBTITLE_FMT_ASS instead.
+#endif
+
+    AV_SUBTITLE_FMT_NB,         ///< number of subtitle formats, DO NOT USE THIS if you want to link with shared libav* because the number of formats might differ between versions.
+};
+
+#endif /* AVUTIL_SUBFMT_H */
diff --git a/libavutil/version.h b/libavutil/version.h
index 953aac9d94..5bf48f6304 100644
--- a/libavutil/version.h
+++ b/libavutil/version.h
@@ -110,6 +110,7 @@
 #define FF_API_COLORSPACE_NAME          (LIBAVUTIL_VERSION_MAJOR < 58)
 #define FF_API_AV_MALLOCZ_ARRAY         (LIBAVUTIL_VERSION_MAJOR < 58)
 #define FF_API_FIFO_PEEK2               (LIBAVUTIL_VERSION_MAJOR < 58)
+#define FF_API_OLD_SUBTITLES            (LIBAVUTIL_VERSION_MAJOR < 58)
 
 /**
  * @}
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v2 02/26] avutil/frame: Prepare AVFrame for subtitle handling
  2022-01-20  2:48 ` [FFmpeg-devel] [PATCH v2 00/26] Subtitle Filtering 2022 ffmpegagent
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 01/26] avcodec, avutil: Move enum AVSubtitleType to avutil, add new and deprecate old values ffmpegagent
@ 2022-01-20  2:48   ` ffmpegagent
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 03/26] avcodec/subtitles: Introduce new frame-based subtitle decoding API ffmpegagent
                     ` (24 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  2:48 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Root commit for adding subtitle filtering capabilities.
In detail:

- Add type (AVMediaType) field to AVFrame
  Replaces previous way of distinction which was based on checking
  width and height to determine whether a frame is audio or video
- Add subtitle fields to AVFrame
- Add new struct AVSubtitleArea, similar to AVSubtitleRect, but
  different allocation logic. Cannot and must not be used
  interchangeably, hence the new struct

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavutil/Makefile |   1 +
 libavutil/frame.c  | 211 ++++++++++++++++++++++++++++++++++++++++-----
 libavutil/frame.h  |  85 +++++++++++++++++-
 libavutil/subfmt.c |  45 ++++++++++
 libavutil/subfmt.h |  47 ++++++++++
 5 files changed, 364 insertions(+), 25 deletions(-)
 create mode 100644 libavutil/subfmt.c

diff --git a/libavutil/Makefile b/libavutil/Makefile
index ce644f4d48..8bc0a14942 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -160,6 +160,7 @@ OBJS = adler32.o                                                        \
        slicethread.o                                                    \
        spherical.o                                                      \
        stereo3d.o                                                       \
+       subfmt.o                                                         \
        threadmessage.o                                                  \
        time.o                                                           \
        timecode.o                                                       \
diff --git a/libavutil/frame.c b/libavutil/frame.c
index 8997c85e35..2b95830b6f 100644
--- a/libavutil/frame.c
+++ b/libavutil/frame.c
@@ -26,6 +26,7 @@
 #include "imgutils.h"
 #include "mem.h"
 #include "samplefmt.h"
+#include "subfmt.h"
 #include "hwcontext.h"
 
 #define CHECK_CHANNELS_CONSISTENCY(frame) \
@@ -50,6 +51,9 @@ const char *av_get_colorspace_name(enum AVColorSpace val)
     return name[val];
 }
 #endif
+
+static int frame_copy_subtitles(AVFrame *dst, const AVFrame *src, int copy_data);
+
 static void get_frame_defaults(AVFrame *frame)
 {
     memset(frame, 0, sizeof(*frame));
@@ -70,7 +74,12 @@ static void get_frame_defaults(AVFrame *frame)
     frame->colorspace          = AVCOL_SPC_UNSPECIFIED;
     frame->color_range         = AVCOL_RANGE_UNSPECIFIED;
     frame->chroma_location     = AVCHROMA_LOC_UNSPECIFIED;
-    frame->flags               = 0;
+    frame->num_subtitle_areas  = 0;
+    frame->subtitle_areas      = NULL;
+    frame->subtitle_header     = NULL;
+    frame->repeat_sub          = 0;
+    frame->subtitle_timing.start_pts = 0;
+    frame->subtitle_timing.duration  = 0;
 }
 
 static void free_side_data(AVFrameSideData **ptr_sd)
@@ -240,23 +249,55 @@ static int get_audio_buffer(AVFrame *frame, int align)
 
 }
 
+static int get_subtitle_buffer(AVFrame *frame)
+{
+    // Buffers in AVFrame->buf[] are not used in case of subtitle frames.
+    // To accomodate with existing code, checking ->buf[0] to determine
+    // whether a frame is ref-counted or has data, we're adding a 1-byte
+    // buffer here, which marks the subtitle frame to contain data.
+    frame->buf[0] = av_buffer_alloc(1);
+    if (!frame->buf[0]) {
+        av_frame_unref(frame);
+        return AVERROR(ENOMEM);
+    }
+
+    frame->extended_data = frame->data;
+
+    return 0;
+}
+
 int av_frame_get_buffer(AVFrame *frame, int align)
+{
+    if (frame->width > 0 && frame->height > 0)
+        frame->type = AVMEDIA_TYPE_VIDEO;
+    else if (frame->nb_samples > 0 && (frame->channel_layout || frame->channels > 0))
+        frame->type = AVMEDIA_TYPE_AUDIO;
+
+    return av_frame_get_buffer2(frame, align);
+}
+
+int av_frame_get_buffer2(AVFrame *frame, int align)
 {
     if (frame->format < 0)
         return AVERROR(EINVAL);
 
-    if (frame->width > 0 && frame->height > 0)
+    switch(frame->type) {
+    case AVMEDIA_TYPE_VIDEO:
         return get_video_buffer(frame, align);
-    else if (frame->nb_samples > 0 && (frame->channel_layout || frame->channels > 0))
+    case AVMEDIA_TYPE_AUDIO:
         return get_audio_buffer(frame, align);
-
-    return AVERROR(EINVAL);
+    case AVMEDIA_TYPE_SUBTITLE:
+        return get_subtitle_buffer(frame);
+    default:
+        return AVERROR(EINVAL);
+    }
 }
 
 static int frame_copy_props(AVFrame *dst, const AVFrame *src, int force_copy)
 {
     int ret, i;
 
+    dst->type                   = src->type;
     dst->key_frame              = src->key_frame;
     dst->pict_type              = src->pict_type;
     dst->sample_aspect_ratio    = src->sample_aspect_ratio;
@@ -288,6 +329,12 @@ static int frame_copy_props(AVFrame *dst, const AVFrame *src, int force_copy)
     dst->colorspace             = src->colorspace;
     dst->color_range            = src->color_range;
     dst->chroma_location        = src->chroma_location;
+    dst->repeat_sub             = src->repeat_sub;
+    dst->subtitle_timing.start_pts = src->subtitle_timing.start_pts;
+    dst->subtitle_timing.duration  = src->subtitle_timing.duration;
+
+    if ((ret = av_buffer_replace(&dst->subtitle_header, src->subtitle_header)) < 0)
+        return ret;
 
     av_dict_copy(&dst->metadata, src->metadata, 0);
 
@@ -329,6 +376,7 @@ int av_frame_ref(AVFrame *dst, const AVFrame *src)
     av_assert1(dst->width == 0 && dst->height == 0);
     av_assert1(dst->channels == 0);
 
+    dst->type           = src->type;
     dst->format         = src->format;
     dst->width          = src->width;
     dst->height         = src->height;
@@ -342,7 +390,7 @@ int av_frame_ref(AVFrame *dst, const AVFrame *src)
 
     /* duplicate the frame data if it's not refcounted */
     if (!src->buf[0]) {
-        ret = av_frame_get_buffer(dst, 0);
+        ret = av_frame_get_buffer2(dst, 0);
         if (ret < 0)
             goto fail;
 
@@ -364,6 +412,10 @@ int av_frame_ref(AVFrame *dst, const AVFrame *src)
         }
     }
 
+    /* copy subtitle areas */
+    if (src->type == AVMEDIA_TYPE_SUBTITLE)
+        frame_copy_subtitles(dst, src, 0);
+
     if (src->extended_buf) {
         dst->extended_buf = av_calloc(src->nb_extended_buf,
                                       sizeof(*dst->extended_buf));
@@ -434,7 +486,7 @@ AVFrame *av_frame_clone(const AVFrame *src)
 
 void av_frame_unref(AVFrame *frame)
 {
-    int i;
+    unsigned i, n;
 
     if (!frame)
         return;
@@ -453,6 +505,21 @@ void av_frame_unref(AVFrame *frame)
     av_buffer_unref(&frame->opaque_ref);
     av_buffer_unref(&frame->private_ref);
 
+    av_buffer_unref(&frame->subtitle_header);
+
+    for (i = 0; i < frame->num_subtitle_areas; i++) {
+        AVSubtitleArea *area = frame->subtitle_areas[i];
+
+        for (n = 0; n < FF_ARRAY_ELEMS(area->buf); n++)
+            av_buffer_unref(&area->buf[n]);
+
+        av_freep(&area->text);
+        av_freep(&area->ass);
+        av_free(area);
+    }
+
+    av_freep(&frame->subtitle_areas);
+
     if (frame->extended_data != frame->data)
         av_freep(&frame->extended_data);
 
@@ -472,18 +539,28 @@ void av_frame_move_ref(AVFrame *dst, AVFrame *src)
 
 int av_frame_is_writable(AVFrame *frame)
 {
-    int i, ret = 1;
+    AVSubtitleArea *area;
+    int ret = 1;
 
     /* assume non-refcounted frames are not writable */
     if (!frame->buf[0])
         return 0;
 
-    for (i = 0; i < FF_ARRAY_ELEMS(frame->buf); i++)
+    for (unsigned i = 0; i < FF_ARRAY_ELEMS(frame->buf); i++)
         if (frame->buf[i])
             ret &= !!av_buffer_is_writable(frame->buf[i]);
-    for (i = 0; i < frame->nb_extended_buf; i++)
+    for (unsigned i = 0; i < frame->nb_extended_buf; i++)
         ret &= !!av_buffer_is_writable(frame->extended_buf[i]);
 
+    for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+        area = frame->subtitle_areas[i];
+        if (area) {
+            for (unsigned n = 0; n < FF_ARRAY_ELEMS(area->buf); n++)
+                if (area->buf[n])
+                    ret &= !!av_buffer_is_writable(area->buf[n]);
+            }
+    }
+
     return ret;
 }
 
@@ -499,6 +576,7 @@ int av_frame_make_writable(AVFrame *frame)
         return 0;
 
     memset(&tmp, 0, sizeof(tmp));
+    tmp.type           = frame->type;
     tmp.format         = frame->format;
     tmp.width          = frame->width;
     tmp.height         = frame->height;
@@ -509,7 +587,7 @@ int av_frame_make_writable(AVFrame *frame)
     if (frame->hw_frames_ctx)
         ret = av_hwframe_get_buffer(frame->hw_frames_ctx, &tmp, 0);
     else
-        ret = av_frame_get_buffer(&tmp, 0);
+        ret = av_frame_get_buffer2(&tmp, 0);
     if (ret < 0)
         return ret;
 
@@ -544,14 +622,22 @@ AVBufferRef *av_frame_get_plane_buffer(AVFrame *frame, int plane)
     uint8_t *data;
     int planes, i;
 
-    if (frame->nb_samples) {
-        int channels = frame->channels;
-        if (!channels)
-            return NULL;
-        CHECK_CHANNELS_CONSISTENCY(frame);
-        planes = av_sample_fmt_is_planar(frame->format) ? channels : 1;
-    } else
+    switch(frame->type) {
+    case AVMEDIA_TYPE_VIDEO:
         planes = 4;
+        break;
+    case AVMEDIA_TYPE_AUDIO:
+        {
+            int channels = frame->channels;
+            if (!channels)
+                return NULL;
+            CHECK_CHANNELS_CONSISTENCY(frame);
+            planes = av_sample_fmt_is_planar(frame->format) ? channels : 1;
+            break;
+        }
+    default:
+        return NULL;
+    }
 
     if (plane < 0 || plane >= planes || !frame->extended_data[plane])
         return NULL;
@@ -675,17 +761,98 @@ static int frame_copy_audio(AVFrame *dst, const AVFrame *src)
     return 0;
 }
 
+static int av_subtitle_area2area(AVSubtitleArea *dst, const AVSubtitleArea *src, int copy_data)
+{
+    dst->x         =  src->x;
+    dst->y         =  src->y;
+    dst->w         =  src->w;
+    dst->h         =  src->h;
+    dst->nb_colors =  src->nb_colors;
+    dst->type      =  src->type;
+    dst->flags     =  src->flags;
+
+    switch (dst->type) {
+    case AV_SUBTITLE_FMT_BITMAP:
+
+        for (unsigned i = 0; i < AV_NUM_BUFFER_POINTERS; i++) {
+            if (src->h > 0 && src->w > 0 && src->buf[i]) {
+                dst->buf[0] = av_buffer_ref(src->buf[i]);
+                if (!dst->buf[i])
+                    return AVERROR(ENOMEM);
+
+                if (copy_data) {
+                    const int ret = av_buffer_make_writable(&dst->buf[i]);
+                    if (ret < 0)
+                        return ret;
+                }
+
+                dst->linesize[i] = src->linesize[i];
+            }
+        }
+
+        memcpy(&dst->pal[0], &src->pal[0], sizeof(src->pal[0]) * 256);
+
+        break;
+    case AV_SUBTITLE_FMT_TEXT:
+
+        if (src->text) {
+            dst->text = av_strdup(src->text);
+            if (!dst->text)
+                return AVERROR(ENOMEM);
+        }
+
+        break;
+    case AV_SUBTITLE_FMT_ASS:
+
+        if (src->ass) {
+            dst->ass = av_strdup(src->ass);
+            if (!dst->ass)
+                return AVERROR(ENOMEM);
+        }
+
+        break;
+    default:
+
+        av_log(NULL, AV_LOG_ERROR, "Subtitle area has invalid format: %d", dst->type);
+        return AVERROR(ENOMEM);
+    }
+
+    return 0;
+}
+
+static int frame_copy_subtitles(AVFrame *dst, const AVFrame *src, int copy_data)
+{
+    dst->format = src->format;
+
+    dst->num_subtitle_areas = src->num_subtitle_areas;
+
+    if (src->num_subtitle_areas) {
+        dst->subtitle_areas = av_malloc_array(src->num_subtitle_areas, sizeof(AVSubtitleArea *));
+
+        for (unsigned i = 0; i < src->num_subtitle_areas; i++) {
+            dst->subtitle_areas[i] = av_mallocz(sizeof(AVSubtitleArea));
+            av_subtitle_area2area(dst->subtitle_areas[i], src->subtitle_areas[i], copy_data);
+        }
+    }
+
+    return 0;
+}
+
 int av_frame_copy(AVFrame *dst, const AVFrame *src)
 {
     if (dst->format != src->format || dst->format < 0)
         return AVERROR(EINVAL);
 
-    if (dst->width > 0 && dst->height > 0)
+    switch(dst->type) {
+    case AVMEDIA_TYPE_VIDEO:
         return frame_copy_video(dst, src);
-    else if (dst->nb_samples > 0 && dst->channels > 0)
+    case AVMEDIA_TYPE_AUDIO:
         return frame_copy_audio(dst, src);
-
-    return AVERROR(EINVAL);
+    case AVMEDIA_TYPE_SUBTITLE:
+        return frame_copy_subtitles(dst, src, 1);
+    default:
+        return AVERROR(EINVAL);
+    }
 }
 
 void av_frame_remove_side_data(AVFrame *frame, enum AVFrameSideDataType type)
diff --git a/libavutil/frame.h b/libavutil/frame.h
index 18e239f870..ed519c5e2b 100644
--- a/libavutil/frame.h
+++ b/libavutil/frame.h
@@ -25,7 +25,6 @@
 #ifndef AVUTIL_FRAME_H
 #define AVUTIL_FRAME_H
 
-#include <stddef.h>
 #include <stdint.h>
 
 #include "avutil.h"
@@ -34,6 +33,7 @@
 #include "rational.h"
 #include "samplefmt.h"
 #include "pixfmt.h"
+#include "subfmt.h"
 #include "version.h"
 
 
@@ -285,7 +285,7 @@ typedef struct AVRegionOfInterest {
 } AVRegionOfInterest;
 
 /**
- * This structure describes decoded (raw) audio or video data.
+ * This structure describes decoded (raw) audio, video or subtitle data.
  *
  * AVFrame must be allocated using av_frame_alloc(). Note that this only
  * allocates the AVFrame itself, the buffers for the data must be managed
@@ -399,7 +399,7 @@ typedef struct AVFrame {
     /**
      * format of the frame, -1 if unknown or unset
      * Values correspond to enum AVPixelFormat for video frames,
-     * enum AVSampleFormat for audio)
+     * enum AVSampleFormat for audio, AVSubtitleType for subtitles)
      */
     int format;
 
@@ -681,6 +681,53 @@ typedef struct AVFrame {
      * for the target frame's private_ref field.
      */
     AVBufferRef *private_ref;
+
+    /**
+     * Media type of the frame (audio, video, subtitles..)
+     *
+     * See AVMEDIA_TYPE_xxx
+     */
+    enum AVMediaType type;
+
+    /**
+     * Number of items in the @ref subtitle_areas array.
+     */
+    unsigned num_subtitle_areas;
+
+    /**
+     * Array of subtitle areas, may be empty.
+     */
+    AVSubtitleArea **subtitle_areas;
+
+    /**
+     * Header containing style information for text subtitles.
+     */
+    AVBufferRef *subtitle_header;
+
+    /**
+     * Indicates that a subtitle frame is a repeated frame for arbitrating flow
+     * in a filter graph.
+     * The field subtitle_timing.start_pts always indicates the original presentation
+     * time, while the frame's pts field may be different.
+     */
+    int repeat_sub;
+
+    struct SubtitleTiming
+    {
+        /**
+         * The display start time, in AV_TIME_BASE.
+         *
+         * For subtitle frames, AVFrame.pts is populated from the packet pts value,
+         * which is not always the same as this value.
+         */
+        int64_t start_pts;
+
+        /**
+         * Display duration, in AV_TIME_BASE.
+         */
+        int64_t duration;
+
+    } subtitle_timing;
 } AVFrame;
 
 
@@ -757,6 +804,8 @@ void av_frame_move_ref(AVFrame *dst, AVFrame *src);
 /**
  * Allocate new buffer(s) for audio or video data.
  *
+ * Note: For subtitle data, use av_frame_get_buffer2
+ *
  * The following fields must be set on frame before calling this function:
  * - format (pixel format for video, sample format for audio)
  * - width and height for video
@@ -776,9 +825,39 @@ void av_frame_move_ref(AVFrame *dst, AVFrame *src);
  *              recommended to pass 0 here unless you know what you are doing.
  *
  * @return 0 on success, a negative AVERROR on error.
+ *
+ * @deprecated Use @ref av_frame_get_buffer2 instead and set @ref AVFrame.type
+ * before calling.
  */
+attribute_deprecated
 int av_frame_get_buffer(AVFrame *frame, int align);
 
+/**
+ * Allocate new buffer(s) for audio, video or subtitle data.
+ *
+ * The following fields must be set on frame before calling this function:
+ * - format (pixel format for video, sample format for audio)
+ * - width and height for video
+ * - nb_samples and channel_layout for audio
+ * - type (AVMediaType)
+ *
+ * This function will fill AVFrame.data and AVFrame.buf arrays and, if
+ * necessary, allocate and fill AVFrame.extended_data and AVFrame.extended_buf.
+ * For planar formats, one buffer will be allocated for each plane.
+ *
+ * @warning: if frame already has been allocated, calling this function will
+ *           leak memory. In addition, undefined behavior can occur in certain
+ *           cases.
+ *
+ * @param frame frame in which to store the new buffers.
+ * @param align Required buffer size alignment. If equal to 0, alignment will be
+ *              chosen automatically for the current CPU. It is highly
+ *              recommended to pass 0 here unless you know what you are doing.
+ *
+ * @return 0 on success, a negative AVERROR on error.
+ */
+int av_frame_get_buffer2(AVFrame *frame, int align);
+
 /**
  * Check if the frame data is writable.
  *
diff --git a/libavutil/subfmt.c b/libavutil/subfmt.c
new file mode 100644
index 0000000000..c72ebe2a43
--- /dev/null
+++ b/libavutil/subfmt.c
@@ -0,0 +1,45 @@
+/*
+ * 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
+ */
+
+#include <string.h>
+#include "common.h"
+#include "subfmt.h"
+
+static const char sub_fmt_info[AV_SUBTITLE_FMT_NB][24] = {
+    [AV_SUBTITLE_FMT_UNKNOWN] = "Unknown subtitle format",
+    [AV_SUBTITLE_FMT_BITMAP]  = "Graphical subtitles",
+    [AV_SUBTITLE_FMT_TEXT]    = "Text subtitles (plain)",
+    [AV_SUBTITLE_FMT_ASS]     = "Text subtitles (ass)",
+};
+
+const char *av_get_subtitle_fmt_name(enum AVSubtitleType sub_fmt)
+{
+    if (sub_fmt < 0 || sub_fmt >= AV_SUBTITLE_FMT_NB)
+        return NULL;
+    return sub_fmt_info[sub_fmt];
+}
+
+enum AVSubtitleType av_get_subtitle_fmt(const char *name)
+{
+    for (int i = 0; i < AV_SUBTITLE_FMT_NB; i++)
+        if (!strcmp(sub_fmt_info[i], name))
+            return i;
+    return AV_SUBTITLE_FMT_NONE;
+}
diff --git a/libavutil/subfmt.h b/libavutil/subfmt.h
index 791b45519f..3b8a0613b2 100644
--- a/libavutil/subfmt.h
+++ b/libavutil/subfmt.h
@@ -21,6 +21,9 @@
 #ifndef AVUTIL_SUBFMT_H
 #define AVUTIL_SUBFMT_H
 
+#include <stdint.h>
+
+#include "buffer.h"
 #include "version.h"
 
 enum AVSubtitleType {
@@ -65,4 +68,48 @@ enum AVSubtitleType {
     AV_SUBTITLE_FMT_NB,         ///< number of subtitle formats, DO NOT USE THIS if you want to link with shared libav* because the number of formats might differ between versions.
 };
 
+typedef struct AVSubtitleArea {
+#define AV_NUM_BUFFER_POINTERS 1
+
+    enum AVSubtitleType type;
+    int flags;
+
+    int x;         ///< top left corner  of area.
+    int y;         ///< top left corner  of area.
+    int w;         ///< width            of area.
+    int h;         ///< height           of area.
+    int nb_colors; ///< number of colors in bitmap palette (@ref pal).
+
+    /**
+     * Buffers and line sizes for the bitmap of this subtitle.
+     *
+     * @{
+     */
+    AVBufferRef *buf[AV_NUM_BUFFER_POINTERS];
+    int linesize[AV_NUM_BUFFER_POINTERS];
+    /**
+     * @}
+     */
+
+    uint32_t pal[256]; ///< RGBA palette for the bitmap.
+
+    char *text;        ///< 0-terminated plain UTF-8 text
+    char *ass;         ///< 0-terminated ASS/SSA compatible event line.
+
+} AVSubtitleArea;
+
+/**
+ * Return the name of sub_fmt, or NULL if sub_fmt is not
+ * recognized.
+ */
+const char *av_get_subtitle_fmt_name(enum AVSubtitleType sub_fmt);
+
+/**
+ * Return a subtitle format corresponding to name, or AV_SUBTITLE_FMT_NONE
+ * on error.
+ *
+ * @param name Subtitle format name.
+ */
+enum AVSubtitleType av_get_subtitle_fmt(const char *name);
+
 #endif /* AVUTIL_SUBFMT_H */
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v2 03/26] avcodec/subtitles: Introduce new frame-based subtitle decoding API
  2022-01-20  2:48 ` [FFmpeg-devel] [PATCH v2 00/26] Subtitle Filtering 2022 ffmpegagent
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 01/26] avcodec, avutil: Move enum AVSubtitleType to avutil, add new and deprecate old values ffmpegagent
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 02/26] avutil/frame: Prepare AVFrame for subtitle handling ffmpegagent
@ 2022-01-20  2:48   ` ffmpegagent
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 04/26] avfilter/subtitles: Update vf_subtitles to use new decoding api ffmpegagent
                     ` (23 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  2:48 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

- Modify avcodec_send_packet() to support subtitles via the regular
  frame based decoding API
- Add decode_subtitle_shim() which takes subtitle frames,
  and serves as a compatibility shim to the legacy subtitle decoding
  API until all subtitle decoders are migrated to the frame-based API
- Add additional methods for conversion between old and new API

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/avcodec.h    |   8 +-
 libavcodec/codec_desc.c |  11 +++
 libavcodec/codec_desc.h |   8 ++
 libavcodec/decode.c     |  60 +++++++++++--
 libavcodec/internal.h   |  16 ++++
 libavcodec/utils.c      | 184 ++++++++++++++++++++++++++++++++++++++++
 6 files changed, 278 insertions(+), 9 deletions(-)

diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index fe5a83cf85..9d59f6e840 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -1675,7 +1675,7 @@ typedef struct AVCodecContext {
 
     /**
      * Header containing style information for text subtitles.
-     * For SUBTITLE_ASS subtitle type, it should contain the whole ASS
+     * For AV_SUBTITLE_FMT_ASS subtitle type, it should contain the whole ASS
      * [Script Info] and [V4+ Styles] section, plus the [Events] line and
      * the Format line following. It shouldn't include any Dialogue line.
      * - encoding: Set/allocated/freed by user (before avcodec_open2())
@@ -2415,7 +2415,10 @@ int avcodec_close(AVCodecContext *avctx);
  * Free all allocated data in the given subtitle struct.
  *
  * @param sub AVSubtitle to free.
+ *
+ * @deprecated Use the regular frame based encode and decode APIs instead.
  */
+attribute_deprecated
 void avsubtitle_free(AVSubtitle *sub);
 
 /**
@@ -2508,7 +2511,10 @@ enum AVChromaLocation avcodec_chroma_pos_to_enum(int xpos, int ypos);
  *                 must be freed with avsubtitle_free if *got_sub_ptr is set.
  * @param[in,out] got_sub_ptr Zero if no subtitle could be decompressed, otherwise, it is nonzero.
  * @param[in] avpkt The input AVPacket containing the input buffer.
+ *
+ * @deprecated Use the new decode API (avcodec_send_packet, avcodec_receive_frame) instead.
  */
+attribute_deprecated
 int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub,
                             int *got_sub_ptr,
                             AVPacket *avpkt);
diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
index 0974ee03de..e48e4532ba 100644
--- a/libavcodec/codec_desc.c
+++ b/libavcodec/codec_desc.c
@@ -3548,3 +3548,14 @@ enum AVMediaType avcodec_get_type(enum AVCodecID codec_id)
     const AVCodecDescriptor *desc = avcodec_descriptor_get(codec_id);
     return desc ? desc->type : AVMEDIA_TYPE_UNKNOWN;
 }
+
+enum AVSubtitleType avcodec_descriptor_get_subtitle_format(const AVCodecDescriptor *codec_descriptor)
+{
+    if(codec_descriptor->props & AV_CODEC_PROP_BITMAP_SUB)
+        return AV_SUBTITLE_FMT_BITMAP;
+
+    if(codec_descriptor->props & AV_CODEC_PROP_TEXT_SUB)
+        return AV_SUBTITLE_FMT_ASS;
+
+    return AV_SUBTITLE_FMT_UNKNOWN;
+}
diff --git a/libavcodec/codec_desc.h b/libavcodec/codec_desc.h
index 126b52df47..ba68d24e0e 100644
--- a/libavcodec/codec_desc.h
+++ b/libavcodec/codec_desc.h
@@ -121,6 +121,14 @@ const AVCodecDescriptor *avcodec_descriptor_next(const AVCodecDescriptor *prev);
  */
 const AVCodecDescriptor *avcodec_descriptor_get_by_name(const char *name);
 
+/**
+ * Return subtitle format from a codec descriptor
+ *
+ * @param codec_descriptor codec descriptor
+ * @return                 the subtitle type (e.g. bitmap, text)
+ */
+enum AVSubtitleType avcodec_descriptor_get_subtitle_format(const AVCodecDescriptor *codec_descriptor);
+
 /**
  * @}
  */
diff --git a/libavcodec/decode.c b/libavcodec/decode.c
index 0912f86a14..e3cf1cfa3d 100644
--- a/libavcodec/decode.c
+++ b/libavcodec/decode.c
@@ -576,6 +576,39 @@ static int decode_receive_frame_internal(AVCodecContext *avctx, AVFrame *frame)
     return ret;
 }
 
+static int decode_subtitle2_priv(AVCodecContext *avctx, AVSubtitle *sub,
+                                 int *got_sub_ptr, AVPacket *avpkt);
+
+static int decode_subtitle_shim(AVCodecContext *avctx, AVFrame *frame, AVPacket *avpkt)
+{
+    int ret, got_sub_ptr = 0;
+    AVSubtitle subtitle = { 0 };
+
+    if (frame->buf[0])
+        return AVERROR(EAGAIN);
+
+    av_frame_unref(frame);
+
+    ret = decode_subtitle2_priv(avctx, &subtitle, &got_sub_ptr, avpkt);
+
+    if (ret >= 0 && got_sub_ptr) {
+        frame->type = AVMEDIA_TYPE_SUBTITLE;
+        frame->format = subtitle.format;
+        ret = av_frame_get_buffer2(frame, 0);
+
+        if (ret >= 0)
+            ret = ff_frame_put_subtitle(frame, &subtitle);
+
+        frame->width = avctx->width;
+        frame->height = avctx->height;
+        frame->pkt_dts = avpkt->dts;
+    }
+
+    avsubtitle_free(&subtitle);
+
+    return ret;
+}
+
 int attribute_align_arg avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt)
 {
     AVCodecInternal *avci = avctx->internal;
@@ -590,6 +623,13 @@ int attribute_align_arg avcodec_send_packet(AVCodecContext *avctx, const AVPacke
     if (avpkt && !avpkt->size && avpkt->data)
         return AVERROR(EINVAL);
 
+    if (avctx->codec_type == AVMEDIA_TYPE_SUBTITLE)
+		// this does not exactly implement the avcodec_send_packet/avcodec_receive_frame API 
+	    // but we know that no subtitle decoder produces multiple AVSubtitles per packet through
+		// the legacy API, and this will be changed when migrating the subtitle decoders
+		// to the frame based decoding api
+        return decode_subtitle_shim(avctx, avci->buffer_frame, avpkt);
+
     av_packet_unref(avci->buffer_pkt);
     if (avpkt && (avpkt->data || avpkt->side_data_elems)) {
         ret = av_packet_ref(avci->buffer_pkt, avpkt);
@@ -651,7 +691,9 @@ int attribute_align_arg avcodec_receive_frame(AVCodecContext *avctx, AVFrame *fr
 
     if (avci->buffer_frame->buf[0]) {
         av_frame_move_ref(frame, avci->buffer_frame);
-    } else {
+    } else if (avctx->codec_type == AVMEDIA_TYPE_SUBTITLE)
+        return AVERROR(EAGAIN);
+    else {
         ret = decode_receive_frame_internal(avctx, frame);
         if (ret < 0)
             return ret;
@@ -802,9 +844,8 @@ static int utf8_check(const uint8_t *str)
     return 1;
 }
 
-int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub,
-                             int *got_sub_ptr,
-                             AVPacket *avpkt)
+static int decode_subtitle2_priv(AVCodecContext *avctx, AVSubtitle *sub,
+                             int *got_sub_ptr, AVPacket *avpkt)
 {
     int ret = 0;
 
@@ -850,10 +891,7 @@ int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub,
                                                  avctx->pkt_timebase, ms);
         }
 
-        if (avctx->codec_descriptor->props & AV_CODEC_PROP_BITMAP_SUB)
-            sub->format = 0;
-        else if (avctx->codec_descriptor->props & AV_CODEC_PROP_TEXT_SUB)
-            sub->format = 1;
+        sub->format = avcodec_descriptor_get_subtitle_format(avctx->codec_descriptor);
 
         for (unsigned i = 0; i < sub->num_rects; i++) {
             if (avctx->sub_charenc_mode != FF_SUB_CHARENC_MODE_IGNORE &&
@@ -874,6 +912,12 @@ int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub,
     return ret;
 }
 
+int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub,
+                             int *got_sub_ptr, AVPacket *avpkt)
+{
+    return decode_subtitle2_priv(avctx, sub, got_sub_ptr, avpkt);
+}
+
 enum AVPixelFormat avcodec_default_get_format(struct AVCodecContext *avctx,
                                               const enum AVPixelFormat *fmt)
 {
diff --git a/libavcodec/internal.h b/libavcodec/internal.h
index 72ca1553f6..d2ef58474f 100644
--- a/libavcodec/internal.h
+++ b/libavcodec/internal.h
@@ -363,4 +363,20 @@ int ff_int_from_list_or_default(void *ctx, const char * val_name, int val,
 
 void ff_dvdsub_parse_palette(uint32_t *palette, const char *p);
 
+/**
+ * Copies subtitle data from AVSubtitle to AVFrame.
+ *
+ * @deprecated This is a compatibility method for interoperability with
+ * the legacy subtitle API.
+ */
+int ff_frame_put_subtitle(AVFrame* frame, const AVSubtitle* sub);
+
+/**
+ * Copies subtitle data from AVFrame to AVSubtitle.
+ *
+ * @deprecated This is a compatibility method for interoperability with
+ * the legacy subtitle API.
+ */
+int ff_frame_get_subtitle(AVSubtitle* sub, AVFrame* frame);
+
 #endif /* AVCODEC_INTERNAL_H */
diff --git a/libavcodec/utils.c b/libavcodec/utils.c
index b19befef21..72c742c176 100644
--- a/libavcodec/utils.c
+++ b/libavcodec/utils.c
@@ -813,6 +813,190 @@ int av_get_audio_frame_duration(AVCodecContext *avctx, int frame_bytes)
     return FFMAX(0, duration);
 }
 
+static int subtitle_area2rect(AVSubtitleRect *dst, const AVSubtitleArea *src)
+{
+    dst->x         =  src->x;
+    dst->y         =  src->y;
+    dst->w         =  src->w;
+    dst->h         =  src->h;
+    dst->nb_colors =  src->nb_colors;
+    dst->type      =  src->type;
+    dst->flags     =  src->flags;
+
+    switch (dst->type) {
+    case AV_SUBTITLE_FMT_BITMAP:
+
+        if (src->h > 0 && src->w > 0 && src->buf[0]) {
+            uint32_t *pal;
+            AVBufferRef *buf = src->buf[0];
+            dst->data[0] = av_mallocz(buf->size);
+            memcpy(dst->data[0], buf->data, buf->size);
+            dst->linesize[0] = src->linesize[0];
+
+            dst->data[1] = av_mallocz(256 * 4);
+            pal = (uint32_t *)dst->data[1];
+
+            for (unsigned i = 0; i < 256; i++) {
+                pal[i] = src->pal[i];
+            }
+        }
+
+        break;
+    case AV_SUBTITLE_FMT_TEXT:
+
+        if (src->text)
+            dst->text = av_strdup(src->text);
+        else
+            dst->text = av_strdup("");
+
+        if (!dst->text)
+            return AVERROR(ENOMEM);
+
+        break;
+    case AV_SUBTITLE_FMT_ASS:
+
+        if (src->ass)
+            dst->ass = av_strdup(src->ass);
+        else
+            dst->ass = av_strdup("");
+
+        if (!dst->ass)
+            return AVERROR(ENOMEM);
+
+        break;
+    default:
+
+        av_log(NULL, AV_LOG_ERROR, "Subtitle rect has invalid format: %d", dst->type);
+        return AVERROR(EINVAL);
+    }
+
+    return 0;
+}
+
+static int subtitle_rect2area(AVSubtitleArea *dst, const AVSubtitleRect *src)
+{
+    dst->x         =  src->x;
+    dst->y         =  src->y;
+    dst->w         =  src->w;
+    dst->h         =  src->h;
+    dst->nb_colors =  src->nb_colors;
+    dst->type      =  src->type;
+    dst->flags     =  src->flags;
+
+    switch (dst->type) {
+    case AV_SUBTITLE_FMT_BITMAP:
+
+        if (src->h > 0 && src->w > 0 && src->data[0]) {
+            AVBufferRef *buf = av_buffer_allocz(src->h * src->linesize[0]);
+            memcpy(buf->data, src->data[0], buf->size);
+
+            dst->buf[0] = buf;
+            dst->linesize[0] = src->linesize[0];
+        }
+
+        if (src->data[1]) {
+            uint32_t *pal = (uint32_t *)src->data[1];
+
+            for (unsigned i = 0; i < 256; i++) {
+                dst->pal[i] = pal[i];
+            }
+        }
+
+        break;
+    case AV_SUBTITLE_FMT_TEXT:
+
+        if (src->text) {
+            dst->text = av_strdup(src->text);
+            if (!dst->text)
+                return AVERROR(ENOMEM);
+        }
+
+        break;
+    case AV_SUBTITLE_FMT_ASS:
+
+        if (src->ass) {
+            dst->ass = av_strdup(src->ass);
+            if (!dst->ass)
+                return AVERROR(ENOMEM);
+        }
+
+        break;
+    default:
+
+        av_log(NULL, AV_LOG_ERROR, "Subtitle area has invalid format: %d", dst->type);
+        return AVERROR(EINVAL);
+    }
+
+    return 0;
+}
+
+/**
+ * Copies subtitle data from AVSubtitle (deprecated) to AVFrame
+ *
+ * @note This is a compatibility method for conversion to the legacy API
+ */
+int ff_frame_put_subtitle(AVFrame *frame, const AVSubtitle *sub)
+{
+    frame->format = sub->format;
+    frame->subtitle_timing.start_pts = sub->pts;
+    frame->subtitle_timing.start_pts += av_rescale_q(sub->start_display_time, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q);
+    frame->subtitle_timing.duration = av_rescale_q(sub->end_display_time - sub->start_display_time, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q);
+
+    if (sub->num_rects) {
+        frame->subtitle_areas = av_malloc_array(sub->num_rects, sizeof(AVSubtitleArea*));
+        if (!frame->subtitle_areas)
+            return AVERROR(ENOMEM);
+
+        for (unsigned i = 0; i < sub->num_rects; i++) {
+            int ret;
+            frame->subtitle_areas[i] = av_mallocz(sizeof(AVSubtitleArea));
+            if (!frame->subtitle_areas[i])
+                return AVERROR(ENOMEM);
+            ret = subtitle_rect2area(frame->subtitle_areas[i], sub->rects[i]);
+            if (ret < 0) {
+                frame->num_subtitle_areas = i;
+                return ret;
+            }
+        }
+    }
+
+    frame->num_subtitle_areas = sub->num_rects;
+    return 0;
+}
+
+/**
+ * Copies subtitle data from AVFrame to AVSubtitle (deprecated)
+ *
+ * @note This is a compatibility method for conversion to the legacy API
+ */
+int ff_frame_get_subtitle(AVSubtitle *sub, AVFrame *frame)
+{
+    const int64_t duration_ms = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, (AVRational){ 1, 1000 });
+
+    sub->start_display_time = 0;
+    sub->end_display_time = (int32_t)duration_ms;
+    sub->pts = frame->subtitle_timing.start_pts;
+
+    if (frame->num_subtitle_areas) {
+        sub->rects = av_malloc_array(frame->num_subtitle_areas, sizeof(AVSubtitleRect*));
+        if (!sub->rects)
+            return AVERROR(ENOMEM);
+
+        for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+            int ret;
+            sub->rects[i] = av_mallocz(sizeof(AVSubtitleRect));
+            ret = subtitle_area2rect(sub->rects[i], frame->subtitle_areas[i]);
+            if (ret < 0) {
+                sub->num_rects = i;
+                return ret;
+            }
+        }
+    }
+
+    sub->num_rects = frame->num_subtitle_areas;
+    return 0;
+}
+
 int av_get_audio_frame_duration2(AVCodecParameters *par, int frame_bytes)
 {
     int duration = get_audio_frame_duration(par->codec_id, par->sample_rate,
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v2 04/26] avfilter/subtitles: Update vf_subtitles to use new decoding api
  2022-01-20  2:48 ` [FFmpeg-devel] [PATCH v2 00/26] Subtitle Filtering 2022 ffmpegagent
                     ` (2 preceding siblings ...)
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 03/26] avcodec/subtitles: Introduce new frame-based subtitle decoding API ffmpegagent
@ 2022-01-20  2:48   ` ffmpegagent
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 05/26] avcodec, avutil: Move ass helper functions to avutil as avpriv_ and extend ass dialog parsing ffmpegagent
                     ` (22 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  2:48 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavfilter/vf_subtitles.c | 56 +++++++++++++++++++++++++++++---------
 1 file changed, 43 insertions(+), 13 deletions(-)

diff --git a/libavfilter/vf_subtitles.c b/libavfilter/vf_subtitles.c
index 3fc4eeb63d..25e217e845 100644
--- a/libavfilter/vf_subtitles.c
+++ b/libavfilter/vf_subtitles.c
@@ -35,14 +35,12 @@
 # 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"
 
 typedef struct AssContext {
     const AVClass *class;
@@ -292,6 +290,29 @@ static int attachment_is_font(AVStream * st)
     return 0;
 }
 
+static int decode(AVCodecContext *avctx, AVFrame *frame, int *got_frame, AVPacket *pkt)
+{
+    int ret;
+
+    *got_frame = 0;
+
+    if (pkt) {
+        ret = avcodec_send_packet(avctx, pkt);
+        // In particular, we don't expect AVERROR(EAGAIN), because we read all
+        // decoded frames with avcodec_receive_frame() until done.
+        if (ret < 0 && ret != AVERROR_EOF)
+            return ret;
+    }
+
+    ret = avcodec_receive_frame(avctx, frame);
+    if (ret < 0 && ret != AVERROR(EAGAIN))
+        return ret;
+    if (ret >= 0)
+        *got_frame = 1;
+
+    return 0;
+}
+
 AVFILTER_DEFINE_CLASS(subtitles);
 
 static av_cold int init_subtitles(AVFilterContext *ctx)
@@ -306,6 +327,7 @@ static av_cold int init_subtitles(AVFilterContext *ctx)
     AVStream *st;
     AVPacket pkt;
     AssContext *ass = ctx->priv;
+    enum AVSubtitleType subtitle_format;
 
     /* Init libass */
     ret = init(ctx);
@@ -386,13 +408,17 @@ static av_cold int init_subtitles(AVFilterContext *ctx)
         ret = AVERROR_DECODER_NOT_FOUND;
         goto end;
     }
+
     dec_desc = avcodec_descriptor_get(st->codecpar->codec_id);
-    if (dec_desc && !(dec_desc->props & AV_CODEC_PROP_TEXT_SUB)) {
+    subtitle_format = avcodec_descriptor_get_subtitle_format(dec_desc);
+
+    if (subtitle_format != AV_SUBTITLE_FMT_ASS) {
         av_log(ctx, AV_LOG_ERROR,
-               "Only text based subtitles are currently supported\n");
-        ret = AVERROR_PATCHWELCOME;
+               "Only text based subtitles are supported by this filter\n");
+        ret = AVERROR_INVALIDDATA;
         goto end;
     }
+
     if (ass->charenc)
         av_dict_set(&codec_opts, "sub_charenc", ass->charenc, 0);
 
@@ -448,27 +474,31 @@ static av_cold int init_subtitles(AVFilterContext *ctx)
                                   dec_ctx->subtitle_header_size);
     while (av_read_frame(fmt, &pkt) >= 0) {
         int i, got_subtitle;
-        AVSubtitle sub = {0};
+        AVFrame *sub = av_frame_alloc();
+        if (!sub) {
+            ret = AVERROR(ENOMEM);
+            goto end;
+        }
 
         if (pkt.stream_index == sid) {
-            ret = avcodec_decode_subtitle2(dec_ctx, &sub, &got_subtitle, &pkt);
+            ret = decode(dec_ctx, sub, &got_subtitle, &pkt);
             if (ret < 0) {
                 av_log(ctx, AV_LOG_WARNING, "Error decoding: %s (ignored)\n",
                        av_err2str(ret));
             } else if (got_subtitle) {
-                const int64_t start_time = av_rescale_q(sub.pts, AV_TIME_BASE_Q, av_make_q(1, 1000));
-                const int64_t duration   = sub.end_display_time;
-                for (i = 0; i < sub.num_rects; i++) {
-                    char *ass_line = sub.rects[i]->ass;
+                const int64_t start_time = av_rescale_q(sub->subtitle_timing.start_pts, AV_TIME_BASE_Q, av_make_q(1, 1000));
+                const int64_t duration   = av_rescale_q(sub->subtitle_timing.duration, AV_TIME_BASE_Q, av_make_q(1, 1000));
+                for (i = 0; i < sub->num_subtitle_areas; i++) {
+                    char *ass_line = sub->subtitle_areas[i]->ass;
                     if (!ass_line)
-                        break;
+                        continue;
                     ass_process_chunk(ass->track, ass_line, strlen(ass_line),
                                       start_time, duration);
                 }
             }
         }
         av_packet_unref(&pkt);
-        avsubtitle_free(&sub);
+        av_frame_free(&sub);
     }
 
 end:
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v2 05/26] avcodec, avutil: Move ass helper functions to avutil as avpriv_ and extend ass dialog parsing
  2022-01-20  2:48 ` [FFmpeg-devel] [PATCH v2 00/26] Subtitle Filtering 2022 ffmpegagent
                     ` (3 preceding siblings ...)
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 04/26] avfilter/subtitles: Update vf_subtitles to use new decoding api ffmpegagent
@ 2022-01-20  2:48   ` ffmpegagent
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 06/26] avcodec/subtitles: Replace deprecated enum values ffmpegagent
                     ` (21 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  2:48 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Also add

- hard_space callback (for upcoming fix)
- extensible callback (for future extension)

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/Makefile                           |  56 +++----
 libavcodec/ass.h                              | 144 ++++++------------
 libavcodec/assdec.c                           |   2 +-
 libavcodec/assenc.c                           |   2 +-
 libavcodec/ccaption_dec.c                     |  19 +--
 libavcodec/jacosubdec.c                       |   2 +-
 libavcodec/libaribb24.c                       |   2 +-
 libavcodec/libzvbi-teletextdec.c              |  14 +-
 libavcodec/microdvddec.c                      |   7 +-
 libavcodec/movtextdec.c                       |   3 +-
 libavcodec/movtextenc.c                       |  20 +--
 libavcodec/mpl2dec.c                          |   2 +-
 libavcodec/realtextdec.c                      |   2 +-
 libavcodec/samidec.c                          |   2 +-
 libavcodec/srtdec.c                           |   2 +-
 libavcodec/srtenc.c                           |  16 +-
 libavcodec/subviewerdec.c                     |   2 +-
 libavcodec/textdec.c                          |   4 +-
 libavcodec/ttmlenc.c                          |  15 +-
 libavcodec/webvttdec.c                        |   2 +-
 libavcodec/webvttenc.c                        |  16 +-
 libavutil/Makefile                            |   2 +
 {libavcodec => libavutil}/ass.c               |  91 +++++------
 libavutil/ass_internal.h                      | 135 ++++++++++++++++
 {libavcodec => libavutil}/ass_split.c         |  30 ++--
 .../ass_split_internal.h                      |  32 ++--
 26 files changed, 355 insertions(+), 269 deletions(-)
 rename {libavcodec => libavutil}/ass.c (65%)
 create mode 100644 libavutil/ass_internal.h
 rename {libavcodec => libavutil}/ass_split.c (94%)
 rename libavcodec/ass_split.h => libavutil/ass_split_internal.h (86%)

diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index cfc70a3eaf..80bf8ff2d2 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -215,10 +215,10 @@ OBJS-$(CONFIG_APNG_DECODER)            += png.o pngdec.o pngdsp.o
 OBJS-$(CONFIG_APNG_ENCODER)            += png.o pngenc.o
 OBJS-$(CONFIG_ARBC_DECODER)            += arbc.o
 OBJS-$(CONFIG_ARGO_DECODER)            += argo.o
-OBJS-$(CONFIG_SSA_DECODER)             += assdec.o ass.o
-OBJS-$(CONFIG_SSA_ENCODER)             += assenc.o ass.o
-OBJS-$(CONFIG_ASS_DECODER)             += assdec.o ass.o
-OBJS-$(CONFIG_ASS_ENCODER)             += assenc.o ass.o
+OBJS-$(CONFIG_SSA_DECODER)             += assdec.o
+OBJS-$(CONFIG_SSA_ENCODER)             += assenc.o
+OBJS-$(CONFIG_ASS_DECODER)             += assdec.o
+OBJS-$(CONFIG_ASS_ENCODER)             += assenc.o
 OBJS-$(CONFIG_ASV1_DECODER)            += asvdec.o asv.o mpeg12data.o
 OBJS-$(CONFIG_ASV1_ENCODER)            += asvenc.o asv.o mpeg12data.o
 OBJS-$(CONFIG_ASV2_DECODER)            += asvdec.o asv.o mpeg12data.o
@@ -259,7 +259,7 @@ OBJS-$(CONFIG_BRENDER_PIX_DECODER)     += brenderpix.o
 OBJS-$(CONFIG_C93_DECODER)             += c93.o
 OBJS-$(CONFIG_CAVS_DECODER)            += cavs.o cavsdec.o cavsdsp.o \
                                           cavsdata.o
-OBJS-$(CONFIG_CCAPTION_DECODER)        += ccaption_dec.o ass.o
+OBJS-$(CONFIG_CCAPTION_DECODER)        += ccaption_dec.o
 OBJS-$(CONFIG_CDGRAPHICS_DECODER)      += cdgraphics.o
 OBJS-$(CONFIG_CDTOONS_DECODER)         += cdtoons.o
 OBJS-$(CONFIG_CDXL_DECODER)            += cdxl.o
@@ -434,7 +434,7 @@ OBJS-$(CONFIG_INTERPLAY_ACM_DECODER)   += interplayacm.o
 OBJS-$(CONFIG_INTERPLAY_DPCM_DECODER)  += dpcm.o
 OBJS-$(CONFIG_INTERPLAY_VIDEO_DECODER) += interplayvideo.o
 OBJS-$(CONFIG_IPU_DECODER)             += mpeg12dec.o mpeg12.o mpeg12data.o
-OBJS-$(CONFIG_JACOSUB_DECODER)         += jacosubdec.o ass.o
+OBJS-$(CONFIG_JACOSUB_DECODER)         += jacosubdec.o
 OBJS-$(CONFIG_JPEG2000_ENCODER)        += j2kenc.o mqcenc.o mqc.o jpeg2000.o \
                                           jpeg2000dwt.o
 OBJS-$(CONFIG_JPEG2000_DECODER)        += jpeg2000dec.o jpeg2000.o jpeg2000dsp.o \
@@ -456,7 +456,7 @@ OBJS-$(CONFIG_MAGICYUV_ENCODER)        += magicyuvenc.o
 OBJS-$(CONFIG_MDEC_DECODER)            += mdec.o mpeg12.o mpeg12data.o
 OBJS-$(CONFIG_METASOUND_DECODER)       += metasound.o metasound_data.o \
                                           twinvq.o
-OBJS-$(CONFIG_MICRODVD_DECODER)        += microdvddec.o ass.o
+OBJS-$(CONFIG_MICRODVD_DECODER)        += microdvddec.o
 OBJS-$(CONFIG_MIMIC_DECODER)           += mimic.o
 OBJS-$(CONFIG_MJPEG_DECODER)           += mjpegdec.o mjpegdec_common.o
 OBJS-$(CONFIG_MJPEG_QSV_DECODER)       += qsvdec.o
@@ -471,8 +471,8 @@ OBJS-$(CONFIG_MLP_ENCODER)             += mlpenc.o mlp.o
 OBJS-$(CONFIG_MMVIDEO_DECODER)         += mmvideo.o
 OBJS-$(CONFIG_MOBICLIP_DECODER)        += mobiclip.o
 OBJS-$(CONFIG_MOTIONPIXELS_DECODER)    += motionpixels.o
-OBJS-$(CONFIG_MOVTEXT_DECODER)         += movtextdec.o ass.o
-OBJS-$(CONFIG_MOVTEXT_ENCODER)         += movtextenc.o ass_split.o
+OBJS-$(CONFIG_MOVTEXT_DECODER)         += movtextdec.o
+OBJS-$(CONFIG_MOVTEXT_ENCODER)         += movtextenc.o
 OBJS-$(CONFIG_MP1_DECODER)             += mpegaudiodec_fixed.o
 OBJS-$(CONFIG_MP1FLOAT_DECODER)        += mpegaudiodec_float.o
 OBJS-$(CONFIG_MP2_DECODER)             += mpegaudiodec_fixed.o
@@ -513,7 +513,7 @@ OBJS-$(CONFIG_MPEG4_MEDIACODEC_DECODER) += mediacodecdec.o
 OBJS-$(CONFIG_MPEG4_OMX_ENCODER)       += omx.o
 OBJS-$(CONFIG_MPEG4_V4L2M2M_DECODER)   += v4l2_m2m_dec.o
 OBJS-$(CONFIG_MPEG4_V4L2M2M_ENCODER)   += v4l2_m2m_enc.o
-OBJS-$(CONFIG_MPL2_DECODER)            += mpl2dec.o ass.o
+OBJS-$(CONFIG_MPL2_DECODER)            += mpl2dec.o
 OBJS-$(CONFIG_MSA1_DECODER)            += mss3.o
 OBJS-$(CONFIG_MSCC_DECODER)            += mscc.o
 OBJS-$(CONFIG_MSMPEG4V1_DECODER)       += msmpeg4dec.o msmpeg4.o msmpeg4data.o
@@ -566,7 +566,7 @@ OBJS-$(CONFIG_PGX_DECODER)             += pgxdec.o
 OBJS-$(CONFIG_PHOTOCD_DECODER)         += photocd.o
 OBJS-$(CONFIG_PICTOR_DECODER)          += pictordec.o cga_data.o
 OBJS-$(CONFIG_PIXLET_DECODER)          += pixlet.o
-OBJS-$(CONFIG_PJS_DECODER)             += textdec.o ass.o
+OBJS-$(CONFIG_PJS_DECODER)             += textdec.o
 OBJS-$(CONFIG_PNG_DECODER)             += png.o pngdec.o pngdsp.o
 OBJS-$(CONFIG_PNG_ENCODER)             += png.o pngenc.o
 OBJS-$(CONFIG_PPM_DECODER)             += pnmdec.o pnm.o
@@ -599,7 +599,7 @@ OBJS-$(CONFIG_RALF_DECODER)            += ralf.o
 OBJS-$(CONFIG_RASC_DECODER)            += rasc.o
 OBJS-$(CONFIG_RAWVIDEO_DECODER)        += rawdec.o
 OBJS-$(CONFIG_RAWVIDEO_ENCODER)        += rawenc.o
-OBJS-$(CONFIG_REALTEXT_DECODER)        += realtextdec.o ass.o
+OBJS-$(CONFIG_REALTEXT_DECODER)        += realtextdec.o
 OBJS-$(CONFIG_RL2_DECODER)             += rl2.o
 OBJS-$(CONFIG_ROQ_DECODER)             += roqvideodec.o roqvideo.o
 OBJS-$(CONFIG_ROQ_ENCODER)             += roqvideoenc.o roqvideo.o elbg.o
@@ -614,7 +614,7 @@ OBJS-$(CONFIG_RV20_DECODER)            += rv10.o
 OBJS-$(CONFIG_RV20_ENCODER)            += rv20enc.o
 OBJS-$(CONFIG_RV30_DECODER)            += rv30.o rv34.o rv30dsp.o
 OBJS-$(CONFIG_RV40_DECODER)            += rv40.o rv34.o rv40dsp.o
-OBJS-$(CONFIG_SAMI_DECODER)            += samidec.o ass.o htmlsubtitles.o
+OBJS-$(CONFIG_SAMI_DECODER)            += samidec.o htmlsubtitles.o
 OBJS-$(CONFIG_S302M_DECODER)           += s302m.o
 OBJS-$(CONFIG_S302M_ENCODER)           += s302menc.o
 OBJS-$(CONFIG_SANM_DECODER)            += sanm.o
@@ -649,13 +649,13 @@ OBJS-$(CONFIG_SPEEDHQ_ENCODER)         += speedhq.o mpeg12data.o mpeg12enc.o spe
 OBJS-$(CONFIG_SPEEX_DECODER)           += speexdec.o
 OBJS-$(CONFIG_SP5X_DECODER)            += sp5xdec.o
 OBJS-$(CONFIG_SRGC_DECODER)            += mscc.o
-OBJS-$(CONFIG_SRT_DECODER)             += srtdec.o ass.o htmlsubtitles.o
-OBJS-$(CONFIG_SRT_ENCODER)             += srtenc.o ass_split.o
-OBJS-$(CONFIG_STL_DECODER)             += textdec.o ass.o
-OBJS-$(CONFIG_SUBRIP_DECODER)          += srtdec.o ass.o htmlsubtitles.o
-OBJS-$(CONFIG_SUBRIP_ENCODER)          += srtenc.o ass_split.o
-OBJS-$(CONFIG_SUBVIEWER1_DECODER)      += textdec.o ass.o
-OBJS-$(CONFIG_SUBVIEWER_DECODER)       += subviewerdec.o ass.o
+OBJS-$(CONFIG_SRT_DECODER)             += srtdec.o htmlsubtitles.o
+OBJS-$(CONFIG_SRT_ENCODER)             += srtenc.o
+OBJS-$(CONFIG_STL_DECODER)             += textdec.o
+OBJS-$(CONFIG_SUBRIP_DECODER)          += srtdec.o htmlsubtitles.o
+OBJS-$(CONFIG_SUBRIP_ENCODER)          += srtenc.o
+OBJS-$(CONFIG_SUBVIEWER1_DECODER)      += textdec.o
+OBJS-$(CONFIG_SUBVIEWER_DECODER)       += subviewerdec.o
 OBJS-$(CONFIG_SUNRAST_DECODER)         += sunrast.o
 OBJS-$(CONFIG_SUNRAST_ENCODER)         += sunrastenc.o
 OBJS-$(CONFIG_LIBRSVG_DECODER)         += librsvgdec.o
@@ -665,8 +665,8 @@ OBJS-$(CONFIG_SVQ1_DECODER)            += svq1dec.o svq1.o h263data.o
 OBJS-$(CONFIG_SVQ1_ENCODER)            += svq1enc.o svq1.o  h263data.o  \
                                           h263.o ituh263enc.o
 OBJS-$(CONFIG_SVQ3_DECODER)            += svq3.o mpegutils.o h264data.o
-OBJS-$(CONFIG_TEXT_DECODER)            += textdec.o ass.o
-OBJS-$(CONFIG_TEXT_ENCODER)            += srtenc.o ass_split.o
+OBJS-$(CONFIG_TEXT_DECODER)            += textdec.o
+OBJS-$(CONFIG_TEXT_ENCODER)            += srtenc.o
 OBJS-$(CONFIG_TAK_DECODER)             += takdec.o tak.o takdsp.o
 OBJS-$(CONFIG_TARGA_DECODER)           += targa.o
 OBJS-$(CONFIG_TARGA_ENCODER)           += targaenc.o rle.o
@@ -686,7 +686,7 @@ OBJS-$(CONFIG_TSCC_DECODER)            += tscc.o msrledec.o
 OBJS-$(CONFIG_TSCC2_DECODER)           += tscc2.o
 OBJS-$(CONFIG_TTA_DECODER)             += tta.o ttadata.o ttadsp.o
 OBJS-$(CONFIG_TTA_ENCODER)             += ttaenc.o ttaencdsp.o ttadata.o
-OBJS-$(CONFIG_TTML_ENCODER)            += ttmlenc.o ass_split.o
+OBJS-$(CONFIG_TTML_ENCODER)            += ttmlenc.o
 OBJS-$(CONFIG_TWINVQ_DECODER)          += twinvqdec.o twinvq.o metasound_data.o
 OBJS-$(CONFIG_TXD_DECODER)             += txd.o
 OBJS-$(CONFIG_ULTI_DECODER)            += ulti.o
@@ -741,15 +741,15 @@ OBJS-$(CONFIG_VP9_MEDIACODEC_DECODER)  += mediacodecdec.o
 OBJS-$(CONFIG_VP9_RKMPP_DECODER)       += rkmppdec.o
 OBJS-$(CONFIG_VP9_VAAPI_ENCODER)       += vaapi_encode_vp9.o
 OBJS-$(CONFIG_VP9_QSV_ENCODER)         += qsvenc_vp9.o
-OBJS-$(CONFIG_VPLAYER_DECODER)         += textdec.o ass.o
+OBJS-$(CONFIG_VPLAYER_DECODER)         += textdec.o
 OBJS-$(CONFIG_VP9_V4L2M2M_DECODER)     += v4l2_m2m_dec.o
 OBJS-$(CONFIG_VQA_DECODER)             += vqavideo.o
 OBJS-$(CONFIG_WAVPACK_DECODER)         += wavpack.o wavpackdata.o dsd.o
 OBJS-$(CONFIG_WAVPACK_ENCODER)         += wavpackdata.o wavpackenc.o
 OBJS-$(CONFIG_WCMV_DECODER)            += wcmv.o
 OBJS-$(CONFIG_WEBP_DECODER)            += webp.o
-OBJS-$(CONFIG_WEBVTT_DECODER)          += webvttdec.o ass.o
-OBJS-$(CONFIG_WEBVTT_ENCODER)          += webvttenc.o ass_split.o
+OBJS-$(CONFIG_WEBVTT_DECODER)          += webvttdec.o
+OBJS-$(CONFIG_WEBVTT_ENCODER)          += webvttenc.o
 OBJS-$(CONFIG_WMALOSSLESS_DECODER)     += wmalosslessdec.o wma_common.o
 OBJS-$(CONFIG_WMAPRO_DECODER)          += wmaprodec.o wma.o wma_common.o
 OBJS-$(CONFIG_WMAV1_DECODER)           += wmadec.o wma.o wma_common.o aactab.o
@@ -1040,7 +1040,7 @@ OBJS-$(CONFIG_PCM_ALAW_AT_ENCODER)        += audiotoolboxenc.o
 OBJS-$(CONFIG_PCM_MULAW_AT_ENCODER)       += audiotoolboxenc.o
 OBJS-$(CONFIG_LIBAOM_AV1_DECODER)         += libaomdec.o
 OBJS-$(CONFIG_LIBAOM_AV1_ENCODER)         += libaomenc.o
-OBJS-$(CONFIG_LIBARIBB24_DECODER)         += libaribb24.o ass.o
+OBJS-$(CONFIG_LIBARIBB24_DECODER)         += libaribb24.o
 OBJS-$(CONFIG_LIBCELT_DECODER)            += libcelt_dec.o
 OBJS-$(CONFIG_LIBCODEC2_DECODER)          += libcodec2.o
 OBJS-$(CONFIG_LIBCODEC2_ENCODER)          += libcodec2.o
@@ -1091,7 +1091,7 @@ OBJS-$(CONFIG_LIBX265_ENCODER)            += libx265.o
 OBJS-$(CONFIG_LIBXAVS_ENCODER)            += libxavs.o
 OBJS-$(CONFIG_LIBXAVS2_ENCODER)           += libxavs2.o
 OBJS-$(CONFIG_LIBXVID_ENCODER)            += libxvid.o
-OBJS-$(CONFIG_LIBZVBI_TELETEXT_DECODER)   += libzvbi-teletextdec.o ass.o
+OBJS-$(CONFIG_LIBZVBI_TELETEXT_DECODER)   += libzvbi-teletextdec.o
 
 # parsers
 OBJS-$(CONFIG_AAC_LATM_PARSER)         += latm_parser.o
diff --git a/libavcodec/ass.h b/libavcodec/ass.h
index 2c260e4e78..8bc13d7ab8 100644
--- a/libavcodec/ass.h
+++ b/libavcodec/ass.h
@@ -23,117 +23,73 @@
 #define AVCODEC_ASS_H
 
 #include "avcodec.h"
-#include "libavutil/bprint.h"
-
-#define ASS_DEFAULT_PLAYRESX 384
-#define ASS_DEFAULT_PLAYRESY 288
-
-/**
- * @name Default values for ASS style
- * @{
- */
-#define ASS_DEFAULT_FONT        "Arial"
-#define ASS_DEFAULT_FONT_SIZE   16
-#define ASS_DEFAULT_COLOR       0xffffff
-#define ASS_DEFAULT_BACK_COLOR  0
-#define ASS_DEFAULT_BOLD        0
-#define ASS_DEFAULT_ITALIC      0
-#define ASS_DEFAULT_UNDERLINE   0
-#define ASS_DEFAULT_ALIGNMENT   2
-#define ASS_DEFAULT_BORDERSTYLE 1
-/** @} */
+#include "libavutil/ass_internal.h"
 
 typedef struct FFASSDecoderContext {
     int readorder;
 } FFASSDecoderContext;
 
-/**
- * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS.
- * Can specify all fields explicitly
- *
- * @param avctx pointer to the AVCodecContext
- * @param play_res_x subtitle frame width
- * @param play_res_y subtitle frame height
- * @param font name of the default font face to use
- * @param font_size default font size to use
- * @param primary_color default text color to use (ABGR)
- * @param secondary_color default secondary text color to use (ABGR)
- * @param outline_color default outline color to use (ABGR)
- * @param back_color default background color to use (ABGR)
- * @param bold 1 for bold text, 0 for normal text
- * @param italic 1 for italic text, 0 for normal text
- * @param underline 1 for underline text, 0 for normal text
- * @param border_style 1 for outline, 3 for opaque box
- * @param alignment position of the text (left, center, top...), defined after
- *                  the layout of the numpad (1-3 sub, 4-6 mid, 7-9 top)
- * @return >= 0 on success otherwise an error code <0
- */
-int ff_ass_subtitle_header_full(AVCodecContext *avctx,
+static inline int ff_ass_subtitle_header_full(AVCodecContext *avctx,
                                 int play_res_x, int play_res_y,
                                 const char *font, int font_size,
                                 int primary_color, int secondary_color,
                                 int outline_color, int back_color,
                                 int bold, int italic, int underline,
-                                int border_style, int alignment);
-/**
- * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS.
- *
- * @param avctx pointer to the AVCodecContext
- * @param font name of the default font face to use
- * @param font_size default font size to use
- * @param color default text color to use (ABGR)
- * @param back_color default background color to use (ABGR)
- * @param bold 1 for bold text, 0 for normal text
- * @param italic 1 for italic text, 0 for normal text
- * @param underline 1 for underline text, 0 for normal text
- * @param alignment position of the text (left, center, top...), defined after
- *                  the layout of the numpad (1-3 sub, 4-6 mid, 7-9 top)
- * @return >= 0 on success otherwise an error code <0
- */
-int ff_ass_subtitle_header(AVCodecContext *avctx,
-                           const char *font, int font_size,
-                           int color, int back_color,
-                           int bold, int italic, int underline,
-                           int border_style, int alignment);
+                                int border_style, int alignment)
+{
+    avctx->subtitle_header = (uint8_t *)avpriv_ass_get_subtitle_header_full(
+                                play_res_x, play_res_y, font, font_size,
+                                primary_color, secondary_color, outline_color,
+                                back_color, bold,italic,underline,border_style,alignment,
+                                !(avctx->flags & AV_CODEC_FLAG_BITEXACT));
 
-/**
- * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS
- * with default style.
- *
- * @param avctx pointer to the AVCodecContext
- * @return >= 0 on success otherwise an error code <0
- */
-int ff_ass_subtitle_header_default(AVCodecContext *avctx);
+    if (!avctx->subtitle_header)
+        return AVERROR(ENOMEM);
+    avctx->subtitle_header_size = strlen((char *)avctx->subtitle_header);
+    return 0;
+}
 
-/**
- * Craft an ASS dialog string.
- */
-char *ff_ass_get_dialog(int readorder, int layer, const char *style,
-                        const char *speaker, const char *text);
+static inline int ff_ass_subtitle_header_default(AVCodecContext *avctx)
+{
+    avctx->subtitle_header = (uint8_t *)avpriv_ass_get_subtitle_header_default(!(avctx->flags & AV_CODEC_FLAG_BITEXACT));
+
+    if (!avctx->subtitle_header)
+        return AVERROR(ENOMEM);
+    avctx->subtitle_header_size = strlen((char *)avctx->subtitle_header);
+    return 0;
+}
+
+static inline void ff_ass_decoder_flush(AVCodecContext *avctx)
+{
+    FFASSDecoderContext *s = avctx->priv_data;
+    if (!(avctx->flags2 & AV_CODEC_FLAG2_RO_FLUSH_NOOP))
+        s->readorder = 0;
+}
 
 /**
  * Add an ASS dialog to a subtitle.
  */
-int ff_ass_add_rect(AVSubtitle *sub, const char *dialog,
+static inline int avpriv_ass_add_rect(AVSubtitle *sub, const char *dialog,
                     int readorder, int layer, const char *style,
-                    const char *speaker);
+                    const char *speaker)
+{
+    char *ass_str;
+    AVSubtitleRect **rects;
 
-/**
- * Helper to flush a text subtitles decoder making use of the
- * FFASSDecoderContext.
- */
-void ff_ass_decoder_flush(AVCodecContext *avctx);
+    rects = av_realloc_array(sub->rects, sub->num_rects+1, sizeof(*sub->rects));
+    if (!rects)
+        return AVERROR(ENOMEM);
+    sub->rects = rects;
+    rects[sub->num_rects]       = av_mallocz(sizeof(*rects[0]));
+    if (!rects[sub->num_rects])
+        return AVERROR(ENOMEM);
+    rects[sub->num_rects]->type = SUBTITLE_ASS;
+    ass_str = avpriv_ass_get_dialog(readorder, layer, style, speaker, dialog);
+    if (!ass_str)
+        return AVERROR(ENOMEM);
+    rects[sub->num_rects]->ass = ass_str;
+    sub->num_rects++;
+    return 0;
+}
 
-/**
- * Escape a text subtitle using ASS syntax into an AVBPrint buffer.
- * Newline characters will be escaped to \N.
- *
- * @param buf pointer to an initialized AVBPrint buffer
- * @param p source text
- * @param size size of the source text
- * @param linebreaks additional newline chars, which will be escaped to \N
- * @param keep_ass_markup braces and backslash will not be escaped if set
- */
-void ff_ass_bprint_text_event(AVBPrint *buf, const char *p, int size,
-                             const char *linebreaks, int keep_ass_markup);
 #endif /* AVCODEC_ASS_H */
diff --git a/libavcodec/assdec.c b/libavcodec/assdec.c
index 319279490c..7802a44e71 100644
--- a/libavcodec/assdec.c
+++ b/libavcodec/assdec.c
@@ -22,7 +22,7 @@
 #include <string.h>
 
 #include "avcodec.h"
-#include "ass.h"
+#include "libavutil/ass_internal.h"
 #include "internal.h"
 #include "libavutil/internal.h"
 #include "libavutil/mem.h"
diff --git a/libavcodec/assenc.c b/libavcodec/assenc.c
index a6d107ded2..b0e475834b 100644
--- a/libavcodec/assenc.c
+++ b/libavcodec/assenc.c
@@ -22,7 +22,7 @@
 #include <string.h>
 
 #include "avcodec.h"
-#include "ass.h"
+#include "libavutil/ass_internal.h"
 #include "internal.h"
 #include "libavutil/avstring.h"
 #include "libavutil/internal.h"
diff --git a/libavcodec/ccaption_dec.c b/libavcodec/ccaption_dec.c
index 27c61527f6..27eef75657 100644
--- a/libavcodec/ccaption_dec.c
+++ b/libavcodec/ccaption_dec.c
@@ -272,15 +272,12 @@ static av_cold int init_decoder(AVCodecContext *avctx)
     ctx->bg_color = CCCOL_BLACK;
     ctx->rollup = 2;
     ctx->cursor_row = 10;
-    ret = ff_ass_subtitle_header(avctx, "Monospace",
+    ret = ff_ass_subtitle_header_full(avctx, ASS_DEFAULT_PLAYRESX, ASS_DEFAULT_PLAYRESY, "Monospace",
                                  ASS_DEFAULT_FONT_SIZE,
-                                 ASS_DEFAULT_COLOR,
-                                 ASS_DEFAULT_BACK_COLOR,
-                                 ASS_DEFAULT_BOLD,
-                                 ASS_DEFAULT_ITALIC,
-                                 ASS_DEFAULT_UNDERLINE,
-                                 3,
-                                 ASS_DEFAULT_ALIGNMENT);
+                                 ASS_DEFAULT_COLOR, ASS_DEFAULT_COLOR,
+                                 ASS_DEFAULT_BACK_COLOR, ASS_DEFAULT_BACK_COLOR,
+                                 ASS_DEFAULT_BOLD, ASS_DEFAULT_ITALIC, ASS_DEFAULT_UNDERLINE,
+                                 3, ASS_DEFAULT_ALIGNMENT);
     if (ret < 0) {
         return ret;
     }
@@ -886,7 +883,7 @@ static int decode(AVCodecContext *avctx, void *data, int *got_sub, AVPacket *avp
                                                      AV_TIME_BASE_Q, ms_tb);
             else
                 sub->end_display_time = -1;
-            ret = ff_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL);
+            ret = avpriv_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL);
             if (ret < 0)
                 return ret;
             ctx->last_real_time = sub->pts;
@@ -896,7 +893,7 @@ static int decode(AVCodecContext *avctx, void *data, int *got_sub, AVPacket *avp
 
     if (!bptr && !ctx->real_time && ctx->buffer[!ctx->buffer_index].str[0]) {
         bidx = !ctx->buffer_index;
-        ret = ff_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL);
         if (ret < 0)
             return ret;
         sub->pts = ctx->buffer_time[1];
@@ -914,7 +911,7 @@ static int decode(AVCodecContext *avctx, void *data, int *got_sub, AVPacket *avp
         capture_screen(ctx);
         ctx->buffer_changed = 0;
 
-        ret = ff_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL);
         if (ret < 0)
             return ret;
         sub->end_display_time = -1;
diff --git a/libavcodec/jacosubdec.c b/libavcodec/jacosubdec.c
index 698895a86b..6a53ec3e34 100644
--- a/libavcodec/jacosubdec.c
+++ b/libavcodec/jacosubdec.c
@@ -183,7 +183,7 @@ static int jacosub_decode_frame(AVCodecContext *avctx,
 
         av_bprint_init(&buffer, JSS_MAX_LINESIZE, JSS_MAX_LINESIZE);
         jacosub_to_ass(avctx, &buffer, ptr);
-        ret = ff_ass_add_rect(sub, buffer.str, s->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, buffer.str, s->readorder++, 0, NULL, NULL);
         av_bprint_finalize(&buffer, NULL);
         if (ret < 0)
             return ret;
diff --git a/libavcodec/libaribb24.c b/libavcodec/libaribb24.c
index 0766c0079d..3fb7e5f16e 100644
--- a/libavcodec/libaribb24.c
+++ b/libavcodec/libaribb24.c
@@ -273,7 +273,7 @@ next_region:
         av_log(avctx, AV_LOG_DEBUG, "Styled ASS line: %s\n",
                buf.str);
 
-        ret = ff_ass_add_rect(sub, buf.str, b24->read_order++,
+        ret = avpriv_ass_add_rect(sub, buf.str, b24->read_order++,
                               0, NULL, NULL);
     }
 
diff --git a/libavcodec/libzvbi-teletextdec.c b/libavcodec/libzvbi-teletextdec.c
index 1073d6a0bd..bd9edc34d7 100644
--- a/libavcodec/libzvbi-teletextdec.c
+++ b/libavcodec/libzvbi-teletextdec.c
@@ -152,12 +152,12 @@ static char *create_ass_text(TeletextContext *ctx, const char *text)
     AVBPrint buf;
 
     av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
-    ff_ass_bprint_text_event(&buf, text, strlen(text), "", 0);
+    avpriv_ass_bprint_text_event(&buf, text, strlen(text), "", 0);
     if (!av_bprint_is_complete(&buf)) {
         av_bprint_finalize(&buf, NULL);
         return NULL;
     }
-    dialog = ff_ass_get_dialog(ctx->readorder++, 0, NULL, NULL, buf.str);
+    dialog = avpriv_ass_get_dialog(ctx->readorder++, 0, NULL, NULL, buf.str);
     av_bprint_finalize(&buf, NULL);
     return dialog;
 }
@@ -224,7 +224,7 @@ static int gen_sub_text(TeletextContext *ctx, AVSubtitleRect *sub_rect, vbi_page
         }
         av_log(ctx, AV_LOG_DEBUG, "subtext:%s:txetbus\n", sub_rect->ass);
     } else {
-        sub_rect->type = SUBTITLE_NONE;
+        sub_rect->type = AV_SUBTITLE_FMT_NONE;
     }
     av_bprint_finalize(&buf, NULL);
     return 0;
@@ -394,7 +394,7 @@ static int gen_sub_ass(TeletextContext *ctx, AVSubtitleRect *sub_rect, vbi_page
 
     if (buf.len) {
         sub_rect->type = SUBTITLE_ASS;
-        sub_rect->ass = ff_ass_get_dialog(ctx->readorder++, 0, is_subtitle_page ? "Subtitle" : "Teletext", NULL, buf.str);
+        sub_rect->ass = avpriv_ass_get_dialog(ctx->readorder++, 0, is_subtitle_page ? "Subtitle" : "Teletext", NULL, buf.str);
 
         if (!sub_rect->ass) {
             av_bprint_finalize(&buf, NULL);
@@ -402,7 +402,7 @@ static int gen_sub_ass(TeletextContext *ctx, AVSubtitleRect *sub_rect, vbi_page
         }
         av_log(ctx, AV_LOG_DEBUG, "subtext:%s:txetbus\n", sub_rect->ass);
     } else {
-        sub_rect->type = SUBTITLE_NONE;
+        sub_rect->type = AV_SUBTITLE_FMT_NONE;
     }
     av_bprint_finalize(&buf, NULL);
     return 0;
@@ -462,7 +462,7 @@ static int gen_sub_bitmap(TeletextContext *ctx, AVSubtitleRect *sub_rect, vbi_pa
 
     if (vc >= vcend) {
         av_log(ctx, AV_LOG_DEBUG, "dropping empty page %3x\n", page->pgno);
-        sub_rect->type = SUBTITLE_NONE;
+        sub_rect->type = AV_SUBTITLE_FMT_NONE;
         return 0;
     }
 
@@ -695,7 +695,7 @@ static int teletext_decode_frame(AVCodecContext *avctx, void *data, int *got_sub
         sub->num_rects = 0;
         sub->pts = ctx->pages->pts;
 
-        if (ctx->pages->sub_rect->type != SUBTITLE_NONE) {
+        if (ctx->pages->sub_rect->type != AV_SUBTITLE_FMT_NONE) {
             sub->rects = av_malloc(sizeof(*sub->rects));
             if (sub->rects) {
                 sub->num_rects = 1;
diff --git a/libavcodec/microdvddec.c b/libavcodec/microdvddec.c
index c45fe043bf..fc09db8997 100644
--- a/libavcodec/microdvddec.c
+++ b/libavcodec/microdvddec.c
@@ -310,7 +310,7 @@ static int microdvd_decode_frame(AVCodecContext *avctx,
         }
     }
     if (new_line.len) {
-        int ret = ff_ass_add_rect(sub, new_line.str, s->readorder++, 0, NULL, NULL);
+        int ret = avpriv_ass_add_rect(sub, new_line.str, s->readorder++, 0, NULL, NULL);
         av_bprint_finalize(&new_line, NULL);
         if (ret < 0)
             return ret;
@@ -363,8 +363,9 @@ static int microdvd_init(AVCodecContext *avctx)
             }
         }
     }
-    return ff_ass_subtitle_header(avctx, font_buf.str, font_size, color,
-                                  ASS_DEFAULT_BACK_COLOR, bold, italic,
+    return ff_ass_subtitle_header_full(avctx, ASS_DEFAULT_PLAYRESX, ASS_DEFAULT_PLAYRESY,
+                                  font_buf.str, font_size, color, color,
+                                  ASS_DEFAULT_BACK_COLOR, ASS_DEFAULT_BACK_COLOR, bold, italic,
                                   underline, ASS_DEFAULT_BORDERSTYLE,
                                   alignment);
 }
diff --git a/libavcodec/movtextdec.c b/libavcodec/movtextdec.c
index 825632ca9b..9b17bac9ea 100644
--- a/libavcodec/movtextdec.c
+++ b/libavcodec/movtextdec.c
@@ -22,7 +22,6 @@
 #include "avcodec.h"
 #include "ass.h"
 #include "libavutil/opt.h"
-#include "libavutil/avstring.h"
 #include "libavutil/common.h"
 #include "libavutil/bprint.h"
 #include "libavutil/intreadwrite.h"
@@ -554,7 +553,7 @@ static int mov_text_decode_frame(AVCodecContext *avctx,
     } else
         text_to_ass(&buf, ptr, end, avctx);
 
-    ret = ff_ass_add_rect(sub, buf.str, m->readorder++, 0, NULL, NULL);
+    ret = avpriv_ass_add_rect(sub, buf.str, m->readorder++, 0, NULL, NULL);
     av_bprint_finalize(&buf, NULL);
     if (ret < 0)
         return ret;
diff --git a/libavcodec/movtextenc.c b/libavcodec/movtextenc.c
index 221cd76fea..d506ed5c37 100644
--- a/libavcodec/movtextenc.c
+++ b/libavcodec/movtextenc.c
@@ -26,8 +26,8 @@
 #include "libavutil/intreadwrite.h"
 #include "libavutil/mem.h"
 #include "libavutil/common.h"
-#include "ass_split.h"
-#include "ass.h"
+#include "libavutil/ass_split_internal.h"
+#include "libavutil/ass_internal.h"
 #include "bytestream.h"
 #include "internal.h"
 
@@ -167,7 +167,7 @@ static int mov_text_encode_close(AVCodecContext *avctx)
 {
     MovTextContext *s = avctx->priv_data;
 
-    ff_ass_split_free(s->ass_ctx);
+    avpriv_ass_split_free(s->ass_ctx);
     av_freep(&s->style_attributes);
     av_freep(&s->fonts);
     av_bprint_finalize(&s->buffer, NULL);
@@ -222,7 +222,7 @@ static int encode_sample_description(AVCodecContext *avctx)
     else
         s->font_scale_factor = 1;
 
-    style = ff_ass_style_get(s->ass_ctx, "Default");
+    style = avpriv_ass_style_get(s->ass_ctx, "Default");
     if (!style && ass->styles_count) {
         style = &ass->styles[0];
     }
@@ -329,7 +329,7 @@ static av_cold int mov_text_encode_init(AVCodecContext *avctx)
 
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
 
-    s->ass_ctx = ff_ass_split(avctx->subtitle_header);
+    s->ass_ctx = avpriv_ass_split(avctx->subtitle_header);
     if (!s->ass_ctx)
         return AVERROR_INVALIDDATA;
     ret = encode_sample_description(avctx);
@@ -566,7 +566,7 @@ static void mov_text_ass_style_set(MovTextContext *s, ASSStyle *style)
 
 static void mov_text_dialog(MovTextContext *s, ASSDialog *dialog)
 {
-    ASSStyle *style = ff_ass_style_get(s->ass_ctx, dialog->style);
+    ASSStyle *style = avpriv_ass_style_get(s->ass_ctx, dialog->style);
 
     s->ass_dialog_style = style;
     mov_text_ass_style_set(s, style);
@@ -580,7 +580,7 @@ static void mov_text_cancel_overrides_cb(void *priv, const char *style_name)
     if (!style_name || !*style_name)
         style = s->ass_dialog_style;
     else
-        style= ff_ass_style_get(s->ass_ctx, style_name);
+        style= avpriv_ass_style_get(s->ass_ctx, style_name);
 
     mov_text_ass_style_set(s, style);
 }
@@ -652,12 +652,12 @@ static int mov_text_encode_frame(AVCodecContext *avctx, unsigned char *buf,
             return AVERROR(EINVAL);
         }
 
-        dialog = ff_ass_split_dialog(s->ass_ctx, ass);
+        dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
         if (!dialog)
             return AVERROR(ENOMEM);
         mov_text_dialog(s, dialog);
-        ff_ass_split_override_codes(&mov_text_callbacks, s, dialog->text);
-        ff_ass_free_dialog(&dialog);
+        avpriv_ass_split_override_codes(&mov_text_callbacks, s, dialog->text);
+        avpriv_ass_free_dialog(&dialog);
     }
 
     if (s->buffer.len > UINT16_MAX)
diff --git a/libavcodec/mpl2dec.c b/libavcodec/mpl2dec.c
index 61e47050ec..fe806b4927 100644
--- a/libavcodec/mpl2dec.c
+++ b/libavcodec/mpl2dec.c
@@ -74,7 +74,7 @@ static int mpl2_decode_frame(AVCodecContext *avctx, void *data,
 
     av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
     if (ptr && avpkt->size > 0 && *ptr && !mpl2_event_to_ass(&buf, ptr))
-        ret = ff_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
     av_bprint_finalize(&buf, NULL);
     if (ret < 0)
         return ret;
diff --git a/libavcodec/realtextdec.c b/libavcodec/realtextdec.c
index 11b586d493..acd548b6b5 100644
--- a/libavcodec/realtextdec.c
+++ b/libavcodec/realtextdec.c
@@ -67,7 +67,7 @@ static int realtext_decode_frame(AVCodecContext *avctx,
 
     av_bprint_init(&buf, 0, 4096);
     if (ptr && avpkt->size > 0 && !rt_event_to_ass(&buf, ptr))
-        ret = ff_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
     av_bprint_finalize(&buf, NULL);
     if (ret < 0)
         return ret;
diff --git a/libavcodec/samidec.c b/libavcodec/samidec.c
index 32d07447b4..1249b629d6 100644
--- a/libavcodec/samidec.c
+++ b/libavcodec/samidec.c
@@ -144,7 +144,7 @@ static int sami_decode_frame(AVCodecContext *avctx,
         if (ret < 0)
             return ret;
         // TODO: pass escaped sami->encoded_source.str as source
-        ret = ff_ass_add_rect(sub, sami->full.str, sami->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, sami->full.str, sami->readorder++, 0, NULL, NULL);
         if (ret < 0)
             return ret;
     }
diff --git a/libavcodec/srtdec.c b/libavcodec/srtdec.c
index 4f16226b83..847352eb37 100644
--- a/libavcodec/srtdec.c
+++ b/libavcodec/srtdec.c
@@ -78,7 +78,7 @@ static int srt_decode_frame(AVCodecContext *avctx,
 
     ret = srt_to_ass(avctx, &buffer, avpkt->data, x1, y1, x2, y2);
     if (ret >= 0)
-        ret = ff_ass_add_rect(sub, buffer.str, s->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, buffer.str, s->readorder++, 0, NULL, NULL);
     av_bprint_finalize(&buffer, NULL);
     if (ret < 0)
         return ret;
diff --git a/libavcodec/srtenc.c b/libavcodec/srtenc.c
index 2e3ac55770..a7c5fccefe 100644
--- a/libavcodec/srtenc.c
+++ b/libavcodec/srtenc.c
@@ -23,8 +23,8 @@
 #include "avcodec.h"
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
-#include "ass_split.h"
-#include "ass.h"
+#include "libavutil/ass_split_internal.h"
+#include "libavutil/ass_internal.h"
 #include "internal.h"
 
 
@@ -94,7 +94,7 @@ static void srt_stack_push_pop(SRTContext *s, const char c, int close)
 
 static void srt_style_apply(SRTContext *s, const char *style)
 {
-    ASSStyle *st = ff_ass_style_get(s->ass_ctx, style);
+    ASSStyle *st = avpriv_ass_style_get(s->ass_ctx, style);
     if (st) {
         int c = st->primary_color & 0xFFFFFF;
         if (st->font_name && strcmp(st->font_name, ASS_DEFAULT_FONT) ||
@@ -135,7 +135,7 @@ static av_cold int srt_encode_init(AVCodecContext *avctx)
 {
     SRTContext *s = avctx->priv_data;
     s->avctx = avctx;
-    s->ass_ctx = ff_ass_split(avctx->subtitle_header);
+    s->ass_ctx = avpriv_ass_split(avctx->subtitle_header);
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
     return s->ass_ctx ? 0 : AVERROR_INVALIDDATA;
 }
@@ -245,14 +245,14 @@ static int encode_frame(AVCodecContext *avctx,
             return AVERROR(EINVAL);
         }
 
-        dialog = ff_ass_split_dialog(s->ass_ctx, ass);
+        dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
         if (!dialog)
             return AVERROR(ENOMEM);
         s->alignment_applied = 0;
         if (avctx->codec_id == AV_CODEC_ID_SUBRIP)
             srt_style_apply(s, dialog->style);
-        ff_ass_split_override_codes(cb, s, dialog->text);
-        ff_ass_free_dialog(&dialog);
+        avpriv_ass_split_override_codes(cb, s, dialog->text);
+        avpriv_ass_free_dialog(&dialog);
     }
 
     if (!av_bprint_is_complete(&s->buffer))
@@ -284,7 +284,7 @@ static int text_encode_frame(AVCodecContext *avctx,
 static int srt_encode_close(AVCodecContext *avctx)
 {
     SRTContext *s = avctx->priv_data;
-    ff_ass_split_free(s->ass_ctx);
+    avpriv_ass_split_free(s->ass_ctx);
     av_bprint_finalize(&s->buffer, NULL);
     return 0;
 }
diff --git a/libavcodec/subviewerdec.c b/libavcodec/subviewerdec.c
index 5c650d0cde..04e9ae7c09 100644
--- a/libavcodec/subviewerdec.c
+++ b/libavcodec/subviewerdec.c
@@ -58,7 +58,7 @@ static int subviewer_decode_frame(AVCodecContext *avctx,
 
     av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
     if (ptr && avpkt->size > 0 && !subviewer_event_to_ass(&buf, ptr))
-        ret = ff_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
     av_bprint_finalize(&buf, NULL);
     if (ret < 0)
         return ret;
diff --git a/libavcodec/textdec.c b/libavcodec/textdec.c
index 308553660a..7556deefe8 100644
--- a/libavcodec/textdec.c
+++ b/libavcodec/textdec.c
@@ -54,8 +54,8 @@ static int text_decode_frame(AVCodecContext *avctx, void *data,
 
     av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
     if (ptr && avpkt->size > 0 && *ptr) {
-        ff_ass_bprint_text_event(&buf, ptr, avpkt->size, text->linebreaks, text->keep_ass_markup);
-        ret = ff_ass_add_rect(sub, buf.str, text->readorder++, 0, NULL, NULL);
+        avpriv_ass_bprint_text_event(&buf, ptr, avpkt->size, text->linebreaks, text->keep_ass_markup);
+        ret = avpriv_ass_add_rect(sub, buf.str, text->readorder++, 0, NULL, NULL);
     }
     av_bprint_finalize(&buf, NULL);
     if (ret < 0)
diff --git a/libavcodec/ttmlenc.c b/libavcodec/ttmlenc.c
index ad2eddfdd5..083f2dd67a 100644
--- a/libavcodec/ttmlenc.c
+++ b/libavcodec/ttmlenc.c
@@ -32,8 +32,7 @@
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
 #include "libavutil/internal.h"
-#include "ass_split.h"
-#include "ass.h"
+#include "libavutil/ass_split_internal.h"
 #include "ttmlenc.h"
 
 typedef struct {
@@ -95,7 +94,7 @@ static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf,
             return AVERROR(EINVAL);
         }
 
-        dialog = ff_ass_split_dialog(s->ass_ctx, ass);
+        dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
         if (!dialog)
             return AVERROR(ENOMEM);
 
@@ -107,7 +106,7 @@ static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf,
             av_bprintf(&s->buffer, "\">");
         }
 
-        ret = ff_ass_split_override_codes(&ttml_callbacks, s, dialog->text);
+        ret = avpriv_ass_split_override_codes(&ttml_callbacks, s, dialog->text);
         if (ret < 0) {
             int log_level = (ret != AVERROR_INVALIDDATA ||
                              avctx->err_recognition & AV_EF_EXPLODE) ?
@@ -118,7 +117,7 @@ static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf,
                    av_err2str(ret));
 
             if (log_level == AV_LOG_ERROR) {
-                ff_ass_free_dialog(&dialog);
+                avpriv_ass_free_dialog(&dialog);
                 return ret;
             }
         }
@@ -126,7 +125,7 @@ static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf,
         if (dialog->style)
             av_bprintf(&s->buffer, "</span>");
 
-        ff_ass_free_dialog(&dialog);
+        avpriv_ass_free_dialog(&dialog);
     }
 
     if (!av_bprint_is_complete(&s->buffer))
@@ -148,7 +147,7 @@ static av_cold int ttml_encode_close(AVCodecContext *avctx)
 {
     TTMLContext *s = avctx->priv_data;
 
-    ff_ass_split_free(s->ass_ctx);
+    avpriv_ass_split_free(s->ass_ctx);
 
     av_bprint_finalize(&s->buffer, NULL);
 
@@ -372,7 +371,7 @@ static av_cold int ttml_encode_init(AVCodecContext *avctx)
 
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
 
-    if (!(s->ass_ctx = ff_ass_split(avctx->subtitle_header))) {
+    if (!(s->ass_ctx = avpriv_ass_split(avctx->subtitle_header))) {
         return AVERROR_INVALIDDATA;
     }
 
diff --git a/libavcodec/webvttdec.c b/libavcodec/webvttdec.c
index 0093f328fa..d304edc705 100644
--- a/libavcodec/webvttdec.c
+++ b/libavcodec/webvttdec.c
@@ -91,7 +91,7 @@ static int webvtt_decode_frame(AVCodecContext *avctx,
 
     av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
     if (ptr && avpkt->size > 0 && !webvtt_event_to_ass(&buf, ptr))
-        ret = ff_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
     av_bprint_finalize(&buf, NULL);
     if (ret < 0)
         return ret;
diff --git a/libavcodec/webvttenc.c b/libavcodec/webvttenc.c
index 89b49e42bf..761099b69a 100644
--- a/libavcodec/webvttenc.c
+++ b/libavcodec/webvttenc.c
@@ -24,8 +24,8 @@
 #include "avcodec.h"
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
-#include "ass_split.h"
-#include "ass.h"
+#include "libavutil/ass_split_internal.h"
+#include "libavutil/ass_internal.h"
 #include "internal.h"
 
 #define WEBVTT_STACK_SIZE 64
@@ -93,7 +93,7 @@ static void webvtt_stack_push_pop(WebVTTContext *s, const char c, int close)
 
 static void webvtt_style_apply(WebVTTContext *s, const char *style)
 {
-    ASSStyle *st = ff_ass_style_get(s->ass_ctx, style);
+    ASSStyle *st = avpriv_ass_style_get(s->ass_ctx, style);
     if (st) {
         if (st->bold != ASS_DEFAULT_BOLD) {
             webvtt_print(s, "<b>");
@@ -172,12 +172,12 @@ static int webvtt_encode_frame(AVCodecContext *avctx,
             return AVERROR(EINVAL);
         }
 
-        dialog = ff_ass_split_dialog(s->ass_ctx, ass);
+        dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
         if (!dialog)
             return AVERROR(ENOMEM);
         webvtt_style_apply(s, dialog->style);
-        ff_ass_split_override_codes(&webvtt_callbacks, s, dialog->text);
-        ff_ass_free_dialog(&dialog);
+        avpriv_ass_split_override_codes(&webvtt_callbacks, s, dialog->text);
+        avpriv_ass_free_dialog(&dialog);
     }
 
     if (!av_bprint_is_complete(&s->buffer))
@@ -197,7 +197,7 @@ static int webvtt_encode_frame(AVCodecContext *avctx,
 static int webvtt_encode_close(AVCodecContext *avctx)
 {
     WebVTTContext *s = avctx->priv_data;
-    ff_ass_split_free(s->ass_ctx);
+    avpriv_ass_split_free(s->ass_ctx);
     av_bprint_finalize(&s->buffer, NULL);
     return 0;
 }
@@ -206,7 +206,7 @@ static av_cold int webvtt_encode_init(AVCodecContext *avctx)
 {
     WebVTTContext *s = avctx->priv_data;
     s->avctx = avctx;
-    s->ass_ctx = ff_ass_split(avctx->subtitle_header);
+    s->ass_ctx = avpriv_ass_split(avctx->subtitle_header);
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
     return s->ass_ctx ? 0 : AVERROR_INVALIDDATA;
 }
diff --git a/libavutil/Makefile b/libavutil/Makefile
index 8bc0a14942..7d4c4793b1 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -101,6 +101,8 @@ BUILT_HEADERS = avconfig.h                                              \
 OBJS = adler32.o                                                        \
        aes.o                                                            \
        aes_ctr.o                                                        \
+       ass.o                                                            \
+       ass_split.o                                                      \
        audio_fifo.o                                                     \
        avstring.o                                                       \
        avsscanf.o                                                       \
diff --git a/libavcodec/ass.c b/libavutil/ass.c
similarity index 65%
rename from libavcodec/ass.c
rename to libavutil/ass.c
index 725e4d42ba..9eeaa38ba9 100644
--- a/libavcodec/ass.c
+++ b/libavutil/ass.c
@@ -19,21 +19,22 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-#include "avcodec.h"
-#include "ass.h"
+#include "ass_internal.h"
+
+#include "subfmt.h"
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
 #include "libavutil/common.h"
 
-int ff_ass_subtitle_header_full(AVCodecContext *avctx,
-                                int play_res_x, int play_res_y,
-                                const char *font, int font_size,
-                                int primary_color, int secondary_color,
-                                int outline_color, int back_color,
-                                int bold, int italic, int underline,
-                                int border_style, int alignment)
+char* avpriv_ass_get_subtitle_header_full(int play_res_x, int play_res_y,
+                                    const char *font, int font_size,
+                                    int primary_color, int secondary_color,
+                                    int outline_color, int back_color,
+                                    int bold, int italic, int underline,
+                                    int border_style, int alignment,
+                                    int print_av_version)
 {
-    avctx->subtitle_header = av_asprintf(
+    char* header = av_asprintf(
              "[Script Info]\r\n"
              "; Script generated by FFmpeg/Lavc%s\r\n"
              "ScriptType: v4.00+\r\n"
@@ -68,34 +69,31 @@ int ff_ass_subtitle_header_full(AVCodecContext *avctx,
              "\r\n"
              "[Events]\r\n"
              "Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\r\n",
-             !(avctx->flags & AV_CODEC_FLAG_BITEXACT) ? AV_STRINGIFY(LIBAVCODEC_VERSION) : "",
+             print_av_version ? AV_STRINGIFY(LIBAVCODEC_VERSION) : "",
              play_res_x, play_res_y, font, font_size,
              primary_color, secondary_color, outline_color, back_color,
              -bold, -italic, -underline, border_style, alignment);
 
-    if (!avctx->subtitle_header)
-        return AVERROR(ENOMEM);
-    avctx->subtitle_header_size = strlen(avctx->subtitle_header);
-    return 0;
+    return header;
 }
 
-int ff_ass_subtitle_header(AVCodecContext *avctx,
-                           const char *font, int font_size,
+char* avpriv_ass_get_subtitle_header(const char *font, int font_size,
                            int color, int back_color,
                            int bold, int italic, int underline,
-                           int border_style, int alignment)
+                           int border_style, int alignment,
+                           int print_av_version)
 {
-    return ff_ass_subtitle_header_full(avctx,
-                               ASS_DEFAULT_PLAYRESX, ASS_DEFAULT_PLAYRESY,
-                               font, font_size, color, color,
-                               back_color, back_color,
-                               bold, italic, underline,
-                               border_style, alignment);
+    return avpriv_ass_get_subtitle_header_full(ASS_DEFAULT_PLAYRESX, ASS_DEFAULT_PLAYRESY,
+                                       font, font_size, color, color,
+                                       back_color, back_color,
+                                       bold, italic, underline,
+                                       border_style, alignment,
+                                       print_av_version);
 }
 
-int ff_ass_subtitle_header_default(AVCodecContext *avctx)
+char* avpriv_ass_get_subtitle_header_default(int print_av_version)
 {
-    return ff_ass_subtitle_header(avctx, ASS_DEFAULT_FONT,
+    return avpriv_ass_get_subtitle_header(ASS_DEFAULT_FONT,
                                ASS_DEFAULT_FONT_SIZE,
                                ASS_DEFAULT_COLOR,
                                ASS_DEFAULT_BACK_COLOR,
@@ -103,10 +101,11 @@ int ff_ass_subtitle_header_default(AVCodecContext *avctx)
                                ASS_DEFAULT_ITALIC,
                                ASS_DEFAULT_UNDERLINE,
                                ASS_DEFAULT_BORDERSTYLE,
-                               ASS_DEFAULT_ALIGNMENT);
+                               ASS_DEFAULT_ALIGNMENT,
+                               print_av_version);
 }
 
-char *ff_ass_get_dialog(int readorder, int layer, const char *style,
+char *avpriv_ass_get_dialog(int readorder, int layer, const char *style,
                         const char *speaker, const char *text)
 {
     return av_asprintf("%d,%d,%s,%s,0,0,0,,%s",
@@ -114,37 +113,17 @@ char *ff_ass_get_dialog(int readorder, int layer, const char *style,
                        speaker ? speaker : "", text);
 }
 
-int ff_ass_add_rect(AVSubtitle *sub, const char *dialog,
-                    int readorder, int layer, const char *style,
-                    const char *speaker)
+char *avpriv_ass_get_dialog_ex(int readorder, int layer, const char *style,
+                        const char *speaker, int margin_l, int margin_r,
+                        int margin_v, const char *text)
 {
-    AVSubtitleRect **rects, *rect;
-    char *ass_str;
-
-    rects = av_realloc_array(sub->rects, sub->num_rects+1, sizeof(*sub->rects));
-    if (!rects)
-        return AVERROR(ENOMEM);
-    sub->rects = rects;
-    rect       = av_mallocz(sizeof(*rect));
-    if (!rect)
-        return AVERROR(ENOMEM);
-    rects[sub->num_rects++] = rect;
-    rect->type = SUBTITLE_ASS;
-    ass_str = ff_ass_get_dialog(readorder, layer, style, speaker, dialog);
-    if (!ass_str)
-        return AVERROR(ENOMEM);
-    rect->ass = ass_str;
-    return 0;
-}
-
-void ff_ass_decoder_flush(AVCodecContext *avctx)
-{
-    FFASSDecoderContext *s = avctx->priv_data;
-    if (!(avctx->flags2 & AV_CODEC_FLAG2_RO_FLUSH_NOOP))
-        s->readorder = 0;
+    return av_asprintf("%d,%d,%s,%s,%d,%d,%d,,%s",
+                       readorder, layer, style ? style : "Default",
+                       speaker ? speaker : "", margin_l, margin_r,
+                       margin_v, text);
 }
 
-void ff_ass_bprint_text_event(AVBPrint *buf, const char *p, int size,
+void avpriv_ass_bprint_text_event(AVBPrint *buf, const char *p, int size,
                              const char *linebreaks, int keep_ass_markup)
 {
     const char *p_end = p + size;
diff --git a/libavutil/ass_internal.h b/libavutil/ass_internal.h
new file mode 100644
index 0000000000..cde5561cd3
--- /dev/null
+++ b/libavutil/ass_internal.h
@@ -0,0 +1,135 @@
+/*
+ * SSA/ASS common functions
+ * Copyright (c) 2010  Aurelien Jacobs <aurel@gnuage.org>
+ *
+ * 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
+ */
+
+#ifndef AVUTIL_ASS_INTERNAL_H
+#define AVUTIL_ASS_INTERNAL_H
+
+#include "subfmt.h"
+#include "libavutil/bprint.h"
+
+#define ASS_DEFAULT_PLAYRESX 384
+#define ASS_DEFAULT_PLAYRESY 288
+
+/**
+ * @name Default values for ASS style
+ * @{
+ */
+#define ASS_DEFAULT_FONT        "Arial"
+#define ASS_DEFAULT_FONT_SIZE   16
+#define ASS_DEFAULT_COLOR       0xffffff
+#define ASS_DEFAULT_BACK_COLOR  0
+#define ASS_DEFAULT_BOLD        0
+#define ASS_DEFAULT_ITALIC      0
+#define ASS_DEFAULT_UNDERLINE   0
+#define ASS_DEFAULT_ALIGNMENT   2
+#define ASS_DEFAULT_BORDERSTYLE 1
+/** @} */
+
+/**
+ * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS.
+ * Can specify all fields explicitly
+ *
+ * @param play_res_x subtitle frame width
+ * @param play_res_y subtitle frame height
+ * @param font name of the default font face to use
+ * @param font_size default font size to use
+ * @param primary_color default text color to use (ABGR)
+ * @param secondary_color default secondary text color to use (ABGR)
+ * @param outline_color default outline color to use (ABGR)
+ * @param back_color default background color to use (ABGR)
+ * @param bold 1 for bold text, 0 for normal text
+ * @param italic 1 for italic text, 0 for normal text
+ * @param underline 1 for underline text, 0 for normal text
+ * @param border_style 1 for outline, 3 for opaque box
+ * @param alignment position of the text (left, center, top...), defined after
+ *                  the layout of the numpad (1-3 sub, 4-6 mid, 7-9 top)
+ * @param print_av_version include library version in header
+ * @return a string containing the subtitle header that needs
+ *         to be released via av_free()
+ */
+char* avpriv_ass_get_subtitle_header_full(int play_res_x, int play_res_y,
+                                  const char *font, int font_size,
+                                  int primary_color, int secondary_color,
+                                  int outline_color, int back_color,
+                                  int bold, int italic, int underline,
+                                  int border_style, int alignment,
+                                  int print_av_version);
+
+/**
+ * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS.
+ *
+ * @param font name of the default font face to use
+ * @param font_size default font size to use
+ * @param color default text color to use (ABGR)
+ * @param back_color default background color to use (ABGR)
+ * @param bold 1 for bold text, 0 for normal text
+ * @param italic 1 for italic text, 0 for normal text
+ * @param underline 1 for underline text, 0 for normal text
+ * @param border_style 1 for outline, 3 for opaque box
+ * @param alignment position of the text (left, center, top...), defined after
+ *                  the layout of the numpad (1-3 sub, 4-6 mid, 7-9 top)
+ * @param print_av_version include library version in header
+ * @return a string containing the subtitle header that needs
+ *         to be released via av_free()
+ */
+char* avpriv_ass_get_subtitle_header(const char *font, int font_size,
+                                int color, int back_color,
+                                int bold, int italic, int underline,
+                                int border_style, int alignment,
+                                int print_av_version);
+
+/**
+ * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS
+ * with default style.
+ *
+ * @param print_av_version include library version in header
+ * @return a string containing the subtitle header that needs
+ *         to be released via av_free()
+ */
+char* avpriv_ass_get_subtitle_header_default(int print_av_version);
+
+/**
+ * Craft an ASS dialog string.
+ */
+char *avpriv_ass_get_dialog(int readorder, int layer, const char *style,
+                        const char *speaker, const char *text);
+
+/**
+ * Craft an ASS dialog string.
+ */
+char *avpriv_ass_get_dialog_ex(int readorder, int layer, const char *style,
+                        const char *speaker, int margin_l, int margin_r,
+                        int margin_v, const char *text);
+
+/**
+ * Escape a text subtitle using ASS syntax into an AVBPrint buffer.
+ * Newline characters will be escaped to \N.
+ *
+ * @param buf pointer to an initialized AVBPrint buffer
+ * @param p source text
+ * @param size size of the source text
+ * @param linebreaks additional newline chars, which will be escaped to \N
+ * @param keep_ass_markup braces and backslash will not be escaped if set
+ */
+void avpriv_ass_bprint_text_event(AVBPrint *buf, const char *p, int size,
+                             const char *linebreaks, int keep_ass_markup);
+
+#endif /* AVUTIL_ASS_INTERNAL_H */
diff --git a/libavcodec/ass_split.c b/libavutil/ass_split.c
similarity index 94%
rename from libavcodec/ass_split.c
rename to libavutil/ass_split.c
index 05c5453e53..c5963351fc 100644
--- a/libavcodec/ass_split.c
+++ b/libavutil/ass_split.c
@@ -22,7 +22,7 @@
 #include "libavutil/common.h"
 #include "libavutil/error.h"
 #include "libavutil/mem.h"
-#include "ass_split.h"
+#include "ass_split_internal.h"
 
 typedef enum {
     ASS_STR,
@@ -373,7 +373,7 @@ static int ass_split(ASSSplitContext *ctx, const char *buf)
     return buf ? 0 : AVERROR_INVALIDDATA;
 }
 
-ASSSplitContext *ff_ass_split(const char *buf)
+ASSSplitContext *avpriv_ass_split(const char *buf)
 {
     ASSSplitContext *ctx = av_mallocz(sizeof(*ctx));
     if (!ctx)
@@ -382,7 +382,7 @@ ASSSplitContext *ff_ass_split(const char *buf)
         buf += 3;
     ctx->current_section = -1;
     if (ass_split(ctx, buf) < 0) {
-        ff_ass_split_free(ctx);
+        avpriv_ass_split_free(ctx);
         return NULL;
     }
     return ctx;
@@ -412,7 +412,7 @@ static void free_section(ASSSplitContext *ctx, const ASSSection *section)
         av_freep((uint8_t *)&ctx->ass + section->offset);
 }
 
-void ff_ass_free_dialog(ASSDialog **dialogp)
+void avpriv_ass_free_dialog(ASSDialog **dialogp)
 {
     ASSDialog *dialog = *dialogp;
     if (!dialog)
@@ -424,7 +424,7 @@ void ff_ass_free_dialog(ASSDialog **dialogp)
     av_freep(dialogp);
 }
 
-ASSDialog *ff_ass_split_dialog(ASSSplitContext *ctx, const char *buf)
+ASSDialog *avpriv_ass_split_dialog(ASSSplitContext *ctx, const char *buf)
 {
     int i;
     static const ASSFields fields[] = {
@@ -451,7 +451,7 @@ ASSDialog *ff_ass_split_dialog(ASSSplitContext *ctx, const char *buf)
         buf = skip_space(buf);
         len = last ? strlen(buf) : strcspn(buf, ",");
         if (len >= INT_MAX) {
-            ff_ass_free_dialog(&dialog);
+            avpriv_ass_free_dialog(&dialog);
             return NULL;
         }
         convert_func[type](ptr, buf, len);
@@ -461,7 +461,7 @@ ASSDialog *ff_ass_split_dialog(ASSSplitContext *ctx, const char *buf)
     return dialog;
 }
 
-void ff_ass_split_free(ASSSplitContext *ctx)
+void avpriv_ass_split_free(ASSSplitContext *ctx)
 {
     if (ctx) {
         int i;
@@ -474,7 +474,7 @@ void ff_ass_split_free(ASSSplitContext *ctx)
 }
 
 
-int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
+int avpriv_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
                                 const char *buf)
 {
     const char *text = NULL;
@@ -497,8 +497,8 @@ int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
             while (*buf == '\\') {
                 char style[2], c[2], sep[2], c_num[2] = "0", tmp[128] = {0};
                 unsigned int color = 0xFFFFFFFF;
-                int len, size = -1, an = -1, alpha = -1;
-                int x1, y1, x2, y2, t1 = -1, t2 = -1;
+                int len, size = -1, an = -1, alpha = -1, scale = 0;
+                int x1, y1, x2, y2, t1 = -1, t2 = -1, accel = 1;
                 if (sscanf(buf, "\\%1[bisu]%1[01\\}]%n", style, c, &len) > 1) {
                     int close = c[0] == '0' ? 1 : c[0] == '1' ? 0 : -1;
                     len += close != -1;
@@ -546,6 +546,14 @@ int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
                 } else if (sscanf(buf, "\\org(%d,%d)%1[\\}]%n", &x1, &y1, sep, &len) > 2) {
                     if (callbacks->origin)
                         callbacks->origin(priv, x1, y1);
+                } else if (sscanf(buf, "\\t(%d,%d,%1[\\}]%n", &t1, &t2, sep, &len) > 2 ||
+                           sscanf(buf, "\\t(%d,%d,%d,%1[\\}]%n", &t1, &t2, &accel, sep, &len) > 3) {
+                    if (callbacks->animate)
+                        callbacks->animate(priv, t1, t2, accel, tmp);
+                } else if (sscanf(buf, "\\p%1[\\}]%n", sep, &len) > 0 ||
+                           sscanf(buf, "\\p%u%1[\\}]%n", &scale, sep, &len) > 1) {
+                    if (callbacks->drawing_mode)
+                        callbacks->drawing_mode(priv, scale);
                 } else {
                     len = strcspn(buf+1, "\\}") + 2;  /* skip unknown code */
                 }
@@ -569,7 +577,7 @@ int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
     return 0;
 }
 
-ASSStyle *ff_ass_style_get(ASSSplitContext *ctx, const char *style)
+ASSStyle *avpriv_ass_style_get(ASSSplitContext *ctx, const char *style)
 {
     ASS *ass = &ctx->ass;
     int i;
diff --git a/libavcodec/ass_split.h b/libavutil/ass_split_internal.h
similarity index 86%
rename from libavcodec/ass_split.h
rename to libavutil/ass_split_internal.h
index a45fb9b8a1..eee49ef0f5 100644
--- a/libavcodec/ass_split.h
+++ b/libavutil/ass_split_internal.h
@@ -19,8 +19,8 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-#ifndef AVCODEC_ASS_SPLIT_H
-#define AVCODEC_ASS_SPLIT_H
+#ifndef AVUTIL_ASS_SPLIT_INTERNAL_H
+#define AVUTIL_ASS_SPLIT_INTERNAL_H
 
 /**
  * fields extracted from the [Script Info] section
@@ -81,7 +81,7 @@ typedef struct {
     char *effect;
     char *text;     /**< actual text which will be displayed as a subtitle,
                          can include style override control codes (see
-                         ff_ass_split_override_codes()) */
+                         avpriv_ass_split_override_codes()) */
 } ASSDialog;
 
 /**
@@ -107,12 +107,12 @@ typedef struct ASSSplitContext ASSSplitContext;
  * @param buf String containing the ASS formatted data.
  * @return Newly allocated struct containing split data.
  */
-ASSSplitContext *ff_ass_split(const char *buf);
+ASSSplitContext *avpriv_ass_split(const char *buf);
 
 /**
- * Free a dialogue obtained from ff_ass_split_dialog().
+ * Free a dialogue obtained from avpriv_ass_split_dialog().
  */
-void ff_ass_free_dialog(ASSDialog **dialogp);
+void avpriv_ass_free_dialog(ASSDialog **dialogp);
 
 /**
  * Split one ASS Dialogue line from a string buffer.
@@ -121,14 +121,14 @@ void ff_ass_free_dialog(ASSDialog **dialogp);
  * @param buf String containing the ASS "Dialogue" line.
  * @return Pointer to the split ASSDialog. Must be freed with ff_ass_free_dialog()
  */
-ASSDialog *ff_ass_split_dialog(ASSSplitContext *ctx, const char *buf);
+ASSDialog *avpriv_ass_split_dialog(ASSSplitContext *ctx, const char *buf);
 
 /**
  * Free all the memory allocated for an ASSSplitContext.
  *
  * @param ctx Context previously initialized by ff_ass_split().
  */
-void ff_ass_split_free(ASSSplitContext *ctx);
+void avpriv_ass_split_free(ASSSplitContext *ctx);
 
 
 /**
@@ -141,6 +141,7 @@ typedef struct {
      * @{
      */
     void (*text)(void *priv, const char *text, int len);
+    void (*hard_space)(void *priv);
     void (*new_line)(void *priv, int forced);
     void (*style)(void *priv, char style, int close);
     void (*color)(void *priv, unsigned int /* color */, unsigned int color_id);
@@ -156,7 +157,16 @@ typedef struct {
      * @{
      */
     void (*move)(void *priv, int x1, int y1, int x2, int y2, int t1, int t2);
+    void (*animate)(void *priv, int t1, int t2, int accel, char *style);
     void (*origin)(void *priv, int x, int y);
+    void (*drawing_mode)(void *priv, int scale);
+    /** @} */
+
+    /**
+     * @defgroup ass_ext    ASS extensible parsing callback
+     * @{
+     */
+    void (*ext)(void *priv, int ext_id, const char *text, int p1, int p2);
     /** @} */
 
     /**
@@ -176,7 +186,7 @@ typedef struct {
  * @param buf The ASS "Dialogue" Text field to split.
  * @return >= 0 on success otherwise an error code <0
  */
-int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
+int avpriv_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
                                 const char *buf);
 
 /**
@@ -186,6 +196,6 @@ int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
  * @param style name of the style to search for.
  * @return the ASSStyle corresponding to style, or NULL if style can't be found
  */
-ASSStyle *ff_ass_style_get(ASSSplitContext *ctx, const char *style);
+ASSStyle *avpriv_ass_style_get(ASSSplitContext *ctx, const char *style);
 
-#endif /* AVCODEC_ASS_SPLIT_H */
+#endif /* AVUTIL_ASS_SPLIT_INTERNAL_H */
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v2 06/26] avcodec/subtitles: Replace deprecated enum values
  2022-01-20  2:48 ` [FFmpeg-devel] [PATCH v2 00/26] Subtitle Filtering 2022 ffmpegagent
                     ` (4 preceding siblings ...)
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 05/26] avcodec, avutil: Move ass helper functions to avutil as avpriv_ and extend ass dialog parsing ffmpegagent
@ 2022-01-20  2:48   ` ffmpegagent
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 07/26] fftools/play, probe: Adjust for subtitle changes ffmpegagent
                     ` (20 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  2:48 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/ass.h       | 2 +-
 libavcodec/assdec.c    | 2 +-
 libavcodec/dvbsubdec.c | 2 +-
 libavcodec/dvdsubdec.c | 2 +-
 libavcodec/dvdsubenc.c | 2 +-
 libavcodec/pgssubdec.c | 2 +-
 libavcodec/xsubdec.c   | 2 +-
 7 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/libavcodec/ass.h b/libavcodec/ass.h
index 8bc13d7ab8..43c5ad651a 100644
--- a/libavcodec/ass.h
+++ b/libavcodec/ass.h
@@ -83,7 +83,7 @@ static inline int avpriv_ass_add_rect(AVSubtitle *sub, const char *dialog,
     rects[sub->num_rects]       = av_mallocz(sizeof(*rects[0]));
     if (!rects[sub->num_rects])
         return AVERROR(ENOMEM);
-    rects[sub->num_rects]->type = SUBTITLE_ASS;
+    rects[sub->num_rects]->type = AV_SUBTITLE_FMT_ASS;
     ass_str = avpriv_ass_get_dialog(readorder, layer, style, speaker, dialog);
     if (!ass_str)
         return AVERROR(ENOMEM);
diff --git a/libavcodec/assdec.c b/libavcodec/assdec.c
index 7802a44e71..fd321e7004 100644
--- a/libavcodec/assdec.c
+++ b/libavcodec/assdec.c
@@ -54,7 +54,7 @@ static int ass_decode_frame(AVCodecContext *avctx, void *data, int *got_sub_ptr,
     if (!sub->rects[0])
         return AVERROR(ENOMEM);
     sub->num_rects = 1;
-    sub->rects[0]->type = SUBTITLE_ASS;
+    sub->rects[0]->type = AV_SUBTITLE_FMT_ASS;
     sub->rects[0]->ass  = av_strdup(avpkt->data);
     if (!sub->rects[0]->ass)
         return AVERROR(ENOMEM);
diff --git a/libavcodec/dvbsubdec.c b/libavcodec/dvbsubdec.c
index bc741a1de6..0d64c6e71c 100644
--- a/libavcodec/dvbsubdec.c
+++ b/libavcodec/dvbsubdec.c
@@ -795,7 +795,7 @@ static int save_subtitle_set(AVCodecContext *avctx, AVSubtitle *sub, int *got_ou
             rect->w = region->width;
             rect->h = region->height;
             rect->nb_colors = (1 << region->depth);
-            rect->type      = SUBTITLE_BITMAP;
+            rect->type      = AV_SUBTITLE_FMT_BITMAP;
             rect->linesize[0] = region->width;
 
             clut = get_clut(ctx, region->clut);
diff --git a/libavcodec/dvdsubdec.c b/libavcodec/dvdsubdec.c
index 52259f0730..b39b3d1838 100644
--- a/libavcodec/dvdsubdec.c
+++ b/libavcodec/dvdsubdec.c
@@ -406,7 +406,7 @@ static int decode_dvd_subtitles(DVDSubContext *ctx, AVSubtitle *sub_header,
                 sub_header->rects[0]->y = y1;
                 sub_header->rects[0]->w = w;
                 sub_header->rects[0]->h = h;
-                sub_header->rects[0]->type = SUBTITLE_BITMAP;
+                sub_header->rects[0]->type = AV_SUBTITLE_FMT_BITMAP;
                 sub_header->rects[0]->linesize[0] = w;
                 sub_header->rects[0]->flags = is_menu ? AV_SUBTITLE_FLAG_FORCED : 0;
             }
diff --git a/libavcodec/dvdsubenc.c b/libavcodec/dvdsubenc.c
index ff4fbed39d..943a7466d9 100644
--- a/libavcodec/dvdsubenc.c
+++ b/libavcodec/dvdsubenc.c
@@ -268,7 +268,7 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
     if (rects == 0 || !h->rects)
         return AVERROR(EINVAL);
     for (i = 0; i < rects; i++)
-        if (h->rects[i]->type != SUBTITLE_BITMAP) {
+        if (h->rects[i]->type != AV_SUBTITLE_FMT_BITMAP) {
             av_log(avctx, AV_LOG_ERROR, "Bitmap subtitle required\n");
             return AVERROR(EINVAL);
         }
diff --git a/libavcodec/pgssubdec.c b/libavcodec/pgssubdec.c
index bdd20c914b..22b6616f9b 100644
--- a/libavcodec/pgssubdec.c
+++ b/libavcodec/pgssubdec.c
@@ -535,7 +535,7 @@ static int display_end_segment(AVCodecContext *avctx, void *data,
         if (!rect)
             return AVERROR(ENOMEM);
         sub->rects[sub->num_rects++] = rect;
-        rect->type = SUBTITLE_BITMAP;
+        rect->type = AV_SUBTITLE_FMT_BITMAP;
 
         /* Process bitmap */
         object = find_object(ctx->presentation.objects[i].id, &ctx->objects);
diff --git a/libavcodec/xsubdec.c b/libavcodec/xsubdec.c
index 85cd7d1c20..a4be18a1d8 100644
--- a/libavcodec/xsubdec.c
+++ b/libavcodec/xsubdec.c
@@ -107,7 +107,7 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_sub_ptr,
     sub->num_rects = 1;
     rect->x = x; rect->y = y;
     rect->w = w; rect->h = h;
-    rect->type = SUBTITLE_BITMAP;
+    rect->type = AV_SUBTITLE_FMT_BITMAP;
     rect->linesize[0] = w;
     rect->data[0] = av_malloc(w * h);
     rect->nb_colors = 4;
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v2 07/26] fftools/play, probe: Adjust for subtitle changes
  2022-01-20  2:48 ` [FFmpeg-devel] [PATCH v2 00/26] Subtitle Filtering 2022 ffmpegagent
                     ` (5 preceding siblings ...)
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 06/26] avcodec/subtitles: Replace deprecated enum values ffmpegagent
@ 2022-01-20  2:48   ` ffmpegagent
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 08/26] avfilter/subtitles: Add subtitles.c for subtitle frame allocation ffmpegagent
                     ` (19 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  2:48 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 fftools/ffplay.c  | 102 +++++++++++++++++++++-------------------------
 fftools/ffprobe.c |  47 +++++++++++++--------
 2 files changed, 77 insertions(+), 72 deletions(-)

diff --git a/fftools/ffplay.c b/fftools/ffplay.c
index e7b20be76b..94286eb678 100644
--- a/fftools/ffplay.c
+++ b/fftools/ffplay.c
@@ -152,7 +152,6 @@ typedef struct Clock {
 /* Common struct for handling all types of decoded data and allocated render buffers. */
 typedef struct Frame {
     AVFrame *frame;
-    AVSubtitle sub;
     int serial;
     double pts;           /* presentation timestamp for the frame */
     double duration;      /* estimated duration of the frame */
@@ -586,7 +585,7 @@ static int decoder_init(Decoder *d, AVCodecContext *avctx, PacketQueue *queue, S
     return 0;
 }
 
-static int decoder_decode_frame(Decoder *d, AVFrame *frame, AVSubtitle *sub) {
+static int decoder_decode_frame(Decoder *d, AVFrame *frame) {
     int ret = AVERROR(EAGAIN);
 
     for (;;) {
@@ -620,6 +619,9 @@ static int decoder_decode_frame(Decoder *d, AVFrame *frame, AVSubtitle *sub) {
                             }
                         }
                         break;
+                    case AVMEDIA_TYPE_SUBTITLE:
+                        ret = avcodec_receive_frame(d->avctx, frame);
+                        break;
                 }
                 if (ret == AVERROR_EOF) {
                     d->finished = d->pkt_serial;
@@ -652,25 +654,11 @@ static int decoder_decode_frame(Decoder *d, AVFrame *frame, AVSubtitle *sub) {
             av_packet_unref(d->pkt);
         } while (1);
 
-        if (d->avctx->codec_type == AVMEDIA_TYPE_SUBTITLE) {
-            int got_frame = 0;
-            ret = avcodec_decode_subtitle2(d->avctx, sub, &got_frame, d->pkt);
-            if (ret < 0) {
-                ret = AVERROR(EAGAIN);
-            } else {
-                if (got_frame && !d->pkt->data) {
-                    d->packet_pending = 1;
-                }
-                ret = got_frame ? 0 : (d->pkt->data ? AVERROR(EAGAIN) : AVERROR_EOF);
-            }
-            av_packet_unref(d->pkt);
+        if (avcodec_send_packet(d->avctx, d->pkt) == AVERROR(EAGAIN)) {
+            av_log(d->avctx, AV_LOG_ERROR, "Receive_frame and send_packet both returned EAGAIN, which is an API violation.\n");
+            d->packet_pending = 1;
         } else {
-            if (avcodec_send_packet(d->avctx, d->pkt) == AVERROR(EAGAIN)) {
-                av_log(d->avctx, AV_LOG_ERROR, "Receive_frame and send_packet both returned EAGAIN, which is an API violation.\n");
-                d->packet_pending = 1;
-            } else {
-                av_packet_unref(d->pkt);
-            }
+            av_packet_unref(d->pkt);
         }
     }
 }
@@ -683,7 +671,6 @@ static void decoder_destroy(Decoder *d) {
 static void frame_queue_unref_item(Frame *vp)
 {
     av_frame_unref(vp->frame);
-    avsubtitle_free(&vp->sub);
 }
 
 static int frame_queue_init(FrameQueue *f, PacketQueue *pktq, int max_size, int keep_last)
@@ -981,7 +968,7 @@ static void video_image_display(VideoState *is)
         if (frame_queue_nb_remaining(&is->subpq) > 0) {
             sp = frame_queue_peek(&is->subpq);
 
-            if (vp->pts >= sp->pts + ((float) sp->sub.start_display_time / 1000)) {
+            if (vp->pts >= sp->pts) {
                 if (!sp->uploaded) {
                     uint8_t* pixels[4];
                     int pitch[4];
@@ -993,25 +980,27 @@ static void video_image_display(VideoState *is)
                     if (realloc_texture(&is->sub_texture, SDL_PIXELFORMAT_ARGB8888, sp->width, sp->height, SDL_BLENDMODE_BLEND, 1) < 0)
                         return;
 
-                    for (i = 0; i < sp->sub.num_rects; i++) {
-                        AVSubtitleRect *sub_rect = sp->sub.rects[i];
+                    for (i = 0; i < sp->frame->num_subtitle_areas; i++) {
+                        AVSubtitleArea *area = sp->frame->subtitle_areas[i];
+                        SDL_Rect sdl_rect = { .x = area->x, .y = area->y, .w = area->w, .h = area->h };
 
-                        sub_rect->x = av_clip(sub_rect->x, 0, sp->width );
-                        sub_rect->y = av_clip(sub_rect->y, 0, sp->height);
-                        sub_rect->w = av_clip(sub_rect->w, 0, sp->width  - sub_rect->x);
-                        sub_rect->h = av_clip(sub_rect->h, 0, sp->height - sub_rect->y);
+                        area->x = av_clip(area->x, 0, sp->width );
+                        area->y = av_clip(area->y, 0, sp->height);
+                        area->w = av_clip(area->w, 0, sp->width  - area->x);
+                        area->h = av_clip(area->h, 0, sp->height - area->y);
 
                         is->sub_convert_ctx = sws_getCachedContext(is->sub_convert_ctx,
-                            sub_rect->w, sub_rect->h, AV_PIX_FMT_PAL8,
-                            sub_rect->w, sub_rect->h, AV_PIX_FMT_BGRA,
+                            area->w, area->h, AV_PIX_FMT_PAL8,
+                            area->w, area->h, AV_PIX_FMT_BGRA,
                             0, NULL, NULL, NULL);
                         if (!is->sub_convert_ctx) {
                             av_log(NULL, AV_LOG_FATAL, "Cannot initialize the conversion context\n");
                             return;
                         }
-                        if (!SDL_LockTexture(is->sub_texture, (SDL_Rect *)sub_rect, (void **)pixels, pitch)) {
-                            sws_scale(is->sub_convert_ctx, (const uint8_t * const *)sub_rect->data, sub_rect->linesize,
-                                      0, sub_rect->h, pixels, pitch);
+                        if (!SDL_LockTexture(is->sub_texture, &sdl_rect, (void **)pixels, pitch)) {
+                            const uint8_t* data[2] = { area->buf[0]->data, (uint8_t *)&area->pal };
+                            sws_scale(is->sub_convert_ctx, data, area->linesize,
+                                      0, area->h, pixels, pitch);
                             SDL_UnlockTexture(is->sub_texture);
                         }
                     }
@@ -1038,16 +1027,18 @@ static void video_image_display(VideoState *is)
 #if USE_ONEPASS_SUBTITLE_RENDER
         SDL_RenderCopy(renderer, is->sub_texture, NULL, &rect);
 #else
-        int i;
+        unsigned i;
         double xratio = (double)rect.w / (double)sp->width;
         double yratio = (double)rect.h / (double)sp->height;
-        for (i = 0; i < sp->sub.num_rects; i++) {
-            SDL_Rect *sub_rect = (SDL_Rect*)sp->sub.rects[i];
-            SDL_Rect target = {.x = rect.x + sub_rect->x * xratio,
-                               .y = rect.y + sub_rect->y * yratio,
-                               .w = sub_rect->w * xratio,
-                               .h = sub_rect->h * yratio};
-            SDL_RenderCopy(renderer, is->sub_texture, sub_rect, &target);
+        for (i = 0; i < sp->frame->num_subtitle_areas; i++) {
+            AVSubtitleArea *area = sp->frame->subtitle_areas[i];
+            SDL_Rect sub_rect = { .x = area->x, .y = area->y,
+                                  .w = area->w, .h = area->h};
+            SDL_Rect target = {.x = rect.x + sub_rect.x * xratio,
+                               .y = rect.y + sub_rect.y * yratio,
+                               .w = sub_rect.w * xratio,
+                               .h = sub_rect.h * yratio};
+            SDL_RenderCopy(renderer, is->sub_texture, &sub_rect, &target);
         }
 #endif
     }
@@ -1651,19 +1642,20 @@ retry:
                         sp2 = NULL;
 
                     if (sp->serial != is->subtitleq.serial
-                            || (is->vidclk.pts > (sp->pts + ((float) sp->sub.end_display_time / 1000)))
-                            || (sp2 && is->vidclk.pts > (sp2->pts + ((float) sp2->sub.start_display_time / 1000))))
+                            || (is->vidclk.pts > (sp->pts + ((float) sp->frame->subtitle_timing.duration / AV_TIME_BASE)))
+                            || (sp2 && is->vidclk.pts > (sp2->pts)))
                     {
                         if (sp->uploaded) {
                             int i;
-                            for (i = 0; i < sp->sub.num_rects; i++) {
-                                AVSubtitleRect *sub_rect = sp->sub.rects[i];
+                            for (i = 0; i < sp->frame->num_subtitle_areas; i++) {
+                                AVSubtitleArea *area = sp->frame->subtitle_areas[i];
+                                SDL_Rect sdl_rect = { .x = area->x, .y = area->y, .w = area->w, .h = area->h };
                                 uint8_t *pixels;
                                 int pitch, j;
 
-                                if (!SDL_LockTexture(is->sub_texture, (SDL_Rect *)sub_rect, (void **)&pixels, &pitch)) {
-                                    for (j = 0; j < sub_rect->h; j++, pixels += pitch)
-                                        memset(pixels, 0, sub_rect->w << 2);
+                                if (!SDL_LockTexture(is->sub_texture, &sdl_rect, (void **)&pixels, &pitch)) {
+                                    for (j = 0; j < area->h; j++, pixels += pitch)
+                                        memset(pixels, 0, area->w << 2);
                                     SDL_UnlockTexture(is->sub_texture);
                                 }
                             }
@@ -1774,7 +1766,7 @@ static int get_video_frame(VideoState *is, AVFrame *frame)
 {
     int got_picture;
 
-    if ((got_picture = decoder_decode_frame(&is->viddec, frame, NULL)) < 0)
+    if ((got_picture = decoder_decode_frame(&is->viddec, frame)) < 0)
         return -1;
 
     if (got_picture) {
@@ -2048,7 +2040,7 @@ static int audio_thread(void *arg)
         return AVERROR(ENOMEM);
 
     do {
-        if ((got_frame = decoder_decode_frame(&is->auddec, frame, NULL)) < 0)
+        if ((got_frame = decoder_decode_frame(&is->auddec, frame)) < 0)
             goto the_end;
 
         if (got_frame) {
@@ -2246,14 +2238,14 @@ static int subtitle_thread(void *arg)
         if (!(sp = frame_queue_peek_writable(&is->subpq)))
             return 0;
 
-        if ((got_subtitle = decoder_decode_frame(&is->subdec, NULL, &sp->sub)) < 0)
+        if ((got_subtitle = decoder_decode_frame(&is->subdec, sp->frame)) < 0)
             break;
 
         pts = 0;
 
-        if (got_subtitle && sp->sub.format == 0) {
-            if (sp->sub.pts != AV_NOPTS_VALUE)
-                pts = sp->sub.pts / (double)AV_TIME_BASE;
+        if (got_subtitle && sp->frame->format == AV_SUBTITLE_FMT_BITMAP) {
+            if (sp->frame->subtitle_timing.start_pts != AV_NOPTS_VALUE)
+                pts = (double)sp->frame->subtitle_timing.start_pts / (double)AV_TIME_BASE;
             sp->pts = pts;
             sp->serial = is->subdec.pkt_serial;
             sp->width = is->subdec.avctx->width;
@@ -2263,7 +2255,7 @@ static int subtitle_thread(void *arg)
             /* now we can update the picture count */
             frame_queue_push(&is->subpq);
         } else if (got_subtitle) {
-            avsubtitle_free(&sp->sub);
+            av_frame_free(&sp->frame);
         }
     }
     return 0;
diff --git a/fftools/ffprobe.c b/fftools/ffprobe.c
index 20582ca7ac..a969faec1d 100644
--- a/fftools/ffprobe.c
+++ b/fftools/ffprobe.c
@@ -2375,22 +2375,42 @@ static void show_packet(WriterContext *w, InputFile *ifile, AVPacket *pkt, int p
     fflush(stdout);
 }
 
-static void show_subtitle(WriterContext *w, AVSubtitle *sub, AVStream *stream,
+static void show_subtitle(WriterContext *w, AVFrame *sub, AVStream *stream,
                           AVFormatContext *fmt_ctx)
 {
     AVBPrint pbuf;
+    const char *s;
 
     av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
 
     writer_print_section_header(w, SECTION_ID_SUBTITLE);
 
     print_str ("media_type",         "subtitle");
-    print_ts  ("pts",                 sub->pts);
-    print_time("pts_time",            sub->pts, &AV_TIME_BASE_Q);
-    print_int ("format",              sub->format);
-    print_int ("start_display_time",  sub->start_display_time);
-    print_int ("end_display_time",    sub->end_display_time);
-    print_int ("num_rects",           sub->num_rects);
+    print_ts  ("pts",                 sub->subtitle_timing.start_pts);
+    print_time("pts_time",            sub->subtitle_timing.start_pts, &AV_TIME_BASE_Q);
+    print_time("duration",            sub->subtitle_timing.duration, &AV_TIME_BASE_Q);
+
+    // Remain compatible with previous outputs
+    switch (sub->format) {
+    case AV_SUBTITLE_FMT_BITMAP:
+        print_int ("format",         0);
+        break;
+    case AV_SUBTITLE_FMT_TEXT:
+        print_int ("format",         1);
+        break;
+    case AV_SUBTITLE_FMT_ASS:
+        print_int ("format",         1);
+        break;
+    default:
+        print_int ("format",         -1);
+        break;
+    }
+
+    s = av_get_subtitle_fmt_name(sub->format);
+    if (s) print_str    ("format_str", s);
+    else   print_str_opt("format_str", "unknown");
+
+    print_int ("num_subtitle_rects",           sub->num_subtitle_areas);
 
     writer_print_section_footer(w);
 
@@ -2557,7 +2577,6 @@ static av_always_inline int process_frame(WriterContext *w,
     AVFormatContext *fmt_ctx = ifile->fmt_ctx;
     AVCodecContext *dec_ctx = ifile->streams[pkt->stream_index].dec_ctx;
     AVCodecParameters *par = ifile->streams[pkt->stream_index].st->codecpar;
-    AVSubtitle sub;
     int ret = 0, got_frame = 0;
 
     clear_log(1);
@@ -2565,6 +2584,7 @@ static av_always_inline int process_frame(WriterContext *w,
         switch (par->codec_type) {
         case AVMEDIA_TYPE_VIDEO:
         case AVMEDIA_TYPE_AUDIO:
+        case AVMEDIA_TYPE_SUBTITLE:
             if (*packet_new) {
                 ret = avcodec_send_packet(dec_ctx, pkt);
                 if (ret == AVERROR(EAGAIN)) {
@@ -2583,12 +2603,6 @@ static av_always_inline int process_frame(WriterContext *w,
                 }
             }
             break;
-
-        case AVMEDIA_TYPE_SUBTITLE:
-            if (*packet_new)
-                ret = avcodec_decode_subtitle2(dec_ctx, &sub, &got_frame, pkt);
-            *packet_new = 0;
-            break;
         default:
             *packet_new = 0;
         }
@@ -2603,12 +2617,11 @@ static av_always_inline int process_frame(WriterContext *w,
         nb_streams_frames[pkt->stream_index]++;
         if (do_show_frames)
             if (is_sub)
-                show_subtitle(w, &sub, ifile->streams[pkt->stream_index].st, fmt_ctx);
+                show_subtitle(w, frame, ifile->streams[pkt->stream_index].st, fmt_ctx);
             else
                 show_frame(w, frame, ifile->streams[pkt->stream_index].st, fmt_ctx);
-        if (is_sub)
-            avsubtitle_free(&sub);
     }
+
     return got_frame || *packet_new;
 }
 
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v2 08/26] avfilter/subtitles: Add subtitles.c for subtitle frame allocation
  2022-01-20  2:48 ` [FFmpeg-devel] [PATCH v2 00/26] Subtitle Filtering 2022 ffmpegagent
                     ` (6 preceding siblings ...)
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 07/26] fftools/play, probe: Adjust for subtitle changes ffmpegagent
@ 2022-01-20  2:48   ` ffmpegagent
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 09/26] avfilter/avfilter: Handle subtitle frames ffmpegagent
                     ` (18 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  2:48 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Analog to avfilter/video.c and avfilter/audio.c

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavfilter/Makefile    |  1 +
 libavfilter/avfilter.c  |  4 +++
 libavfilter/internal.h  |  1 +
 libavfilter/subtitles.c | 63 +++++++++++++++++++++++++++++++++++++++++
 libavfilter/subtitles.h | 44 ++++++++++++++++++++++++++++
 5 files changed, 113 insertions(+)
 create mode 100644 libavfilter/subtitles.c
 create mode 100644 libavfilter/subtitles.h

diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 1adbea75bd..283dd436cd 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -19,6 +19,7 @@ OBJS = allfilters.o                                                     \
        framequeue.o                                                     \
        graphdump.o                                                      \
        graphparser.o                                                    \
+       subtitles.o                                                      \
        video.o                                                          \
 
 OBJS-$(HAVE_THREADS)                         += pthread.o
diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c
index 7362bcdab5..df5b8f483c 100644
--- a/libavfilter/avfilter.c
+++ b/libavfilter/avfilter.c
@@ -43,6 +43,7 @@
 #include "formats.h"
 #include "framepool.h"
 #include "internal.h"
+#include "subtitles.h"
 
 #include "libavutil/ffversion.h"
 const char av_filter_ffversion[] = "FFmpeg version " FFMPEG_VERSION;
@@ -1475,6 +1476,9 @@ int ff_inlink_make_frame_writable(AVFilterLink *link, AVFrame **rframe)
     case AVMEDIA_TYPE_AUDIO:
         out = ff_get_audio_buffer(link, frame->nb_samples);
         break;
+    case AVMEDIA_TYPE_SUBTITLE:
+        out = ff_get_subtitles_buffer(link, link->format);
+        break;
     default:
         return AVERROR(EINVAL);
     }
diff --git a/libavfilter/internal.h b/libavfilter/internal.h
index 1099b82b4b..fc09ef574c 100644
--- a/libavfilter/internal.h
+++ b/libavfilter/internal.h
@@ -90,6 +90,7 @@ struct AVFilterPad {
     union {
         AVFrame *(*video)(AVFilterLink *link, int w, int h);
         AVFrame *(*audio)(AVFilterLink *link, int nb_samples);
+        AVFrame *(*subtitle)(AVFilterLink *link, int format);
     } get_buffer;
 
     /**
diff --git a/libavfilter/subtitles.c b/libavfilter/subtitles.c
new file mode 100644
index 0000000000..951bfd612c
--- /dev/null
+++ b/libavfilter/subtitles.c
@@ -0,0 +1,63 @@
+/*
+ * 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
+ */
+
+#include "libavutil/common.h"
+
+#include "subtitles.h"
+#include "avfilter.h"
+#include "internal.h"
+
+
+AVFrame *ff_null_get_subtitles_buffer(AVFilterLink *link, int format)
+{
+    return ff_get_subtitles_buffer(link->dst->outputs[0], format);
+}
+
+AVFrame *ff_default_get_subtitles_buffer(AVFilterLink *link, int format)
+{
+    AVFrame *frame;
+
+    frame = av_frame_alloc();
+    if (!frame)
+        return NULL;
+
+    frame->format = format;
+    frame->type = AVMEDIA_TYPE_SUBTITLE;
+
+    if (av_frame_get_buffer2(frame, 0) < 0) {
+        av_frame_free(&frame);
+        return NULL;
+    }
+
+    return frame;
+}
+
+AVFrame *ff_get_subtitles_buffer(AVFilterLink *link, int format)
+{
+    AVFrame *ret = NULL;
+
+    if (link->dstpad->get_buffer.subtitle)
+        ret = link->dstpad->get_buffer.subtitle(link, format);
+
+    if (!ret)
+        ret = ff_default_get_subtitles_buffer(link, format);
+
+    return ret;
+}
diff --git a/libavfilter/subtitles.h b/libavfilter/subtitles.h
new file mode 100644
index 0000000000..4a9115126e
--- /dev/null
+++ b/libavfilter/subtitles.h
@@ -0,0 +1,44 @@
+/*
+ * 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
+ */
+
+#ifndef AVFILTER_SUBTITLES_H
+#define AVFILTER_SUBTITLES_H
+
+#include "avfilter.h"
+#include "internal.h"
+
+/** default handler for get_subtitles_buffer() for subtitle inputs */
+AVFrame *ff_default_get_subtitles_buffer(AVFilterLink *link, int format);
+
+/** get_subtitles_buffer() handler for filters which simply pass subtitles along */
+AVFrame *ff_null_get_subtitles_buffer(AVFilterLink *link, int format);
+
+/**
+ * Request a subtitles frame with a specific set of permissions.
+ *
+ * @param link           the output link to the filter from which the buffer will
+ *                       be requested
+ * @param format         The subtitles format.
+ * @return               A reference to the frame. This must be unreferenced with
+ *                       av_frame_free when you are finished with it.
+*/
+AVFrame *ff_get_subtitles_buffer(AVFilterLink *link, int format);
+
+#endif /* AVFILTER_SUBTITLES_H */
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v2 09/26] avfilter/avfilter: Handle subtitle frames
  2022-01-20  2:48 ` [FFmpeg-devel] [PATCH v2 00/26] Subtitle Filtering 2022 ffmpegagent
                     ` (7 preceding siblings ...)
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 08/26] avfilter/subtitles: Add subtitles.c for subtitle frame allocation ffmpegagent
@ 2022-01-20  2:48   ` ffmpegagent
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 10/26] avfilter/avfilter: Fix hardcoded input index ffmpegagent
                     ` (17 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  2:48 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavfilter/avfilter.c      |  8 +++++---
 libavfilter/avfilter.h      | 11 +++++++++++
 libavfilter/avfiltergraph.c |  5 +++++
 libavfilter/formats.c       | 22 ++++++++++++++++++++++
 libavfilter/formats.h       |  3 +++
 libavfilter/internal.h      | 18 +++++++++++++++---
 6 files changed, 61 insertions(+), 6 deletions(-)

diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c
index df5b8f483c..75d5e86539 100644
--- a/libavfilter/avfilter.c
+++ b/libavfilter/avfilter.c
@@ -56,7 +56,8 @@ static void tlog_ref(void *ctx, AVFrame *ref, int end)
             ref->linesize[0], ref->linesize[1], ref->linesize[2], ref->linesize[3],
             ref->pts, ref->pkt_pos);
 
-    if (ref->width) {
+    switch(ref->type) {
+    case AVMEDIA_TYPE_VIDEO:
         ff_tlog(ctx, " a:%d/%d s:%dx%d i:%c iskey:%d type:%c",
                 ref->sample_aspect_ratio.num, ref->sample_aspect_ratio.den,
                 ref->width, ref->height,
@@ -64,12 +65,13 @@ static void tlog_ref(void *ctx, AVFrame *ref, int end)
                 ref->top_field_first ? 'T' : 'B',    /* Top / Bottom */
                 ref->key_frame,
                 av_get_picture_type_char(ref->pict_type));
-    }
-    if (ref->nb_samples) {
+        break;
+    case AVMEDIA_TYPE_AUDIO:
         ff_tlog(ctx, " cl:%"PRId64"d n:%d r:%d",
                 ref->channel_layout,
                 ref->nb_samples,
                 ref->sample_rate);
+        break;
     }
 
     ff_tlog(ctx, "]%s", end ? "\n" : "");
diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h
index b105dc3159..9f917deb41 100644
--- a/libavfilter/avfilter.h
+++ b/libavfilter/avfilter.h
@@ -45,6 +45,7 @@
 #include "libavutil/log.h"
 #include "libavutil/samplefmt.h"
 #include "libavutil/pixfmt.h"
+#include "libavutil/subfmt.h"
 #include "libavutil/rational.h"
 
 #include "libavfilter/version.h"
@@ -343,6 +344,12 @@ typedef struct AVFilter {
          * and outputs use the same sample rate and channel count/layout.
          */
         const enum AVSampleFormat *samples_list;
+        /**
+         * Analogous to pixels, but delimited by AV_SUBTITLE_FMT_NONE
+         * and restricted to filters that only have AVMEDIA_TYPE_SUBTITLE
+         * inputs and outputs.
+         */
+        const enum AVSubtitleType *subs_list;
         /**
          * Equivalent to { pix_fmt, AV_PIX_FMT_NONE } as pixels_list.
          */
@@ -351,6 +358,10 @@ typedef struct AVFilter {
          * Equivalent to { sample_fmt, AV_SAMPLE_FMT_NONE } as samples_list.
          */
         enum AVSampleFormat sample_fmt;
+        /**
+         * Equivalent to { sub_fmt, AV_SUBTITLE_FMT_NONE } as subs_list.
+         */
+        enum AVSubtitleType sub_fmt;
     } formats;
 
     int priv_size;      ///< size of private data to allocate for the filter
diff --git a/libavfilter/avfiltergraph.c b/libavfilter/avfiltergraph.c
index b8b432e98b..f4987654af 100644
--- a/libavfilter/avfiltergraph.c
+++ b/libavfilter/avfiltergraph.c
@@ -311,6 +311,8 @@ static int filter_link_check_formats(void *log, AVFilterLink *link, AVFilterForm
             return ret;
         break;
 
+    case AVMEDIA_TYPE_SUBTITLE:
+        return 0;
     default:
         av_assert0(!"reached");
     }
@@ -441,6 +443,9 @@ static int query_formats(AVFilterGraph *graph, void *log_ctx)
             if (!link)
                 continue;
 
+            if (link->type == AVMEDIA_TYPE_SUBTITLE)
+                continue;
+
             neg = ff_filter_get_negotiation(link);
             av_assert0(neg);
             for (neg_step = 1; neg_step < neg->nb_mergers; neg_step++) {
diff --git a/libavfilter/formats.c b/libavfilter/formats.c
index ba62f73248..5c972bb183 100644
--- a/libavfilter/formats.c
+++ b/libavfilter/formats.c
@@ -19,6 +19,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
+#include "libavutil/subfmt.h"
 #include "libavutil/avassert.h"
 #include "libavutil/channel_layout.h"
 #include "libavutil/common.h"
@@ -431,6 +432,12 @@ int ff_add_channel_layout(AVFilterChannelLayouts **l, uint64_t channel_layout)
     return 0;
 }
 
+int ff_add_subtitle_type(AVFilterFormats **avff, int64_t fmt)
+{
+    ADD_FORMAT(avff, fmt, ff_formats_unref, int, formats, nb_formats);
+    return 0;
+}
+
 AVFilterFormats *ff_make_formats_list_singleton(int fmt)
 {
     int fmts[2] = { fmt, -1 };
@@ -450,6 +457,13 @@ AVFilterFormats *ff_all_formats(enum AVMediaType type)
                 return NULL;
             fmt++;
         }
+    } else if (type == AVMEDIA_TYPE_SUBTITLE) {
+        if (ff_add_subtitle_type(&ret, AV_SUBTITLE_FMT_BITMAP) < 0)
+            return NULL;
+        if (ff_add_subtitle_type(&ret, AV_SUBTITLE_FMT_ASS) < 0)
+            return NULL;
+        if (ff_add_subtitle_type(&ret, AV_SUBTITLE_FMT_TEXT) < 0)
+            return NULL;
     }
 
     return ret;
@@ -724,6 +738,10 @@ int ff_default_query_formats(AVFilterContext *ctx)
         type    = AVMEDIA_TYPE_AUDIO;
         formats = ff_make_format_list(f->formats.samples_list);
         break;
+    case FF_FILTER_FORMATS_SUBFMTS_LIST:
+        type    = AVMEDIA_TYPE_SUBTITLE;
+        formats = ff_make_format_list(f->formats.subs_list);
+        break;
     case FF_FILTER_FORMATS_SINGLE_PIXFMT:
         type    = AVMEDIA_TYPE_VIDEO;
         formats = ff_make_formats_list_singleton(f->formats.pix_fmt);
@@ -732,6 +750,10 @@ int ff_default_query_formats(AVFilterContext *ctx)
         type    = AVMEDIA_TYPE_AUDIO;
         formats = ff_make_formats_list_singleton(f->formats.sample_fmt);
         break;
+    case FF_FILTER_FORMATS_SINGLE_SUBFMT:
+        type    = AVMEDIA_TYPE_SUBTITLE;
+        formats = ff_make_formats_list_singleton(f->formats.sub_fmt);
+        break;
     default:
         av_assert2(!"Unreachable");
     /* Intended fallthrough */
diff --git a/libavfilter/formats.h b/libavfilter/formats.h
index a884d15213..94754ebd88 100644
--- a/libavfilter/formats.h
+++ b/libavfilter/formats.h
@@ -180,6 +180,9 @@ int ff_set_common_formats_from_list(AVFilterContext *ctx, const int *fmts);
 av_warn_unused_result
 int ff_add_channel_layout(AVFilterChannelLayouts **l, uint64_t channel_layout);
 
+av_warn_unused_result
+int ff_add_subtitle_type(AVFilterFormats **avff, int64_t fmt);
+
 /**
  * Add *ref as a new reference to f.
  */
diff --git a/libavfilter/internal.h b/libavfilter/internal.h
index fc09ef574c..192b0ae196 100644
--- a/libavfilter/internal.h
+++ b/libavfilter/internal.h
@@ -149,9 +149,11 @@ static av_always_inline int ff_filter_execute(AVFilterContext *ctx, avfilter_act
 
 enum FilterFormatsState {
     /**
-     * The default value meaning that this filter supports all formats
-     * and (for audio) sample rates and channel layouts/counts as long
-     * as these properties agree for all inputs and outputs.
+     * The default value meaning that this filter supports
+     * - For video:     all formats
+     * - For audio:     all sample rates and channel layouts/counts
+     * - For subtitles: all subtitle formats
+     * as long as these properties agree for all inputs and outputs.
      * This state is only allowed in case all inputs and outputs actually
      * have the same type.
      * The union is unused in this state.
@@ -162,8 +164,10 @@ enum FilterFormatsState {
     FF_FILTER_FORMATS_QUERY_FUNC,       ///< formats.query active.
     FF_FILTER_FORMATS_PIXFMT_LIST,      ///< formats.pixels_list active.
     FF_FILTER_FORMATS_SAMPLEFMTS_LIST,  ///< formats.samples_list active.
+    FF_FILTER_FORMATS_SUBFMTS_LIST,     ///< formats.subs_list active.
     FF_FILTER_FORMATS_SINGLE_PIXFMT,    ///< formats.pix_fmt active
     FF_FILTER_FORMATS_SINGLE_SAMPLEFMT, ///< formats.sample_fmt active.
+    FF_FILTER_FORMATS_SINGLE_SUBFMT,    ///< formats.sub_fmt active.
 };
 
 #define FILTER_QUERY_FUNC(func)        \
@@ -175,16 +179,24 @@ enum FilterFormatsState {
 #define FILTER_SAMPLEFMTS_ARRAY(array) \
         .formats.samples_list = array, \
         .formats_state        = FF_FILTER_FORMATS_SAMPLEFMTS_LIST
+#define FILTER_SUBFMTS_ARRAY(array) \
+        .formats.subs_list = array, \
+        .formats_state        = FF_FILTER_FORMATS_SUBFMTS_LIST
 #define FILTER_PIXFMTS(...)            \
     FILTER_PIXFMTS_ARRAY(((const enum AVPixelFormat []) { __VA_ARGS__, AV_PIX_FMT_NONE }))
 #define FILTER_SAMPLEFMTS(...)         \
     FILTER_SAMPLEFMTS_ARRAY(((const enum AVSampleFormat[]) { __VA_ARGS__, AV_SAMPLE_FMT_NONE }))
+#define FILTER_SUBFMTS(...)         \
+    FILTER_SUBFMTS_ARRAY(((const enum AVSubtitleType[]) { __VA_ARGS__, AV_SUBTITLE_FMT_NONE }))
 #define FILTER_SINGLE_PIXFMT(pix_fmt_)  \
         .formats.pix_fmt = pix_fmt_,    \
         .formats_state   = FF_FILTER_FORMATS_SINGLE_PIXFMT
 #define FILTER_SINGLE_SAMPLEFMT(sample_fmt_) \
         .formats.sample_fmt = sample_fmt_,   \
         .formats_state      = FF_FILTER_FORMATS_SINGLE_SAMPLEFMT
+#define FILTER_SINGLE_SUBFMT(sub_fmt_) \
+        .formats.sub_fmt = sub_fmt_,   \
+        .formats_state      = FF_FILTER_FORMATS_SINGLE_SUBFMT
 
 #define FILTER_INOUTPADS(inout, array) \
        .inout        = array, \
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v2 10/26] avfilter/avfilter: Fix hardcoded input index
  2022-01-20  2:48 ` [FFmpeg-devel] [PATCH v2 00/26] Subtitle Filtering 2022 ffmpegagent
                     ` (8 preceding siblings ...)
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 09/26] avfilter/avfilter: Handle subtitle frames ffmpegagent
@ 2022-01-20  2:48   ` ffmpegagent
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 11/26] avfilter/sbuffer: Add sbuffersrc and sbuffersink filters ffmpegagent
                     ` (16 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  2:48 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

This fix targets (rare) cases where multiple input pads have a
.filter_frame function. ff_request_frame_to_filter needs
to call ff_request_frame with the correct input pad
instead of the hardcoded first one.

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavfilter/avfilter.c | 18 +++++++++++++-----
 1 file changed, 13 insertions(+), 5 deletions(-)

diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c
index 75d5e86539..aa9aa71f53 100644
--- a/libavfilter/avfilter.c
+++ b/libavfilter/avfilter.c
@@ -463,7 +463,7 @@ static int64_t guess_status_pts(AVFilterContext *ctx, int status, AVRational lin
     return AV_NOPTS_VALUE;
 }
 
-static int ff_request_frame_to_filter(AVFilterLink *link)
+static int ff_request_frame_to_filter(AVFilterLink *link, int input_index)
 {
     int ret = -1;
 
@@ -472,8 +472,8 @@ static int ff_request_frame_to_filter(AVFilterLink *link)
     link->frame_blocked_in = 1;
     if (link->srcpad->request_frame)
         ret = link->srcpad->request_frame(link);
-    else if (link->src->inputs[0])
-        ret = ff_request_frame(link->src->inputs[0]);
+    else if (link->src->inputs[input_index])
+        ret = ff_request_frame(link->src->inputs[input_index]);
     if (ret < 0) {
         if (ret != AVERROR(EAGAIN) && ret != link->status_in)
             ff_avfilter_link_set_in_status(link, ret, guess_status_pts(link->src, ret, link->time_base));
@@ -1172,6 +1172,14 @@ static int forward_status_change(AVFilterContext *filter, AVFilterLink *in)
 {
     unsigned out = 0, progress = 0;
     int ret;
+    int input_index = 0;
+
+    for (int i = 0; i < in->dst->nb_inputs; i++) {
+        if (&in->dst->input_pads[i] == in->dstpad) {
+            input_index = i;
+            break;
+        }
+    }
 
     av_assert0(!in->status_out);
     if (!filter->nb_outputs) {
@@ -1181,7 +1189,7 @@ static int forward_status_change(AVFilterContext *filter, AVFilterLink *in)
     while (!in->status_out) {
         if (!filter->outputs[out]->status_in) {
             progress++;
-            ret = ff_request_frame_to_filter(filter->outputs[out]);
+            ret = ff_request_frame_to_filter(filter->outputs[out], input_index);
             if (ret < 0)
                 return ret;
         }
@@ -1218,7 +1226,7 @@ static int ff_filter_activate_default(AVFilterContext *filter)
     for (i = 0; i < filter->nb_outputs; i++) {
         if (filter->outputs[i]->frame_wanted_out &&
             !filter->outputs[i]->frame_blocked_in) {
-            return ff_request_frame_to_filter(filter->outputs[i]);
+            return ff_request_frame_to_filter(filter->outputs[i], 0);
         }
     }
     return FFERROR_NOT_READY;
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v2 11/26] avfilter/sbuffer: Add sbuffersrc and sbuffersink filters
  2022-01-20  2:48 ` [FFmpeg-devel] [PATCH v2 00/26] Subtitle Filtering 2022 ffmpegagent
                     ` (9 preceding siblings ...)
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 10/26] avfilter/avfilter: Fix hardcoded input index ffmpegagent
@ 2022-01-20  2:48   ` ffmpegagent
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 12/26] avfilter/overlaygraphicsubs: Add overlaygraphicsubs and graphicsub2video filters ffmpegagent
                     ` (15 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  2:48 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 configure                |  2 +-
 libavfilter/allfilters.c |  2 ++
 libavfilter/buffersink.c | 54 ++++++++++++++++++++++++++++++
 libavfilter/buffersink.h |  7 ++++
 libavfilter/buffersrc.c  | 72 ++++++++++++++++++++++++++++++++++++++++
 libavfilter/buffersrc.h  |  1 +
 6 files changed, 137 insertions(+), 1 deletion(-)

diff --git a/configure b/configure
index 1413122d87..7c2931bd50 100755
--- a/configure
+++ b/configure
@@ -7854,7 +7854,7 @@ print_enabled_components(){
         fi
     done
     if [ "$name" = "filter_list" ]; then
-        for c in asrc_abuffer vsrc_buffer asink_abuffer vsink_buffer; do
+        for c in asrc_abuffer vsrc_buffer ssrc_sbuffer asink_abuffer vsink_buffer ssink_sbuffer; do
             printf "    &ff_%s,\n" $c >> $TMPH
         done
     fi
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 4325a3e557..8a3bd03924 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -556,8 +556,10 @@ extern const AVFilter ff_avsrc_movie;
  * being the same while having different 'types'). */
 extern  const AVFilter ff_asrc_abuffer;
 extern  const AVFilter ff_vsrc_buffer;
+extern  const AVFilter ff_ssrc_sbuffer;
 extern  const AVFilter ff_asink_abuffer;
 extern  const AVFilter ff_vsink_buffer;
+extern  const AVFilter ff_ssink_sbuffer;
 extern const AVFilter ff_af_afifo;
 extern const AVFilter ff_vf_fifo;
 
diff --git a/libavfilter/buffersink.c b/libavfilter/buffersink.c
index c0215669e7..0b268c2fa4 100644
--- a/libavfilter/buffersink.c
+++ b/libavfilter/buffersink.c
@@ -29,6 +29,8 @@
 #include "libavutil/internal.h"
 #include "libavutil/opt.h"
 
+#include "libavcodec/avcodec.h"
+
 #define FF_INTERNAL_FIELDS 1
 #include "framequeue.h"
 
@@ -57,6 +59,10 @@ typedef struct BufferSinkContext {
     int *sample_rates;                  ///< list of accepted sample rates
     int sample_rates_size;
 
+    /* only used for subtitles */
+    enum AVSubtitleType *subtitle_types;     ///< list of accepted subtitle types, must be terminated with -1
+    int subtitle_types_size;
+
     AVFrame *peeked_frame;
 } BufferSinkContext;
 
@@ -305,6 +311,28 @@ static int asink_query_formats(AVFilterContext *ctx)
     return 0;
 }
 
+static int ssink_query_formats(AVFilterContext *ctx)
+{
+    BufferSinkContext *buf = ctx->priv;
+    AVFilterFormats *formats = NULL;
+    unsigned i;
+    int ret;
+
+    CHECK_LIST_SIZE(subtitle_types)
+    if (buf->subtitle_types_size) {
+        for (i = 0; i < NB_ITEMS(buf->subtitle_types); i++)
+            if ((ret = ff_add_subtitle_type(&formats, buf->subtitle_types[i])) < 0)
+                return ret;
+        if ((ret = ff_set_common_formats(ctx, formats)) < 0)
+            return ret;
+    } else {
+        if ((ret = ff_default_query_formats(ctx)) < 0)
+            return ret;
+    }
+
+    return 0;
+}
+
 #define OFFSET(x) offsetof(BufferSinkContext, x)
 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
 static const AVOption buffersink_options[] = {
@@ -322,9 +350,16 @@ static const AVOption abuffersink_options[] = {
     { NULL },
 };
 #undef FLAGS
+#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_SUBTITLE_PARAM
+static const AVOption sbuffersink_options[] = {
+    { "subtitle_types", "set the supported subtitle formats", OFFSET(subtitle_types), AV_OPT_TYPE_BINARY, .flags = FLAGS },
+    { NULL },
+};
+#undef FLAGS
 
 AVFILTER_DEFINE_CLASS(buffersink);
 AVFILTER_DEFINE_CLASS(abuffersink);
+AVFILTER_DEFINE_CLASS(sbuffersink);
 
 static const AVFilterPad avfilter_vsink_buffer_inputs[] = {
     {
@@ -363,3 +398,22 @@ const AVFilter ff_asink_abuffer = {
     .outputs       = NULL,
     FILTER_QUERY_FUNC(asink_query_formats),
 };
+
+static const AVFilterPad avfilter_ssink_sbuffer_inputs[] = {
+    {
+        .name = "default",
+        .type = AVMEDIA_TYPE_SUBTITLE,
+    },
+};
+
+const AVFilter ff_ssink_sbuffer = {
+    .name          = "sbuffersink",
+    .description   = NULL_IF_CONFIG_SMALL("Buffer subtitle frames, and make them available to the end of the filter graph."),
+    .priv_class    = &sbuffersink_class,
+    .priv_size     = sizeof(BufferSinkContext),
+    .init          = common_init,
+    .activate      = activate,
+    FILTER_INPUTS(avfilter_ssink_sbuffer_inputs),
+    .outputs       = NULL,
+    FILTER_QUERY_FUNC(ssink_query_formats),
+};
diff --git a/libavfilter/buffersink.h b/libavfilter/buffersink.h
index 69ed0f29a8..11905abdc5 100644
--- a/libavfilter/buffersink.h
+++ b/libavfilter/buffersink.h
@@ -129,6 +129,13 @@ typedef struct AVABufferSinkParams {
  */
 attribute_deprecated
 AVABufferSinkParams *av_abuffersink_params_alloc(void);
+
+/**
+ * Deprecated and unused struct to use for initializing an sbuffersink context.
+ */
+typedef struct AVSBufferSinkParams {
+    const int *subtitle_type;
+} AVSBufferSinkParams;
 #endif
 
 /**
diff --git a/libavfilter/buffersrc.c b/libavfilter/buffersrc.c
index b0611872f1..d2362999a2 100644
--- a/libavfilter/buffersrc.c
+++ b/libavfilter/buffersrc.c
@@ -39,6 +39,7 @@
 #include "formats.h"
 #include "internal.h"
 #include "video.h"
+#include "libavcodec/avcodec.h"
 
 typedef struct BufferSourceContext {
     const AVClass    *class;
@@ -63,6 +64,9 @@ typedef struct BufferSourceContext {
     uint64_t channel_layout;
     char    *channel_layout_str;
 
+    /* subtitle only */
+    enum AVSubtitleType subtitle_type;
+
     int eof;
 } BufferSourceContext;
 
@@ -130,6 +134,13 @@ int av_buffersrc_parameters_set(AVFilterContext *ctx, AVBufferSrcParameters *par
         if (param->channel_layout)
             s->channel_layout = param->channel_layout;
         break;
+    case AVMEDIA_TYPE_SUBTITLE:
+        s->subtitle_type = param->format;
+        if (param->width > 0)
+            s->w = param->width;
+        if (param->height > 0)
+            s->h = param->height;
+        break;
     default:
         return AVERROR_BUG;
     }
@@ -197,6 +208,8 @@ int attribute_align_arg av_buffersrc_add_frame_flags(AVFilterContext *ctx, AVFra
             CHECK_AUDIO_PARAM_CHANGE(ctx, s, frame->sample_rate, frame->channel_layout,
                                      frame->channels, frame->format, frame->pts);
             break;
+        case AVMEDIA_TYPE_SUBTITLE:
+            break;
         default:
             return AVERROR(EINVAL);
         }
@@ -269,6 +282,7 @@ unsigned av_buffersrc_get_nb_failed_requests(AVFilterContext *buffer_src)
 #define OFFSET(x) offsetof(BufferSourceContext, x)
 #define A AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_AUDIO_PARAM
 #define V AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
+#define S AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_SUBTITLE_PARAM
 
 static const AVOption buffer_options[] = {
     { "width",         NULL,                     OFFSET(w),                AV_OPT_TYPE_INT,      { .i64 = 0 }, 0, INT_MAX, V },
@@ -298,6 +312,16 @@ static const AVOption abuffer_options[] = {
 
 AVFILTER_DEFINE_CLASS(abuffer);
 
+static const AVOption sbuffer_options[] = {
+    { "time_base",     NULL, OFFSET(time_base),            AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, 0, INT_MAX, S },
+    { "subtitle_type", NULL, OFFSET(subtitle_type),        AV_OPT_TYPE_INT,      { .i64 = 0 }, 0, INT_MAX, S },
+    { "width",         NULL, OFFSET(w),                    AV_OPT_TYPE_INT,      { .i64 = 0 }, 0, INT_MAX, V },
+    { "height",        NULL, OFFSET(h),                    AV_OPT_TYPE_INT,      { .i64 = 0 }, 0, INT_MAX, V },
+    { NULL },
+};
+
+AVFILTER_DEFINE_CLASS(sbuffer);
+
 static av_cold int init_audio(AVFilterContext *ctx)
 {
     BufferSourceContext *s = ctx->priv;
@@ -347,6 +371,21 @@ static av_cold int init_audio(AVFilterContext *ctx)
     return ret;
 }
 
+static av_cold int init_subtitle(AVFilterContext *ctx)
+{
+    BufferSourceContext *c = ctx->priv;
+
+    if (c->subtitle_type == AV_SUBTITLE_FMT_BITMAP)
+        av_log(ctx, AV_LOG_VERBOSE, "graphical subtitles - w:%d h:%d tb:%d/%d\n",
+               c->w, c->h, c->time_base.num, c->time_base.den);
+    else
+        av_log(ctx, AV_LOG_VERBOSE, "text subtitles -  w:%d h:%d tb:%d/%d\n",
+               c->w, c->h, c->time_base.num, c->time_base.den);
+
+    return 0;
+}
+
+
 static av_cold void uninit(AVFilterContext *ctx)
 {
     BufferSourceContext *s = ctx->priv;
@@ -381,6 +420,11 @@ static int query_formats(AVFilterContext *ctx)
         if ((ret = ff_set_common_channel_layouts(ctx, channel_layouts)) < 0)
             return ret;
         break;
+    case AVMEDIA_TYPE_SUBTITLE:
+        if ((ret = ff_add_format         (&formats, c->subtitle_type)) < 0 ||
+            (ret = ff_set_common_formats (ctx     , formats   )) < 0)
+            return ret;
+        break;
     default:
         return AVERROR(EINVAL);
     }
@@ -408,6 +452,11 @@ static int config_props(AVFilterLink *link)
         if (!c->channel_layout)
             c->channel_layout = link->channel_layout;
         break;
+    case AVMEDIA_TYPE_SUBTITLE:
+        link->format = c->subtitle_type;
+        link->w = c->w;
+        link->h = c->h;
+        break;
     default:
         return AVERROR(EINVAL);
     }
@@ -472,3 +521,26 @@ const AVFilter ff_asrc_abuffer = {
     FILTER_QUERY_FUNC(query_formats),
     .priv_class = &abuffer_class,
 };
+
+static const AVFilterPad ssrc_sbuffer_outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_SUBTITLE,
+        .request_frame = request_frame,
+        .config_props  = config_props,
+    },
+};
+
+const AVFilter ff_ssrc_sbuffer = {
+    .name          = "sbuffer",
+    .description   = NULL_IF_CONFIG_SMALL("Buffer subtitle frames, and make them accessible to the filterchain."),
+    .priv_size     = sizeof(BufferSourceContext),
+
+    .init      = init_subtitle,
+    .uninit    = uninit,
+
+    .inputs    = NULL,
+    FILTER_OUTPUTS(ssrc_sbuffer_outputs),
+    FILTER_QUERY_FUNC(query_formats),
+    .priv_class = &sbuffer_class,
+};
diff --git a/libavfilter/buffersrc.h b/libavfilter/buffersrc.h
index 08fbd18a47..929a2fa249 100644
--- a/libavfilter/buffersrc.h
+++ b/libavfilter/buffersrc.h
@@ -74,6 +74,7 @@ typedef struct AVBufferSrcParameters {
     /**
      * video: the pixel format, value corresponds to enum AVPixelFormat
      * audio: the sample format, value corresponds to enum AVSampleFormat
+     * subtitles: the subtitle format, value corresponds to enum AVSubtitleType
      */
     int format;
     /**
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v2 12/26] avfilter/overlaygraphicsubs: Add overlaygraphicsubs and graphicsub2video filters
  2022-01-20  2:48 ` [FFmpeg-devel] [PATCH v2 00/26] Subtitle Filtering 2022 ffmpegagent
                     ` (10 preceding siblings ...)
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 11/26] avfilter/sbuffer: Add sbuffersrc and sbuffersink filters ffmpegagent
@ 2022-01-20  2:48   ` ffmpegagent
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 13/26] avfilter/overlaytextsubs: Add overlaytextsubs and textsubs2video filters ffmpegagent
                     ` (14 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  2:48 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

- overlaygraphicsubs (VS -> V)
  Overlay graphic subtitles onto a video stream

- graphicsub2video {S -> V)
  Converts graphic subtitles to video frames (with alpha)
  Gets auto-inserted for retaining compatibility with
  sub2video command lines

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 doc/filters.texi                    | 118 +++++
 libavfilter/Makefile                |   2 +
 libavfilter/allfilters.c            |   2 +
 libavfilter/vf_overlaygraphicsubs.c | 765 ++++++++++++++++++++++++++++
 4 files changed, 887 insertions(+)
 create mode 100644 libavfilter/vf_overlaygraphicsubs.c

diff --git a/doc/filters.texi b/doc/filters.texi
index 05d4b1a56e..4fc4a57dbb 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -25713,6 +25713,124 @@ tools.
 
 @c man end VIDEO SINKS
 
+@chapter Subtitle Filters
+@c man begin SUBTITLE FILTERS
+
+When you configure your FFmpeg build, you can disable any of the
+existing filters using @code{--disable-filters}.
+
+Below is a description of the currently available subtitle filters.
+
+@section graphicsub2video
+
+Renders graphic subtitles as video frames.
+
+This filter replaces the previous "sub2video" hack which did the conversion implicitly and up-front as subtitle filtering wasn't possible at that time.
+To retain compatibility with earlier sub2video command lines, this filter is being auto-inserted in those cases.
+
+For overlaying graphicsal subtitles it is recommended to use the 'overlay_graphicsubs' filter which is more efficient and takes less processing resources.
+
+This filter is still useful in cases where the overlay is done with hardware acceleration (e.g. overlay_qsv, overlay_vaapi, overlay_cuda) for preparing the overlay frames.
+
+Inputs:
+@itemize
+@item 0: Subtitles [BITMAP]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Video [RGB32]
+@end itemize
+
+
+It accepts the following parameters:
+
+@table @option
+@item size, s
+Set the size of the output video frame.
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Overlay PGS subtitles
+(not recommended - better use overlay_graphicsubs)
+@example
+ffmpeg -i "https://streams.videolan.org/samples/sub/PGS/Girl_With_The_Dragon_Tattoo_2%3A23%3A56.mkv" -filter_complex "[0:1]graphicsub2video[subs];[0:0][subs]overlay" output.mp4
+@end example
+
+@item
+Overlay PGS subtitles implicitly
+The graphicsub2video is inserted automatically for compatibility with legacy command lines.
+@example
+ffmpeg -i "https://streams.videolan.org/samples/sub/PGS/Girl_With_The_Dragon_Tattoo_2%3A23%3A56.mkv" -filter_complex "[0:0][0:1]overlay" output.mp4
+@end example
+@end itemize
+
+@section overlaygraphicsubs
+
+Overlay graphic subtitles onto a video stream.
+
+This filter can blend graphical subtitles on a video stream directly, i.e. without creating full-size alpha images first.
+The blending operation is limited to the area of the subtitle rectangles, which also means that no processing is done at times where no subtitles are to be displayed.
+
+Inputs:
+@itemize
+@item 0: Video [YUV420P, YUV422P, YUV444P, ARGB, RGBA, ABGR, BGRA, RGB24, BGR24]
+@item 1: Subtitles [BITMAP]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Video (same as input)
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+@item x
+@item y
+Set the expression for the x and y coordinates of the overlaid video
+on the main video. Default value is "0" for both expressions. In case
+the expression is invalid, it is set to a huge value (meaning that the
+overlay will not be displayed within the output visible area).
+
+@item eof_action
+See @ref{framesync}.
+
+@item eval
+Set when the expressions for @option{x}, and @option{y} are evaluated.
+
+It accepts the following values:
+@table @samp
+@item init
+only evaluate expressions once during the filter initialization or
+when a command is processed
+
+@item frame
+evaluate expressions for each incoming frame
+@end table
+
+Default value is @samp{frame}.
+
+@item shortest
+See @ref{framesync}.
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Overlay PGS subtitles
+@example
+ffmpeg -i "https://streams.videolan.org/samples/sub/PGS/Girl_With_The_Dragon_Tattoo_2%3A23%3A56.mkv" -filter_complex "[0:0][0:1]overlaygraphicsubs" output.mp4
+@end example
+@end itemize
+@c man end SUBTITLE FILTERS
+
 @chapter Multimedia Filters
 @c man begin MULTIMEDIA FILTERS
 
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 283dd436cd..a372effc12 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -298,6 +298,7 @@ OBJS-$(CONFIG_GBLUR_FILTER)                  += vf_gblur.o
 OBJS-$(CONFIG_GBLUR_VULKAN_FILTER)           += vf_gblur_vulkan.o vulkan.o vulkan_filter.o
 OBJS-$(CONFIG_GEQ_FILTER)                    += vf_geq.o
 OBJS-$(CONFIG_GRADFUN_FILTER)                += vf_gradfun.o
+OBJS-$(CONFIG_GRAPHICSUB2VIDEO_FILTER)       += vf_overlaygraphicsubs.o framesync.o
 OBJS-$(CONFIG_GRAPHMONITOR_FILTER)           += f_graphmonitor.o
 OBJS-$(CONFIG_GRAYWORLD_FILTER)              += vf_grayworld.o
 OBJS-$(CONFIG_GREYEDGE_FILTER)               += vf_colorconstancy.o
@@ -378,6 +379,7 @@ OBJS-$(CONFIG_OVERLAY_OPENCL_FILTER)         += vf_overlay_opencl.o opencl.o \
                                                 opencl/overlay.o framesync.o
 OBJS-$(CONFIG_OVERLAY_QSV_FILTER)            += vf_overlay_qsv.o framesync.o
 OBJS-$(CONFIG_OVERLAY_VULKAN_FILTER)         += vf_overlay_vulkan.o vulkan.o vulkan_filter.o
+OBJS-$(CONFIG_OVERLAYGRAPHICSUBS_FILTER)     += vf_overlaygraphicsubs.o framesync.o
 OBJS-$(CONFIG_OWDENOISE_FILTER)              += vf_owdenoise.o
 OBJS-$(CONFIG_PAD_FILTER)                    += vf_pad.o
 OBJS-$(CONFIG_PAD_OPENCL_FILTER)             += vf_pad_opencl.o opencl.o opencl/pad.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 8a3bd03924..eb4e30a270 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -358,6 +358,7 @@ extern const AVFilter ff_vf_oscilloscope;
 extern const AVFilter ff_vf_overlay;
 extern const AVFilter ff_vf_overlay_opencl;
 extern const AVFilter ff_vf_overlay_qsv;
+extern const AVFilter ff_vf_overlaygraphicsubs;
 extern const AVFilter ff_vf_overlay_vulkan;
 extern const AVFilter ff_vf_overlay_cuda;
 extern const AVFilter ff_vf_owdenoise;
@@ -545,6 +546,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_svf_graphicsub2video;
 
 /* multimedia sources */
 extern const AVFilter ff_avsrc_amovie;
diff --git a/libavfilter/vf_overlaygraphicsubs.c b/libavfilter/vf_overlaygraphicsubs.c
new file mode 100644
index 0000000000..7b26d8ef37
--- /dev/null
+++ b/libavfilter/vf_overlaygraphicsubs.c
@@ -0,0 +1,765 @@
+/*
+ * Copyright (c) 2021 softworkz (derived from vf_overlay)
+ * Copyright (c) 2010 Stefano Sabatini
+ * Copyright (c) 2010 Baptiste Coudurier
+ * Copyright (c) 2007 Bobby Bingham
+ *
+ * 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
+ * overlay graphical subtitles on top of a video frame
+ */
+
+#include "libavutil/eval.h"
+#include "libavutil/imgutils.h"
+#include "libavutil/opt.h"
+#include "internal.h"
+#include "drawutils.h"
+#include "framesync.h"
+
+enum var_name {
+    VAR_MAIN_W,    VAR_MW,
+    VAR_MAIN_H,    VAR_MH,
+    VAR_OVERLAY_W, VAR_OW,
+    VAR_OVERLAY_H, VAR_OH,
+    VAR_HSUB,
+    VAR_VSUB,
+    VAR_X,
+    VAR_Y,
+    VAR_N,
+    VAR_POS,
+    VAR_T,
+    VAR_VARS_NB
+};
+
+typedef struct OverlaySubsContext {
+    const AVClass *class;
+    int x, y;                   ///< position of overlaid picture
+    int w, h;
+    AVFrame *outpicref;
+
+    int main_is_packed_rgb;
+    uint8_t main_rgba_map[4];
+    int main_has_alpha;
+    uint8_t overlay_rgba_map[4];
+    int eval_mode;              ///< EvalMode
+    int use_caching;
+    AVFrame *cache_frame;
+
+    FFFrameSync fs;
+
+    int main_pix_step[4];       ///< steps per pixel for each plane of the main output
+    int hsub, vsub;             ///< chroma subsampling values
+    const AVPixFmtDescriptor *main_desc; ///< format descriptor for main input
+
+    double var_values[VAR_VARS_NB];
+    char *x_expr, *y_expr;
+
+    AVExpr *x_pexpr, *y_pexpr;
+
+    int pic_counter;
+} OverlaySubsContext;
+
+static const char *const var_names[] = {
+    "main_w",    "W", ///< width  of the main    video
+    "main_h",    "H", ///< height of the main    video
+    "overlay_w", "w", ///< width  of the overlay video
+    "overlay_h", "h", ///< height of the overlay video
+    "hsub",
+    "vsub",
+    "x",
+    "y",
+    "n",            ///< number of frame
+    "pos",          ///< position in the file
+    "t",            ///< timestamp expressed in seconds
+    NULL
+};
+
+#define MAIN    0
+#define OVERLAY 1
+
+#define R 0
+#define G 1
+#define B 2
+#define A 3
+
+#define Y 0
+#define U 1
+#define V 2
+
+enum EvalMode {
+    EVAL_MODE_INIT,
+    EVAL_MODE_FRAME,
+    EVAL_MODE_NB
+};
+
+static av_cold void overlay_graphicsubs_uninit(AVFilterContext *ctx)
+{
+    OverlaySubsContext *s = ctx->priv;
+
+    av_frame_free(&s->cache_frame);
+    ff_framesync_uninit(&s->fs);
+    av_expr_free(s->x_pexpr); s->x_pexpr = NULL;
+    av_expr_free(s->y_pexpr); s->y_pexpr = NULL;
+}
+
+static inline int normalize_xy(double d, int chroma_sub)
+{
+    if (isnan(d))
+        return INT_MAX;
+    return (int)d & ~((1 << chroma_sub) - 1);
+}
+
+static void eval_expr(AVFilterContext *ctx)
+{
+    OverlaySubsContext *s = ctx->priv;
+
+    s->var_values[VAR_X] = av_expr_eval(s->x_pexpr, s->var_values, NULL);
+    s->var_values[VAR_Y] = av_expr_eval(s->y_pexpr, s->var_values, NULL);
+    /* It is necessary if x is expressed from y  */
+    s->var_values[VAR_X] = av_expr_eval(s->x_pexpr, s->var_values, NULL);
+    s->x = normalize_xy(s->var_values[VAR_X], s->hsub);
+    s->y = normalize_xy(s->var_values[VAR_Y], s->vsub);
+}
+
+static int set_expr(AVExpr **pexpr, const char *expr, const char *option, void *log_ctx)
+{
+    int ret;
+    AVExpr *old = NULL;
+
+    if (*pexpr)
+        old = *pexpr;
+    ret = av_expr_parse(pexpr, expr, var_names,
+                        NULL, NULL, NULL, NULL, 0, log_ctx);
+    if (ret < 0) {
+        av_log(log_ctx, AV_LOG_ERROR,
+               "Error when evaluating the expression '%s' for %s\n",
+               expr, option);
+        *pexpr = old;
+        return ret;
+    }
+
+    av_expr_free(old);
+    return 0;
+}
+
+static int overlay_graphicsubs_query_formats(AVFilterContext *ctx)
+{
+    AVFilterFormats *formats;
+    AVFilterLink *inlink0 = ctx->inputs[0];
+    AVFilterLink *inlink1 = ctx->inputs[1];
+    AVFilterLink *outlink = ctx->outputs[0];
+    int ret;
+    static const enum AVSubtitleType subtitle_fmts[] = { AV_SUBTITLE_FMT_BITMAP, AV_SUBTITLE_FMT_NONE };
+    static const enum AVPixelFormat supported_pix_fmts[] = {
+        AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV444P,
+        AV_PIX_FMT_ARGB,  AV_PIX_FMT_RGBA,
+        AV_PIX_FMT_ABGR,  AV_PIX_FMT_BGRA,
+        AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24,
+        AV_PIX_FMT_NONE
+    };
+
+    /* set input0 video formats */
+    formats = ff_make_format_list(supported_pix_fmts);
+    if ((ret = ff_formats_ref(formats, &inlink0->outcfg.formats)) < 0)
+        return ret;
+
+    /* set output0 video formats */
+    if ((ret = ff_formats_ref(formats, &outlink->incfg.formats)) < 0)
+        return ret;
+
+    /* set input1 subtitle formats */
+    formats = ff_make_format_list(subtitle_fmts);
+    if ((ret = ff_formats_ref(formats, &inlink1->outcfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    OverlaySubsContext *s = ctx->priv;
+    int ret;
+
+    if ((ret = ff_framesync_init_dualinput(&s->fs, ctx)) < 0)
+        return ret;
+
+    outlink->w = ctx->inputs[MAIN]->w;
+    outlink->h = ctx->inputs[MAIN]->h;
+    outlink->time_base = ctx->inputs[MAIN]->time_base;
+    outlink->frame_rate = ctx->inputs[MAIN]->frame_rate;
+
+    return ff_framesync_configure(&s->fs);
+}
+
+// divide by 255 and round to nearest
+// apply a fast variant: (X+127)/255 = ((X+127)*257+257)>>16 = ((X+128)*257)>>16
+#define FAST_DIV255(x) ((((x) + 128) * 257) >> 16)
+
+// calculate the non-pre-multiplied alpha, applying the general equation:
+// alpha = alpha_overlay / ( (alpha_main + alpha_overlay) - (alpha_main * alpha_overlay) )
+// (((x) << 16) - ((x) << 9) + (x)) is a faster version of: 255 * 255 * x
+// ((((x) + (y)) << 8) - ((x) + (y)) - (y) * (x)) is a faster version of: 255 * (x + y)
+#define UNPREMULTIPLY_ALPHA(x, y) ((((x) << 16) - ((x) << 9) + (x)) / ((((x) + (y)) << 8) - ((x) + (y)) - (y) * (x)))
+
+/**
+ * Blend image in src to destination buffer dst at position (x, y).
+ */
+static av_always_inline void blend_packed_rgb(const AVFilterContext *ctx,
+    const AVFrame *dst, const AVSubtitleArea *src,
+    int x, int y,
+    int is_straight)
+{
+    OverlaySubsContext *s = ctx->priv;
+    int i, imax, j, jmax;
+    const int src_w = src->w;
+    const int src_h = src->h;
+    const int dst_w = dst->width;
+    const int dst_h = dst->height;
+    uint8_t alpha;          ///< the amount of overlay to blend on to main
+    const int dr = s->main_rgba_map[R];
+    const int dg = s->main_rgba_map[G];
+    const int db = s->main_rgba_map[B];
+    const int da = s->main_rgba_map[A];
+    const int dstep = s->main_pix_step[0];
+    const int sr = s->overlay_rgba_map[R];
+    const int sg = s->overlay_rgba_map[G];
+    const int sb = s->overlay_rgba_map[B];
+    const int sa = s->overlay_rgba_map[A];
+    int slice_start, slice_end;
+    uint8_t *S, *sp, *d, *dp;
+
+    i = FFMAX(-y, 0);
+    imax = FFMIN3(-y + dst_h, FFMIN(src_h, dst_h), y + src_h);
+
+    slice_start = i;
+    slice_end = i + imax;
+
+    sp = src->buf[0]->data + slice_start       * src->linesize[0];
+    dp = dst->data[0] + (slice_start + y) * dst->linesize[0];
+
+    for (i = slice_start; i < slice_end; i++) {
+        j = FFMAX(-x, 0);
+        S = sp + j;
+        d = dp + ((x + j) * dstep);
+
+        for (jmax = FFMIN(-x + dst_w, src_w); j < jmax; j++) {
+            uint32_t val = src->pal[*S];
+            const uint8_t *sval = (uint8_t *)&val;
+            alpha = sval[sa];
+
+            // if the main channel has an alpha channel, alpha has to be calculated
+            // to create an un-premultiplied (straight) alpha value
+            if (s->main_has_alpha && alpha != 0 && alpha != 255) {
+                const uint8_t alpha_d = d[da];
+                alpha = UNPREMULTIPLY_ALPHA(alpha, alpha_d);
+            }
+
+            switch (alpha) {
+            case 0:
+                break;
+            case 255:
+                d[dr] = sval[sr];
+                d[dg] = sval[sg];
+                d[db] = sval[sb];
+                break;
+            default:
+                // main_value = main_value * (1 - alpha) + overlay_value * alpha
+                // since alpha is in the range 0-255, the result must divided by 255
+                d[dr] = is_straight ? FAST_DIV255(d[dr] * (255 - alpha) + sval[sr] * alpha) :
+                        FFMIN(FAST_DIV255(d[dr] * (255 - alpha)) + sval[sr], 255);
+                d[dg] = is_straight ? FAST_DIV255(d[dg] * (255 - alpha) + sval[sg] * alpha) :
+                        FFMIN(FAST_DIV255(d[dg] * (255 - alpha)) + sval[sg], 255);
+                d[db] = is_straight ? FAST_DIV255(d[db] * (255 - alpha) + sval[sb] * alpha) :
+                        FFMIN(FAST_DIV255(d[db] * (255 - alpha)) + sval[sb], 255);
+            }
+
+            if (s->main_has_alpha) {
+                switch (alpha) {
+                case 0:
+                    break;
+                case 255:
+                    d[da] = sval[sa];
+                    break;
+                default:
+                    // apply alpha compositing: main_alpha += (1-main_alpha) * overlay_alpha
+                    d[da] += FAST_DIV255((255 - d[da]) * S[sa]);
+                }
+            }
+            d += dstep;
+            S += 1;
+        }
+        dp += dst->linesize[0];
+        sp += src->linesize[0];
+    }
+}
+
+static av_always_inline void blend_plane_8_8bits(const AVFilterContext *ctx, const AVFrame *dst, const AVSubtitleArea *area,
+    const uint32_t *yuv_pal, int src_w, int src_h, int dst_w, int dst_h, int plane, int hsub, int vsub,
+    int x, int y, int dst_plane, int dst_offset, int dst_step)
+{
+    const int src_wp = AV_CEIL_RSHIFT(src_w, hsub);
+    const int src_hp = AV_CEIL_RSHIFT(src_h, vsub);
+    const int dst_wp = AV_CEIL_RSHIFT(dst_w, hsub);
+    const int dst_hp = AV_CEIL_RSHIFT(dst_h, vsub);
+    const int yp = y >> vsub;
+    const int xp = x >> hsub;
+    uint8_t *s, *sp, *d, *dp, *dap;
+    int imax, i, j, jmax;
+    int slice_start, slice_end;
+
+    i = FFMAX(-yp, 0);                                                                                     \
+    imax = FFMIN3(-yp + dst_hp, FFMIN(src_hp, dst_hp), yp + src_hp);                                       \
+
+    slice_start = i;
+    slice_end = i + imax;
+
+    sp = area->buf[0]->data + (slice_start << vsub) * area->linesize[0];
+    dp = dst->data[dst_plane] + (yp + slice_start) * dst->linesize[dst_plane] + dst_offset;
+
+    dap = dst->data[3] + ((yp + slice_start) << vsub) * dst->linesize[3];
+
+    for (i = slice_start; i < slice_end; i++) {
+        j = FFMAX(-xp, 0);
+        d = dp + (xp + j) * dst_step;
+        s = sp + (j << hsub);
+        jmax = FFMIN(-xp + dst_wp, src_wp);
+
+        for (; j < jmax; j++) {
+            uint32_t val = yuv_pal[*s];
+            const uint8_t *sval = (uint8_t *)&val;
+            const int alpha = sval[3];
+            const int max = 255, mid = 128;
+            const int d_int = *d;
+            const int sval_int = sval[plane];
+
+            switch (alpha) {
+            case 0:
+                break;
+            case 255:
+                *d = sval[plane];
+                break;
+            default:
+                if (plane > 0)
+                    *d = av_clip(FAST_DIV255((d_int - mid) * (max - alpha) + (sval_int - mid) * alpha) , -mid, mid) + mid;
+                else
+                    *d = FAST_DIV255(d_int * (max - alpha) + sval_int * alpha);
+                break;
+            }
+
+            d += dst_step;
+            s += 1 << hsub;
+        }
+        dp += dst->linesize[dst_plane];
+        sp +=  (1 << vsub) * area->linesize[0];
+        dap += (1 << vsub) * dst->linesize[3];
+    }
+}
+
+#define RGB2Y(r, g, b) (uint8_t)(((66 * (r) + 129 * (g) +  25 * (b) + 128) >> 8) +  16)
+#define RGB2U(r, g, b) (uint8_t)(((-38 * (r) - 74 * (g) + 112 * (b) + 128) >> 8) + 128)
+#define RGB2V(r, g, b) (uint8_t)(((112 * (r) - 94 * (g) -  18 * (b) + 128) >> 8) + 128)
+/* Converts R8 G8 B8 color to YUV. */
+static av_always_inline void rgb_2_yuv(uint8_t r, uint8_t g, uint8_t b, uint8_t* y, uint8_t* u, uint8_t* v)
+{
+    *y = RGB2Y((int)r, (int)g, (int)b);
+    *u = RGB2U((int)r, (int)g, (int)b);
+    *v = RGB2V((int)r, (int)g, (int)b);
+}
+
+
+static av_always_inline void blend_yuv_8_8bits(AVFilterContext *ctx, AVFrame *dst, const AVSubtitleArea *area, int hsub, int vsub, int x, int y)
+{
+    OverlaySubsContext *s = ctx->priv;
+    const int src_w = area->w;
+    const int src_h = area->h;
+    const int dst_w = dst->width;
+    const int dst_h = dst->height;
+    const int sr = s->overlay_rgba_map[R];
+    const int sg = s->overlay_rgba_map[G];
+    const int sb = s->overlay_rgba_map[B];
+    const int sa = s->overlay_rgba_map[A];
+    uint32_t yuvpal[256];
+
+    for (int i = 0; i < 256; ++i) {
+        const uint8_t *rgba = (const uint8_t *)&area->pal[i];
+        uint8_t *yuva = (uint8_t *)&yuvpal[i];
+        rgb_2_yuv(rgba[sr], rgba[sg], rgba[sb], &yuva[Y], &yuva[U], &yuva[V]);
+        yuva[3] = rgba[sa];
+    }
+
+    blend_plane_8_8bits(ctx, dst, area, yuvpal, src_w, src_h, dst_w, dst_h, Y, 0,    0,    x, y, s->main_desc->comp[Y].plane, s->main_desc->comp[Y].offset, s->main_desc->comp[Y].step);
+    blend_plane_8_8bits(ctx, dst, area, yuvpal, src_w, src_h, dst_w, dst_h, U, hsub, vsub, x, y, s->main_desc->comp[U].plane, s->main_desc->comp[U].offset, s->main_desc->comp[U].step);
+    blend_plane_8_8bits(ctx, dst, area, yuvpal, src_w, src_h, dst_w, dst_h, V, hsub, vsub, x, y, s->main_desc->comp[V].plane, s->main_desc->comp[V].offset, s->main_desc->comp[V].step);
+}
+
+static int config_input_main(AVFilterLink *inlink)
+{
+    int ret;
+    AVFilterContext *ctx  = inlink->dst;
+    OverlaySubsContext *s = inlink->dst->priv;
+    const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(inlink->format);
+
+    av_image_fill_max_pixsteps(s->main_pix_step,    NULL, pix_desc);
+    ff_fill_rgba_map(s->overlay_rgba_map, AV_PIX_FMT_RGB32); // it's actually AV_PIX_FMT_PAL8);
+
+    s->hsub = pix_desc->log2_chroma_w;
+    s->vsub = pix_desc->log2_chroma_h;
+
+    s->main_desc = pix_desc;
+
+    s->main_is_packed_rgb = ff_fill_rgba_map(s->main_rgba_map, inlink->format) >= 0;
+    s->main_has_alpha = !!(pix_desc->flags & AV_PIX_FMT_FLAG_ALPHA);
+
+    /* Finish the configuration by evaluating the expressions
+       now when both inputs are configured. */
+    s->var_values[VAR_MAIN_W   ] = s->var_values[VAR_MW] = ctx->inputs[MAIN   ]->w;
+    s->var_values[VAR_MAIN_H   ] = s->var_values[VAR_MH] = ctx->inputs[MAIN   ]->h;
+    s->var_values[VAR_OVERLAY_W] = s->var_values[VAR_OW] = ctx->inputs[OVERLAY]->w;
+    s->var_values[VAR_OVERLAY_H] = s->var_values[VAR_OH] = ctx->inputs[OVERLAY]->h;
+    s->var_values[VAR_HSUB]  = 1<<pix_desc->log2_chroma_w;
+    s->var_values[VAR_VSUB]  = 1<<pix_desc->log2_chroma_h;
+    s->var_values[VAR_X]     = NAN;
+    s->var_values[VAR_Y]     = NAN;
+    s->var_values[VAR_N]     = 0;
+    s->var_values[VAR_T]     = NAN;
+    s->var_values[VAR_POS]   = NAN;
+
+    if ((ret = set_expr(&s->x_pexpr,      s->x_expr,      "x",      ctx)) < 0 ||
+        (ret = set_expr(&s->y_pexpr,      s->y_expr,      "y",      ctx)) < 0)
+        return ret;
+
+    if (s->eval_mode == EVAL_MODE_INIT) {
+        eval_expr(ctx);
+        av_log(ctx, AV_LOG_VERBOSE, "x:%f xi:%d y:%f yi:%d\n",
+               s->var_values[VAR_X], s->x,
+               s->var_values[VAR_Y], s->y);
+    }
+
+    av_log(ctx, AV_LOG_VERBOSE,
+           "main w:%d h:%d fmt:%s overlay w:%d h:%d fmt:%s\n",
+           ctx->inputs[MAIN]->w, ctx->inputs[MAIN]->h,
+           av_get_pix_fmt_name(ctx->inputs[MAIN]->format),
+           ctx->inputs[OVERLAY]->w, ctx->inputs[OVERLAY]->h,
+           av_get_pix_fmt_name(ctx->inputs[OVERLAY]->format));
+    return 0;
+}
+
+static int do_blend(FFFrameSync *fs)
+{
+    AVFilterContext *ctx = fs->parent;
+    AVFrame *mainpic, *second;
+    OverlaySubsContext *s = ctx->priv;
+    AVFilterLink *inlink = ctx->inputs[0];
+    unsigned i;
+    int ret;
+
+    ret = ff_framesync_dualinput_get_writable(fs, &mainpic, &second);
+    if (ret < 0)
+        return ret;
+    if (!second)
+        return ff_filter_frame(ctx->outputs[0], mainpic);
+
+    if (s->eval_mode == EVAL_MODE_FRAME) {
+        int64_t pos = mainpic->pkt_pos;
+
+        s->var_values[VAR_N] = (double)inlink->frame_count_out;
+        s->var_values[VAR_T] = mainpic->pts == AV_NOPTS_VALUE ?
+            NAN :(double)mainpic->pts * av_q2d(inlink->time_base);
+        s->var_values[VAR_POS] = pos == -1 ? NAN : (double)pos;
+
+        s->var_values[VAR_OVERLAY_W] = s->var_values[VAR_OW] = second->width;
+        s->var_values[VAR_OVERLAY_H] = s->var_values[VAR_OH] = second->height;
+        s->var_values[VAR_MAIN_W   ] = s->var_values[VAR_MW] = mainpic->width;
+        s->var_values[VAR_MAIN_H   ] = s->var_values[VAR_MH] = mainpic->height;
+
+        eval_expr(ctx);
+        av_log(ctx, AV_LOG_DEBUG, "n:%f t:%f pos:%f x:%f xi:%d y:%f yi:%d\n",
+               s->var_values[VAR_N], s->var_values[VAR_T], s->var_values[VAR_POS],
+               s->var_values[VAR_X], s->x,
+               s->var_values[VAR_Y], s->y);
+    }
+
+    for (i = 0; i < second->num_subtitle_areas; i++) {
+        const AVSubtitleArea *sub_area = second->subtitle_areas[i];
+
+        if (sub_area->type != AV_SUBTITLE_FMT_BITMAP) {
+            av_log(NULL, AV_LOG_WARNING, "overlay_graphicsubs: non-bitmap subtitle\n");
+            return AVERROR_INVALIDDATA;
+        }
+
+        switch (inlink->format) {
+        case AV_PIX_FMT_YUV420P:
+            blend_yuv_8_8bits(ctx, mainpic, sub_area, 1, 1, sub_area->x + s->x, sub_area->y + s->y);
+            break;
+        case AV_PIX_FMT_YUV422P:
+            blend_yuv_8_8bits(ctx, mainpic, sub_area, 1, 0, sub_area->x + s->x, sub_area->y + s->y);
+            break;
+        case AV_PIX_FMT_YUV444P:
+            blend_yuv_8_8bits(ctx, mainpic, sub_area, 0, 0, sub_area->x + s->x, sub_area->y + s->y);
+            break;
+        case AV_PIX_FMT_RGB24:
+        case AV_PIX_FMT_BGR24:
+        case AV_PIX_FMT_ARGB:
+        case AV_PIX_FMT_RGBA:
+        case AV_PIX_FMT_BGRA:
+        case AV_PIX_FMT_ABGR:
+            blend_packed_rgb(ctx, mainpic, sub_area, sub_area->x + s->x, sub_area->y + s->y, 1);
+            break;
+        default:
+            av_log(NULL, AV_LOG_ERROR, "Unsupported input pix fmt: %d\n", inlink->format);
+            return AVERROR(EINVAL);
+        }
+    }
+
+    return ff_filter_frame(ctx->outputs[0], mainpic);
+}
+
+static av_cold int overlay_graphicsubs_init(AVFilterContext *ctx)
+{
+    OverlaySubsContext *s = ctx->priv;
+
+    s->fs.on_event = do_blend;
+    return 0;
+}
+
+static int overlay_graphicsubs_activate(AVFilterContext *ctx)
+{
+    OverlaySubsContext *s = ctx->priv;
+    return ff_framesync_activate(&s->fs);
+}
+
+static int graphicsub2video_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_BITMAP, AV_SUBTITLE_FMT_NONE };
+    static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_RGB32, AV_PIX_FMT_NONE };
+    int ret;
+
+    /* set input subtitle formats */
+    formats = ff_make_format_list(subtitle_fmts);
+    if ((ret = ff_formats_ref(formats, &inlink->outcfg.formats)) < 0)
+        return ret;
+
+    /* set output video formats */
+    formats = ff_make_format_list(pix_fmts);
+    if ((ret = ff_formats_ref(formats, &outlink->incfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int graphicsub2video_config_input(AVFilterLink *inlink)
+{
+    AVFilterContext *ctx = inlink->dst;
+    OverlaySubsContext *s = ctx->priv;
+
+    if (s->w <= 0 || s->h <= 0) {
+        s->w = inlink->w;
+        s->h = inlink->h;
+    }
+    return 0;
+}
+
+static int graphicsub2video_config_output(AVFilterLink *outlink)
+{
+    const AVFilterContext *ctx  = outlink->src;
+    OverlaySubsContext *s = ctx->priv;
+    const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(outlink->format);
+
+    outlink->w = s->w;
+    outlink->h = s->h;
+
+    if (outlink->w == 0 && outlink->h == 0) {
+        outlink->w = 1;
+        outlink->h = 1;
+    }
+    outlink->sample_aspect_ratio = (AVRational){1,1};
+    outlink->time_base = ctx->inputs[0]->time_base;
+    outlink->frame_rate = ctx->inputs[0]->frame_rate;
+
+    av_image_fill_max_pixsteps(s->main_pix_step, NULL, pix_desc);
+    ff_fill_rgba_map(s->overlay_rgba_map, AV_PIX_FMT_RGB32);
+
+    s->hsub = pix_desc->log2_chroma_w;
+    s->vsub = pix_desc->log2_chroma_h;
+
+    s->main_desc = pix_desc;
+
+    s->main_is_packed_rgb = ff_fill_rgba_map(s->main_rgba_map, outlink->format) >= 0;
+    s->main_has_alpha = !!(pix_desc->flags & AV_PIX_FMT_FLAG_ALPHA);
+
+    return 0;
+}
+
+static int graphicsub2video_filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFilterLink *outlink = inlink->dst->outputs[0];
+    const AVFilterContext *ctx  = outlink->src;
+    OverlaySubsContext *s = ctx->priv;
+    AVFrame *out;
+    const unsigned num_rects = frame->num_subtitle_areas;
+    unsigned int i;
+    int ret;
+
+    if (s->use_caching && s->cache_frame && frame->repeat_sub
+        && s->cache_frame->subtitle_timing.start_pts == frame->subtitle_timing.start_pts) {
+        out = av_frame_clone(s->cache_frame);
+        if (!out)
+            return AVERROR(ENOMEM);
+
+        ret = av_frame_copy_props(out, frame);
+        if (ret < 0)
+            return ret;
+
+        av_log(inlink->dst, AV_LOG_DEBUG, "graphicsub2video CACHED - size %dx%d  pts: %"PRId64"  areas: %d\n", frame->width, frame->height, frame->subtitle_timing.start_pts, frame->num_subtitle_areas);
+        av_frame_free(&frame);
+        return ff_filter_frame(outlink, out);
+    }
+
+
+    out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
+    if (!out) {
+        av_frame_free(&frame);
+        return AVERROR(ENOMEM);
+    }
+
+    memset(out->data[0], 0, (size_t)out->linesize[0] * out->height);
+
+    out->pts = out->pkt_dts = out->best_effort_timestamp = frame->pts;
+    out->coded_picture_number = out->display_picture_number = s->pic_counter++;
+
+    for (i = 0; i < num_rects; i++) {
+        const AVSubtitleArea  *sub_rect = frame->subtitle_areas[i];
+
+        if (sub_rect->type != AV_SUBTITLE_FMT_BITMAP) {
+            av_log(NULL, AV_LOG_WARNING, "graphicsub2video: non-bitmap subtitle\n");
+            av_frame_free(&frame);
+            return AVERROR_INVALIDDATA;
+        }
+
+        blend_packed_rgb(inlink->dst, out, sub_rect, sub_rect->x, sub_rect->y, 1);
+    }
+
+    av_log(inlink->dst, AV_LOG_DEBUG, "graphicsub2video output - size %dx%d  pts: %"PRId64"  areas: %d\n", out->width, out->height, out->pts, frame->num_subtitle_areas);
+
+    if (s->use_caching) {
+        av_frame_free(&s->cache_frame);
+        s->cache_frame = av_frame_clone(out);
+    }
+
+    av_frame_free(&frame);
+    return ff_filter_frame(outlink, out);
+}
+
+#define OFFSET(x) offsetof(OverlaySubsContext, x)
+#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption overlaygraphicsubs_options[] = {
+    { "x", "set the x expression", OFFSET(x_expr), AV_OPT_TYPE_STRING, {.str = "0"}, 0, 0, .flags = FLAGS },
+    { "y", "set the y expression", OFFSET(y_expr), AV_OPT_TYPE_STRING, {.str = "0"}, 0, 0, .flags = FLAGS },
+    { "eof_action", "Action to take when encountering EOF from secondary input ",
+        OFFSET(fs.opt_eof_action), AV_OPT_TYPE_INT, { .i64 = EOF_ACTION_REPEAT },
+        EOF_ACTION_REPEAT, EOF_ACTION_PASS, .flags = FLAGS, "eof_action" },
+        { "repeat", "Repeat the previous frame.",   0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_REPEAT }, .flags = FLAGS, "eof_action" },
+        { "endall", "End both streams.",            0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_ENDALL }, .flags = FLAGS, "eof_action" },
+        { "pass",   "Pass through the main input.", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_PASS },   .flags = FLAGS, "eof_action" },
+    { "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, {.i64 = EVAL_MODE_FRAME}, 0, EVAL_MODE_NB-1, FLAGS, "eval" },
+         { "init",  "eval expressions once during initialization", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_INIT},  .flags = FLAGS, .unit = "eval" },
+         { "frame", "eval expressions per-frame",                  0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_FRAME}, .flags = FLAGS, .unit = "eval" },
+    { "shortest", "force termination when the shortest input terminates", OFFSET(fs.opt_shortest), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, .flags = FLAGS },
+    { "repeatlast", "repeat overlay of the last overlay frame", OFFSET(fs.opt_repeatlast), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, .flags = FLAGS },
+    { NULL }
+};
+
+static const AVOption graphicsub2video_options[] = {
+    { "size",        "set output frame size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, .flags = FLAGS },
+    { "s",           "set output frame size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, .flags = FLAGS },
+    { "use_caching", "Cache output frames",   OFFSET(use_caching), AV_OPT_TYPE_BOOL, { .i64 = 1}, 0, 1, .flags = FLAGS },
+    { NULL }
+};
+
+FRAMESYNC_DEFINE_CLASS(overlaygraphicsubs, OverlaySubsContext, fs);
+
+static const AVFilterPad overlaygraphicsubs_inputs[] = {
+    {
+        .name         = "main",
+        .type         = AVMEDIA_TYPE_VIDEO,
+        .config_props = config_input_main,
+        .flags        = AVFILTERPAD_FLAG_NEEDS_WRITABLE,
+    },
+    {
+        .name         = "overlay",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+    },
+};
+
+static const AVFilterPad overlaygraphicsubs_outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_VIDEO,
+        .config_props  = config_output,
+    },
+};
+
+const AVFilter ff_vf_overlaygraphicsubs = {
+    .name          = "overlaygraphicsubs",
+    .description   = NULL_IF_CONFIG_SMALL("Overlay graphical subtitles on top of the input."),
+    .preinit       = overlaygraphicsubs_framesync_preinit,
+    .init          = overlay_graphicsubs_init,
+    .uninit        = overlay_graphicsubs_uninit,
+    .priv_size     = sizeof(OverlaySubsContext),
+    .priv_class    = &overlaygraphicsubs_class,
+    .activate      = overlay_graphicsubs_activate,
+    FILTER_INPUTS(overlaygraphicsubs_inputs),
+    FILTER_OUTPUTS(overlaygraphicsubs_outputs),
+    FILTER_QUERY_FUNC(overlay_graphicsubs_query_formats),
+};
+
+AVFILTER_DEFINE_CLASS(graphicsub2video);
+
+static const AVFilterPad graphicsub2video_inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .filter_frame = graphicsub2video_filter_frame,
+        .config_props = graphicsub2video_config_input,
+    },
+};
+
+static const AVFilterPad graphicsub2video_outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_VIDEO,
+        .config_props  = graphicsub2video_config_output,
+    },
+};
+
+const AVFilter ff_svf_graphicsub2video = {
+    .name          = "graphicsub2video",
+    .description   = NULL_IF_CONFIG_SMALL("Convert graphical subtitles to video"),
+    .priv_size     = sizeof(OverlaySubsContext),
+    .priv_class    = &graphicsub2video_class,
+    FILTER_INPUTS(graphicsub2video_inputs),
+    FILTER_OUTPUTS(graphicsub2video_outputs),
+    FILTER_QUERY_FUNC(graphicsub2video_query_formats),
+};
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v2 13/26] avfilter/overlaytextsubs: Add overlaytextsubs and textsubs2video filters
  2022-01-20  2:48 ` [FFmpeg-devel] [PATCH v2 00/26] Subtitle Filtering 2022 ffmpegagent
                     ` (11 preceding siblings ...)
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 12/26] avfilter/overlaygraphicsubs: Add overlaygraphicsubs and graphicsub2video filters ffmpegagent
@ 2022-01-20  2:48   ` ffmpegagent
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 14/26] avfilter/textmod: Add textmod, censor and show_speaker filters ffmpegagent
                     ` (13 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  2:48 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

- overlaytextsubs {VS -> V)
  Overlay text subtitles onto a video stream.

- textsubs2video {S -> V)
  Converts text subtitles to video frames

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 configure                        |   2 +
 doc/filters.texi                 | 113 ++++++
 libavfilter/Makefile             |   2 +
 libavfilter/allfilters.c         |   4 +-
 libavfilter/vf_overlaytextsubs.c | 678 +++++++++++++++++++++++++++++++
 5 files changed, 798 insertions(+), 1 deletion(-)
 create mode 100644 libavfilter/vf_overlaytextsubs.c

diff --git a/configure b/configure
index 7c2931bd50..340a198fa6 100755
--- a/configure
+++ b/configure
@@ -3691,6 +3691,7 @@ overlay_opencl_filter_deps="opencl"
 overlay_qsv_filter_deps="libmfx"
 overlay_qsv_filter_select="qsvvpp"
 overlay_vulkan_filter_deps="vulkan spirv_compiler"
+overlaytextsubs_filter_deps="avcodec libass"
 owdenoise_filter_deps="gpl"
 pad_opencl_filter_deps="opencl"
 pan_filter_deps="swresample"
@@ -3735,6 +3736,7 @@ superequalizer_filter_deps="avcodec"
 superequalizer_filter_select="rdft"
 surround_filter_deps="avcodec"
 surround_filter_select="rdft"
+textsub2video_filter_deps="avcodec libass"
 tinterlace_filter_deps="gpl"
 tinterlace_merge_test_deps="tinterlace_filter"
 tinterlace_pad_test_deps="tinterlace_filter"
diff --git a/doc/filters.texi b/doc/filters.texi
index 4fc4a57dbb..51771f88e4 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -25829,6 +25829,119 @@ Overlay PGS subtitles
 ffmpeg -i "https://streams.videolan.org/samples/sub/PGS/Girl_With_The_Dragon_Tattoo_2%3A23%3A56.mkv" -filter_complex "[0:0][0:1]overlaygraphicsubs" output.mp4
 @end example
 @end itemize
+
+@section overlaytextsubs
+
+Overlay text subtitles onto a video stream.
+
+This filter supersedes the classic @ref{subtitles} filter opposed to which it does no longer require to open and access the source stream separately, which is often causing problems or doesn't even work for non-local or slow sources.
+
+Inputs:
+@itemize
+@item 0: Video [YUV420P, YUV422P, YUV444P, ARGB, RGBA, ABGR, BGRA, RGB24, BGR24]
+@item 1: Subtitles [TEXT]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Video (same as input)
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+
+@item alpha
+Process alpha channel, by default alpha channel is untouched.
+
+@item fonts_dir
+Set a directory path containing fonts that can be used by the filter.
+These fonts will be used in addition to whatever the font provider uses.
+
+@item default_font_path
+Path to a font file to be used as the default font.
+
+@item font_size
+Set the default font size.
+
+@item fontconfig_file
+Path to ASS fontconfig configuration file.
+
+@item force_style
+Override default style or script info parameters of the subtitles. It accepts a
+string containing ASS style format @code{KEY=VALUE} couples separated by ",".
+
+@item margin
+Set the rendering margin in pixels.
+
+@item render_latest_only
+For rendering, alway use the latest event only, which is covering the given point in time
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Overlay ASS subtitles with animations:
+@example
+ffmpeg -i "http://streams.videolan.org/samples/sub/SSA/subtitle_testing_complex.mkv" -filter_complex "[0:v]overlaytextsubs" -map 0 -y out.mkv
+@end example
+@end itemize
+
+@section textsub2video
+
+Converts text subtitles to video frames.
+
+For overlaying text subtitles onto video frames it is recommended to use the overlay_textsubs filter.
+The textsub2video is useful for for creating transparent text-frames when overlay is done via hw acceleration
+
+Inputs:
+@itemize
+@item 0: Subtitles [TEXT]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Video [RGB32]
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+
+@item rate, r
+Set the framerate for updating overlay frames.
+Normally, overlay frames will only be updated each time when the subtitles to display are changing.
+In cases where subtitles include advanced features (like animation), this parameter determines the frequency by which the overlay frames should be updated.
+
+@item size, s
+Set the output frame size.
+Allows to override the size of output video frames.
+
+@item fonts_dir
+Set a directory path containing fonts that can be used by the filter.
+These fonts will be used in addition to whatever the font provider uses.
+
+@item default_font_path
+Path to a font file to be used as the default font.
+
+@item font_size
+Set the default font size.
+
+@item fontconfig_file
+Path to ASS fontconfig configuration file.
+
+@item force_style
+Override default style or script info parameters of the subtitles. It accepts a
+string containing ASS style format @code{KEY=VALUE} couples separated by ",".
+
+@item margin
+Set the rendering margin in pixels.
+
+@item render_latest_only
+For rendering, alway use the latest event only, which is covering the given point in time.
+@end table
+
 @c man end SUBTITLE FILTERS
 
 @chapter Multimedia Filters
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index a372effc12..48c9182c55 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -380,6 +380,7 @@ OBJS-$(CONFIG_OVERLAY_OPENCL_FILTER)         += vf_overlay_opencl.o opencl.o \
 OBJS-$(CONFIG_OVERLAY_QSV_FILTER)            += vf_overlay_qsv.o framesync.o
 OBJS-$(CONFIG_OVERLAY_VULKAN_FILTER)         += vf_overlay_vulkan.o vulkan.o vulkan_filter.o
 OBJS-$(CONFIG_OVERLAYGRAPHICSUBS_FILTER)     += vf_overlaygraphicsubs.o framesync.o
+OBJS-$(CONFIG_OVERLAYTEXTSUBS_FILTER)        += vf_overlaytextsubs.o
 OBJS-$(CONFIG_OWDENOISE_FILTER)              += vf_owdenoise.o
 OBJS-$(CONFIG_PAD_FILTER)                    += vf_pad.o
 OBJS-$(CONFIG_PAD_OPENCL_FILTER)             += vf_pad_opencl.o opencl.o opencl/pad.o
@@ -469,6 +470,7 @@ OBJS-$(CONFIG_SWAPRECT_FILTER)               += vf_swaprect.o
 OBJS-$(CONFIG_SWAPUV_FILTER)                 += vf_swapuv.o
 OBJS-$(CONFIG_TBLEND_FILTER)                 += vf_blend.o framesync.o
 OBJS-$(CONFIG_TELECINE_FILTER)               += vf_telecine.o
+OBJS-$(CONFIG_TEXTSUB2VIDEO_FILTER)          += vf_overlaytextsubs.o
 OBJS-$(CONFIG_THISTOGRAM_FILTER)             += vf_histogram.o
 OBJS-$(CONFIG_THRESHOLD_FILTER)              += vf_threshold.o framesync.o
 OBJS-$(CONFIG_THUMBNAIL_FILTER)              += vf_thumbnail.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index eb4e30a270..9ba89db128 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -358,9 +358,10 @@ extern const AVFilter ff_vf_oscilloscope;
 extern const AVFilter ff_vf_overlay;
 extern const AVFilter ff_vf_overlay_opencl;
 extern const AVFilter ff_vf_overlay_qsv;
-extern const AVFilter ff_vf_overlaygraphicsubs;
 extern const AVFilter ff_vf_overlay_vulkan;
 extern const AVFilter ff_vf_overlay_cuda;
+extern const AVFilter ff_vf_overlaygraphicsubs;
+extern const AVFilter ff_vf_overlaytextsubs;
 extern const AVFilter ff_vf_owdenoise;
 extern const AVFilter ff_vf_pad;
 extern const AVFilter ff_vf_pad_opencl;
@@ -547,6 +548,7 @@ extern const AVFilter ff_avf_showwaves;
 extern const AVFilter ff_avf_showwavespic;
 extern const AVFilter ff_vaf_spectrumsynth;
 extern const AVFilter ff_svf_graphicsub2video;
+extern const AVFilter ff_svf_textsub2video;
 
 /* multimedia sources */
 extern const AVFilter ff_avsrc_amovie;
diff --git a/libavfilter/vf_overlaytextsubs.c b/libavfilter/vf_overlaytextsubs.c
new file mode 100644
index 0000000000..173e7149bb
--- /dev/null
+++ b/libavfilter/vf_overlaytextsubs.c
@@ -0,0 +1,678 @@
+/*
+ * 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
+ * overlay text subtitles on top of a video frame
+ */
+
+#include <ass/ass.h>
+#include "libavutil/ass_internal.h"
+#include "libavutil/thread.h"
+
+#include "drawutils.h"
+#include "filters.h"
+
+#include "libavutil/opt.h"
+#include "libavutil/pixdesc.h"
+
+typedef struct TextSubsContext {
+    const AVClass *class;
+    AVMutex mutex;
+    int is_mutex_initialized;
+
+    ASS_Library   *library;
+    ASS_Renderer  *renderer;
+    ASS_Track     *track;
+
+    char *default_font_path;
+    char *fonts_dir;
+    char *fc_file;
+    double font_size;
+    char *force_style;
+    char *language;
+    int margin;
+    int render_latest_only;
+
+    int alpha;
+    FFDrawContext draw;
+
+    int got_header;
+    int out_w, out_h;
+    AVRational frame_rate;
+    AVFrame *last_frame;
+    int need_frame;
+    int eof;
+} TextSubsContext;
+
+/* libass supports a log level ranging from 0 to 7 */
+static const int ass_libavfilter_log_level_map[] = {
+    AV_LOG_QUIET,               /* 0 */
+    AV_LOG_PANIC,               /* 1 */
+    AV_LOG_FATAL,               /* 2 */
+    AV_LOG_ERROR,               /* 3 */
+    AV_LOG_WARNING,             /* 4 */
+    AV_LOG_INFO,                /* 5 */
+    AV_LOG_VERBOSE,             /* 6 */
+    AV_LOG_DEBUG,               /* 7 */
+};
+
+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 void uninit(AVFilterContext *ctx)
+{
+    TextSubsContext *s = ctx->priv;
+
+    if (s->track)
+        ass_free_track(s->track);
+    if (s->renderer)
+        ass_renderer_done(s->renderer);
+    if (s->library)
+        ass_library_done(s->library);
+
+    s->track = NULL;
+    s->renderer = NULL;
+    s->library = NULL;
+
+    if (s->is_mutex_initialized) {
+        ff_mutex_destroy(&s->mutex);
+        s->is_mutex_initialized = 0;
+    }
+
+    av_frame_free(&s->last_frame);
+}
+
+static int overlay_textsubs_query_formats(AVFilterContext *ctx)
+{
+    AVFilterFormats *formats;
+    AVFilterLink *inlink0 = ctx->inputs[0];
+    AVFilterLink *inlink1 = ctx->inputs[1];
+    AVFilterLink *outlink = ctx->outputs[0];
+    static const enum AVSubtitleType subtitle_fmts[] = { AV_SUBTITLE_FMT_ASS, AV_SUBTITLE_FMT_NONE };
+    int ret;
+
+    /* set input0 and output0 video formats */
+    formats = ff_draw_supported_pixel_formats(0);
+    if ((ret = ff_formats_ref(formats, &inlink0->outcfg.formats)) < 0)
+        return ret;
+    if ((ret = ff_formats_ref(formats, &outlink->incfg.formats)) < 0)
+        return ret;
+
+    /* set input1 subtitle formats */
+    formats = ff_make_format_list(subtitle_fmts);
+    if ((ret = ff_formats_ref(formats, &inlink1->outcfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+
+    outlink->w = ctx->inputs[0]->w;
+    outlink->h = ctx->inputs[0]->h;
+    outlink->time_base = ctx->inputs[0]->time_base;
+    outlink->frame_rate = ctx->inputs[0]->frame_rate;
+
+    return 0;
+}
+
+static int config_input_main(AVFilterLink *inlink)
+{
+    AVFilterContext *ctx  = inlink->dst;
+    TextSubsContext *s = inlink->dst->priv;
+    int ret;
+
+    ret = ff_draw_init(&s->draw, inlink->format, s->alpha ? FF_DRAW_PROCESS_ALPHA : 0);
+    if (ret < 0) {
+        av_log(ctx, AV_LOG_ERROR, "Could not initialize ff_draw.\n");
+        return ret;
+    }
+
+    ass_set_frame_size  (s->renderer, inlink->w, inlink->h);
+    ass_set_pixel_aspect(s->renderer, av_q2d(inlink->sample_aspect_ratio));
+
+    av_log(ctx, AV_LOG_VERBOSE, "Subtitle screen: %dx%d\n\n\n\n", inlink->w, inlink->h);
+
+    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(TextSubsContext *s, 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(&s->draw, &color, rgba_color);
+        ff_blend_mask(&s->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 void process_header(AVFilterContext *link, AVFrame *frame)
+{
+    TextSubsContext *s = link->priv;
+    ASS_Track *track = s->track;
+    ASS_Style *style;
+    int sid = 0;
+
+    if (!track)
+        return;
+
+    if (frame && frame->subtitle_header) {
+        char *subtitle_header = (char *)frame->subtitle_header->data;
+        ass_process_codec_private(s->track, subtitle_header, strlen(subtitle_header));
+    }
+    else {
+        char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+        if (!subtitle_header)
+            return;
+
+        ass_process_codec_private(s->track, subtitle_header, strlen(subtitle_header));
+        av_free(subtitle_header);
+    }
+
+    if (s->language)
+        s->track->Language = av_strdup(s->language);
+
+    if (!s->track->event_format) {
+        s->track->event_format = av_strdup("ReadOrder, Layer, Style, Name, MarginL, MarginR, MarginV, Effect, Text");
+    }
+
+    if (s->track->n_styles == 0) {
+        sid = ass_alloc_style(track);
+        style = &s->track->styles[sid];
+        style->Name             = av_strdup("Default");
+        style->PrimaryColour    = 0xffffff00;
+        style->SecondaryColour  = 0x00ffff00;
+        style->OutlineColour    = 0x00000000;
+        style->BackColour       = 0x00000080;
+        style->Bold             = 200;
+        style->ScaleX           = 1.0;
+        style->ScaleY           = 1.0;
+        style->Spacing          = 0;
+        style->BorderStyle      = 1;
+        style->Outline          = 2;
+        style->Shadow           = 3;
+        style->Alignment        = 2;
+    }
+    else
+        style = &s->track->styles[sid];
+
+    style->FontSize         = s->font_size;
+    style->MarginL = style->MarginR = style->MarginV = s->margin;
+
+    track->default_style = sid;
+
+    s->got_header = 1;
+}
+
+static int filter_video_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFilterContext *ctx = inlink->dst;
+    TextSubsContext *s = ctx->priv;
+    int detect_change = 0;
+    ASS_Image *image;
+
+
+    int64_t time_ms  = (int64_t)((double)frame->pts * av_q2d(inlink->time_base) * 1000);
+    int64_t time_ms1 = (int64_t)((double)ctx->inputs[1]->current_pts * av_q2d(ctx->inputs[1]->time_base) * 1000);
+
+    if (time_ms1 < time_ms + 1000)
+        ff_request_frame(ctx->inputs[1]);
+
+    av_log(ctx, AV_LOG_DEBUG, "filter_video_frame - video: %"PRId64"ms  sub: %"PRId64"ms  rel %d\n", time_ms, time_ms1, (time_ms1 < time_ms));
+
+    ff_mutex_lock(&s->mutex);
+    image = ass_render_frame(s->renderer, s->track, time_ms, &detect_change);
+    ff_mutex_unlock(&s->mutex);
+
+    if (detect_change)
+        av_log(ctx, AV_LOG_DEBUG, "Change happened at time ms:%"PRId64"\n", time_ms);
+
+    overlay_ass_image(s, frame, image);
+
+    return ff_filter_frame(ctx->outputs[0], frame);
+}
+
+static int filter_subtitle_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFilterContext *ctx = inlink->dst;
+    TextSubsContext *s = ctx->priv;
+    const int64_t start_time = av_rescale_q(frame->subtitle_timing.start_pts, AV_TIME_BASE_Q, av_make_q(1, 1000));
+    const int64_t duration   = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, av_make_q(1, 1000));
+    const int64_t frame_time = (int64_t)((double)frame->pts * av_q2d(inlink->time_base) * 1000);
+
+    // Postpone header processing until we receive a frame with content
+    if (!s->got_header && frame->num_subtitle_areas > 0)
+        process_header(ctx, frame);
+
+    av_log(ctx, AV_LOG_DEBUG, "filter_subtitle_frame dur: %"PRId64"ms frame: %"PRId64"ms  sub: %"PRId64"ms  repeat_sub %d\n", duration, frame_time, start_time, frame->repeat_sub);
+
+    if (frame->repeat_sub)
+        goto exit;
+
+    ff_mutex_lock(&s->mutex);
+
+    if (s->render_latest_only && s->track->n_events > 0) {
+        const int64_t previous_start_time = s->track->events[s->track->n_events - 1].Start;
+        const int64_t diff = start_time - previous_start_time;
+        for (int i = s->track->n_events - 1; i >= 0; i--) {
+            if (previous_start_time != s->track->events[i].Start)
+                break;
+
+            if (s->track->events[i].Duration > diff)
+                s->track->events[i].Duration = diff;
+        }
+    }
+
+    for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+        char *ass_line = frame->subtitle_areas[i]->ass;
+        if (!ass_line)
+            continue;
+
+        ass_process_chunk(s->track, ass_line, strlen(ass_line), start_time, duration);
+    }
+
+    ff_mutex_unlock(&s->mutex);
+
+exit:
+    av_frame_free(&frame);
+    return 0;
+}
+
+static av_cold int init(AVFilterContext *ctx)
+{
+    int ret;
+    TextSubsContext *s = ctx->priv;
+
+    s->library = ass_library_init();
+
+    if (!s->library) {
+        av_log(ctx, AV_LOG_ERROR, "Could not initialize libass.\n");
+        return AVERROR(EINVAL);
+    }
+
+    ass_set_message_cb(s->library, ass_log, ctx);
+
+    /* Initialize fonts */
+    if (s->fonts_dir)
+        ass_set_fonts_dir(s->library, s->fonts_dir);
+
+    ass_set_extract_fonts(s->library, 1);
+
+    s->renderer = ass_renderer_init(s->library);
+    if (!s->renderer) {
+        av_log(ctx, AV_LOG_ERROR, "Could not initialize libass renderer.\n");
+        return AVERROR(EINVAL);
+    }
+
+    s->track = ass_new_track(s->library);
+    if (!s->track) {
+        av_log(ctx, AV_LOG_ERROR, "ass_new_track() failed!\n");
+        return AVERROR(EINVAL);
+    }
+
+    ass_set_check_readorder(s->track, 0);
+
+    ass_set_fonts(s->renderer, s->default_font_path, NULL, 1, s->fc_file, 1);
+
+    if (s->force_style) {
+        char **list = NULL;
+        char *temp = NULL;
+        char *ptr = av_strtok(s->force_style, ",", &temp);
+        int i = 0;
+        while (ptr) {
+            av_dynarray_add(&list, &i, ptr);
+            if (!list) {
+                return AVERROR(ENOMEM);
+            }
+            ptr = av_strtok(NULL, ",", &temp);
+        }
+        av_dynarray_add(&list, &i, NULL);
+        if (!list) {
+            return AVERROR(ENOMEM);
+        }
+        ass_set_style_overrides(s->library, list);
+        av_free(list);
+    }
+
+    ret = ff_mutex_init(&s->mutex, NULL);
+    if (ret) {
+        av_log(ctx, AV_LOG_ERROR, "mutex initialiuzation failed! Error code: %d\n", ret);
+        return AVERROR(EINVAL);
+    }
+
+    s->is_mutex_initialized = 1;
+
+    return ret;
+}
+
+static int textsub2video_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 input0 subtitle format */
+    formats = ff_make_format_list(subtitle_fmts);
+    if ((ret = ff_formats_ref(formats, &inlink->outcfg.formats)) < 0)
+        return ret;
+
+    /* set output0 video format */
+    formats = ff_draw_supported_pixel_formats(AV_PIX_FMT_FLAG_ALPHA);
+    if ((ret = ff_formats_ref(formats, &outlink->incfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int textsub2video_config_input(AVFilterLink *inlink)
+{
+    AVFilterContext *ctx = inlink->dst;
+    TextSubsContext *s = ctx->priv;
+
+    if (s->out_w <= 0 || s->out_h <= 0) {
+        s->out_w = inlink->w;
+        s->out_h = inlink->h;
+    }
+
+    return 0;
+}
+
+static int textsub2video_config_output(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    TextSubsContext *s = ctx->priv;
+    int ret;
+
+    ret = ff_draw_init(&s->draw, outlink->format, FF_DRAW_PROCESS_ALPHA);
+    if (ret < 0) {
+        av_log(ctx, AV_LOG_ERROR, "Could not initialize ff_draw.\n");
+        return ret;
+    }
+
+    if (s->out_w <= 0 || s->out_h <= 0) {
+        av_log(ctx, AV_LOG_ERROR, "No output image size set.\n");
+        return AVERROR(EINVAL);
+    }
+
+    ass_set_frame_size  (s->renderer, s->out_w, s->out_h);
+
+    outlink->w = s->out_w;
+    outlink->h = s->out_h;
+    outlink->sample_aspect_ratio = (AVRational){1,1};
+    outlink->frame_rate = s->frame_rate;
+
+    return 0;
+}
+
+static int textsub2video_request_frame(AVFilterLink *outlink)
+{
+    TextSubsContext *s = outlink->src->priv;
+    AVFilterLink *inlink = outlink->src->inputs[0];
+    int64_t last_pts = outlink->current_pts;
+    int64_t next_pts, time_ms;
+    int i, detect_change = 0, status;
+    AVFrame *out;
+    ASS_Image *image;
+
+    status = ff_outlink_get_status(inlink);
+    if (status == AVERROR_EOF)
+        return AVERROR_EOF;
+
+    if (s->eof)
+        return AVERROR_EOF;
+
+    if (inlink->current_pts == AV_NOPTS_VALUE) { // || outlink->current_pts > inlink->current_pts) {
+        int ret = ff_request_frame(inlink);
+        if (ret == AVERROR_EOF) {
+            s->eof = 1;
+        }
+
+        if (ret != 0)
+            av_log(outlink->src, AV_LOG_DEBUG, "ff_request_frame returned: %d\n", ret);
+
+        s->need_frame = 1;
+        return 0;
+    }
+
+    if (last_pts == AV_NOPTS_VALUE)
+        next_pts = last_pts = inlink->current_pts * av_q2d(inlink->time_base) / av_q2d(outlink->time_base);
+    else
+        next_pts = last_pts + (int64_t)(1.0 / av_q2d(outlink->frame_rate) / av_q2d(outlink->time_base));
+
+    time_ms = (int64_t)((double)next_pts * av_q2d(outlink->time_base) * 1000);
+
+    ff_mutex_lock(&s->mutex);
+    image = ass_render_frame(s->renderer, s->track, time_ms, &detect_change);
+    ff_mutex_unlock(&s->mutex);
+
+    if (detect_change)
+        av_log(outlink->src, AV_LOG_VERBOSE, "Change happened at time ms:%"PRId64" pts:%"PRId64"\n", time_ms, next_pts);
+    else if (s->last_frame) {
+        out = av_frame_clone(s->last_frame);
+        if (!out)
+            return AVERROR(ENOMEM);
+
+        out->pts = out->pkt_dts = out->best_effort_timestamp = next_pts;
+        return ff_filter_frame(outlink, out);
+    }
+
+    out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
+    if (!out)
+        return AVERROR(ENOMEM);
+
+    for (i = 0; i < AV_NUM_DATA_POINTERS; i++) {
+        if (out->buf[i] && i != 1)
+            memset(out->buf[i]->data, 0, out->buf[i]->size);
+    }
+
+    out->pts = out->pkt_dts = out->best_effort_timestamp = next_pts;
+
+    if (image)
+        overlay_ass_image(s, out, image);
+
+    av_frame_free(&s->last_frame);
+
+    s->last_frame = av_frame_clone(out);
+
+    return ff_filter_frame(outlink, out);
+}
+
+static int textsub2video_filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFilterContext *ctx = inlink->dst;
+    TextSubsContext *s = ctx->priv;
+    const int64_t start_time = av_rescale_q(frame->subtitle_timing.start_pts, AV_TIME_BASE_Q, av_make_q(1, 1000));
+    const int64_t duration   = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, av_make_q(1, 1000));
+
+    av_log(ctx, AV_LOG_VERBOSE, "textsub2video_filter_frame num_subtitle_rects: %d, start_time_ms: %"PRId64"\n", frame->num_subtitle_areas, start_time);
+
+    if (!s->got_header && frame->num_subtitle_areas > 0)
+        process_header(ctx, frame);
+
+    if (frame->repeat_sub)
+        goto exit;
+
+    ff_mutex_lock(&s->mutex);
+
+    if (s->render_latest_only && s->track->n_events > 0) {
+        const int64_t previous_start_time = s->track->events[s->track->n_events - 1].Start;
+        const int64_t diff = start_time - previous_start_time;
+        for (int i = s->track->n_events - 1; i >= 0; i--) {
+            if (previous_start_time != s->track->events[i].Start)
+                break;
+
+            if (s->track->events[i].Duration > diff)
+                s->track->events[i].Duration = diff;
+
+        }
+    }
+
+    for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+        char *ass_line = frame->subtitle_areas[i]->ass;
+        if (!ass_line)
+            continue;
+
+        ass_process_chunk(s->track, ass_line, strlen(ass_line), start_time, duration);
+    }
+
+
+    ff_mutex_unlock(&s->mutex);
+
+exit:
+    av_frame_free(&frame);
+
+    if (s->need_frame) {
+        s->need_frame = 0;
+        return textsub2video_request_frame(ctx->outputs[0]);
+    }
+
+    return 0;
+}
+
+#define OFFSET(x) offsetof(TextSubsContext, x)
+#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption overlaytextsubs_options[] = {
+    {"alpha",              "enable processing of alpha channel", OFFSET(alpha),              AV_OPT_TYPE_BOOL,   {.i64 = 0   }, 0,         1,        .flags =  FLAGS},
+    {"font_size",          "default font size",                  OFFSET(font_size),          AV_OPT_TYPE_DOUBLE, {.dbl = 18.0}, 0.0,       100.0,    .flags =  FLAGS},
+    {"force_style",        "force subtitle style",               OFFSET(force_style),        AV_OPT_TYPE_STRING, {.str = NULL}, 0,         0,        .flags =  FLAGS},
+    {"margin",             "default margin",                     OFFSET(margin),             AV_OPT_TYPE_INT,    {.i64 = 20  }, 0,         INT_MAX,  .flags =  FLAGS},
+    {"default_font_path",  "path to default font",               OFFSET(default_font_path),  AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"fonts_dir",          "directory to scan for fonts",        OFFSET(fonts_dir),          AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"fontsdir",           "directory to scan for fonts",        OFFSET(fonts_dir),          AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"fontconfig_file",    "fontconfig file to load",            OFFSET(fc_file),            AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"language",           "default language",                   OFFSET(language),           AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"render_latest_only", "newest sub event for each time",     OFFSET(render_latest_only), AV_OPT_TYPE_BOOL,   {.i64 = 0   }, 0,         1,        .flags =  FLAGS},
+    { .name = NULL }
+};
+
+static const AVOption textsub2video_options[] = {
+    {"rate",               "set frame rate",                   OFFSET(frame_rate),         AV_OPT_TYPE_VIDEO_RATE, {.str="8"},   0,         INT_MAX,   .flags =  FLAGS},
+    {"r",                  "set frame rate",                   OFFSET(frame_rate),         AV_OPT_TYPE_VIDEO_RATE, {.str="8"},   0,         INT_MAX,   .flags =  FLAGS},
+    {"size",               "set video size",                   OFFSET(out_w),              AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0,         0,        .flags =  FLAGS},
+    {"s",                  "set video size",                   OFFSET(out_w),              AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0,         0,        .flags =  FLAGS},
+    {"font_size",          "default font size",                OFFSET(font_size),          AV_OPT_TYPE_DOUBLE,     {.dbl = 18.0}, 0.0,       100.0,    .flags =  FLAGS},
+    {"force_style",        "force subtitle style",             OFFSET(force_style),        AV_OPT_TYPE_STRING,     {.str = NULL}, 0,         0,        .flags =  FLAGS},
+    {"margin",             "default margin",                   OFFSET(margin),             AV_OPT_TYPE_INT,        {.i64 = 20  }, 0,         INT_MAX,  .flags =  FLAGS},
+    {"default_font_path",  "path to default font",             OFFSET(default_font_path),  AV_OPT_TYPE_STRING,     {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"fonts_dir",          "directory to scan for fonts",      OFFSET(fonts_dir),          AV_OPT_TYPE_STRING,     {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"fontsdir",           "directory to scan for fonts",      OFFSET(fonts_dir),          AV_OPT_TYPE_STRING,     {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"fontconfig_file",    "fontconfig file to load",          OFFSET(fc_file),            AV_OPT_TYPE_STRING,     {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"language",           "default language",                 OFFSET(language),           AV_OPT_TYPE_STRING,     {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"render_latest_only", "newest sub event for each time",   OFFSET(render_latest_only), AV_OPT_TYPE_BOOL,       {.i64 = 0   }, 0,         1,        .flags =  FLAGS},
+    { .name = NULL }
+};
+
+#if CONFIG_OVERLAYTEXTSUBS_FILTER
+
+AVFILTER_DEFINE_CLASS(overlaytextsubs);
+
+static const AVFilterPad overlaytextsubs_inputs[] = {
+    {
+        .name         = "main",
+        .type         = AVMEDIA_TYPE_VIDEO,
+        .config_props = config_input_main,
+        .flags        = AVFILTERPAD_FLAG_NEEDS_WRITABLE,
+        .filter_frame = filter_video_frame,
+    },
+    {
+        .name         = "overlay",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .filter_frame = filter_subtitle_frame,
+    },
+};
+
+static const AVFilterPad overlaytextsubs_outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_VIDEO,
+        .config_props  = config_output,
+    },
+};
+
+const AVFilter ff_vf_overlaytextsubs = {
+    .name          = "overlaytextsubs",
+    .description   = NULL_IF_CONFIG_SMALL("Overlay textual subtitles on top of the input."),
+    .init          = init,
+    .uninit        = uninit,
+    .priv_size     = sizeof(TextSubsContext),
+    .priv_class    = &overlaytextsubs_class,
+    FILTER_INPUTS(overlaytextsubs_inputs),
+    FILTER_OUTPUTS(overlaytextsubs_outputs),
+    FILTER_QUERY_FUNC(overlay_textsubs_query_formats),
+};
+#endif
+
+#if CONFIG_TEXTSUB2VIDEO_FILTER
+
+AVFILTER_DEFINE_CLASS(textsub2video);
+
+static const AVFilterPad textsub2video_inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .config_props = textsub2video_config_input,
+        .filter_frame = textsub2video_filter_frame,
+    },
+};
+
+static const AVFilterPad textsub2video_outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_VIDEO,
+        .config_props  = textsub2video_config_output,
+        .request_frame = textsub2video_request_frame,
+    },
+};
+
+const AVFilter ff_svf_textsub2video = {
+    .name          = "textsub2video",
+    .description   = NULL_IF_CONFIG_SMALL("Convert textual subtitles to video frames"),
+    .init          = init,
+    .uninit        = uninit,
+    .priv_size     = sizeof(TextSubsContext),
+    .priv_class    = &textsub2video_class,
+    FILTER_INPUTS(textsub2video_inputs),
+    FILTER_OUTPUTS(textsub2video_outputs),
+    FILTER_QUERY_FUNC(textsub2video_query_formats),
+};
+#endif
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v2 14/26] avfilter/textmod: Add textmod, censor and show_speaker filters
  2022-01-20  2:48 ` [FFmpeg-devel] [PATCH v2 00/26] Subtitle Filtering 2022 ffmpegagent
                     ` (12 preceding siblings ...)
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 13/26] avfilter/overlaytextsubs: Add overlaytextsubs and textsubs2video filters ffmpegagent
@ 2022-01-20  2:48   ` ffmpegagent
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 15/26] avfilter/stripstyles: Add stripstyles filter ffmpegagent
                     ` (12 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  2:48 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

- textmod {S -> S)
  Modify subtitle text in a number of ways

- censor {S -> S)
  Censor subtitles using a word list

- show_speaker {S -> S)
  Prepend speaker names from ASS subtitles to the visible text lines

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 doc/filters.texi         | 206 ++++++++++++
 libavfilter/Makefile     |   5 +
 libavfilter/allfilters.c |   3 +
 libavfilter/sf_textmod.c | 710 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 924 insertions(+)
 create mode 100644 libavfilter/sf_textmod.c

diff --git a/doc/filters.texi b/doc/filters.texi
index 51771f88e4..494ee6f062 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -25721,6 +25721,145 @@ existing filters using @code{--disable-filters}.
 
 Below is a description of the currently available subtitle filters.
 
+
+@section censor
+
+Censor selected words in text subtitles.
+
+Inputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+@item mode
+The censoring mode to apply.
+
+Supported censoring modes are:
+
+@table @var
+@item 0, keep_first_last
+Replace all characters with the 'censor_char' except the first and the last character of a word.
+For words with less than 4 characters, the last character will be replaced as well.
+For words with less than 3 characters, the first character will be replaced as well.
+@item 1, keep_first
+Replace all characters with the 'censor_char' except the first character of a word.
+For words with less than 3 characters, the first character will be replaced as well.
+@item 2, all
+Replace all characters with the 'censor_char'.
+@end table
+
+@item words
+A list of words to censor, separated by 'separator'.
+
+@item words_file
+Specify a file from which to load the contents for the 'words' parameter.
+
+@item censor_char
+Single character used as replacement for censoring.
+
+@item separator
+Delimiter character for words. Used with replace_words and remove_words- Must be a single character.
+The default is '.'.
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Censor a few given words with a pound character.
+@example
+ffmpeg -i "http://streams.videolan.org/samples/sub/SSA/subtitle_testing_complex.mkv" -filter_complex "[0:1]censor=words='diss,louder,hope,beam,word':censor_char='#'" -map 0 -y output.mkv
+@end example
+@end itemize
+
+
+@section textmod
+
+Modify subtitle text in a number of ways.
+
+Inputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+@item mode
+The kind of text modification to apply
+
+Supported operation modes are:
+
+@table @var
+@item 0, leet
+Convert subtitle text to 'leet speak'. It's primarily useful for testing as the modification will be visible with almost all text lines.
+@item 1, to_upper
+Change all text to upper case. Might improve readability.
+@item 2, to_lower
+Change all text to lower case.
+@item 3, replace_chars
+Replace one or more characters. Requires the find and replace parameters to be specified.
+Both need to be equal in length.
+The first char in find is replaced by the first char in replace, same for all subsequent chars.
+@item 4, remove_chars
+Remove certain characters. Requires the find parameter to be specified.
+All chars in the find parameter string will be removed from all subtitle text.
+@item 5, replace_words
+Replace one or more words. Requires the find and replace parameters to be specified. Multiple words must be separated by the delimiter char specified vie the separator parameter (default: ',').
+The number of words in the find and replace parameters needs to be equal.
+The first word in find is replaced by the first word in replace, same for all subsequent words
+@item 6, remove_words
+Remove certain words. Requires the find parameter to be specified. Multiple words must be separated by the delimiter char specified vie the separator parameter (default: ',').
+All words in the find parameter string will be removed from all subtitle text.
+@end table
+
+@item find
+Required for replace_chars, remove_chars, replace_words and remove_words.
+
+@item find_file
+Specify a file from which to load the contents for the 'find' parameter.
+
+@item replace
+Required for replace_chars and replace_words.
+
+@item replace_file
+Specify a file from which to load the contents for the 'replace' parameter.
+
+@item separator
+Delimiter character for words. Used with replace_words and remove_words- Must be a single character.
+The default is '.'.
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Change all characters to upper case while keeping all styles and animations:
+@example
+ffmpeg -i "https://streams.videolan.org/ffmpeg/mkv_subtitles.mkv" -filter_complex "[0:s]textmod=mode=to_upper" -map 0 -y out.mkv
+@end example
+@item
+Remove a set of symbol characters for am improved and smoother visual apperance:
+@example
+ffmpeg -i "https://streams.videolan.org/ffmpeg/mkv_subtitles.mkv" -filter_complex "[0:s]textmod=mode=remove_chars:find='$&#@*§'" -map 0 -y out.mkv
+@end example
+@end itemize
+
 @section graphicsub2video
 
 Renders graphic subtitles as video frames.
@@ -25888,6 +26027,73 @@ ffmpeg -i "http://streams.videolan.org/samples/sub/SSA/subtitle_testing_complex.
 @end example
 @end itemize
 
+@section showspeaker
+
+Prepend speaker names to subtitle lines (when available).
+
+Subtitles in ASS/SSA format are often including the names of the persons
+or character for each subtitle line. The showspeaker filter adds those names
+to the actual subtitle text to make it visible on playback.
+
+Inputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+@item format
+The format for prepending speaker names. Default is 'square_brackets'.
+
+Supported operation modes are:
+
+@table @var
+@item 0, square_brackets
+Enclose the speaker name in square brackets, followed by space ('[speaker] text').
+@item 1, round_brackets
+Enclose the speaker name in round brackets, followed by space ('(speaker) text').
+@item 2, colon
+Separate the speaker name with a colon and space ('speaker: text').
+@item 3, plain
+Separate the speaker name with a space only ('speaker text').
+@end table
+
+@item line_break
+Set thís parameter to insert a line break between speaker name and text instead of the space character.
+
+@item style
+Allows to set a specific style for the speaker name text.
+
+This can be either a named style that exists in the ass subtitle header script (e.g. 'Default') or an ass style override code.
+Example:  @{\\c&HDD0000&\\be1\\i1\\bord10@}
+This sets the color to blue, enables edge blurring, italic font and a border of size 10.
+
+The behavior is as follows:
+
+- When the style parameter is not provided, the filter will find the first position in the event string that is actual text.
+  The speaker name will be inserted at this position. This allows to have the speaker name shown in the same style like the
+  regular text, in case the string would start with a sequence of style codes.
+- When the style parameter is provided, everything will be prepended to the original text:
+  Style Code or Style name >> Speaker Name >> Style Reset Code >> Original Text
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Prepend speaker names with blue text, smooth edges and blend/overlay that onto the video.
+@example
+ffmpeg -i INPUT -filter_complex "showspeaker=format=colon:style='@{\\c&HDD0000&\\be1@}',[0:v]overlay_textsubs"
+@end example
+@end itemize
+
 @section textsub2video
 
 Converts text subtitles to video frames.
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 48c9182c55..1af4f4b9bc 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -561,6 +561,11 @@ OBJS-$(CONFIG_YUVTESTSRC_FILTER)             += vsrc_testsrc.o
 
 OBJS-$(CONFIG_NULLSINK_FILTER)               += vsink_nullsink.o
 
+# subtitle filters
+OBJS-$(CONFIG_CENSOR_FILTER)                 += sf_textmod.o
+OBJS-$(CONFIG_SHOW_SPEAKER_FILTER)           += sf_textmod.o
+OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
+
 # multimedia filters
 OBJS-$(CONFIG_ABITSCOPE_FILTER)              += avf_abitscope.o
 OBJS-$(CONFIG_ADRAWGRAPH_FILTER)             += f_drawgraph.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 9ba89db128..ac7d71547b 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -547,6 +547,9 @@ 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_censor;
+extern const AVFilter ff_sf_showspeaker;
+extern const AVFilter ff_sf_textmod;
 extern const AVFilter ff_svf_graphicsub2video;
 extern const AVFilter ff_svf_textsub2video;
 
diff --git a/libavfilter/sf_textmod.c b/libavfilter/sf_textmod.c
new file mode 100644
index 0000000000..c75244cfb2
--- /dev/null
+++ b/libavfilter/sf_textmod.c
@@ -0,0 +1,710 @@
+/*
+ * 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 allows to modify subtitle text in several ways
+ */
+
+#include <libavutil/ass_internal.h>
+
+#include "libavutil/opt.h"
+#include "internal.h"
+#include "libavutil/ass_split_internal.h"
+#include "libavutil/bprint.h"
+#include "libavutil/file.h"
+
+static const char* leet_src = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+static const char* leet_dst = "abcd3f6#1jklmn0pq257uvwxyzAB(D3F6#1JKLMN0PQ257UVWXYZ";
+
+enum TextModFilterType {
+    TM_TEXTMOD,
+    TM_CENSOR,
+    TM_SHOW_SPEAKER,
+};
+
+enum TextModOperation {
+    OP_LEET,
+    OP_TO_UPPER,
+    OP_TO_LOWER,
+    OP_REPLACE_CHARS,
+    OP_REMOVE_CHARS,
+    OP_REPLACE_WORDS,
+    OP_REMOVE_WORDS,
+    NB_OPS,
+};
+
+enum CensorMode {
+    CM_KEEP_FIRST_LAST,
+    CM_KEEP_FIRST,
+    CM_ALL,
+};
+
+enum ShowSpeakerMode {
+    SM_SQUARE_BRACKETS,
+    SM_ROUND_BRACKETS,
+    SM_COLON,
+    SM_PLAIN,
+};
+
+typedef struct TextModContext {
+    const AVClass *class;
+    enum AVSubtitleType format;
+    enum TextModFilterType filter_type;
+    enum TextModOperation operation;
+    enum CensorMode censor_mode;
+    enum ShowSpeakerMode speaker_mode;
+    char *find;
+    char *find_file;
+    char *style;
+    char *replace;
+    char *replace_file;
+    char *separator;
+    char *censor_char;
+    char **find_list;
+    int  line_break;
+    int  nb_find_list;
+    char **replace_list;
+    int  nb_replace_list;
+} TextModContext;
+
+static char **split_string(char *source, int *nb_elems, const char *delim)
+{
+    char **list = NULL;
+    char *temp = NULL;
+    char *ptr = av_strtok(source, delim, &temp);
+
+    while (ptr) {
+        if (strlen(ptr)) {
+            av_dynarray_add(&list, nb_elems, ptr);
+            if (!list)
+                return NULL;
+        }
+
+        ptr = av_strtok(NULL, delim, &temp);
+    }
+
+    if (!list)
+        return NULL;
+
+    for (int i = 0; i < *nb_elems; i++) {
+        list[i] = av_strdup(list[i]);
+        if (!list[i]) {
+            for (int n = 0; n < i; n++)
+                av_free(list[n]);
+            av_free(list);
+            return NULL;
+        }
+    }
+
+    return list;
+}
+
+static int load_text_from_file(AVFilterContext *ctx, const char *file_name, char **text, char separator)
+{
+    int err;
+    uint8_t *textbuf;
+    char *tmp;
+    size_t textbuf_size;
+    int offset = 0;
+
+    if ((err = av_file_map(file_name, &textbuf, &textbuf_size, 0, ctx)) < 0) {
+        av_log(ctx, AV_LOG_ERROR, "The text file '%s' could not be read or is empty\n", file_name);
+        return err;
+    }
+
+    if (textbuf_size > 1 &&
+        (textbuf[0] == 0xFF && textbuf[1] == 0xFE
+        || textbuf[0] == 0xFE && textbuf[1] == 0xFF)) {
+        av_log(ctx, AV_LOG_ERROR, "UTF text files are not supported. File: %s\n", file_name);
+        return AVERROR(EINVAL);
+    }
+
+    if (textbuf_size > 2 && textbuf[0] == 0xEF && textbuf[1] == 0xBB && textbuf[2] == 0xBF)
+        offset = 3; // UTF-8
+
+    if (textbuf_size > SIZE_MAX - 1 || !((tmp = av_strndup((char *)textbuf + offset, textbuf_size - offset)))) {
+        av_file_unmap(textbuf, textbuf_size);
+        return AVERROR(ENOMEM);
+    }
+
+    av_file_unmap(textbuf, textbuf_size);
+
+    for (size_t i = 0; i < strlen(tmp); i++) {
+        switch (tmp[i]) {
+        case '\n':
+        case '\r':
+        case '\f':
+        case '\v':
+            tmp[i] = separator;
+        }
+    }
+
+    *text = tmp;
+
+    return 0;
+}
+
+static int load_files(AVFilterContext *ctx)
+{
+    TextModContext *s = ctx->priv;
+    int ret;
+
+    if (!s->separator || strlen(s->separator) != 1) {
+        av_log(ctx, AV_LOG_ERROR, "A single character needs to be specified for the separator parameter.\n");
+        return AVERROR(EINVAL);
+    }
+
+    if (s->find_file && strlen(s->find_file)) {
+        ret = load_text_from_file(ctx, s->find_file, &s->find, s->separator[0]);
+        if (ret < 0 )
+            return ret;
+    }
+
+    if (s->replace_file && strlen(s->replace_file)) {
+        ret = load_text_from_file(ctx, s->replace_file, &s->replace, s->separator[0]);
+        if (ret < 0 )
+            return ret;
+    }
+
+    return 0;
+}
+
+static int init_censor(AVFilterContext *ctx)
+{
+    TextModContext *s = ctx->priv;
+    int ret;
+
+    s->filter_type = TM_CENSOR;
+    s->operation = OP_REPLACE_WORDS;
+
+    ret = load_files(ctx);
+    if (ret < 0 )
+        return ret;
+
+    if (!s->find || !strlen(s->find)) {
+        av_log(ctx, AV_LOG_ERROR, "Either the 'words' or the 'words_file' parameter needs to be specified\n");
+        return AVERROR(EINVAL);
+    }
+
+    if (!s->censor_char || strlen(s->censor_char) != 1) {
+        av_log(ctx, AV_LOG_ERROR, "A single character needs to be specified for the censor_char parameter\n");
+        return AVERROR(EINVAL);
+    }
+
+    s->find_list = split_string(s->find, &s->nb_find_list, s->separator);
+    if (!s->find_list)
+        return AVERROR(ENOMEM);
+
+    s->replace_list = av_calloc(s->nb_find_list, sizeof(char *));
+    if (!s->replace_list)
+        return AVERROR(ENOMEM);
+
+    for (int i = 0; i < s->nb_find_list; i++) {
+        size_t len, start = 0, end;
+        char *item = av_strdup(s->find_list[i]);
+        if (!item)
+            return AVERROR(ENOMEM);
+
+        len = end = strlen(item);
+
+        switch (s->censor_mode) {
+        case CM_KEEP_FIRST_LAST:
+
+            if (len > 2)
+                start = 1;
+            if (len > 3)
+                end--;
+
+            break;
+        case CM_KEEP_FIRST:
+
+            if (len > 2)
+                start = 1;
+
+            break;
+        }
+
+        for (size_t n = start; n < end; n++)
+            item[n] = s->censor_char[0];
+
+        s->replace_list[i] = item;
+    }
+
+    return 0;
+}
+
+static int init_showspeaker(AVFilterContext *ctx)
+{
+    TextModContext *s = ctx->priv;
+    s->filter_type = TM_SHOW_SPEAKER;
+
+    return 0;
+}
+
+static int init(AVFilterContext *ctx)
+{
+    TextModContext *s = ctx->priv;
+    int ret;
+
+    ret = load_files(ctx);
+    if (ret < 0 )
+        return ret;
+
+    switch (s->operation) {
+    case OP_REPLACE_CHARS:
+    case OP_REMOVE_CHARS:
+    case OP_REPLACE_WORDS:
+    case OP_REMOVE_WORDS:
+        if (!s->find || !strlen(s->find)) {
+            av_log(ctx, AV_LOG_ERROR, "Selected mode requires the 'find' parameter to be specified\n");
+            return AVERROR(EINVAL);
+        }
+        break;
+    }
+
+    switch (s->operation) {
+    case OP_REPLACE_CHARS:
+    case OP_REPLACE_WORDS:
+        if (!s->replace || !strlen(s->replace)) {
+            av_log(ctx, AV_LOG_ERROR, "Selected mode requires the 'replace' parameter to be specified\n");
+            return AVERROR(EINVAL);
+        }
+        break;
+    }
+
+    if (s->operation == OP_REPLACE_CHARS && strlen(s->find) != strlen(s->replace)) {
+        av_log(ctx, AV_LOG_ERROR, "Selected mode requires the 'find' and 'replace' parameters to have the same length\n");
+        return AVERROR(EINVAL);
+    }
+
+    if (s->operation == OP_REPLACE_WORDS || s->operation == OP_REMOVE_WORDS) {
+        if (!s->separator || strlen(s->separator) != 1) {
+            av_log(ctx, AV_LOG_ERROR, "Selected mode requires a single separator char to be specified\n");
+            return AVERROR(EINVAL);
+        }
+
+        s->find_list = split_string(s->find, &s->nb_find_list, s->separator);
+        if (!s->find_list)
+            return AVERROR(ENOMEM);
+
+        if (s->operation == OP_REPLACE_WORDS) {
+
+            s->replace_list = split_string(s->replace, &s->nb_replace_list, s->separator);
+            if (!s->replace_list)
+                return AVERROR(ENOMEM);
+
+            if (s->nb_find_list != s->nb_replace_list) {
+                av_log(ctx, AV_LOG_ERROR, "The number of words in 'find' and 'replace' needs to be equal\n");
+                return AVERROR(EINVAL);
+            }
+        }
+    }
+
+    return 0;
+}
+
+static void uninit(AVFilterContext *ctx)
+{
+    TextModContext *s = ctx->priv;
+
+    for (int i = 0; i < s->nb_find_list; i++)
+        av_freep(&s->find_list[i]);
+
+    s->nb_find_list = 0;
+    av_freep(&s->find_list);
+
+    for (int i = 0; i < s->nb_replace_list; i++)
+        av_freep(&s->replace_list[i]);
+
+    s->nb_replace_list = 0;
+    av_freep(&s->replace_list);
+}
+
+static char *process_text(TextModContext *s, char *text)
+{
+    const char *char_src = s->find;
+    const char *char_dst = s->replace;
+    char *result         = NULL;
+    int escape_level     = 0, k = 0;
+
+    switch (s->operation) {
+    case OP_LEET:
+    case OP_REPLACE_CHARS:
+
+        if (s->operation == OP_LEET) {
+            char_src = leet_src;
+            char_dst = leet_dst;
+        }
+
+        result = av_strdup(text);
+        if (!result)
+            return NULL;
+
+        for (size_t n = 0; n < strlen(result); n++) {
+            if (result[n] == '{')
+                escape_level++;
+
+            if (!escape_level) {
+                size_t len = strlen(char_src);
+                for (size_t t = 0; t < len; t++) {
+                    if (result[n] == char_src[t]) {
+                        result[n] = char_dst[t];
+                        break;
+                    }
+                }
+            }
+
+            if (result[n] == '}')
+                escape_level--;
+        }
+
+        break;
+    case OP_TO_UPPER:
+    case OP_TO_LOWER:
+
+        result = av_strdup(text);
+        if (!result)
+            return NULL;
+
+        for (size_t n = 0; n < strlen(result); n++) {
+            if (result[n] == '{')
+                escape_level++;
+            if (!escape_level)
+                result[n] = s->operation == OP_TO_LOWER ? av_tolower(result[n]) : av_toupper(result[n]);
+            if (result[n] == '}')
+                escape_level--;
+        }
+
+        break;
+    case OP_REMOVE_CHARS:
+
+        result = av_strdup(text);
+        if (!result)
+            return NULL;
+
+        for (size_t n = 0; n < strlen(result); n++) {
+            int skip_char = 0;
+
+            if (result[n] == '{')
+                escape_level++;
+
+            if (!escape_level) {
+                size_t len = strlen(char_src);
+                for (size_t t = 0; t < len; t++) {
+                    if (result[n] == char_src[t]) {
+                        skip_char = 1;
+                        break;
+                    }
+                }
+            }
+
+            if (!skip_char)
+                result[k++] = result[n];
+
+            if (result[n] == '}')
+                escape_level--;
+        }
+
+        result[k] = 0;
+
+        break;
+    case OP_REPLACE_WORDS:
+    case OP_REMOVE_WORDS:
+
+        result = av_strdup(text);
+        if (!result)
+            return NULL;
+
+        for (int n = 0; n < s->nb_find_list; n++) {
+            char *tmp           = result;
+            const char *replace = (s->operation == OP_REPLACE_WORDS) ? s->replace_list[n] : "";
+
+            result = av_strireplace(result, s->find_list[n], replace);
+            if (!result)
+                return NULL;
+
+            av_free(tmp);
+        }
+
+        break;
+    }
+
+    return result;
+}
+
+static char *process_dialog_show_speaker(TextModContext *s, char *ass_line)
+{
+    ASSDialog *dialog = avpriv_ass_split_dialog(NULL, ass_line);
+    int escape_level = 0;
+    unsigned pos = 0, len;
+    char *result, *text;
+    AVBPrint pbuf;
+
+    if (!dialog)
+        return NULL;
+
+    text = process_text(s, dialog->text);
+    if (!text)
+        return NULL;
+
+    if (!dialog->name || !strlen(dialog->name) || !dialog->text || !strlen(dialog->text))
+        return av_strdup(ass_line);
+
+    // Find insertion point in case the line starts with style codes
+    len = (unsigned)strlen(dialog->text);
+    for (unsigned i = 0; i < len; i++) {
+
+        if (dialog->text[i] == '{')
+            escape_level++;
+
+        if (dialog->text[i] == '}')
+            escape_level--;
+
+        if (escape_level == 0) {
+            pos = i;
+            break;
+        }
+    }
+
+    if (s->style && strlen(s->style))
+        // When a style is specified reset the insertion point
+        // (always add speaker plus style at the start in that case)
+        pos = 0;
+
+    if (pos >= len - 1)
+        return av_strdup(ass_line);
+
+    av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
+
+    if (pos > 0) {
+        av_bprint_append_data(&pbuf, dialog->text, pos);
+    }
+
+    if (s->style && strlen(s->style)) {
+        if (s->style[0] == '{')
+            // Assume complete and valid style code, e.g. {\c&HFF0000&}
+            av_bprintf(&pbuf, "%s", s->style);
+        else
+            // Otherwise it must be a style name
+            av_bprintf(&pbuf, "{\\r%s}", s->style);
+    }
+
+    switch (s->speaker_mode) {
+    case SM_SQUARE_BRACKETS:
+        av_bprintf(&pbuf, "[%s]", dialog->name);
+        break;
+    case SM_ROUND_BRACKETS:
+        av_bprintf(&pbuf, "(%s)", dialog->name);
+        break;
+    case SM_COLON:
+        av_bprintf(&pbuf, "%s:", dialog->name);
+        break;
+    case SM_PLAIN:
+        av_bprintf(&pbuf, "%s", dialog->name);
+        break;
+    }
+
+    if (s->style && strlen(s->style)) {
+        // Reset line style
+        if (dialog->style && strlen(dialog->style) && !av_strcasecmp(dialog->style, "default"))
+            av_bprintf(&pbuf, "{\\r%s}", dialog->style);
+        else
+            av_bprintf(&pbuf, "{\\r}");
+    }
+
+    if (s->line_break)
+        av_bprintf(&pbuf, "\\N");
+    else
+        av_bprintf(&pbuf, " ");
+
+    av_bprint_append_data(&pbuf, dialog->text + pos, len - pos);
+
+    av_bprint_finalize(&pbuf, &text);
+
+    result = avpriv_ass_get_dialog_ex(dialog->readorder, dialog->layer, dialog->style, dialog->name, dialog->margin_l, dialog->margin_r, dialog->margin_v, text);
+
+    av_free(text);
+    avpriv_ass_free_dialog(&dialog);
+    return result;
+}
+
+static char *process_dialog(TextModContext *s, char *ass_line)
+{
+    ASSDialog *dialog;
+    char *result, *text;
+
+    if (s->filter_type == TM_SHOW_SPEAKER)
+        return process_dialog_show_speaker(s, ass_line);
+
+    dialog = avpriv_ass_split_dialog(NULL, ass_line);
+    if (!dialog)
+        return NULL;
+
+    text = process_text(s, dialog->text);
+    if (!text)
+        return NULL;
+
+    result = avpriv_ass_get_dialog_ex(dialog->readorder, dialog->layer, dialog->style, dialog->name, dialog->margin_l, dialog->margin_r, dialog->margin_v, text);
+
+    av_free(text);
+    avpriv_ass_free_dialog(&dialog);
+    return result;
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    AVFilterLink *inlink = outlink->src->inputs[0];
+
+    outlink->w = inlink->w;
+    outlink->h = inlink->h;
+    outlink->time_base = inlink->time_base;
+    outlink->frame_rate = inlink->frame_rate;
+
+    return 0;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    TextModContext *s = inlink->dst->priv;
+    AVFilterLink *outlink = inlink->dst->outputs[0];
+    int ret;
+
+    outlink->format = inlink->format;
+
+    ret = av_frame_make_writable(frame);
+    if (ret < 0)
+        return ret;
+
+    for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+
+        AVSubtitleArea *area = frame->subtitle_areas[i];
+
+        if (area->ass) {
+            char *tmp = area->ass;
+            area->ass = process_dialog(s, area->ass);
+            av_free(tmp);
+            if (!area->ass)
+                return AVERROR(ENOMEM);
+        }
+    }
+
+    return ff_filter_frame(outlink, frame);
+}
+
+#define OFFSET(x) offsetof(TextModContext, x)
+#define FLAGS (AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption textmod_options[] = {
+    { "mode",             "set operation mode",              OFFSET(operation),    AV_OPT_TYPE_INT,    {.i64=OP_LEET},          OP_LEET, NB_OPS-1, FLAGS, "mode" },
+    {   "leet",           "convert text to 'leet speak'",    0,                    AV_OPT_TYPE_CONST,  {.i64=OP_LEET},          0,       0,        FLAGS, "mode" },
+    {   "to_upper",       "change to upper case",            0,                    AV_OPT_TYPE_CONST,  {.i64=OP_TO_UPPER},      0,       0,        FLAGS, "mode" },
+    {   "to_lower",       "change to lower case",            0,                    AV_OPT_TYPE_CONST,  {.i64=OP_TO_LOWER},      0,       0,        FLAGS, "mode" },
+    {   "replace_chars",  "replace characters",              0,                    AV_OPT_TYPE_CONST,  {.i64=OP_REPLACE_CHARS}, 0,       0,        FLAGS, "mode" },
+    {   "remove_chars",   "remove characters",               0,                    AV_OPT_TYPE_CONST,  {.i64=OP_REMOVE_CHARS},  0,       0,        FLAGS, "mode" },
+    {   "replace_words",  "replace words",                   0,                    AV_OPT_TYPE_CONST,  {.i64=OP_REPLACE_WORDS}, 0,       0,        FLAGS, "mode" },
+    {   "remove_words",   "remove words",                    0,                    AV_OPT_TYPE_CONST,  {.i64=OP_REMOVE_WORDS},  0,       0,        FLAGS, "mode" },
+    { "find",             "chars/words to find or remove",   OFFSET(find),         AV_OPT_TYPE_STRING, {.str = NULL},           0,       0,        FLAGS, NULL   },
+    { "find_file",        "load find param from file",       OFFSET(find_file),    AV_OPT_TYPE_STRING, {.str = NULL},           0,       0,        FLAGS, NULL   },
+    { "replace",          "chars/words to replace",          OFFSET(replace),      AV_OPT_TYPE_STRING, {.str = NULL},           0,       0,        FLAGS, NULL   },
+    { "replace_file",     "load replace param from file",    OFFSET(replace_file), AV_OPT_TYPE_STRING, {.str = NULL},           0,       0,        FLAGS, NULL   },
+    { "separator",        "word separator",                  OFFSET(separator),    AV_OPT_TYPE_STRING, {.str = ","},            0,       0,        FLAGS, NULL   },
+    { NULL },
+};
+
+
+static const AVOption censor_options[] = {
+    { "mode",               "set censoring mode",        OFFSET(censor_mode), AV_OPT_TYPE_INT,    {.i64=CM_KEEP_FIRST_LAST}, 0, 2, FLAGS, "mode" },
+    {   "keep_first_last",  "censor inner chars",        0,                   AV_OPT_TYPE_CONST,  {.i64=CM_KEEP_FIRST_LAST}, 0, 0, FLAGS, "mode" },
+    {   "keep_first",       "censor all but first char", 0,                   AV_OPT_TYPE_CONST,  {.i64=CM_KEEP_FIRST},      0, 0, FLAGS, "mode" },
+    {   "all",              "censor all chars",          0,                   AV_OPT_TYPE_CONST,  {.i64=CM_ALL},             0, 0, FLAGS, "mode" },
+    { "words",              "list of words to censor",   OFFSET(find),        AV_OPT_TYPE_STRING, {.str = NULL},             0, 0, FLAGS, NULL   },
+    { "words_file",         "path to word list file",    OFFSET(find_file),   AV_OPT_TYPE_STRING, {.str = NULL},             0, 0, FLAGS, NULL   },
+    { "separator",          "word separator",            OFFSET(separator),   AV_OPT_TYPE_STRING, {.str = ","},              0, 0, FLAGS, NULL   },
+    { "censor_char",        "replacement character",     OFFSET(censor_char), AV_OPT_TYPE_STRING, {.str = "*"},              0, 0, FLAGS, NULL   },
+    { NULL },
+};
+
+static const AVOption showspeaker_options[] = {
+    { "format",             "speaker name formatting",        OFFSET(speaker_mode), AV_OPT_TYPE_INT,    {.i64=SM_SQUARE_BRACKETS}, 0, 2, FLAGS, "format" },
+    {   "square_brackets",  "[speaker] text",                 0,                    AV_OPT_TYPE_CONST,  {.i64=SM_SQUARE_BRACKETS}, 0, 0, FLAGS, "format" },
+    {   "round_brackets",   "(speaker) text",                 0,                    AV_OPT_TYPE_CONST,  {.i64=SM_ROUND_BRACKETS},  0, 0, FLAGS, "format" },
+    {   "colon",            "speaker: text",                  0,                    AV_OPT_TYPE_CONST,  {.i64=SM_COLON},           0, 0, FLAGS, "format" },
+    {   "plain",            "speaker text",                   0,                    AV_OPT_TYPE_CONST,  {.i64=SM_PLAIN},           0, 0, FLAGS, "format" },
+    { "line_break",         "insert line break",              OFFSET(line_break),   AV_OPT_TYPE_BOOL,   {.i64=0},                  0, 1, FLAGS, NULL     },
+    { "style",              "ass type name or style code",    OFFSET(style),        AV_OPT_TYPE_STRING, {.str = NULL},             0, 0, FLAGS, NULL     },
+    { NULL },
+};
+
+AVFILTER_DEFINE_CLASS(textmod);
+AVFILTER_DEFINE_CLASS(censor);
+AVFILTER_DEFINE_CLASS(showspeaker);
+
+static const AVFilterPad inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .filter_frame = filter_frame,
+    },
+};
+
+static const AVFilterPad outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_SUBTITLE,
+        .config_props  = config_output,
+    },
+};
+
+const AVFilter ff_sf_textmod = {
+    .name          = "textmod",
+    .description   = NULL_IF_CONFIG_SMALL("Modify subtitle text in several ways"),
+    .init          = init,
+    .uninit        = uninit,
+    .priv_size     = sizeof(TextModContext),
+    .priv_class    = &textmod_class,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_SINGLE_SUBFMT(AV_SUBTITLE_FMT_ASS),
+};
+
+const AVFilter ff_sf_censor = {
+    .name          = "censor",
+    .description   = NULL_IF_CONFIG_SMALL("Censor words in subtitle text"),
+    .init          = init_censor,
+    .uninit        = uninit,
+    .priv_size     = sizeof(TextModContext),
+    .priv_class    = &censor_class,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_SINGLE_SUBFMT(AV_SUBTITLE_FMT_ASS),
+};
+
+const AVFilter ff_sf_showspeaker = {
+    .name          = "showspeaker",
+    .description   = NULL_IF_CONFIG_SMALL("Prepend speaker names to text subtitles (when available)"),
+    .init          = init_showspeaker,
+    .uninit        = uninit,
+    .priv_size     = sizeof(TextModContext),
+    .priv_class    = &showspeaker_class,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_SINGLE_SUBFMT(AV_SUBTITLE_FMT_ASS),
+};
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v2 15/26] avfilter/stripstyles: Add stripstyles filter
  2022-01-20  2:48 ` [FFmpeg-devel] [PATCH v2 00/26] Subtitle Filtering 2022 ffmpegagent
                     ` (13 preceding siblings ...)
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 14/26] avfilter/textmod: Add textmod, censor and show_speaker filters ffmpegagent
@ 2022-01-20  2:48   ` ffmpegagent
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 16/26] avfilter/splitcc: Add splitcc filter for closed caption handling ffmpegagent
                     ` (11 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  2:48 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

- stripstyles {S -> S)
  Remove all inline styles from subtitle events

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 doc/filters.texi             |  37 ++++++
 libavfilter/Makefile         |   1 +
 libavfilter/allfilters.c     |   1 +
 libavfilter/sf_stripstyles.c | 211 +++++++++++++++++++++++++++++++++++
 4 files changed, 250 insertions(+)
 create mode 100644 libavfilter/sf_stripstyles.c

diff --git a/doc/filters.texi b/doc/filters.texi
index 494ee6f062..c0f0fe13e7 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -25781,6 +25781,43 @@ ffmpeg -i "http://streams.videolan.org/samples/sub/SSA/subtitle_testing_complex.
 @end example
 @end itemize
 
+@section stripstyles
+
+Remove all inline styles from subtitle events.
+
+Inputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+@item remove_animated
+Also remove text which is subject to animation (default: true)
+Usually, animated text elements are used used in addition to static subtitle lines for creating effects, so in most cases it is safe to remove the animation content.
+If subtitle text is missing, try setting this to false.
+
+@item select_layer
+Process only ASS subtitle events from a specific layer. This allows to filter out certain effects where an ASS author duplicates the text onto multiple layers.
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Remove styles and animations from ASS subtitles and output events from ass layer 0 only. Then convert asn save as SRT stream:
+@example
+ffmpeg -i "https://streams.videolan.org/samples/sub/SSA/subtitle_testing_complex.mkv" -filter_complex "[0:1]stripstyles=select_layer=0" -map 0 -c:s srt output.mkv
+@end example
+@end itemize
+
 
 @section textmod
 
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 1af4f4b9bc..d330020f67 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -565,6 +565,7 @@ OBJS-$(CONFIG_NULLSINK_FILTER)               += vsink_nullsink.o
 OBJS-$(CONFIG_CENSOR_FILTER)                 += sf_textmod.o
 OBJS-$(CONFIG_SHOW_SPEAKER_FILTER)           += sf_textmod.o
 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 ac7d71547b..b0c12595af 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -549,6 +549,7 @@ extern const AVFilter ff_avf_showwavespic;
 extern const AVFilter ff_vaf_spectrumsynth;
 extern const AVFilter ff_sf_censor;
 extern const AVFilter ff_sf_showspeaker;
+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..bc3c5d1441
--- /dev/null
+++ b/libavfilter/sf_stripstyles.c
@@ -0,0 +1,211 @@
+/*
+ * 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 "libavutil/opt.h"
+#include "internal.h"
+#include "libavutil/ass_split_internal.h"
+#include "libavutil/bprint.h"
+
+typedef struct StripStylesContext {
+    const AVClass *class;
+    enum AVSubtitleType format;
+    int remove_animated;
+    int select_layer;
+} StripStylesContext;
+
+typedef struct DialogContext {
+    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;
+
+    av_log(s->ss_ctx, AV_LOG_DEBUG, "dialog_text_cb: %s\n", text);
+
+    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 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)
+{
+    DialogContext dlg_ctx = { .ss_ctx = s };
+    ASSDialog *dialog = avpriv_ass_split_dialog(NULL, ass_line);
+    char *result = NULL;
+
+    if (!dialog)
+        return NULL;
+
+    if (s->select_layer >= 0 && dialog->layer != s->select_layer) {
+        avpriv_ass_free_dialog(&dialog);
+        return NULL;
+    }
+
+    dlg_ctx.ss_ctx = s;
+
+    av_bprint_init(&dlg_ctx.buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
+
+    avpriv_ass_split_override_codes(&dialog_callbacks, &dlg_ctx, dialog->text);
+
+    if (av_bprint_is_complete(&dlg_ctx.buffer)
+        && dlg_ctx.buffer.len > 0)
+        result = ass_get_line(dialog->readorder, dialog->layer, dialog->style, dialog->name, dialog->effect, dlg_ctx.buffer.str);
+
+    av_bprint_finalize(&dlg_ctx.buffer, NULL);
+    avpriv_ass_free_dialog(&dialog);
+
+    return result;
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    AVFilterLink *inlink = outlink->src->inputs[0];
+
+    outlink->w = inlink->w;
+    outlink->h = inlink->h;
+    outlink->time_base = inlink->time_base;
+    outlink->frame_rate = inlink->frame_rate;
+
+    return 0;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    StripStylesContext *s = inlink->dst->priv;
+    AVFilterLink *outlink = inlink->dst->outputs[0];
+    int ret;
+
+    outlink->format = inlink->format;
+
+    ret = av_frame_make_writable(frame);
+    if (ret <0 ) {
+        av_frame_free(&frame);
+        return AVERROR(ENOMEM);
+    }
+
+    for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+
+        AVSubtitleArea *area = frame->subtitle_areas[i];
+
+        if (area->ass) {
+            char *tmp = area->ass;
+            area->ass = process_dialog(s, area->ass);
+
+            if (area->ass) {
+                av_log(inlink->dst, AV_LOG_INFO, "original: %d %s\n", i, tmp);
+                av_log(inlink->dst, AV_LOG_INFO, "stripped: %d %s\n", i, area->ass);
+            }
+            else
+                area->ass = NULL;
+
+            av_free(tmp);
+        }
+    }
+
+    return ff_filter_frame(outlink, frame);
+}
+
+#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 },
+    { "select_layer", "process a specific ass layer only",   OFFSET(remove_animated),  AV_OPT_TYPE_INT, {.i64 = -1 }, -1, INT_MAX, 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,
+        .config_props  = config_output,
+    },
+};
+
+const AVFilter ff_sf_stripstyles = {
+    .name          = "stripstyles",
+    .description   = NULL_IF_CONFIG_SMALL("Strip subtitle inline styles"),
+    .priv_size     = sizeof(StripStylesContext),
+    .priv_class    = &stripstyles_class,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_SINGLE_SUBFMT(AV_SUBTITLE_FMT_ASS),
+};
+
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v2 16/26] avfilter/splitcc: Add splitcc filter for closed caption handling
  2022-01-20  2:48 ` [FFmpeg-devel] [PATCH v2 00/26] Subtitle Filtering 2022 ffmpegagent
                     ` (14 preceding siblings ...)
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 15/26] avfilter/stripstyles: Add stripstyles filter ffmpegagent
@ 2022-01-20  2:48   ` ffmpegagent
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 17/26] avfilter/graphicsub2text: Add new graphicsub2text filter (OCR) ffmpegagent
                     ` (10 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  2:48 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

- splitcc {V -> VS)
  Extract closed-caption (A53) data from video
  frames as subtitle Frames

ffmpeg -y -loglevel verbose -i "https://streams.videolan.org/streams
/ts/CC/NewsStream-608-ac3.ts" -filter_complex "[0:v]splitcc[vid1],
textmod=mode=remove_chars:find='@',[vid1]overlay_textsubs" output.mkv

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 configure                |   1 +
 doc/filters.texi         |  63 +++++++
 libavfilter/Makefile     |   1 +
 libavfilter/allfilters.c |   1 +
 libavfilter/sf_splitcc.c | 395 +++++++++++++++++++++++++++++++++++++++
 5 files changed, 461 insertions(+)
 create mode 100644 libavfilter/sf_splitcc.c

diff --git a/configure b/configure
index 340a198fa6..c1d2bc41c2 100755
--- a/configure
+++ b/configure
@@ -3729,6 +3729,7 @@ spp_filter_select="fft idctdsp fdctdsp me_cmp pixblockdsp"
 sr_filter_deps="avformat swscale"
 sr_filter_select="dnn"
 stereo3d_filter_deps="gpl"
+splitcc_filter_deps="avcodec"
 subtitles_filter_deps="avformat avcodec libass"
 super2xsai_filter_deps="gpl"
 pixfmts_super2xsai_test_deps="super2xsai_filter"
diff --git a/doc/filters.texi b/doc/filters.texi
index c0f0fe13e7..265a267e9d 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -26131,6 +26131,69 @@ ffmpeg -i INPUT -filter_complex "showspeaker=format=colon:style='@{\\c&HDD0000&\
 @end example
 @end itemize
 
+
+@section splitcc
+
+Split-out closed-caption/A53 subtitles from video frame side data.
+
+This filter provides an input and an output for video frames, which are just passed through without modification.
+The second out provides subtitle frames which are extracted from video frame side data.
+
+Inputs:
+@itemize
+@item 0: Video [ALL]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Video (same as input)
+@item 1: Subtitles [TEXT]
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+
+@item use_cc_styles
+Emit closed caption style header.
+This will make closed captions appear in white font with a black rectangle background.
+
+@item real_time
+Emit subtitle events as they are decoded for real-time display.
+
+@item real_time_latency_msec
+Minimum elapsed time between emitting real-time subtitle events.
+Only applies to real_time mode.
+
+@item data_field
+Select data field. Possible values:
+
+@table @samp
+@item auto
+Pick first one that appears.
+@item first
+@item second
+@end table
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Extract closed captions as text subtitle stream and overlay it onto the video in cc style (black bar background):
+@example
+ffmpeg -i "https://streams.videolan.org/streams/ts/CC/NewsStream-608-ac3.ts" -filter_complex  "[0:v:0]splitcc=use_cc_styles=1[vid1][sub1];[vid1][sub1]overlaytextsubs" output.mkv
+@end example
+
+@item
+A nicer variant, using realtime output from cc_dec and rendering it with the render_latest_only parameter from overlaytextsubs to avoid ghosting by timely overlap.
+@example
+ffmpeg -i "https://streams.videolan.org/streams/ts/CC/NewsStream-608-ac3.ts" -filter_complex  "[0:v:0]splitcc=real_time=1:real_time_latency_msec=200[vid1][sub1];[vid1][sub1]overlaytextsubs=render_latest_only=1" output.mkv
+@end example
+@end itemize
+
+
 @section textsub2video
 
 Converts text subtitles to video frames.
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index d330020f67..c6a4a4f5ae 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -565,6 +565,7 @@ OBJS-$(CONFIG_NULLSINK_FILTER)               += vsink_nullsink.o
 OBJS-$(CONFIG_CENSOR_FILTER)                 += sf_textmod.o
 OBJS-$(CONFIG_SHOW_SPEAKER_FILTER)           += sf_textmod.o
 OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
+OBJS-$(CONFIG_SPLITCC_FILTER)                += sf_splitcc.o
 OBJS-$(CONFIG_STRIPSTYLES_FILTER)            += sf_stripstyles.o
 
 # multimedia filters
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index b0c12595af..50498e8ec4 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -549,6 +549,7 @@ extern const AVFilter ff_avf_showwavespic;
 extern const AVFilter ff_vaf_spectrumsynth;
 extern const AVFilter ff_sf_censor;
 extern const AVFilter ff_sf_showspeaker;
+extern const AVFilter ff_sf_splitcc;
 extern const AVFilter ff_sf_stripstyles;
 extern const AVFilter ff_sf_textmod;
 extern const AVFilter ff_svf_graphicsub2video;
diff --git a/libavfilter/sf_splitcc.c b/libavfilter/sf_splitcc.c
new file mode 100644
index 0000000000..14235e822c
--- /dev/null
+++ b/libavfilter/sf_splitcc.c
@@ -0,0 +1,395 @@
+/*
+ * 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
+ * subtitle filter for splitting out closed-caption/A53 subtitles from video frame side data
+ */
+
+#include "filters.h"
+#include "libavutil/opt.h"
+#include "subtitles.h"
+#include "libavcodec/avcodec.h"
+
+static const AVRational ms_tb = {1, 1000};
+
+typedef struct SplitCaptionsContext {
+    const AVClass *class;
+    enum AVSubtitleType format;
+    AVCodecContext *cc_dec;
+    int eof;
+    AVFrame *next_sub_frame;
+    AVFrame *empty_sub_frame;
+    int new_frame;
+    int64_t next_repetition_pts;
+    int had_keyframe;
+    AVBufferRef *subtitle_header;
+    int use_cc_styles;
+    int real_time;
+    int real_time_latency_msec;
+    int data_field;
+    int scatter_realtime_output;
+} SplitCaptionsContext;
+
+static int64_t ms_to_avtb(int64_t ms)
+{
+    return av_rescale_q(ms, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q);
+}
+
+static int init(AVFilterContext *ctx)
+{
+    SplitCaptionsContext *s = ctx->priv;
+    AVDictionary *options = NULL;
+
+    int ret;
+    const AVCodec *codec = avcodec_find_decoder(AV_CODEC_ID_EIA_608);
+    if (!codec) {
+        av_log(ctx, AV_LOG_ERROR, "failed to find EIA-608/708 decoder\n");
+        return AVERROR_DECODER_NOT_FOUND;
+    }
+
+    if (!((s->cc_dec = avcodec_alloc_context3(codec)))) {
+        av_log(ctx, AV_LOG_ERROR, "failed to allocate EIA-608/708 decoder\n");
+        return AVERROR(ENOMEM);
+    }
+
+    av_dict_set_int(&options, "real_time", s->real_time, 0);
+    av_dict_set_int(&options, "real_time_latency_msec", s->real_time_latency_msec, 0);
+    av_dict_set_int(&options, "data_field", s->data_field, 0);
+
+    if ((ret = avcodec_open2(s->cc_dec, codec, &options)) < 0) {
+        av_log(ctx, AV_LOG_ERROR, "failed to open EIA-608/708 decoder: %i\n", ret);
+        return ret;
+    }
+
+    if (s->use_cc_styles && s->cc_dec->subtitle_header && s->cc_dec->subtitle_header[0] != 0) {
+        char* subtitle_header =  av_strdup((char *)s->cc_dec->subtitle_header);
+        if (!subtitle_header)
+            return AVERROR(ENOMEM);
+        s->subtitle_header = av_buffer_create((uint8_t *)subtitle_header, strlen(subtitle_header) + 1, NULL, NULL, 0);
+        if (!s->subtitle_header) {
+            av_free(subtitle_header);
+            return AVERROR(ENOMEM);
+        }
+    }
+
+    return 0;
+}
+
+static void uninit(AVFilterContext *ctx)
+{
+    SplitCaptionsContext *s = ctx->priv;
+    av_frame_free(&s->next_sub_frame);
+    av_frame_free(&s->empty_sub_frame);
+    av_buffer_unref(&s->subtitle_header);
+}
+
+static int config_input(AVFilterLink *link)
+{
+    const SplitCaptionsContext *context = link->dst->priv;
+
+    if (context->cc_dec)
+        context->cc_dec->pkt_timebase = link->time_base;
+
+    return 0;
+}
+
+static int query_formats(AVFilterContext *ctx)
+{
+    AVFilterFormats *formats;
+    AVFilterLink *inlink0 = ctx->inputs[0];
+    AVFilterLink *outlink0 = ctx->outputs[0];
+    AVFilterLink *outlink1 = ctx->outputs[1];
+    static const enum AVSubtitleType subtitle_fmts[] = { AV_SUBTITLE_FMT_ASS, AV_SUBTITLE_FMT_NONE };
+    int ret;
+
+    /* set input0 video formats */
+    formats = ff_all_formats(AVMEDIA_TYPE_VIDEO);
+    if ((ret = ff_formats_ref(formats, &inlink0->outcfg.formats)) < 0)
+        return ret;
+
+    /* set output0 video formats */
+    if ((ret = ff_formats_ref(formats, &outlink0->incfg.formats)) < 0)
+        return ret;
+
+    /* set output1 subtitle formats */
+    formats = ff_make_format_list(subtitle_fmts);
+    if ((ret = ff_formats_ref(formats, &outlink1->incfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int config_video_output(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    const AVFilterLink *inlink = outlink->src->inputs[0];
+
+    outlink->w = ctx->inputs[0]->w;
+    outlink->h = ctx->inputs[0]->h;
+    outlink->frame_rate = ctx->inputs[0]->frame_rate;
+    outlink->time_base = ctx->inputs[0]->time_base;
+    outlink->sample_aspect_ratio = ctx->inputs[0]->sample_aspect_ratio;
+
+    if (inlink->hw_frames_ctx)
+        outlink->hw_frames_ctx = av_buffer_ref(inlink->hw_frames_ctx);
+
+    return 0;
+}
+
+static int config_sub_output(AVFilterLink *outlink)
+{
+    SplitCaptionsContext *s = outlink->src->priv;
+    const AVFilterLink *inlink = outlink->src->inputs[0];
+
+    outlink->time_base = inlink->time_base;
+    outlink->format = AV_SUBTITLE_FMT_ASS;
+    outlink->frame_rate = (AVRational){1000, s->real_time_latency_msec};
+
+    return 0;
+}
+
+static int request_sub_frame(AVFilterLink *outlink)
+{
+    SplitCaptionsContext *s = outlink->src->priv;
+    int status;
+    int64_t pts;
+
+    if (!s->empty_sub_frame) {
+        s->empty_sub_frame = ff_get_subtitles_buffer(outlink, outlink->format);
+        if (!s->empty_sub_frame)
+            return AVERROR(ENOMEM);
+    }
+
+    if (!s->eof && ff_inlink_acknowledge_status(outlink->src->inputs[0], &status, &pts)) {
+        if (status == AVERROR_EOF)
+            s->eof = 1;
+    }
+
+    if (s->eof)
+        return AVERROR_EOF;
+
+    if (s->next_sub_frame) {
+
+        AVFrame *out = NULL;
+        s->next_sub_frame->pts++;
+
+        if (s->new_frame)
+            out = av_frame_clone(s->next_sub_frame);
+        else if (s->empty_sub_frame) {
+            s->empty_sub_frame->pts = s->next_sub_frame->pts;
+            out = av_frame_clone(s->empty_sub_frame);
+            av_frame_copy_props(out, s->next_sub_frame);
+            out->repeat_sub = 1;
+        }
+
+        if (!out)
+            return AVERROR(ENOMEM);
+
+        out->subtitle_timing.start_pts = av_rescale_q(s->next_sub_frame->pts, outlink->time_base, AV_TIME_BASE_Q);
+        s->new_frame = 0;
+
+        return ff_filter_frame(outlink, out);
+    }
+
+    return 0;
+}
+
+static int decode(AVCodecContext *avctx, AVFrame *frame, int *got_frame, AVPacket *pkt)
+{
+    int ret;
+
+    *got_frame = 0;
+
+    if (pkt) {
+        ret = avcodec_send_packet(avctx, pkt);
+        // In particular, we don't expect AVERROR(EAGAIN), because we read all
+        // decoded frames with avcodec_receive_frame() until done.
+        if (ret < 0 && ret != AVERROR_EOF)
+            return ret;
+    }
+
+    ret = avcodec_receive_frame(avctx, frame);
+    if (ret < 0 && ret != AVERROR(EAGAIN))
+        return ret;
+    if (ret >= 0)
+        *got_frame = 1;
+
+    return 0;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFrameSideData *sd;
+    SplitCaptionsContext *s = inlink->dst->priv;
+    AVFilterLink *outlink0 = inlink->dst->outputs[0];
+    AVFilterLink *outlink1 = inlink->dst->outputs[1];
+    AVPacket *pkt = NULL;
+    AVFrame *sub_out = NULL;
+
+    int ret;
+
+    outlink0->format = inlink->format;
+
+    sd = av_frame_get_side_data(frame, AV_FRAME_DATA_A53_CC);
+
+    if (sd && (s->had_keyframe || frame->key_frame)) {
+        int got_output = 0;
+
+        s->had_keyframe = 1;
+        pkt = av_packet_alloc();
+        pkt->buf = av_buffer_ref(sd->buf);
+        if (!pkt->buf) {
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
+
+        pkt->data = pkt->buf->data;
+        pkt->size = pkt->buf->size;
+        pkt->pts  = av_rescale_q(frame->pts, inlink->time_base, AV_TIME_BASE_Q);
+
+        sub_out = ff_get_subtitles_buffer(outlink1, AV_SUBTITLE_FMT_ASS);
+        if (!sub_out) {
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
+
+        if ((ret = av_buffer_replace(&sub_out->subtitle_header, s->subtitle_header)) < 0)
+            goto fail;
+
+        ret = decode(s->cc_dec, sub_out, &got_output, pkt);
+
+        ////if (got_output) {
+        ////    av_log(inlink->dst, AV_LOG_INFO, "CC Packet PTS: %"PRId64" got_output: %d  out_frame_pts: %"PRId64"  out_sub_pts: %"PRId64"\n", pkt->pts, got_output, sub_out->pts, sub_out->subtitle_pts);
+        ////}
+
+        av_packet_free(&pkt);
+
+        if (ret < 0) {
+            av_log(inlink->dst, AV_LOG_ERROR, "Decode error: %d \n", ret);
+            goto fail;
+        }
+
+        if (got_output) {
+            sub_out->pts = frame->pts;
+            av_frame_free(&s->next_sub_frame);
+            s->next_sub_frame = sub_out;
+            sub_out = NULL;
+            s->new_frame = 1;
+            s->next_sub_frame->pts = frame->pts;
+
+            if ((ret = av_buffer_replace(&s->next_sub_frame->subtitle_header, s->subtitle_header)) < 0)
+                goto fail;
+
+            if (s->real_time && s->scatter_realtime_output) {
+                if (s->next_repetition_pts)
+                    s->next_sub_frame->pts = s->next_repetition_pts;
+
+                s->next_sub_frame->subtitle_timing.duration = ms_to_avtb(s->real_time_latency_msec);
+                s->next_repetition_pts = s->next_sub_frame->pts + av_rescale_q(s->real_time_latency_msec, ms_tb, inlink->time_base);
+            }
+
+            ret = request_sub_frame(outlink1);
+            if (ret < 0)
+                goto fail;
+        }
+    }
+
+    if (s->real_time && s->scatter_realtime_output && !s->new_frame && s->next_repetition_pts > 0 && frame->pts > s->next_repetition_pts) {
+        s->new_frame = 1;
+        s->next_sub_frame->pts = s->next_repetition_pts;
+        s->next_repetition_pts = s->next_sub_frame->pts + av_rescale_q(s->real_time_latency_msec, ms_tb, inlink->time_base);
+    }
+
+    if (!s->next_sub_frame) {
+        s->next_sub_frame = ff_get_subtitles_buffer(outlink1, outlink1->format);
+        if (!s->next_sub_frame) {
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
+
+        s->next_sub_frame->subtitle_timing.duration = ms_to_avtb(s->real_time_latency_msec);
+        s->next_sub_frame->pts = frame->pts;
+        s->new_frame = 1;
+
+        if ((ret = av_buffer_replace(&s->next_sub_frame->subtitle_header, s->subtitle_header)) < 0)
+            goto fail;
+    }
+
+    ret = ff_filter_frame(outlink0, frame);
+
+fail:
+    av_packet_free(&pkt);
+    av_frame_free(&sub_out);
+    return ret;
+}
+
+#define OFFSET(x) offsetof(SplitCaptionsContext, x)
+#define FLAGS (AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption split_cc_options[] = {
+    { "use_cc_styles",    "Emit closed caption style header", OFFSET(use_cc_styles),  AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, NULL },
+    { "real_time", "emit subtitle events as they are decoded for real-time display", OFFSET(real_time), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS },
+    { "real_time_latency_msec", "minimum elapsed time between emitting real-time subtitle events", OFFSET(real_time_latency_msec), AV_OPT_TYPE_INT, { .i64 = 200 }, 0, 500, FLAGS },
+    { "scatter_realtime_output", "scatter output events to a duration of real_time_latency_msec", OFFSET(scatter_realtime_output), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS },
+    { "data_field", "select data field", OFFSET(data_field), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1, FLAGS, "data_field" },
+    { "auto",   "pick first one that appears", 0, AV_OPT_TYPE_CONST, { .i64 =-1 }, 0, 0, FLAGS, "data_field" },
+    { "first",  NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, FLAGS, "data_field" },
+    { "second", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, FLAGS, "data_field" },
+    { NULL },
+};
+
+AVFILTER_DEFINE_CLASS(split_cc);
+
+static const AVFilterPad inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_VIDEO,
+        .filter_frame = filter_frame,
+        .config_props = config_input,
+    },
+};
+
+static const AVFilterPad outputs[] = {
+    {
+        .name          = "video_passthrough",
+        .type          = AVMEDIA_TYPE_VIDEO,
+        .config_props  = config_video_output,
+    },
+    {
+        .name          = "subtitles",
+        .type          = AVMEDIA_TYPE_SUBTITLE,
+        .request_frame = request_sub_frame,
+        .config_props  = config_sub_output,
+    },
+};
+
+const AVFilter ff_sf_splitcc = {
+    .name           = "splitcc",
+    .description    = NULL_IF_CONFIG_SMALL("Extract closed-caption (A53) data from video as subtitle stream."),
+    .init           = init,
+    .uninit         = uninit,
+    .priv_size      = sizeof(SplitCaptionsContext),
+    .priv_class     = &split_cc_class,
+    .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_QUERY_FUNC(query_formats),
+};
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v2 17/26] avfilter/graphicsub2text: Add new graphicsub2text filter (OCR)
  2022-01-20  2:48 ` [FFmpeg-devel] [PATCH v2 00/26] Subtitle Filtering 2022 ffmpegagent
                     ` (15 preceding siblings ...)
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 16/26] avfilter/splitcc: Add splitcc filter for closed caption handling ffmpegagent
@ 2022-01-20  2:48   ` ffmpegagent
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 18/26] avfilter/subscale: Add filter for scaling and/or re-arranging graphical subtitles ffmpegagent
                     ` (9 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  2:48 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 configure                        |    1 +
 doc/filters.texi                 |   55 ++
 libavfilter/Makefile             |    2 +
 libavfilter/allfilters.c         |    1 +
 libavfilter/sf_graphicsub2text.c | 1132 ++++++++++++++++++++++++++++++
 5 files changed, 1191 insertions(+)
 create mode 100644 libavfilter/sf_graphicsub2text.c

diff --git a/configure b/configure
index c1d2bc41c2..ee7afffb05 100755
--- a/configure
+++ b/configure
@@ -3665,6 +3665,7 @@ frei0r_filter_deps="frei0r"
 frei0r_src_filter_deps="frei0r"
 fspp_filter_deps="gpl"
 gblur_vulkan_filter_deps="vulkan spirv_compiler"
+graphicsub2text_filter_deps="libtesseract"
 hflip_vulkan_filter_deps="vulkan spirv_compiler"
 histeq_filter_deps="gpl"
 hqdn3d_filter_deps="gpl"
diff --git a/doc/filters.texi b/doc/filters.texi
index 265a267e9d..9311714f82 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -25897,6 +25897,61 @@ ffmpeg -i "https://streams.videolan.org/ffmpeg/mkv_subtitles.mkv" -filter_comple
 @end example
 @end itemize
 
+@section graphicsub2text
+
+Converts graphic subtitles to text subtitles by performing OCR.
+
+For this filter to be available, ffmpeg needs to be compiled with libtesseract (see https://github.com/tesseract-ocr/tesseract).
+Language models need to be downloaded from https://github.com/tesseract-ocr/tessdata and put into as subfolder named 'tessdata' or into a folder specified via the environment variable 'TESSDATA_PREFIX'.
+The path can also be specified via filter option (see below).
+
+Note: These models are including the data for both OCR modes.
+
+Inputs:
+- 0: Subtitles [bitmap]
+
+Outputs:
+- 0: Subtitles [text]
+
+It accepts the following parameters:
+
+@table @option
+@item ocr_mode
+The character recognition mode to use.
+
+Supported OCR modes are:
+
+@table @var
+@item 0, tesseract
+This is the classic libtesseract operation mode. It is fast but less accurate than LSTM.
+@item 1, lstm
+Newer OCR implementation based on ML models. Provides usually better results, requires more processing resources.
+@item 2, both
+Use a combination of both modes.
+@end table
+
+@item tessdata_path
+The path to a folder containing the language models to be used.
+
+@item language
+The recognition language. It needs to match the first three characters of a  language model file in the tessdata path.
+
+@end table
+
+
+@subsection Examples
+
+@itemize
+@item
+Convert DVB graphic subtitles to ASS (text) subtitles
+
+Note: For this to work, you need to have the data file 'eng.traineddata' in a 'tessdata' subfolder (see above).
+@example
+ffmpeg -loglevel verbose -i "https://streams.videolan.org/streams/ts/video_subs_ttxt%2Bdvbsub.ts" -filter_complex "[0:13]graphicsub2text=delay_when_no_duration=1" -c:s ass -y output.mkv
+@end example
+@end itemize
+
+
 @section graphicsub2video
 
 Renders graphic subtitles as video frames.
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index c6a4a4f5ae..ead3e38507 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -299,6 +299,8 @@ OBJS-$(CONFIG_GBLUR_VULKAN_FILTER)           += vf_gblur_vulkan.o vulkan.o vulka
 OBJS-$(CONFIG_GEQ_FILTER)                    += vf_geq.o
 OBJS-$(CONFIG_GRADFUN_FILTER)                += vf_gradfun.o
 OBJS-$(CONFIG_GRAPHICSUB2VIDEO_FILTER)       += vf_overlaygraphicsubs.o framesync.o
+OBJS-$(CONFIG_GRAPHICSUB2TEXT_FILTER)        += sf_graphicsub2text.o
+OBJS-$(CONFIG_GRAPHICSUB2VIDEO_FILTER)       += vf_overlaygraphicsubs.o framesync.o
 OBJS-$(CONFIG_GRAPHMONITOR_FILTER)           += f_graphmonitor.o
 OBJS-$(CONFIG_GRAYWORLD_FILTER)              += vf_grayworld.o
 OBJS-$(CONFIG_GREYEDGE_FILTER)               += vf_colorconstancy.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 50498e8ec4..34576016ce 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -548,6 +548,7 @@ extern const AVFilter ff_avf_showwaves;
 extern const AVFilter ff_avf_showwavespic;
 extern const AVFilter ff_vaf_spectrumsynth;
 extern const AVFilter ff_sf_censor;
+extern const AVFilter ff_sf_graphicsub2text;
 extern const AVFilter ff_sf_showspeaker;
 extern const AVFilter ff_sf_splitcc;
 extern const AVFilter ff_sf_stripstyles;
diff --git a/libavfilter/sf_graphicsub2text.c b/libavfilter/sf_graphicsub2text.c
new file mode 100644
index 0000000000..9b413d314e
--- /dev/null
+++ b/libavfilter/sf_graphicsub2text.c
@@ -0,0 +1,1132 @@
+/*
+ * 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
+ * subtitle filter to convert graphical subs to text subs via OCR
+ */
+
+#include <tesseract/capi.h>
+#include <libavutil/ass_internal.h>
+
+#include "drawutils.h"
+#include "libavutil/opt.h"
+#include "subtitles.h"
+
+#include "libavcodec/elbg.h"
+
+enum {
+    RFLAGS_NONE         = 0,
+    RFLAGS_HALIGN       = 1 << 0,
+    RFLAGS_VALIGN       = 1 << 1,
+    RFLAGS_FBOLD        = 1 << 2,
+    RFLAGS_FITALIC      = 1 << 3,
+    RFLAGS_FUNDERLINE   = 1 << 4,
+    RFLAGS_FONT         = 1 << 5,
+    RFLAGS_FONTSIZE     = 1 << 6,
+    RFLAGS_COLOR        = 1 << 7,
+    RFLAGS_OUTLINECOLOR = 1 << 8,
+    RFLAGS_ALL = RFLAGS_HALIGN | RFLAGS_VALIGN | RFLAGS_FBOLD | RFLAGS_FITALIC | RFLAGS_FUNDERLINE |
+                RFLAGS_FONT | RFLAGS_FONTSIZE | RFLAGS_COLOR | RFLAGS_OUTLINECOLOR,
+};
+
+typedef struct SubOcrContext {
+    const AVClass *class;
+    int w, h;
+
+    TessBaseAPI *tapi;
+    TessOcrEngineMode ocr_mode;
+    char *tessdata_path;
+    char *language;
+    int preprocess_images;
+    int dump_bitmaps;
+    int delay_when_no_duration;
+    int recognize;
+    double font_size_factor;
+
+    int readorder_counter;
+
+    AVFrame *pending_frame;
+    AVBufferRef *subtitle_header;
+    AVBPrint buffer;
+
+    // Color Quantization Fields
+    struct ELBGContext *ctx;
+    AVLFG lfg;
+    int *codeword;
+    int *codeword_closest_codebook_idxs;
+    int *codebook;
+    int r_idx, g_idx, b_idx, a_idx;
+    int64_t last_subtitle_pts;
+} SubOcrContext;
+
+typedef struct OcrImageProps {
+    int background_color_index;
+    int fill_color_index;
+
+} OcrImageProps;
+
+static int64_t ms_to_avtb(int64_t ms)
+{
+    return av_rescale_q(ms, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q);
+}
+
+static int create_ass_header(AVFilterContext* ctx)
+{
+    SubOcrContext* s = ctx->priv;
+
+    if (!(s->w && s->h)) {
+        av_log(ctx, AV_LOG_WARNING, "create_ass_header: no width and height specified!\n");
+        s->w = ASS_DEFAULT_PLAYRESX;
+        s->h = ASS_DEFAULT_PLAYRESY;
+    }
+
+    char* subtitle_header_text = avpriv_ass_get_subtitle_header_full(s->w, s->h, ASS_DEFAULT_FONT, ASS_DEFAULT_FONT_SIZE,
+        ASS_DEFAULT_COLOR, ASS_DEFAULT_COLOR, ASS_DEFAULT_BACK_COLOR, ASS_DEFAULT_BACK_COLOR, ASS_DEFAULT_BOLD,
+        ASS_DEFAULT_ITALIC, ASS_DEFAULT_UNDERLINE, ASS_DEFAULT_BORDERSTYLE, ASS_DEFAULT_ALIGNMENT, 0);
+
+    if (!subtitle_header_text)
+        return AVERROR(ENOMEM);
+
+    s->subtitle_header = av_buffer_create((uint8_t*)subtitle_header_text, strlen(subtitle_header_text) + 1, NULL, NULL, 0);
+
+    if (!s->subtitle_header)
+        return AVERROR(ENOMEM);
+
+    return 0;
+}
+
+static int init(AVFilterContext *ctx)
+{
+    SubOcrContext *s = ctx->priv;
+    const char* tver = TessVersion();
+    uint8_t rgba_map[4];
+    int ret;
+
+    s->tapi = TessBaseAPICreate();
+
+    if (!s->tapi || !tver || !strlen(tver)) {
+        av_log(ctx, AV_LOG_ERROR, "Failed to access libtesseract\n");
+        return AVERROR(ENOSYS);
+    }
+
+    av_log(ctx, AV_LOG_VERBOSE, "Initializing libtesseract, version: %s\n", tver);
+
+    ret = TessBaseAPIInit4(s->tapi, s->tessdata_path, s->language, s->ocr_mode, NULL, 0, NULL, NULL, 0, 1);
+    if (ret < 0 ) {
+        av_log(ctx, AV_LOG_ERROR, "Failed to initialize libtesseract. Error: %d\n", ret);
+        return AVERROR(ENOSYS);
+    }
+
+    ret = TessBaseAPISetVariable(s->tapi, "tessedit_char_blacklist", "|");
+    if (ret < 0 ) {
+        av_log(ctx, AV_LOG_ERROR, "Failed to set 'tessedit_char_blacklist'. Error: %d\n", ret);
+        return AVERROR(EINVAL);
+    }
+
+    av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
+
+    ff_fill_rgba_map(&rgba_map[0], AV_PIX_FMT_RGB32);
+
+    s->r_idx = rgba_map[0]; // R
+    s->g_idx = rgba_map[1]; // G
+    s->b_idx = rgba_map[2]; // B
+    s->a_idx = rgba_map[3]; // A
+
+    av_lfg_init(&s->lfg, 123456789);
+
+    return 0;
+}
+
+static void uninit(AVFilterContext *ctx)
+{
+    SubOcrContext *s = ctx->priv;
+
+    av_buffer_unref(&s->subtitle_header);
+    av_bprint_finalize(&s->buffer, NULL);
+
+    if (s->tapi) {
+        TessBaseAPIEnd(s->tapi);
+        TessBaseAPIDelete(s->tapi);
+    }
+
+    avpriv_elbg_free(&s->ctx);
+}
+
+static int query_formats(AVFilterContext *ctx)
+{
+    AVFilterFormats *formats, *formats2;
+    AVFilterLink *inlink = ctx->inputs[0];
+    AVFilterLink *outlink = ctx->outputs[0];
+    static const enum AVSubtitleType in_fmts[] = { AV_SUBTITLE_FMT_BITMAP, AV_SUBTITLE_FMT_NONE };
+    static const enum AVSubtitleType out_fmts[] = { AV_SUBTITLE_FMT_ASS, AV_SUBTITLE_FMT_NONE };
+    int ret;
+
+    /* set input format */
+    formats = ff_make_format_list(in_fmts);
+    if ((ret = ff_formats_ref(formats, &inlink->outcfg.formats)) < 0)
+        return ret;
+
+    /* set output format */
+    formats2 = ff_make_format_list(out_fmts);
+    if ((ret = ff_formats_ref(formats2, &outlink->incfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int config_input(AVFilterLink *inlink)
+{
+    AVFilterContext *ctx = inlink->dst;
+    SubOcrContext *s = ctx->priv;
+
+    if (s->w <= 0 || s->h <= 0) {
+        s->w = inlink->w;
+        s->h = inlink->h;
+    }
+
+    return create_ass_header(ctx);
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    AVFilterLink *inlink = outlink->src->inputs[0];
+    const AVFilterContext *ctx  = outlink->src;
+    SubOcrContext *s = ctx->priv;
+
+    outlink->format = AV_SUBTITLE_FMT_ASS;
+    outlink->w = s->w;
+    outlink->h = s->h;
+    outlink->time_base = inlink->time_base;
+    outlink->frame_rate = inlink->frame_rate;
+
+    return 0;
+}
+
+static void free_subtitle_area(AVSubtitleArea *area)
+{
+    for (unsigned n = 0; n < FF_ARRAY_ELEMS(area->buf); n++)
+        av_buffer_unref(&area->buf[n]);
+
+    av_freep(&area->text);
+    av_freep(&area->ass);
+    av_free(area);
+
+}
+
+static AVSubtitleArea *copy_subtitle_area(const AVSubtitleArea *src)
+{
+    AVSubtitleArea *dst = av_mallocz(sizeof(AVSubtitleArea));
+
+    if (!dst)
+        return NULL;
+
+    dst->x         =  src->x;
+    dst->y         =  src->y;
+    dst->w         =  src->w;
+    dst->h         =  src->h;
+    dst->nb_colors =  src->nb_colors;
+    dst->type      =  src->type;
+    dst->flags     =  src->flags;
+
+    for (unsigned i = 0; i < AV_NUM_BUFFER_POINTERS; i++) {
+        if (src->h > 0 && src->w > 0 && src->buf[i]) {
+            dst->buf[0] = av_buffer_ref(src->buf[i]);
+            if (!dst->buf[i])
+                return NULL;
+
+            const int ret = av_buffer_make_writable(&dst->buf[i]);
+            if (ret < 0)
+                return NULL;
+
+            dst->linesize[i] = src->linesize[i];
+        }
+    }
+
+    memcpy(&dst->pal[0], &src->pal[0], sizeof(src->pal[0]) * 256);
+
+
+    return dst;
+}
+
+static int quantize_image_colors(SubOcrContext *const s, AVSubtitleArea *subtitle_area)
+{
+    const int num_quantized_colors = 3;
+    int k, ret;
+    const int codeword_length = subtitle_area->w * subtitle_area->h;
+    uint8_t *src_data = subtitle_area->buf[0]->data;
+
+    if (subtitle_area->nb_colors <= num_quantized_colors) {
+        av_log(s, AV_LOG_DEBUG, "No need to quantize colors. Color count: %d\n", subtitle_area->nb_colors);
+        return 0;
+    }
+
+    // Convert palette to grayscale
+    for (int i = 0; i < subtitle_area->nb_colors; i++) {
+        uint8_t *color        = (uint8_t *)&subtitle_area->pal[i];
+        const uint8_t average = (uint8_t)(((int)color[s->r_idx] + color[s->g_idx] + color[s->b_idx]) / 3);
+        color[s->b_idx]       = average;
+        color[s->g_idx]       = average;
+        color[s->r_idx]       = average;
+    }
+
+    /* Re-Initialize */
+    s->codeword = av_realloc_f(s->codeword, codeword_length, 4 * sizeof(*s->codeword));
+    if (!s->codeword)
+        return AVERROR(ENOMEM);
+
+    s->codeword_closest_codebook_idxs = av_realloc_f(s->codeword_closest_codebook_idxs,
+        codeword_length, sizeof(*s->codeword_closest_codebook_idxs));
+    if (!s->codeword_closest_codebook_idxs)
+        return AVERROR(ENOMEM);
+
+    s->codebook = av_realloc_f(s->codebook, num_quantized_colors, 4 * sizeof(*s->codebook));
+    if (!s->codebook)
+        return AVERROR(ENOMEM);
+
+    /* build the codeword */
+    k = 0;
+    for (int i = 0; i < subtitle_area->h; i++) {
+        uint8_t *p = src_data;
+        for (int j = 0; j < subtitle_area->w; j++) {
+            const uint8_t *color = (uint8_t *)&subtitle_area->pal[*p];
+            s->codeword[k++] = color[s->b_idx];
+            s->codeword[k++] = color[s->g_idx];
+            s->codeword[k++] = color[s->r_idx];
+            s->codeword[k++] = color[s->a_idx];
+            p++;
+        }
+        src_data += subtitle_area->linesize[0];
+    }
+
+    /* compute the codebook */
+    ret = avpriv_elbg_do(&s->ctx, s->codeword, 4, codeword_length, s->codebook,
+        num_quantized_colors, 1, s->codeword_closest_codebook_idxs, &s->lfg, 0);
+    if (ret < 0)
+        return ret;
+
+    /* Write Palette */
+    for (int i = 0; i < num_quantized_colors; i++) {
+        subtitle_area->pal[i] = s->codebook[i*4+3] << 24  |
+                    (s->codebook[i*4+2] << 16) |
+                    (s->codebook[i*4+1] <<  8) |
+                    (s->codebook[i*4  ] <<  0);
+    }
+
+
+    av_log(s, AV_LOG_DEBUG, "Quantized colors from %d to %d\n", subtitle_area->nb_colors, num_quantized_colors);
+
+    subtitle_area->nb_colors = num_quantized_colors;
+    src_data = subtitle_area->buf[0]->data;
+
+    /* Write Image */
+    k = 0;
+    for (int i = 0; i < subtitle_area->h; i++) {
+        uint8_t *p = src_data;
+        for (int j = 0; j < subtitle_area->w; j++, p++) {
+            p[0] = (uint8_t)s->codeword_closest_codebook_idxs[k++];
+        }
+
+        src_data += subtitle_area->linesize[0];
+    }
+
+    return ret;
+}
+
+#define MEASURE_LINE_COUNT 6
+
+static uint8_t get_background_color_index(SubOcrContext *const s, const AVSubtitleArea *subtitle_area)
+{
+    const int linesize = subtitle_area->linesize[0];
+    int index_counts[256] = {0};
+    const unsigned int line_offsets[MEASURE_LINE_COUNT] = {
+        0,
+        linesize,
+        2 * linesize,
+        (subtitle_area->h - 3) * linesize,
+        (subtitle_area->h - 2) * linesize,
+        (subtitle_area->h - 1) * linesize
+    };
+
+    const uint8_t *src_data = subtitle_area->buf[0]->data;
+    const uint8_t tl = src_data[0];
+    const uint8_t tr = src_data[subtitle_area->w - 1];
+    const uint8_t bl = src_data[(subtitle_area->h - 1) * linesize + 0];
+    const uint8_t br = src_data[(subtitle_area->h - 1) * linesize + subtitle_area->w - 1];
+    uint8_t max_index = 0;
+    int max_count;
+
+    // When all corner pixels are equal, assume that as background color
+    if (tl == tr == bl == br || subtitle_area->h < 6)
+        return tl;
+
+    for (unsigned int i = 0; i < MEASURE_LINE_COUNT; i++) {
+        uint8_t *p = subtitle_area->buf[0]->data + line_offsets[i];
+        for (int k = 0; k < subtitle_area->w; k++)
+            index_counts[p[k]]++;
+    }
+
+    max_count = index_counts[0];
+
+    for (uint8_t i = 1; i < subtitle_area->nb_colors; i++) {
+        if (index_counts[i] > max_count) {
+            max_count = index_counts[i];
+            max_index = i;
+        }
+    }
+
+    return max_index;
+}
+
+static uint8_t get_text_color_index(SubOcrContext *const s, const AVSubtitleArea *subtitle_area, const uint8_t bg_color_index, uint8_t *outline_color_index)
+{
+    const int linesize = subtitle_area->linesize[0];
+    int index_counts[256] = {0};
+    uint8_t last_index = bg_color_index;
+    int max_count, min_req_count;
+    uint8_t max_index = 0;
+
+    for (int i = 3; i < subtitle_area->h - 3; i += 5) {
+        const uint8_t *p = subtitle_area->buf[0]->data + ((ptrdiff_t)linesize * i);
+        for (int k = 0; k < subtitle_area->w; k++) {
+            const uint8_t cur_index = p[k];
+
+            // When color hasn't changed, continue
+            if (cur_index == last_index)
+                continue;
+
+            if (cur_index != bg_color_index)
+                index_counts[cur_index]++;
+
+            last_index = cur_index;
+        }
+    }
+
+    max_count = index_counts[0];
+
+    for (uint8_t i = 1; i < subtitle_area->nb_colors; i++) {
+        if (index_counts[i] > max_count) {
+            max_count = index_counts[i];
+            max_index = i;
+        }
+    }
+
+    min_req_count = max_count / 3;
+
+    for (uint8_t i = 1; i < subtitle_area->nb_colors; i++) {
+        if (index_counts[i] < min_req_count)
+            index_counts[i] = 0;
+    }
+
+    *outline_color_index = max_index;
+
+    index_counts[max_index] = 0;
+    max_count = 0;
+
+    for (uint8_t i = 0; i < subtitle_area->nb_colors; i++) {
+        if (index_counts[i] > max_count) {
+            max_count = index_counts[i];
+            max_index = i;
+        }
+    }
+
+    if (*outline_color_index == max_index)
+        *outline_color_index = 255;
+
+    return max_index;
+}
+
+static void make_image_binary(SubOcrContext *const s, AVSubtitleArea *subtitle_area, const uint8_t text_color_index)
+{
+    for (int i = 0; i < subtitle_area->nb_colors; i++) {
+
+        if (i != text_color_index)
+            subtitle_area->pal[i] = 0xffffffff;
+        else
+            subtitle_area->pal[i] = 0xff000000;
+    }
+}
+
+static int get_crop_region(SubOcrContext *const s, const AVSubtitleArea *subtitle_area, uint8_t text_color_index, int *x, int *y, int *w, int *h)
+{
+    const int linesize = subtitle_area->linesize[0];
+    int max_y = 0, max_x = 0;
+    int min_y = subtitle_area->h - 1, min_x = subtitle_area->w - 1;
+
+    for (int i = 0; i < subtitle_area->h; i += 3) {
+        const uint8_t *p = subtitle_area->buf[0]->data + ((ptrdiff_t)linesize * i);
+        for (int k = 0; k < subtitle_area->w; k += 2) {
+            if (p[k] == text_color_index) {
+                min_y = FFMIN(min_y, i);
+                min_x = FFMIN(min_x, k);
+                max_y = FFMAX(max_y, i);
+                max_x = FFMAX(max_x, k);
+            }
+        }
+    }
+
+    if (max_y <= min_y || max_x <= min_x) {
+        av_log(s, AV_LOG_WARNING, "Unable to detect crop region\n");
+        *x = 0;
+        *y = 0;
+        *w = subtitle_area->w;
+        *h = subtitle_area->h;
+    }    else {
+        *x = FFMAX(min_x - 10, 0);
+        *y = FFMAX(min_y - 10, 0);
+        *w = FFMIN(max_x + 10 - *x, (subtitle_area->w - *x));
+        *h = FFMIN(max_y + 10 - *y, (subtitle_area->h - *y));
+    }
+
+    return 0;
+}
+
+static int crop_area_bitmap(SubOcrContext *const s, AVSubtitleArea *subtitle_area, int x, int y, int w, int h)
+{
+    const int linesize = subtitle_area->linesize[0];
+    AVBufferRef *dst = av_buffer_allocz(h * w);
+    uint8_t *d;
+
+    if (!dst)
+        return AVERROR(ENOMEM);
+
+    d = dst->data;
+
+    for (int i = y; i < y + h; i++) {
+        const uint8_t *p = subtitle_area->buf[0]->data + ((ptrdiff_t)linesize * i);
+        for (int k = x; k < x + w; k++) {
+            *d = p[k];
+            d++;
+        }
+    }
+
+    subtitle_area->w = w;
+    subtitle_area->h = h;
+    subtitle_area->x += x;
+    subtitle_area->y += y;
+    subtitle_area->linesize[0] = w;
+    av_buffer_replace(&subtitle_area->buf[0], dst);
+
+    av_buffer_unref(&dst);
+    return 0;
+}
+
+#define R 0
+#define G 1
+#define B 2
+#define A 3
+
+static int print_code(AVBPrint *buf, int in_code, const char *fmt, ...)
+{
+    va_list vl;
+
+    if (!in_code)
+        av_bprint_chars(buf, '{', 1);
+
+    va_start(vl, fmt);
+    av_vbprintf(buf, fmt, vl);
+    va_end(vl);
+
+    return 1;
+}
+
+static int end_code(AVBPrint *buf, int in_code)
+{
+    if (in_code)
+        av_bprint_chars(buf, '}', 1);
+    return 0;
+}
+
+static uint8_t* create_grayscale_image(AVFilterContext *ctx, AVSubtitleArea *area, int invert)
+{
+    uint8_t gray_pal[256];
+    const size_t img_size = area->buf[0]->size;
+    const uint8_t* img    = area->buf[0]->data;
+    uint8_t* gs_img       = av_malloc(img_size);
+
+    if (!gs_img)
+        return NULL;
+
+    for (unsigned i = 0; i < 256; i++) {
+        const uint8_t *col = (uint8_t*)&area->pal[i];
+        const int val      = (int)col[3] * FFMAX3(col[0], col[1], col[2]);
+        gray_pal[i]        = (uint8_t)(val >> 8);
+    }
+
+    if (invert)
+        for (unsigned i = 0; i < img_size; i++)
+            gs_img[i]   = 255 - gray_pal[img[i]];
+    else
+        for (unsigned i = 0; i < img_size; i++)
+            gs_img[i]   = gray_pal[img[i]];
+
+    return gs_img;
+}
+
+static uint8_t* create_bitmap_image(AVFilterContext *ctx, AVSubtitleArea *area, const uint8_t text_color_index)
+{
+    const size_t img_size = area->buf[0]->size;
+    const uint8_t* img    = area->buf[0]->data;
+    uint8_t* gs_img       = av_malloc(img_size);
+
+    if (!gs_img)
+        return NULL;
+
+    for (unsigned i = 0; i < img_size; i++) {
+        if (img[i] == text_color_index)
+            gs_img[i]   = 0;
+        else
+            gs_img[i]   = 255;
+    }
+
+    return gs_img;
+}
+
+static void png_save(AVFilterContext *ctx, const char *filename, AVSubtitleArea *area)
+{
+    int x, y;
+    int v;
+    FILE *f;
+    char fname[40];
+    const uint8_t *data = area->buf[0]->data;
+
+    snprintf(fname, sizeof(fname), "%s.ppm", filename);
+
+    f = fopen(fname, "wb");
+    if (!f) {
+        perror(fname);
+        return;
+    }
+    fprintf(f, "P6\n"
+            "%d %d\n"
+            "%d\n",
+            area->w, area->h, 255);
+    for(y = 0; y < area->h; y++) {
+        for(x = 0; x < area->w; x++) {
+            const uint8_t index = data[y * area->linesize[0] + x];
+            v = (int)area->pal[index];
+            putc(v >> 16 & 0xff, f);
+            putc(v >> 8 & 0xff, f);
+            putc(v >> 0 & 0xff, f);
+        }
+    }
+
+    fclose(f);
+}
+
+static int get_max_index(int score[256])
+{
+    int max_val = 0, max_index = 0;
+
+    for (int i = 0; i < 256; i++) {
+        if (score[i] > max_val) {
+            max_val = score[i];
+            max_index = i;
+        }
+    }
+
+    return max_index;
+}
+
+static int get_word_colors(AVFilterContext *ctx, TessResultIterator* ri, const AVSubtitleArea* area, const AVSubtitleArea* original_area,
+                           uint8_t bg_color_index, uint8_t text_color_index, uint8_t outline_color_index,
+                           uint32_t* bg_color, uint32_t* text_color, uint32_t* outline_color)
+{
+    int left = 0, top = 0, right = 0, bottom = 0, ret;
+    int bg_score[256] = {0}, text_score[256] = {0}, outline_score[256] = {0};
+    int max_index;
+
+    ret = TessPageIteratorBoundingBox((TessPageIterator*)ri, RIL_WORD, &left, &top, &right, &bottom);
+    if (ret < 0) {
+        av_log(ctx, AV_LOG_WARNING, "get_word_colors: IteratorBoundingBox failed: %d\n", ret);
+        return  ret;
+    }
+
+    if (left >= area->w || right >= area->w || top >= area->h || bottom >= area->h) {
+        av_log(ctx, AV_LOG_WARNING, "get_word_colors: word bounding box (l: %d, t: %d r: %d, b: %d) out of image bounds (%dx%d)\n", left,top, right, bottom, area->w, area->h);
+        return  AVERROR(EINVAL);
+    }
+
+    for (int y = top; y < bottom; y += 3) {
+        uint8_t *p = area->buf[0]->data + (y * area->linesize[0]) + left;
+        uint8_t *porig = original_area->buf[0]->data + (y * original_area->linesize[0]) + left;
+        uint8_t current_index = 255;
+
+        for (int x = left; x < right; x++, p++, porig++) {
+
+            if (*p == current_index) {
+                if (*p == bg_color_index)
+                    bg_score[*porig]++;
+                if (*p == text_color_index)
+                    text_score[*porig]++;
+                if (*p == outline_color_index)
+                    outline_score[*porig]++;
+            }
+
+            current_index = *p;
+        }
+    }
+
+    max_index = get_max_index(bg_score);
+    if (bg_score[max_index] > 0)
+        *bg_color = original_area->pal[max_index];
+
+    max_index = get_max_index(text_score);
+    if (text_score[max_index] > 0)
+        *text_color = original_area->pal[max_index];
+
+    max_index = get_max_index(outline_score);
+    if (outline_score[max_index] > 0)
+        *outline_color = original_area->pal[max_index];
+
+    return 0;
+}
+
+static int convert_area(AVFilterContext *ctx, AVSubtitleArea *area, const AVFrame *frame, const unsigned area_index, int *margin_v)
+{
+    SubOcrContext *s = ctx->priv;
+    char *ocr_text = NULL;
+    int ret = 0;
+    uint8_t *gs_img;
+    uint8_t bg_color_index;
+    uint8_t text_color_index = 255;
+    uint8_t outline_color_index = 255;
+    char filename[32];
+    AVSubtitleArea *original_area = copy_subtitle_area(area);
+
+    if (!original_area)
+        return AVERROR(ENOMEM);
+
+    if (area->w < 6 || area->h < 6) {
+        area->ass = NULL;
+        goto exit;
+    }
+
+    if (s->dump_bitmaps) {
+        snprintf(filename, sizeof(filename), "graphicsub2text_%"PRId64"_%d_original", frame->subtitle_timing.start_pts, area_index);
+        png_save(ctx, filename, area);
+    }
+
+    if (s->preprocess_images) {
+        ret = quantize_image_colors(s, area);
+        if (ret < 0)
+            goto exit;
+        if (s->dump_bitmaps && original_area->nb_colors != area->nb_colors) {
+            snprintf(filename, sizeof(filename), "graphicsub2text_%"PRId64"_%d_quantized", frame->subtitle_timing.start_pts, area_index);
+            png_save(ctx, filename, area);
+        }
+    }
+
+    bg_color_index = get_background_color_index(s, area);
+
+    if (s->preprocess_images) {
+        int x, y, w, h;
+
+        for (int i = 0; i < area->nb_colors; ++i) {
+            av_log(s, AV_LOG_DEBUG, "Color #%d: %0.8X\n", i, area->pal[i]);
+        }
+
+        text_color_index = get_text_color_index(s, area, bg_color_index, &outline_color_index);
+
+        get_crop_region(s, area, text_color_index, &x, &y, &w, &h);
+
+        if ((ret = crop_area_bitmap(s, area, x, y, w, h) < 0))
+            goto exit;
+
+        if ((ret = crop_area_bitmap(s, original_area, x, y, w, h) < 0))
+            goto exit;
+
+        make_image_binary(s, area, text_color_index);
+
+        if (s->dump_bitmaps) {
+            snprintf(filename, sizeof(filename), "graphicsub2text_%"PRId64"_%d_preprocessed", frame->subtitle_timing.start_pts, area_index);
+            png_save(ctx, filename, area);
+        }
+
+        gs_img = create_bitmap_image(ctx, area, text_color_index);
+    } else
+        gs_img = create_grayscale_image(ctx, area, 1);
+
+    if (!gs_img) {
+        ret = AVERROR(ENOMEM);
+        goto exit;
+    }
+
+    area->type = AV_SUBTITLE_FMT_ASS;
+    TessBaseAPISetImage(s->tapi, gs_img, area->w, area->h, 1, area->linesize[0]);
+
+    TessBaseAPISetSourceResolution(s->tapi, 72);
+
+    ret = TessBaseAPIRecognize(s->tapi, NULL);
+    if (ret == 0)
+        ocr_text = TessBaseAPIGetUTF8Text(s->tapi);
+
+    if (!ocr_text || !strlen(ocr_text)) {
+        av_log(ctx, AV_LOG_WARNING, "OCR didn't return a text. ret=%d\n", ret);
+        area->ass = NULL;
+
+        goto exit;
+    }
+
+    const size_t len = strlen(ocr_text);
+    if (len > 0 && ocr_text[len - 1] == '\n')
+        ocr_text[len - 1] = 0;
+
+    av_log(ctx, AV_LOG_VERBOSE, "OCR Result: %s\n", ocr_text);
+
+    area->ass = av_strdup(ocr_text);
+    TessDeleteText(ocr_text);
+
+    // End of simple recognition
+
+    if (s->recognize != RFLAGS_NONE) {
+        TessResultIterator* ri = 0;
+        const TessPageIteratorLevel level = RIL_WORD;
+        int cur_is_bold = 0, cur_is_italic = 0, cur_is_underlined = 0, cur_pointsize = 0;
+        uint32_t cur_text_color = 0, cur_bg_color = 0, cur_outline_color = 0;
+
+        char *cur_font_name = NULL;
+        int valign = 0; // 0: bottom, 4: top, 8 middle
+        int halign = 2; // 1: left, 2: center, 3: right
+        int in_code = 0;
+        double font_factor = (0.000666 * (s->h - 480) + 1) * s->font_size_factor;
+
+        av_freep(&area->ass);
+        av_bprint_clear(&s->buffer);
+
+        ri = TessBaseAPIGetIterator(s->tapi);
+
+        // Horizontal Alignment
+        if (s->w && s->recognize & RFLAGS_HALIGN) {
+            int left_margin = area->x;
+            int right_margin = s->w - area->x - area->w;
+            double relative_diff = ((double)left_margin - right_margin) / s->w;
+
+            if (FFABS(relative_diff) < 0.1)
+                halign = 2; // center
+            else if (relative_diff > 0)
+                halign = 3; // right
+            else
+                halign = 1; // left
+        }
+
+        // Vertical Alignment
+        if (s->h && frame->height && s->recognize & RFLAGS_VALIGN) {
+            int left = 0, top = 0, right = 0, bottom = 0;
+
+            TessPageIteratorBoundingBox((TessPageIterator*)ri, RIL_TEXTLINE, &left, &top, &right, &bottom);
+            av_log(s, AV_LOG_DEBUG, "RIL_TEXTLINE - TOP: %d  BOTTOM: %d HEIGHT: %d\n", top, bottom, bottom - top);
+
+            TessPageIteratorBoundingBox((TessPageIterator*)ri, RIL_BLOCK, &left, &top, &right, &bottom);
+
+            const int vertical_pos = area->y + area->h / 2;
+            if (vertical_pos < s->h / 3) {
+                *margin_v = area->y + top;
+                valign = 4;
+            }
+            else if (vertical_pos < s->h / 3 * 2) {
+                *margin_v = 0;
+                valign = 8;
+            } else {
+                *margin_v = frame->height - area->y - area->h;
+                valign = 0;
+            }
+        }
+
+        if (*margin_v < 0)
+            *margin_v = 0;
+
+        // Set alignment when not default (2)
+        if ((valign | halign) != 2)
+            in_code = print_code(&s->buffer, in_code, "\\a%d", valign | halign);
+
+        do {
+            int is_bold, is_italic, is_underlined, is_monospace, is_serif, is_smallcaps, pointsize, font_id;
+            char* word;
+            const char *font_name = TessResultIteratorWordFontAttributes(ri, &is_bold, &is_italic, &is_underlined, &is_monospace, &is_serif, &is_smallcaps, &pointsize, &font_id);
+            uint32_t text_color = 0, bg_color = 0, outline_color = 0;
+
+            if (cur_is_underlined && !is_underlined && s->recognize & RFLAGS_FUNDERLINE)
+                in_code = print_code(&s->buffer, in_code, "\\u0");
+
+            if (cur_is_bold && !is_bold && s->recognize & RFLAGS_FBOLD)
+                in_code = print_code(&s->buffer, in_code, "\\b0");
+
+            if (cur_is_italic && !is_italic && s->recognize & RFLAGS_FITALIC)
+                in_code = print_code(&s->buffer, in_code, "\\i0");
+
+
+            if (TessPageIteratorIsAtBeginningOf((TessPageIterator*)ri, RIL_TEXTLINE) && !TessPageIteratorIsAtBeginningOf((TessPageIterator*)ri, RIL_BLOCK)) {
+                in_code = end_code(&s->buffer, in_code);
+                av_bprintf(&s->buffer, "\\N");
+            }
+
+            if (get_word_colors(ctx, ri, area, original_area, bg_color_index, text_color_index, outline_color_index, &bg_color, &text_color, &outline_color) == 0) {
+
+                if (text_color > 0 && cur_text_color != text_color && s->recognize & RFLAGS_COLOR) {
+                    const uint8_t* tval = (uint8_t*)&text_color;
+                    const int color = (int)tval[R] << 16 | (int)tval[G] << 8 | tval[B];
+
+                    in_code = print_code(&s->buffer, in_code, "\\1c&H%0.6X&", color);
+                    if (tval[A] != 255)
+                        in_code = print_code(&s->buffer, in_code, "\\1a&H%0.2X&", 255 - tval[A]);
+                }
+
+                if (outline_color > 0 && cur_outline_color != outline_color && s->recognize & RFLAGS_OUTLINECOLOR) {
+                    const uint8_t* tval = (uint8_t*)&outline_color;
+                    const int color = (int)tval[R] << 16 | (int)tval[G] << 8 | tval[B];
+
+                    in_code = print_code(&s->buffer, in_code, "\\3c&H%0.6X&\\bord2", color);
+                    in_code = print_code(&s->buffer, in_code, "\\3a&H%0.2X&", FFMIN(255 - tval[A], 30));
+                }
+
+                cur_text_color = text_color;
+                cur_outline_color = outline_color;
+            }
+
+            if (font_name && strlen(font_name) && s->recognize & RFLAGS_FONT) {
+                if (!cur_font_name || !strlen(cur_font_name) || strcmp(cur_font_name, font_name) != 0) {
+                    char *sanitized_font_name = av_strireplace(font_name, "_", " ");
+                    if (!sanitized_font_name) {
+                        ret = AVERROR(ENOMEM);
+                        goto exit;
+                    }
+
+                    in_code = print_code(&s->buffer, in_code, "\\fn%s", sanitized_font_name);
+                    av_freep(&sanitized_font_name);
+
+                    if (cur_font_name)
+                        av_freep(&cur_font_name);
+                    cur_font_name = av_strdup(font_name);
+                    if (!cur_font_name) {
+                        ret = AVERROR(ENOMEM);
+                        goto exit;
+                    }
+                }
+            }
+
+            if (pointsize != cur_pointsize && s->recognize & RFLAGS_FONTSIZE) {
+                av_log(s, AV_LOG_DEBUG, "pointsize - pointsize: %d\n", pointsize);
+                in_code = print_code(&s->buffer, in_code, "\\fs%d", (int)(pointsize * font_factor));
+                cur_pointsize = pointsize;
+            }
+
+            if (is_italic && !cur_is_italic && s->recognize & RFLAGS_FITALIC)
+                in_code = print_code(&s->buffer, in_code, "\\i1");
+
+            if (is_bold && !cur_is_bold && s->recognize & RFLAGS_FBOLD)
+                in_code = print_code(&s->buffer, in_code, "\\b1");
+
+            if (is_underlined && !cur_is_underlined && s->recognize & RFLAGS_FUNDERLINE)
+                in_code = print_code(&s->buffer, in_code, "\\u1");
+
+            in_code = end_code(&s->buffer, in_code);
+
+            cur_is_underlined = is_underlined;
+            cur_is_bold = is_bold;
+            cur_is_italic = is_italic;
+
+            if (!TessPageIteratorIsAtBeginningOf((TessPageIterator*)ri, RIL_TEXTLINE))
+                av_bprint_chars(&s->buffer, ' ', 1);
+
+            word = TessResultIteratorGetUTF8Text(ri, level);
+            av_bprint_append_data(&s->buffer, word, strlen(word));
+            TessDeleteText(word);
+
+        } while (TessResultIteratorNext(ri, level));
+
+        if (!av_bprint_is_complete(&s->buffer))
+            ret = AVERROR(ENOMEM);
+        else {
+            av_log(ctx, AV_LOG_VERBOSE, "ASS Result: %s\n", s->buffer.str);
+            area->ass = av_strdup(s->buffer.str);
+        }
+
+        TessResultIteratorDelete(ri);
+        av_freep(&cur_font_name);
+    }
+
+exit:
+    free_subtitle_area(original_area);
+    av_freep(&gs_img);
+    av_buffer_unref(&area->buf[0]);
+    area->type = AV_SUBTITLE_FMT_ASS;
+
+    return ret;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFilterContext *ctx = inlink->dst;
+    SubOcrContext *s = ctx->priv;
+    AVFilterLink *outlink = inlink->dst->outputs[0];
+    int ret, frame_sent = 0;
+
+    if (s->pending_frame && !frame->repeat_sub) {
+        const int64_t pts_diff = frame->subtitle_timing.start_pts - s->pending_frame->subtitle_timing.start_pts;
+
+        if (pts_diff == 0) {
+            // This is just a repetition of the previous frame, ignore it
+            av_frame_free(&frame);
+            return 0;
+        }
+
+        s->pending_frame->subtitle_timing.duration = pts_diff;
+
+        if ((ret = av_buffer_replace(&s->pending_frame->subtitle_header, s->subtitle_header)) < 0)
+            return ret;
+
+        ret = ff_filter_frame(outlink, s->pending_frame);
+        s->pending_frame = NULL;
+        if (ret < 0)
+            return  ret;
+
+        frame_sent = 1;
+        s->last_subtitle_pts = frame->subtitle_timing.start_pts;
+    }
+
+    if (frame->repeat_sub) {
+        // Ignore repeated frame
+        av_frame_free(&frame);
+        return 0;
+    }
+
+    s->last_subtitle_pts = frame->subtitle_timing.start_pts;
+
+    ret = av_frame_make_writable(frame);
+
+    if (ret < 0) {
+        av_frame_free(&frame);
+        return ret;
+    }
+
+    frame->format = AV_SUBTITLE_FMT_ASS;
+
+    av_log(ctx, AV_LOG_VERBOSE, "filter_frame sub_pts: %"PRIu64", duration: %"PRIu64", num_areas: %d\n",
+        frame->subtitle_timing.start_pts, frame->subtitle_timing.duration, frame->num_subtitle_areas);
+
+    if (frame->num_subtitle_areas > 1 &&
+        frame->subtitle_areas[0]->y > frame->subtitle_areas[frame->num_subtitle_areas - 1]->y) {
+
+        for (unsigned i = 0; i < frame->num_subtitle_areas / 2; i++)
+            FFSWAP(AVSubtitleArea*, frame->subtitle_areas[i], frame->subtitle_areas[frame->num_subtitle_areas - i - 1]);
+    }
+
+    for (int i = 0; i < frame->num_subtitle_areas; i++) {
+        AVSubtitleArea *area = frame->subtitle_areas[i];
+        int margin_v = 0;
+
+        ret = convert_area(ctx, area, frame, i, &margin_v);
+        if (ret < 0)
+            return ret;
+
+        if (area->ass && area->ass[0] != '\0') {
+
+            const int layer = s->recognize ? i : 0;
+            char *tmp = area->ass;
+            area->ass = avpriv_ass_get_dialog_ex(s->readorder_counter++, layer, "Default", NULL, 0, 0, margin_v, tmp);
+            av_free(tmp);
+        }
+    }
+
+    // When decoders can't determine the end time, they are setting it either to UINT32_NAX
+    // or 30s (dvbsub).
+    if (s->delay_when_no_duration && frame->subtitle_timing.duration >= ms_to_avtb(29000)) {
+        // Can't send it without end time, wait for the next frame to determine the end_display time
+        s->pending_frame = frame;
+
+        if (frame_sent)
+            return 0;
+
+        // To keep all going, send an empty frame instead
+        frame = ff_get_subtitles_buffer(outlink, AV_SUBTITLE_FMT_ASS);
+        if (!frame)
+            return AVERROR(ENOMEM);
+
+        av_frame_copy_props(frame, s->pending_frame);
+        frame->subtitle_timing.start_pts = 0;
+        frame->subtitle_timing.duration = 1;
+        frame->repeat_sub = 1;
+    }
+
+    if ((ret = av_buffer_replace(&frame->subtitle_header, s->subtitle_header)) < 0)
+        return ret;
+
+    return ff_filter_frame(outlink, frame);
+}
+
+#define OFFSET(x) offsetof(SubOcrContext, x)
+#define FLAGS (AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption graphicsub2text_options[] = {
+    { "delay_when_no_duration", "delay output when duration is unknown", OFFSET(delay_when_no_duration), AV_OPT_TYPE_BOOL,   { .i64 = 0 },                         0,                  1,       FLAGS, NULL },
+    { "dump_bitmaps",           "save processed bitmaps as .ppm",        OFFSET(dump_bitmaps),           AV_OPT_TYPE_BOOL,   { .i64 = 0 },                         0,                  1,       FLAGS, NULL },
+    { "font_size_factor",       "font size adjustment factor",           OFFSET(font_size_factor),       AV_OPT_TYPE_DOUBLE, { .dbl = 1.0 },                       0.2,                5,       FLAGS, NULL },
+    { "language",               "ocr language",                          OFFSET(language),               AV_OPT_TYPE_STRING, { .str = "eng" },                     0,                  0,       FLAGS, NULL },
+    { "ocr_mode",               "set ocr mode",                          OFFSET(ocr_mode),               AV_OPT_TYPE_INT,    { .i64=OEM_TESSERACT_ONLY },          OEM_TESSERACT_ONLY, 2,       FLAGS, "ocr_mode" },
+    {   "tesseract",            "classic tesseract ocr",                 0,                              AV_OPT_TYPE_CONST,  { .i64=OEM_TESSERACT_ONLY },          0,                  0,       FLAGS, "ocr_mode" },
+    {   "lstm",                 "lstm (ML based)",                       0,                              AV_OPT_TYPE_CONST,  { .i64=OEM_LSTM_ONLY},                0,                  0,       FLAGS, "ocr_mode" },
+    {   "both",                 "use both models combined",              0,                              AV_OPT_TYPE_CONST,  { .i64=OEM_TESSERACT_LSTM_COMBINED }, 0,                  0,       FLAGS, "ocr_mode" },
+    { "preprocess_images",      "reduce colors, remove outlines",        OFFSET(preprocess_images),      AV_OPT_TYPE_BOOL,   { .i64 = 1 },                         0,                  1,       FLAGS, NULL },
+    { "recognize",              "detect fonts, styles and colors",       OFFSET(recognize),              AV_OPT_TYPE_FLAGS,  { .i64 = RFLAGS_ALL},                  0,                  INT_MAX, FLAGS, "reco_flags" },
+        { "none",         "no format detection",  0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_NONE         }, 0, 0, FLAGS, "reco_flags" },
+        { "halign",       "horizontal alignment", 0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_HALIGN       }, 0, 0, FLAGS, "reco_flags" },
+        { "valign",       "vertical alignment",   0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_VALIGN       }, 0, 0, FLAGS, "reco_flags" },
+        { "bold",         "font bold",            0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_FBOLD        }, 0, 0, FLAGS, "reco_flags" },
+        { "italic",       "font italic",          0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_FITALIC      }, 0, 0, FLAGS, "reco_flags" },
+        { "underline",    "font underline",       0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_FUNDERLINE   }, 0, 0, FLAGS, "reco_flags" },
+        { "font",         "font name",            0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_FONT         }, 0, 0, FLAGS, "reco_flags" },
+        { "fontsize",     "font size",            0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_FONTSIZE     }, 0, 0, FLAGS, "reco_flags" },
+        { "color",        "font color",           0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_COLOR        }, 0, 0, FLAGS, "reco_flags" },
+        { "outlinecolor", "outline color",        0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_OUTLINECOLOR }, 0, 0, FLAGS, "reco_flags" },
+    { "tessdata_path",          "path to tesseract data",                OFFSET(tessdata_path),          AV_OPT_TYPE_STRING, { .str = NULL },                      0,                  0,       FLAGS, NULL },
+    { NULL },
+};
+
+AVFILTER_DEFINE_CLASS(graphicsub2text);
+
+static const AVFilterPad inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .filter_frame = filter_frame,
+        .config_props = config_input,
+    },
+};
+
+static const AVFilterPad outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_SUBTITLE,
+        .config_props  = config_output,
+    },
+};
+
+const AVFilter ff_sf_graphicsub2text = {
+    .name          = "graphicsub2text",
+    .description   = NULL_IF_CONFIG_SMALL("Convert graphical subtitles to text subtitles via OCR"),
+    .init          = init,
+    .uninit        = uninit,
+    .priv_size     = sizeof(SubOcrContext),
+    .priv_class    = &graphicsub2text_class,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_QUERY_FUNC(query_formats),
+};
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v2 18/26] avfilter/subscale: Add filter for scaling and/or re-arranging graphical subtitles
  2022-01-20  2:48 ` [FFmpeg-devel] [PATCH v2 00/26] Subtitle Filtering 2022 ffmpegagent
                     ` (16 preceding siblings ...)
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 17/26] avfilter/graphicsub2text: Add new graphicsub2text filter (OCR) ffmpegagent
@ 2022-01-20  2:48   ` ffmpegagent
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 19/26] avfilter/subfeed: add subtitle feed filter ffmpegagent
                     ` (8 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  2:48 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 configure                 |   1 +
 doc/filters.texi          | 164 +++++++
 libavfilter/Makefile      |   1 +
 libavfilter/allfilters.c  |   1 +
 libavfilter/sf_subscale.c | 884 ++++++++++++++++++++++++++++++++++++++
 5 files changed, 1051 insertions(+)
 create mode 100644 libavfilter/sf_subscale.c

diff --git a/configure b/configure
index ee7afffb05..a8b7ce8a26 100755
--- a/configure
+++ b/configure
@@ -3731,6 +3731,7 @@ sr_filter_deps="avformat swscale"
 sr_filter_select="dnn"
 stereo3d_filter_deps="gpl"
 splitcc_filter_deps="avcodec"
+subscale_filter_deps="swscale avcodec"
 subtitles_filter_deps="avformat avcodec libass"
 super2xsai_filter_deps="gpl"
 pixfmts_super2xsai_test_deps="super2xsai_filter"
diff --git a/doc/filters.texi b/doc/filters.texi
index 9311714f82..b3d11c0ac0 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -26303,6 +26303,170 @@ Set the rendering margin in pixels.
 For rendering, alway use the latest event only, which is covering the given point in time.
 @end table
 
+@section subscale
+
+Provides high-quality scaling and rearranging functionality for graphical subtitles.
+
+The subscale filter provides multiple approaches for manipulating
+the size and position of graphical subtitle rectangles wich can
+be combined or used separately.
+Scaling is performed by converting the palettized subtitle bitmaps
+to RGBA and re-quantization to palette colors afterwards via elbg algorithm.
+
+The two major operations are 'scale' and 're-arrange' with the
+latter being separated as 'arrange_h' and 'arrange_v'.
+
+
+Inputs:
+- 0: Subtitles [bitmap]
+
+Outputs:
+- 0: Subtitles [bitmap]
+
+It accepts the following parameters:
+
+@table @option
+
+@item w, width
+Set the width of the output.
+Width and height in case of graphical subtitles are just indicating
+a virtual size for which the output (consisting of 0-n bitmap rectangles)
+is intended to be displayed on.
+
+@item h, height
+Set the height of the output.
+
+@item margin_h
+Sets a horizontal margin to be preserverved when using any
+of the arrange modes.
+
+@item margin_v
+Sets a vertical margin to be preserverved when using any
+of the arrange modes.
+
+@item force_original_aspect_ratio
+Enable decreasing or increasing output video width or height if necessary to
+keep the original aspect ratio. Possible values:
+
+@table @samp
+@item disable
+Scale the video as specified and disable this feature.
+
+@item decrease
+The output video dimensions will automatically be decreased if needed.
+
+@item increase
+The output video dimensions will automatically be increased if needed.
+
+@end table
+
+
+@item scale_mode
+Specifies how subtitle bitmaps should be scaled.
+The scale factor is determined by the the factor between input
+and output size.
+
+@table @samp
+@item none
+Do not apply any common scaling.
+
+@item uniform
+Uniformly scale all subtitle bitmaps including their positions.
+
+@item uniform_no_reposition
+Uniformly scale all subtitle bitmaps without changing positions.
+
+@end table
+
+
+@item arrange_h
+Specifies how subtitle bitmaps should be arranged horizontally.
+
+@item arrange_v
+Specifies how subtitle bitmaps should be arranged vertically.
+
+
+@table @samp
+@item none
+Do not rearrange subtitle bitmaps.
+
+@item margin_no_scale
+Move subtitle bitmaps to be positioned inside the specified
+margin (margin_h or margin_v) when possible and without scaling.
+
+@item margin_and_scale
+Move subtitle bitmaps to be positioned inside the specified
+margin (margin_h or margin_v) and scale in case it doesn't fit.
+
+@item snapalign_no_scale
+Categorize subtitle bitmap positions as one of left/center/right
+or top/bottom/middle based on original positioning and apply
+these alignments for the target positioning.
+No scaling will be applied.
+
+@item snapalign_and_scale
+Categorize subtitle bitmap positions as one of left/center/right
+or top/bottom/middle based on original positioning and apply
+these alignments for the target positioning.
+Bitmaps that do not fit inside the margins borders are
+scaled to fit.
+@end table
+
+@item eval
+Set evaluation mode for the expressions (@option{width}, @option{height}).
+
+It accepts the following values:
+@table @samp
+@item init
+Evaluate expressions only once during the filter initialization.
+
+@item frame
+Evaluate expressions for each incoming frame. This is way slower than the
+@samp{init} mode since it requires all the scalers to be re-computed, but it
+allows advanced dynamic expressions.
+@end table
+
+Default value is @samp{init}.
+
+
+@item num_colors
+Set the number of palette colors for output images.
+Choose the maximum (256) when further processing is done (e.g.
+overlaying on a video).
+When subtitles will be encoded as bitmap subtitles (e.g. dvbsub),
+a smaller number of palette colors (e.g. 4-16) might need to be used, depending
+on the target format and codec.
+
+@item bitmap_width_align
+@item bitmap_height_align
+Make sure that subtitle bitmap sizes are a multiple of this
+value. Default is 2.
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Uniformly scale down video and bitmap subtitles and encode
+subtitles as dvbsub.
+@example
+ffmpeg -y -loglevel verbose -ss 32 -i "https://streams.videolan.org/samples/sub/largeres_vobsub.mkv" -filter_complex "[0:0]scale=480x270[vid1];[0:10]subscale=w=480:h=270:num_colors=16[sub1]" -map [vid1] -map [sub1] -c:s dvbsub -c:v libx265 output.ts
+@end example
+@item
+Squeeze video vertically and arrange subtitle bitmaps
+inside the video area without scaling, then overlay
+subtitles onto the video.
+@example
+ffmpeg -y -loglevel verbose -ss 32 -i "https://streams.videolan.org/samples/sub/largeres_vobsub.mkv" -filter_complex "[0:0]scale=1920x400,setsar=1[vid1];[0:10]subscale=w=1920:h=400:scale_mode=none:margin_h=50:arrange_h=margin_no_scale:arrange_v=margin_no_scale:margin_v=50:num_colors=256[sub1];[vid1][sub1]overlay_graphicsubs" -c:v libx265 output.ts
+@end example
+@item
+Scale both, a video and its embedded VOB subs, then encode them as separate streams into an MKV.
+@example
+ffmpeg -y -y -loglevel verbose -i "https://streams.videolan.org/samples/sub/largeres_vobsub.mkv" -filter_complex "[0:0]scale=720x576[vid1];[0:7]subscale=w=720:h=576:num_colors=16[sub1]" -map [vid1] -map [sub1] -c:s dvdsub out.mkv
+@end example
+@end itemize
+
 @c man end SUBTITLE FILTERS
 
 @chapter Multimedia Filters
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index ead3e38507..0e3e48613e 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -569,6 +569,7 @@ OBJS-$(CONFIG_SHOW_SPEAKER_FILTER)           += sf_textmod.o
 OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
 OBJS-$(CONFIG_SPLITCC_FILTER)                += sf_splitcc.o
 OBJS-$(CONFIG_STRIPSTYLES_FILTER)            += sf_stripstyles.o
+OBJS-$(CONFIG_SUBSCALE_FILTER)               += sf_subscale.o
 
 # multimedia filters
 OBJS-$(CONFIG_ABITSCOPE_FILTER)              += avf_abitscope.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 34576016ce..8e4f2feca3 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -552,6 +552,7 @@ extern const AVFilter ff_sf_graphicsub2text;
 extern const AVFilter ff_sf_showspeaker;
 extern const AVFilter ff_sf_splitcc;
 extern const AVFilter ff_sf_stripstyles;
+extern const AVFilter ff_sf_subscale;
 extern const AVFilter ff_sf_textmod;
 extern const AVFilter ff_svf_graphicsub2video;
 extern const AVFilter ff_svf_textsub2video;
diff --git a/libavfilter/sf_subscale.c b/libavfilter/sf_subscale.c
new file mode 100644
index 0000000000..04f0f16c27
--- /dev/null
+++ b/libavfilter/sf_subscale.c
@@ -0,0 +1,884 @@
+/*
+ * 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
+ * scale graphical subtitles filter
+ */
+
+#include <string.h>
+
+#include "drawutils.h"
+#include "internal.h"
+#include "scale_eval.h"
+#include "libavutil/eval.h"
+#include "libavutil/opt.h"
+#include "libswscale/swscale.h"
+
+#include "libavcodec/elbg.h"
+
+static const char *const var_names[] = {
+    "in_w",   "iw",
+    "in_h",   "ih",
+    "out_w",  "ow",
+    "out_h",  "oh",
+    "a",
+    "sar",
+    "dar",
+    "margin_h",
+    "margin_v",
+    NULL
+};
+
+enum var_name {
+    VAR_IN_W,   VAR_IW,
+    VAR_IN_H,   VAR_IH,
+    VAR_OUT_W,  VAR_OW,
+    VAR_OUT_H,  VAR_OH,
+    VAR_A,
+    VAR_SAR,
+    VAR_DAR,
+    VARS_B_H,
+    VARS_B_V,
+    VARS_NB
+};
+
+enum EvalMode {
+    EVAL_MODE_INIT,
+    EVAL_MODE_FRAME,
+    EVAL_MODE_NB
+};
+
+enum SubScaleMode {
+    SSM_NONE,
+    SSM_UNIFORM,
+    SSM_UNIFORM_NO_REPOSITION,
+};
+
+enum SubArrangeMode {
+    SAM_NONE,
+    SAM_ENSUREMARGIN_NO_SCALE,
+    SAM_ENSUREMARGIN_AND_SCALE,
+    SAM_SNAPALIGNMENT_NO_SCALE,
+    SAM_SNAPALIGNMENT_AND_SCALE,
+};
+
+typedef struct SubScaleContext {
+    const AVClass *class;
+    struct SwsContext *sws;
+    AVDictionary *opts;
+
+    int w, h;
+
+    char *w_expr;               ///< width  expression string
+    char *h_expr;               ///< height expression string
+    AVExpr *w_pexpr;
+    AVExpr *h_pexpr;
+    double var_values[VARS_NB];
+
+    int force_original_aspect_ratio;
+    int eval_mode;               ///< expression evaluation mode
+
+    int use_caching;
+
+    // Scale Options
+    enum SubScaleMode scale_mode;
+
+    // Arrange Options
+    enum SubArrangeMode arrange_mode_h;
+    enum SubArrangeMode arrange_mode_v;
+    int margin_h;
+    int margin_v;
+    char *margin_h_expr;
+    char *margin_v_expr;
+    AVExpr *margin_h_pexpr;
+    AVExpr *margin_v_pexpr;
+
+    // Bitmap Options
+    int num_output_colors;
+    int bitmap_width_align;
+    int bitmap_height_align;
+
+    // Color Quantization Fields
+    struct ELBGContext *ctx;
+    AVLFG lfg;
+    int *codeword;
+    int *codeword_closest_codebook_idxs;
+    int *codebook;
+    int r_idx, g_idx, b_idx, a_idx;
+    AVFrame *cache_frame;
+} SubScaleContext;
+
+
+static int config_output(AVFilterLink *outlink);
+
+static int check_exprs(AVFilterContext *ctx)
+{
+    const SubScaleContext *s = ctx->priv;
+    unsigned vars_w[VARS_NB] = { 0 }, vars_h[VARS_NB] = { 0 };
+
+    if (!s->w_pexpr && !s->h_pexpr)
+        return AVERROR(EINVAL);
+
+    if (s->w_pexpr)
+        av_expr_count_vars(s->w_pexpr, vars_w, VARS_NB);
+    if (s->h_pexpr)
+        av_expr_count_vars(s->h_pexpr, vars_h, VARS_NB);
+
+    if (vars_w[VAR_OUT_W] || vars_w[VAR_OW]) {
+        av_log(ctx, AV_LOG_ERROR, "Width expression cannot be self-referencing: '%s'.\n", s->w_expr);
+        return AVERROR(EINVAL);
+    }
+
+    if (vars_h[VAR_OUT_H] || vars_h[VAR_OH]) {
+        av_log(ctx, AV_LOG_ERROR, "Height expression cannot be self-referencing: '%s'.\n", s->h_expr);
+        return AVERROR(EINVAL);
+    }
+
+    if ((vars_w[VAR_OUT_H] || vars_w[VAR_OH]) &&
+        (vars_h[VAR_OUT_W] || vars_h[VAR_OW])) {
+        av_log(ctx, AV_LOG_WARNING, "Circular references detected for width '%s' and height '%s' - possibly invalid.\n", s->w_expr, s->h_expr);
+    }
+
+    if (s->margin_h_pexpr)
+        av_expr_count_vars(s->margin_h_pexpr, vars_w, VARS_NB);
+    if (s->margin_v_pexpr)
+        av_expr_count_vars(s->margin_v_pexpr, vars_h, VARS_NB);
+
+    return 0;
+}
+
+static int scale_parse_expr(AVFilterContext *ctx, char *str_expr, AVExpr **pexpr_ptr, const char *var, const char *args)
+{
+    SubScaleContext *s = ctx->priv;
+    int ret, is_inited = 0;
+    char *old_str_expr = NULL;
+    AVExpr *old_pexpr = NULL;
+
+    if (str_expr) {
+        old_str_expr = av_strdup(str_expr);
+        if (!old_str_expr)
+            return AVERROR(ENOMEM);
+        av_opt_set(s, var, args, 0);
+    }
+
+    if (*pexpr_ptr) {
+        old_pexpr = *pexpr_ptr;
+        *pexpr_ptr = NULL;
+        is_inited = 1;
+    }
+
+    ret = av_expr_parse(pexpr_ptr, args, var_names,
+                        NULL, NULL, NULL, NULL, 0, ctx);
+    if (ret < 0) {
+        av_log(ctx, AV_LOG_ERROR, "Cannot parse expression for %s: '%s'\n", var, args);
+        goto revert;
+    }
+
+    ret = check_exprs(ctx);
+    if (ret < 0)
+        goto revert;
+
+    if (is_inited && (ret = config_output(ctx->outputs[0])) < 0)
+        goto revert;
+
+    av_expr_free(old_pexpr);
+    av_freep(&old_str_expr);
+
+    return 0;
+
+revert:
+    av_expr_free(*pexpr_ptr);
+    *pexpr_ptr = NULL;
+    if (old_str_expr) {
+        av_opt_set(s, var, old_str_expr, 0);
+        av_free(old_str_expr);
+    }
+    if (old_pexpr)
+        *pexpr_ptr = old_pexpr;
+
+    return ret;
+}
+
+static av_cold int init_dict(AVFilterContext *ctx, AVDictionary **opts)
+{
+    SubScaleContext *s = ctx->priv;
+    uint8_t rgba_map[4];
+    int ret;
+
+    if (!s->w_expr)
+        av_opt_set(s, "w", "iw", 0);
+    if (!s->h_expr)
+        av_opt_set(s, "h", "ih", 0);
+
+    ret = scale_parse_expr(ctx, NULL, &s->w_pexpr, "width", s->w_expr);
+    if (ret < 0)
+        return ret;
+
+    ret = scale_parse_expr(ctx, NULL, &s->h_pexpr, "height", s->h_expr);
+    if (ret < 0)
+        return ret;
+
+    av_log(ctx, AV_LOG_VERBOSE, "w:%s h:%s\n",
+           s->w_expr, s->h_expr);
+
+    if (!s->margin_h_expr)
+        av_opt_set(s, "margin_h", "0", 0);
+    if (!s->margin_v_expr)
+        av_opt_set(s, "margin_v", "0", 0);
+
+    ret = scale_parse_expr(ctx, NULL, &s->margin_h_pexpr, "margin_h", s->margin_h_expr);
+    if (ret < 0)
+        return ret;
+
+    ret = scale_parse_expr(ctx, NULL, &s->margin_v_pexpr, "margin_v", s->margin_v_expr);
+    if (ret < 0)
+        return ret;
+
+    s->opts = *opts;
+    *opts = NULL;
+
+    ff_fill_rgba_map(&rgba_map[0], AV_PIX_FMT_RGB32);
+
+    s->r_idx = rgba_map[0]; // R
+    s->g_idx = rgba_map[1]; // G
+    s->b_idx = rgba_map[2]; // B
+    s->a_idx = rgba_map[3]; // A
+
+    av_lfg_init(&s->lfg, 123456789);
+
+
+    return 0;
+}
+
+static av_cold void uninit(AVFilterContext *ctx)
+{
+    SubScaleContext *s = ctx->priv;
+
+    av_frame_free(&s->cache_frame);
+
+    av_expr_free(s->w_pexpr);
+    av_expr_free(s->h_pexpr);
+    s->w_pexpr = s->h_pexpr = NULL;
+
+    av_expr_free(s->margin_h_pexpr);
+    av_expr_free(s->margin_v_pexpr);
+    s->margin_h_pexpr = s->margin_v_pexpr = NULL;
+
+    sws_freeContext(s->sws);
+    s->sws = NULL;
+    av_dict_free(&s->opts);
+
+    avpriv_elbg_free(&s->ctx);
+
+    av_freep(&s->codebook);
+    av_freep(&s->codeword);
+    av_freep(&s->codeword_closest_codebook_idxs);
+}
+
+static int config_input(AVFilterLink *inlink)
+{
+    ////const AVFilterContext *ctx = inlink->dst;
+    ////SubScaleContext *s = ctx->priv;
+
+    ////if (s->w <= 0 || s->h <= 0) {
+    ////    s->w = inlink->w;
+    ////    s->h = inlink->h;
+    ////}
+    return 0;
+}
+
+static int scale_eval_dimensions(AVFilterContext *ctx)
+{
+    SubScaleContext *s = ctx->priv;
+    const AVFilterLink *inlink = ctx->inputs[0];
+    char *expr;
+    int eval_w, eval_h, margin_h, margin_v;
+    int ret;
+    double res;
+
+    s->var_values[VAR_IN_W]  = s->var_values[VAR_IW] = inlink->w;
+    s->var_values[VAR_IN_H]  = s->var_values[VAR_IH] = inlink->h;
+    s->var_values[VAR_OUT_W] = s->var_values[VAR_OW] = NAN;
+    s->var_values[VAR_OUT_H] = s->var_values[VAR_OH] = NAN;
+    s->var_values[VARS_B_H]  = s->var_values[VARS_B_V] = 0;
+    s->var_values[VAR_A]     = (double) inlink->w / inlink->h;
+    s->var_values[VAR_SAR]   = inlink->sample_aspect_ratio.num ?
+        (double) inlink->sample_aspect_ratio.num / inlink->sample_aspect_ratio.den : 1;
+    s->var_values[VAR_DAR]   = s->var_values[VAR_A] * s->var_values[VAR_SAR];
+
+    res = av_expr_eval(s->w_pexpr, s->var_values, NULL);
+    s->var_values[VAR_OUT_W] = s->var_values[VAR_OW] = (int) res == 0 ? inlink->w : (int) res;
+
+    res = av_expr_eval(s->h_pexpr, s->var_values, NULL);
+    if (isnan(res)) {
+        expr = s->h_expr;
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+    eval_h = s->var_values[VAR_OUT_H] = s->var_values[VAR_OH] = (int) res == 0 ? inlink->h : (int) res;
+
+    res = av_expr_eval(s->w_pexpr, s->var_values, NULL);
+    if (isnan(res)) {
+        expr = s->w_expr;
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+    eval_w = s->var_values[VAR_OUT_W] = s->var_values[VAR_OW] = (int) res == 0 ? inlink->w : (int) res;
+
+    s->w = eval_w;
+    s->h = eval_h;
+
+    res = av_expr_eval(s->margin_h_pexpr, s->var_values, NULL);
+    s->var_values[VARS_B_H] = (int)res;
+
+    res = av_expr_eval(s->margin_v_pexpr, s->var_values, NULL);
+    if (isnan(res)) {
+        expr = s->margin_v_expr;
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+    margin_v = s->var_values[VARS_B_V] = (int)res;
+
+    res = av_expr_eval(s->margin_h_pexpr, s->var_values, NULL);
+    if (isnan(res)) {
+        expr = s->margin_h_expr;
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+    margin_h = s->var_values[VARS_B_H] = (int)res;
+
+    s->margin_h = margin_h;
+    s->margin_v = margin_v;
+
+    return 0;
+
+fail:
+    av_log(ctx, AV_LOG_ERROR,
+           "Error when evaluating the expression '%s'.\n", expr);
+    return ret;
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    AVFilterLink *inlink  = outlink->src->inputs[0];
+    SubScaleContext *s = ctx->priv;
+    int ret;
+
+    outlink->format = AV_SUBTITLE_FMT_BITMAP;
+    outlink->time_base = ctx->inputs[0]->time_base;
+    outlink->frame_rate = ctx->inputs[0]->frame_rate;
+
+    if ((ret = scale_eval_dimensions(ctx)) < 0)
+        goto fail;
+
+    ff_scale_adjust_dimensions(inlink, &s->w, &s->h,
+                               s->force_original_aspect_ratio, 2);
+
+    if (s->w > INT_MAX ||
+        s->h > INT_MAX ||
+        (s->h * inlink->w) > INT_MAX ||
+        (s->w * inlink->h) > INT_MAX)
+        av_log(ctx, AV_LOG_ERROR, "Rescaled value for width or height is too big.\n");
+
+    outlink->w = s->w;
+    outlink->h = s->h;
+
+    if (s->sws)
+        sws_freeContext(s->sws);
+
+    s->sws = sws_alloc_context();
+    if (!s->sws)
+        return AVERROR(ENOMEM);
+
+    av_opt_set_pixel_fmt(s->sws, "src_format", AV_PIX_FMT_PAL8, 0);
+    av_opt_set_int(s->sws, "dst_format", AV_PIX_FMT_RGB32, 0);
+    av_opt_set_int(s->sws, "threads", ff_filter_get_nb_threads(ctx), 0);
+
+    if (s->opts) {
+        const AVDictionaryEntry *e = NULL;
+        while ((e = av_dict_get(s->opts, "", e, AV_DICT_IGNORE_SUFFIX))) {
+            if ((ret = av_opt_set(s->sws, e->key, e->value, 0)) < 0)
+                return ret;
+        }
+    }
+
+    if ((ret = sws_init_context(s->sws, NULL, NULL)) < 0)
+        return ret;
+
+    if (inlink->sample_aspect_ratio.num){
+        outlink->sample_aspect_ratio = av_mul_q((AVRational){outlink->h * inlink->w, outlink->w * inlink->h}, inlink->sample_aspect_ratio);
+    } else
+        outlink->sample_aspect_ratio = inlink->sample_aspect_ratio;
+
+    av_log(ctx, AV_LOG_VERBOSE, "Output size set to %dx%d.\n", outlink->w, outlink->h);
+
+    return 0;
+fail:
+    return ret;
+}
+
+static int palettize_image(SubScaleContext *const s, const int w, const int h, const int src_linesize, uint8_t *src_data,
+                          int dst_linesize, uint8_t *dst_data, uint32_t *dst_pal)
+{
+    int k, ret;
+    const int codeword_length = w * h;
+
+    /* Re-Initialize */
+    s->codeword = av_realloc_f(s->codeword, codeword_length, 4 * sizeof(*s->codeword));
+    if (!s->codeword)
+        return AVERROR(ENOMEM);
+
+    s->codeword_closest_codebook_idxs =
+        av_realloc_f(s->codeword_closest_codebook_idxs, codeword_length,
+                     sizeof(*s->codeword_closest_codebook_idxs));
+    if (!s->codeword_closest_codebook_idxs)
+        return AVERROR(ENOMEM);
+
+    s->codebook = av_realloc_f(s->codebook, s->num_output_colors, 4 * sizeof(*s->codebook));
+    if (!s->codebook)
+        return AVERROR(ENOMEM);
+
+    /* build the codeword */
+    k = 0;
+    for (int i = 0; i < h; i++) {
+        uint8_t *p = src_data;
+        for (int j = 0; j < w; j++) {
+            s->codeword[k++] = p[s->b_idx];
+            s->codeword[k++] = p[s->g_idx];
+            s->codeword[k++] = p[s->r_idx];
+            s->codeword[k++] = p[s->a_idx];
+            p += 4;
+        }
+        src_data += src_linesize;
+    }
+
+    /* compute the codebook */
+    ret = avpriv_elbg_do(&s->ctx, s->codeword, 4,
+        codeword_length, s->codebook,
+        s->num_output_colors, 1,
+        s->codeword_closest_codebook_idxs, &s->lfg, 0);
+
+    if (ret < 0)
+        return ret;
+
+    /* Write Palette */
+    for (int i = 0; i < s->num_output_colors; i++) {
+        dst_pal[i] = s->codebook[i*4+3] << 24  |
+                    (s->codebook[i*4+2] << 16) |
+                    (s->codebook[i*4+1] <<  8) |
+                     s->codebook[i*4  ];
+    }
+
+    /* Write Image */
+    k = 0;
+    for (int i = 0; i < h; i++) {
+        uint8_t *p = dst_data;
+        for (int j = 0; j < w; j++, p++) {
+            p[0] = s->codeword_closest_codebook_idxs[k++];
+        }
+
+        dst_data += dst_linesize;
+    }
+
+    return ret;
+}
+
+static int rescale_size(int64_t a, AVRational factor)
+{
+    const int64_t res = av_rescale_rnd(a, factor.num, factor.den, AV_ROUND_NEAR_INF);
+    if (res > INT32_MAX || res < 0)
+        return 0;
+
+    return (int)res;
+}
+
+
+static int scale_area(AVFilterLink *link, AVSubtitleArea *area, const int target_width, const int target_height)
+{
+    const AVFilterContext *ctx = link->dst;
+    SubScaleContext *s = ctx->priv;
+    int ret;
+
+    AVBufferRef *dst_buffer;
+    const uint8_t* data[2]    = { area->buf[0]->data, (uint8_t *)&area->pal };
+    const int dstW            = FFALIGN(target_width, s->bitmap_width_align);
+    const int dstH            = FFALIGN(target_height, s->bitmap_height_align);
+    const int tmp_linesize[2] = { FFALIGN(dstW * 4, 32), 0 };
+    const int dst_linesize[2] = { dstW, 0 };
+    uint8_t* tmp[2] = { 0, 0 };
+
+    AVBufferRef *tmp_buffer = av_buffer_allocz(tmp_linesize[0] * dstH);
+    if (!tmp_buffer)
+        return AVERROR(ENOMEM);
+
+    if (!s->sws)
+        return 0;
+
+    tmp[0] = tmp_buffer->data;
+
+    s->sws = sws_getCachedContext(s->sws, area->w, area->h, AV_PIX_FMT_PAL8,
+        dstW, dstH, AV_PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL);
+    if (!s->sws) {
+        av_log(NULL, AV_LOG_FATAL, "Cannot initialize the conversion context. dstW=%d dstH=%d\n", dstW, dstH);
+        return AVERROR(EINVAL);
+    }
+
+    // Rescale to ARGB
+    ret = sws_scale(s->sws, data, area->linesize, 0, area->h, tmp, tmp_linesize);
+    if (ret < 0) {
+        av_buffer_unref(&tmp_buffer);
+        return ret;
+    }
+
+    // Alloc output buffer
+    dst_buffer = av_buffer_allocz(dst_linesize[0] * dstH);
+    if (!dst_buffer) {
+        av_buffer_unref(&tmp_buffer);
+        return AVERROR(ENOMEM);
+    }
+
+    // Quantize to palettized image
+    ret = palettize_image(s, dstW, dstH, tmp_linesize[0], tmp[0], dst_linesize[0], dst_buffer->data, area->pal);
+    av_buffer_unref(&tmp_buffer);
+
+    if (ret < 0) {
+        av_buffer_unref(&dst_buffer);
+        return ret;
+    }
+
+    av_buffer_unref(&area->buf[0]);
+    ret = av_buffer_replace(&area->buf[0], dst_buffer);
+    if (ret < 0) {
+        av_buffer_unref(&dst_buffer);
+        return ret;
+    }
+
+    area->w = dstW;
+    area->h = dstH;
+    area->linesize[0] = dst_linesize[0];
+    area->nb_colors = s->num_output_colors;
+
+    return ret;
+}
+
+static int process_area(AVFilterLink *inlink, AVSubtitleArea *area, AVRational x_factor, AVRational y_factor)
+{
+    AVFilterContext *ctx     = inlink->dst;
+    const SubScaleContext *s = ctx->priv;
+    int target_w, target_h, target_x, target_y;
+    const int border_l = s->margin_h;
+    const int border_r = s->w - s->margin_h;
+    const int border_t = s->margin_v;
+    const int border_b = s->h - s->margin_v;
+
+    av_log(ctx, AV_LOG_DEBUG, "process_area -  start: x/y: (%d:%d) size: %dx%d scale_mode: %d x-factor: %d:%d y-factor: %d:%d\n",
+        area->x, area->y, area->w, area->h, s->scale_mode, x_factor.num, x_factor.den, y_factor.num, y_factor.den);
+
+    switch (s->scale_mode) {
+    case SSM_NONE:
+        target_w = area->w;
+        target_h = area->h;
+        target_x = area->x;
+        target_y = area->y;
+        break;
+    case SSM_UNIFORM:
+        target_w = rescale_size(area->w, x_factor);
+        target_h = rescale_size(area->h, y_factor);
+        target_x = rescale_size(area->x, x_factor);
+        target_y = rescale_size(area->y, y_factor);
+        break;
+    case SSM_UNIFORM_NO_REPOSITION:
+        target_w = rescale_size(area->w, x_factor);
+        target_h = rescale_size(area->h, y_factor);
+        target_x = area->x;
+        target_y = area->y;
+        break;
+    default:
+        av_log(ctx, AV_LOG_ERROR, "Invalid scale_mode: %d\n", s->scale_mode);
+        return AVERROR(EINVAL);
+    }
+
+    av_log(ctx, AV_LOG_DEBUG, "process_area - scaled: x/y: (%d:%d) size: %dx%d.\n", target_x, target_y, target_w, target_h);
+
+
+    switch (s->arrange_mode_h) {
+    case SAM_ENSUREMARGIN_AND_SCALE:
+    case SAM_SNAPALIGNMENT_AND_SCALE:
+        {
+            // IF it doesn't fit - scale it
+            int max_width = s->w - 2 * s->margin_h;
+
+            if (max_width < 2)
+                max_width = 2;
+
+            if (target_w > max_width) {
+                target_h = (int)av_rescale(target_h, max_width, target_w);
+                target_w = max_width;
+                target_x = s->margin_h;
+            }
+        }
+        break;
+    }
+
+    switch (s->arrange_mode_h) {
+    case SAM_NONE:
+        break;
+    case SAM_ENSUREMARGIN_NO_SCALE:
+    case SAM_ENSUREMARGIN_AND_SCALE:
+        // Left border
+        if (target_x < border_l)
+            target_x = border_l;
+
+        // Right border
+        if (target_x + target_w > border_r)
+            target_x = border_r - target_w;
+
+        break;
+    case SAM_SNAPALIGNMENT_NO_SCALE:
+    case SAM_SNAPALIGNMENT_AND_SCALE:
+        {
+            // Use original values to detect alignment
+            const int left_margin          = area->x;
+            const int right_margin         = inlink->w - area->x - area->w;
+            const AVRational diff_factor_r = { left_margin - right_margin, area->w };
+            const float diff_factor        = (float)av_q2d(diff_factor_r);
+
+            if (diff_factor > 0.2f) {
+                // Right aligned
+                target_x = border_r - target_w;
+            } else if (diff_factor < -0.2f) {
+                // Left aligned
+                target_x = border_l;
+            } else {
+                // Centered
+                target_x = (inlink->w - area->w) / 2;
+            }
+        }
+
+        break;
+    default:
+        av_log(ctx, AV_LOG_ERROR, "Invalid arrange_mode_h: %d\n", s->arrange_mode_h);
+        return AVERROR(EINVAL);
+    }
+
+    av_log(ctx, AV_LOG_DEBUG, "process_area -  arr_h: x/y: (%d:%d) size: %dx%d.\n", target_x, target_y, target_w, target_h);
+
+
+    switch (s->arrange_mode_v) {
+    case SAM_ENSUREMARGIN_AND_SCALE:
+    case SAM_SNAPALIGNMENT_AND_SCALE:
+        {
+            // IF it doesn't fit - scale it
+            int max_height = s->h - 2 * s->margin_v;
+
+            if (max_height < 2)
+                max_height = 2;
+
+            if (target_h > max_height) {
+                target_w = (int)av_rescale(target_w, max_height, target_h);
+                target_h = max_height;
+                target_y = s->margin_v;
+            }
+        }
+        break;
+    }
+
+    switch (s->arrange_mode_v) {
+    case SAM_NONE:
+        break;
+    case SAM_ENSUREMARGIN_NO_SCALE:
+    case SAM_ENSUREMARGIN_AND_SCALE:
+        // Top border
+        if (target_y < border_t)
+            target_y = border_t;
+
+        // Bottom border
+        if (target_y + target_h > border_b)
+            target_y = border_b - target_h;
+
+        break;
+    case SAM_SNAPALIGNMENT_NO_SCALE:
+    case SAM_SNAPALIGNMENT_AND_SCALE:
+        {
+            // Use original values to detect alignment
+            const int top_margin           = area->y;
+            const int bottom_margin        = inlink->h - area->y - area->h;
+            const AVRational diff_factor_r = { top_margin - bottom_margin, area->h };
+            const float diff_factor        = (float)av_q2d(diff_factor_r);
+
+            if (diff_factor > 0.2f) {
+                // Bottom aligned
+                target_y = border_b - target_h;
+            } else if (diff_factor < -0.2f) {
+                // Top aligned
+                target_y = border_t;
+            } else {
+                // Centered
+                target_y = (inlink->h - area->h) / 2;
+            }
+        }
+
+        break;
+    default:
+        av_log(ctx, AV_LOG_ERROR, "Invalid arrange_mode_v: %d\n", s->arrange_mode_v);
+        return AVERROR(EINVAL);
+    }
+
+    av_log(ctx, AV_LOG_VERBOSE, "process_area -  arr_v: x/y: (%d:%d) size: %dx%d.\n", target_x, target_y, target_w, target_h);
+
+    area->x = target_x;
+    area->y = target_y;
+
+    if (area->w != target_w || area->h != target_h)
+        return scale_area(inlink, area, target_w, target_h);
+
+    return 0;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    const AVFilterContext *ctx = inlink->dst;
+    SubScaleContext *s = ctx->priv;
+    AVFilterLink *outlink = ctx->outputs[0];
+    int ret;
+
+    // just forward empty frames
+    if (frame->num_subtitle_areas == 0) {
+        av_frame_free(&s->cache_frame);
+        return ff_filter_frame(outlink, frame);
+    }
+
+    if (s->use_caching && s->cache_frame && frame->repeat_sub
+        && s->cache_frame->subtitle_timing.start_pts == frame->subtitle_timing.start_pts) {
+        AVFrame *out = av_frame_clone(s->cache_frame);
+        if (!out)
+            return AVERROR(ENOMEM);
+
+        ret = av_frame_copy_props(out, frame);
+        if (ret < 0)
+            return ret;
+
+        av_log(inlink->dst, AV_LOG_DEBUG, "subscale CACHED - size %dx%d  pts: %"PRId64"  areas: %d\n", frame->width, frame->height, frame->subtitle_timing.start_pts, frame->num_subtitle_areas);
+        av_frame_free(&frame);
+        return ff_filter_frame(outlink, out);
+    }
+
+    ret = av_frame_make_writable(frame);
+    if (ret >= 0) {
+        const AVRational x_factor = { .num = outlink->w, .den = inlink->w} ;
+        const AVRational y_factor = { .num = outlink->h, .den = inlink->h} ;
+
+        for (unsigned i = 0; i < frame->num_subtitle_areas; ++i) {
+            AVSubtitleArea *area = frame->subtitle_areas[i];
+
+            ret = process_area(inlink, area, x_factor, y_factor);
+            if (ret < 0)
+                return ret;
+        }
+
+        av_log(inlink->dst, AV_LOG_DEBUG, "subscale output - size %dx%d  pts: %"PRId64"  areas: %d\n", frame->width, frame->height, frame->subtitle_timing.start_pts, frame->num_subtitle_areas);
+
+        if (s->use_caching) {
+            av_frame_free(&s->cache_frame);
+            s->cache_frame = av_frame_clone(frame);
+        }
+
+        return ff_filter_frame(outlink, frame);
+    }
+
+    return ret;
+}
+
+#define OFFSET(x) offsetof(SubScaleContext, x)
+#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption subscale_options[] = {
+    { "margin_h", "horizontal border",        OFFSET(margin_h_expr),    AV_OPT_TYPE_STRING,        .flags = FLAGS },
+    { "margin_v", "vertical border",          OFFSET(margin_v_expr),    AV_OPT_TYPE_STRING,        .flags = FLAGS },
+    { "w",     "Output video width",          OFFSET(w_expr),    AV_OPT_TYPE_STRING,        .flags = FLAGS },
+    { "width", "Output video width",          OFFSET(w_expr),    AV_OPT_TYPE_STRING,        .flags = FLAGS },
+    { "h",     "Output video height",         OFFSET(h_expr),    AV_OPT_TYPE_STRING,        .flags = FLAGS },
+    { "height","Output video height",         OFFSET(h_expr),    AV_OPT_TYPE_STRING,        .flags = FLAGS },
+    { "force_original_aspect_ratio", "decrease or increase w/h if necessary to keep the original AR", OFFSET(force_original_aspect_ratio), AV_OPT_TYPE_INT, { .i64 = 0}, 0, 2, FLAGS, "force_oar" },
+    { "disable",  NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, 0, 0, FLAGS, "force_oar" },
+    { "decrease", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 1 }, 0, 0, FLAGS, "force_oar" },
+    { "increase", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 2 }, 0, 0, FLAGS, "force_oar" },
+    { "scale_mode", "specify how to scale subtitles", OFFSET(scale_mode), AV_OPT_TYPE_INT, {.i64 = SSM_UNIFORM}, 0, SSM_UNIFORM_NO_REPOSITION, FLAGS, "scale_mode" },
+         { "none",  "no common scaling", 0, AV_OPT_TYPE_CONST, {.i64=SSM_NONE},  .flags = FLAGS, .unit = "scale_mode" },
+         { "uniform",  "uniformly scale and reposition", 0, AV_OPT_TYPE_CONST, {.i64=SSM_UNIFORM},  .flags = FLAGS, .unit = "scale_mode" },
+         { "uniform_no_reposition",  "uniformly scale but keep positions", 0, AV_OPT_TYPE_CONST, {.i64=SSM_UNIFORM_NO_REPOSITION},  .flags = FLAGS, .unit = "scale_mode" },
+    { "use_caching", "Cache output frames",   OFFSET(use_caching), AV_OPT_TYPE_BOOL, { .i64 = 1}, 0, 1, .flags = FLAGS },
+
+    { "arrange_h", "specify how to arrange subtitles horizontally", OFFSET(arrange_mode_h), AV_OPT_TYPE_INT, {.i64 = SAM_NONE}, 0, SAM_SNAPALIGNMENT_AND_SCALE, FLAGS, "arrange" },
+    { "arrange_v", "specify how to arrange subtitles vertically", OFFSET(arrange_mode_v), AV_OPT_TYPE_INT, {.i64 = SAM_NONE}, 0, SAM_SNAPALIGNMENT_AND_SCALE, FLAGS, "arrange" },
+         { "none",  "no repositioning", 0, AV_OPT_TYPE_CONST, {.i64=SAM_NONE},  .flags = FLAGS, .unit = "arrange" },
+         { "margin_no_scale",  "move subs inside border when possible", 0, AV_OPT_TYPE_CONST, {.i64=SAM_ENSUREMARGIN_NO_SCALE,},  .flags = FLAGS, .unit = "arrange" },
+         { "margin_and_scale",  "move subs inside border and scale as needed", 0, AV_OPT_TYPE_CONST, {.i64=SAM_ENSUREMARGIN_AND_SCALE,},  .flags = FLAGS, .unit = "arrange" },
+         { "snapalign_no_scale",  "snap subs to near/far/center when possible", 0, AV_OPT_TYPE_CONST, {.i64=SAM_SNAPALIGNMENT_NO_SCALE,},  .flags = FLAGS, .unit = "arrange" },
+         { "snapalign_and_scale", "snap subs to near/far/center and scale as needed", 0, AV_OPT_TYPE_CONST, {.i64=SAM_SNAPALIGNMENT_AND_SCALE,}, .flags = FLAGS, .unit = "arrange" },
+
+    { "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, {.i64 = EVAL_MODE_INIT}, 0, EVAL_MODE_NB-1, FLAGS, "eval" },
+         { "init",  "eval expressions once during initialization", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_INIT},  .flags = FLAGS, .unit = "eval" },
+         { "frame", "eval expressions during initialization and per-frame", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_FRAME}, .flags = FLAGS, .unit = "eval" },
+    { "num_colors",     "number of palette colors in output", OFFSET(num_output_colors),  AV_OPT_TYPE_INT, {.i64 = 256 }, 2, 256, .flags = FLAGS },
+    { "bitmap_width_align",     "Bitmap width alignment", OFFSET(bitmap_width_align),  AV_OPT_TYPE_INT, {.i64 = 2 }, 1, 256, .flags = FLAGS },
+    { "bitmap_height_align",     "Bitmap height alignment", OFFSET(bitmap_height_align),  AV_OPT_TYPE_INT, {.i64 = 2 }, 1, 256, .flags = FLAGS },
+    { .name =  NULL }
+};
+
+static const AVClass subscale_class = {
+    .class_name       = "subscale",
+    .item_name        = av_default_item_name,
+    .option           = subscale_options,
+    .version          = LIBAVUTIL_VERSION_INT,
+    .category         = AV_CLASS_CATEGORY_FILTER,
+};
+
+static const AVFilterPad inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .filter_frame = filter_frame,
+        .config_props = config_input,
+    },
+};
+
+static const AVFilterPad outputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .config_props = config_output,
+    },
+};
+
+const AVFilter ff_sf_subscale = {
+    .name            = "subscale",
+    .description     = NULL_IF_CONFIG_SMALL("Scale graphical subtitles."),
+    .init_dict       = init_dict,
+    .uninit          = uninit,
+    .priv_size       = sizeof(SubScaleContext),
+    .priv_class      = &subscale_class,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_SINGLE_SUBFMT(AV_SUBTITLE_FMT_BITMAP),
+};
+
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v2 19/26] avfilter/subfeed: add subtitle feed filter
  2022-01-20  2:48 ` [FFmpeg-devel] [PATCH v2 00/26] Subtitle Filtering 2022 ffmpegagent
                     ` (17 preceding siblings ...)
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 18/26] avfilter/subscale: Add filter for scaling and/or re-arranging graphical subtitles ffmpegagent
@ 2022-01-20  2:48   ` ffmpegagent
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 20/26] avcodec/subtitles: Migrate subtitle encoders to frame-based API ffmpegagent
                     ` (7 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  2:48 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavfilter/Makefile     |   1 +
 libavfilter/allfilters.c |   1 +
 libavfilter/sf_subfeed.c | 366 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 368 insertions(+)
 create mode 100644 libavfilter/sf_subfeed.c

diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 0e3e48613e..5711c7770b 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -570,6 +570,7 @@ OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
 OBJS-$(CONFIG_SPLITCC_FILTER)                += sf_splitcc.o
 OBJS-$(CONFIG_STRIPSTYLES_FILTER)            += sf_stripstyles.o
 OBJS-$(CONFIG_SUBSCALE_FILTER)               += sf_subscale.o
+OBJS-$(CONFIG_SUBFEED_FILTER)                += sf_subfeed.o
 
 # multimedia filters
 OBJS-$(CONFIG_ABITSCOPE_FILTER)              += avf_abitscope.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 8e4f2feca3..10b14b7b8e 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -553,6 +553,7 @@ extern const AVFilter ff_sf_showspeaker;
 extern const AVFilter ff_sf_splitcc;
 extern const AVFilter ff_sf_stripstyles;
 extern const AVFilter ff_sf_subscale;
+extern const AVFilter ff_sf_subfeed;
 extern const AVFilter ff_sf_textmod;
 extern const AVFilter ff_svf_graphicsub2video;
 extern const AVFilter ff_svf_textsub2video;
diff --git a/libavfilter/sf_subfeed.c b/libavfilter/sf_subfeed.c
new file mode 100644
index 0000000000..7df6641f6a
--- /dev/null
+++ b/libavfilter/sf_subfeed.c
@@ -0,0 +1,366 @@
+/*
+ * 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
+ * subtitle filter for feeding subtitle frames into a filtergraph in a contiguous way
+ *
+ *
+ * also supports
+ *   - duration fixup
+ *     delaying a subtitle event with unknown duration and infer duration from the
+ *     start time of the subsequent subtitle
+ *   - scattering
+ *     splitting a subtitle event with unknown duration into multiple ones with
+ *     a short and fixed duration
+ *
+ */
+
+#include "filters.h"
+#include "libavutil/opt.h"
+#include "subtitles.h"
+#include "libavutil/avassert.h"
+
+enum SubFeedMode {
+    FM_REPEAT,
+    FM_SCATTER,
+    FM_FORWARD,
+};
+
+typedef struct SubFeedContext {
+    const AVClass *class;
+    enum AVSubtitleType format;
+    enum SubFeedMode mode;
+
+    AVRational frame_rate;
+    int fix_durations;
+    int fix_overlap;
+
+    int current_frame_isnew;
+    int eof;
+    int got_first_input;
+    int need_frame;
+    int64_t next_pts_offset;
+    int64_t recent_subtitle_pts;
+
+    int64_t counter;
+
+    /**
+     * Queue of frames waiting to be filtered.
+     */
+    FFFrameQueue fifo;
+
+} SubFeedContext;
+
+static int64_t ms_to_avtb(int64_t ms)
+{
+    return av_rescale_q(ms, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q);
+}
+
+static int64_t avtb_to_ms(int64_t avtb)
+{
+    return av_rescale_q(avtb, AV_TIME_BASE_Q, (AVRational){ 1, 1000 });
+}
+
+static int init(AVFilterContext *ctx)
+{
+    SubFeedContext *s = ctx->priv;
+
+    ff_framequeue_init(&s->fifo, NULL);
+
+    return 0;
+}
+
+static void uninit(AVFilterContext *ctx)
+{
+    SubFeedContext *s = ctx->priv;
+    ff_framequeue_free(&s->fifo);
+}
+
+static int config_input(AVFilterLink *link)
+{
+    ////const subfeedContext *context = link->dst->priv;
+
+    return 0;
+}
+
+static int query_formats(AVFilterContext *ctx)
+{
+    AVFilterFormats *formats;
+    AVFilterLink *inlink0 = ctx->inputs[0];
+    AVFilterLink *outlink0 = ctx->outputs[0];
+    static const enum AVSubtitleType subtitle_fmts[] = { AV_SUBTITLE_FMT_BITMAP, AV_SUBTITLE_FMT_ASS, AV_SUBTITLE_FMT_NB };
+    int ret;
+
+    formats = ff_make_format_list(subtitle_fmts);
+
+    if ((ret = ff_formats_ref(formats, &inlink0->outcfg.formats)) < 0)
+        return ret;
+
+    if ((ret = ff_formats_ref(formats, &outlink0->incfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    SubFeedContext *s = outlink->src->priv;
+    const AVFilterLink *inlink = outlink->src->inputs[0];
+
+    outlink->time_base = AV_TIME_BASE_Q;
+    outlink->format = inlink->format;
+    outlink->w = inlink->w;
+    outlink->h = inlink->h;
+
+    if (s->mode == FM_FORWARD)
+        outlink->frame_rate = (AVRational) { 1, 0 };
+    else
+        outlink->frame_rate = s->frame_rate;
+
+    return 0;
+}
+
+static int request_frame(AVFilterLink *outlink)
+{
+    SubFeedContext *s = outlink->src->priv;
+    AVFilterLink *inlink = outlink->src->inputs[0];
+    int64_t last_pts = outlink->current_pts;
+    int64_t next_pts;
+    int64_t interval = ms_to_avtb((int64_t)(av_q2d(av_inv_q(outlink->frame_rate)) * 1000));
+    AVFrame *out;
+    int status;
+
+    if (s->mode == FM_FORWARD)
+        return ff_request_frame(inlink);
+
+    s->counter++;
+    if (interval == 0)
+        interval =  ms_to_avtb(200);
+
+    status = ff_outlink_get_status(inlink);
+    if (status == AVERROR_EOF)
+        s->eof = 1;
+
+    if (s->eof)
+        return AVERROR_EOF;
+
+    if (!s->got_first_input && inlink->current_pts != AV_NOPTS_VALUE) {
+
+        s->got_first_input = 1;
+        next_pts = av_rescale_q(inlink->current_pts, inlink->time_base, AV_TIME_BASE_Q);
+        if (next_pts < last_pts)
+            next_pts = last_pts + interval;
+
+    } else if (last_pts == AV_NOPTS_VALUE)
+        next_pts = av_rescale_q(inlink->current_pts, inlink->time_base, AV_TIME_BASE_Q);
+    else
+        next_pts = last_pts + interval;
+
+    if (next_pts == AV_NOPTS_VALUE)
+        next_pts = 0;
+
+    if (s->next_pts_offset) {
+        next_pts -= s->next_pts_offset;
+        s->next_pts_offset = 0;
+    }
+
+retry:
+    if (ff_framequeue_queued_frames(&s->fifo) && !s->current_frame_isnew) {
+
+        const AVFrame *current_frame = ff_framequeue_peek(&s->fifo, 0);
+        const int64_t sub_end_time   = current_frame->subtitle_timing.start_pts + current_frame->subtitle_timing.duration;
+
+        if (next_pts > sub_end_time) {
+            AVFrame *remove_frame = ff_framequeue_take(&s->fifo);
+            av_frame_free(&remove_frame);
+
+            if (ff_framequeue_queued_frames(&s->fifo)) {
+                s->current_frame_isnew = 1;
+                goto retry;
+            }
+        }
+    }
+
+    if (ff_framequeue_queued_frames(&s->fifo)) {
+        AVFrame *current_frame = ff_framequeue_peek(&s->fifo, 0);
+
+        if (current_frame && current_frame->subtitle_timing.start_pts <= next_pts + interval) {
+            if (!s->current_frame_isnew)
+                current_frame->repeat_sub++;
+
+            out = av_frame_clone(current_frame);
+
+            if (!out)
+                return AVERROR(ENOMEM);
+
+            if (!s->current_frame_isnew) {
+                out->pts = next_pts;
+            } else {
+                out->pts = out->subtitle_timing.start_pts;
+
+                if (out->pts < next_pts)
+                    out->pts = next_pts;
+
+                s->next_pts_offset = (out->pts - next_pts) % interval;
+            }
+
+            if (s->mode == FM_SCATTER) {
+                const int64_t sub_end_time  = current_frame->subtitle_timing.start_pts + current_frame->subtitle_timing.duration;
+
+                if (s->current_frame_isnew == 1 && current_frame->subtitle_timing.start_pts < out->pts) {
+                    const int64_t diff = out->pts - current_frame->subtitle_timing.start_pts;
+                    current_frame->subtitle_timing.duration -= diff;
+                }
+
+                out->repeat_sub = 0;
+                out->subtitle_timing.start_pts = out->pts;
+                out->subtitle_timing.duration = interval;
+                av_assert1(out->pts >= next_pts);
+                av_assert1(out->pts < next_pts + interval);
+                av_assert1(out->pts < sub_end_time);
+
+                if (out->pts > next_pts)
+                    out->subtitle_timing.duration -= out->pts - next_pts;
+
+                if (sub_end_time < next_pts + interval) {
+                    const int64_t diff = next_pts + interval - sub_end_time;
+                    av_assert1(diff <= out->subtitle_timing.duration);
+                    out->subtitle_timing.duration -= diff;
+                }
+            }
+
+            s->current_frame_isnew = 0;
+            s->recent_subtitle_pts = out->subtitle_timing.start_pts;
+
+            return ff_filter_frame(outlink, out);
+        }
+    }
+
+    if (ff_framequeue_queued_frames(&s->fifo) == 0) {
+        status = ff_request_frame(inlink);
+        if (status == AVERROR_EOF) {
+            s->eof = 1;
+            return status;
+        }
+
+        if (s->counter > 1 && s->counter % 2)
+            return 0;
+    }
+
+    out = ff_get_subtitles_buffer(outlink, outlink->format);
+    out->pts = next_pts;
+    out->repeat_sub = 1;
+    out->subtitle_timing.start_pts = s->recent_subtitle_pts;
+
+    return ff_filter_frame(outlink, out);
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFilterContext *ctx        = inlink->dst;
+    SubFeedContext *s           = inlink->dst->priv;
+    AVFilterLink *outlink       = inlink->dst->outputs[0];
+    const int64_t index         = (int64_t)ff_framequeue_queued_frames(&s->fifo) - 1;
+    int ret = 0;
+
+    frame->pts = av_rescale_q(frame->pts, inlink->time_base, AV_TIME_BASE_Q);
+
+    if (index < 0) {
+        s->current_frame_isnew = 1;
+    } else if (s->fix_durations || s->fix_overlap) {
+        AVFrame *previous_frame = ff_framequeue_peek(&s->fifo, index);
+        const int64_t pts_diff = frame->subtitle_timing.start_pts - previous_frame->subtitle_timing.start_pts;
+
+        if (s->fix_durations && pts_diff > 0 && previous_frame->subtitle_timing.duration > ms_to_avtb(29000))
+                    previous_frame->subtitle_timing.duration = frame->subtitle_timing.start_pts - previous_frame->subtitle_timing.start_pts;
+
+        if (s->fix_overlap && pts_diff > 0 && previous_frame->subtitle_timing.duration > pts_diff)
+                    previous_frame->subtitle_timing.duration = pts_diff;
+    }
+
+    ff_framequeue_add(&s->fifo, frame);
+
+    if (index > 3)
+        av_log(ctx, AV_LOG_WARNING, "frame queue count: %d\n", (int)index);
+
+    if (s->mode == FM_FORWARD && ff_framequeue_queued_frames(&s->fifo)) {
+
+        AVFrame *first_frame = ff_framequeue_peek(&s->fifo, 0);
+
+        if (s->fix_overlap && ff_framequeue_queued_frames(&s->fifo) < 2)
+            return 0;
+
+        if (s->fix_durations && first_frame->subtitle_timing.duration > ms_to_avtb(29000))
+            return 0;
+
+        first_frame = ff_framequeue_take(&s->fifo);
+        return ff_filter_frame(outlink, first_frame);
+    }
+
+    return ret;
+}
+
+#define OFFSET(x) offsetof(SubFeedContext, x)
+#define FLAGS (AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption subfeed_options[] = {
+    { "fix_durations", "delay output and determine duration from next frame", OFFSET(fix_durations),   AV_OPT_TYPE_BOOL,   { .i64=1 },                  0, 1, FLAGS, NULL     },
+    { "fix_overlap", "delay output and adjust durations to prevent overlap", OFFSET(fix_overlap),   AV_OPT_TYPE_BOOL,   { .i64=0 },                  0, 1, FLAGS, NULL     },
+    { "mode",       "set feed mode",         OFFSET(mode),      AV_OPT_TYPE_INT,                 { .i64=FM_REPEAT },  FM_REPEAT, FM_FORWARD,  FLAGS, "mode" },
+    {   "repeat",     "repeat recent while valid, send empty otherwise",   0, AV_OPT_TYPE_CONST, { .i64=FM_REPEAT },  0,                  0,  FLAGS, "mode" },
+    {   "scatter",    "subdivide subtitles into 1/framerate segments",     0, AV_OPT_TYPE_CONST, { .i64=FM_SCATTER }, 0,                  0,  FLAGS, "mode" },
+    {   "forward",    "forward only (clears output framerate)",            0, AV_OPT_TYPE_CONST, { .i64=FM_FORWARD }, 0,                  0,  FLAGS, "mode" },
+    { "rate",       "output frame rate",     OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE,         { .str = "5"},       0,         INT_MAX,     FLAGS, NULL },\
+    { "r",          "output frame rate",     OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE,         { .str = "5"},       0,         INT_MAX,     FLAGS, NULL },\
+    { NULL },
+};
+
+AVFILTER_DEFINE_CLASS(subfeed);
+
+static const AVFilterPad inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .filter_frame = filter_frame,
+        .config_props = config_input,
+    },
+};
+
+static const AVFilterPad outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_SUBTITLE,
+        .request_frame = request_frame,
+        .config_props  = config_output,
+    },
+};
+
+const AVFilter ff_sf_subfeed = {
+    .name           = "subfeed",
+    .description    = NULL_IF_CONFIG_SMALL("Control subtitle frame timing and flow in a filtergraph"),
+    .init           = init,
+    .uninit         = uninit,
+    .priv_size      = sizeof(SubFeedContext),
+    .priv_class     = &subfeed_class,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_QUERY_FUNC(query_formats),
+};
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v2 20/26] avcodec/subtitles: Migrate subtitle encoders to frame-based API
  2022-01-20  2:48 ` [FFmpeg-devel] [PATCH v2 00/26] Subtitle Filtering 2022 ffmpegagent
                     ` (18 preceding siblings ...)
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 19/26] avfilter/subfeed: add subtitle feed filter ffmpegagent
@ 2022-01-20  2:48   ` ffmpegagent
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 21/26] fftools/ffmpeg: Introduce subtitle filtering and new frame-based subtitle encoding ffmpegagent
                     ` (6 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  2:48 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

and provide a compatibility shim for the legacy api

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/assenc.c        | 189 ++++++++++++++++++++++++++++++-------
 libavcodec/avcodec.h       |   5 +-
 libavcodec/dvbsubenc.c     |  96 ++++++++++---------
 libavcodec/dvdsubenc.c     | 102 ++++++++++++--------
 libavcodec/encode.c        |  61 +++++++++++-
 libavcodec/movtextenc.c    | 114 ++++++++++++++++------
 libavcodec/srtenc.c        | 108 ++++++++++++++-------
 libavcodec/tests/avcodec.c |   2 -
 libavcodec/ttmlenc.c       | 101 +++++++++++++++-----
 libavcodec/webvttenc.c     |  86 ++++++++++++-----
 libavcodec/xsubenc.c       |  88 ++++++++++-------
 11 files changed, 687 insertions(+), 265 deletions(-)

diff --git a/libavcodec/assenc.c b/libavcodec/assenc.c
index b0e475834b..e1401b1ac5 100644
--- a/libavcodec/assenc.c
+++ b/libavcodec/assenc.c
@@ -22,70 +22,195 @@
 #include <string.h>
 
 #include "avcodec.h"
+#include "encode.h"
 #include "libavutil/ass_internal.h"
 #include "internal.h"
 #include "libavutil/avstring.h"
 #include "libavutil/internal.h"
 #include "libavutil/mem.h"
 
+typedef struct {
+    AVCodecContext *avctx;
+    AVFrame* current_frame;
+    int have_frame;
+    int current_area;
+} AssEncContext;
+
+static void check_write_header(AVCodecContext* avctx, const AVFrame* frame)
+{
+    if (avctx->extradata_size)
+        return;
+
+    if (frame->subtitle_header && frame->subtitle_header->size > 0) {
+        const char* subtitle_header = (char*)frame->subtitle_header->data;
+        avctx->extradata_size = strlen(subtitle_header);
+        avctx->extradata = av_mallocz(frame->subtitle_header->size + 1);
+        memcpy(avctx->extradata, subtitle_header, avctx->extradata_size);
+        avctx->extradata[avctx->extradata_size] = 0;
+    }
+
+    if (!avctx->extradata_size) {
+        const char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+        if (!subtitle_header)
+            return;
+
+        avctx->extradata_size = strlen(subtitle_header);
+        avctx->extradata = av_mallocz(avctx->extradata_size + 1);
+        memcpy(avctx->extradata, subtitle_header, avctx->extradata_size);
+        avctx->extradata[avctx->extradata_size] = 0;
+        av_freep(&subtitle_header);
+    }
+}
+
 static av_cold int ass_encode_init(AVCodecContext *avctx)
 {
-    avctx->extradata = av_malloc(avctx->subtitle_header_size + 1);
-    if (!avctx->extradata)
-        return AVERROR(ENOMEM);
-    memcpy(avctx->extradata, avctx->subtitle_header, avctx->subtitle_header_size);
-    avctx->extradata_size = avctx->subtitle_header_size;
-    avctx->extradata[avctx->extradata_size] = 0;
+    AssEncContext *s = avctx->priv_data;
+
+    if (avctx->subtitle_header_size) {
+        avctx->extradata = av_malloc(avctx->subtitle_header_size + 1);
+        if (!avctx->extradata)
+            return AVERROR(ENOMEM);
+        memcpy(avctx->extradata, avctx->subtitle_header, avctx->subtitle_header_size);
+        avctx->extradata_size                   = avctx->subtitle_header_size;
+        avctx->extradata[avctx->extradata_size] = 0;
+    }
+
+    s->current_frame = av_frame_alloc();
+    return 0;
+}
+
+static av_cold int ass_encode_close(AVCodecContext *avctx)
+{
+    AssEncContext *s = avctx->priv_data;
+    av_frame_free(&s->current_frame);
     return 0;
 }
 
-static int ass_encode_frame(AVCodecContext *avctx,
-                            unsigned char *buf, int bufsize,
-                            const AVSubtitle *sub)
+////static int ass_encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+////                            const AVFrame* frame, int* got_packet)
+////{
+////    int ret;
+////    size_t req_len = 0, total_len = 0;
+////
+////    check_write_header(avctx, frame);
+////
+////    for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+////        const char *ass = frame->subtitle_areas[i]->ass;
+////
+////        if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) {
+////            av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
+////            return AVERROR(EINVAL);
+////        }
+////
+////        if (ass)
+////            req_len += strlen(ass);
+////    }
+////
+////    ret = ff_get_encode_buffer(avctx, avpkt, req_len + 1, 0);
+////    if (ret < 0) {
+////        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+////        return ret;
+////    }
+////
+////    for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+////        const char *ass = frame->subtitle_areas[i]->ass;
+////
+////        if (ass) {
+////            size_t len = av_strlcpy((char *)avpkt->data + total_len, ass, avpkt->size - total_len);
+////            total_len += len;
+////        }
+////    }
+////
+////    avpkt->size = total_len;
+////    *got_packet = total_len > 0;
+////
+////    return 0;
+////}
+
+static int ass_receive_packet(AVCodecContext *avctx, AVPacket *avpkt)
 {
-    int i, len, total_len = 0;
+    AssEncContext *s = avctx->priv_data;
+    int ret;
+
+    if (!s->have_frame) {
+        s->current_area = 0;
+        ret = ff_encode_get_frame(avctx, s->current_frame);
+
+        if (ret < 0) {
+            av_frame_unref(s->current_frame);
+            return ret;
+        }
+
+        s->have_frame = 1;
+    }
 
-    for (i=0; i<sub->num_rects; i++) {
-        const char *ass = sub->rects[i]->ass;
+    check_write_header(avctx, s->current_frame);
 
-        if (sub->rects[i]->type != SUBTITLE_ASS) {
-            av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
+    if (s->current_frame->repeat_sub) {
+        av_frame_unref(s->current_frame);
+        s->have_frame = 0;
+        return AVERROR(EAGAIN);
+    }
+
+    if (s->current_area < s->current_frame->num_subtitle_areas) {
+        const AVSubtitleArea *area = s->current_frame->subtitle_areas[s->current_area];
+        const char *ass = area->ass;
+
+        if (area->type != AV_SUBTITLE_FMT_ASS) {
+            av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
             return AVERROR(EINVAL);
         }
 
-        len = av_strlcpy(buf+total_len, ass, bufsize-total_len);
+        if (ass) {
+            size_t len = strlen(ass);
+
+            ret = ff_get_encode_buffer(avctx, avpkt, len + 1, 0);
+            if (ret < 0) {
+                av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+                return ret;
+            }
+
+            len = av_strlcpy((char *)avpkt->data, ass, avpkt->size);
 
-        if (len > bufsize-total_len-1) {
-            av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n");
-            return AVERROR_BUFFER_TOO_SMALL;
+            avpkt->size = len;
         }
 
-        total_len += len;
+        s->current_area++;
     }
 
-    return total_len;
+    if (s->current_area < s->current_frame->num_subtitle_areas)
+        return 0;
+
+    av_frame_unref(s->current_frame);
+    s->have_frame = 0;
+
+    return 0;
 }
 
 #if CONFIG_SSA_ENCODER
 const AVCodec ff_ssa_encoder = {
-    .name         = "ssa",
-    .long_name    = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"),
-    .type         = AVMEDIA_TYPE_SUBTITLE,
-    .id           = AV_CODEC_ID_ASS,
-    .init         = ass_encode_init,
-    .encode_sub   = ass_encode_frame,
+    .name           = "ssa",
+    .long_name      = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"),
+    .type           = AVMEDIA_TYPE_SUBTITLE,
+    .id             = AV_CODEC_ID_ASS,
+    .priv_data_size = sizeof(AssEncContext),
+    .init           = ass_encode_init,
+    .close          = ass_encode_close,
+    .receive_packet = ass_receive_packet,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
 };
 #endif
 
 #if CONFIG_ASS_ENCODER
 const AVCodec ff_ass_encoder = {
-    .name         = "ass",
-    .long_name    = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"),
-    .type         = AVMEDIA_TYPE_SUBTITLE,
-    .id           = AV_CODEC_ID_ASS,
-    .init         = ass_encode_init,
-    .encode_sub   = ass_encode_frame,
+    .name           = "ass",
+    .long_name      = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"),
+    .type           = AVMEDIA_TYPE_SUBTITLE,
+    .priv_data_size = sizeof(AssEncContext),
+    .id             = AV_CODEC_ID_ASS,
+    .init           = ass_encode_init,
+    .close          = ass_encode_close,
+    .receive_packet = ass_receive_packet,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
 };
 #endif
diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index 9d59f6e840..93063dc6e9 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -2995,10 +2995,13 @@ void av_parser_close(AVCodecParserContext *s);
  * @{
  */
 
+ /**
+  * @deprecated Use @ref avcodec_encode_subtitle2() instead.
+  */
+attribute_deprecated
 int avcodec_encode_subtitle(AVCodecContext *avctx, uint8_t *buf, int buf_size,
                             const AVSubtitle *sub);
 
-
 /**
  * @}
  */
diff --git a/libavcodec/dvbsubenc.c b/libavcodec/dvbsubenc.c
index 322fc27cb4..3b5a76daa7 100644
--- a/libavcodec/dvbsubenc.c
+++ b/libavcodec/dvbsubenc.c
@@ -20,6 +20,7 @@
  */
 #include "avcodec.h"
 #include "bytestream.h"
+#include "encode.h"
 #include "libavutil/colorspace.h"
 
 typedef struct DVBSubtitleContext {
@@ -268,21 +269,29 @@ static int dvb_encode_rle8(uint8_t **pq, int buf_size,
     return len;
 }
 
-static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
-                         const AVSubtitle *h)
+static int dvbsub_encode(AVCodecContext* avctx, AVPacket* avpkt,
+                         const AVFrame* frame, int* got_packet)
 {
     DVBSubtitleContext *s = avctx->priv_data;
     uint8_t *q, *pseg_len;
     int page_id, region_id, clut_id, object_id, i, bpp_index, page_state;
-
-
-    q = outbuf;
+    size_t buf_size;
+    int ret;
 
     page_id = 1;
 
-    if (h->num_rects && !h->rects)
+    if (frame->num_subtitle_areas && !frame->subtitle_areas)
         return AVERROR(EINVAL);
 
+    ret = ff_get_encode_buffer(avctx, avpkt, 1024 * 1024, 0);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+        return ret;
+    }
+
+    buf_size = avpkt->size;
+    q = avpkt->data;
+
     if (avctx->width > 0 && avctx->height > 0) {
         if (buf_size < 11)
             return AVERROR_BUFFER_TOO_SMALL;
@@ -301,7 +310,7 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
 
     /* page composition segment */
 
-    if (buf_size < 8 + h->num_rects * 6)
+    if (buf_size < 8 + frame->num_subtitle_areas * 6)
         return AVERROR_BUFFER_TOO_SMALL;
     *q++ = 0x0f; /* sync_byte */
     *q++ = 0x10; /* segment_type */
@@ -313,30 +322,30 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
     /* page_version = 0 + page_state */
     *q++ = (s->object_version << 4) | (page_state << 2) | 3;
 
-    for (region_id = 0; region_id < h->num_rects; region_id++) {
+    for (region_id = 0; region_id < frame->num_subtitle_areas; region_id++) {
         *q++ = region_id;
         *q++ = 0xff; /* reserved */
-        bytestream_put_be16(&q, h->rects[region_id]->x); /* left pos */
-        bytestream_put_be16(&q, h->rects[region_id]->y); /* top pos */
+        bytestream_put_be16(&q, frame->subtitle_areas[region_id]->x); /* left pos */
+        bytestream_put_be16(&q, frame->subtitle_areas[region_id]->y); /* top pos */
     }
 
     bytestream_put_be16(&pseg_len, q - pseg_len - 2);
-    buf_size -= 8 + h->num_rects * 6;
+    buf_size -= 8 + frame->num_subtitle_areas * 6;
 
-    if (h->num_rects) {
-        for (clut_id = 0; clut_id < h->num_rects; clut_id++) {
-            if (buf_size < 6 + h->rects[clut_id]->nb_colors * 6)
+    if (frame->num_subtitle_areas) {
+        for (clut_id = 0; clut_id < frame->num_subtitle_areas; clut_id++) {
+            if (buf_size < 6 + frame->subtitle_areas[clut_id]->nb_colors * 6)
                 return AVERROR_BUFFER_TOO_SMALL;
 
             /* CLUT segment */
 
-            if (h->rects[clut_id]->nb_colors <= 4) {
+            if (frame->subtitle_areas[clut_id]->nb_colors <= 4) {
                 /* 2 bpp, some decoders do not support it correctly */
                 bpp_index = 0;
-            } else if (h->rects[clut_id]->nb_colors <= 16) {
+            } else if (frame->subtitle_areas[clut_id]->nb_colors <= 16) {
                 /* 4 bpp, standard encoding */
                 bpp_index = 1;
-            } else if (h->rects[clut_id]->nb_colors <= 256) {
+            } else if (frame->subtitle_areas[clut_id]->nb_colors <= 256) {
                 /* 8 bpp, standard encoding */
                 bpp_index = 2;
             } else {
@@ -353,12 +362,12 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
             *q++ = clut_id;
             *q++ = (0 << 4) | 0xf; /* version = 0 */
 
-            for(i = 0; i < h->rects[clut_id]->nb_colors; i++) {
+            for(i = 0; i < frame->subtitle_areas[clut_id]->nb_colors; i++) {
                 *q++ = i; /* clut_entry_id */
                 *q++ = (1 << (7 - bpp_index)) | (0xf << 1) | 1; /* 2 bits/pixel full range */
                 {
                     int a, r, g, b;
-                    uint32_t x= ((uint32_t*)h->rects[clut_id]->data[1])[i];
+                    uint32_t x= ((uint32_t*)frame->subtitle_areas[clut_id]->pal)[i];
                     a = (x >> 24) & 0xff;
                     r = (x >> 16) & 0xff;
                     g = (x >>  8) & 0xff;
@@ -372,22 +381,22 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
             }
 
             bytestream_put_be16(&pseg_len, q - pseg_len - 2);
-            buf_size -= 6 + h->rects[clut_id]->nb_colors * 6;
+            buf_size -= 6 + frame->subtitle_areas[clut_id]->nb_colors * 6;
         }
 
-        if (buf_size < h->num_rects * 22)
+        if (buf_size < frame->num_subtitle_areas * 22)
             return AVERROR_BUFFER_TOO_SMALL;
-        for (region_id = 0; region_id < h->num_rects; region_id++) {
+        for (region_id = 0; region_id < frame->num_subtitle_areas; region_id++) {
 
             /* region composition segment */
 
-            if (h->rects[region_id]->nb_colors <= 4) {
+            if (frame->subtitle_areas[region_id]->nb_colors <= 4) {
                 /* 2 bpp, some decoders do not support it correctly */
                 bpp_index = 0;
-            } else if (h->rects[region_id]->nb_colors <= 16) {
+            } else if (frame->subtitle_areas[region_id]->nb_colors <= 16) {
                 /* 4 bpp, standard encoding */
                 bpp_index = 1;
-            } else if (h->rects[region_id]->nb_colors <= 256) {
+            } else if (frame->subtitle_areas[region_id]->nb_colors <= 256) {
                 /* 8 bpp, standard encoding */
                 bpp_index = 2;
             } else {
@@ -401,8 +410,8 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
             q += 2; /* segment length */
             *q++ = region_id;
             *q++ = (s->object_version << 4) | (0 << 3) | 0x07; /* version , no fill */
-            bytestream_put_be16(&q, h->rects[region_id]->w); /* region width */
-            bytestream_put_be16(&q, h->rects[region_id]->h); /* region height */
+            bytestream_put_be16(&q, frame->subtitle_areas[region_id]->w); /* region width */
+            bytestream_put_be16(&q, frame->subtitle_areas[region_id]->h); /* region height */
             *q++ = ((1 + bpp_index) << 5) | ((1 + bpp_index) << 2) | 0x03;
             *q++ = region_id; /* clut_id == region_id */
             *q++ = 0; /* 8 bit fill colors */
@@ -416,9 +425,9 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
 
             bytestream_put_be16(&pseg_len, q - pseg_len - 2);
         }
-        buf_size -= h->num_rects * 22;
+        buf_size -= frame->num_subtitle_areas * 22;
 
-        for (object_id = 0; object_id < h->num_rects; object_id++) {
+        for (object_id = 0; object_id < frame->num_subtitle_areas; object_id++) {
             int (*dvb_encode_rle)(uint8_t **pq, int buf_size,
                                   const uint8_t *bitmap, int linesize,
                                   int w, int h);
@@ -427,13 +436,13 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
                 return AVERROR_BUFFER_TOO_SMALL;
 
             /* bpp_index maths */
-            if (h->rects[object_id]->nb_colors <= 4) {
+            if (frame->subtitle_areas[object_id]->nb_colors <= 4) {
                 /* 2 bpp, some decoders do not support it correctly */
                 dvb_encode_rle = dvb_encode_rle2;
-            } else if (h->rects[object_id]->nb_colors <= 16) {
+            } else if (frame->subtitle_areas[object_id]->nb_colors <= 16) {
                 /* 4 bpp, standard encoding */
                 dvb_encode_rle = dvb_encode_rle4;
-            } else if (h->rects[object_id]->nb_colors <= 256) {
+            } else if (frame->subtitle_areas[object_id]->nb_colors <= 256) {
                 /* 8 bpp, standard encoding */
                 dvb_encode_rle = dvb_encode_rle8;
             } else {
@@ -463,19 +472,19 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
 
                 top_ptr = q;
                 ret = dvb_encode_rle(&q, buf_size,
-                                     h->rects[object_id]->data[0],
-                                     h->rects[object_id]->w * 2,
-                                     h->rects[object_id]->w,
-                                     h->rects[object_id]->h >> 1);
+                                     frame->subtitle_areas[object_id]->buf[0]->data,
+                                     frame->subtitle_areas[object_id]->w * 2,
+                                     frame->subtitle_areas[object_id]->w,
+                                     frame->subtitle_areas[object_id]->h >> 1);
                 if (ret < 0)
                     return ret;
                 buf_size -= ret;
                 bottom_ptr = q;
                 ret = dvb_encode_rle(&q, buf_size,
-                                     h->rects[object_id]->data[0] + h->rects[object_id]->w,
-                                     h->rects[object_id]->w * 2,
-                                     h->rects[object_id]->w,
-                                     h->rects[object_id]->h >> 1);
+                                     frame->subtitle_areas[object_id]->buf[0]->data + frame->subtitle_areas[object_id]->w,
+                                     frame->subtitle_areas[object_id]->w * 2,
+                                     frame->subtitle_areas[object_id]->w,
+                                     frame->subtitle_areas[object_id]->h >> 1);
                 if (ret < 0)
                     return ret;
                 buf_size -= ret;
@@ -502,7 +511,10 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
     buf_size -= 6;
 
     s->object_version = (s->object_version + 1) & 0xf;
-    return q - outbuf;
+    avpkt->size = q - avpkt->data;
+    *got_packet = 1;
+
+    return 0;
 }
 
 const AVCodec ff_dvbsub_encoder = {
@@ -511,5 +523,5 @@ const AVCodec ff_dvbsub_encoder = {
     .type           = AVMEDIA_TYPE_SUBTITLE,
     .id             = AV_CODEC_ID_DVB_SUBTITLE,
     .priv_data_size = sizeof(DVBSubtitleContext),
-    .encode_sub     = dvbsub_encode,
+    .encode2        = dvbsub_encode,
 };
diff --git a/libavcodec/dvdsubenc.c b/libavcodec/dvdsubenc.c
index 943a7466d9..ff21e75ef3 100644
--- a/libavcodec/dvdsubenc.c
+++ b/libavcodec/dvdsubenc.c
@@ -20,6 +20,7 @@
  */
 #include "avcodec.h"
 #include "bytestream.h"
+#include "encode.h"
 #include "internal.h"
 #include "libavutil/avassert.h"
 #include "libavutil/bprint.h"
@@ -114,15 +115,14 @@ static int color_distance(uint32_t a, uint32_t b)
  * Count colors used in a rectangle, quantizing alpha and grouping by
  * nearest global palette entry.
  */
-static void count_colors(AVCodecContext *avctx, unsigned hits[33],
-                         const AVSubtitleRect *r)
+static void count_colors(const AVCodecContext *avctx, unsigned hits[33],
+                         const AVSubtitleArea *r)
 {
     DVDSubtitleContext *dvdc = avctx->priv_data;
     unsigned count[256] = { 0 };
-    uint32_t *palette = (uint32_t *)r->data[1];
     uint32_t color;
     int x, y, i, j, match, d, best_d, av_uninit(best_j);
-    uint8_t *p = r->data[0];
+    uint8_t *p = r->buf[0]->data;
 
     for (y = 0; y < r->h; y++) {
         for (x = 0; x < r->w; x++)
@@ -132,7 +132,7 @@ static void count_colors(AVCodecContext *avctx, unsigned hits[33],
     for (i = 0; i < 256; i++) {
         if (!count[i]) /* avoid useless search */
             continue;
-        color = palette[i];
+        color = r->pal[i];
         /* 0: transparent, 1-16: semi-transparent, 17-33 opaque */
         match = color < 0x33000000 ? 0 : color < 0xCC000000 ? 1 : 17;
         if (match) {
@@ -232,13 +232,13 @@ static void build_color_map(AVCodecContext *avctx, int cmap[],
     }
 }
 
-static void copy_rectangle(AVSubtitleRect *dst, AVSubtitleRect *src, int cmap[])
+static void copy_rectangle(AVSubtitleArea*dst, AVSubtitleArea *src, int cmap[])
 {
     int x, y;
     uint8_t *p, *q;
 
-    p = src->data[0];
-    q = dst->data[0] + (src->x - dst->x) +
+    p = src->buf[0]->data;
+    q = dst->buf[0]->data + (src->x - dst->x) +
                             (src->y - dst->y) * dst->linesize[0];
     for (y = 0; y < src->h; y++) {
         for (x = 0; x < src->w; x++)
@@ -248,51 +248,57 @@ static void copy_rectangle(AVSubtitleRect *dst, AVSubtitleRect *src, int cmap[])
     }
 }
 
-static int encode_dvd_subtitles(AVCodecContext *avctx,
-                                uint8_t *outbuf, int outbuf_size,
-                                const AVSubtitle *h)
+static int encode_dvd_subtitles(AVCodecContext* avctx, AVPacket* avpkt,
+                                const AVFrame* frame, int* got_packet)
 {
     DVDSubtitleContext *dvdc = avctx->priv_data;
     uint8_t *q, *qq;
     int offset1, offset2;
-    int i, rects = h->num_rects, ret;
+    int ret = 0;
+    unsigned i, rects = frame->num_subtitle_areas;
     unsigned global_palette_hits[33] = { 0 };
     int cmap[256];
     int out_palette[4];
     int out_alpha[4];
-    AVSubtitleRect vrect;
-    uint8_t *vrect_data = NULL;
+    AVSubtitleArea vrect;
+    uint8_t* vrect_data = NULL, *outbuf;
     int x2, y2;
     int forced = 0;
+    int outbuf_size;
+    int64_t duration_ms;
 
-    if (rects == 0 || !h->rects)
+    if (rects == 0)
+        return 0;
+
+    if (!frame->subtitle_areas)
         return AVERROR(EINVAL);
+
     for (i = 0; i < rects; i++)
-        if (h->rects[i]->type != AV_SUBTITLE_FMT_BITMAP) {
+        if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_BITMAP) {
             av_log(avctx, AV_LOG_ERROR, "Bitmap subtitle required\n");
             return AVERROR(EINVAL);
         }
     /* Mark this subtitle forced if any of the rectangles is forced. */
     for (i = 0; i < rects; i++)
-        if ((h->rects[i]->flags & AV_SUBTITLE_FLAG_FORCED) != 0) {
+        if ((frame->subtitle_areas[i]->flags & AV_SUBTITLE_FLAG_FORCED) != 0) {
             forced = 1;
             break;
         }
 
-    vrect = *h->rects[0];
+    vrect = *frame->subtitle_areas[0];
 
     if (rects > 1) {
         /* DVD subtitles can have only one rectangle: build a virtual
            rectangle containing all actual rectangles.
            The data of the rectangles will be copied later, when the palette
            is decided, because the rectangles may have different palettes. */
-        int xmin = h->rects[0]->x, xmax = xmin + h->rects[0]->w;
-        int ymin = h->rects[0]->y, ymax = ymin + h->rects[0]->h;
+        int xmin = frame->subtitle_areas[0]->x, xmax = xmin + frame->subtitle_areas[0]->w;
+        int ymin = frame->subtitle_areas[0]->y, ymax = ymin + frame->subtitle_areas[0]->h;
         for (i = 1; i < rects; i++) {
-            xmin = FFMIN(xmin, h->rects[i]->x);
-            ymin = FFMIN(ymin, h->rects[i]->y);
-            xmax = FFMAX(xmax, h->rects[i]->x + h->rects[i]->w);
-            ymax = FFMAX(ymax, h->rects[i]->y + h->rects[i]->h);
+            xmin = FFMIN(xmin, frame->subtitle_areas[i]->x);
+            ymin = FFMIN(ymin, frame->subtitle_areas[i]->y);
+            xmax = FFMAX(xmax, frame->subtitle_areas[i]->x + frame->subtitle_areas[i]->w);
+            ymax = FFMAX(ymax, frame->subtitle_areas[i]->y + frame->subtitle_areas[i]->h);
         }
         vrect.x = xmin;
         vrect.y = ymin;
@@ -304,27 +310,29 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
         /* Count pixels outside the virtual rectangle as transparent */
         global_palette_hits[0] = vrect.w * vrect.h;
         for (i = 0; i < rects; i++)
-            global_palette_hits[0] -= h->rects[i]->w * h->rects[i]->h;
+            global_palette_hits[0] -= frame->subtitle_areas[i]->w * frame->subtitle_areas[i]->h;
     }
 
     for (i = 0; i < rects; i++)
-        count_colors(avctx, global_palette_hits, h->rects[i]);
+        count_colors(avctx, global_palette_hits, frame->subtitle_areas[i]);
     select_palette(avctx, out_palette, out_alpha, global_palette_hits);
 
     if (rects > 1) {
-        if (!(vrect_data = av_calloc(vrect.w, vrect.h)))
+
+        vrect.buf[0] = av_buffer_allocz((size_t)vrect.w * vrect.h);
+        if (!vrect.buf[0])
             return AVERROR(ENOMEM);
-        vrect.data    [0] = vrect_data;
+
         vrect.linesize[0] = vrect.w;
         for (i = 0; i < rects; i++) {
-            build_color_map(avctx, cmap, (uint32_t *)h->rects[i]->data[1],
+            build_color_map(avctx, cmap, frame->subtitle_areas[i]->pal,
                             out_palette, out_alpha);
-            copy_rectangle(&vrect, h->rects[i], cmap);
+            copy_rectangle(&vrect, frame->subtitle_areas[i], cmap);
         }
         for (i = 0; i < 4; i++)
             cmap[i] = i;
     } else {
-        build_color_map(avctx, cmap, (uint32_t *)h->rects[0]->data[1],
+        build_color_map(avctx, cmap, frame->subtitle_areas[0]->pal,
                         out_palette, out_alpha);
     }
 
@@ -335,6 +343,16 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
                out_palette[i], out_alpha[i] >> 4);
     av_log(avctx, AV_LOG_DEBUG, "\n");
 
+
+    ret = ff_get_encode_buffer(avctx, avpkt, 4 + vrect.w * vrect.h / 2 + 17 + 21, 0);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+        return ret;
+    }
+
+    outbuf_size = avpkt->size;
+    outbuf = avpkt->data;
+
     // encode data block
     q = outbuf + 4;
     offset1 = q - outbuf;
@@ -344,10 +362,10 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
         ret = AVERROR_BUFFER_TOO_SMALL;
         goto fail;
     }
-    dvd_encode_rle(&q, vrect.data[0], vrect.w * 2,
+    dvd_encode_rle(&q, vrect.buf[0]->data, vrect.w * 2,
                    vrect.w, (vrect.h + 1) >> 1, cmap);
     offset2 = q - outbuf;
-    dvd_encode_rle(&q, vrect.data[0] + vrect.w, vrect.w * 2,
+    dvd_encode_rle(&q, vrect.buf[0]->data + vrect.w, vrect.w * 2,
                    vrect.w, vrect.h >> 1, cmap);
 
     if (dvdc->even_rows_fix && (vrect.h & 1)) {
@@ -362,7 +380,7 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
     bytestream_put_be16(&qq, q - outbuf);
 
     // send start display command
-    bytestream_put_be16(&q, (h->start_display_time*90) >> 10);
+    bytestream_put_be16(&q, 0);
     bytestream_put_be16(&q, (q - outbuf) /*- 2 */ + 8 + 12 + 2);
     *q++ = 0x03; // palette - 4 nibbles
     *q++ = (out_palette[3] << 4) | out_palette[2];
@@ -394,7 +412,8 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
     *q++ = 0xff; // terminating command
 
     // send stop display command last
-    bytestream_put_be16(&q, (h->end_display_time*90) >> 10);
+    duration_ms = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, (AVRational){ 1, 1000 });
+    bytestream_put_be16(&q, (duration_ms*90) >> 10);
     bytestream_put_be16(&q, (q - outbuf) - 2 /*+ 4*/);
     *q++ = 0x02; // set end
     *q++ = 0xff; // terminating command
@@ -403,7 +422,9 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
     bytestream_put_be16(&qq, q - outbuf);
 
     av_log(NULL, AV_LOG_DEBUG, "subtitle_packet size=%"PTRDIFF_SPECIFIER"\n", q - outbuf);
-    ret = q - outbuf;
+    avpkt->size = q - outbuf;
+    ret = 0;
+    *got_packet = 1;
 
 fail:
     av_free(vrect_data);
@@ -467,14 +488,13 @@ static int dvdsub_init(AVCodecContext *avctx)
     return 0;
 }
 
-static int dvdsub_encode(AVCodecContext *avctx,
-                         unsigned char *buf, int buf_size,
-                         const AVSubtitle *sub)
+static int dvdsub_encode(struct AVCodecContext* avctx, struct AVPacket* avpkt,
+                         const struct AVFrame* frame, int* got_packet)
 {
     //DVDSubtitleContext *s = avctx->priv_data;
     int ret;
 
-    ret = encode_dvd_subtitles(avctx, buf, buf_size, sub);
+    ret = encode_dvd_subtitles(avctx, avpkt, frame, got_packet);
     return ret;
 }
 
@@ -499,7 +519,7 @@ const AVCodec ff_dvdsub_encoder = {
     .type           = AVMEDIA_TYPE_SUBTITLE,
     .id             = AV_CODEC_ID_DVD_SUBTITLE,
     .init           = dvdsub_init,
-    .encode_sub     = dvdsub_encode,
+    .encode2        = dvdsub_encode,
     .priv_class     = &dvdsubenc_class,
     .priv_data_size = sizeof(DVDSubtitleContext),
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
diff --git a/libavcodec/encode.c b/libavcodec/encode.c
index 618be0573d..da195bb77a 100644
--- a/libavcodec/encode.c
+++ b/libavcodec/encode.c
@@ -140,17 +140,70 @@ fail:
     return ret;
 }
 
-int avcodec_encode_subtitle(AVCodecContext *avctx, uint8_t *buf, int buf_size,
-                            const AVSubtitle *sub)
+int avcodec_encode_subtitle(AVCodecContext *avctx, uint8_t *buf, int buf_size, const AVSubtitle *sub)
 {
-    int ret;
+    int ret = 0, got_packet = 0;
+    AVFrame *frame = NULL;
+    AVPacket* avpkt = NULL;
+
     if (sub->start_display_time) {
         av_log(avctx, AV_LOG_ERROR, "start_display_time must be 0.\n");
         return -1;
     }
 
-    ret = avctx->codec->encode_sub(avctx, buf, buf_size, sub);
+    memset(buf, 0, buf_size);
+    // Create a temporary frame for calling the regular api:
+    frame = av_frame_alloc();
+    if (!frame) {
+        ret = AVERROR(ENOMEM);
+        goto exit;
+    }
+
+    frame->format = sub->format;
+    frame->type = AVMEDIA_TYPE_SUBTITLE;
+    ret = av_frame_get_buffer2(frame, 0);
+    if (ret < 0)
+        goto exit;
+
+    // Create a temporary packet
+    avpkt = av_packet_alloc();
+    if (!avpkt) {
+        ret = AVERROR(ENOMEM);
+        goto exit;
+    }
+
+    // Copy legacy subtitle data to temp frame
+    ret = ff_frame_put_subtitle(frame, sub);
+    if (ret < 0)
+        goto exit;
+
+    ret = avcodec_send_frame(avctx, frame);
+    if (ret < 0)
+        goto exit;
+
+    ret = avcodec_receive_packet(avctx, avpkt);
+
+    if (ret < 0 && ret != AVERROR(EAGAIN))
+        goto exit;
+
+    //ret = avctx->codec->encode2(avctx, avpkt, frame, &got_packet);
+
     avctx->frame_number++;
+
+    if (avpkt->size) {
+        if (avpkt->size > buf_size) {
+            ret = AVERROR_BUFFER_TOO_SMALL;
+            goto exit;
+        }
+
+        memcpy(buf, avpkt->data, avpkt->size);
+        ret = avpkt->size;
+    }
+
+exit:
+
+    av_packet_free(&avpkt);
+    av_frame_free(&frame);
     return ret;
 }
 
diff --git a/libavcodec/movtextenc.c b/libavcodec/movtextenc.c
index d506ed5c37..6383d694a8 100644
--- a/libavcodec/movtextenc.c
+++ b/libavcodec/movtextenc.c
@@ -29,6 +29,7 @@
 #include "libavutil/ass_split_internal.h"
 #include "libavutil/ass_internal.h"
 #include "bytestream.h"
+#include "encode.h"
 #include "internal.h"
 
 #define STYLE_FLAG_BOLD         (1<<0)
@@ -73,6 +74,7 @@ typedef struct {
     AVCodecContext *avctx;
 
     ASSSplitContext *ass_ctx;
+    int is_default_ass_context;
     ASSStyle *ass_dialog_style;
     StyleBox *style_attributes;
     unsigned  count;
@@ -329,7 +331,7 @@ static av_cold int mov_text_encode_init(AVCodecContext *avctx)
 
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
 
-    s->ass_ctx = avpriv_ass_split(avctx->subtitle_header);
+    s->ass_ctx = avpriv_ass_split((char*)avctx->subtitle_header);
     if (!s->ass_ctx)
         return AVERROR_INVALIDDATA;
     ret = encode_sample_description(avctx);
@@ -633,56 +635,112 @@ static const ASSCodesCallbacks mov_text_callbacks = {
     .end              = mov_text_end_cb,
 };
 
-static int mov_text_encode_frame(AVCodecContext *avctx, unsigned char *buf,
-                                 int bufsize, const AVSubtitle *sub)
+static void ensure_ass_context(AVCodecContext* avctx, const AVFrame* frame)
+{
+    MovTextContext* s = avctx->priv_data;
+    int ret;
+
+    if (s->ass_ctx && !s->is_default_ass_context)
+        // We already have a (non-default context)
+        return;
+
+    if (!frame->num_subtitle_areas)
+        // Don't need ass context for processing empty subtitle frames
+        return;
+
+    // The frame has content, so we need to set up a context
+    if (frame->subtitle_header && frame->subtitle_header->size > 0) {
+        const char* subtitle_header = (char*)frame->subtitle_header->data;
+        avpriv_ass_split_free(s->ass_ctx);
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 0;
+    }
+    else if (!s->ass_ctx) {
+        char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+        if (!subtitle_header)
+            return;
+
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 1;
+        av_free(subtitle_header);
+    }
+
+    if (s->ass_ctx && !avctx->extradata_size) {
+        ret = encode_sample_description(avctx);
+        if (ret < 0)
+            av_log(avctx, AV_LOG_ERROR, "Error during encode_sample_description().\n");
+    }
+}
+
+static int mov_text_encode_frame(AVCodecContext *avctx, AVPacket *avpkt,
+                                 const AVFrame *frame, int *got_packet)
 {
     MovTextContext *s = avctx->priv_data;
     ASSDialog *dialog;
-    int i, length;
+    int i, ret = 0;
+    size_t j;
+    uint8_t* buf;
+
+    ensure_ass_context(avctx, frame);
 
     s->text_pos = 0;
     s->count = 0;
     s->box_flags = 0;
     av_bprint_clear(&s->buffer);
-    for (i = 0; i < sub->num_rects; i++) {
-        const char *ass = sub->rects[i]->ass;
+    for (i = 0; i < frame->num_subtitle_areas; i++) {
+        const char *ass = frame->subtitle_areas[i]->ass;
 
-        if (sub->rects[i]->type != SUBTITLE_ASS) {
-            av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
+        if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) {
+            av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
             return AVERROR(EINVAL);
         }
 
-        dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
-        if (!dialog)
-            return AVERROR(ENOMEM);
-        mov_text_dialog(s, dialog);
-        avpriv_ass_split_override_codes(&mov_text_callbacks, s, dialog->text);
-        avpriv_ass_free_dialog(&dialog);
+        if (ass) {
+
+            if (i > 0)
+                mov_text_new_line_cb(s, 0);
+
+            dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
+            if (!dialog)
+                return AVERROR(ENOMEM);
+            mov_text_dialog(s, dialog);
+            avpriv_ass_split_override_codes(&mov_text_callbacks, s, dialog->text);
+            avpriv_ass_free_dialog(&dialog);
+
+        }
     }
 
-    if (s->buffer.len > UINT16_MAX)
-        return AVERROR(ERANGE);
-    AV_WB16(buf, s->buffer.len);
-    buf += 2;
+    if (!av_bprint_is_complete(&s->buffer)) {
+        return AVERROR(ENOMEM);
+    }
 
-    for (size_t j = 0; j < box_count; j++)
+    for (j = 0; j < box_count; j++) {
         box_types[j].encode(s);
+    }
 
-    if (!av_bprint_is_complete(&s->buffer))
-        return AVERROR(ENOMEM);
+    ret = ff_get_encode_buffer(avctx, avpkt, s->buffer.len + 3, 0);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+        return ret;
+    }
 
-    if (!s->buffer.len)
-        return 0;
+    buf = avpkt->data;
 
-    if (s->buffer.len > bufsize - 3) {
+    AV_WB16(buf, s->buffer.len);
+    buf += 2;
+
+    if (s->buffer.len > avpkt->size - 3) {
         av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n");
-        return AVERROR_BUFFER_TOO_SMALL;
+        ret = AVERROR_BUFFER_TOO_SMALL;
+        goto exit;
     }
 
     memcpy(buf, s->buffer.str, s->buffer.len);
-    length = s->buffer.len + 2;
+    avpkt->size = s->buffer.len + 2;
+    *got_packet = 1;
 
-    return length;
+exit:
+    return ret;
 }
 
 #define OFFSET(x) offsetof(MovTextContext, x)
@@ -707,7 +765,7 @@ const AVCodec ff_movtext_encoder = {
     .priv_data_size = sizeof(MovTextContext),
     .priv_class     = &mov_text_encoder_class,
     .init           = mov_text_encode_init,
-    .encode_sub     = mov_text_encode_frame,
+    .encode2        = mov_text_encode_frame,
     .close          = mov_text_encode_close,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP,
 };
diff --git a/libavcodec/srtenc.c b/libavcodec/srtenc.c
index a7c5fccefe..ebe42ef817 100644
--- a/libavcodec/srtenc.c
+++ b/libavcodec/srtenc.c
@@ -21,6 +21,7 @@
 
 #include <stdarg.h>
 #include "avcodec.h"
+#include "encode.h"
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
 #include "libavutil/ass_split_internal.h"
@@ -33,6 +34,7 @@
 typedef struct {
     AVCodecContext *avctx;
     ASSSplitContext *ass_ctx;
+    int is_default_ass_context;
     AVBPrint buffer;
     char stack[SRT_STACK_SIZE];
     int stack_ptr;
@@ -130,14 +132,13 @@ static void srt_style_apply(SRTContext *s, const char *style)
     }
 }
 
-
 static av_cold int srt_encode_init(AVCodecContext *avctx)
 {
     SRTContext *s = avctx->priv_data;
     s->avctx = avctx;
-    s->ass_ctx = avpriv_ass_split(avctx->subtitle_header);
+    s->ass_ctx = avpriv_ass_split((char *)avctx->subtitle_header);
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
-    return s->ass_ctx ? 0 : AVERROR_INVALIDDATA;
+    return 0;
 }
 
 static void srt_text_cb(void *priv, const char *text, int len)
@@ -227,58 +228,95 @@ static const ASSCodesCallbacks text_callbacks = {
     .new_line         = srt_new_line_cb,
 };
 
-static int encode_frame(AVCodecContext *avctx,
-                        unsigned char *buf, int bufsize, const AVSubtitle *sub,
-                        const ASSCodesCallbacks *cb)
+static void ensure_ass_context(SRTContext* s, const AVFrame* frame)
+{
+    if (s->ass_ctx && !s->is_default_ass_context)
+        // We already have a (non-default context)
+        return;
+
+    if (!frame->num_subtitle_areas)
+        // Don't need ass context for processing empty subtitle frames
+        return;
+
+    // The frame has content, so we need to set up a context
+    if (frame->subtitle_header && frame->subtitle_header->size > 0) {
+        const char* subtitle_header = (char*)frame->subtitle_header->data;
+        avpriv_ass_split_free(s->ass_ctx);
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 0;
+    }
+    else if (!s->ass_ctx) {
+        char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+        if (!subtitle_header)
+            return;
+
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 1;
+        av_free(subtitle_header);
+    }
+}
+
+static int encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+                        const AVFrame* frame, int* got_packet, const ASSCodesCallbacks* cb)
 {
     SRTContext *s = avctx->priv_data;
     ASSDialog *dialog;
-    int i;
+    int i, ret;
+
+    ensure_ass_context(s, frame);
 
     av_bprint_clear(&s->buffer);
 
-    for (i=0; i<sub->num_rects; i++) {
-        const char *ass = sub->rects[i]->ass;
+    for (i=0; i< frame->num_subtitle_areas; i++) {
+        const char *ass = frame->subtitle_areas[i]->ass;
 
-        if (sub->rects[i]->type != SUBTITLE_ASS) {
-            av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
+        if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) {
+            av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
             return AVERROR(EINVAL);
         }
 
-        dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
-        if (!dialog)
-            return AVERROR(ENOMEM);
-        s->alignment_applied = 0;
-        if (avctx->codec_id == AV_CODEC_ID_SUBRIP)
-            srt_style_apply(s, dialog->style);
-        avpriv_ass_split_override_codes(cb, s, dialog->text);
-        avpriv_ass_free_dialog(&dialog);
+        if (ass) {
+
+            if (i > 0)
+                srt_new_line_cb(s, 0);
+
+            dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
+            if (!dialog)
+                return AVERROR(ENOMEM);
+            s->alignment_applied = 0;
+            if (avctx->codec_id == AV_CODEC_ID_SUBRIP)
+                srt_style_apply(s, dialog->style);
+            avpriv_ass_split_override_codes(cb, s, dialog->text);
+            avpriv_ass_free_dialog(&dialog);
+        }
     }
 
     if (!av_bprint_is_complete(&s->buffer))
         return AVERROR(ENOMEM);
-    if (!s->buffer.len)
-        return 0;
 
-    if (s->buffer.len > bufsize) {
-        av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n");
-        return AVERROR_BUFFER_TOO_SMALL;
+    ret = ff_get_encode_buffer(avctx, avpkt, s->buffer.len + 1, 0);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+        return ret;
     }
-    memcpy(buf, s->buffer.str, s->buffer.len);
 
-    return s->buffer.len;
+    memcpy(avpkt->data, s->buffer.str, s->buffer.len);
+    avpkt->size = s->buffer.len;
+    *got_packet = 1;
+
+    return 0;
 }
 
-static int srt_encode_frame(AVCodecContext *avctx,
-                               unsigned char *buf, int bufsize, const AVSubtitle *sub)
+static int srt_encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+                            const AVFrame* frame, int* got_packet)
 {
-    return encode_frame(avctx, buf, bufsize, sub, &srt_callbacks);
+    return encode_frame(avctx, avpkt, frame, got_packet, &srt_callbacks);
 }
 
-static int text_encode_frame(AVCodecContext *avctx,
-                             unsigned char *buf, int bufsize, const AVSubtitle *sub)
+static int text_encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+                             const AVFrame* frame, int* got_packet)
 {
-    return encode_frame(avctx, buf, bufsize, sub, &text_callbacks);
+    return encode_frame(avctx, avpkt, frame, got_packet, &text_callbacks);
 }
 
 static int srt_encode_close(AVCodecContext *avctx)
@@ -298,7 +336,7 @@ const AVCodec ff_srt_encoder = {
     .id             = AV_CODEC_ID_SUBRIP,
     .priv_data_size = sizeof(SRTContext),
     .init           = srt_encode_init,
-    .encode_sub     = srt_encode_frame,
+    .encode2        = srt_encode_frame,
     .close          = srt_encode_close,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
 };
@@ -312,7 +350,7 @@ const AVCodec ff_subrip_encoder = {
     .id             = AV_CODEC_ID_SUBRIP,
     .priv_data_size = sizeof(SRTContext),
     .init           = srt_encode_init,
-    .encode_sub     = srt_encode_frame,
+    .encode2        = srt_encode_frame,
     .close          = srt_encode_close,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
 };
@@ -326,7 +364,7 @@ const AVCodec ff_text_encoder = {
     .id             = AV_CODEC_ID_TEXT,
     .priv_data_size = sizeof(SRTContext),
     .init           = srt_encode_init,
-    .encode_sub     = text_encode_frame,
+    .encode2        = text_encode_frame,
     .close          = srt_encode_close,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
 };
diff --git a/libavcodec/tests/avcodec.c b/libavcodec/tests/avcodec.c
index 5d0ff9432c..bd979b2184 100644
--- a/libavcodec/tests/avcodec.c
+++ b/libavcodec/tests/avcodec.c
@@ -107,8 +107,6 @@ int main(void){
             continue;
         }
         if (is_encoder) {
-            if (codec->type == AVMEDIA_TYPE_SUBTITLE ^ !!codec->encode_sub)
-                ERR("Encoder %s is both subtitle encoder and not subtitle encoder.");
             if (!!codec->encode_sub + !!codec->encode2 + !!codec->receive_packet != 1)
                 ERR("Encoder %s does not implement exactly one encode API.\n");
             if (codec->update_thread_context || codec->update_thread_context_for_user || codec->bsfs)
diff --git a/libavcodec/ttmlenc.c b/libavcodec/ttmlenc.c
index 083f2dd67a..e6d8a01a73 100644
--- a/libavcodec/ttmlenc.c
+++ b/libavcodec/ttmlenc.c
@@ -33,11 +33,17 @@
 #include "libavutil/bprint.h"
 #include "libavutil/internal.h"
 #include "libavutil/ass_split_internal.h"
+#include "libavutil/ass_internal.h"
 #include "ttmlenc.h"
 
+#include "encode.h"
+
+
 typedef struct {
     AVCodecContext *avctx;
     ASSSplitContext *ass_ctx;
+    int is_default_ass_context;
+    int extradata_written;
     AVBPrint buffer;
 } TTMLContext;
 
@@ -76,28 +82,75 @@ static const ASSCodesCallbacks ttml_callbacks = {
     .new_line         = ttml_new_line_cb,
 };
 
-static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf,
-                             int bufsize, const AVSubtitle *sub)
+static int ttml_write_header_content(AVCodecContext* avctx);
+
+static void ensure_ass_context(AVCodecContext* avctx, const AVFrame* frame)
+{
+    TTMLContext* s = avctx->priv_data;
+    int ret;
+
+    if (s->ass_ctx && !s->is_default_ass_context)
+        // We already have a (non-default context)
+        return;
+
+    if (!frame->num_subtitle_areas)
+        // Don't need ass context for processing empty subtitle frames
+        return;
+
+    // The frame has content, so we need to set up a context
+    if (frame->subtitle_header && frame->subtitle_header->size > 0) {
+        const char* subtitle_header = (char*)frame->subtitle_header->data;
+        avpriv_ass_split_free(s->ass_ctx);
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 0;
+    }
+    else if (!s->ass_ctx) {
+        char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+        if (!subtitle_header)
+            return;
+
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 1;
+        av_free(subtitle_header);
+    }
+
+    if (s->ass_ctx && !s->extradata_written) {
+        s->extradata_written = 1;
+        if ((ret = ttml_write_header_content(avctx)) < 0) {
+            av_log(avctx, AV_LOG_ERROR, "Error writing header content.\n");
+        }
+    }
+}
+
+static int ttml_encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+                             const AVFrame* frame, int* got_packet)
 {
     TTMLContext *s = avctx->priv_data;
     ASSDialog *dialog;
-    int i;
+    int i, ret;
+
+    ensure_ass_context(avctx, frame);
 
     av_bprint_clear(&s->buffer);
 
-    for (i=0; i<sub->num_rects; i++) {
-        const char *ass = sub->rects[i]->ass;
-        int ret;
+    for (i=0; i< frame->num_subtitle_areas; i++) {
+        const char *ass = frame->subtitle_areas[i]->ass;
 
-        if (sub->rects[i]->type != SUBTITLE_ASS) {
-            av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
+        if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) {
+            av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
             return AVERROR(EINVAL);
         }
 
+        if (!ass)
+            continue;
+
         dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
         if (!dialog)
             return AVERROR(ENOMEM);
 
+        if (i > 0)
+            ttml_new_line_cb(s, 0);
+
         if (dialog->style) {
             av_bprintf(&s->buffer, "<span region=\"");
             av_bprint_escape(&s->buffer, dialog->style, NULL,
@@ -130,17 +183,19 @@ static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf,
 
     if (!av_bprint_is_complete(&s->buffer))
         return AVERROR(ENOMEM);
-    if (!s->buffer.len)
-        return 0;
-
-    // force null-termination, so in case our destination buffer is
-    // too small, the return value is larger than bufsize minus null.
-    if (av_strlcpy(buf, s->buffer.str, bufsize) > bufsize - 1) {
-        av_log(avctx, AV_LOG_ERROR, "Buffer too small for TTML event.\n");
-        return AVERROR_BUFFER_TOO_SMALL;
+
+    ret = ff_get_encode_buffer(avctx, avpkt, s->buffer.len + 3, 0);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+        return ret;
     }
 
-    return s->buffer.len;
+    av_strlcpy(avpkt->data, s->buffer.str, avpkt->size);
+
+    avpkt->size = s->buffer.len;
+    *got_packet = 1;
+
+    return 0;
 }
 
 static av_cold int ttml_encode_close(AVCodecContext *avctx)
@@ -370,13 +425,13 @@ static av_cold int ttml_encode_init(AVCodecContext *avctx)
     s->avctx   = avctx;
 
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
+    s->ass_ctx = avpriv_ass_split((char*)avctx->subtitle_header);
 
-    if (!(s->ass_ctx = avpriv_ass_split(avctx->subtitle_header))) {
-        return AVERROR_INVALIDDATA;
-    }
+    if (s->ass_ctx) {
+        if (ret = ttml_write_header_content(avctx) < 0)
+            return ret;
 
-    if ((ret = ttml_write_header_content(avctx)) < 0) {
-        return ret;
+        s->extradata_written = 1;
     }
 
     return 0;
@@ -389,7 +444,7 @@ const AVCodec ff_ttml_encoder = {
     .id             = AV_CODEC_ID_TTML,
     .priv_data_size = sizeof(TTMLContext),
     .init           = ttml_encode_init,
-    .encode_sub     = ttml_encode_frame,
+    .encode2        = ttml_encode_frame,
     .close          = ttml_encode_close,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP,
 };
diff --git a/libavcodec/webvttenc.c b/libavcodec/webvttenc.c
index 761099b69a..c0436f5739 100644
--- a/libavcodec/webvttenc.c
+++ b/libavcodec/webvttenc.c
@@ -22,6 +22,7 @@
 
 #include <stdarg.h>
 #include "avcodec.h"
+#include "encode.h"
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
 #include "libavutil/ass_split_internal.h"
@@ -32,6 +33,7 @@
 typedef struct {
     AVCodecContext *avctx;
     ASSSplitContext *ass_ctx;
+    int is_default_ass_context;
     AVBPrint buffer;
     unsigned timestamp_end;
     int count;
@@ -155,43 +157,81 @@ static const ASSCodesCallbacks webvtt_callbacks = {
     .end              = webvtt_end_cb,
 };
 
-static int webvtt_encode_frame(AVCodecContext *avctx,
-                               unsigned char *buf, int bufsize, const AVSubtitle *sub)
+static void ensure_ass_context(WebVTTContext* s, const AVFrame* frame)
+{
+    if (s->ass_ctx && !s->is_default_ass_context)
+        // We already have a (non-default context)
+        return;
+
+    if (!frame->num_subtitle_areas)
+        // Don't need ass context for processing empty subtitle frames
+        return;
+
+    // The frame has content, so we need to set up a context
+    if (frame->subtitle_header && frame->subtitle_header->size > 0) {
+        avpriv_ass_split_free(s->ass_ctx);
+        const char* subtitle_header = (char*)frame->subtitle_header->data;
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 0;
+    }
+    else if (!s->ass_ctx) {
+        char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+        if (!subtitle_header)
+            return;
+
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 1;
+        av_free(subtitle_header);
+    }
+}
+
+static int webvtt_encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+                               const AVFrame* frame, int* got_packet)
 {
     WebVTTContext *s = avctx->priv_data;
     ASSDialog *dialog;
-    int i;
+    int ret, i;
+
+    ensure_ass_context(s, frame);
 
     av_bprint_clear(&s->buffer);
 
-    for (i=0; i<sub->num_rects; i++) {
-        const char *ass = sub->rects[i]->ass;
+    for (i=0; i< frame->num_subtitle_areas; i++) {
+        const char *ass = frame->subtitle_areas[i]->ass;
 
-        if (sub->rects[i]->type != SUBTITLE_ASS) {
-            av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
+        if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) {
+            av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
             return AVERROR(EINVAL);
         }
 
-        dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
-        if (!dialog)
-            return AVERROR(ENOMEM);
-        webvtt_style_apply(s, dialog->style);
-        avpriv_ass_split_override_codes(&webvtt_callbacks, s, dialog->text);
-        avpriv_ass_free_dialog(&dialog);
+        if (ass) {
+
+            if (i > 0)
+                webvtt_new_line_cb(s, 0);
+
+            dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
+            if (!dialog)
+                return AVERROR(ENOMEM);
+            webvtt_style_apply(s, dialog->style);
+            avpriv_ass_split_override_codes(&webvtt_callbacks, s, dialog->text);
+            avpriv_ass_free_dialog(&dialog);
+        }
     }
 
     if (!av_bprint_is_complete(&s->buffer))
         return AVERROR(ENOMEM);
-    if (!s->buffer.len)
-        return 0;
 
-    if (s->buffer.len > bufsize) {
-        av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n");
-        return AVERROR_BUFFER_TOO_SMALL;
+    ret = ff_get_encode_buffer(avctx, avpkt, s->buffer.len + 1, 0);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+        return ret;
     }
-    memcpy(buf, s->buffer.str, s->buffer.len);
 
-    return s->buffer.len;
+    memcpy(avpkt->data, s->buffer.str, s->buffer.len);
+    avpkt->size = s->buffer.len;
+    *got_packet = s->buffer.len > 0;
+
+    return 0;
 }
 
 static int webvtt_encode_close(AVCodecContext *avctx)
@@ -206,9 +246,9 @@ static av_cold int webvtt_encode_init(AVCodecContext *avctx)
 {
     WebVTTContext *s = avctx->priv_data;
     s->avctx = avctx;
-    s->ass_ctx = avpriv_ass_split(avctx->subtitle_header);
+    s->ass_ctx = avpriv_ass_split((char*)avctx->subtitle_header);
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
-    return s->ass_ctx ? 0 : AVERROR_INVALIDDATA;
+    return 0;
 }
 
 const AVCodec ff_webvtt_encoder = {
@@ -218,7 +258,7 @@ const AVCodec ff_webvtt_encoder = {
     .id             = AV_CODEC_ID_WEBVTT,
     .priv_data_size = sizeof(WebVTTContext),
     .init           = webvtt_encode_init,
-    .encode_sub     = webvtt_encode_frame,
+    .encode2        = webvtt_encode_frame,
     .close          = webvtt_encode_close,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
 };
diff --git a/libavcodec/xsubenc.c b/libavcodec/xsubenc.c
index 03d0dc2d86..ef804f21f4 100644
--- a/libavcodec/xsubenc.c
+++ b/libavcodec/xsubenc.c
@@ -22,6 +22,7 @@
 
 #include "avcodec.h"
 #include "bytestream.h"
+#include "encode.h"
 #include "internal.h"
 #include "put_bits.h"
 
@@ -111,39 +112,56 @@ static int make_tc(uint64_t ms, int *tc)
     return ms > 99;
 }
 
-static int xsub_encode(AVCodecContext *avctx, unsigned char *buf,
-                       int bufsize, const AVSubtitle *h)
+static int xsub_encode(AVCodecContext* avctx, AVPacket* avpkt,
+                       const AVFrame* frame, int* got_packet)
 {
-    uint64_t startTime = h->pts / 1000; // FIXME: need better solution...
-    uint64_t endTime = startTime + h->end_display_time - h->start_display_time;
+    const int64_t duration_ms = (int64_t)((double)frame->subtitle_timing.duration * av_q2d(AV_TIME_BASE_Q) * 1000);
+    const uint64_t startTime = (int64_t)((double)frame->subtitle_timing.start_pts * av_q2d(AV_TIME_BASE_Q) * 1000);
+    const uint64_t endTime   = startTime + duration_ms;
     int start_tc[4], end_tc[4];
-    uint8_t *hdr = buf + 27; // Point behind the timestamp
+    uint8_t *hdr;
     uint8_t *rlelenptr;
     uint16_t width, height;
-    int i;
+    int i, ret;
     PutBitContext pb;
+    uint8_t* buf;
+    int64_t req_size;
 
-    if (bufsize < 27 + 7*2 + 4*3) {
-        av_log(avctx, AV_LOG_ERROR, "Buffer too small for XSUB header.\n");
-        return AVERROR_BUFFER_TOO_SMALL;
+    if (!frame->num_subtitle_areas) {
+        // Don't encode empty sub events
+        return 0;
     }
 
+    // Estimate size (timestamp 27, header 7*2 + 4*3, padding 10)
+    req_size = 27 + 7*2 + 4*3 + 10;
+    req_size += frame->subtitle_areas[0]->linesize[0] * frame->subtitle_areas[0]->h;
+    req_size += 256; // Palette
+
+    ret = ff_get_encode_buffer(avctx, avpkt, req_size, 0);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+        return ret;
+    }
+
+    buf = avpkt->data;
+    hdr = avpkt->data + 27; // Point behind the timestamp
+
     // TODO: support multiple rects
-    if (h->num_rects != 1)
-        av_log(avctx, AV_LOG_WARNING, "Only single rects supported (%d in subtitle.)\n", h->num_rects);
+    if (frame->num_subtitle_areas != 1)
+        av_log(avctx, AV_LOG_WARNING, "Only single rects supported (%d in subtitle.)\n", frame->num_subtitle_areas);
 
     // TODO: render text-based subtitles into bitmaps
-    if (!h->rects[0]->data[0] || !h->rects[0]->data[1]) {
+    if (!frame->subtitle_areas[0]->buf[0]->data || !frame->subtitle_areas[0]->pal) {
         av_log(avctx, AV_LOG_WARNING, "No subtitle bitmap available.\n");
         return AVERROR(EINVAL);
     }
 
     // TODO: color reduction, similar to dvdsub encoder
-    if (h->rects[0]->nb_colors > 4)
-        av_log(avctx, AV_LOG_WARNING, "No more than 4 subtitle colors supported (%d found.)\n", h->rects[0]->nb_colors);
+    if (frame->subtitle_areas[0]->nb_colors > 4)
+        av_log(avctx, AV_LOG_WARNING, "No more than 4 subtitle colors supported (%d found.)\n", frame->subtitle_areas[0]->nb_colors);
 
     // TODO: Palette swapping if color zero is not transparent
-    if (((uint32_t *)h->rects[0]->data[1])[0] & 0xff000000)
+    if (((uint32_t *)frame->subtitle_areas[0]->pal)[0] & 0xff000000)
         av_log(avctx, AV_LOG_WARNING, "Color index 0 is not transparent. Transparency will be messed up.\n");
 
     if (make_tc(startTime, start_tc) || make_tc(endTime, end_tc)) {
@@ -151,7 +169,7 @@ static int xsub_encode(AVCodecContext *avctx, unsigned char *buf,
         return AVERROR(EINVAL);
     }
 
-    snprintf(buf, 28,
+    snprintf((char *)avpkt->data, 28,
         "[%02d:%02d:%02d.%03d-%02d:%02d:%02d.%03d]",
         start_tc[3], start_tc[2], start_tc[1], start_tc[0],
         end_tc[3],   end_tc[2],   end_tc[1],   end_tc[0]);
@@ -160,45 +178,47 @@ static int xsub_encode(AVCodecContext *avctx, unsigned char *buf,
     // 2 pixels required on either side of subtitle.
     // Possibly due to limitations of hardware renderers.
     // TODO: check if the bitmap is already padded
-    width  = FFALIGN(h->rects[0]->w, 2) + PADDING * 2;
-    height = FFALIGN(h->rects[0]->h, 2);
+    width  = FFALIGN(frame->subtitle_areas[0]->w, 2) + PADDING * 2;
+    height = FFALIGN(frame->subtitle_areas[0]->h, 2);
 
     bytestream_put_le16(&hdr, width);
     bytestream_put_le16(&hdr, height);
-    bytestream_put_le16(&hdr, h->rects[0]->x);
-    bytestream_put_le16(&hdr, h->rects[0]->y);
-    bytestream_put_le16(&hdr, h->rects[0]->x + width -1);
-    bytestream_put_le16(&hdr, h->rects[0]->y + height -1);
+    bytestream_put_le16(&hdr, frame->subtitle_areas[0]->x);
+    bytestream_put_le16(&hdr, frame->subtitle_areas[0]->y);
+    bytestream_put_le16(&hdr, frame->subtitle_areas[0]->x + width -1);
+    bytestream_put_le16(&hdr, frame->subtitle_areas[0]->y + height -1);
 
     rlelenptr = hdr; // Will store length of first field here later.
     hdr+=2;
 
     // Palette
     for (i=0; i<4; i++)
-        bytestream_put_be24(&hdr, ((uint32_t *)h->rects[0]->data[1])[i]);
+        bytestream_put_be24(&hdr, ((uint32_t *)frame->subtitle_areas[0]->pal)[i]);
 
     // Bitmap
     // RLE buffer. Reserve 2 bytes for possible padding after the last row.
-    init_put_bits(&pb, hdr, bufsize - (hdr - buf) - 2);
-    if (xsub_encode_rle(&pb, h->rects[0]->data[0],
-                        h->rects[0]->linesize[0] * 2,
-                        h->rects[0]->w, (h->rects[0]->h + 1) >> 1))
+    init_put_bits(&pb, hdr, avpkt->size - (hdr - buf) - 2);
+    if (xsub_encode_rle(&pb, frame->subtitle_areas[0]->buf[0]->data,
+                        frame->subtitle_areas[0]->linesize[0] * 2,
+                        frame->subtitle_areas[0]->w, (frame->subtitle_areas[0]->h + 1) >> 1))
         return AVERROR_BUFFER_TOO_SMALL;
     bytestream_put_le16(&rlelenptr, put_bytes_count(&pb, 0)); // Length of first field
 
-    if (xsub_encode_rle(&pb, h->rects[0]->data[0] + h->rects[0]->linesize[0],
-                        h->rects[0]->linesize[0] * 2,
-                        h->rects[0]->w, h->rects[0]->h >> 1))
+    if (xsub_encode_rle(&pb, frame->subtitle_areas[0]->buf[0]->data + frame->subtitle_areas[0]->linesize[0],
+                        frame->subtitle_areas[0]->linesize[0] * 2,
+                        frame->subtitle_areas[0]->w, frame->subtitle_areas[0]->h >> 1))
         return AVERROR_BUFFER_TOO_SMALL;
 
     // Enforce total height to be a multiple of 2
-    if (h->rects[0]->h & 1) {
-        put_xsub_rle(&pb, h->rects[0]->w, PADDING_COLOR);
+    if (frame->subtitle_areas[0]->h & 1) {
+        put_xsub_rle(&pb, frame->subtitle_areas[0]->w, PADDING_COLOR);
     }
 
     flush_put_bits(&pb);
 
-    return hdr - buf + put_bytes_output(&pb);
+    avpkt->size = hdr - buf + put_bytes_output(&pb);
+    *got_packet = 1;
+    return 0;
 }
 
 static av_cold int xsub_encoder_init(AVCodecContext *avctx)
@@ -217,6 +237,6 @@ const AVCodec ff_xsub_encoder = {
     .type       = AVMEDIA_TYPE_SUBTITLE,
     .id         = AV_CODEC_ID_XSUB,
     .init       = xsub_encoder_init,
-    .encode_sub = xsub_encode,
+    .encode2    = xsub_encode,
     .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE,
 };
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v2 21/26] fftools/ffmpeg: Introduce subtitle filtering and new frame-based subtitle encoding
  2022-01-20  2:48 ` [FFmpeg-devel] [PATCH v2 00/26] Subtitle Filtering 2022 ffmpegagent
                     ` (19 preceding siblings ...)
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 20/26] avcodec/subtitles: Migrate subtitle encoders to frame-based API ffmpegagent
@ 2022-01-20  2:48   ` ffmpegagent
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 22/26] avutil/ass_split: Add parsing of hard-space tags (\h) ffmpegagent
                     ` (5 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  2:48 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

This commit actually enables subtitle filtering in ffmpeg by
sending and receiving subtitle frames to and from a filtergraph.

The heartbeat functionality from the previous sub2video implementation
is removed and now provided by the 'subfeed' filter.
The other part of sub2video functionality is retained by
auto-insertion of the new graphicsub2video filter.

Justification for changed test refs:

- sub2video
  The new results are identical excepting the last frame which
  is due to the implementation changes

- sub2video_basic
  The previous results had some incorrect output because multiple
  frames had the same dts
  The non-empty content frames are visually identical, the different
  CRC is due to the different blending algorithm that is being used.

- sub2video_time_limited
  The third frame in the previous ref was a repetition, which doesn't
  happen anymore with the new subtitle filtering.

- sub-dvb
  Running ffprobe -show_frames on the source file shows that there
  are 7 subtitle frames with 0 rects in the source at the start
  and 2 at the end. This translates to the 14 and 4 additional
  entries in the new test results.

- filter-overlay-dvdsub-2397
  Overlay results have slightly different CRCs due to different
  blending implementation

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 fftools/ffmpeg.c                          |  501 ++++-----
 fftools/ffmpeg.h                          |   13 +-
 fftools/ffmpeg_filter.c                   |  235 ++--
 fftools/ffmpeg_hw.c                       |    2 +-
 fftools/ffmpeg_opt.c                      |    3 +-
 tests/ref/fate/filter-overlay-dvdsub-2397 |  182 +--
 tests/ref/fate/sub-dvb                    |  162 +--
 tests/ref/fate/sub2video                  | 1091 +++++++++++++++++-
 tests/ref/fate/sub2video_basic            | 1239 +++++++++++++++++++--
 tests/ref/fate/sub2video_time_limited     |   78 +-
 10 files changed, 2837 insertions(+), 669 deletions(-)

diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c
index 5d134b025f..e916f0aaad 100644
--- a/fftools/ffmpeg.c
+++ b/fftools/ffmpeg.c
@@ -143,8 +143,6 @@ static int want_sdp = 1;
 static BenchmarkTimeStamps current_time;
 AVIOContext *progress_avio = NULL;
 
-static uint8_t *subtitle_out;
-
 InputStream **input_streams = NULL;
 int        nb_input_streams = 0;
 InputFile   **input_files   = NULL;
@@ -169,163 +167,6 @@ static int restore_tty;
 static void free_input_threads(void);
 #endif
 
-/* sub2video hack:
-   Convert subtitles to video with alpha to insert them in filter graphs.
-   This is a temporary solution until libavfilter gets real subtitles support.
- */
-
-static int sub2video_get_blank_frame(InputStream *ist)
-{
-    int ret;
-    AVFrame *frame = ist->sub2video.frame;
-
-    av_frame_unref(frame);
-    ist->sub2video.frame->width  = ist->dec_ctx->width  ? ist->dec_ctx->width  : ist->sub2video.w;
-    ist->sub2video.frame->height = ist->dec_ctx->height ? ist->dec_ctx->height : ist->sub2video.h;
-    ist->sub2video.frame->format = AV_PIX_FMT_RGB32;
-    if ((ret = av_frame_get_buffer(frame, 0)) < 0)
-        return ret;
-    memset(frame->data[0], 0, frame->height * frame->linesize[0]);
-    return 0;
-}
-
-static void sub2video_copy_rect(uint8_t *dst, int dst_linesize, int w, int h,
-                                AVSubtitleRect *r)
-{
-    uint32_t *pal, *dst2;
-    uint8_t *src, *src2;
-    int x, y;
-
-    if (r->type != SUBTITLE_BITMAP) {
-        av_log(NULL, AV_LOG_WARNING, "sub2video: non-bitmap subtitle\n");
-        return;
-    }
-    if (r->x < 0 || r->x + r->w > w || r->y < 0 || r->y + r->h > h) {
-        av_log(NULL, AV_LOG_WARNING, "sub2video: rectangle (%d %d %d %d) overflowing %d %d\n",
-            r->x, r->y, r->w, r->h, w, h
-        );
-        return;
-    }
-
-    dst += r->y * dst_linesize + r->x * 4;
-    src = r->data[0];
-    pal = (uint32_t *)r->data[1];
-    for (y = 0; y < r->h; y++) {
-        dst2 = (uint32_t *)dst;
-        src2 = src;
-        for (x = 0; x < r->w; x++)
-            *(dst2++) = pal[*(src2++)];
-        dst += dst_linesize;
-        src += r->linesize[0];
-    }
-}
-
-static void sub2video_push_ref(InputStream *ist, int64_t pts)
-{
-    AVFrame *frame = ist->sub2video.frame;
-    int i;
-    int ret;
-
-    av_assert1(frame->data[0]);
-    ist->sub2video.last_pts = frame->pts = pts;
-    for (i = 0; i < ist->nb_filters; i++) {
-        ret = av_buffersrc_add_frame_flags(ist->filters[i]->filter, frame,
-                                           AV_BUFFERSRC_FLAG_KEEP_REF |
-                                           AV_BUFFERSRC_FLAG_PUSH);
-        if (ret != AVERROR_EOF && ret < 0)
-            av_log(NULL, AV_LOG_WARNING, "Error while add the frame to buffer source(%s).\n",
-                   av_err2str(ret));
-    }
-}
-
-void sub2video_update(InputStream *ist, int64_t heartbeat_pts, AVSubtitle *sub)
-{
-    AVFrame *frame = ist->sub2video.frame;
-    int8_t *dst;
-    int     dst_linesize;
-    int num_rects, i;
-    int64_t pts, end_pts;
-
-    if (!frame)
-        return;
-    if (sub) {
-        pts       = av_rescale_q(sub->pts + sub->start_display_time * 1000LL,
-                                 AV_TIME_BASE_Q, ist->st->time_base);
-        end_pts   = av_rescale_q(sub->pts + sub->end_display_time   * 1000LL,
-                                 AV_TIME_BASE_Q, ist->st->time_base);
-        num_rects = sub->num_rects;
-    } else {
-        /* If we are initializing the system, utilize current heartbeat
-           PTS as the start time, and show until the following subpicture
-           is received. Otherwise, utilize the previous subpicture's end time
-           as the fall-back value. */
-        pts       = ist->sub2video.initialize ?
-                    heartbeat_pts : ist->sub2video.end_pts;
-        end_pts   = INT64_MAX;
-        num_rects = 0;
-    }
-    if (sub2video_get_blank_frame(ist) < 0) {
-        av_log(ist->dec_ctx, AV_LOG_ERROR,
-               "Impossible to get a blank canvas.\n");
-        return;
-    }
-    dst          = frame->data    [0];
-    dst_linesize = frame->linesize[0];
-    for (i = 0; i < num_rects; i++)
-        sub2video_copy_rect(dst, dst_linesize, frame->width, frame->height, sub->rects[i]);
-    sub2video_push_ref(ist, pts);
-    ist->sub2video.end_pts = end_pts;
-    ist->sub2video.initialize = 0;
-}
-
-static void sub2video_heartbeat(InputStream *ist, int64_t pts)
-{
-    InputFile *infile = input_files[ist->file_index];
-    int i, j, nb_reqs;
-    int64_t pts2;
-
-    /* When a frame is read from a file, examine all sub2video streams in
-       the same file and send the sub2video frame again. Otherwise, decoded
-       video frames could be accumulating in the filter graph while a filter
-       (possibly overlay) is desperately waiting for a subtitle frame. */
-    for (i = 0; i < infile->nb_streams; i++) {
-        InputStream *ist2 = input_streams[infile->ist_index + i];
-        if (!ist2->sub2video.frame)
-            continue;
-        /* subtitles seem to be usually muxed ahead of other streams;
-           if not, subtracting a larger time here is necessary */
-        pts2 = av_rescale_q(pts, ist->st->time_base, ist2->st->time_base) - 1;
-        /* do not send the heartbeat frame if the subtitle is already ahead */
-        if (pts2 <= ist2->sub2video.last_pts)
-            continue;
-        if (pts2 >= ist2->sub2video.end_pts || ist2->sub2video.initialize)
-            /* if we have hit the end of the current displayed subpicture,
-               or if we need to initialize the system, update the
-               overlayed subpicture and its start/end times */
-            sub2video_update(ist2, pts2 + 1, NULL);
-        for (j = 0, nb_reqs = 0; j < ist2->nb_filters; j++)
-            nb_reqs += av_buffersrc_get_nb_failed_requests(ist2->filters[j]->filter);
-        if (nb_reqs)
-            sub2video_push_ref(ist2, pts2);
-    }
-}
-
-static void sub2video_flush(InputStream *ist)
-{
-    int i;
-    int ret;
-
-    if (ist->sub2video.end_pts < INT64_MAX)
-        sub2video_update(ist, INT64_MAX, NULL);
-    for (i = 0; i < ist->nb_filters; i++) {
-        ret = av_buffersrc_add_frame(ist->filters[i]->filter, NULL);
-        if (ret != AVERROR_EOF && ret < 0)
-            av_log(NULL, AV_LOG_WARNING, "Flush the frame error.\n");
-    }
-}
-
-/* end of sub2video hack */
-
 static void term_exit_sigsafe(void)
 {
 #if HAVE_TERMIOS_H
@@ -526,7 +367,6 @@ static void ffmpeg_cleanup(int ret)
         avfilter_graph_free(&fg->graph);
         for (j = 0; j < fg->nb_inputs; j++) {
             InputFilter *ifilter = fg->inputs[j];
-            struct InputStream *ist = ifilter->ist;
 
             while (av_fifo_size(ifilter->frame_queue)) {
                 AVFrame *frame;
@@ -536,15 +376,6 @@ static void ffmpeg_cleanup(int ret)
             }
             av_fifo_freep(&ifilter->frame_queue);
             av_freep(&ifilter->displaymatrix);
-            if (ist->sub2video.sub_queue) {
-                while (av_fifo_size(ist->sub2video.sub_queue)) {
-                    AVSubtitle sub;
-                    av_fifo_generic_read(ist->sub2video.sub_queue,
-                                         &sub, sizeof(sub), NULL);
-                    avsubtitle_free(&sub);
-                }
-                av_fifo_freep(&ist->sub2video.sub_queue);
-            }
             av_buffer_unref(&ifilter->hw_frames_ctx);
             av_freep(&ifilter->name);
             av_freep(&fg->inputs[j]);
@@ -564,7 +395,7 @@ static void ffmpeg_cleanup(int ret)
     }
     av_freep(&filtergraphs);
 
-    av_freep(&subtitle_out);
+    ////av_freep(&subtitle_out);
 
     /* close files */
     for (i = 0; i < nb_output_files; i++) {
@@ -632,12 +463,12 @@ static void ffmpeg_cleanup(int ret)
         av_frame_free(&ist->decoded_frame);
         av_packet_free(&ist->pkt);
         av_dict_free(&ist->decoder_opts);
-        avsubtitle_free(&ist->prev_sub.subtitle);
-        av_frame_free(&ist->sub2video.frame);
+        av_frame_free(&ist->prev_sub.subtitle);
         av_freep(&ist->filters);
         av_freep(&ist->hwaccel_device);
         av_freep(&ist->dts_buffer);
 
+        av_buffer_unref(&ist->subtitle_header);
         avcodec_free_context(&ist->dec_ctx);
 
         av_freep(&input_streams[i]);
@@ -1055,33 +886,76 @@ error:
     exit_program(1);
 }
 
-static void do_subtitle_out(OutputFile *of,
-                            OutputStream *ost,
-                            AVSubtitle *sub)
+static void encode_subtitle_frame(OutputFile *of, OutputStream *ost, AVFrame *frame, AVPacket *pkt, int64_t pts_offset)
 {
-    int subtitle_out_max_size = 1024 * 1024;
-    int subtitle_out_size, nb, i;
+    AVCodecContext *enc = ost->enc_ctx;
+    int ret;
+
+        ost->frames_encoded++;
+
+        ret = avcodec_send_frame(enc, frame);
+        if (ret < 0)
+            goto error;
+
+        while (1) {
+            ret = avcodec_receive_packet(enc, pkt);
+            update_benchmark("encode_subtitles %d.%d", ost->file_index, ost->index);
+            if (ret == AVERROR(EAGAIN))
+                break;
+            if (ret < 0)
+                goto error;
+
+            if (debug_ts) {
+                av_log(NULL, AV_LOG_INFO, "encoder -> type:subtitles "
+                       "pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s\n",
+                       av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &enc->time_base),
+                       av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &enc->time_base));
+            }
+
+            pkt->pts  = av_rescale_q(frame->subtitle_timing.start_pts, AV_TIME_BASE_Q, ost->mux_timebase);
+            pkt->duration = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, ost->mux_timebase);
+            pkt->pts += pts_offset;
+
+            pkt->dts = pkt->pts;
+            output_packet(of, pkt, ost, 0);
+        }
+        ost->sync_opts++;
+        ost->frame_number++;
+
+    return;
+error:
+    av_log(NULL, AV_LOG_FATAL, "Subtitle encoding failed - Error code: %d\n", ret);
+    exit_program(1);
+}
+
+static void do_subtitle_out(OutputFile *of, OutputStream *ost, AVFrame *frame)
+{
+    int nb, i;
     AVCodecContext *enc;
     AVPacket *pkt = ost->pkt;
     int64_t pts;
 
-    if (sub->pts == AV_NOPTS_VALUE) {
+    if (!frame)
+        return;
+
+    av_log(NULL, AV_LOG_DEBUG, "do_subtitle_out: sub->pts: %"PRId64"  frame->pts: %"PRId64"\n", frame->subtitle_timing.start_pts, frame->pts);
+
+    if (frame->subtitle_timing.start_pts == AV_NOPTS_VALUE) {
         av_log(NULL, AV_LOG_ERROR, "Subtitle packets must have a pts\n");
         if (exit_on_error)
             exit_program(1);
         return;
     }
 
-    enc = ost->enc_ctx;
-
-    if (!subtitle_out) {
-        subtitle_out = av_malloc(subtitle_out_max_size);
-        if (!subtitle_out) {
-            av_log(NULL, AV_LOG_FATAL, "Failed to allocate subtitle_out\n");
-            exit_program(1);
-        }
+    if (frame->repeat_sub) {
+        av_log(NULL, AV_LOG_WARNING, "Ignoring repeated subtitle frame\n");
+        return;
     }
 
+    init_output_stream_wrapper(ost, frame, 1);
+
+    enc = ost->enc_ctx;
+
     /* Note: DVB subtitle need one packet to draw them and one other
        packet to clear them */
     /* XXX: signal it in the codec context ? */
@@ -1091,50 +965,38 @@ static void do_subtitle_out(OutputFile *of,
         nb = 1;
 
     /* shift timestamp to honor -ss and make check_recording_time() work with -t */
-    pts = sub->pts;
+    pts = frame->subtitle_timing.start_pts;
     if (output_files[ost->file_index]->start_time != AV_NOPTS_VALUE)
         pts -= output_files[ost->file_index]->start_time;
-    for (i = 0; i < nb; i++) {
-        unsigned save_num_rects = sub->num_rects;
 
-        ost->sync_opts = av_rescale_q(pts, AV_TIME_BASE_Q, enc->time_base);
-        if (!check_recording_time(ost))
-            return;
+    ost->sync_opts = av_rescale_q(pts, AV_TIME_BASE_Q, enc->time_base);
+    if (!check_recording_time(ost))
+        return;
 
-        sub->pts = pts;
-        // start_display_time is required to be 0
-        sub->pts               += av_rescale_q(sub->start_display_time, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q);
-        sub->end_display_time  -= sub->start_display_time;
-        sub->start_display_time = 0;
-        if (i == 1)
-            sub->num_rects = 0;
+    frame->subtitle_timing.start_pts = pts;
+
+    for (i = 0; i < nb; i++) {
+        const unsigned save_num_rects = frame->num_subtitle_areas;
+        int64_t pts_offset = 0;
 
         ost->frames_encoded++;
 
-        subtitle_out_size = avcodec_encode_subtitle(enc, subtitle_out,
-                                                    subtitle_out_max_size, sub);
         if (i == 1)
-            sub->num_rects = save_num_rects;
-        if (subtitle_out_size < 0) {
-            av_log(NULL, AV_LOG_FATAL, "Subtitle encoding failed\n");
-            exit_program(1);
-        }
+            frame->num_subtitle_areas = 0;
 
-        av_packet_unref(pkt);
-        pkt->data = subtitle_out;
-        pkt->size = subtitle_out_size;
-        pkt->pts  = av_rescale_q(sub->pts, AV_TIME_BASE_Q, ost->mux_timebase);
-        pkt->duration = av_rescale_q(sub->end_display_time, (AVRational){ 1, 1000 }, ost->mux_timebase);
         if (enc->codec_id == AV_CODEC_ID_DVB_SUBTITLE) {
             /* XXX: the pts correction is handled here. Maybe handling
                it in the codec would be better */
             if (i == 0)
-                pkt->pts += av_rescale_q(sub->start_display_time, (AVRational){ 1, 1000 }, ost->mux_timebase);
+                pts_offset = 0;
             else
-                pkt->pts += av_rescale_q(sub->end_display_time, (AVRational){ 1, 1000 }, ost->mux_timebase);
+                pts_offset = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, ost->mux_timebase);
         }
-        pkt->dts = pkt->pts;
-        output_packet(of, pkt, ost, 0);
+
+        encode_subtitle_frame(of, ost, frame, pkt, pts_offset);
+
+        if (i == 1)
+            frame->num_subtitle_areas = save_num_rects;
     }
 }
 
@@ -1544,8 +1406,26 @@ static int reap_filters(int flush)
                 }
                 do_audio_out(of, ost, filtered_frame);
                 break;
+            case AVMEDIA_TYPE_SUBTITLE:
+
+                if (filtered_frame->format == AV_SUBTITLE_FMT_ASS && !enc->subtitle_header
+                    && filtered_frame->subtitle_header) {
+                    const char *subtitle_header = (char *)filtered_frame->subtitle_header->data;
+                    enc->subtitle_header = (uint8_t *)av_strdup(subtitle_header);
+                    if (!enc->subtitle_header)
+                        return AVERROR(ENOMEM);
+                    enc->subtitle_header_size = strlen(subtitle_header);
+                }
+
+                if ((ost->enc_ctx->width == 0 || ost->enc_ctx->height == 0)
+                    && filter->inputs[0]->w > 0 && filter->inputs[0]->h > 0 ) {
+                    ost->enc_ctx->width = filter->inputs[0]->w;
+                    ost->enc_ctx->height = filter->inputs[0]->h;
+                }
+
+                do_subtitle_out(of, ost, filtered_frame);
+                break;
             default:
-                // TODO support subtitle filters
                 av_assert0(0);
             }
 
@@ -2138,7 +2018,8 @@ static int ifilter_has_all_input_formats(FilterGraph *fg)
     int i;
     for (i = 0; i < fg->nb_inputs; i++) {
         if (fg->inputs[i]->format < 0 && (fg->inputs[i]->type == AVMEDIA_TYPE_AUDIO ||
-                                          fg->inputs[i]->type == AVMEDIA_TYPE_VIDEO))
+                                          fg->inputs[i]->type == AVMEDIA_TYPE_VIDEO ||
+                                          fg->inputs[i]->type == AVMEDIA_TYPE_SUBTITLE))
             return 0;
     }
     return 1;
@@ -2166,6 +2047,18 @@ static int ifilter_send_frame(InputFilter *ifilter, AVFrame *frame, int keep_ref
     case AVMEDIA_TYPE_VIDEO:
         need_reinit |= ifilter->width  != frame->width ||
                        ifilter->height != frame->height;
+
+        if (need_reinit)
+            need_reinit = 1;
+        break;
+    case AVMEDIA_TYPE_SUBTITLE:
+        need_reinit |= ifilter->width  != frame->width ||
+                       ifilter->height != frame->height;
+
+        need_reinit &= (ifilter->width == 0 || ifilter->height == 0);
+
+        if (need_reinit)
+            need_reinit = 1;
         break;
     }
 
@@ -2243,7 +2136,7 @@ static int ifilter_send_eof(InputFilter *ifilter, int64_t pts)
         // the filtergraph was never configured
         if (ifilter->format < 0)
             ifilter_parameters_from_codecpar(ifilter, ifilter->ist->st->codecpar);
-        if (ifilter->format < 0 && (ifilter->type == AVMEDIA_TYPE_AUDIO || ifilter->type == AVMEDIA_TYPE_VIDEO)) {
+        if (ifilter->format < 0 && (ifilter->type == AVMEDIA_TYPE_AUDIO || ifilter->type == AVMEDIA_TYPE_VIDEO || ifilter->type == AVMEDIA_TYPE_SUBTITLE)) {
             av_log(NULL, AV_LOG_ERROR, "Cannot determine format of input stream %d:%d after EOF\n", ifilter->ist->file_index, ifilter->ist->st->index);
             return AVERROR_INVALIDDATA;
         }
@@ -2281,7 +2174,7 @@ static int decode(AVCodecContext *avctx, AVFrame *frame, int *got_frame, AVPacke
 
 static int send_frame_to_filters(InputStream *ist, AVFrame *decoded_frame)
 {
-    int i, ret;
+    int i, ret = 0;
 
     av_assert1(ist->nb_filters > 0); /* ensure ret is initialized */
     for (i = 0; i < ist->nb_filters; i++) {
@@ -2482,81 +2375,114 @@ fail:
     return err < 0 ? err : ret;
 }
 
-static int transcode_subtitles(InputStream *ist, AVPacket *pkt, int *got_output,
+static InputStream *get_input_stream(OutputStream *ost)
+{
+    if (ost->source_index >= 0)
+        return input_streams[ost->source_index];
+    return NULL;
+}
+
+static int decode_subtitles(InputStream *ist, AVPacket *pkt, int *got_output,
                                int *decode_failed)
 {
-    AVSubtitle subtitle;
-    int free_sub = 1;
-    int i, ret = avcodec_decode_subtitle2(ist->dec_ctx,
-                                          &subtitle, got_output, pkt);
+    AVFrame *decoded_frame;
+    AVCodecContext *avctx = ist->dec_ctx;
+    int i = 0, ret = 0, err = 0;
+
+    if (!ist->decoded_frame && !(ist->decoded_frame = av_frame_alloc()))
+        return AVERROR(ENOMEM);
+    decoded_frame = ist->decoded_frame;
+    decoded_frame->type = AVMEDIA_TYPE_SUBTITLE;
+    decoded_frame->format = avcodec_descriptor_get_subtitle_format(avctx->codec_descriptor);
 
-    check_decode_result(NULL, got_output, ret);
+    if (!ist->subtitle_header && avctx->subtitle_header && avctx->subtitle_header_size > 0) {
+        ist->subtitle_header = av_buffer_allocz(avctx->subtitle_header_size + 1);
+        if (!ist->subtitle_header)
+            return AVERROR(ENOMEM);
+        memcpy(ist->subtitle_header->data, avctx->subtitle_header, avctx->subtitle_header_size);
+    }
+
+    ret = decode(avctx, decoded_frame, got_output, pkt);
+
+    if (ret != AVERROR_EOF)
+        check_decode_result(NULL, got_output, ret);
 
     if (ret < 0 || !*got_output) {
         *decode_failed = 1;
-        if (!pkt->size)
-            sub2video_flush(ist);
+        if (!pkt->size) {
+            // Flush
+            for (i = 0; i < ist->nb_filters; i++) {
+                ret = av_buffersrc_add_frame(ist->filters[i]->filter, NULL);
+                if (ret != AVERROR_EOF && ret < 0)
+                    av_log(NULL, AV_LOG_WARNING, "Flush the frame error.\n");
+            }
+        }
         return ret;
     }
 
     if (ist->fix_sub_duration) {
-        int end = 1;
-        if (ist->prev_sub.got_output) {
-            end = av_rescale(subtitle.pts - ist->prev_sub.subtitle.pts,
-                             1000, AV_TIME_BASE);
-            if (end < ist->prev_sub.subtitle.end_display_time) {
-                av_log(ist->dec_ctx, AV_LOG_DEBUG,
-                       "Subtitle duration reduced from %"PRId32" to %d%s\n",
-                       ist->prev_sub.subtitle.end_display_time, end,
-                       end <= 0 ? ", dropping it" : "");
-                ist->prev_sub.subtitle.end_display_time = end;
+        int64_t end = 1;
+        if (ist->prev_sub.got_output && ist->prev_sub.subtitle) {
+
+            const int64_t duration = ist->prev_sub.subtitle->subtitle_timing.duration;
+            end = decoded_frame->subtitle_timing.start_pts - ist->prev_sub.subtitle->subtitle_timing.start_pts;
+
+            if (end < duration) {
+                av_log(avctx, AV_LOG_DEBUG, "Subtitle duration reduced from %"PRId64" to %"PRId64"%s\n",
+                    duration, end, end <= 0 ? ", dropping it" : "");
+                ist->prev_sub.subtitle->subtitle_timing.duration = end;
             }
         }
-        FFSWAP(int,        *got_output, ist->prev_sub.got_output);
-        FFSWAP(int,        ret,         ist->prev_sub.ret);
-        FFSWAP(AVSubtitle, subtitle,    ist->prev_sub.subtitle);
+        FFSWAP(int,        *got_output,   ist->prev_sub.got_output);
+        FFSWAP(int,        ret,           ist->prev_sub.ret);
+        FFSWAP(AVFrame*,   decoded_frame, ist->prev_sub.subtitle);
         if (end <= 0)
-            goto out;
+            return (int)end;
     }
 
-    if (!*got_output)
+    if (!*got_output || !decoded_frame)
         return ret;
 
-    if (ist->sub2video.frame) {
-        sub2video_update(ist, INT64_MIN, &subtitle);
-    } else if (ist->nb_filters) {
-        if (!ist->sub2video.sub_queue)
-            ist->sub2video.sub_queue = av_fifo_alloc(8 * sizeof(AVSubtitle));
-        if (!ist->sub2video.sub_queue)
-            exit_program(1);
-        if (!av_fifo_space(ist->sub2video.sub_queue)) {
-            ret = av_fifo_realloc2(ist->sub2video.sub_queue, 2 * av_fifo_size(ist->sub2video.sub_queue));
-            if (ret < 0)
-                exit_program(1);
+    decoded_frame->type = AVMEDIA_TYPE_SUBTITLE;
+    decoded_frame->format = avcodec_descriptor_get_subtitle_format(avctx->codec_descriptor);
+
+    if ((ret = av_buffer_replace(&decoded_frame->subtitle_header, ist->subtitle_header)) < 0)
+        return ret;
+
+    decoded_frame->pts = av_rescale_q(decoded_frame->subtitle_timing.start_pts, AV_TIME_BASE_Q, ist->st->time_base);
+
+    if (ist->nb_filters > 0) {
+        AVFrame *filter_frame = av_frame_clone(decoded_frame);
+        if (!filter_frame) {
+            err = AVERROR(ENOMEM);
+            goto end;
         }
-        av_fifo_generic_write(ist->sub2video.sub_queue, &subtitle, sizeof(subtitle), NULL);
-        free_sub = 0;
-    }
 
-    if (!subtitle.num_rects)
-        goto out;
+        err = send_frame_to_filters(ist, filter_frame);
+        av_frame_free(&filter_frame);
+    }
 
-    ist->frames_decoded++;
+    if (err >= 0) {
+        for (i = 0; i < nb_output_streams; i++) {
+            OutputStream *ost = output_streams[i];
+            InputStream *ist_src = get_input_stream(ost);
 
-    for (i = 0; i < nb_output_streams; i++) {
-        OutputStream *ost = output_streams[i];
+            if (!ist_src || !check_output_constraints(ist, ost)
+                || ist_src != ist
+                || !ost->encoding_needed
+                || ost->enc->type != AVMEDIA_TYPE_SUBTITLE)
+                continue;
 
-        if (!check_output_constraints(ist, ost) || !ost->encoding_needed
-            || ost->enc->type != AVMEDIA_TYPE_SUBTITLE)
-            continue;
+            if (ost->filter && ost->filter->filter->nb_inputs > 0)
+                continue;
 
-        do_subtitle_out(output_files[ost->file_index], ost, &subtitle);
+            do_subtitle_out(output_files[ost->file_index], ost, decoded_frame);
+        }
     }
 
-out:
-    if (free_sub)
-        avsubtitle_free(&subtitle);
-    return ret;
+end:
+    av_frame_unref(decoded_frame);
+    return err < 0 ? err : ret;
 }
 
 static int send_filter_eof(InputStream *ist)
@@ -2660,7 +2586,7 @@ static int process_input_packet(InputStream *ist, const AVPacket *pkt, int no_eo
         case AVMEDIA_TYPE_SUBTITLE:
             if (repeating)
                 break;
-            ret = transcode_subtitles(ist, avpkt, &got_output, &decode_failed);
+            ret = decode_subtitles(ist, avpkt, &got_output, &decode_failed);
             if (!pkt && ret >= 0)
                 ret = AVERROR_EOF;
             av_packet_unref(avpkt);
@@ -2928,13 +2854,6 @@ FF_ENABLE_DEPRECATION_WARNINGS
     return 0;
 }
 
-static InputStream *get_input_stream(OutputStream *ost)
-{
-    if (ost->source_index >= 0)
-        return input_streams[ost->source_index];
-    return NULL;
-}
-
 static int compare_int64(const void *a, const void *b)
 {
     return FFDIFFSIGN(*(const int64_t *)a, *(const int64_t *)b);
@@ -3411,7 +3330,7 @@ static int init_output_stream_encode(OutputStream *ost, AVFrame *frame)
         break;
     case AVMEDIA_TYPE_SUBTITLE:
         enc_ctx->time_base = AV_TIME_BASE_Q;
-        if (!enc_ctx->width) {
+        if (!enc_ctx->width && ost->source_index >= 0) {
             enc_ctx->width     = input_streams[ost->source_index]->st->codecpar->width;
             enc_ctx->height    = input_streams[ost->source_index]->st->codecpar->height;
         }
@@ -3464,19 +3383,14 @@ static int init_output_stream(OutputStream *ost, AVFrame *frame,
         }
 
         if (ist && ist->dec->type == AVMEDIA_TYPE_SUBTITLE && ost->enc->type == AVMEDIA_TYPE_SUBTITLE) {
-            int input_props = 0, output_props = 0;
-            AVCodecDescriptor const *input_descriptor =
-                avcodec_descriptor_get(dec->codec_id);
-            AVCodecDescriptor const *output_descriptor =
-                avcodec_descriptor_get(ost->enc_ctx->codec_id);
-            if (input_descriptor)
-                input_props = input_descriptor->props & (AV_CODEC_PROP_TEXT_SUB | AV_CODEC_PROP_BITMAP_SUB);
-            if (output_descriptor)
-                output_props = output_descriptor->props & (AV_CODEC_PROP_TEXT_SUB | AV_CODEC_PROP_BITMAP_SUB);
-            if (input_props && output_props && input_props != output_props) {
-                snprintf(error, error_len,
-                         "Subtitle encoding currently only possible from text to text "
-                         "or bitmap to bitmap");
+            AVCodecDescriptor const *input_descriptor     = avcodec_descriptor_get(dec->codec_id);
+            AVCodecDescriptor const *output_descriptor    = avcodec_descriptor_get(ost->enc_ctx->codec_id);
+            const enum AVSubtitleType in_subtitle_format  = output_descriptor ? avcodec_descriptor_get_subtitle_format(input_descriptor) : AV_SUBTITLE_FMT_UNKNOWN;
+            const enum AVSubtitleType out_subtitle_format = output_descriptor ? avcodec_descriptor_get_subtitle_format(output_descriptor) : AV_SUBTITLE_FMT_UNKNOWN;
+
+            if (ist->nb_filters == 0 && in_subtitle_format != AV_SUBTITLE_FMT_UNKNOWN && out_subtitle_format != AV_SUBTITLE_FMT_UNKNOWN
+                && in_subtitle_format != out_subtitle_format) {
+                snprintf(error, error_len, "Subtitle encoding is only possible from text to text or bitmap to bitmap");
                 return AVERROR_INVALIDDATA;
             }
         }
@@ -3640,7 +3554,8 @@ static int transcode_init(void)
     for (i = 0; i < nb_output_streams; i++) {
         if (!output_streams[i]->stream_copy &&
             (output_streams[i]->enc_ctx->codec_type == AVMEDIA_TYPE_VIDEO ||
-             output_streams[i]->enc_ctx->codec_type == AVMEDIA_TYPE_AUDIO))
+             output_streams[i]->enc_ctx->codec_type == AVMEDIA_TYPE_AUDIO ||
+             output_streams[i]->enc_ctx->codec_type == AVMEDIA_TYPE_SUBTITLE))
             continue;
 
         ret = init_output_stream_wrapper(output_streams[i], NULL, 0);
@@ -4477,8 +4392,6 @@ static int process_input(int file_index)
                av_ts2timestr(input_files[ist->file_index]->ts_offset, &AV_TIME_BASE_Q));
     }
 
-    sub2video_heartbeat(ist, pkt->pts);
-
     process_input_packet(ist, pkt, 0);
 
 discard_packet:
diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h
index 9b200b806a..5ebd01c14e 100644
--- a/fftools/ffmpeg.h
+++ b/fftools/ffmpeg.h
@@ -349,17 +349,10 @@ typedef struct InputStream {
     struct { /* previous decoded subtitle and related variables */
         int got_output;
         int ret;
-        AVSubtitle subtitle;
+        AVFrame *subtitle;
     } prev_sub;
 
-    struct sub2video {
-        int64_t last_pts;
-        int64_t end_pts;
-        AVFifoBuffer *sub_queue;    ///< queue of AVSubtitle* before filter init
-        AVFrame *frame;
-        int w, h;
-        unsigned int initialize; ///< marks if sub2video_update should force an initialization
-    } sub2video;
+    AVBufferRef *subtitle_header;
 
     /* decoded data from this stream goes into all those filters
      * currently video and audio only */
@@ -659,8 +652,6 @@ int filtergraph_is_simple(FilterGraph *fg);
 int init_simple_filtergraph(InputStream *ist, OutputStream *ost);
 int init_complex_filtergraph(FilterGraph *fg);
 
-void sub2video_update(InputStream *ist, int64_t heartbeat_pts, AVSubtitle *sub);
-
 int ifilter_parameters_from_frame(InputFilter *ifilter, const AVFrame *frame);
 
 int ffmpeg_parse_options(int argc, char **argv);
diff --git a/fftools/ffmpeg_filter.c b/fftools/ffmpeg_filter.c
index 1f6cba2c04..17ce79cfaa 100644
--- a/fftools/ffmpeg_filter.c
+++ b/fftools/ffmpeg_filter.c
@@ -22,6 +22,8 @@
 
 #include "ffmpeg.h"
 
+#include "libavutil/ass_split_internal.h"
+
 #include "libavfilter/avfilter.h"
 #include "libavfilter/buffersink.h"
 #include "libavfilter/buffersrc.h"
@@ -30,11 +32,9 @@
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
 #include "libavutil/channel_layout.h"
-#include "libavutil/display.h"
 #include "libavutil/opt.h"
 #include "libavutil/pixdesc.h"
 #include "libavutil/pixfmt.h"
-#include "libavutil/imgutils.h"
 #include "libavutil/samplefmt.h"
 
 // FIXME: YUV420P etc. are actually supported with full color range,
@@ -215,9 +215,8 @@ static void init_input_filter(FilterGraph *fg, AVFilterInOut *in)
     InputFilter *ifilter;
     int i;
 
-    // TODO: support other filter types
-    if (type != AVMEDIA_TYPE_VIDEO && type != AVMEDIA_TYPE_AUDIO) {
-        av_log(NULL, AV_LOG_FATAL, "Only video and audio filters supported "
+    if (type != AVMEDIA_TYPE_VIDEO && type != AVMEDIA_TYPE_AUDIO && type != AVMEDIA_TYPE_SUBTITLE) {
+        av_log(NULL, AV_LOG_FATAL, "Only video, audio and subtitle filters supported "
                "currently.\n");
         exit_program(1);
     }
@@ -238,8 +237,9 @@ static void init_input_filter(FilterGraph *fg, AVFilterInOut *in)
         for (i = 0; i < s->nb_streams; i++) {
             enum AVMediaType stream_type = s->streams[i]->codecpar->codec_type;
             if (stream_type != type &&
-                !(stream_type == AVMEDIA_TYPE_SUBTITLE &&
-                  type == AVMEDIA_TYPE_VIDEO /* sub2video hack */))
+                // in the followng case we auto-insert the graphicsub2video conversion filter
+                // for retaining compatibility with the previous sub2video hack
+                !(stream_type == AVMEDIA_TYPE_SUBTITLE && type == AVMEDIA_TYPE_VIDEO))
                 continue;
             if (check_stream_specifier(s, s->streams[i], *p == ':' ? p + 1 : p) == 1) {
                 st = s->streams[i];
@@ -286,6 +286,17 @@ static void init_input_filter(FilterGraph *fg, AVFilterInOut *in)
     ifilter->type   = ist->st->codecpar->codec_type;
     ifilter->name   = describe_filter_link(fg, in, 1);
 
+    if (ist->st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE && ist->dec_ctx) {
+        const AVCodecDescriptor *codec_descriptor = ist->dec_ctx->codec_descriptor;
+        if (!codec_descriptor)
+            codec_descriptor = avcodec_descriptor_get(ist->dec_ctx->codec_id);
+
+        // For subtitles, we need to set the format here. Would we leave the format
+        // at -1, it would delay processing (ifilter_has_all_input_formats()) until
+        // the first subtile frame arrives, which could never happen in the worst case
+        fg->inputs[fg->nb_inputs - 1]->format = avcodec_descriptor_get_subtitle_format(codec_descriptor);
+    }
+
     ifilter->frame_queue = av_fifo_alloc(8 * sizeof(AVFrame*));
     if (!ifilter->frame_queue)
         exit_program(1);
@@ -405,6 +416,39 @@ static int insert_filter(AVFilterContext **last_filter, int *pad_idx,
     return 0;
 }
 
+static int configure_output_subtitle_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out)
+{
+    OutputStream *ost = ofilter->ost;
+    AVFilterContext *last_filter = out->filter_ctx;
+    int pad_idx = out->pad_idx;
+    int ret;
+    char name[255];
+
+    snprintf(name, sizeof(name), "out_%d_%d", ost->file_index, ost->index);
+    ret = avfilter_graph_create_filter(&ofilter->filter,
+                                       avfilter_get_by_name("sbuffersink"),
+                                       name, NULL, NULL, fg->graph);
+
+    if (ret < 0) {
+        av_log(NULL, AV_LOG_ERROR, "Unable to create filter sbuffersink\n");
+        return ret;
+    }
+
+    ////snprintf(name, sizeof(name), "trim_out_%d_%d",
+    ////         ost->file_index, ost->index);
+    ////ret = insert_trim(of->start_time, of->recording_time,
+    ////                  &last_filter, &pad_idx, name);
+    ////if (ret < 0)
+    ////    return ret;
+
+    ////ost->st->codecpar->codec_tag = MKTAG('a', 's', 's', 's');
+
+    if ((ret = avfilter_link(last_filter, pad_idx, ofilter->filter, 0)) < 0)
+        return ret;
+
+    return 0;
+}
+
 static int configure_output_video_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out)
 {
     OutputStream *ost = ofilter->ost;
@@ -585,7 +629,8 @@ static int configure_output_audio_filter(FilterGraph *fg, OutputFilter *ofilter,
         int i;
 
         for (i=0; i<of->ctx->nb_streams; i++)
-            if (of->ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
+            if (of->ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ||
+                of->ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)
                 break;
 
         if (i<of->ctx->nb_streams) {
@@ -619,6 +664,7 @@ static int configure_output_filter(FilterGraph *fg, OutputFilter *ofilter,
     switch (avfilter_pad_get_type(out->filter_ctx->output_pads, out->pad_idx)) {
     case AVMEDIA_TYPE_VIDEO: return configure_output_video_filter(fg, ofilter, out);
     case AVMEDIA_TYPE_AUDIO: return configure_output_audio_filter(fg, ofilter, out);
+    case AVMEDIA_TYPE_SUBTITLE: return configure_output_subtitle_filter(fg, ofilter, out);
     default: av_assert0(0); return 0;
     }
 }
@@ -638,51 +684,126 @@ void check_filter_outputs(void)
     }
 }
 
-static int sub2video_prepare(InputStream *ist, InputFilter *ifilter)
+static int configure_input_subtitle_filter(FilterGraph *fg, InputFilter *ifilter,
+                                        AVFilterInOut *in)
 {
-    AVFormatContext *avf = input_files[ist->file_index]->ctx;
-    int i, w, h;
+    AVFilterContext *last_filter;
+    const AVFilter *buffer_filt = avfilter_get_by_name("sbuffer");
+    InputStream *ist = ifilter->ist;
+    AVBPrint args;
+    char name[255];
+    int ret, pad_idx = 0;
+    int w, h;
+    AVBufferSrcParameters *par = av_buffersrc_parameters_alloc();
+    enum AVMediaType media_type;
+
+    if (!par)
+        return AVERROR(ENOMEM);
+
+    par->format = AV_PIX_FMT_NONE;
+
+    if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) {
+        av_log(NULL, AV_LOG_ERROR, "Cannot connect subtitle filter to audio input\n");
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+
+    if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO) {
+        av_log(NULL, AV_LOG_ERROR, "Cannot connect subtitle filter to video input\n");
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
 
-    /* Compute the size of the canvas for the subtitles stream.
-       If the subtitles codecpar has set a size, use it. Otherwise use the
-       maximum dimensions of the video streams in the same file. */
     w = ifilter->width;
     h = ifilter->height;
+
     if (!(w && h)) {
-        for (i = 0; i < avf->nb_streams; i++) {
-            if (avf->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
-                w = FFMAX(w, avf->streams[i]->codecpar->width);
-                h = FFMAX(h, avf->streams[i]->codecpar->height);
-            }
-        }
-        if (!(w && h)) {
-            w = FFMAX(w, 720);
-            h = FFMAX(h, 576);
-        }
-        av_log(avf, AV_LOG_INFO, "sub2video: using %dx%d canvas\n", w, h);
+        w = ist->dec_ctx->width;
+        h = ist->dec_ctx->height;
     }
-    ist->sub2video.w = ifilter->width  = w;
-    ist->sub2video.h = ifilter->height = h;
 
-    ifilter->width  = ist->dec_ctx->width  ? ist->dec_ctx->width  : ist->sub2video.w;
-    ifilter->height = ist->dec_ctx->height ? ist->dec_ctx->height : ist->sub2video.h;
+    if (!(w && h) && ist->dec_ctx->subtitle_header) {
+        ASSSplitContext *ass_ctx = avpriv_ass_split((char *)ist->dec_ctx->subtitle_header);
+        ASS *ass = (ASS *)ass_ctx;
+        w = ass->script_info.play_res_x;
+        h = ass->script_info.play_res_y;
+        avpriv_ass_split_free(ass_ctx);
+    }
 
-    /* rectangles are AV_PIX_FMT_PAL8, but we have no guarantee that the
-       palettes for all rectangles are identical or compatible */
-    ifilter->format = AV_PIX_FMT_RGB32;
+    ifilter->width = w;
+    ifilter->height = h;
+    ist->dec_ctx->width = w;
+    ist->dec_ctx->height = h;
 
-    ist->sub2video.frame = av_frame_alloc();
-    if (!ist->sub2video.frame)
-        return AVERROR(ENOMEM);
-    ist->sub2video.last_pts = INT64_MIN;
-    ist->sub2video.end_pts  = INT64_MIN;
+    snprintf(name, sizeof(name), "graph %d subtitle input from stream %d:%d", fg->index,
+             ist->file_index, ist->st->index);
+
+
+    av_bprint_init(&args, 0, AV_BPRINT_SIZE_AUTOMATIC);
+    av_bprintf(&args,
+             "subtitle_type=%d:width=%d:height=%d:time_base=%d/%d:",
+             ifilter->format, ifilter->width, ifilter->height,
+             ist->st->time_base.num, ist->st->time_base.den);
+    if ((ret = avfilter_graph_create_filter(&ifilter->filter, buffer_filt, name,
+                                            args.str, NULL, fg->graph)) < 0)
+        goto fail;
+
+    par->hw_frames_ctx = ifilter->hw_frames_ctx;
+    par->format = ifilter->format;
+    par->width = ifilter->width;
+    par->height = ifilter->height;
+
+    ret = av_buffersrc_parameters_set(ifilter->filter, par);
+    if (ret < 0)
+        goto fail;
+    av_freep(&par);
+    last_filter = ifilter->filter;
+
+    // This is for sub2video compatibility
+    media_type = avfilter_pad_get_type(in->filter_ctx->input_pads, in->pad_idx);
+    if (media_type == AVMEDIA_TYPE_VIDEO) {
+        int subscale_w = w, subscale_h = h;
+
+        av_log(NULL, AV_LOG_INFO, "Auto-inserting subfeed filter\n");
+        ret = insert_filter(&last_filter, &pad_idx, "subfeed", NULL);
+        if (ret < 0)
+            return ret;
+
+        if (!(subscale_w && subscale_h)) {
+            // If the subtitle frame size is unknown, try to find a video input
+            // and use its size for adding a subscale filter
+            for (int i = 0; i < fg->nb_inputs; i++) {
+                InputFilter *input = fg->inputs[i];
+                if (input->type == AVMEDIA_TYPE_VIDEO && input->width && input->height) {
+                    subscale_w = input->width;
+                    subscale_h = input->height;
+                    break;
+                }
+            }
+        }
+
+        if (subscale_w && subscale_h) {
+            char subscale_params[64];
+            snprintf(subscale_params, sizeof(subscale_params), "w=%d:h=%d", subscale_w, subscale_h);
+            ret = insert_filter(&last_filter, &pad_idx, "subscale", subscale_params);
+            if (ret < 0)
+                return ret;
+        }
 
-    /* sub2video structure has been (re-)initialized.
-       Mark it as such so that the system will be
-       initialized with the first received heartbeat. */
-    ist->sub2video.initialize = 1;
+        av_log(NULL, AV_LOG_INFO, "Auto-inserting graphicsub2video filter\n");
+        ret = insert_filter(&last_filter, &pad_idx, "graphicsub2video", NULL);
+        if (ret < 0)
+            return ret;
+    }
+
+    if ((ret = avfilter_link(last_filter, 0, in->filter_ctx, in->pad_idx)) < 0)
+        return ret;
 
     return 0;
+fail:
+    av_freep(&par);
+
+    return ret;
 }
 
 static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter,
@@ -701,8 +822,15 @@ static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter,
     char name[255];
     int ret, pad_idx = 0;
     int64_t tsoffset = 0;
-    AVBufferSrcParameters *par = av_buffersrc_parameters_alloc();
+    AVBufferSrcParameters *par;
 
+    if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_SUBTITLE) {
+        // Automatically insert conversion filter to retain compatibility
+        // with sub2video command lines
+        return configure_input_subtitle_filter(fg, ifilter, in);
+    }
+
+    par = av_buffersrc_parameters_alloc();
     if (!par)
         return AVERROR(ENOMEM);
     memset(par, 0, sizeof(*par));
@@ -717,12 +845,6 @@ static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter,
     if (!fr.num)
         fr = av_guess_frame_rate(input_files[ist->file_index]->ctx, ist->st, NULL);
 
-    if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_SUBTITLE) {
-        ret = sub2video_prepare(ist, ifilter);
-        if (ret < 0)
-            goto fail;
-    }
-
     sar = ifilter->sample_aspect_ratio;
     if(!sar.den)
         sar = (AVRational){0,1};
@@ -734,7 +856,7 @@ static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter,
              tb.num, tb.den, sar.num, sar.den);
     if (fr.num && fr.den)
         av_bprintf(&args, ":frame_rate=%d/%d", fr.num, fr.den);
-    snprintf(name, sizeof(name), "graph %d input from stream %d:%d", fg->index,
+    snprintf(name, sizeof(name), "graph %d video input from stream %d:%d", fg->index,
              ist->file_index, ist->st->index);
 
 
@@ -932,6 +1054,7 @@ static int configure_input_filter(FilterGraph *fg, InputFilter *ifilter,
     switch (avfilter_pad_get_type(in->filter_ctx->input_pads, in->pad_idx)) {
     case AVMEDIA_TYPE_VIDEO: return configure_input_video_filter(fg, ifilter, in);
     case AVMEDIA_TYPE_AUDIO: return configure_input_audio_filter(fg, ifilter, in);
+    case AVMEDIA_TYPE_SUBTITLE: return configure_input_subtitle_filter(fg, ifilter, in);
     default: av_assert0(0); return 0;
     }
 }
@@ -1125,19 +1248,6 @@ int configure_filtergraph(FilterGraph *fg)
         }
     }
 
-    /* process queued up subtitle packets */
-    for (i = 0; i < fg->nb_inputs; i++) {
-        InputStream *ist = fg->inputs[i]->ist;
-        if (ist->sub2video.sub_queue && ist->sub2video.frame) {
-            while (av_fifo_size(ist->sub2video.sub_queue)) {
-                AVSubtitle tmp;
-                av_fifo_generic_read(ist->sub2video.sub_queue, &tmp, sizeof(tmp), NULL);
-                sub2video_update(ist, INT64_MIN, &tmp);
-                avsubtitle_free(&tmp);
-            }
-        }
-    }
-
     return 0;
 
 fail:
@@ -1160,6 +1270,7 @@ int ifilter_parameters_from_frame(InputFilter *ifilter, const AVFrame *frame)
     ifilter->sample_rate         = frame->sample_rate;
     ifilter->channels            = frame->channels;
     ifilter->channel_layout      = frame->channel_layout;
+    ifilter->type                = frame->type;
 
     av_freep(&ifilter->displaymatrix);
     sd = av_frame_get_side_data(frame, AV_FRAME_DATA_DISPLAYMATRIX);
diff --git a/fftools/ffmpeg_hw.c b/fftools/ffmpeg_hw.c
index 14e702bd92..be69d54aaf 100644
--- a/fftools/ffmpeg_hw.c
+++ b/fftools/ffmpeg_hw.c
@@ -449,7 +449,7 @@ int hw_device_setup_for_encode(OutputStream *ost)
     AVBufferRef *frames_ref = NULL;
     int i;
 
-    if (ost->filter) {
+    if (ost->filter && ost->filter->filter) {
         frames_ref = av_buffersink_get_hw_frames_ctx(ost->filter->filter);
         if (frames_ref &&
             ((AVHWFramesContext*)frames_ref->data)->format ==
diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c
index 9c820ab73f..476ef628e4 100644
--- a/fftools/ffmpeg_opt.c
+++ b/fftools/ffmpeg_opt.c
@@ -2211,8 +2211,9 @@ static void init_output_filter(OutputFilter *ofilter, OptionsContext *o,
     switch (ofilter->type) {
     case AVMEDIA_TYPE_VIDEO: ost = new_video_stream(o, oc, -1); break;
     case AVMEDIA_TYPE_AUDIO: ost = new_audio_stream(o, oc, -1); break;
+    case AVMEDIA_TYPE_SUBTITLE: ost = new_subtitle_stream(o, oc, -1); break;
     default:
-        av_log(NULL, AV_LOG_FATAL, "Only video and audio filters are supported "
+        av_log(NULL, AV_LOG_FATAL, "Only video, audio and subtitle filters are supported "
                "currently.\n");
         exit_program(1);
     }
diff --git a/tests/ref/fate/filter-overlay-dvdsub-2397 b/tests/ref/fate/filter-overlay-dvdsub-2397
index 483e5fa4e0..42042b967c 100644
--- a/tests/ref/fate/filter-overlay-dvdsub-2397
+++ b/tests/ref/fate/filter-overlay-dvdsub-2397
@@ -490,368 +490,368 @@
 1,       3877,       3877,       10,     2013, 0x95a39f9c
 1,       3887,       3887,       10,     2013, 0x4f7ea123
 1,       3897,       3897,       10,     2013, 0x9efb9ba1
-0,        117,        117,        1,   518400, 0xbf8523da
+0,        117,        117,        1,   518400, 0xc44e1d4c
 1,       3907,       3907,       10,     2013, 0xf395b2cd
 1,       3917,       3917,       10,     2013, 0x261a881e
 1,       3927,       3927,       10,     2013, 0x7f2d9f72
 1,       3937,       3937,       10,     2013, 0x0105b38d
-0,        118,        118,        1,   518400, 0x41890ed6
+0,        118,        118,        1,   518400, 0xad1a084c
 1,       3952,       3952,       10,     2013, 0x0e5db67e
 1,       3962,       3962,       10,     2013, 0xfc9baf97
-0,        119,        119,        1,   518400, 0x588534fc
+0,        119,        119,        1,   518400, 0x52d52e73
 1,       3972,       3972,       10,     2013, 0x8e02a1b1
 1,       3982,       3982,       10,     2013, 0x6eecaac8
 1,       3992,       3992,       10,     2013, 0xf5558f0c
 1,       4002,       4002,       10,     2013, 0x512ba99b
-0,        120,        120,        1,   518400, 0x2145ebc1
+0,        120,        120,        1,   518400, 0xc732e546
 1,       4012,       4012,       10,     2013, 0x932b9932
 1,       4022,       4022,       10,     2013, 0xc01ea987
-0,        121,        121,        1,   518400, 0x28bca595
+0,        121,        121,        1,   518400, 0xc36f9f14
 1,       4038,       4038,       10,     2013, 0x10879cf7
 1,       4048,       4048,       10,     2013, 0x90679338
 1,       4058,       4058,       10,     2013, 0x077d8a9e
 1,       4068,       4068,       10,     2013, 0x969fa57c
-0,        122,        122,        1,   518400, 0x77dc951e
+0,        122,        122,        1,   518400, 0x78428e8b
 1,       4078,       4078,       10,     2013, 0xe049ab07
 1,       4088,       4088,       10,     2013, 0xf535b3b3
 1,       4098,       4098,       10,     2013, 0xfe76bd37
-0,        123,        123,        1,   518400, 0xe8924c17
+0,        123,        123,        1,   518400, 0xf0f8458d
 1,       4108,       4108,       10,     2013, 0xde79ad8c
 1,       4123,       4123,       10,     2013, 0xe89b9c47
 1,       4133,       4133,       10,     2013, 0xc570b0f0
-0,        124,        124,        1,   518400, 0xadb4cccc
+0,        124,        124,        1,   518400, 0x7083c653
 1,       4143,       4143,       10,     2013, 0xee709cd9
 1,       4153,       4153,       10,     2013, 0xcfe5afab
 1,       4163,       4163,       10,     2013, 0x98ff8ce4
-0,        125,        125,        1,   518400, 0x1d7b56ac
+0,        125,        125,        1,   518400, 0xa105502c
 1,       4173,       4173,       10,     2013, 0x9d19b44c
 1,       4183,       4183,       10,     2013, 0x4349917a
 1,       4193,       4193,       10,     2013, 0xbf54a59a
-0,        126,        126,        1,   518400, 0xad5739a4
+0,        126,        126,        1,   518400, 0xd411331a
 1,       4208,       4208,       10,     2013, 0xc4a399e0
 1,       4218,       4218,       10,     2013, 0x1bf58ff0
 1,       4228,       4228,       10,     2013, 0x3518ac56
-0,        127,        127,        1,   518400, 0x2733d35a
+0,        127,        127,        1,   518400, 0x83b0ccdb
 1,       4238,       4238,       10,     2013, 0xcd38c1de
 1,       4248,       4248,       10,     2013, 0xbe7d9c4d
 1,       4258,       4258,       10,     2013, 0xe113a306
 1,       4268,       4268,       10,     2013, 0x083197ea
-0,        128,        128,        1,   518400, 0x78e76da2
+0,        128,        128,        1,   518400, 0xa9be671a
 1,       4278,       4278,       10,     2013, 0x1929b1eb
 1,       4294,       4294,       10,     2013, 0x5d6ea5af
 1,       4304,       4304,       10,     2013, 0x05519d53
-0,        129,        129,        1,   518400, 0x6c076013
+0,        129,        129,        1,   518400, 0xaeb75983
 1,       4314,       4314,       10,     2013, 0x5773b380
 1,       4324,       4324,       10,     2013, 0xaa70a8f5
 1,       4334,       4334,       10,     2013, 0x990db0ec
-0,        130,        130,        1,   518400, 0x7854f2b1
+0,        130,        130,        1,   518400, 0x81f8ec13
 1,       4344,       4344,       10,     2013, 0x91d3a623
 1,       4354,       4354,       10,     2013, 0xc91f9824
 1,       4364,       4364,       10,     2013, 0x1d058abf
-0,        131,        131,        1,   518400, 0xd2ae1ecd
+0,        131,        131,        1,   518400, 0x8aaa1839
 1,       4379,       4379,       10,     2013, 0x8de1b8d5
 1,       4389,       4389,       10,     2013, 0x7872b06b
 1,       4399,       4399,       10,     2013, 0xa084c203
-0,        132,        132,        1,   518400, 0xf5eab38d
+0,        132,        132,        1,   518400, 0xc98bacf5
 1,       4409,       4409,       10,     2013, 0xff90ae8d
 1,       4419,       4419,       10,     2013, 0x61dead8e
 1,       4429,       4429,       10,     2013, 0xee76b284
-0,        133,        133,        1,   518400, 0x994d3e9c
+0,        133,        133,        1,   518400, 0x31083804
 1,       4439,       4439,       10,     2013, 0xe888af7f
 1,       4449,       4449,       10,     2013, 0x5d57b115
 1,       4464,       4464,       10,     2013, 0xcdbfb1d0
-0,        134,        134,        1,   518400, 0x95ab705a
+0,        134,        134,        1,   518400, 0x540a69dc
 1,       4474,       4474,       10,     2013, 0x2e28a952
 1,       4484,       4484,       10,     2013, 0x4795a994
 1,       4494,       4494,       10,     2013, 0x7e7ea304
 1,       4504,       4504,       10,     2013, 0x9502c1e1
-0,        135,        135,        1,   518400, 0x3c83c5ce
+0,        135,        135,        1,   518400, 0x80d3bf46
 1,       4514,       4514,       10,     2013, 0xf7c78ab2
 1,       4524,       4524,       10,     2013, 0x24049816
 1,       4534,       4534,       10,     2013, 0x52089dcf
-0,        136,        136,        1,   518400, 0xfa22c508
+0,        136,        136,        1,   518400, 0x2967be7f
 1,       4550,       4550,       10,     2013, 0x2150a0b1
 1,       4560,       4560,       10,     2013, 0x3c2e9b93
 1,       4570,       4570,       10,     2013, 0x491f932b
-0,        137,        137,        1,   518400, 0xddda1712
+0,        137,        137,        1,   518400, 0x5a3b1092
 1,       4580,       4580,       10,     2013, 0x31359cf8
 1,       4590,       4590,       10,     2013, 0x1b00ac3f
 1,       4600,       4600,       10,     2013, 0x8d7ab3cb
-0,        138,        138,        1,   518400, 0x985a3b93
+0,        138,        138,        1,   518400, 0x8741350b
 1,       4610,       4610,       10,     2013, 0xb2c2a4de
 1,       4620,       4620,       10,     2013, 0x80a4abf2
 1,       4635,       4635,       10,     2013, 0x0701a4ee
-0,        139,        139,        1,   518400, 0xea63c5e7
+0,        139,        139,        1,   518400, 0xd5a9bf60
 1,       4645,       4645,       10,     2013, 0xdc1ba5bc
 1,       4655,       4655,       10,     2013, 0x6083a8a4
 1,       4665,       4665,       10,     2013, 0x6226ad45
-0,        140,        140,        1,   518400, 0xef64983d
+0,        140,        140,        1,   518400, 0xc05f91ba
 1,       4675,       4675,       10,     2013, 0x2732a205
 1,       4685,       4685,       10,     2013, 0x0f62a0d3
 1,       4695,       4695,       10,     2013, 0xc1799249
-0,        141,        141,        1,   518400, 0x747bb193
+0,        141,        141,        1,   518400, 0x3fdaab0b
 1,       4705,       4705,       10,     2013, 0xbccfa9c8
 1,       4720,       4720,       10,     2013, 0xded096e7
 1,       4730,       4730,       10,     2013, 0x7f0daf43
-0,        142,        142,        1,   518400, 0xb8748862
+0,        142,        142,        1,   518400, 0xab7281d9
 1,       4740,       4740,       10,     2013, 0xc47ea682
 1,       4750,       4750,       10,     2013, 0x5a72b07a
 1,       4760,       4760,       10,     2013, 0x386faa8c
 1,       4770,       4770,       10,     2013, 0xf9919a91
-0,        143,        143,        1,   518400, 0xaab55a5f
+0,        143,        143,        1,   518400, 0xc80053d6
 1,       4780,       4780,       10,     2013, 0x4908897e
 1,       4790,       4790,       10,     2013, 0x4882b594
-0,        144,        144,        1,   518400, 0x7b468add
+0,        144,        144,        1,   518400, 0x6526845c
 1,       4806,       4806,       10,     2013, 0x113e98d1
 1,       4816,       4816,       10,     2013, 0x5098b30d
 1,       4826,       4826,       10,     2013, 0x0ef7b857
 1,       4836,       4836,       10,     2013, 0x216ea176
-0,        145,        145,        1,   518400, 0xf2078707
+0,        145,        145,        1,   518400, 0x1b788089
 1,       4846,       4846,       10,     2013, 0xf906944a
 1,       4856,       4856,       10,     2013, 0xee9b92fb
 1,       4866,       4866,       10,     2013, 0xd6029209
-0,        146,        146,        1,   518400, 0x6a2d931e
+0,        146,        146,        1,   518400, 0xfa8e8ca9
 1,       4876,       4876,       10,     2013, 0x2256a12e
 1,       4891,       4891,       10,     2013, 0x89de8e4a
 1,       4901,       4901,       10,     2013, 0x0bf0a584
-0,        147,        147,        1,   518400, 0xbbe3c417
+0,        147,        147,        1,   518400, 0xb278bda1
 1,       4911,       4911,       10,     2013, 0x6a5ebd58
 1,       4921,       4921,       10,     2013, 0x3edd9aa4
 1,       4931,       4931,       10,     2013, 0xbd66ac26
-0,        148,        148,        1,   518400, 0x6294e449
+0,        148,        148,        1,   518400, 0xb0c3ddca
 1,       4941,       4941,       10,     2013, 0x313896ea
 1,       4951,       4951,       10,     2013, 0x6b83a6a0
 1,       4961,       4961,       10,     2013, 0x9aafb109
-0,        149,        149,        1,   518400, 0xa05721e7
+0,        149,        149,        1,   518400, 0x10351b53
 1,       4976,       4976,       10,     2013, 0x5192a85a
 1,       4986,       4986,       10,     2013, 0x1f919f79
 1,       4996,       4996,       10,     2013, 0xc0799c40
-0,        150,        150,        1,   518400, 0x37749183
+0,        150,        150,        1,   518400, 0xc1408aee
 1,       5006,       5006,       10,     2013, 0x2988bcd8
 1,       5016,       5016,       10,     2013, 0x1482913a
 1,       5026,       5026,       10,     2013, 0x74da9a94
 1,       5036,       5036,       10,     2013, 0x763eb709
-0,        151,        151,        1,   518400, 0xf9d9dca0
+0,        151,        151,        1,   518400, 0xf016d615
 1,       5046,       5046,       10,     2013, 0x1285b405
 1,       5062,       5062,       10,     2013, 0xb6ab9dfc
-0,        152,        152,        1,   518400, 0x5f8ccf08
+0,        152,        152,        1,   518400, 0xa768c892
 1,       5072,       5072,       10,     2013, 0xe4c8bf19
 1,       5082,       5082,       10,     2013, 0xabbbade8
 1,       5092,       5092,       10,     2013, 0xf8b69d89
 1,       5102,       5102,       10,     2013, 0xce04a866
-0,        153,        153,        1,   518400, 0x7303f77b
+0,        153,        153,        1,   518400, 0x11c3f11e
 1,       5112,       5112,       10,     2013, 0x07528abf
 1,       5122,       5122,       10,     2013, 0x74fb98bf
 1,       5132,       5132,       10,     2013, 0x579fb1c9
-0,        154,        154,        1,   518400, 0x22b0513f
+0,        154,        154,        1,   518400, 0xcd9a4ac4
 1,       5147,       5147,       10,     2013, 0x7ddea2ed
 1,       5157,       5157,       10,     2013, 0x296caa2c
 1,       5167,       5167,       10,     2013, 0x346d9c4f
-0,        155,        155,        1,   518400, 0x330485d2
+0,        155,        155,        1,   518400, 0x4ade7f5e
 1,       5177,       5177,       10,     2013, 0x3e1fba15
 1,       5187,       5187,       10,     2013, 0x48a2908f
 1,       5197,       5197,       10,     2013, 0xc1938d09
-0,        156,        156,        1,   518400, 0x7f83daea
+0,        156,        156,        1,   518400, 0x655dd46b
 1,       5207,       5207,       10,     2013, 0x0e96a060
 1,       5217,       5217,       10,     2013, 0x7b6a9e06
 1,       5232,       5232,       10,     2013, 0x5b779d28
-0,        157,        157,        1,   518400, 0xee19f2df
+0,        157,        157,        1,   518400, 0x5ab5ec61
 1,       5242,       5242,       10,     2013, 0xf600aca1
 1,       5252,       5252,       10,     2013, 0x3a6c9e68
 1,       5262,       5262,       10,     2013, 0x0c8dc1b0
-0,        158,        158,        1,   518400, 0xb71b1c77
+0,        158,        158,        1,   518400, 0x45dc15e6
 1,       5272,       5272,       10,     2013, 0x26beb245
 1,       5282,       5282,       10,     2013, 0x2bc09557
 1,       5292,       5292,       10,     2013, 0x27fc8845
 1,       5302,       5302,       10,     2013, 0x1025aa47
-0,        159,        159,        1,   518400, 0xbffc1856
+0,        159,        159,        1,   518400, 0x201911d3
 1,       5318,       5318,       10,     2013, 0xc2e69baa
 1,       5328,       5328,       10,     2013, 0xdb249b92
 1,       5338,       5338,       10,     2013, 0x6ccda29e
-0,        160,        160,        1,   518400, 0xabc125aa
+0,        160,        160,        1,   518400, 0x0fbc1f46
 1,       5348,       5348,       10,     2013, 0xeaf6a1cf
 1,       5358,       5358,       10,     2013, 0x509ba397
 1,       5368,       5368,       10,     2013, 0xfaf8a2df
-0,        161,        161,        1,   518400, 0x5ee467f8
+0,        161,        161,        1,   518400, 0x7e316179
 1,       5378,       5378,       10,     2013, 0x41388f28
 1,       5388,       5388,       10,     2013, 0xfe5eab39
 1,       5403,       5403,       10,     2013, 0xd5ffa066
-0,        162,        162,        1,   518400, 0x6c2cf168
+0,        162,        162,        1,   518400, 0x73bbeaed
 1,       5413,       5413,       10,     2013, 0x6813a30a
 1,       5423,       5423,       10,     2013, 0x9be89718
 1,       5433,       5433,       10,     2013, 0xaec3a27b
-0,        163,        163,        1,   518400, 0x63996b26
+0,        163,        163,        1,   518400, 0x3a7c648a
 1,       5446,       5446,       10,     2013, 0x579a983e
 1,       5456,       5456,       10,     2013, 0x98cea21f
 1,       5466,       5466,       10,     2013, 0xca77a58a
-0,        164,        164,        1,   518400, 0xb34d789a
+0,        164,        164,        1,   518400, 0x9f707209
 1,       5476,       5476,       10,     2013, 0xcbc3b1ee
 1,       5486,       5486,       10,     2013, 0xf3bb8f07
 1,       5496,       5496,       10,     2013, 0x6aeebd92
-0,        165,        165,        1,   518400, 0xf49c030f
+0,        165,        165,        1,   518400, 0x9f25fc5c
 1,       5506,       5506,       10,     2013, 0xe955a449
 1,       5516,       5516,       10,     2013, 0x9436aa5b
 1,       5531,       5531,       10,     2013, 0x4f0a8f9f
-0,        166,        166,        1,   518400, 0x092dc41a
+0,        166,        166,        1,   518400, 0x2ed8bd75
 1,       5541,       5541,       10,     2013, 0x3551b22d
 1,       5551,       5551,       10,     2013, 0x0959a3d4
 1,       5561,       5561,       10,     2013, 0x2ed5a11b
 1,       5571,       5571,       10,     2013, 0x8f52a5c3
-0,        167,        167,        1,   518400, 0x4134c577
+0,        167,        167,        1,   518400, 0xb493becb
 1,       5581,       5581,       10,     2013, 0x6552978d
 1,       5591,       5591,       10,     2013, 0x7dcca0c1
 1,       5601,       5601,       10,     2013, 0xbcd4a3c9
-0,        168,        168,        1,   518400, 0x261de1ed
+0,        168,        168,        1,   518400, 0x7df6db57
 1,       5616,       5616,       10,     2013, 0xfe41a8d8
 1,       5626,       5626,       10,     2013, 0xc85aae14
 1,       5636,       5636,       10,     2013, 0x1185b346
-0,        169,        169,        1,   518400, 0xcbc8566a
+0,        169,        169,        1,   518400, 0x1cb94fca
 1,       5646,       5646,       10,     2013, 0xf7429a0d
 1,       5656,       5656,       10,     2013, 0x48c2a160
 1,       5666,       5666,       10,     2013, 0x9d85a85d
-0,        170,        170,        1,   518400, 0x407a5c76
+0,        170,        170,        1,   518400, 0x70db55d8
 1,       5676,       5676,       10,     2013, 0xbbe89fe9
 1,       5686,       5686,       10,     2013, 0xea429fe2
 1,       5702,       5702,       10,     2013, 0x221ca1d4
-0,        171,        171,        1,   518400, 0x1ed73bb2
+0,        171,        171,        1,   518400, 0xc1d9351b
 1,       5712,       5712,       10,     2013, 0x394b925b
 1,       5722,       5722,       10,     2013, 0x556dc26f
 1,       5732,       5732,       10,     2013, 0xce21a5e1
-0,        172,        172,        1,   518400, 0x8467ddb5
+0,        172,        172,        1,   518400, 0xa4b0d717
 1,       5742,       5742,       10,     2013, 0xbc87c0a8
 1,       5752,       5752,       10,     2013, 0xbac4ac07
 1,       5762,       5762,       10,     2013, 0xdeefa4aa
 1,       5772,       5772,       10,     2013, 0x1f15b362
-0,        173,        173,        1,   518400, 0x0523dc73
+0,        173,        173,        1,   518400, 0x3730d5e9
 1,       5787,       5787,       10,     2013, 0x6406b7b2
 1,       5797,       5797,       10,     2013, 0x8030a03d
-0,        174,        174,        1,   518400, 0x81f5e895
+0,        174,        174,        1,   518400, 0x9673e1ec
 1,       5807,       5807,       10,     2013, 0x0373a5b1
 1,       5817,       5817,       10,     2013, 0x34ef93da
 1,       5827,       5827,       10,     2013, 0x94c198fe
 1,       5837,       5837,       10,     2013, 0xfefcabad
-0,        175,        175,        1,   518400, 0xfc74608d
+0,        175,        175,        1,   518400, 0x877959d5
 1,       5847,       5847,       10,     2013, 0x8755b3ec
 1,       5857,       5857,       10,     2013, 0xe436a6fd
 1,       5872,       5872,       10,     2013, 0x9cf5a11e
-0,        176,        176,        1,   518400, 0xc4e0dae0
+0,        176,        176,        1,   518400, 0x04f3d421
 1,       5882,       5882,       10,     2013, 0x03b8a98c
 1,       5892,       5892,       10,     2013, 0x6216a138
 1,       5902,       5902,       10,     2013, 0xd87b9f12
-0,        177,        177,        1,   518400, 0x98367f5b
+0,        177,        177,        1,   518400, 0x4f3078bc
 1,       5912,       5912,       10,     2013, 0x4ce99653
 1,       5922,       5922,       10,     2013, 0x6c2ea9e2
 1,       5932,       5932,       10,     2013, 0x918cae4c
-0,        178,        178,        1,   518400, 0x0f1a869d
+0,        178,        178,        1,   518400, 0x8a127ff8
 1,       5942,       5942,       10,     2013, 0xd19fa5f2
 1,       5958,       5958,       10,     2013, 0x0bdda7c6
 1,       5968,       5968,       10,     2013, 0x0f9ab0ca
-0,        179,        179,        1,   518400, 0x45b6ccf2
+0,        179,        179,        1,   518400, 0x5864c64f
 1,       5978,       5978,       10,     2013, 0x410a92b1
 1,       5988,       5988,       10,     2013, 0xcfbe9d1c
 1,       5998,       5998,       10,     2013, 0x59ed9d15
-0,        180,        180,        1,   518400, 0x5f9ccb77
+0,        180,        180,        1,   518400, 0xdaccc4c0
 1,       6008,       6008,       10,     2013, 0x4e129e27
 1,       6018,       6018,       10,     2013, 0x7bb9ac0a
 1,       6028,       6028,       10,     2013, 0x826ca82b
-0,        181,        181,        1,   518400, 0x5f15ea31
+0,        181,        181,        1,   518400, 0xd999e376
 1,       6043,       6043,       10,     2013, 0x9ad5a74b
 1,       6053,       6053,       10,     2013, 0x6c5f969a
 1,       6063,       6063,       10,     2013, 0x8479a0e5
-0,        182,        182,        1,   518400, 0x86369f27
+0,        182,        182,        1,   518400, 0x8af39876
 1,       6073,       6073,       10,     2013, 0x165298ef
 1,       6083,       6083,       10,     2013, 0xdcadb4a1
 1,       6093,       6093,       10,     2013, 0xa90e987c
 1,       6103,       6103,       10,     2013, 0x1ac5b510
-0,        183,        183,        1,   518400, 0x2e27f9fa
+0,        183,        183,        1,   518400, 0x5e72f33d
 1,       6113,       6113,       10,     2013, 0x66728d85
 1,       6128,       6128,       10,     2013, 0xe4859fc5
 1,       6138,       6138,       10,     2013, 0x9901786e
-0,        184,        184,        1,   518400, 0xc029a44d
+0,        184,        184,        1,   518400, 0x14af9d92
 1,       6148,       6148,       10,     2013, 0x6aebb406
 1,       6158,       6158,       10,     2013, 0x7d13a2cc
 1,       6168,       6168,       10,     2013, 0x99b7a8cc
-0,        185,        185,        1,   518400, 0xebee33b0
+0,        185,        185,        1,   518400, 0x50b82d10
 1,       6178,       6178,       10,     2013, 0x80b8a624
 1,       6188,       6188,       10,     2013, 0xbb6aa271
 1,       6198,       6198,       10,     2013, 0x17af9e4a
-0,        186,        186,        1,   518400, 0x19e5494f
+0,        186,        186,        1,   518400, 0xc068429c
 1,       6214,       6214,       10,     2013, 0xfaf0a8f1
 1,       6224,       6224,       10,     2013, 0xd6849b93
 1,       6234,       6234,       10,     2013, 0xe9829669
-0,        187,        187,        1,   518400, 0xf697bd7c
+0,        187,        187,        1,   518400, 0x8934b6d1
 1,       6244,       6244,       10,     2013, 0x7ec98944
 1,       6254,       6254,       10,     2013, 0x2b2099a4
 1,       6264,       6264,       10,     2013, 0x1033a82f
-0,        188,        188,        1,   518400, 0x82569002
+0,        188,        188,        1,   518400, 0x11d08947
 1,       6274,       6274,       10,     2013, 0x5ec88990
 1,       6284,       6284,       10,     2013, 0xd2a19b3d
 1,       6299,       6299,       10,     2013, 0xa377b268
-0,        189,        189,        1,   518400, 0xfcb6d707
+0,        189,        189,        1,   518400, 0x8a27d041
 1,       6309,       6309,       10,     2013, 0xfa859901
 1,       6319,       6319,       10,     2013, 0x1713955a
 1,       6329,       6329,       10,     2013, 0x70aab0da
 1,       6339,       6339,       10,     2013, 0xcdaea422
-0,        190,        190,        1,   518400, 0x82a9662b
+0,        190,        190,        1,   518400, 0xab265f7d
 1,       6349,       6349,       10,     2013, 0x65c3bf80
 1,       6359,       6359,       10,     2013, 0x1d75a55f
 1,       6369,       6369,       10,     2013, 0xa5bea4de
-0,        191,        191,        1,   518400, 0x212e16ee
+0,        191,        191,        1,   518400, 0xff491040
 1,       6384,       6384,       10,     2013, 0x184db71c
 1,       6394,       6394,       10,     2013, 0x99858ec8
 1,       6404,       6404,       10,     2013, 0xb8f2aee5
-0,        192,        192,        1,   518400, 0x2ca34dca
+0,        192,        192,        1,   518400, 0x822b4704
 1,       6414,       6414,       10,     2013, 0x4435b2ef
 1,       6424,       6424,       10,     2013, 0x8acfa6c7
 1,       6434,       6434,       10,     2013, 0x42b4c01f
-0,        193,        193,        1,   518400, 0xe9ebe0a5
+0,        193,        193,        1,   518400, 0x4523d9f4
 1,       6444,       6444,       10,     2013, 0x6e308c13
 1,       6454,       6454,       10,     2013, 0x8227a0f6
 1,       6470,       6470,       10,     2013, 0x6f12a7a2
-0,        194,        194,        1,   518400, 0x4e6b6917
+0,        194,        194,        1,   518400, 0xfc3c626e
 1,       6480,       6480,       10,     2013, 0x785392be
 1,       6490,       6490,       10,     2013, 0x81849c2b
 1,       6500,       6500,       10,     2013, 0x5cf2af65
-0,        195,        195,        1,   518400, 0x7dcf20ab
+0,        195,        195,        1,   518400, 0x237319e5
 1,       6510,       6510,       10,     2013, 0x0c6ca6b4
 1,       6520,       6520,       10,     2013, 0x412fab9f
 1,       6530,       6530,       10,     2013, 0x08e792b4
-0,        196,        196,        1,   518400, 0xf30fac97
+0,        196,        196,        1,   518400, 0x892ca5d8
 1,       6540,       6540,       10,     2013, 0x407aace3
 1,       6555,       6555,       10,     2013, 0xd26bac16
 1,       6565,       6565,       10,     2013, 0xac8bb295
-0,        197,        197,        1,   518400, 0xcb9fc692
+0,        197,        197,        1,   518400, 0xc4c0bfc7
 1,       6575,       6575,       10,     2013, 0xddd1949c
 1,       6585,       6585,       10,     2013, 0x6b26b868
 1,       6595,       6595,       10,     2013, 0x5eaba587
 1,       6605,       6605,       10,     2013, 0xef0793b9
-0,        198,        198,        1,   518400, 0x5d05601e
+0,        198,        198,        1,   518400, 0x57c85956
 1,       6615,       6615,       10,     2013, 0xdef19bd6
 1,       6625,       6625,       10,     2013, 0xca98a635
-0,        199,        199,        1,   518400, 0x456c1417
+0,        199,        199,        1,   518400, 0xd6300d46
 1,       6640,       6640,       10,     2013, 0x06269a5a
 1,       6650,       6650,       10,     2013, 0x32cb9952
 1,       6660,       6660,       10,     2013, 0xf01fa95a
 1,       6670,       6670,       10,     2013, 0xefab9e55
-0,        200,        200,        1,   518400, 0x9a0fd1ad
+0,        200,        200,        1,   518400, 0xd3dacaec
 1,       6680,       6680,       10,     2013, 0x55a3b63a
 1,       6690,       6690,       10,     2013, 0xcd36a553
 1,       6700,       6700,       10,     2013, 0x2ec19877
-0,        201,        201,        1,   518400, 0x55db9716
+0,        201,        201,        1,   518400, 0x65429052
 1,       6710,       6710,       10,     2013, 0xc18b924c
 1,       6726,       6726,       10,     2013, 0xf132b04c
 1,       6736,       6736,       10,     2013, 0x7975a44d
-0,        202,        202,        1,   518400, 0x1f0d40d6
+0,        202,        202,        1,   518400, 0xec803a15
 1,       6746,       6746,       10,     2013, 0x2aaf94cb
 1,       6756,       6756,       10,     2013, 0x58cfa60f
 1,       6766,       6766,       10,     2013, 0x9757a658
-0,        203,        203,        1,   518400, 0x73695c82
+0,        203,        203,        1,   518400, 0x7a9a55c9
 1,       6776,       6776,       10,     2013, 0x67ebc0d5
 1,       6786,       6786,       10,     2013, 0x3c50a70e
 1,       6796,       6796,       10,     2013, 0x9c5799c6
-0,        204,        204,        1,   518400, 0xb0f10812
+0,        204,        204,        1,   518400, 0xcac30160
 1,       6811,       6811,       10,     2013, 0x018d85b2
 1,       6821,       6821,       10,     2013, 0x5367a956
-0,        205,        205,        1,   518400, 0xdec18505
-0,        208,        208,        1,   518400, 0xb147b947
-0,        240,        240,        1,   518400, 0x9d2e3977
+0,        205,        205,        1,   518400, 0x7e187e4f
+0,        208,        208,        1,   518400, 0x0be0b2a2
+0,        213,        213,        1,   518400, 0x0be0b2a2
diff --git a/tests/ref/fate/sub-dvb b/tests/ref/fate/sub-dvb
index cbd1801d64..8f33c75d70 100644
--- a/tests/ref/fate/sub-dvb
+++ b/tests/ref/fate/sub-dvb
@@ -1,75 +1,93 @@
 #tb 0: 1/1000000
 #media_type 0: subtitle
 #codec_id 0: dvb_subtitle
-0,   15600000,   15600000,   159000,     1168, 0xd0f89d82
-0,   15759000,   15759000,   159000,       14, 0x064900eb
-0,   15760000,   15760000,   239000,     1544, 0xe60f1751
-0,   15999000,   15999000,   239000,       14, 0x0729010b
-0,   16000000,   16000000,   339000,     1658, 0xbe343093
-0,   16339000,   16339000,   339000,       14, 0x0809012b
-0,   16340000,   16340000,   599000,     2343, 0xc68f07ef
-0,   16939000,   16939000,   599000,       14, 0x08e9014b
-0,   16940000,   16940000,   459000,     2568, 0x0ee657b1
-0,   17399000,   17399000,   459000,       14, 0x09c9016b
-0,   17400000,   17400000,   359000,     3422, 0xba5b63ce
-0,   17759000,   17759000,   359000,       14, 0x0aa9018b
-0,   17760000,   17760000,   219000,     5078, 0x95b19902
-0,   17979000,   17979000,   219000,       14, 0x0b8901ab
-0,   17980000,   17980000,   959000,     5808, 0xc9717b89
-0,   18939000,   18939000,   959000,       14, 0x0c6901cb
-0,   18940000,   18940000,   219000,     6015, 0x0becbfac
-0,   19159000,   19159000,   219000,       14, 0x064900eb
-0,   19160000,   19160000,   259000,     6519, 0xfcd24d26
-0,   19419000,   19419000,   259000,       14, 0x0729010b
-0,   19420000,   19420000,    99000,     7061, 0xf0320408
-0,   19519000,   19519000,    99000,       14, 0x0809012b
-0,   19520000,   19520000,   219000,     4773, 0x66c93074
-0,   19739000,   19739000,   219000,       14, 0x08e9014b
-0,   19740000,   19740000,   219000,     5546, 0x06052c81
-0,   19959000,   19959000,   219000,       14, 0x09c9016b
-0,   19960000,   19960000,   239000,     5754, 0x904f7325
-0,   20199000,   20199000,   239000,       14, 0x0aa9018b
-0,   20200000,   20200000,   139000,     6099, 0xe30cde07
-0,   20339000,   20339000,   139000,       14, 0x0b8901ab
-0,   20340000,   20340000,   799000,     6839, 0x770fcb6c
-0,   21139000,   21139000,   799000,       14, 0x0c6901cb
-0,   21140000,   21140000,   239000,     4744, 0xa91e1b41
-0,   21379000,   21379000,   239000,       14, 0x064900eb
-0,   21380000,   21380000,   339000,     5824, 0xcf6d782b
-0,   21719000,   21719000,   339000,       14, 0x0729010b
-0,   21720000,   21720000,  1439000,     6212, 0xabf8f7cf
-0,   23159000,   23159000,  1439000,       14, 0x0809012b
-0,   23160000,   23160000,  1319000,     7082, 0xd7ca10f2
-0,   24479000,   24479000,  1319000,       14, 0x08e9014b
-0,   24480000,   24480000,   219000,     5345, 0x12b2cae0
-0,   24699000,   24699000,   219000,       14, 0x09c9016b
-0,   24700000,   24700000,   219000,     5765, 0xc7d46192
-0,   24919000,   24919000,   219000,       14, 0x0aa9018b
-0,   24920000,   24920000,   599000,     6557, 0xcb995d30
-0,   25519000,   25519000,   599000,       14, 0x0b8901ab
-0,   25520000,   25520000,   219000,     7091, 0xe6ea0559
-0,   25739000,   25739000,   219000,       14, 0x0c6901cb
-0,   25740000,   25740000,   239000,     7305, 0xb66c404e
-0,   25979000,   25979000,   239000,       14, 0x064900eb
-0,   25980000,   25980000,   359000,     7590, 0x0cc2a481
-0,   26339000,   26339000,   359000,       14, 0x0729010b
-0,   26340000,   26340000,   219000,     4629, 0xe18cfea8
-0,   26559000,   26559000,   219000,       14, 0x0809012b
-0,   26560000,   26560000,   719000,     4785, 0x82043fc0
-0,   27279000,   27279000,   719000,       14, 0x08e9014b
-0,   27280000,   27280000,   459000,     6061, 0xbde7d245
-0,   27739000,   27739000,   459000,       14, 0x09c9016b
-0,   27740000,   27740000,   239000,     6301, 0x92d01a51
-0,   27979000,   27979000,   239000,       14, 0x0aa9018b
-0,   27980000,   27980000,    99000,     6736, 0xbd25a134
-0,   28079000,   28079000,    99000,       14, 0x0b8901ab
-0,   28080000,   28080000,   219000,     7214, 0x7ef93c13
-0,   28299000,   28299000,   219000,       14, 0x0c6901cb
-0,   28300000,   28300000,   239000,     7366, 0x5bed7fcd
-0,   28539000,   28539000,   239000,       14, 0x064900eb
-0,   28540000,   28540000,   599000,     4564, 0x7f4c014b
-0,   29139000,   29139000,   599000,       14, 0x0729010b
-0,   29140000,   29140000,   219000,     4637, 0x682626b7
-0,   29359000,   29359000,   219000,       14, 0x0809012b
-0,   29360000,   29360000,  1679000,     5358, 0x29e30c48
-0,   31039000,   31039000,  1679000,       14, 0x08e9014b
+0,          0,          0,   279000,       14, 0x05d900db
+0,     279000,     279000,   279000,       14, 0x064900eb
+0,     280000,     280000,  4999000,       14, 0x06b900fb
+0,    5279000,    5279000,  4999000,       14, 0x0729010b
+0,    5280000,    5280000,  5019000,       14, 0x0799011b
+0,   10299000,   10299000,  5019000,       14, 0x0809012b
+0,   10300000,   10300000,  3599000,       14, 0x0879013b
+0,   13899000,   13899000,  3599000,       14, 0x08e9014b
+0,   13900000,   13900000,   219000,       14, 0x0959015b
+0,   14119000,   14119000,   219000,       14, 0x09c9016b
+0,   14120000,   14120000,  1439000,       14, 0x0a39017b
+0,   15559000,   15559000,  1439000,       14, 0x0aa9018b
+0,   15560000,   15560000,    39000,       14, 0x0b19019b
+0,   15599000,   15599000,    39000,       14, 0x0b8901ab
+0,   15600000,   15600000,   159000,     1168, 0xd69da022
+0,   15759000,   15759000,   159000,       14, 0x0c6901cb
+0,   15760000,   15760000,   239000,     1544, 0xc5f116f1
+0,   15999000,   15999000,   239000,       14, 0x064900eb
+0,   16000000,   16000000,   339000,     1658, 0x73563033
+0,   16339000,   16339000,   339000,       14, 0x0729010b
+0,   16340000,   16340000,   599000,     2343, 0x7ac2078f
+0,   16939000,   16939000,   599000,       14, 0x0809012b
+0,   16940000,   16940000,   459000,     2568, 0x6eaa5751
+0,   17399000,   17399000,   459000,       14, 0x08e9014b
+0,   17400000,   17400000,   359000,     3422, 0xd9d0636e
+0,   17759000,   17759000,   359000,       14, 0x09c9016b
+0,   17760000,   17760000,   219000,     5078, 0x722c9862
+0,   17979000,   17979000,   219000,       14, 0x0aa9018b
+0,   17980000,   17980000,   959000,     5808, 0x38dd7ae9
+0,   18939000,   18939000,   959000,       14, 0x0b8901ab
+0,   18940000,   18940000,   219000,     6015, 0xd4d2c40c
+0,   19159000,   19159000,   219000,       14, 0x0c6901cb
+0,   19160000,   19160000,   259000,     6519, 0x08af4c86
+0,   19419000,   19419000,   259000,       14, 0x064900eb
+0,   19420000,   19420000,    99000,     7061, 0xecf10368
+0,   19519000,   19519000,    99000,       14, 0x0729010b
+0,   19520000,   19520000,   219000,     4773, 0xbee42fd4
+0,   19739000,   19739000,   219000,       14, 0x0809012b
+0,   19740000,   19740000,   219000,     5546, 0xdb822be1
+0,   19959000,   19959000,   219000,       14, 0x08e9014b
+0,   19960000,   19960000,   239000,     5754, 0xfdcc7285
+0,   20199000,   20199000,   239000,       14, 0x09c9016b
+0,   20200000,   20200000,   139000,     6099, 0xa409dd67
+0,   20339000,   20339000,   139000,       14, 0x0aa9018b
+0,   20340000,   20340000,   799000,     6839, 0xc5eecacc
+0,   21139000,   21139000,   799000,       14, 0x0b8901ab
+0,   21140000,   21140000,   239000,     4744, 0x4e451fa1
+0,   21379000,   21379000,   239000,       14, 0x0c6901cb
+0,   21380000,   21380000,   339000,     5824, 0x5299778b
+0,   21719000,   21719000,   339000,       14, 0x064900eb
+0,   21720000,   21720000,  1439000,     6212, 0x6d15f72f
+0,   23159000,   23159000,  1439000,       14, 0x0729010b
+0,   23160000,   23160000,  1319000,     7082, 0xe5c91052
+0,   24479000,   24479000,  1319000,       14, 0x0809012b
+0,   24480000,   24480000,   219000,     5345, 0x2e5eca40
+0,   24699000,   24699000,   219000,       14, 0x08e9014b
+0,   24700000,   24700000,   219000,     5765, 0x118060f2
+0,   24919000,   24919000,   219000,       14, 0x09c9016b
+0,   24920000,   24920000,   599000,     6557, 0x89275c90
+0,   25519000,   25519000,   599000,       14, 0x0aa9018b
+0,   25520000,   25520000,   219000,     7091, 0x996904b9
+0,   25739000,   25739000,   219000,       14, 0x0b8901ab
+0,   25740000,   25740000,   239000,     7305, 0xc23e44ae
+0,   25979000,   25979000,   239000,       14, 0x0c6901cb
+0,   25980000,   25980000,   359000,     7590, 0xc5a3a3e1
+0,   26339000,   26339000,   359000,       14, 0x064900eb
+0,   26340000,   26340000,   219000,     4629, 0x7ad6fe08
+0,   26559000,   26559000,   219000,       14, 0x0729010b
+0,   26560000,   26560000,   719000,     4785, 0xcd3f3f20
+0,   27279000,   27279000,   719000,       14, 0x0809012b
+0,   27280000,   27280000,   459000,     6061, 0x8b04d1a5
+0,   27739000,   27739000,   459000,       14, 0x08e9014b
+0,   27740000,   27740000,   239000,     6301, 0xe7de19b1
+0,   27979000,   27979000,   239000,       14, 0x09c9016b
+0,   27980000,   27980000,    99000,     6736, 0x38b3a094
+0,   28079000,   28079000,    99000,       14, 0x0aa9018b
+0,   28080000,   28080000,   219000,     7214, 0x0b783b73
+0,   28299000,   28299000,   219000,       14, 0x0b8901ab
+0,   28300000,   28300000,   239000,     7366, 0x98bf842d
+0,   28539000,   28539000,   239000,       14, 0x0c6901cb
+0,   28540000,   28540000,   599000,     4564, 0x3d9600ab
+0,   29139000,   29139000,   599000,       14, 0x064900eb
+0,   29140000,   29140000,   219000,     4637, 0x01f02617
+0,   29359000,   29359000,   219000,       14, 0x0729010b
+0,   29360000,   29360000,  1679000,     5358, 0x5b0f0ba8
+0,   31039000,   31039000,  1679000,       14, 0x0809012b
+0,   31040000,   31040000,   359000,       14, 0x0879013b
+0,   31399000,   31399000,   359000,       14, 0x08e9014b
+0,   31400000,   31400000,   479000,       14, 0x0959015b
+0,   31879000,   31879000,   479000,       14, 0x09c9016b
diff --git a/tests/ref/fate/sub2video b/tests/ref/fate/sub2video
index 80abe9c905..6f9f92b8e0 100644
--- a/tests/ref/fate/sub2video
+++ b/tests/ref/fate/sub2video
@@ -58,129 +58,1136 @@
 0,         47,         47,        1,   518400, 0xde69683f
 0,         48,         48,        1,   518400, 0x7df08fba
 0,         49,         49,        1,   518400, 0xbab197ea
+0,         50,         50,        1,   518400, 0xbab197ea
+0,         51,         51,        1,   518400, 0xbab197ea
+0,         52,         52,        1,   518400, 0xbab197ea
+0,         53,         53,        1,   518400, 0xbab197ea
+0,         54,         54,        1,   518400, 0xbab197ea
+0,         55,         55,        1,   518400, 0xbab197ea
+0,         56,         56,        1,   518400, 0xbab197ea
+0,         57,         57,        1,   518400, 0xbab197ea
+0,         58,         58,        1,   518400, 0xbab197ea
+0,         59,         59,        1,   518400, 0xbab197ea
+0,         60,         60,        1,   518400, 0xbab197ea
+0,         61,         61,        1,   518400, 0xbab197ea
+0,         62,         62,        1,   518400, 0xbab197ea
+0,         63,         63,        1,   518400, 0xbab197ea
+0,         64,         64,        1,   518400, 0xbab197ea
+0,         65,         65,        1,   518400, 0xbab197ea
+0,         66,         66,        1,   518400, 0xbab197ea
+0,         67,         67,        1,   518400, 0xbab197ea
+0,         68,         68,        1,   518400, 0xbab197ea
+0,         69,         69,        1,   518400, 0xbab197ea
+0,         70,         70,        1,   518400, 0xbab197ea
+0,         71,         71,        1,   518400, 0xbab197ea
+0,         72,         72,        1,   518400, 0xbab197ea
+0,         73,         73,        1,   518400, 0xbab197ea
+0,         74,         74,        1,   518400, 0xbab197ea
+0,         75,         75,        1,   518400, 0xbab197ea
+0,         76,         76,        1,   518400, 0xbab197ea
 1,   15355000,   15355000,  4733000,     2094, 0x3c171425
 0,         77,         77,        1,   518400, 0x902285d9
-0,        100,        100,        1,   518400, 0xbab197ea
+0,         78,         78,        1,   518400, 0x902285d9
+0,         79,         79,        1,   518400, 0x902285d9
+0,         80,         80,        1,   518400, 0x902285d9
+0,         81,         81,        1,   518400, 0x902285d9
+0,         82,         82,        1,   518400, 0x902285d9
+0,         83,         83,        1,   518400, 0x902285d9
+0,         84,         84,        1,   518400, 0x902285d9
+0,         85,         85,        1,   518400, 0x902285d9
+0,         86,         86,        1,   518400, 0x902285d9
+0,         87,         87,        1,   518400, 0x902285d9
+0,         88,         88,        1,   518400, 0x902285d9
+0,         89,         89,        1,   518400, 0x902285d9
+0,         90,         90,        1,   518400, 0x902285d9
+0,         91,         91,        1,   518400, 0x902285d9
+0,         92,         92,        1,   518400, 0x902285d9
+0,         93,         93,        1,   518400, 0x902285d9
+0,         94,         94,        1,   518400, 0x902285d9
+0,         95,         95,        1,   518400, 0x902285d9
+0,         96,         96,        1,   518400, 0x902285d9
+0,         97,         97,        1,   518400, 0x902285d9
+0,         98,         98,        1,   518400, 0x902285d9
+0,         99,         99,        1,   518400, 0x902285d9
+0,        100,        100,        1,   518400, 0x902285d9
+0,        101,        101,        1,   518400, 0xbab197ea
+0,        102,        102,        1,   518400, 0xbab197ea
+0,        103,        103,        1,   518400, 0xbab197ea
+0,        104,        104,        1,   518400, 0xbab197ea
+0,        105,        105,        1,   518400, 0xbab197ea
+0,        106,        106,        1,   518400, 0xbab197ea
+0,        107,        107,        1,   518400, 0xbab197ea
+0,        108,        108,        1,   518400, 0xbab197ea
+0,        109,        109,        1,   518400, 0xbab197ea
+0,        110,        110,        1,   518400, 0xbab197ea
+0,        111,        111,        1,   518400, 0xbab197ea
+0,        112,        112,        1,   518400, 0xbab197ea
+0,        113,        113,        1,   518400, 0xbab197ea
+0,        114,        114,        1,   518400, 0xbab197ea
+0,        115,        115,        1,   518400, 0xbab197ea
+0,        116,        116,        1,   518400, 0xbab197ea
+0,        117,        117,        1,   518400, 0xbab197ea
+0,        118,        118,        1,   518400, 0xbab197ea
+0,        119,        119,        1,   518400, 0xbab197ea
+0,        120,        120,        1,   518400, 0xbab197ea
+0,        121,        121,        1,   518400, 0xbab197ea
+0,        122,        122,        1,   518400, 0xbab197ea
+0,        123,        123,        1,   518400, 0xbab197ea
+0,        124,        124,        1,   518400, 0xbab197ea
+0,        125,        125,        1,   518400, 0xbab197ea
+0,        126,        126,        1,   518400, 0xbab197ea
+0,        127,        127,        1,   518400, 0xbab197ea
+0,        128,        128,        1,   518400, 0xbab197ea
+0,        129,        129,        1,   518400, 0xbab197ea
+0,        130,        130,        1,   518400, 0xbab197ea
+0,        131,        131,        1,   518400, 0xbab197ea
+0,        132,        132,        1,   518400, 0xbab197ea
+0,        133,        133,        1,   518400, 0xbab197ea
+0,        134,        134,        1,   518400, 0xbab197ea
+0,        135,        135,        1,   518400, 0xbab197ea
+0,        136,        136,        1,   518400, 0xbab197ea
+0,        137,        137,        1,   518400, 0xbab197ea
+0,        138,        138,        1,   518400, 0xbab197ea
+0,        139,        139,        1,   518400, 0xbab197ea
+0,        140,        140,        1,   518400, 0xbab197ea
+0,        141,        141,        1,   518400, 0xbab197ea
+0,        142,        142,        1,   518400, 0xbab197ea
+0,        143,        143,        1,   518400, 0xbab197ea
+0,        144,        144,        1,   518400, 0xbab197ea
+0,        145,        145,        1,   518400, 0xbab197ea
+0,        146,        146,        1,   518400, 0xbab197ea
+0,        147,        147,        1,   518400, 0xbab197ea
+0,        148,        148,        1,   518400, 0xbab197ea
+0,        149,        149,        1,   518400, 0xbab197ea
+0,        150,        150,        1,   518400, 0xbab197ea
+0,        151,        151,        1,   518400, 0xbab197ea
+0,        152,        152,        1,   518400, 0xbab197ea
+0,        153,        153,        1,   518400, 0xbab197ea
+0,        154,        154,        1,   518400, 0xbab197ea
+0,        155,        155,        1,   518400, 0xbab197ea
+0,        156,        156,        1,   518400, 0xbab197ea
+0,        157,        157,        1,   518400, 0xbab197ea
+0,        158,        158,        1,   518400, 0xbab197ea
+0,        159,        159,        1,   518400, 0xbab197ea
+0,        160,        160,        1,   518400, 0xbab197ea
+0,        161,        161,        1,   518400, 0xbab197ea
+0,        162,        162,        1,   518400, 0xbab197ea
+0,        163,        163,        1,   518400, 0xbab197ea
+0,        164,        164,        1,   518400, 0xbab197ea
+0,        165,        165,        1,   518400, 0xbab197ea
+0,        166,        166,        1,   518400, 0xbab197ea
+0,        167,        167,        1,   518400, 0xbab197ea
+0,        168,        168,        1,   518400, 0xbab197ea
+0,        169,        169,        1,   518400, 0xbab197ea
+0,        170,        170,        1,   518400, 0xbab197ea
+0,        171,        171,        1,   518400, 0xbab197ea
+0,        172,        172,        1,   518400, 0xbab197ea
+0,        173,        173,        1,   518400, 0xbab197ea
+0,        174,        174,        1,   518400, 0xbab197ea
+0,        175,        175,        1,   518400, 0xbab197ea
+0,        176,        176,        1,   518400, 0xbab197ea
+0,        177,        177,        1,   518400, 0xbab197ea
+0,        178,        178,        1,   518400, 0xbab197ea
+0,        179,        179,        1,   518400, 0xbab197ea
+0,        180,        180,        1,   518400, 0xbab197ea
+0,        181,        181,        1,   518400, 0xbab197ea
+0,        182,        182,        1,   518400, 0xbab197ea
+0,        183,        183,        1,   518400, 0xbab197ea
+0,        184,        184,        1,   518400, 0xbab197ea
+0,        185,        185,        1,   518400, 0xbab197ea
+0,        186,        186,        1,   518400, 0xbab197ea
+0,        187,        187,        1,   518400, 0xbab197ea
+0,        188,        188,        1,   518400, 0xbab197ea
+0,        189,        189,        1,   518400, 0xbab197ea
+0,        190,        190,        1,   518400, 0xbab197ea
+0,        191,        191,        1,   518400, 0xbab197ea
+0,        192,        192,        1,   518400, 0xbab197ea
+0,        193,        193,        1,   518400, 0xbab197ea
+0,        194,        194,        1,   518400, 0xbab197ea
+0,        195,        195,        1,   518400, 0xbab197ea
+0,        196,        196,        1,   518400, 0xbab197ea
+0,        197,        197,        1,   518400, 0xbab197ea
+0,        198,        198,        1,   518400, 0xbab197ea
+0,        199,        199,        1,   518400, 0xbab197ea
+0,        200,        200,        1,   518400, 0xbab197ea
+0,        201,        201,        1,   518400, 0xbab197ea
+0,        202,        202,        1,   518400, 0xbab197ea
+0,        203,        203,        1,   518400, 0xbab197ea
+0,        204,        204,        1,   518400, 0xbab197ea
+0,        205,        205,        1,   518400, 0xbab197ea
+0,        206,        206,        1,   518400, 0xbab197ea
+0,        207,        207,        1,   518400, 0xbab197ea
+0,        208,        208,        1,   518400, 0xbab197ea
+0,        209,        209,        1,   518400, 0xbab197ea
+0,        210,        210,        1,   518400, 0xbab197ea
+0,        211,        211,        1,   518400, 0xbab197ea
+0,        212,        212,        1,   518400, 0xbab197ea
+0,        213,        213,        1,   518400, 0xbab197ea
+0,        214,        214,        1,   518400, 0xbab197ea
+0,        215,        215,        1,   518400, 0xbab197ea
+0,        216,        216,        1,   518400, 0xbab197ea
+0,        217,        217,        1,   518400, 0xbab197ea
+0,        218,        218,        1,   518400, 0xbab197ea
+0,        219,        219,        1,   518400, 0xbab197ea
+0,        220,        220,        1,   518400, 0xbab197ea
+0,        221,        221,        1,   518400, 0xbab197ea
+0,        222,        222,        1,   518400, 0xbab197ea
+0,        223,        223,        1,   518400, 0xbab197ea
+0,        224,        224,        1,   518400, 0xbab197ea
+0,        225,        225,        1,   518400, 0xbab197ea
+0,        226,        226,        1,   518400, 0xbab197ea
+0,        227,        227,        1,   518400, 0xbab197ea
+0,        228,        228,        1,   518400, 0xbab197ea
+0,        229,        229,        1,   518400, 0xbab197ea
+0,        230,        230,        1,   518400, 0xbab197ea
+0,        231,        231,        1,   518400, 0xbab197ea
+0,        232,        232,        1,   518400, 0xbab197ea
+0,        233,        233,        1,   518400, 0xbab197ea
+0,        234,        234,        1,   518400, 0xbab197ea
+0,        235,        235,        1,   518400, 0xbab197ea
+0,        236,        236,        1,   518400, 0xbab197ea
+0,        237,        237,        1,   518400, 0xbab197ea
+0,        238,        238,        1,   518400, 0xbab197ea
+0,        239,        239,        1,   518400, 0xbab197ea
+0,        240,        240,        1,   518400, 0xbab197ea
+0,        241,        241,        1,   518400, 0xbab197ea
+0,        242,        242,        1,   518400, 0xbab197ea
+0,        243,        243,        1,   518400, 0xbab197ea
 1,   48797000,   48797000,  2560000,     2480, 0x7c0edf21
 0,        244,        244,        1,   518400, 0x7a11c812
-0,        257,        257,        1,   518400, 0xbab197ea
+0,        245,        245,        1,   518400, 0x7a11c812
+0,        246,        246,        1,   518400, 0x7a11c812
+0,        247,        247,        1,   518400, 0x7a11c812
+0,        248,        248,        1,   518400, 0x7a11c812
+0,        249,        249,        1,   518400, 0x7a11c812
+0,        250,        250,        1,   518400, 0x7a11c812
+0,        251,        251,        1,   518400, 0x7a11c812
+0,        252,        252,        1,   518400, 0x7a11c812
+0,        253,        253,        1,   518400, 0x7a11c812
+0,        254,        254,        1,   518400, 0x7a11c812
+0,        255,        255,        1,   518400, 0x7a11c812
+0,        256,        256,        1,   518400, 0x7a11c812
+0,        257,        257,        1,   518400, 0x7a11c812
 1,   51433000,   51433000,  2366000,     3059, 0xc95b8a05
 0,        258,        258,        1,   518400, 0x34cdddee
-0,        269,        269,        1,   518400, 0xbab197ea
+0,        259,        259,        1,   518400, 0x34cdddee
+0,        260,        260,        1,   518400, 0x34cdddee
+0,        261,        261,        1,   518400, 0x34cdddee
+0,        262,        262,        1,   518400, 0x34cdddee
+0,        263,        263,        1,   518400, 0x34cdddee
+0,        264,        264,        1,   518400, 0x34cdddee
+0,        265,        265,        1,   518400, 0x34cdddee
+0,        266,        266,        1,   518400, 0x34cdddee
+0,        267,        267,        1,   518400, 0x34cdddee
+0,        268,        268,        1,   518400, 0x34cdddee
+0,        269,        269,        1,   518400, 0x34cdddee
 1,   53910000,   53910000,  2696000,     2095, 0x61bb15ed
 0,        270,        270,        1,   518400, 0x4db4ce51
-0,        283,        283,        1,   518400, 0xbab197ea
+0,        271,        271,        1,   518400, 0x4db4ce51
+0,        272,        272,        1,   518400, 0x4db4ce51
+0,        273,        273,        1,   518400, 0x4db4ce51
+0,        274,        274,        1,   518400, 0x4db4ce51
+0,        275,        275,        1,   518400, 0x4db4ce51
+0,        276,        276,        1,   518400, 0x4db4ce51
+0,        277,        277,        1,   518400, 0x4db4ce51
+0,        278,        278,        1,   518400, 0x4db4ce51
+0,        279,        279,        1,   518400, 0x4db4ce51
+0,        280,        280,        1,   518400, 0x4db4ce51
+0,        281,        281,        1,   518400, 0x4db4ce51
+0,        282,        282,        1,   518400, 0x4db4ce51
+0,        283,        283,        1,   518400, 0x4db4ce51
 1,   56663000,   56663000,  1262000,     1013, 0xc9ae89b7
 0,        284,        284,        1,   518400, 0xe6bc0ea9
-0,        290,        290,        1,   518400, 0xbab197ea
+0,        285,        285,        1,   518400, 0xe6bc0ea9
+0,        286,        286,        1,   518400, 0xe6bc0ea9
+0,        287,        287,        1,   518400, 0xe6bc0ea9
+0,        288,        288,        1,   518400, 0xe6bc0ea9
+0,        289,        289,        1,   518400, 0xe6bc0ea9
+0,        290,        290,        1,   518400, 0xe6bc0ea9
 1,   58014000,   58014000,  1661000,      969, 0xe01878f0
 0,        291,        291,        1,   518400, 0xa8643af7
-0,        298,        298,        1,   518400, 0xbab197ea
+0,        292,        292,        1,   518400, 0xa8643af7
+0,        293,        293,        1,   518400, 0xa8643af7
+0,        294,        294,        1,   518400, 0xa8643af7
+0,        295,        295,        1,   518400, 0xa8643af7
+0,        296,        296,        1,   518400, 0xa8643af7
+0,        297,        297,        1,   518400, 0xa8643af7
+0,        298,        298,        1,   518400, 0xa8643af7
+0,        299,        299,        1,   518400, 0xbab197ea
+0,        300,        300,        1,   518400, 0xbab197ea
+0,        301,        301,        1,   518400, 0xbab197ea
+0,        302,        302,        1,   518400, 0xbab197ea
+0,        303,        303,        1,   518400, 0xbab197ea
+0,        304,        304,        1,   518400, 0xbab197ea
+0,        305,        305,        1,   518400, 0xbab197ea
+0,        306,        306,        1,   518400, 0xbab197ea
+0,        307,        307,        1,   518400, 0xbab197ea
+0,        308,        308,        1,   518400, 0xbab197ea
+0,        309,        309,        1,   518400, 0xbab197ea
+0,        310,        310,        1,   518400, 0xbab197ea
+0,        311,        311,        1,   518400, 0xbab197ea
+0,        312,        312,        1,   518400, 0xbab197ea
+0,        313,        313,        1,   518400, 0xbab197ea
+0,        314,        314,        1,   518400, 0xbab197ea
+0,        315,        315,        1,   518400, 0xbab197ea
+0,        316,        316,        1,   518400, 0xbab197ea
+0,        317,        317,        1,   518400, 0xbab197ea
+0,        318,        318,        1,   518400, 0xbab197ea
+0,        319,        319,        1,   518400, 0xbab197ea
+0,        320,        320,        1,   518400, 0xbab197ea
+0,        321,        321,        1,   518400, 0xbab197ea
+0,        322,        322,        1,   518400, 0xbab197ea
+0,        323,        323,        1,   518400, 0xbab197ea
+0,        324,        324,        1,   518400, 0xbab197ea
+0,        325,        325,        1,   518400, 0xbab197ea
+0,        326,        326,        1,   518400, 0xbab197ea
+0,        327,        327,        1,   518400, 0xbab197ea
+0,        328,        328,        1,   518400, 0xbab197ea
+0,        329,        329,        1,   518400, 0xbab197ea
+0,        330,        330,        1,   518400, 0xbab197ea
+0,        331,        331,        1,   518400, 0xbab197ea
+0,        332,        332,        1,   518400, 0xbab197ea
+0,        333,        333,        1,   518400, 0xbab197ea
+0,        334,        334,        1,   518400, 0xbab197ea
+0,        335,        335,        1,   518400, 0xbab197ea
+0,        336,        336,        1,   518400, 0xbab197ea
+0,        337,        337,        1,   518400, 0xbab197ea
+0,        338,        338,        1,   518400, 0xbab197ea
 1,   67724000,   67724000,  1365000,      844, 0xe7db4fc1
 0,        339,        339,        1,   518400, 0xb1885c67
-0,        345,        345,        1,   518400, 0xbab197ea
+0,        340,        340,        1,   518400, 0xb1885c67
+0,        341,        341,        1,   518400, 0xb1885c67
+0,        342,        342,        1,   518400, 0xb1885c67
+0,        343,        343,        1,   518400, 0xb1885c67
+0,        344,        344,        1,   518400, 0xb1885c67
+0,        345,        345,        1,   518400, 0xb1885c67
 1,   69175000,   69175000,  1558000,      802, 0xf48531ba
 0,        346,        346,        1,   518400, 0x378e3fd0
-0,        354,        354,        1,   518400, 0xbab197ea
+0,        347,        347,        1,   518400, 0x378e3fd0
+0,        348,        348,        1,   518400, 0x378e3fd0
+0,        349,        349,        1,   518400, 0x378e3fd0
+0,        350,        350,        1,   518400, 0x378e3fd0
+0,        351,        351,        1,   518400, 0x378e3fd0
+0,        352,        352,        1,   518400, 0x378e3fd0
+0,        353,        353,        1,   518400, 0x378e3fd0
+0,        354,        354,        1,   518400, 0x378e3fd0
 1,   70819000,   70819000,  1865000,     1709, 0xb4d5a1bd
 0,        355,        355,        1,   518400, 0xa3782469
-0,        363,        363,        1,   518400, 0xbab197ea
+0,        356,        356,        1,   518400, 0xa3782469
+0,        357,        357,        1,   518400, 0xa3782469
+0,        358,        358,        1,   518400, 0xa3782469
+0,        359,        359,        1,   518400, 0xa3782469
+0,        360,        360,        1,   518400, 0xa3782469
+0,        361,        361,        1,   518400, 0xa3782469
+0,        362,        362,        1,   518400, 0xa3782469
+0,        363,        363,        1,   518400, 0xa3782469
 1,   72762000,   72762000,  1968000,     2438, 0x99d7bc82
 0,        364,        364,        1,   518400, 0xba23a0d5
-0,        374,        374,        1,   518400, 0xbab197ea
+0,        365,        365,        1,   518400, 0xba23a0d5
+0,        366,        366,        1,   518400, 0xba23a0d5
+0,        367,        367,        1,   518400, 0xba23a0d5
+0,        368,        368,        1,   518400, 0xba23a0d5
+0,        369,        369,        1,   518400, 0xba23a0d5
+0,        370,        370,        1,   518400, 0xba23a0d5
+0,        371,        371,        1,   518400, 0xba23a0d5
+0,        372,        372,        1,   518400, 0xba23a0d5
+0,        373,        373,        1,   518400, 0xba23a0d5
+0,        374,        374,        1,   518400, 0xba23a0d5
 1,   74806000,   74806000,  1831000,     2116, 0x96514097
 0,        375,        375,        1,   518400, 0x129de2f8
-0,        383,        383,        1,   518400, 0xbab197ea
+0,        376,        376,        1,   518400, 0x129de2f8
+0,        377,        377,        1,   518400, 0x129de2f8
+0,        378,        378,        1,   518400, 0x129de2f8
+0,        379,        379,        1,   518400, 0x129de2f8
+0,        380,        380,        1,   518400, 0x129de2f8
+0,        381,        381,        1,   518400, 0x129de2f8
+0,        382,        382,        1,   518400, 0x129de2f8
+0,        383,        383,        1,   518400, 0x129de2f8
 1,   76716000,   76716000,  1262000,     1822, 0xefccc72e
 0,        384,        384,        1,   518400, 0x19772f0f
-0,        390,        390,        1,   518400, 0xbab197ea
+0,        385,        385,        1,   518400, 0x19772f0f
+0,        386,        386,        1,   518400, 0x19772f0f
+0,        387,        387,        1,   518400, 0x19772f0f
+0,        388,        388,        1,   518400, 0x19772f0f
+0,        389,        389,        1,   518400, 0x19772f0f
+0,        390,        390,        1,   518400, 0x19772f0f
 1,   78051000,   78051000,  1524000,      987, 0x7b927a27
 0,        391,        391,        1,   518400, 0x56f54e73
-0,        398,        398,        1,   518400, 0xbab197ea
+0,        392,        392,        1,   518400, 0x56f54e73
+0,        393,        393,        1,   518400, 0x56f54e73
+0,        394,        394,        1,   518400, 0x56f54e73
+0,        395,        395,        1,   518400, 0x56f54e73
+0,        396,        396,        1,   518400, 0x56f54e73
+0,        397,        397,        1,   518400, 0x56f54e73
+0,        398,        398,        1,   518400, 0x56f54e73
 1,   79644000,   79644000,  2662000,     2956, 0x190778f7
 0,        399,        399,        1,   518400, 0x300b5247
+0,        400,        400,        1,   518400, 0x300b5247
+0,        401,        401,        1,   518400, 0x300b5247
+0,        402,        402,        1,   518400, 0x300b5247
+0,        403,        403,        1,   518400, 0x300b5247
+0,        404,        404,        1,   518400, 0x300b5247
+0,        405,        405,        1,   518400, 0x300b5247
+0,        406,        406,        1,   518400, 0x300b5247
+0,        407,        407,        1,   518400, 0x300b5247
+0,        408,        408,        1,   518400, 0x300b5247
+0,        409,        409,        1,   518400, 0x300b5247
+0,        410,        410,        1,   518400, 0x300b5247
+0,        411,        411,        1,   518400, 0x300b5247
 1,   82380000,   82380000,  2764000,     3094, 0xc021b7d3
-0,        412,        412,        1,   518400, 0xbab197ea
+0,        412,        412,        1,   518400, 0x300b5247
 0,        413,        413,        1,   518400, 0x6fd028fa
-0,        426,        426,        1,   518400, 0xbab197ea
+0,        414,        414,        1,   518400, 0x6fd028fa
+0,        415,        415,        1,   518400, 0x6fd028fa
+0,        416,        416,        1,   518400, 0x6fd028fa
+0,        417,        417,        1,   518400, 0x6fd028fa
+0,        418,        418,        1,   518400, 0x6fd028fa
+0,        419,        419,        1,   518400, 0x6fd028fa
+0,        420,        420,        1,   518400, 0x6fd028fa
+0,        421,        421,        1,   518400, 0x6fd028fa
+0,        422,        422,        1,   518400, 0x6fd028fa
+0,        423,        423,        1,   518400, 0x6fd028fa
+0,        424,        424,        1,   518400, 0x6fd028fa
+0,        425,        425,        1,   518400, 0x6fd028fa
+0,        426,        426,        1,   518400, 0x6fd028fa
 1,   85225000,   85225000,  2366000,     2585, 0x74d0048f
 0,        427,        427,        1,   518400, 0x01f80e9d
-0,        438,        438,        1,   518400, 0xbab197ea
+0,        428,        428,        1,   518400, 0x01f80e9d
+0,        429,        429,        1,   518400, 0x01f80e9d
+0,        430,        430,        1,   518400, 0x01f80e9d
+0,        431,        431,        1,   518400, 0x01f80e9d
+0,        432,        432,        1,   518400, 0x01f80e9d
+0,        433,        433,        1,   518400, 0x01f80e9d
+0,        434,        434,        1,   518400, 0x01f80e9d
+0,        435,        435,        1,   518400, 0x01f80e9d
+0,        436,        436,        1,   518400, 0x01f80e9d
+0,        437,        437,        1,   518400, 0x01f80e9d
+0,        438,        438,        1,   518400, 0x01f80e9d
 1,   87652000,   87652000,  1831000,      634, 0x8832fda1
 0,        439,        439,        1,   518400, 0xb48d90c0
-0,        447,        447,        1,   518400, 0xbab197ea
+0,        440,        440,        1,   518400, 0xb48d90c0
+0,        441,        441,        1,   518400, 0xb48d90c0
+0,        442,        442,        1,   518400, 0xb48d90c0
+0,        443,        443,        1,   518400, 0xb48d90c0
+0,        444,        444,        1,   518400, 0xb48d90c0
+0,        445,        445,        1,   518400, 0xb48d90c0
+0,        446,        446,        1,   518400, 0xb48d90c0
+0,        447,        447,        1,   518400, 0xb48d90c0
+0,        448,        448,        1,   518400, 0xbab197ea
+0,        449,        449,        1,   518400, 0xbab197ea
+0,        450,        450,        1,   518400, 0xbab197ea
+0,        451,        451,        1,   518400, 0xbab197ea
+0,        452,        452,        1,   518400, 0xbab197ea
+0,        453,        453,        1,   518400, 0xbab197ea
+0,        454,        454,        1,   518400, 0xbab197ea
+0,        455,        455,        1,   518400, 0xbab197ea
+0,        456,        456,        1,   518400, 0xbab197ea
+0,        457,        457,        1,   518400, 0xbab197ea
 1,   91531000,   91531000,  2332000,     2080, 0x97a1146f
 0,        458,        458,        1,   518400, 0xcb5a0173
-0,        469,        469,        1,   518400, 0xbab197ea
+0,        459,        459,        1,   518400, 0xcb5a0173
+0,        460,        460,        1,   518400, 0xcb5a0173
+0,        461,        461,        1,   518400, 0xcb5a0173
+0,        462,        462,        1,   518400, 0xcb5a0173
+0,        463,        463,        1,   518400, 0xcb5a0173
+0,        464,        464,        1,   518400, 0xcb5a0173
+0,        465,        465,        1,   518400, 0xcb5a0173
+0,        466,        466,        1,   518400, 0xcb5a0173
+0,        467,        467,        1,   518400, 0xcb5a0173
+0,        468,        468,        1,   518400, 0xcb5a0173
+0,        469,        469,        1,   518400, 0xcb5a0173
+0,        470,        470,        1,   518400, 0xbab197ea
+0,        471,        471,        1,   518400, 0xbab197ea
+0,        472,        472,        1,   518400, 0xbab197ea
+0,        473,        473,        1,   518400, 0xbab197ea
+0,        474,        474,        1,   518400, 0xbab197ea
+0,        475,        475,        1,   518400, 0xbab197ea
+0,        476,        476,        1,   518400, 0xbab197ea
+0,        477,        477,        1,   518400, 0xbab197ea
 1,   95510000,   95510000,  3299000,     2964, 0x8b8f6684
 0,        478,        478,        1,   518400, 0xb8a323e4
-0,        494,        494,        1,   518400, 0xbab197ea
+0,        479,        479,        1,   518400, 0xb8a323e4
+0,        480,        480,        1,   518400, 0xb8a323e4
+0,        481,        481,        1,   518400, 0xb8a323e4
+0,        482,        482,        1,   518400, 0xb8a323e4
+0,        483,        483,        1,   518400, 0xb8a323e4
+0,        484,        484,        1,   518400, 0xb8a323e4
+0,        485,        485,        1,   518400, 0xb8a323e4
+0,        486,        486,        1,   518400, 0xb8a323e4
+0,        487,        487,        1,   518400, 0xb8a323e4
+0,        488,        488,        1,   518400, 0xb8a323e4
+0,        489,        489,        1,   518400, 0xb8a323e4
+0,        490,        490,        1,   518400, 0xb8a323e4
+0,        491,        491,        1,   518400, 0xb8a323e4
+0,        492,        492,        1,   518400, 0xb8a323e4
+0,        493,        493,        1,   518400, 0xb8a323e4
+0,        494,        494,        1,   518400, 0xb8a323e4
 1,   98872000,   98872000,  2161000,     1875, 0x9002ef71
 0,        495,        495,        1,   518400, 0xc43518ba
-0,        505,        505,        1,   518400, 0xbab197ea
+0,        496,        496,        1,   518400, 0xc43518ba
+0,        497,        497,        1,   518400, 0xc43518ba
+0,        498,        498,        1,   518400, 0xc43518ba
+0,        499,        499,        1,   518400, 0xc43518ba
+0,        500,        500,        1,   518400, 0xc43518ba
+0,        501,        501,        1,   518400, 0xc43518ba
+0,        502,        502,        1,   518400, 0xc43518ba
+0,        503,        503,        1,   518400, 0xc43518ba
+0,        504,        504,        1,   518400, 0xc43518ba
+0,        505,        505,        1,   518400, 0xc43518ba
 1,  101124000,  101124000,  4096000,     3872, 0x20c6ed9c
 0,        506,        506,        1,   518400, 0x04e38692
-0,        526,        526,        1,   518400, 0xbab197ea
+0,        507,        507,        1,   518400, 0x04e38692
+0,        508,        508,        1,   518400, 0x04e38692
+0,        509,        509,        1,   518400, 0x04e38692
+0,        510,        510,        1,   518400, 0x04e38692
+0,        511,        511,        1,   518400, 0x04e38692
+0,        512,        512,        1,   518400, 0x04e38692
+0,        513,        513,        1,   518400, 0x04e38692
+0,        514,        514,        1,   518400, 0x04e38692
+0,        515,        515,        1,   518400, 0x04e38692
+0,        516,        516,        1,   518400, 0x04e38692
+0,        517,        517,        1,   518400, 0x04e38692
+0,        518,        518,        1,   518400, 0x04e38692
+0,        519,        519,        1,   518400, 0x04e38692
+0,        520,        520,        1,   518400, 0x04e38692
+0,        521,        521,        1,   518400, 0x04e38692
+0,        522,        522,        1,   518400, 0x04e38692
+0,        523,        523,        1,   518400, 0x04e38692
+0,        524,        524,        1,   518400, 0x04e38692
+0,        525,        525,        1,   518400, 0x04e38692
+0,        526,        526,        1,   518400, 0x04e38692
 1,  105303000,  105303000,  2730000,     3094, 0xf203a663
 0,        527,        527,        1,   518400, 0x856b0ee5
-0,        540,        540,        1,   518400, 0xbab197ea
+0,        528,        528,        1,   518400, 0x856b0ee5
+0,        529,        529,        1,   518400, 0x856b0ee5
+0,        530,        530,        1,   518400, 0x856b0ee5
+0,        531,        531,        1,   518400, 0x856b0ee5
+0,        532,        532,        1,   518400, 0x856b0ee5
+0,        533,        533,        1,   518400, 0x856b0ee5
+0,        534,        534,        1,   518400, 0x856b0ee5
+0,        535,        535,        1,   518400, 0x856b0ee5
+0,        536,        536,        1,   518400, 0x856b0ee5
+0,        537,        537,        1,   518400, 0x856b0ee5
+0,        538,        538,        1,   518400, 0x856b0ee5
+0,        539,        539,        1,   518400, 0x856b0ee5
+0,        540,        540,        1,   518400, 0x856b0ee5
 1,  108106000,  108106000,  2059000,     2404, 0x41a7b429
 0,        541,        541,        1,   518400, 0x3e5beee2
-0,        551,        551,        1,   518400, 0xbab197ea
+0,        542,        542,        1,   518400, 0x3e5beee2
+0,        543,        543,        1,   518400, 0x3e5beee2
+0,        544,        544,        1,   518400, 0x3e5beee2
+0,        545,        545,        1,   518400, 0x3e5beee2
+0,        546,        546,        1,   518400, 0x3e5beee2
+0,        547,        547,        1,   518400, 0x3e5beee2
+0,        548,        548,        1,   518400, 0x3e5beee2
+0,        549,        549,        1,   518400, 0x3e5beee2
+0,        550,        550,        1,   518400, 0x3e5beee2
+0,        551,        551,        1,   518400, 0x3e5beee2
+0,        552,        552,        1,   518400, 0xbab197ea
+0,        553,        553,        1,   518400, 0xbab197ea
+0,        554,        554,        1,   518400, 0xbab197ea
+0,        555,        555,        1,   518400, 0xbab197ea
+0,        556,        556,        1,   518400, 0xbab197ea
+0,        557,        557,        1,   518400, 0xbab197ea
+0,        558,        558,        1,   518400, 0xbab197ea
+0,        559,        559,        1,   518400, 0xbab197ea
+0,        560,        560,        1,   518400, 0xbab197ea
+0,        561,        561,        1,   518400, 0xbab197ea
+0,        562,        562,        1,   518400, 0xbab197ea
+0,        563,        563,        1,   518400, 0xbab197ea
+0,        564,        564,        1,   518400, 0xbab197ea
+0,        565,        565,        1,   518400, 0xbab197ea
+0,        566,        566,        1,   518400, 0xbab197ea
+0,        567,        567,        1,   518400, 0xbab197ea
+0,        568,        568,        1,   518400, 0xbab197ea
+0,        569,        569,        1,   518400, 0xbab197ea
+0,        570,        570,        1,   518400, 0xbab197ea
+0,        571,        571,        1,   518400, 0xbab197ea
+0,        572,        572,        1,   518400, 0xbab197ea
+0,        573,        573,        1,   518400, 0xbab197ea
+0,        574,        574,        1,   518400, 0xbab197ea
+0,        575,        575,        1,   518400, 0xbab197ea
+0,        576,        576,        1,   518400, 0xbab197ea
+0,        577,        577,        1,   518400, 0xbab197ea
+0,        578,        578,        1,   518400, 0xbab197ea
+0,        579,        579,        1,   518400, 0xbab197ea
+0,        580,        580,        1,   518400, 0xbab197ea
+0,        581,        581,        1,   518400, 0xbab197ea
+0,        582,        582,        1,   518400, 0xbab197ea
+0,        583,        583,        1,   518400, 0xbab197ea
+0,        584,        584,        1,   518400, 0xbab197ea
+0,        585,        585,        1,   518400, 0xbab197ea
+0,        586,        586,        1,   518400, 0xbab197ea
+0,        587,        587,        1,   518400, 0xbab197ea
+0,        588,        588,        1,   518400, 0xbab197ea
+0,        589,        589,        1,   518400, 0xbab197ea
+0,        590,        590,        1,   518400, 0xbab197ea
+0,        591,        591,        1,   518400, 0xbab197ea
+0,        592,        592,        1,   518400, 0xbab197ea
+0,        593,        593,        1,   518400, 0xbab197ea
+0,        594,        594,        1,   518400, 0xbab197ea
+0,        595,        595,        1,   518400, 0xbab197ea
+0,        596,        596,        1,   518400, 0xbab197ea
+0,        597,        597,        1,   518400, 0xbab197ea
+0,        598,        598,        1,   518400, 0xbab197ea
+0,        599,        599,        1,   518400, 0xbab197ea
+0,        600,        600,        1,   518400, 0xbab197ea
+0,        601,        601,        1,   518400, 0xbab197ea
+0,        602,        602,        1,   518400, 0xbab197ea
+0,        603,        603,        1,   518400, 0xbab197ea
+0,        604,        604,        1,   518400, 0xbab197ea
+0,        605,        605,        1,   518400, 0xbab197ea
+0,        606,        606,        1,   518400, 0xbab197ea
+0,        607,        607,        1,   518400, 0xbab197ea
+0,        608,        608,        1,   518400, 0xbab197ea
+0,        609,        609,        1,   518400, 0xbab197ea
+0,        610,        610,        1,   518400, 0xbab197ea
+0,        611,        611,        1,   518400, 0xbab197ea
+0,        612,        612,        1,   518400, 0xbab197ea
+0,        613,        613,        1,   518400, 0xbab197ea
+0,        614,        614,        1,   518400, 0xbab197ea
+0,        615,        615,        1,   518400, 0xbab197ea
+0,        616,        616,        1,   518400, 0xbab197ea
+0,        617,        617,        1,   518400, 0xbab197ea
+0,        618,        618,        1,   518400, 0xbab197ea
+0,        619,        619,        1,   518400, 0xbab197ea
+0,        620,        620,        1,   518400, 0xbab197ea
+0,        621,        621,        1,   518400, 0xbab197ea
+0,        622,        622,        1,   518400, 0xbab197ea
+0,        623,        623,        1,   518400, 0xbab197ea
+0,        624,        624,        1,   518400, 0xbab197ea
+0,        625,        625,        1,   518400, 0xbab197ea
+0,        626,        626,        1,   518400, 0xbab197ea
+0,        627,        627,        1,   518400, 0xbab197ea
+0,        628,        628,        1,   518400, 0xbab197ea
+0,        629,        629,        1,   518400, 0xbab197ea
+0,        630,        630,        1,   518400, 0xbab197ea
+0,        631,        631,        1,   518400, 0xbab197ea
+0,        632,        632,        1,   518400, 0xbab197ea
+0,        633,        633,        1,   518400, 0xbab197ea
+0,        634,        634,        1,   518400, 0xbab197ea
+0,        635,        635,        1,   518400, 0xbab197ea
+0,        636,        636,        1,   518400, 0xbab197ea
+0,        637,        637,        1,   518400, 0xbab197ea
+0,        638,        638,        1,   518400, 0xbab197ea
+0,        639,        639,        1,   518400, 0xbab197ea
+0,        640,        640,        1,   518400, 0xbab197ea
+0,        641,        641,        1,   518400, 0xbab197ea
+0,        642,        642,        1,   518400, 0xbab197ea
+0,        643,        643,        1,   518400, 0xbab197ea
+0,        644,        644,        1,   518400, 0xbab197ea
+0,        645,        645,        1,   518400, 0xbab197ea
+0,        646,        646,        1,   518400, 0xbab197ea
+0,        647,        647,        1,   518400, 0xbab197ea
+0,        648,        648,        1,   518400, 0xbab197ea
+0,        649,        649,        1,   518400, 0xbab197ea
+0,        650,        650,        1,   518400, 0xbab197ea
+0,        651,        651,        1,   518400, 0xbab197ea
+0,        652,        652,        1,   518400, 0xbab197ea
+0,        653,        653,        1,   518400, 0xbab197ea
+0,        654,        654,        1,   518400, 0xbab197ea
+0,        655,        655,        1,   518400, 0xbab197ea
+0,        656,        656,        1,   518400, 0xbab197ea
+0,        657,        657,        1,   518400, 0xbab197ea
+0,        658,        658,        1,   518400, 0xbab197ea
+0,        659,        659,        1,   518400, 0xbab197ea
+0,        660,        660,        1,   518400, 0xbab197ea
+0,        661,        661,        1,   518400, 0xbab197ea
+0,        662,        662,        1,   518400, 0xbab197ea
+0,        663,        663,        1,   518400, 0xbab197ea
+0,        664,        664,        1,   518400, 0xbab197ea
+0,        665,        665,        1,   518400, 0xbab197ea
+0,        666,        666,        1,   518400, 0xbab197ea
+0,        667,        667,        1,   518400, 0xbab197ea
+0,        668,        668,        1,   518400, 0xbab197ea
+0,        669,        669,        1,   518400, 0xbab197ea
+0,        670,        670,        1,   518400, 0xbab197ea
+0,        671,        671,        1,   518400, 0xbab197ea
+0,        672,        672,        1,   518400, 0xbab197ea
+0,        673,        673,        1,   518400, 0xbab197ea
+0,        674,        674,        1,   518400, 0xbab197ea
+0,        675,        675,        1,   518400, 0xbab197ea
+0,        676,        676,        1,   518400, 0xbab197ea
+0,        677,        677,        1,   518400, 0xbab197ea
+0,        678,        678,        1,   518400, 0xbab197ea
+0,        679,        679,        1,   518400, 0xbab197ea
+0,        680,        680,        1,   518400, 0xbab197ea
+0,        681,        681,        1,   518400, 0xbab197ea
+0,        682,        682,        1,   518400, 0xbab197ea
+0,        683,        683,        1,   518400, 0xbab197ea
+0,        684,        684,        1,   518400, 0xbab197ea
+0,        685,        685,        1,   518400, 0xbab197ea
+0,        686,        686,        1,   518400, 0xbab197ea
+0,        687,        687,        1,   518400, 0xbab197ea
+0,        688,        688,        1,   518400, 0xbab197ea
+0,        689,        689,        1,   518400, 0xbab197ea
+0,        690,        690,        1,   518400, 0xbab197ea
+0,        691,        691,        1,   518400, 0xbab197ea
+0,        692,        692,        1,   518400, 0xbab197ea
+0,        693,        693,        1,   518400, 0xbab197ea
+0,        694,        694,        1,   518400, 0xbab197ea
+0,        695,        695,        1,   518400, 0xbab197ea
+0,        696,        696,        1,   518400, 0xbab197ea
+0,        697,        697,        1,   518400, 0xbab197ea
+0,        698,        698,        1,   518400, 0xbab197ea
+0,        699,        699,        1,   518400, 0xbab197ea
+0,        700,        700,        1,   518400, 0xbab197ea
+0,        701,        701,        1,   518400, 0xbab197ea
+0,        702,        702,        1,   518400, 0xbab197ea
+0,        703,        703,        1,   518400, 0xbab197ea
+0,        704,        704,        1,   518400, 0xbab197ea
+0,        705,        705,        1,   518400, 0xbab197ea
+0,        706,        706,        1,   518400, 0xbab197ea
+0,        707,        707,        1,   518400, 0xbab197ea
 1,  141556000,  141556000,  1661000,     1088, 0xde20aa20
 0,        708,        708,        1,   518400, 0xb8bc1365
-0,        716,        716,        1,   518400, 0xbab197ea
+0,        709,        709,        1,   518400, 0xb8bc1365
+0,        710,        710,        1,   518400, 0xb8bc1365
+0,        711,        711,        1,   518400, 0xb8bc1365
+0,        712,        712,        1,   518400, 0xb8bc1365
+0,        713,        713,        1,   518400, 0xb8bc1365
+0,        714,        714,        1,   518400, 0xb8bc1365
+0,        715,        715,        1,   518400, 0xb8bc1365
+0,        716,        716,        1,   518400, 0xb8bc1365
+0,        717,        717,        1,   518400, 0xbab197ea
+0,        718,        718,        1,   518400, 0xbab197ea
+0,        719,        719,        1,   518400, 0xbab197ea
+0,        720,        720,        1,   518400, 0xbab197ea
+0,        721,        721,        1,   518400, 0xbab197ea
+0,        722,        722,        1,   518400, 0xbab197ea
+0,        723,        723,        1,   518400, 0xbab197ea
+0,        724,        724,        1,   518400, 0xbab197ea
+0,        725,        725,        1,   518400, 0xbab197ea
+0,        726,        726,        1,   518400, 0xbab197ea
+0,        727,        727,        1,   518400, 0xbab197ea
+0,        728,        728,        1,   518400, 0xbab197ea
+0,        729,        729,        1,   518400, 0xbab197ea
+0,        730,        730,        1,   518400, 0xbab197ea
+0,        731,        731,        1,   518400, 0xbab197ea
+0,        732,        732,        1,   518400, 0xbab197ea
+0,        733,        733,        1,   518400, 0xbab197ea
+0,        734,        734,        1,   518400, 0xbab197ea
+0,        735,        735,        1,   518400, 0xbab197ea
+0,        736,        736,        1,   518400, 0xbab197ea
+0,        737,        737,        1,   518400, 0xbab197ea
+0,        738,        738,        1,   518400, 0xbab197ea
+0,        739,        739,        1,   518400, 0xbab197ea
+0,        740,        740,        1,   518400, 0xbab197ea
+0,        741,        741,        1,   518400, 0xbab197ea
+0,        742,        742,        1,   518400, 0xbab197ea
+0,        743,        743,        1,   518400, 0xbab197ea
+0,        744,        744,        1,   518400, 0xbab197ea
+0,        745,        745,        1,   518400, 0xbab197ea
+0,        746,        746,        1,   518400, 0xbab197ea
+0,        747,        747,        1,   518400, 0xbab197ea
+0,        748,        748,        1,   518400, 0xbab197ea
+0,        749,        749,        1,   518400, 0xbab197ea
+0,        750,        750,        1,   518400, 0xbab197ea
+0,        751,        751,        1,   518400, 0xbab197ea
+0,        752,        752,        1,   518400, 0xbab197ea
+0,        753,        753,        1,   518400, 0xbab197ea
+0,        754,        754,        1,   518400, 0xbab197ea
+0,        755,        755,        1,   518400, 0xbab197ea
+0,        756,        756,        1,   518400, 0xbab197ea
+0,        757,        757,        1,   518400, 0xbab197ea
+0,        758,        758,        1,   518400, 0xbab197ea
+0,        759,        759,        1,   518400, 0xbab197ea
+0,        760,        760,        1,   518400, 0xbab197ea
+0,        761,        761,        1,   518400, 0xbab197ea
+0,        762,        762,        1,   518400, 0xbab197ea
+0,        763,        763,        1,   518400, 0xbab197ea
+0,        764,        764,        1,   518400, 0xbab197ea
+0,        765,        765,        1,   518400, 0xbab197ea
+0,        766,        766,        1,   518400, 0xbab197ea
+0,        767,        767,        1,   518400, 0xbab197ea
+0,        768,        768,        1,   518400, 0xbab197ea
+0,        769,        769,        1,   518400, 0xbab197ea
+0,        770,        770,        1,   518400, 0xbab197ea
+0,        771,        771,        1,   518400, 0xbab197ea
+0,        772,        772,        1,   518400, 0xbab197ea
+0,        773,        773,        1,   518400, 0xbab197ea
+0,        774,        774,        1,   518400, 0xbab197ea
+0,        775,        775,        1,   518400, 0xbab197ea
+0,        776,        776,        1,   518400, 0xbab197ea
+0,        777,        777,        1,   518400, 0xbab197ea
+0,        778,        778,        1,   518400, 0xbab197ea
+0,        779,        779,        1,   518400, 0xbab197ea
+0,        780,        780,        1,   518400, 0xbab197ea
+0,        781,        781,        1,   518400, 0xbab197ea
+0,        782,        782,        1,   518400, 0xbab197ea
+0,        783,        783,        1,   518400, 0xbab197ea
+0,        784,        784,        1,   518400, 0xbab197ea
+0,        785,        785,        1,   518400, 0xbab197ea
+0,        786,        786,        1,   518400, 0xbab197ea
+0,        787,        787,        1,   518400, 0xbab197ea
+0,        788,        788,        1,   518400, 0xbab197ea
+0,        789,        789,        1,   518400, 0xbab197ea
+0,        790,        790,        1,   518400, 0xbab197ea
+0,        791,        791,        1,   518400, 0xbab197ea
+0,        792,        792,        1,   518400, 0xbab197ea
+0,        793,        793,        1,   518400, 0xbab197ea
+0,        794,        794,        1,   518400, 0xbab197ea
+0,        795,        795,        1,   518400, 0xbab197ea
+0,        796,        796,        1,   518400, 0xbab197ea
+0,        797,        797,        1,   518400, 0xbab197ea
+0,        798,        798,        1,   518400, 0xbab197ea
+0,        799,        799,        1,   518400, 0xbab197ea
+0,        800,        800,        1,   518400, 0xbab197ea
+0,        801,        801,        1,   518400, 0xbab197ea
+0,        802,        802,        1,   518400, 0xbab197ea
+0,        803,        803,        1,   518400, 0xbab197ea
+0,        804,        804,        1,   518400, 0xbab197ea
+0,        805,        805,        1,   518400, 0xbab197ea
+0,        806,        806,        1,   518400, 0xbab197ea
+0,        807,        807,        1,   518400, 0xbab197ea
+0,        808,        808,        1,   518400, 0xbab197ea
+0,        809,        809,        1,   518400, 0xbab197ea
+0,        810,        810,        1,   518400, 0xbab197ea
+0,        811,        811,        1,   518400, 0xbab197ea
+0,        812,        812,        1,   518400, 0xbab197ea
+0,        813,        813,        1,   518400, 0xbab197ea
+0,        814,        814,        1,   518400, 0xbab197ea
+0,        815,        815,        1,   518400, 0xbab197ea
+0,        816,        816,        1,   518400, 0xbab197ea
 0,        817,        817,        1,   518400, 0x83efa32d
 1,  163445000,  163445000,  1331000,      339, 0x8bd186ef
-0,        824,        824,        1,   518400, 0xbab197ea
+0,        818,        818,        1,   518400, 0x83efa32d
+0,        819,        819,        1,   518400, 0x83efa32d
+0,        820,        820,        1,   518400, 0x83efa32d
+0,        821,        821,        1,   518400, 0x83efa32d
+0,        822,        822,        1,   518400, 0x83efa32d
+0,        823,        823,        1,   518400, 0x83efa32d
+0,        824,        824,        1,   518400, 0x83efa32d
+0,        825,        825,        1,   518400, 0xbab197ea
+0,        826,        826,        1,   518400, 0xbab197ea
+0,        827,        827,        1,   518400, 0xbab197ea
+0,        828,        828,        1,   518400, 0xbab197ea
+0,        829,        829,        1,   518400, 0xbab197ea
+0,        830,        830,        1,   518400, 0xbab197ea
+0,        831,        831,        1,   518400, 0xbab197ea
+0,        832,        832,        1,   518400, 0xbab197ea
+0,        833,        833,        1,   518400, 0xbab197ea
+0,        834,        834,        1,   518400, 0xbab197ea
+0,        835,        835,        1,   518400, 0xbab197ea
+0,        836,        836,        1,   518400, 0xbab197ea
+0,        837,        837,        1,   518400, 0xbab197ea
+0,        838,        838,        1,   518400, 0xbab197ea
+0,        839,        839,        1,   518400, 0xbab197ea
 0,        840,        840,        1,   518400, 0x03ea0e90
 1,  168049000,  168049000,  1900000,     1312, 0x0bf20e8d
-0,        850,        850,        1,   518400, 0xbab197ea
+0,        841,        841,        1,   518400, 0x03ea0e90
+0,        842,        842,        1,   518400, 0x03ea0e90
+0,        843,        843,        1,   518400, 0x03ea0e90
+0,        844,        844,        1,   518400, 0x03ea0e90
+0,        845,        845,        1,   518400, 0x03ea0e90
+0,        846,        846,        1,   518400, 0x03ea0e90
+0,        847,        847,        1,   518400, 0x03ea0e90
+0,        848,        848,        1,   518400, 0x03ea0e90
+0,        849,        849,        1,   518400, 0x03ea0e90
+0,        850,        850,        1,   518400, 0x03ea0e90
 1,  170035000,  170035000,  1524000,     1279, 0xb6c2dafe
 0,        851,        851,        1,   518400, 0x8780239e
-0,        858,        858,        1,   518400, 0xbab197ea
+0,        852,        852,        1,   518400, 0x8780239e
+0,        853,        853,        1,   518400, 0x8780239e
+0,        854,        854,        1,   518400, 0x8780239e
+0,        855,        855,        1,   518400, 0x8780239e
+0,        856,        856,        1,   518400, 0x8780239e
+0,        857,        857,        1,   518400, 0x8780239e
+0,        858,        858,        1,   518400, 0x8780239e
+0,        859,        859,        1,   518400, 0xbab197ea
+0,        860,        860,        1,   518400, 0xbab197ea
 0,        861,        861,        1,   518400, 0x6eb72347
 1,  172203000,  172203000,  1695000,     1826, 0x9a1ac769
-0,        869,        869,        1,   518400, 0xbab197ea
+0,        862,        862,        1,   518400, 0x6eb72347
+0,        863,        863,        1,   518400, 0x6eb72347
+0,        864,        864,        1,   518400, 0x6eb72347
+0,        865,        865,        1,   518400, 0x6eb72347
+0,        866,        866,        1,   518400, 0x6eb72347
+0,        867,        867,        1,   518400, 0x6eb72347
+0,        868,        868,        1,   518400, 0x6eb72347
+0,        869,        869,        1,   518400, 0x6eb72347
 1,  173947000,  173947000,  1934000,     1474, 0xa9b03cdc
 0,        870,        870,        1,   518400, 0x9c4a3a3d
-0,        879,        879,        1,   518400, 0xbab197ea
+0,        871,        871,        1,   518400, 0x9c4a3a3d
+0,        872,        872,        1,   518400, 0x9c4a3a3d
+0,        873,        873,        1,   518400, 0x9c4a3a3d
+0,        874,        874,        1,   518400, 0x9c4a3a3d
+0,        875,        875,        1,   518400, 0x9c4a3a3d
+0,        876,        876,        1,   518400, 0x9c4a3a3d
+0,        877,        877,        1,   518400, 0x9c4a3a3d
+0,        878,        878,        1,   518400, 0x9c4a3a3d
+0,        879,        879,        1,   518400, 0x9c4a3a3d
 1,  175957000,  175957000,  1763000,     1019, 0x20409355
 0,        880,        880,        1,   518400, 0xc9ebfa89
-0,        889,        889,        1,   518400, 0xbab197ea
+0,        881,        881,        1,   518400, 0xc9ebfa89
+0,        882,        882,        1,   518400, 0xc9ebfa89
+0,        883,        883,        1,   518400, 0xc9ebfa89
+0,        884,        884,        1,   518400, 0xc9ebfa89
+0,        885,        885,        1,   518400, 0xc9ebfa89
+0,        886,        886,        1,   518400, 0xc9ebfa89
+0,        887,        887,        1,   518400, 0xc9ebfa89
+0,        888,        888,        1,   518400, 0xc9ebfa89
+0,        889,        889,        1,   518400, 0xc9ebfa89
+0,        890,        890,        1,   518400, 0xbab197ea
+0,        891,        891,        1,   518400, 0xbab197ea
+0,        892,        892,        1,   518400, 0xbab197ea
+0,        893,        893,        1,   518400, 0xbab197ea
+0,        894,        894,        1,   518400, 0xbab197ea
+0,        895,        895,        1,   518400, 0xbab197ea
+0,        896,        896,        1,   518400, 0xbab197ea
+0,        897,        897,        1,   518400, 0xbab197ea
+0,        898,        898,        1,   518400, 0xbab197ea
+0,        899,        899,        1,   518400, 0xbab197ea
+0,        900,        900,        1,   518400, 0xbab197ea
+0,        901,        901,        1,   518400, 0xbab197ea
+0,        902,        902,        1,   518400, 0xbab197ea
+0,        903,        903,        1,   518400, 0xbab197ea
+0,        904,        904,        1,   518400, 0xbab197ea
+0,        905,        905,        1,   518400, 0xbab197ea
+0,        906,        906,        1,   518400, 0xbab197ea
+0,        907,        907,        1,   518400, 0xbab197ea
+0,        908,        908,        1,   518400, 0xbab197ea
+0,        909,        909,        1,   518400, 0xbab197ea
+0,        910,        910,        1,   518400, 0xbab197ea
+0,        911,        911,        1,   518400, 0xbab197ea
+0,        912,        912,        1,   518400, 0xbab197ea
+0,        913,        913,        1,   518400, 0xbab197ea
+0,        914,        914,        1,   518400, 0xbab197ea
+0,        915,        915,        1,   518400, 0xbab197ea
+0,        916,        916,        1,   518400, 0xbab197ea
+0,        917,        917,        1,   518400, 0xbab197ea
+0,        918,        918,        1,   518400, 0xbab197ea
+0,        919,        919,        1,   518400, 0xbab197ea
+0,        920,        920,        1,   518400, 0xbab197ea
+0,        921,        921,        1,   518400, 0xbab197ea
+0,        922,        922,        1,   518400, 0xbab197ea
+0,        923,        923,        1,   518400, 0xbab197ea
+0,        924,        924,        1,   518400, 0xbab197ea
+0,        925,        925,        1,   518400, 0xbab197ea
+0,        926,        926,        1,   518400, 0xbab197ea
+0,        927,        927,        1,   518400, 0xbab197ea
+0,        928,        928,        1,   518400, 0xbab197ea
+0,        929,        929,        1,   518400, 0xbab197ea
+0,        930,        930,        1,   518400, 0xbab197ea
+0,        931,        931,        1,   518400, 0xbab197ea
+0,        932,        932,        1,   518400, 0xbab197ea
+0,        933,        933,        1,   518400, 0xbab197ea
+0,        934,        934,        1,   518400, 0xbab197ea
+0,        935,        935,        1,   518400, 0xbab197ea
+0,        936,        936,        1,   518400, 0xbab197ea
+0,        937,        937,        1,   518400, 0xbab197ea
+0,        938,        938,        1,   518400, 0xbab197ea
+0,        939,        939,        1,   518400, 0xbab197ea
+0,        940,        940,        1,   518400, 0xbab197ea
+0,        941,        941,        1,   518400, 0xbab197ea
+0,        942,        942,        1,   518400, 0xbab197ea
+0,        943,        943,        1,   518400, 0xbab197ea
+0,        944,        944,        1,   518400, 0xbab197ea
+0,        945,        945,        1,   518400, 0xbab197ea
 0,        946,        946,        1,   518400, 0xbaf801ef
 1,  189295000,  189295000,  1968000,     1596, 0x408c726e
-0,        956,        956,        1,   518400, 0xbab197ea
+0,        947,        947,        1,   518400, 0xbaf801ef
+0,        948,        948,        1,   518400, 0xbaf801ef
+0,        949,        949,        1,   518400, 0xbaf801ef
+0,        950,        950,        1,   518400, 0xbaf801ef
+0,        951,        951,        1,   518400, 0xbaf801ef
+0,        952,        952,        1,   518400, 0xbaf801ef
+0,        953,        953,        1,   518400, 0xbaf801ef
+0,        954,        954,        1,   518400, 0xbaf801ef
+0,        955,        955,        1,   518400, 0xbaf801ef
+0,        956,        956,        1,   518400, 0xbaf801ef
 1,  191356000,  191356000,  1228000,     1517, 0xae8c5c2b
 0,        957,        957,        1,   518400, 0x59f4e72f
-0,        963,        963,        1,   518400, 0xbab197ea
+0,        958,        958,        1,   518400, 0x59f4e72f
+0,        959,        959,        1,   518400, 0x59f4e72f
+0,        960,        960,        1,   518400, 0x59f4e72f
+0,        961,        961,        1,   518400, 0x59f4e72f
+0,        962,        962,        1,   518400, 0x59f4e72f
+0,        963,        963,        1,   518400, 0x59f4e72f
 1,  192640000,  192640000,  1763000,     2506, 0xa458d6d4
 0,        964,        964,        1,   518400, 0x9d5b9d69
-0,        972,        972,        1,   518400, 0xbab197ea
+0,        965,        965,        1,   518400, 0x9d5b9d69
+0,        966,        966,        1,   518400, 0x9d5b9d69
+0,        967,        967,        1,   518400, 0x9d5b9d69
+0,        968,        968,        1,   518400, 0x9d5b9d69
+0,        969,        969,        1,   518400, 0x9d5b9d69
+0,        970,        970,        1,   518400, 0x9d5b9d69
+0,        971,        971,        1,   518400, 0x9d5b9d69
+0,        972,        972,        1,   518400, 0x9d5b9d69
+0,        973,        973,        1,   518400, 0xbab197ea
+0,        974,        974,        1,   518400, 0xbab197ea
+0,        975,        975,        1,   518400, 0xbab197ea
 1,  195193000,  195193000,  1092000,     1074, 0x397ba9a8
 0,        976,        976,        1,   518400, 0x923d1ce7
-0,        981,        981,        1,   518400, 0xbab197ea
+0,        977,        977,        1,   518400, 0x923d1ce7
+0,        978,        978,        1,   518400, 0x923d1ce7
+0,        979,        979,        1,   518400, 0x923d1ce7
+0,        980,        980,        1,   518400, 0x923d1ce7
+0,        981,        981,        1,   518400, 0x923d1ce7
 1,  196361000,  196361000,  1524000,     1715, 0x695ca41e
 0,        982,        982,        1,   518400, 0x6e652cd2
-0,        989,        989,        1,   518400, 0xbab197ea
+0,        983,        983,        1,   518400, 0x6e652cd2
+0,        984,        984,        1,   518400, 0x6e652cd2
+0,        985,        985,        1,   518400, 0x6e652cd2
+0,        986,        986,        1,   518400, 0x6e652cd2
+0,        987,        987,        1,   518400, 0x6e652cd2
+0,        988,        988,        1,   518400, 0x6e652cd2
+0,        989,        989,        1,   518400, 0x6e652cd2
 1,  197946000,  197946000,  1160000,      789, 0xc63a189e
 0,        990,        990,        1,   518400, 0x25113966
-0,        996,        996,        1,   518400, 0xbab197ea
+0,        991,        991,        1,   518400, 0x25113966
+0,        992,        992,        1,   518400, 0x25113966
+0,        993,        993,        1,   518400, 0x25113966
+0,        994,        994,        1,   518400, 0x25113966
+0,        995,        995,        1,   518400, 0x25113966
+0,        996,        996,        1,   518400, 0x25113966
 1,  199230000,  199230000,  1627000,     1846, 0xeea8c599
 0,        997,        997,        1,   518400, 0x2dc83609
-0,       1004,       1004,        1,   518400, 0xbab197ea
+0,        998,        998,        1,   518400, 0x2dc83609
+0,        999,        999,        1,   518400, 0x2dc83609
+0,       1000,       1000,        1,   518400, 0x2dc83609
+0,       1001,       1001,        1,   518400, 0x2dc83609
+0,       1002,       1002,        1,   518400, 0x2dc83609
+0,       1003,       1003,        1,   518400, 0x2dc83609
+0,       1004,       1004,        1,   518400, 0x2dc83609
 1,  200924000,  200924000,  1763000,      922, 0xd4a87222
 0,       1005,       1005,        1,   518400, 0x90483bc6
-0,       1013,       1013,        1,   518400, 0xbab197ea
+0,       1006,       1006,        1,   518400, 0x90483bc6
+0,       1007,       1007,        1,   518400, 0x90483bc6
+0,       1008,       1008,        1,   518400, 0x90483bc6
+0,       1009,       1009,        1,   518400, 0x90483bc6
+0,       1010,       1010,        1,   518400, 0x90483bc6
+0,       1011,       1011,        1,   518400, 0x90483bc6
+0,       1012,       1012,        1,   518400, 0x90483bc6
+0,       1013,       1013,        1,   518400, 0x90483bc6
+0,       1014,       1014,        1,   518400, 0xbab197ea
+0,       1015,       1015,        1,   518400, 0xbab197ea
+0,       1016,       1016,        1,   518400, 0xbab197ea
+0,       1017,       1017,        1,   518400, 0xbab197ea
+0,       1018,       1018,        1,   518400, 0xbab197ea
+0,       1019,       1019,        1,   518400, 0xbab197ea
+0,       1020,       1020,        1,   518400, 0xbab197ea
+0,       1021,       1021,        1,   518400, 0xbab197ea
+0,       1022,       1022,        1,   518400, 0xbab197ea
+0,       1023,       1023,        1,   518400, 0xbab197ea
+0,       1024,       1024,        1,   518400, 0xbab197ea
+0,       1025,       1025,        1,   518400, 0xbab197ea
+0,       1026,       1026,        1,   518400, 0xbab197ea
+0,       1027,       1027,        1,   518400, 0xbab197ea
+0,       1028,       1028,        1,   518400, 0xbab197ea
+0,       1029,       1029,        1,   518400, 0xbab197ea
+0,       1030,       1030,        1,   518400, 0xbab197ea
+0,       1031,       1031,        1,   518400, 0xbab197ea
+0,       1032,       1032,        1,   518400, 0xbab197ea
+0,       1033,       1033,        1,   518400, 0xbab197ea
+0,       1034,       1034,        1,   518400, 0xbab197ea
+0,       1035,       1035,        1,   518400, 0xbab197ea
+0,       1036,       1036,        1,   518400, 0xbab197ea
+0,       1037,       1037,        1,   518400, 0xbab197ea
+0,       1038,       1038,        1,   518400, 0xbab197ea
+0,       1039,       1039,        1,   518400, 0xbab197ea
+0,       1040,       1040,        1,   518400, 0xbab197ea
+0,       1041,       1041,        1,   518400, 0xbab197ea
+0,       1042,       1042,        1,   518400, 0xbab197ea
+0,       1043,       1043,        1,   518400, 0xbab197ea
+0,       1044,       1044,        1,   518400, 0xbab197ea
+0,       1045,       1045,        1,   518400, 0xbab197ea
+0,       1046,       1046,        1,   518400, 0xbab197ea
+0,       1047,       1047,        1,   518400, 0xbab197ea
+0,       1048,       1048,        1,   518400, 0xbab197ea
+0,       1049,       1049,        1,   518400, 0xbab197ea
+0,       1050,       1050,        1,   518400, 0xbab197ea
+0,       1051,       1051,        1,   518400, 0xbab197ea
+0,       1052,       1052,        1,   518400, 0xbab197ea
 0,       1053,       1053,        1,   518400, 0x3de86ab7
 1,  210600000,  210600000,  1831000,      665, 0x55580135
-0,       1062,       1062,        1,   518400, 0xbab197ea
+0,       1054,       1054,        1,   518400, 0x3de86ab7
+0,       1055,       1055,        1,   518400, 0x3de86ab7
+0,       1056,       1056,        1,   518400, 0x3de86ab7
+0,       1057,       1057,        1,   518400, 0x3de86ab7
+0,       1058,       1058,        1,   518400, 0x3de86ab7
+0,       1059,       1059,        1,   518400, 0x3de86ab7
+0,       1060,       1060,        1,   518400, 0x3de86ab7
+0,       1061,       1061,        1,   518400, 0x3de86ab7
+0,       1062,       1062,        1,   518400, 0x3de86ab7
+0,       1063,       1063,        1,   518400, 0xbab197ea
+0,       1064,       1064,        1,   518400, 0xbab197ea
+0,       1065,       1065,        1,   518400, 0xbab197ea
+0,       1066,       1066,        1,   518400, 0xbab197ea
+0,       1067,       1067,        1,   518400, 0xbab197ea
+0,       1068,       1068,        1,   518400, 0xbab197ea
+0,       1069,       1069,        1,   518400, 0xbab197ea
+0,       1070,       1070,        1,   518400, 0xbab197ea
+0,       1071,       1071,        1,   518400, 0xbab197ea
+0,       1072,       1072,        1,   518400, 0xbab197ea
+0,       1073,       1073,        1,   518400, 0xbab197ea
 1,  214771000,  214771000,  1558000,     1216, 0x50d1f6c5
 0,       1074,       1074,        1,   518400, 0x8c320e68
-0,       1082,       1082,        1,   518400, 0xbab197ea
+0,       1075,       1075,        1,   518400, 0x8c320e68
+0,       1076,       1076,        1,   518400, 0x8c320e68
+0,       1077,       1077,        1,   518400, 0x8c320e68
+0,       1078,       1078,        1,   518400, 0x8c320e68
+0,       1079,       1079,        1,   518400, 0x8c320e68
+0,       1080,       1080,        1,   518400, 0x8c320e68
+0,       1081,       1081,        1,   518400, 0x8c320e68
+0,       1082,       1082,        1,   518400, 0x8c320e68
+0,       1083,       1083,        1,   518400, 0xbab197ea
+0,       1084,       1084,        1,   518400, 0xbab197ea
+0,       1085,       1085,        1,   518400, 0xbab197ea
+0,       1086,       1086,        1,   518400, 0xbab197ea
+0,       1087,       1087,        1,   518400, 0xbab197ea
+0,       1088,       1088,        1,   518400, 0xbab197ea
+0,       1089,       1089,        1,   518400, 0xbab197ea
+0,       1090,       1090,        1,   518400, 0xbab197ea
+0,       1091,       1091,        1,   518400, 0xbab197ea
+0,       1092,       1092,        1,   518400, 0xbab197ea
+0,       1093,       1093,        1,   518400, 0xbab197ea
+0,       1094,       1094,        1,   518400, 0xbab197ea
+0,       1095,       1095,        1,   518400, 0xbab197ea
+0,       1096,       1096,        1,   518400, 0xbab197ea
+0,       1097,       1097,        1,   518400, 0xbab197ea
+0,       1098,       1098,        1,   518400, 0xbab197ea
+0,       1099,       1099,        1,   518400, 0xbab197ea
+0,       1100,       1100,        1,   518400, 0xbab197ea
+0,       1101,       1101,        1,   518400, 0xbab197ea
+0,       1102,       1102,        1,   518400, 0xbab197ea
+0,       1103,       1103,        1,   518400, 0xbab197ea
+0,       1104,       1104,        1,   518400, 0xbab197ea
+0,       1105,       1105,        1,   518400, 0xbab197ea
+0,       1106,       1106,        1,   518400, 0xbab197ea
+0,       1107,       1107,        1,   518400, 0xbab197ea
+0,       1108,       1108,        1,   518400, 0xbab197ea
+0,       1109,       1109,        1,   518400, 0xbab197ea
+0,       1110,       1110,        1,   518400, 0xbab197ea
+0,       1111,       1111,        1,   518400, 0xbab197ea
+0,       1112,       1112,        1,   518400, 0xbab197ea
+0,       1113,       1113,        1,   518400, 0xbab197ea
+0,       1114,       1114,        1,   518400, 0xbab197ea
+0,       1115,       1115,        1,   518400, 0xbab197ea
+0,       1116,       1116,        1,   518400, 0xbab197ea
+0,       1117,       1117,        1,   518400, 0xbab197ea
+0,       1118,       1118,        1,   518400, 0xbab197ea
+0,       1119,       1119,        1,   518400, 0xbab197ea
+0,       1120,       1120,        1,   518400, 0xbab197ea
+0,       1121,       1121,        1,   518400, 0xbab197ea
+0,       1122,       1122,        1,   518400, 0xbab197ea
+0,       1123,       1123,        1,   518400, 0xbab197ea
+0,       1124,       1124,        1,   518400, 0xbab197ea
+0,       1125,       1125,        1,   518400, 0xbab197ea
+0,       1126,       1126,        1,   518400, 0xbab197ea
+0,       1127,       1127,        1,   518400, 0xbab197ea
 0,       1128,       1128,        1,   518400, 0x81e977b2
 1,  225640000,  225640000,  2127000,     2133, 0x670c11a5
-0,       1139,       1139,        1,   518400, 0xbab197ea
+0,       1129,       1129,        1,   518400, 0x81e977b2
+0,       1130,       1130,        1,   518400, 0x81e977b2
+0,       1131,       1131,        1,   518400, 0x81e977b2
+0,       1132,       1132,        1,   518400, 0x81e977b2
+0,       1133,       1133,        1,   518400, 0x81e977b2
+0,       1134,       1134,        1,   518400, 0x81e977b2
+0,       1135,       1135,        1,   518400, 0x81e977b2
+0,       1136,       1136,        1,   518400, 0x81e977b2
+0,       1137,       1137,        1,   518400, 0x81e977b2
+0,       1138,       1138,        1,   518400, 0x81e977b2
+0,       1139,       1139,        1,   518400, 0x81e977b2
 1,  227834000,  227834000,  1262000,     1264, 0xc1d9fc57
 0,       1140,       1140,        1,   518400, 0xb046dd30
-0,       1145,       1145,        1,   518400, 0xbab197ea
diff --git a/tests/ref/fate/sub2video_basic b/tests/ref/fate/sub2video_basic
index 5f72e292c9..3c9fc71b5c 100644
--- a/tests/ref/fate/sub2video_basic
+++ b/tests/ref/fate/sub2video_basic
@@ -1,95 +1,1150 @@
-#tb 0: 1/25
+#tb 0: 1/5
 #media_type 0: video
 #codec_id 0: rawvideo
 #dimensions 0: 720x480
-#sar 0: 0/1
-0,       3312,       3312,        1,  1382400, 0x00000000
-0,       3312,       3312,        1,  1382400, 0x8c93c2ba
-0,       3436,       3436,        1,  1382400, 0x00000000
-0,       3684,       3684,        1,  1382400, 0xb02e32ca
-0,       3802,       3802,        1,  1382400, 0x00000000
-0,       4520,       4520,        1,  1382400, 0x83b71116
-0,       4584,       4584,        1,  1382400, 0x00000000
-0,       4586,       4586,        1,  1382400, 0x85547fd1
-0,       4645,       4645,        1,  1382400, 0x00000000
-0,       4648,       4648,        1,  1382400, 0x00000000
-0,       4648,       4648,        1,  1382400, 0xb6a8f181
-0,       4715,       4715,        1,  1382400, 0x00000000
-0,       4717,       4717,        1,  1382400, 0xb64d1a2c
-0,       4748,       4748,        1,  1382400, 0x00000000
-0,       4750,       4750,        1,  1382400, 0x7b37ecf3
-0,       4792,       4792,        1,  1382400, 0x00000000
-0,       4993,       4993,        1,  1382400, 0xdc025bd1
-0,       5027,       5027,        1,  1382400, 0x00000000
-0,       5029,       5029,        1,  1382400, 0x688b294d
-0,       5068,       5068,        1,  1382400, 0x00000000
-0,       5070,       5070,        1,  1382400, 0xa2b33d1b
-0,       5117,       5117,        1,  1382400, 0x00000000
-0,       5119,       5119,        1,  1382400, 0xb3e525e3
-0,       5168,       5168,        1,  1382400, 0x00000000
-0,       5170,       5170,        1,  1382400, 0xaa8fbdd7
-0,       5216,       5216,        1,  1382400, 0x00000000
-0,       5218,       5218,        1,  1382400, 0x7b7f26dd
-0,       5249,       5249,        1,  1382400, 0x00000000
-0,       5251,       5251,        1,  1382400, 0x15e2f836
-0,       5289,       5289,        1,  1382400, 0x00000000
-0,       5291,       5291,        1,  1382400, 0x0fee9b0c
-0,       5358,       5358,        1,  1382400, 0x00000000
-0,       5360,       5360,        1,  1382400, 0x89d62791
-0,       5429,       5429,        1,  1382400, 0x00000000
-0,       5431,       5431,        1,  1382400, 0xa6a9fd74
-0,       5490,       5490,        1,  1382400, 0x00000000
-0,       5491,       5491,        1,  1382400, 0x7896178d
-0,       5537,       5537,        1,  1382400, 0x00000000
-0,       5588,       5588,        1,  1382400, 0x01751a52
-0,       5647,       5647,        1,  1382400, 0x00000000
-0,       5688,       5688,        1,  1382400, 0xa3959c6f
-0,       5770,       5770,        1,  1382400, 0x00000000
-0,       5772,       5772,        1,  1382400, 0x3d3ea47b
-0,       5826,       5826,        1,  1382400, 0x00000000
-0,       5828,       5828,        1,  1382400, 0x593f8b24
-0,       5931,       5931,        1,  1382400, 0x00000000
-0,       5933,       5933,        1,  1382400, 0x171f05ba
-0,       6001,       6001,        1,  1382400, 0x00000000
-0,       6003,       6003,        1,  1382400, 0xb014cdf1
-0,       6054,       6054,        1,  1382400, 0x00000000
-0,       6839,       6839,        1,  1382400, 0xd918e667
-0,       6880,       6880,        1,  1382400, 0x00000000
-0,       7386,       7386,        1,  1382400, 0xc9406331
-0,       7419,       7419,        1,  1382400, 0x00000000
-0,       7501,       7501,        1,  1382400, 0xaf08b10d
-0,       7549,       7549,        1,  1382400, 0x00000000
-0,       7551,       7551,        1,  1382400, 0x00000000
-0,       7551,       7551,        1,  1382400, 0x853a9d93
-0,       7589,       7589,        1,  1382400, 0x00000000
-0,       7605,       7605,        1,  1382400, 0x7491a87d
-0,       7647,       7647,        1,  1382400, 0x00000000
-0,       7649,       7649,        1,  1382400, 0xf7383c58
-0,       7697,       7697,        1,  1382400, 0x00000000
-0,       7699,       7699,        1,  1382400, 0xe66be411
-0,       7743,       7743,        1,  1382400, 0x00000000
-0,       8032,       8032,        1,  1382400, 0xd6850362
-0,       8082,       8082,        1,  1382400, 0x00000000
-0,       8084,       8084,        1,  1382400, 0x3e1ed109
-0,       8115,       8115,        1,  1382400, 0x00000000
-0,       8116,       8116,        1,  1382400, 0x39c1b7bd
-0,       8160,       8160,        1,  1382400, 0x00000000
-0,       8180,       8180,        1,  1382400, 0x35b85f2e
-0,       8207,       8207,        1,  1382400, 0x00000000
-0,       8209,       8209,        1,  1382400, 0x00000000
-0,       8209,       8209,        1,  1382400, 0x83f103e5
-0,       8247,       8247,        1,  1382400, 0x00000000
-0,       8249,       8249,        1,  1382400, 0xbc1ca9b3
-0,       8278,       8278,        1,  1382400, 0x00000000
-0,       8281,       8281,        1,  1382400, 0x94d4a51e
-0,       8321,       8321,        1,  1382400, 0x00000000
-0,       8323,       8323,        1,  1382400, 0xf88cdfde
-0,       8367,       8367,        1,  1382400, 0x00000000
-0,       8565,       8565,        1,  1382400, 0xdd51423b
-0,       8611,       8611,        1,  1382400, 0x00000000
-0,       8669,       8669,        1,  1382400, 0x08259fa4
-0,       8708,       8708,        1,  1382400, 0x00000000
-0,       8941,       8941,        1,  1382400, 0x1663fa34
-0,       8994,       8994,        1,  1382400, 0x00000000
-0,       8996,       8996,        1,  1382400, 0xda2ceb55
-0,       9027,       9027,        1,  1382400, 0x00000000
+#sar 0: 1/1
+0,          0,          0,        1,  1382400, 0x00000000
+0,        662,        662,        1,  1382400, 0xc637b893
+0,        663,        663,        1,  1382400, 0xc637b893
+0,        664,        664,        1,  1382400, 0xc637b893
+0,        665,        665,        1,  1382400, 0xc637b893
+0,        666,        666,        1,  1382400, 0xc637b893
+0,        667,        667,        1,  1382400, 0xc637b893
+0,        668,        668,        1,  1382400, 0xc637b893
+0,        669,        669,        1,  1382400, 0xc637b893
+0,        670,        670,        1,  1382400, 0xc637b893
+0,        671,        671,        1,  1382400, 0xc637b893
+0,        672,        672,        1,  1382400, 0xc637b893
+0,        673,        673,        1,  1382400, 0xc637b893
+0,        674,        674,        1,  1382400, 0xc637b893
+0,        675,        675,        1,  1382400, 0xc637b893
+0,        676,        676,        1,  1382400, 0xc637b893
+0,        677,        677,        1,  1382400, 0xc637b893
+0,        678,        678,        1,  1382400, 0xc637b893
+0,        679,        679,        1,  1382400, 0xc637b893
+0,        680,        680,        1,  1382400, 0xc637b893
+0,        681,        681,        1,  1382400, 0xc637b893
+0,        682,        682,        1,  1382400, 0xc637b893
+0,        683,        683,        1,  1382400, 0xc637b893
+0,        684,        684,        1,  1382400, 0xc637b893
+0,        685,        685,        1,  1382400, 0xc637b893
+0,        686,        686,        1,  1382400, 0xc637b893
+0,        687,        687,        1,  1382400, 0x00000000
+0,        688,        688,        1,  1382400, 0x00000000
+0,        689,        689,        1,  1382400, 0x00000000
+0,        690,        690,        1,  1382400, 0x00000000
+0,        691,        691,        1,  1382400, 0x00000000
+0,        692,        692,        1,  1382400, 0x00000000
+0,        693,        693,        1,  1382400, 0x00000000
+0,        694,        694,        1,  1382400, 0x00000000
+0,        695,        695,        1,  1382400, 0x00000000
+0,        696,        696,        1,  1382400, 0x00000000
+0,        697,        697,        1,  1382400, 0x00000000
+0,        698,        698,        1,  1382400, 0x00000000
+0,        699,        699,        1,  1382400, 0x00000000
+0,        700,        700,        1,  1382400, 0x00000000
+0,        701,        701,        1,  1382400, 0x00000000
+0,        702,        702,        1,  1382400, 0x00000000
+0,        703,        703,        1,  1382400, 0x00000000
+0,        704,        704,        1,  1382400, 0x00000000
+0,        705,        705,        1,  1382400, 0x00000000
+0,        706,        706,        1,  1382400, 0x00000000
+0,        707,        707,        1,  1382400, 0x00000000
+0,        708,        708,        1,  1382400, 0x00000000
+0,        709,        709,        1,  1382400, 0x00000000
+0,        710,        710,        1,  1382400, 0x00000000
+0,        711,        711,        1,  1382400, 0x00000000
+0,        712,        712,        1,  1382400, 0x00000000
+0,        713,        713,        1,  1382400, 0x00000000
+0,        714,        714,        1,  1382400, 0x00000000
+0,        715,        715,        1,  1382400, 0x00000000
+0,        716,        716,        1,  1382400, 0x00000000
+0,        717,        717,        1,  1382400, 0x00000000
+0,        718,        718,        1,  1382400, 0x00000000
+0,        719,        719,        1,  1382400, 0x00000000
+0,        720,        720,        1,  1382400, 0x00000000
+0,        721,        721,        1,  1382400, 0x00000000
+0,        722,        722,        1,  1382400, 0x00000000
+0,        723,        723,        1,  1382400, 0x00000000
+0,        724,        724,        1,  1382400, 0x00000000
+0,        725,        725,        1,  1382400, 0x00000000
+0,        726,        726,        1,  1382400, 0x00000000
+0,        727,        727,        1,  1382400, 0x00000000
+0,        728,        728,        1,  1382400, 0x00000000
+0,        729,        729,        1,  1382400, 0x00000000
+0,        730,        730,        1,  1382400, 0x00000000
+0,        731,        731,        1,  1382400, 0x00000000
+0,        732,        732,        1,  1382400, 0x00000000
+0,        733,        733,        1,  1382400, 0x00000000
+0,        734,        734,        1,  1382400, 0x00000000
+0,        735,        735,        1,  1382400, 0x00000000
+0,        737,        737,        1,  1382400, 0x4c2960ca
+0,        737,        737,        1,  1382400, 0x4c2960ca
+0,        738,        738,        1,  1382400, 0x4c2960ca
+0,        739,        739,        1,  1382400, 0x4c2960ca
+0,        740,        740,        1,  1382400, 0x4c2960ca
+0,        741,        741,        1,  1382400, 0x4c2960ca
+0,        742,        742,        1,  1382400, 0x4c2960ca
+0,        743,        743,        1,  1382400, 0x4c2960ca
+0,        744,        744,        1,  1382400, 0x4c2960ca
+0,        745,        745,        1,  1382400, 0x4c2960ca
+0,        746,        746,        1,  1382400, 0x4c2960ca
+0,        747,        747,        1,  1382400, 0x4c2960ca
+0,        748,        748,        1,  1382400, 0x4c2960ca
+0,        749,        749,        1,  1382400, 0x4c2960ca
+0,        750,        750,        1,  1382400, 0x4c2960ca
+0,        751,        751,        1,  1382400, 0x4c2960ca
+0,        752,        752,        1,  1382400, 0x4c2960ca
+0,        753,        753,        1,  1382400, 0x4c2960ca
+0,        754,        754,        1,  1382400, 0x4c2960ca
+0,        755,        755,        1,  1382400, 0x4c2960ca
+0,        756,        756,        1,  1382400, 0x4c2960ca
+0,        757,        757,        1,  1382400, 0x4c2960ca
+0,        758,        758,        1,  1382400, 0x4c2960ca
+0,        759,        759,        1,  1382400, 0x4c2960ca
+0,        760,        760,        1,  1382400, 0x00000000
+0,        761,        761,        1,  1382400, 0x00000000
+0,        762,        762,        1,  1382400, 0x00000000
+0,        763,        763,        1,  1382400, 0x00000000
+0,        764,        764,        1,  1382400, 0x00000000
+0,        765,        765,        1,  1382400, 0x00000000
+0,        766,        766,        1,  1382400, 0x00000000
+0,        767,        767,        1,  1382400, 0x00000000
+0,        768,        768,        1,  1382400, 0x00000000
+0,        769,        769,        1,  1382400, 0x00000000
+0,        770,        770,        1,  1382400, 0x00000000
+0,        771,        771,        1,  1382400, 0x00000000
+0,        772,        772,        1,  1382400, 0x00000000
+0,        773,        773,        1,  1382400, 0x00000000
+0,        774,        774,        1,  1382400, 0x00000000
+0,        775,        775,        1,  1382400, 0x00000000
+0,        776,        776,        1,  1382400, 0x00000000
+0,        777,        777,        1,  1382400, 0x00000000
+0,        778,        778,        1,  1382400, 0x00000000
+0,        779,        779,        1,  1382400, 0x00000000
+0,        780,        780,        1,  1382400, 0x00000000
+0,        781,        781,        1,  1382400, 0x00000000
+0,        782,        782,        1,  1382400, 0x00000000
+0,        783,        783,        1,  1382400, 0x00000000
+0,        784,        784,        1,  1382400, 0x00000000
+0,        785,        785,        1,  1382400, 0x00000000
+0,        786,        786,        1,  1382400, 0x00000000
+0,        787,        787,        1,  1382400, 0x00000000
+0,        788,        788,        1,  1382400, 0x00000000
+0,        789,        789,        1,  1382400, 0x00000000
+0,        790,        790,        1,  1382400, 0x00000000
+0,        791,        791,        1,  1382400, 0x00000000
+0,        792,        792,        1,  1382400, 0x00000000
+0,        793,        793,        1,  1382400, 0x00000000
+0,        794,        794,        1,  1382400, 0x00000000
+0,        795,        795,        1,  1382400, 0x00000000
+0,        796,        796,        1,  1382400, 0x00000000
+0,        797,        797,        1,  1382400, 0x00000000
+0,        798,        798,        1,  1382400, 0x00000000
+0,        799,        799,        1,  1382400, 0x00000000
+0,        800,        800,        1,  1382400, 0x00000000
+0,        801,        801,        1,  1382400, 0x00000000
+0,        802,        802,        1,  1382400, 0x00000000
+0,        803,        803,        1,  1382400, 0x00000000
+0,        804,        804,        1,  1382400, 0x00000000
+0,        805,        805,        1,  1382400, 0x00000000
+0,        806,        806,        1,  1382400, 0x00000000
+0,        807,        807,        1,  1382400, 0x00000000
+0,        808,        808,        1,  1382400, 0x00000000
+0,        809,        809,        1,  1382400, 0x00000000
+0,        810,        810,        1,  1382400, 0x00000000
+0,        811,        811,        1,  1382400, 0x00000000
+0,        812,        812,        1,  1382400, 0x00000000
+0,        813,        813,        1,  1382400, 0x00000000
+0,        814,        814,        1,  1382400, 0x00000000
+0,        815,        815,        1,  1382400, 0x00000000
+0,        816,        816,        1,  1382400, 0x00000000
+0,        817,        817,        1,  1382400, 0x00000000
+0,        818,        818,        1,  1382400, 0x00000000
+0,        819,        819,        1,  1382400, 0x00000000
+0,        820,        820,        1,  1382400, 0x00000000
+0,        821,        821,        1,  1382400, 0x00000000
+0,        822,        822,        1,  1382400, 0x00000000
+0,        823,        823,        1,  1382400, 0x00000000
+0,        824,        824,        1,  1382400, 0x00000000
+0,        825,        825,        1,  1382400, 0x00000000
+0,        826,        826,        1,  1382400, 0x00000000
+0,        827,        827,        1,  1382400, 0x00000000
+0,        828,        828,        1,  1382400, 0x00000000
+0,        829,        829,        1,  1382400, 0x00000000
+0,        830,        830,        1,  1382400, 0x00000000
+0,        831,        831,        1,  1382400, 0x00000000
+0,        832,        832,        1,  1382400, 0x00000000
+0,        833,        833,        1,  1382400, 0x00000000
+0,        834,        834,        1,  1382400, 0x00000000
+0,        835,        835,        1,  1382400, 0x00000000
+0,        836,        836,        1,  1382400, 0x00000000
+0,        837,        837,        1,  1382400, 0x00000000
+0,        838,        838,        1,  1382400, 0x00000000
+0,        839,        839,        1,  1382400, 0x00000000
+0,        840,        840,        1,  1382400, 0x00000000
+0,        841,        841,        1,  1382400, 0x00000000
+0,        842,        842,        1,  1382400, 0x00000000
+0,        843,        843,        1,  1382400, 0x00000000
+0,        844,        844,        1,  1382400, 0x00000000
+0,        845,        845,        1,  1382400, 0x00000000
+0,        846,        846,        1,  1382400, 0x00000000
+0,        847,        847,        1,  1382400, 0x00000000
+0,        848,        848,        1,  1382400, 0x00000000
+0,        849,        849,        1,  1382400, 0x00000000
+0,        850,        850,        1,  1382400, 0x00000000
+0,        851,        851,        1,  1382400, 0x00000000
+0,        852,        852,        1,  1382400, 0x00000000
+0,        853,        853,        1,  1382400, 0x00000000
+0,        854,        854,        1,  1382400, 0x00000000
+0,        855,        855,        1,  1382400, 0x00000000
+0,        856,        856,        1,  1382400, 0x00000000
+0,        857,        857,        1,  1382400, 0x00000000
+0,        858,        858,        1,  1382400, 0x00000000
+0,        859,        859,        1,  1382400, 0x00000000
+0,        860,        860,        1,  1382400, 0x00000000
+0,        861,        861,        1,  1382400, 0x00000000
+0,        862,        862,        1,  1382400, 0x00000000
+0,        863,        863,        1,  1382400, 0x00000000
+0,        864,        864,        1,  1382400, 0x00000000
+0,        865,        865,        1,  1382400, 0x00000000
+0,        866,        866,        1,  1382400, 0x00000000
+0,        867,        867,        1,  1382400, 0x00000000
+0,        868,        868,        1,  1382400, 0x00000000
+0,        869,        869,        1,  1382400, 0x00000000
+0,        870,        870,        1,  1382400, 0x00000000
+0,        871,        871,        1,  1382400, 0x00000000
+0,        872,        872,        1,  1382400, 0x00000000
+0,        873,        873,        1,  1382400, 0x00000000
+0,        874,        874,        1,  1382400, 0x00000000
+0,        875,        875,        1,  1382400, 0x00000000
+0,        876,        876,        1,  1382400, 0x00000000
+0,        877,        877,        1,  1382400, 0x00000000
+0,        878,        878,        1,  1382400, 0x00000000
+0,        879,        879,        1,  1382400, 0x00000000
+0,        880,        880,        1,  1382400, 0x00000000
+0,        881,        881,        1,  1382400, 0x00000000
+0,        882,        882,        1,  1382400, 0x00000000
+0,        883,        883,        1,  1382400, 0x00000000
+0,        884,        884,        1,  1382400, 0x00000000
+0,        885,        885,        1,  1382400, 0x00000000
+0,        886,        886,        1,  1382400, 0x00000000
+0,        887,        887,        1,  1382400, 0x00000000
+0,        888,        888,        1,  1382400, 0x00000000
+0,        889,        889,        1,  1382400, 0x00000000
+0,        890,        890,        1,  1382400, 0x00000000
+0,        891,        891,        1,  1382400, 0x00000000
+0,        892,        892,        1,  1382400, 0x00000000
+0,        893,        893,        1,  1382400, 0x00000000
+0,        894,        894,        1,  1382400, 0x00000000
+0,        895,        895,        1,  1382400, 0x00000000
+0,        896,        896,        1,  1382400, 0x00000000
+0,        897,        897,        1,  1382400, 0x00000000
+0,        898,        898,        1,  1382400, 0x00000000
+0,        899,        899,        1,  1382400, 0x00000000
+0,        900,        900,        1,  1382400, 0x00000000
+0,        901,        901,        1,  1382400, 0x00000000
+0,        902,        902,        1,  1382400, 0x00000000
+0,        904,        904,        1,  1382400, 0x5fa18966
+0,        904,        904,        1,  1382400, 0x5fa18966
+0,        905,        905,        1,  1382400, 0x5fa18966
+0,        906,        906,        1,  1382400, 0x5fa18966
+0,        907,        907,        1,  1382400, 0x5fa18966
+0,        908,        908,        1,  1382400, 0x5fa18966
+0,        909,        909,        1,  1382400, 0x5fa18966
+0,        910,        910,        1,  1382400, 0x5fa18966
+0,        911,        911,        1,  1382400, 0x5fa18966
+0,        912,        912,        1,  1382400, 0x5fa18966
+0,        913,        913,        1,  1382400, 0x5fa18966
+0,        914,        914,        1,  1382400, 0x5fa18966
+0,        915,        915,        1,  1382400, 0x5fa18966
+0,        916,        916,        1,  1382400, 0x5fa18966
+0,        917,        917,        1,  1382400, 0x55f4b7b1
+0,        918,        918,        1,  1382400, 0x55f4b7b1
+0,        919,        919,        1,  1382400, 0x55f4b7b1
+0,        920,        920,        1,  1382400, 0x55f4b7b1
+0,        921,        921,        1,  1382400, 0x55f4b7b1
+0,        922,        922,        1,  1382400, 0x55f4b7b1
+0,        923,        923,        1,  1382400, 0x55f4b7b1
+0,        924,        924,        1,  1382400, 0x55f4b7b1
+0,        925,        925,        1,  1382400, 0x55f4b7b1
+0,        926,        926,        1,  1382400, 0x55f4b7b1
+0,        927,        927,        1,  1382400, 0x55f4b7b1
+0,        928,        928,        1,  1382400, 0x55f4b7b1
+0,        929,        929,        1,  1382400, 0x00000000
+0,        930,        930,        1,  1382400, 0xdfa4cf32
+0,        931,        931,        1,  1382400, 0xdfa4cf32
+0,        932,        932,        1,  1382400, 0xdfa4cf32
+0,        933,        933,        1,  1382400, 0xdfa4cf32
+0,        934,        934,        1,  1382400, 0xdfa4cf32
+0,        935,        935,        1,  1382400, 0xdfa4cf32
+0,        936,        936,        1,  1382400, 0xdfa4cf32
+0,        937,        937,        1,  1382400, 0xdfa4cf32
+0,        938,        938,        1,  1382400, 0xdfa4cf32
+0,        939,        939,        1,  1382400, 0xdfa4cf32
+0,        940,        940,        1,  1382400, 0xdfa4cf32
+0,        941,        941,        1,  1382400, 0xdfa4cf32
+0,        942,        942,        1,  1382400, 0xdfa4cf32
+0,        943,        943,        1,  1382400, 0x35023df8
+0,        944,        944,        1,  1382400, 0x35023df8
+0,        945,        945,        1,  1382400, 0x35023df8
+0,        946,        946,        1,  1382400, 0x35023df8
+0,        947,        947,        1,  1382400, 0x35023df8
+0,        948,        948,        1,  1382400, 0x35023df8
+0,        949,        949,        1,  1382400, 0x35023df8
+0,        950,        950,        1,  1382400, 0xed933219
+0,        951,        951,        1,  1382400, 0xed933219
+0,        952,        952,        1,  1382400, 0xed933219
+0,        953,        953,        1,  1382400, 0xed933219
+0,        954,        954,        1,  1382400, 0xed933219
+0,        955,        955,        1,  1382400, 0xed933219
+0,        956,        956,        1,  1382400, 0xed933219
+0,        957,        957,        1,  1382400, 0xed933219
+0,        958,        958,        1,  1382400, 0x00000000
+0,        959,        959,        1,  1382400, 0x00000000
+0,        960,        960,        1,  1382400, 0x00000000
+0,        961,        961,        1,  1382400, 0x00000000
+0,        962,        962,        1,  1382400, 0x00000000
+0,        963,        963,        1,  1382400, 0x00000000
+0,        964,        964,        1,  1382400, 0x00000000
+0,        965,        965,        1,  1382400, 0x00000000
+0,        966,        966,        1,  1382400, 0x00000000
+0,        967,        967,        1,  1382400, 0x00000000
+0,        968,        968,        1,  1382400, 0x00000000
+0,        969,        969,        1,  1382400, 0x00000000
+0,        970,        970,        1,  1382400, 0x00000000
+0,        971,        971,        1,  1382400, 0x00000000
+0,        972,        972,        1,  1382400, 0x00000000
+0,        973,        973,        1,  1382400, 0x00000000
+0,        974,        974,        1,  1382400, 0x00000000
+0,        975,        975,        1,  1382400, 0x00000000
+0,        976,        976,        1,  1382400, 0x00000000
+0,        977,        977,        1,  1382400, 0x00000000
+0,        978,        978,        1,  1382400, 0x00000000
+0,        979,        979,        1,  1382400, 0x00000000
+0,        980,        980,        1,  1382400, 0x00000000
+0,        981,        981,        1,  1382400, 0x00000000
+0,        982,        982,        1,  1382400, 0x00000000
+0,        983,        983,        1,  1382400, 0x00000000
+0,        984,        984,        1,  1382400, 0x00000000
+0,        985,        985,        1,  1382400, 0x00000000
+0,        986,        986,        1,  1382400, 0x00000000
+0,        987,        987,        1,  1382400, 0x00000000
+0,        988,        988,        1,  1382400, 0x00000000
+0,        989,        989,        1,  1382400, 0x00000000
+0,        990,        990,        1,  1382400, 0x00000000
+0,        991,        991,        1,  1382400, 0x00000000
+0,        992,        992,        1,  1382400, 0x00000000
+0,        993,        993,        1,  1382400, 0x00000000
+0,        994,        994,        1,  1382400, 0x00000000
+0,        995,        995,        1,  1382400, 0x00000000
+0,        996,        996,        1,  1382400, 0x00000000
+0,        997,        997,        1,  1382400, 0x00000000
+0,        999,        999,        1,  1382400, 0x1b26389a
+0,        999,        999,        1,  1382400, 0x1b26389a
+0,       1000,       1000,        1,  1382400, 0x1b26389a
+0,       1001,       1001,        1,  1382400, 0x1b26389a
+0,       1002,       1002,        1,  1382400, 0x1b26389a
+0,       1003,       1003,        1,  1382400, 0x1b26389a
+0,       1004,       1004,        1,  1382400, 0x1b26389a
+0,       1006,       1006,        1,  1382400, 0xf0c7028b
+0,       1006,       1006,        1,  1382400, 0xf0c7028b
+0,       1007,       1007,        1,  1382400, 0xf0c7028b
+0,       1008,       1008,        1,  1382400, 0xf0c7028b
+0,       1009,       1009,        1,  1382400, 0xf0c7028b
+0,       1010,       1010,        1,  1382400, 0xf0c7028b
+0,       1011,       1011,        1,  1382400, 0xf0c7028b
+0,       1012,       1012,        1,  1382400, 0xf0c7028b
+0,       1013,       1013,        1,  1382400, 0xf0c7028b
+0,       1014,       1014,        1,  1382400, 0x395f521d
+0,       1015,       1015,        1,  1382400, 0x395f521d
+0,       1016,       1016,        1,  1382400, 0x395f521d
+0,       1017,       1017,        1,  1382400, 0x395f521d
+0,       1018,       1018,        1,  1382400, 0x395f521d
+0,       1019,       1019,        1,  1382400, 0x395f521d
+0,       1020,       1020,        1,  1382400, 0x395f521d
+0,       1021,       1021,        1,  1382400, 0x395f521d
+0,       1022,       1022,        1,  1382400, 0x395f521d
+0,       1024,       1024,        1,  1382400, 0x1ea87415
+0,       1024,       1024,        1,  1382400, 0x1ea87415
+0,       1025,       1025,        1,  1382400, 0x1ea87415
+0,       1026,       1026,        1,  1382400, 0x1ea87415
+0,       1027,       1027,        1,  1382400, 0x1ea87415
+0,       1028,       1028,        1,  1382400, 0x1ea87415
+0,       1029,       1029,        1,  1382400, 0x1ea87415
+0,       1030,       1030,        1,  1382400, 0x1ea87415
+0,       1031,       1031,        1,  1382400, 0x1ea87415
+0,       1032,       1032,        1,  1382400, 0x1ea87415
+0,       1033,       1033,        1,  1382400, 0x1ea87415
+0,       1034,       1034,        1,  1382400, 0xc6effdc1
+0,       1035,       1035,        1,  1382400, 0xc6effdc1
+0,       1036,       1036,        1,  1382400, 0xc6effdc1
+0,       1037,       1037,        1,  1382400, 0xc6effdc1
+0,       1038,       1038,        1,  1382400, 0xc6effdc1
+0,       1039,       1039,        1,  1382400, 0xc6effdc1
+0,       1040,       1040,        1,  1382400, 0xc6effdc1
+0,       1041,       1041,        1,  1382400, 0xc6effdc1
+0,       1042,       1042,        1,  1382400, 0xc6effdc1
+0,       1044,       1044,        1,  1382400, 0xba6846f8
+0,       1044,       1044,        1,  1382400, 0xba6846f8
+0,       1045,       1045,        1,  1382400, 0xba6846f8
+0,       1046,       1046,        1,  1382400, 0xba6846f8
+0,       1047,       1047,        1,  1382400, 0xba6846f8
+0,       1048,       1048,        1,  1382400, 0xba6846f8
+0,       1049,       1049,        1,  1382400, 0xba6846f8
+0,       1050,       1050,        1,  1382400, 0x033c5d5b
+0,       1051,       1051,        1,  1382400, 0x033c5d5b
+0,       1052,       1052,        1,  1382400, 0x033c5d5b
+0,       1053,       1053,        1,  1382400, 0x033c5d5b
+0,       1054,       1054,        1,  1382400, 0x033c5d5b
+0,       1055,       1055,        1,  1382400, 0x033c5d5b
+0,       1056,       1056,        1,  1382400, 0x033c5d5b
+0,       1057,       1057,        1,  1382400, 0x033c5d5b
+0,       1058,       1058,        1,  1382400, 0x00000000
+0,       1059,       1059,        1,  1382400, 0xef5abf66
+0,       1060,       1060,        1,  1382400, 0xef5abf66
+0,       1061,       1061,        1,  1382400, 0xef5abf66
+0,       1062,       1062,        1,  1382400, 0xef5abf66
+0,       1063,       1063,        1,  1382400, 0xef5abf66
+0,       1064,       1064,        1,  1382400, 0xef5abf66
+0,       1065,       1065,        1,  1382400, 0xef5abf66
+0,       1066,       1066,        1,  1382400, 0xef5abf66
+0,       1067,       1067,        1,  1382400, 0xef5abf66
+0,       1068,       1068,        1,  1382400, 0xef5abf66
+0,       1069,       1069,        1,  1382400, 0xef5abf66
+0,       1070,       1070,        1,  1382400, 0xef5abf66
+0,       1071,       1071,        1,  1382400, 0xef5abf66
+0,       1072,       1072,        1,  1382400, 0x00000000
+0,       1073,       1073,        1,  1382400, 0xec747954
+0,       1074,       1074,        1,  1382400, 0xec747954
+0,       1075,       1075,        1,  1382400, 0xec747954
+0,       1076,       1076,        1,  1382400, 0xec747954
+0,       1077,       1077,        1,  1382400, 0xec747954
+0,       1078,       1078,        1,  1382400, 0xec747954
+0,       1079,       1079,        1,  1382400, 0xec747954
+0,       1080,       1080,        1,  1382400, 0xec747954
+0,       1081,       1081,        1,  1382400, 0xec747954
+0,       1082,       1082,        1,  1382400, 0xec747954
+0,       1083,       1083,        1,  1382400, 0xec747954
+0,       1084,       1084,        1,  1382400, 0xec747954
+0,       1085,       1085,        1,  1382400, 0xec747954
+0,       1086,       1086,        1,  1382400, 0x00000000
+0,       1087,       1087,        1,  1382400, 0xfa34bcaf
+0,       1088,       1088,        1,  1382400, 0xfa34bcaf
+0,       1089,       1089,        1,  1382400, 0xfa34bcaf
+0,       1090,       1090,        1,  1382400, 0xfa34bcaf
+0,       1091,       1091,        1,  1382400, 0xfa34bcaf
+0,       1092,       1092,        1,  1382400, 0xfa34bcaf
+0,       1093,       1093,        1,  1382400, 0xfa34bcaf
+0,       1094,       1094,        1,  1382400, 0xfa34bcaf
+0,       1095,       1095,        1,  1382400, 0xfa34bcaf
+0,       1096,       1096,        1,  1382400, 0xfa34bcaf
+0,       1097,       1097,        1,  1382400, 0xfa34bcaf
+0,       1098,       1098,        1,  1382400, 0x00000000
+0,       1099,       1099,        1,  1382400, 0x8b7a709b
+0,       1100,       1100,        1,  1382400, 0x8b7a709b
+0,       1101,       1101,        1,  1382400, 0x8b7a709b
+0,       1102,       1102,        1,  1382400, 0x8b7a709b
+0,       1103,       1103,        1,  1382400, 0x8b7a709b
+0,       1104,       1104,        1,  1382400, 0x8b7a709b
+0,       1105,       1105,        1,  1382400, 0x8b7a709b
+0,       1106,       1106,        1,  1382400, 0x8b7a709b
+0,       1107,       1107,        1,  1382400, 0x00000000
+0,       1108,       1108,        1,  1382400, 0x00000000
+0,       1109,       1109,        1,  1382400, 0x00000000
+0,       1110,       1110,        1,  1382400, 0x00000000
+0,       1111,       1111,        1,  1382400, 0x00000000
+0,       1112,       1112,        1,  1382400, 0x00000000
+0,       1113,       1113,        1,  1382400, 0x00000000
+0,       1114,       1114,        1,  1382400, 0x00000000
+0,       1115,       1115,        1,  1382400, 0x00000000
+0,       1116,       1116,        1,  1382400, 0x00000000
+0,       1118,       1118,        1,  1382400, 0xc333382f
+0,       1118,       1118,        1,  1382400, 0xc333382f
+0,       1119,       1119,        1,  1382400, 0xc333382f
+0,       1120,       1120,        1,  1382400, 0xc333382f
+0,       1121,       1121,        1,  1382400, 0xc333382f
+0,       1122,       1122,        1,  1382400, 0xc333382f
+0,       1123,       1123,        1,  1382400, 0xc333382f
+0,       1124,       1124,        1,  1382400, 0xc333382f
+0,       1125,       1125,        1,  1382400, 0xc333382f
+0,       1126,       1126,        1,  1382400, 0xc333382f
+0,       1127,       1127,        1,  1382400, 0xc333382f
+0,       1128,       1128,        1,  1382400, 0xc333382f
+0,       1129,       1129,        1,  1382400, 0x00000000
+0,       1130,       1130,        1,  1382400, 0x00000000
+0,       1131,       1131,        1,  1382400, 0x00000000
+0,       1132,       1132,        1,  1382400, 0x00000000
+0,       1133,       1133,        1,  1382400, 0x00000000
+0,       1134,       1134,        1,  1382400, 0x00000000
+0,       1135,       1135,        1,  1382400, 0x00000000
+0,       1136,       1136,        1,  1382400, 0x00000000
+0,       1138,       1138,        1,  1382400, 0xabe5dfcf
+0,       1138,       1138,        1,  1382400, 0xabe5dfcf
+0,       1139,       1139,        1,  1382400, 0xabe5dfcf
+0,       1140,       1140,        1,  1382400, 0xabe5dfcf
+0,       1141,       1141,        1,  1382400, 0xabe5dfcf
+0,       1142,       1142,        1,  1382400, 0xabe5dfcf
+0,       1143,       1143,        1,  1382400, 0xabe5dfcf
+0,       1144,       1144,        1,  1382400, 0xabe5dfcf
+0,       1145,       1145,        1,  1382400, 0xabe5dfcf
+0,       1146,       1146,        1,  1382400, 0xabe5dfcf
+0,       1147,       1147,        1,  1382400, 0xabe5dfcf
+0,       1148,       1148,        1,  1382400, 0xabe5dfcf
+0,       1149,       1149,        1,  1382400, 0xabe5dfcf
+0,       1150,       1150,        1,  1382400, 0xabe5dfcf
+0,       1151,       1151,        1,  1382400, 0xabe5dfcf
+0,       1152,       1152,        1,  1382400, 0xabe5dfcf
+0,       1153,       1153,        1,  1382400, 0xabe5dfcf
+0,       1154,       1154,        1,  1382400, 0x56948101
+0,       1155,       1155,        1,  1382400, 0x56948101
+0,       1156,       1156,        1,  1382400, 0x56948101
+0,       1157,       1157,        1,  1382400, 0x56948101
+0,       1158,       1158,        1,  1382400, 0x56948101
+0,       1159,       1159,        1,  1382400, 0x56948101
+0,       1160,       1160,        1,  1382400, 0x56948101
+0,       1161,       1161,        1,  1382400, 0x56948101
+0,       1162,       1162,        1,  1382400, 0x56948101
+0,       1163,       1163,        1,  1382400, 0x56948101
+0,       1164,       1164,        1,  1382400, 0x56948101
+0,       1166,       1166,        1,  1382400, 0xb747834a
+0,       1166,       1166,        1,  1382400, 0xb747834a
+0,       1167,       1167,        1,  1382400, 0xb747834a
+0,       1168,       1168,        1,  1382400, 0xb747834a
+0,       1169,       1169,        1,  1382400, 0xb747834a
+0,       1170,       1170,        1,  1382400, 0xb747834a
+0,       1171,       1171,        1,  1382400, 0xb747834a
+0,       1172,       1172,        1,  1382400, 0xb747834a
+0,       1173,       1173,        1,  1382400, 0xb747834a
+0,       1174,       1174,        1,  1382400, 0xb747834a
+0,       1175,       1175,        1,  1382400, 0xb747834a
+0,       1176,       1176,        1,  1382400, 0xb747834a
+0,       1177,       1177,        1,  1382400, 0xb747834a
+0,       1178,       1178,        1,  1382400, 0xb747834a
+0,       1179,       1179,        1,  1382400, 0xb747834a
+0,       1180,       1180,        1,  1382400, 0xb747834a
+0,       1181,       1181,        1,  1382400, 0xb747834a
+0,       1182,       1182,        1,  1382400, 0xb747834a
+0,       1183,       1183,        1,  1382400, 0xb747834a
+0,       1184,       1184,        1,  1382400, 0xb747834a
+0,       1185,       1185,        1,  1382400, 0xb747834a
+0,       1187,       1187,        1,  1382400, 0x3448baad
+0,       1187,       1187,        1,  1382400, 0x3448baad
+0,       1188,       1188,        1,  1382400, 0x3448baad
+0,       1189,       1189,        1,  1382400, 0x3448baad
+0,       1190,       1190,        1,  1382400, 0x3448baad
+0,       1191,       1191,        1,  1382400, 0x3448baad
+0,       1192,       1192,        1,  1382400, 0x3448baad
+0,       1193,       1193,        1,  1382400, 0x3448baad
+0,       1194,       1194,        1,  1382400, 0x3448baad
+0,       1195,       1195,        1,  1382400, 0x3448baad
+0,       1196,       1196,        1,  1382400, 0x3448baad
+0,       1197,       1197,        1,  1382400, 0x3448baad
+0,       1198,       1198,        1,  1382400, 0x3448baad
+0,       1199,       1199,        1,  1382400, 0x3448baad
+0,       1200,       1200,        1,  1382400, 0x00000000
+0,       1201,       1201,        1,  1382400, 0xaabe4f37
+0,       1202,       1202,        1,  1382400, 0xaabe4f37
+0,       1203,       1203,        1,  1382400, 0xaabe4f37
+0,       1204,       1204,        1,  1382400, 0xaabe4f37
+0,       1205,       1205,        1,  1382400, 0xaabe4f37
+0,       1206,       1206,        1,  1382400, 0xaabe4f37
+0,       1207,       1207,        1,  1382400, 0xaabe4f37
+0,       1208,       1208,        1,  1382400, 0xaabe4f37
+0,       1209,       1209,        1,  1382400, 0xaabe4f37
+0,       1210,       1210,        1,  1382400, 0xaabe4f37
+0,       1211,       1211,        1,  1382400, 0x00000000
+0,       1212,       1212,        1,  1382400, 0x00000000
+0,       1213,       1213,        1,  1382400, 0x00000000
+0,       1214,       1214,        1,  1382400, 0x00000000
+0,       1215,       1215,        1,  1382400, 0x00000000
+0,       1216,       1216,        1,  1382400, 0x00000000
+0,       1217,       1217,        1,  1382400, 0x00000000
+0,       1218,       1218,        1,  1382400, 0x00000000
+0,       1219,       1219,        1,  1382400, 0x00000000
+0,       1220,       1220,        1,  1382400, 0x00000000
+0,       1221,       1221,        1,  1382400, 0x00000000
+0,       1222,       1222,        1,  1382400, 0x00000000
+0,       1223,       1223,        1,  1382400, 0x00000000
+0,       1224,       1224,        1,  1382400, 0x00000000
+0,       1225,       1225,        1,  1382400, 0x00000000
+0,       1226,       1226,        1,  1382400, 0x00000000
+0,       1227,       1227,        1,  1382400, 0x00000000
+0,       1228,       1228,        1,  1382400, 0x00000000
+0,       1229,       1229,        1,  1382400, 0x00000000
+0,       1230,       1230,        1,  1382400, 0x00000000
+0,       1231,       1231,        1,  1382400, 0x00000000
+0,       1232,       1232,        1,  1382400, 0x00000000
+0,       1233,       1233,        1,  1382400, 0x00000000
+0,       1234,       1234,        1,  1382400, 0x00000000
+0,       1235,       1235,        1,  1382400, 0x00000000
+0,       1236,       1236,        1,  1382400, 0x00000000
+0,       1237,       1237,        1,  1382400, 0x00000000
+0,       1238,       1238,        1,  1382400, 0x00000000
+0,       1239,       1239,        1,  1382400, 0x00000000
+0,       1240,       1240,        1,  1382400, 0x00000000
+0,       1241,       1241,        1,  1382400, 0x00000000
+0,       1242,       1242,        1,  1382400, 0x00000000
+0,       1243,       1243,        1,  1382400, 0x00000000
+0,       1244,       1244,        1,  1382400, 0x00000000
+0,       1245,       1245,        1,  1382400, 0x00000000
+0,       1246,       1246,        1,  1382400, 0x00000000
+0,       1247,       1247,        1,  1382400, 0x00000000
+0,       1248,       1248,        1,  1382400, 0x00000000
+0,       1249,       1249,        1,  1382400, 0x00000000
+0,       1250,       1250,        1,  1382400, 0x00000000
+0,       1251,       1251,        1,  1382400, 0x00000000
+0,       1252,       1252,        1,  1382400, 0x00000000
+0,       1253,       1253,        1,  1382400, 0x00000000
+0,       1254,       1254,        1,  1382400, 0x00000000
+0,       1255,       1255,        1,  1382400, 0x00000000
+0,       1256,       1256,        1,  1382400, 0x00000000
+0,       1257,       1257,        1,  1382400, 0x00000000
+0,       1258,       1258,        1,  1382400, 0x00000000
+0,       1259,       1259,        1,  1382400, 0x00000000
+0,       1260,       1260,        1,  1382400, 0x00000000
+0,       1261,       1261,        1,  1382400, 0x00000000
+0,       1262,       1262,        1,  1382400, 0x00000000
+0,       1263,       1263,        1,  1382400, 0x00000000
+0,       1264,       1264,        1,  1382400, 0x00000000
+0,       1265,       1265,        1,  1382400, 0x00000000
+0,       1266,       1266,        1,  1382400, 0x00000000
+0,       1267,       1267,        1,  1382400, 0x00000000
+0,       1268,       1268,        1,  1382400, 0x00000000
+0,       1269,       1269,        1,  1382400, 0x00000000
+0,       1270,       1270,        1,  1382400, 0x00000000
+0,       1271,       1271,        1,  1382400, 0x00000000
+0,       1272,       1272,        1,  1382400, 0x00000000
+0,       1273,       1273,        1,  1382400, 0x00000000
+0,       1274,       1274,        1,  1382400, 0x00000000
+0,       1275,       1275,        1,  1382400, 0x00000000
+0,       1276,       1276,        1,  1382400, 0x00000000
+0,       1277,       1277,        1,  1382400, 0x00000000
+0,       1278,       1278,        1,  1382400, 0x00000000
+0,       1279,       1279,        1,  1382400, 0x00000000
+0,       1280,       1280,        1,  1382400, 0x00000000
+0,       1281,       1281,        1,  1382400, 0x00000000
+0,       1282,       1282,        1,  1382400, 0x00000000
+0,       1283,       1283,        1,  1382400, 0x00000000
+0,       1284,       1284,        1,  1382400, 0x00000000
+0,       1285,       1285,        1,  1382400, 0x00000000
+0,       1286,       1286,        1,  1382400, 0x00000000
+0,       1287,       1287,        1,  1382400, 0x00000000
+0,       1288,       1288,        1,  1382400, 0x00000000
+0,       1289,       1289,        1,  1382400, 0x00000000
+0,       1290,       1290,        1,  1382400, 0x00000000
+0,       1291,       1291,        1,  1382400, 0x00000000
+0,       1292,       1292,        1,  1382400, 0x00000000
+0,       1293,       1293,        1,  1382400, 0x00000000
+0,       1294,       1294,        1,  1382400, 0x00000000
+0,       1295,       1295,        1,  1382400, 0x00000000
+0,       1296,       1296,        1,  1382400, 0x00000000
+0,       1297,       1297,        1,  1382400, 0x00000000
+0,       1298,       1298,        1,  1382400, 0x00000000
+0,       1299,       1299,        1,  1382400, 0x00000000
+0,       1300,       1300,        1,  1382400, 0x00000000
+0,       1301,       1301,        1,  1382400, 0x00000000
+0,       1302,       1302,        1,  1382400, 0x00000000
+0,       1303,       1303,        1,  1382400, 0x00000000
+0,       1304,       1304,        1,  1382400, 0x00000000
+0,       1305,       1305,        1,  1382400, 0x00000000
+0,       1306,       1306,        1,  1382400, 0x00000000
+0,       1307,       1307,        1,  1382400, 0x00000000
+0,       1308,       1308,        1,  1382400, 0x00000000
+0,       1309,       1309,        1,  1382400, 0x00000000
+0,       1310,       1310,        1,  1382400, 0x00000000
+0,       1311,       1311,        1,  1382400, 0x00000000
+0,       1312,       1312,        1,  1382400, 0x00000000
+0,       1313,       1313,        1,  1382400, 0x00000000
+0,       1314,       1314,        1,  1382400, 0x00000000
+0,       1315,       1315,        1,  1382400, 0x00000000
+0,       1316,       1316,        1,  1382400, 0x00000000
+0,       1317,       1317,        1,  1382400, 0x00000000
+0,       1318,       1318,        1,  1382400, 0x00000000
+0,       1319,       1319,        1,  1382400, 0x00000000
+0,       1320,       1320,        1,  1382400, 0x00000000
+0,       1321,       1321,        1,  1382400, 0x00000000
+0,       1322,       1322,        1,  1382400, 0x00000000
+0,       1323,       1323,        1,  1382400, 0x00000000
+0,       1324,       1324,        1,  1382400, 0x00000000
+0,       1325,       1325,        1,  1382400, 0x00000000
+0,       1326,       1326,        1,  1382400, 0x00000000
+0,       1327,       1327,        1,  1382400, 0x00000000
+0,       1328,       1328,        1,  1382400, 0x00000000
+0,       1329,       1329,        1,  1382400, 0x00000000
+0,       1330,       1330,        1,  1382400, 0x00000000
+0,       1331,       1331,        1,  1382400, 0x00000000
+0,       1332,       1332,        1,  1382400, 0x00000000
+0,       1333,       1333,        1,  1382400, 0x00000000
+0,       1334,       1334,        1,  1382400, 0x00000000
+0,       1335,       1335,        1,  1382400, 0x00000000
+0,       1336,       1336,        1,  1382400, 0x00000000
+0,       1337,       1337,        1,  1382400, 0x00000000
+0,       1338,       1338,        1,  1382400, 0x00000000
+0,       1339,       1339,        1,  1382400, 0x00000000
+0,       1340,       1340,        1,  1382400, 0x00000000
+0,       1341,       1341,        1,  1382400, 0x00000000
+0,       1342,       1342,        1,  1382400, 0x00000000
+0,       1343,       1343,        1,  1382400, 0x00000000
+0,       1344,       1344,        1,  1382400, 0x00000000
+0,       1345,       1345,        1,  1382400, 0x00000000
+0,       1346,       1346,        1,  1382400, 0x00000000
+0,       1347,       1347,        1,  1382400, 0x00000000
+0,       1348,       1348,        1,  1382400, 0x00000000
+0,       1349,       1349,        1,  1382400, 0x00000000
+0,       1350,       1350,        1,  1382400, 0x00000000
+0,       1351,       1351,        1,  1382400, 0x00000000
+0,       1352,       1352,        1,  1382400, 0x00000000
+0,       1353,       1353,        1,  1382400, 0x00000000
+0,       1354,       1354,        1,  1382400, 0x00000000
+0,       1355,       1355,        1,  1382400, 0x00000000
+0,       1356,       1356,        1,  1382400, 0x00000000
+0,       1357,       1357,        1,  1382400, 0x00000000
+0,       1358,       1358,        1,  1382400, 0x00000000
+0,       1359,       1359,        1,  1382400, 0x00000000
+0,       1360,       1360,        1,  1382400, 0x00000000
+0,       1361,       1361,        1,  1382400, 0x00000000
+0,       1362,       1362,        1,  1382400, 0x00000000
+0,       1363,       1363,        1,  1382400, 0x00000000
+0,       1364,       1364,        1,  1382400, 0x00000000
+0,       1365,       1365,        1,  1382400, 0x00000000
+0,       1366,       1366,        1,  1382400, 0x00000000
+0,       1368,       1368,        1,  1382400, 0x8a48cd6f
+0,       1368,       1368,        1,  1382400, 0x8a48cd6f
+0,       1369,       1369,        1,  1382400, 0x8a48cd6f
+0,       1370,       1370,        1,  1382400, 0x8a48cd6f
+0,       1371,       1371,        1,  1382400, 0x8a48cd6f
+0,       1372,       1372,        1,  1382400, 0x8a48cd6f
+0,       1373,       1373,        1,  1382400, 0x8a48cd6f
+0,       1374,       1374,        1,  1382400, 0x8a48cd6f
+0,       1375,       1375,        1,  1382400, 0x8a48cd6f
+0,       1376,       1376,        1,  1382400, 0x00000000
+0,       1377,       1377,        1,  1382400, 0x00000000
+0,       1378,       1378,        1,  1382400, 0x00000000
+0,       1379,       1379,        1,  1382400, 0x00000000
+0,       1380,       1380,        1,  1382400, 0x00000000
+0,       1381,       1381,        1,  1382400, 0x00000000
+0,       1382,       1382,        1,  1382400, 0x00000000
+0,       1383,       1383,        1,  1382400, 0x00000000
+0,       1384,       1384,        1,  1382400, 0x00000000
+0,       1385,       1385,        1,  1382400, 0x00000000
+0,       1386,       1386,        1,  1382400, 0x00000000
+0,       1387,       1387,        1,  1382400, 0x00000000
+0,       1388,       1388,        1,  1382400, 0x00000000
+0,       1389,       1389,        1,  1382400, 0x00000000
+0,       1390,       1390,        1,  1382400, 0x00000000
+0,       1391,       1391,        1,  1382400, 0x00000000
+0,       1392,       1392,        1,  1382400, 0x00000000
+0,       1393,       1393,        1,  1382400, 0x00000000
+0,       1394,       1394,        1,  1382400, 0x00000000
+0,       1395,       1395,        1,  1382400, 0x00000000
+0,       1396,       1396,        1,  1382400, 0x00000000
+0,       1397,       1397,        1,  1382400, 0x00000000
+0,       1398,       1398,        1,  1382400, 0x00000000
+0,       1399,       1399,        1,  1382400, 0x00000000
+0,       1400,       1400,        1,  1382400, 0x00000000
+0,       1401,       1401,        1,  1382400, 0x00000000
+0,       1402,       1402,        1,  1382400, 0x00000000
+0,       1403,       1403,        1,  1382400, 0x00000000
+0,       1404,       1404,        1,  1382400, 0x00000000
+0,       1405,       1405,        1,  1382400, 0x00000000
+0,       1406,       1406,        1,  1382400, 0x00000000
+0,       1407,       1407,        1,  1382400, 0x00000000
+0,       1408,       1408,        1,  1382400, 0x00000000
+0,       1409,       1409,        1,  1382400, 0x00000000
+0,       1410,       1410,        1,  1382400, 0x00000000
+0,       1411,       1411,        1,  1382400, 0x00000000
+0,       1412,       1412,        1,  1382400, 0x00000000
+0,       1413,       1413,        1,  1382400, 0x00000000
+0,       1414,       1414,        1,  1382400, 0x00000000
+0,       1415,       1415,        1,  1382400, 0x00000000
+0,       1416,       1416,        1,  1382400, 0x00000000
+0,       1417,       1417,        1,  1382400, 0x00000000
+0,       1418,       1418,        1,  1382400, 0x00000000
+0,       1419,       1419,        1,  1382400, 0x00000000
+0,       1420,       1420,        1,  1382400, 0x00000000
+0,       1421,       1421,        1,  1382400, 0x00000000
+0,       1422,       1422,        1,  1382400, 0x00000000
+0,       1423,       1423,        1,  1382400, 0x00000000
+0,       1424,       1424,        1,  1382400, 0x00000000
+0,       1425,       1425,        1,  1382400, 0x00000000
+0,       1426,       1426,        1,  1382400, 0x00000000
+0,       1427,       1427,        1,  1382400, 0x00000000
+0,       1428,       1428,        1,  1382400, 0x00000000
+0,       1429,       1429,        1,  1382400, 0x00000000
+0,       1430,       1430,        1,  1382400, 0x00000000
+0,       1431,       1431,        1,  1382400, 0x00000000
+0,       1432,       1432,        1,  1382400, 0x00000000
+0,       1433,       1433,        1,  1382400, 0x00000000
+0,       1434,       1434,        1,  1382400, 0x00000000
+0,       1435,       1435,        1,  1382400, 0x00000000
+0,       1436,       1436,        1,  1382400, 0x00000000
+0,       1437,       1437,        1,  1382400, 0x00000000
+0,       1438,       1438,        1,  1382400, 0x00000000
+0,       1439,       1439,        1,  1382400, 0x00000000
+0,       1440,       1440,        1,  1382400, 0x00000000
+0,       1441,       1441,        1,  1382400, 0x00000000
+0,       1442,       1442,        1,  1382400, 0x00000000
+0,       1443,       1443,        1,  1382400, 0x00000000
+0,       1444,       1444,        1,  1382400, 0x00000000
+0,       1445,       1445,        1,  1382400, 0x00000000
+0,       1446,       1446,        1,  1382400, 0x00000000
+0,       1447,       1447,        1,  1382400, 0x00000000
+0,       1448,       1448,        1,  1382400, 0x00000000
+0,       1449,       1449,        1,  1382400, 0x00000000
+0,       1450,       1450,        1,  1382400, 0x00000000
+0,       1451,       1451,        1,  1382400, 0x00000000
+0,       1452,       1452,        1,  1382400, 0x00000000
+0,       1453,       1453,        1,  1382400, 0x00000000
+0,       1454,       1454,        1,  1382400, 0x00000000
+0,       1455,       1455,        1,  1382400, 0x00000000
+0,       1456,       1456,        1,  1382400, 0x00000000
+0,       1457,       1457,        1,  1382400, 0x00000000
+0,       1458,       1458,        1,  1382400, 0x00000000
+0,       1459,       1459,        1,  1382400, 0x00000000
+0,       1460,       1460,        1,  1382400, 0x00000000
+0,       1461,       1461,        1,  1382400, 0x00000000
+0,       1462,       1462,        1,  1382400, 0x00000000
+0,       1463,       1463,        1,  1382400, 0x00000000
+0,       1464,       1464,        1,  1382400, 0x00000000
+0,       1465,       1465,        1,  1382400, 0x00000000
+0,       1466,       1466,        1,  1382400, 0x00000000
+0,       1467,       1467,        1,  1382400, 0x00000000
+0,       1468,       1468,        1,  1382400, 0x00000000
+0,       1469,       1469,        1,  1382400, 0x00000000
+0,       1470,       1470,        1,  1382400, 0x00000000
+0,       1471,       1471,        1,  1382400, 0x00000000
+0,       1472,       1472,        1,  1382400, 0x00000000
+0,       1473,       1473,        1,  1382400, 0x00000000
+0,       1474,       1474,        1,  1382400, 0x00000000
+0,       1475,       1475,        1,  1382400, 0x00000000
+0,       1477,       1477,        1,  1382400, 0x49518c43
+0,       1477,       1477,        1,  1382400, 0x49518c43
+0,       1478,       1478,        1,  1382400, 0x49518c43
+0,       1479,       1479,        1,  1382400, 0x49518c43
+0,       1480,       1480,        1,  1382400, 0x49518c43
+0,       1481,       1481,        1,  1382400, 0x49518c43
+0,       1482,       1482,        1,  1382400, 0x49518c43
+0,       1483,       1483,        1,  1382400, 0x49518c43
+0,       1484,       1484,        1,  1382400, 0x00000000
+0,       1485,       1485,        1,  1382400, 0x00000000
+0,       1486,       1486,        1,  1382400, 0x00000000
+0,       1487,       1487,        1,  1382400, 0x00000000
+0,       1488,       1488,        1,  1382400, 0x00000000
+0,       1489,       1489,        1,  1382400, 0x00000000
+0,       1490,       1490,        1,  1382400, 0x00000000
+0,       1491,       1491,        1,  1382400, 0x00000000
+0,       1492,       1492,        1,  1382400, 0x00000000
+0,       1493,       1493,        1,  1382400, 0x00000000
+0,       1494,       1494,        1,  1382400, 0x00000000
+0,       1495,       1495,        1,  1382400, 0x00000000
+0,       1496,       1496,        1,  1382400, 0x00000000
+0,       1497,       1497,        1,  1382400, 0x00000000
+0,       1498,       1498,        1,  1382400, 0x00000000
+0,       1500,       1500,        1,  1382400, 0x4a72fa21
+0,       1500,       1500,        1,  1382400, 0x4a72fa21
+0,       1501,       1501,        1,  1382400, 0x4a72fa21
+0,       1502,       1502,        1,  1382400, 0x4a72fa21
+0,       1503,       1503,        1,  1382400, 0x4a72fa21
+0,       1504,       1504,        1,  1382400, 0x4a72fa21
+0,       1505,       1505,        1,  1382400, 0x4a72fa21
+0,       1506,       1506,        1,  1382400, 0x4a72fa21
+0,       1507,       1507,        1,  1382400, 0x4a72fa21
+0,       1508,       1508,        1,  1382400, 0x4a72fa21
+0,       1509,       1509,        1,  1382400, 0x4a72fa21
+0,       1510,       1510,        1,  1382400, 0x00000000
+0,       1511,       1511,        1,  1382400, 0xa82f7de8
+0,       1512,       1512,        1,  1382400, 0xa82f7de8
+0,       1513,       1513,        1,  1382400, 0xa82f7de8
+0,       1514,       1514,        1,  1382400, 0xa82f7de8
+0,       1515,       1515,        1,  1382400, 0xa82f7de8
+0,       1516,       1516,        1,  1382400, 0xa82f7de8
+0,       1517,       1517,        1,  1382400, 0xa82f7de8
+0,       1518,       1518,        1,  1382400, 0x00000000
+0,       1519,       1519,        1,  1382400, 0x00000000
+0,       1521,       1521,        1,  1382400, 0xeba0b5f3
+0,       1521,       1521,        1,  1382400, 0xeba0b5f3
+0,       1522,       1522,        1,  1382400, 0xeba0b5f3
+0,       1523,       1523,        1,  1382400, 0xeba0b5f3
+0,       1524,       1524,        1,  1382400, 0xeba0b5f3
+0,       1525,       1525,        1,  1382400, 0xeba0b5f3
+0,       1526,       1526,        1,  1382400, 0xeba0b5f3
+0,       1527,       1527,        1,  1382400, 0xeba0b5f3
+0,       1528,       1528,        1,  1382400, 0xeba0b5f3
+0,       1530,       1530,        1,  1382400, 0xd6a91770
+0,       1530,       1530,        1,  1382400, 0xd6a91770
+0,       1531,       1531,        1,  1382400, 0xd6a91770
+0,       1532,       1532,        1,  1382400, 0xd6a91770
+0,       1533,       1533,        1,  1382400, 0xd6a91770
+0,       1534,       1534,        1,  1382400, 0xd6a91770
+0,       1535,       1535,        1,  1382400, 0xd6a91770
+0,       1536,       1536,        1,  1382400, 0xd6a91770
+0,       1537,       1537,        1,  1382400, 0xd6a91770
+0,       1538,       1538,        1,  1382400, 0xd6a91770
+0,       1539,       1539,        1,  1382400, 0x00000000
+0,       1540,       1540,        1,  1382400, 0x222f827c
+0,       1541,       1541,        1,  1382400, 0x222f827c
+0,       1542,       1542,        1,  1382400, 0x222f827c
+0,       1543,       1543,        1,  1382400, 0x222f827c
+0,       1544,       1544,        1,  1382400, 0x222f827c
+0,       1545,       1545,        1,  1382400, 0x222f827c
+0,       1546,       1546,        1,  1382400, 0x222f827c
+0,       1547,       1547,        1,  1382400, 0x222f827c
+0,       1548,       1548,        1,  1382400, 0x222f827c
+0,       1549,       1549,        1,  1382400, 0x00000000
+0,       1550,       1550,        1,  1382400, 0x00000000
+0,       1551,       1551,        1,  1382400, 0x00000000
+0,       1552,       1552,        1,  1382400, 0x00000000
+0,       1553,       1553,        1,  1382400, 0x00000000
+0,       1554,       1554,        1,  1382400, 0x00000000
+0,       1555,       1555,        1,  1382400, 0x00000000
+0,       1556,       1556,        1,  1382400, 0x00000000
+0,       1557,       1557,        1,  1382400, 0x00000000
+0,       1558,       1558,        1,  1382400, 0x00000000
+0,       1559,       1559,        1,  1382400, 0x00000000
+0,       1560,       1560,        1,  1382400, 0x00000000
+0,       1561,       1561,        1,  1382400, 0x00000000
+0,       1562,       1562,        1,  1382400, 0x00000000
+0,       1563,       1563,        1,  1382400, 0x00000000
+0,       1564,       1564,        1,  1382400, 0x00000000
+0,       1565,       1565,        1,  1382400, 0x00000000
+0,       1566,       1566,        1,  1382400, 0x00000000
+0,       1567,       1567,        1,  1382400, 0x00000000
+0,       1568,       1568,        1,  1382400, 0x00000000
+0,       1569,       1569,        1,  1382400, 0x00000000
+0,       1570,       1570,        1,  1382400, 0x00000000
+0,       1571,       1571,        1,  1382400, 0x00000000
+0,       1572,       1572,        1,  1382400, 0x00000000
+0,       1573,       1573,        1,  1382400, 0x00000000
+0,       1574,       1574,        1,  1382400, 0x00000000
+0,       1575,       1575,        1,  1382400, 0x00000000
+0,       1576,       1576,        1,  1382400, 0x00000000
+0,       1577,       1577,        1,  1382400, 0x00000000
+0,       1578,       1578,        1,  1382400, 0x00000000
+0,       1579,       1579,        1,  1382400, 0x00000000
+0,       1580,       1580,        1,  1382400, 0x00000000
+0,       1581,       1581,        1,  1382400, 0x00000000
+0,       1582,       1582,        1,  1382400, 0x00000000
+0,       1583,       1583,        1,  1382400, 0x00000000
+0,       1584,       1584,        1,  1382400, 0x00000000
+0,       1585,       1585,        1,  1382400, 0x00000000
+0,       1586,       1586,        1,  1382400, 0x00000000
+0,       1587,       1587,        1,  1382400, 0x00000000
+0,       1588,       1588,        1,  1382400, 0x00000000
+0,       1589,       1589,        1,  1382400, 0x00000000
+0,       1590,       1590,        1,  1382400, 0x00000000
+0,       1591,       1591,        1,  1382400, 0x00000000
+0,       1592,       1592,        1,  1382400, 0x00000000
+0,       1593,       1593,        1,  1382400, 0x00000000
+0,       1594,       1594,        1,  1382400, 0x00000000
+0,       1595,       1595,        1,  1382400, 0x00000000
+0,       1596,       1596,        1,  1382400, 0x00000000
+0,       1597,       1597,        1,  1382400, 0x00000000
+0,       1598,       1598,        1,  1382400, 0x00000000
+0,       1599,       1599,        1,  1382400, 0x00000000
+0,       1600,       1600,        1,  1382400, 0x00000000
+0,       1601,       1601,        1,  1382400, 0x00000000
+0,       1602,       1602,        1,  1382400, 0x00000000
+0,       1603,       1603,        1,  1382400, 0x00000000
+0,       1604,       1604,        1,  1382400, 0x00000000
+0,       1606,       1606,        1,  1382400, 0x3270f4ff
+0,       1606,       1606,        1,  1382400, 0x3270f4ff
+0,       1607,       1607,        1,  1382400, 0x3270f4ff
+0,       1608,       1608,        1,  1382400, 0x3270f4ff
+0,       1609,       1609,        1,  1382400, 0x3270f4ff
+0,       1610,       1610,        1,  1382400, 0x3270f4ff
+0,       1611,       1611,        1,  1382400, 0x3270f4ff
+0,       1612,       1612,        1,  1382400, 0x3270f4ff
+0,       1613,       1613,        1,  1382400, 0x3270f4ff
+0,       1614,       1614,        1,  1382400, 0x3270f4ff
+0,       1615,       1615,        1,  1382400, 0x3270f4ff
+0,       1617,       1617,        1,  1382400, 0x40813cb3
+0,       1617,       1617,        1,  1382400, 0x40813cb3
+0,       1618,       1618,        1,  1382400, 0x40813cb3
+0,       1619,       1619,        1,  1382400, 0x40813cb3
+0,       1620,       1620,        1,  1382400, 0x40813cb3
+0,       1621,       1621,        1,  1382400, 0x40813cb3
+0,       1622,       1622,        1,  1382400, 0x40813cb3
+0,       1623,       1623,        1,  1382400, 0x9d8fde41
+0,       1624,       1624,        1,  1382400, 0x9d8fde41
+0,       1625,       1625,        1,  1382400, 0x9d8fde41
+0,       1626,       1626,        1,  1382400, 0x9d8fde41
+0,       1627,       1627,        1,  1382400, 0x9d8fde41
+0,       1628,       1628,        1,  1382400, 0x9d8fde41
+0,       1629,       1629,        1,  1382400, 0x9d8fde41
+0,       1630,       1630,        1,  1382400, 0x9d8fde41
+0,       1631,       1631,        1,  1382400, 0x9d8fde41
+0,       1632,       1632,        1,  1382400, 0x00000000
+0,       1633,       1633,        1,  1382400, 0x00000000
+0,       1634,       1634,        1,  1382400, 0x00000000
+0,       1636,       1636,        1,  1382400, 0xc6d7a701
+0,       1636,       1636,        1,  1382400, 0xc6d7a701
+0,       1637,       1637,        1,  1382400, 0xc6d7a701
+0,       1638,       1638,        1,  1382400, 0xc6d7a701
+0,       1639,       1639,        1,  1382400, 0xc6d7a701
+0,       1640,       1640,        1,  1382400, 0xc6d7a701
+0,       1642,       1642,        1,  1382400, 0x9d45f2dc
+0,       1642,       1642,        1,  1382400, 0x9d45f2dc
+0,       1643,       1643,        1,  1382400, 0x9d45f2dc
+0,       1644,       1644,        1,  1382400, 0x9d45f2dc
+0,       1645,       1645,        1,  1382400, 0x9d45f2dc
+0,       1646,       1646,        1,  1382400, 0x9d45f2dc
+0,       1647,       1647,        1,  1382400, 0x9d45f2dc
+0,       1648,       1648,        1,  1382400, 0x9d45f2dc
+0,       1649,       1649,        1,  1382400, 0x00000000
+0,       1650,       1650,        1,  1382400, 0x8525ee40
+0,       1651,       1651,        1,  1382400, 0x8525ee40
+0,       1652,       1652,        1,  1382400, 0x8525ee40
+0,       1653,       1653,        1,  1382400, 0x8525ee40
+0,       1654,       1654,        1,  1382400, 0x8525ee40
+0,       1655,       1655,        1,  1382400, 0x8525ee40
+0,       1656,       1656,        1,  1382400, 0x5b26b98b
+0,       1657,       1657,        1,  1382400, 0x5b26b98b
+0,       1658,       1658,        1,  1382400, 0x5b26b98b
+0,       1659,       1659,        1,  1382400, 0x5b26b98b
+0,       1660,       1660,        1,  1382400, 0x5b26b98b
+0,       1661,       1661,        1,  1382400, 0x5b26b98b
+0,       1662,       1662,        1,  1382400, 0x5b26b98b
+0,       1663,       1663,        1,  1382400, 0x5b26b98b
+0,       1664,       1664,        1,  1382400, 0x00000000
+0,       1665,       1665,        1,  1382400, 0x51be311f
+0,       1666,       1666,        1,  1382400, 0x51be311f
+0,       1667,       1667,        1,  1382400, 0x51be311f
+0,       1668,       1668,        1,  1382400, 0x51be311f
+0,       1669,       1669,        1,  1382400, 0x51be311f
+0,       1670,       1670,        1,  1382400, 0x51be311f
+0,       1671,       1671,        1,  1382400, 0x51be311f
+0,       1672,       1672,        1,  1382400, 0x51be311f
+0,       1673,       1673,        1,  1382400, 0x00000000
+0,       1674,       1674,        1,  1382400, 0x00000000
+0,       1675,       1675,        1,  1382400, 0x00000000
+0,       1676,       1676,        1,  1382400, 0x00000000
+0,       1677,       1677,        1,  1382400, 0x00000000
+0,       1678,       1678,        1,  1382400, 0x00000000
+0,       1679,       1679,        1,  1382400, 0x00000000
+0,       1680,       1680,        1,  1382400, 0x00000000
+0,       1681,       1681,        1,  1382400, 0x00000000
+0,       1682,       1682,        1,  1382400, 0x00000000
+0,       1683,       1683,        1,  1382400, 0x00000000
+0,       1684,       1684,        1,  1382400, 0x00000000
+0,       1685,       1685,        1,  1382400, 0x00000000
+0,       1686,       1686,        1,  1382400, 0x00000000
+0,       1687,       1687,        1,  1382400, 0x00000000
+0,       1688,       1688,        1,  1382400, 0x00000000
+0,       1689,       1689,        1,  1382400, 0x00000000
+0,       1690,       1690,        1,  1382400, 0x00000000
+0,       1691,       1691,        1,  1382400, 0x00000000
+0,       1692,       1692,        1,  1382400, 0x00000000
+0,       1693,       1693,        1,  1382400, 0x00000000
+0,       1694,       1694,        1,  1382400, 0x00000000
+0,       1695,       1695,        1,  1382400, 0x00000000
+0,       1696,       1696,        1,  1382400, 0x00000000
+0,       1697,       1697,        1,  1382400, 0x00000000
+0,       1698,       1698,        1,  1382400, 0x00000000
+0,       1699,       1699,        1,  1382400, 0x00000000
+0,       1700,       1700,        1,  1382400, 0x00000000
+0,       1701,       1701,        1,  1382400, 0x00000000
+0,       1702,       1702,        1,  1382400, 0x00000000
+0,       1703,       1703,        1,  1382400, 0x00000000
+0,       1704,       1704,        1,  1382400, 0x00000000
+0,       1705,       1705,        1,  1382400, 0x00000000
+0,       1706,       1706,        1,  1382400, 0x00000000
+0,       1707,       1707,        1,  1382400, 0x00000000
+0,       1708,       1708,        1,  1382400, 0x00000000
+0,       1709,       1709,        1,  1382400, 0x00000000
+0,       1710,       1710,        1,  1382400, 0x00000000
+0,       1711,       1711,        1,  1382400, 0x00000000
+0,       1713,       1713,        1,  1382400, 0x00a4f2a3
+0,       1713,       1713,        1,  1382400, 0x00a4f2a3
+0,       1714,       1714,        1,  1382400, 0x00a4f2a3
+0,       1715,       1715,        1,  1382400, 0x00a4f2a3
+0,       1716,       1716,        1,  1382400, 0x00a4f2a3
+0,       1717,       1717,        1,  1382400, 0x00a4f2a3
+0,       1718,       1718,        1,  1382400, 0x00a4f2a3
+0,       1719,       1719,        1,  1382400, 0x00a4f2a3
+0,       1720,       1720,        1,  1382400, 0x00a4f2a3
+0,       1721,       1721,        1,  1382400, 0x00a4f2a3
+0,       1722,       1722,        1,  1382400, 0x00000000
+0,       1723,       1723,        1,  1382400, 0x00000000
+0,       1724,       1724,        1,  1382400, 0x00000000
+0,       1725,       1725,        1,  1382400, 0x00000000
+0,       1726,       1726,        1,  1382400, 0x00000000
+0,       1727,       1727,        1,  1382400, 0x00000000
+0,       1728,       1728,        1,  1382400, 0x00000000
+0,       1729,       1729,        1,  1382400, 0x00000000
+0,       1730,       1730,        1,  1382400, 0x00000000
+0,       1731,       1731,        1,  1382400, 0x00000000
+0,       1732,       1732,        1,  1382400, 0x00000000
+0,       1734,       1734,        1,  1382400, 0x40a445e8
+0,       1734,       1734,        1,  1382400, 0x40a445e8
+0,       1735,       1735,        1,  1382400, 0x40a445e8
+0,       1736,       1736,        1,  1382400, 0x40a445e8
+0,       1737,       1737,        1,  1382400, 0x40a445e8
+0,       1738,       1738,        1,  1382400, 0x40a445e8
+0,       1739,       1739,        1,  1382400, 0x40a445e8
+0,       1740,       1740,        1,  1382400, 0x40a445e8
+0,       1741,       1741,        1,  1382400, 0x40a445e8
+0,       1742,       1742,        1,  1382400, 0x00000000
+0,       1743,       1743,        1,  1382400, 0x00000000
+0,       1744,       1744,        1,  1382400, 0x00000000
+0,       1745,       1745,        1,  1382400, 0x00000000
+0,       1746,       1746,        1,  1382400, 0x00000000
+0,       1747,       1747,        1,  1382400, 0x00000000
+0,       1748,       1748,        1,  1382400, 0x00000000
+0,       1749,       1749,        1,  1382400, 0x00000000
+0,       1750,       1750,        1,  1382400, 0x00000000
+0,       1751,       1751,        1,  1382400, 0x00000000
+0,       1752,       1752,        1,  1382400, 0x00000000
+0,       1753,       1753,        1,  1382400, 0x00000000
+0,       1754,       1754,        1,  1382400, 0x00000000
+0,       1755,       1755,        1,  1382400, 0x00000000
+0,       1756,       1756,        1,  1382400, 0x00000000
+0,       1757,       1757,        1,  1382400, 0x00000000
+0,       1758,       1758,        1,  1382400, 0x00000000
+0,       1759,       1759,        1,  1382400, 0x00000000
+0,       1760,       1760,        1,  1382400, 0x00000000
+0,       1761,       1761,        1,  1382400, 0x00000000
+0,       1762,       1762,        1,  1382400, 0x00000000
+0,       1763,       1763,        1,  1382400, 0x00000000
+0,       1764,       1764,        1,  1382400, 0x00000000
+0,       1765,       1765,        1,  1382400, 0x00000000
+0,       1766,       1766,        1,  1382400, 0x00000000
+0,       1767,       1767,        1,  1382400, 0x00000000
+0,       1768,       1768,        1,  1382400, 0x00000000
+0,       1769,       1769,        1,  1382400, 0x00000000
+0,       1770,       1770,        1,  1382400, 0x00000000
+0,       1771,       1771,        1,  1382400, 0x00000000
+0,       1772,       1772,        1,  1382400, 0x00000000
+0,       1773,       1773,        1,  1382400, 0x00000000
+0,       1774,       1774,        1,  1382400, 0x00000000
+0,       1775,       1775,        1,  1382400, 0x00000000
+0,       1776,       1776,        1,  1382400, 0x00000000
+0,       1777,       1777,        1,  1382400, 0x00000000
+0,       1778,       1778,        1,  1382400, 0x00000000
+0,       1779,       1779,        1,  1382400, 0x00000000
+0,       1780,       1780,        1,  1382400, 0x00000000
+0,       1781,       1781,        1,  1382400, 0x00000000
+0,       1782,       1782,        1,  1382400, 0x00000000
+0,       1783,       1783,        1,  1382400, 0x00000000
+0,       1784,       1784,        1,  1382400, 0x00000000
+0,       1785,       1785,        1,  1382400, 0x00000000
+0,       1786,       1786,        1,  1382400, 0x00000000
+0,       1788,       1788,        1,  1382400, 0x43ef5128
+0,       1788,       1788,        1,  1382400, 0x43ef5128
+0,       1789,       1789,        1,  1382400, 0x43ef5128
+0,       1790,       1790,        1,  1382400, 0x43ef5128
+0,       1791,       1791,        1,  1382400, 0x43ef5128
+0,       1792,       1792,        1,  1382400, 0x43ef5128
+0,       1793,       1793,        1,  1382400, 0x43ef5128
+0,       1794,       1794,        1,  1382400, 0x43ef5128
+0,       1795,       1795,        1,  1382400, 0x43ef5128
+0,       1796,       1796,        1,  1382400, 0x43ef5128
+0,       1797,       1797,        1,  1382400, 0x43ef5128
+0,       1798,       1798,        1,  1382400, 0x43ef5128
+0,       1799,       1799,        1,  1382400, 0x3c3e3819
+0,       1800,       1800,        1,  1382400, 0x3c3e3819
+0,       1801,       1801,        1,  1382400, 0x3c3e3819
+0,       1802,       1802,        1,  1382400, 0x3c3e3819
+0,       1803,       1803,        1,  1382400, 0x3c3e3819
+0,       1804,       1804,        1,  1382400, 0x3c3e3819
+0,       1805,       1805,        1,  1382400, 0x00000000
diff --git a/tests/ref/fate/sub2video_time_limited b/tests/ref/fate/sub2video_time_limited
index 9fb6fb06f9..3a7cca384a 100644
--- a/tests/ref/fate/sub2video_time_limited
+++ b/tests/ref/fate/sub2video_time_limited
@@ -1,8 +1,80 @@
-#tb 0: 1/25
+#tb 0: 1/5
 #media_type 0: video
 #codec_id 0: rawvideo
 #dimensions 0: 1920x1080
-#sar 0: 0/1
-0,          2,          2,        1,  8294400, 0x00000000
+#sar 0: 1/1
+0,          0,          0,        1,  8294400, 0x00000000
+0,          1,          1,        1,  8294400, 0x00000000
 0,          2,          2,        1,  8294400, 0xa87c518f
+0,          3,          3,        1,  8294400, 0xa87c518f
+0,          4,          4,        1,  8294400, 0xa87c518f
+0,          5,          5,        1,  8294400, 0xa87c518f
+0,          6,          6,        1,  8294400, 0xa87c518f
+0,          7,          7,        1,  8294400, 0xa87c518f
+0,          8,          8,        1,  8294400, 0xa87c518f
+0,          9,          9,        1,  8294400, 0xa87c518f
 0,         10,         10,        1,  8294400, 0xa87c518f
+0,         11,         11,        1,  8294400, 0xa87c518f
+0,         12,         12,        1,  8294400, 0xa87c518f
+0,         13,         13,        1,  8294400, 0xa87c518f
+0,         14,         14,        1,  8294400, 0xa87c518f
+0,         15,         15,        1,  8294400, 0xa87c518f
+0,         16,         16,        1,  8294400, 0xa87c518f
+0,         17,         17,        1,  8294400, 0xa87c518f
+0,         18,         18,        1,  8294400, 0xa87c518f
+0,         19,         19,        1,  8294400, 0xa87c518f
+0,         20,         20,        1,  8294400, 0xa87c518f
+0,         21,         21,        1,  8294400, 0xa87c518f
+0,         22,         22,        1,  8294400, 0xa87c518f
+0,         23,         23,        1,  8294400, 0xa87c518f
+0,         24,         24,        1,  8294400, 0xa87c518f
+0,         25,         25,        1,  8294400, 0xa87c518f
+0,         26,         26,        1,  8294400, 0xa87c518f
+0,         27,         27,        1,  8294400, 0xa87c518f
+0,         28,         28,        1,  8294400, 0xa87c518f
+0,         29,         29,        1,  8294400, 0xa87c518f
+0,         30,         30,        1,  8294400, 0xa87c518f
+0,         31,         31,        1,  8294400, 0xa87c518f
+0,         32,         32,        1,  8294400, 0xa87c518f
+0,         33,         33,        1,  8294400, 0xa87c518f
+0,         34,         34,        1,  8294400, 0xa87c518f
+0,         35,         35,        1,  8294400, 0xa87c518f
+0,         36,         36,        1,  8294400, 0xa87c518f
+0,         37,         37,        1,  8294400, 0xa87c518f
+0,         38,         38,        1,  8294400, 0xa87c518f
+0,         39,         39,        1,  8294400, 0xa87c518f
+0,         40,         40,        1,  8294400, 0xa87c518f
+0,         41,         41,        1,  8294400, 0xa87c518f
+0,         42,         42,        1,  8294400, 0xa87c518f
+0,         43,         43,        1,  8294400, 0xa87c518f
+0,         44,         44,        1,  8294400, 0xa87c518f
+0,         45,         45,        1,  8294400, 0xa87c518f
+0,         46,         46,        1,  8294400, 0xa87c518f
+0,         47,         47,        1,  8294400, 0xa87c518f
+0,         48,         48,        1,  8294400, 0xa87c518f
+0,         49,         49,        1,  8294400, 0xa87c518f
+0,         50,         50,        1,  8294400, 0xa87c518f
+0,         51,         51,        1,  8294400, 0xa87c518f
+0,         52,         52,        1,  8294400, 0xa87c518f
+0,         53,         53,        1,  8294400, 0xa87c518f
+0,         54,         54,        1,  8294400, 0xa87c518f
+0,         55,         55,        1,  8294400, 0xa87c518f
+0,         56,         56,        1,  8294400, 0xa87c518f
+0,         57,         57,        1,  8294400, 0xa87c518f
+0,         58,         58,        1,  8294400, 0xa87c518f
+0,         59,         59,        1,  8294400, 0xa87c518f
+0,         60,         60,        1,  8294400, 0xa87c518f
+0,         61,         61,        1,  8294400, 0xa87c518f
+0,         62,         62,        1,  8294400, 0xa87c518f
+0,         63,         63,        1,  8294400, 0xa87c518f
+0,         64,         64,        1,  8294400, 0xa87c518f
+0,         65,         65,        1,  8294400, 0xa87c518f
+0,         66,         66,        1,  8294400, 0xa87c518f
+0,         67,         67,        1,  8294400, 0xa87c518f
+0,         68,         68,        1,  8294400, 0xa87c518f
+0,         69,         69,        1,  8294400, 0xa87c518f
+0,         70,         70,        1,  8294400, 0xa87c518f
+0,         71,         71,        1,  8294400, 0xa87c518f
+0,         72,         72,        1,  8294400, 0xa87c518f
+0,         73,         73,        1,  8294400, 0xa87c518f
+0,         74,         74,        1,  8294400, 0xa87c518f
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v2 22/26] avutil/ass_split: Add parsing of hard-space tags (\h)
  2022-01-20  2:48 ` [FFmpeg-devel] [PATCH v2 00/26] Subtitle Filtering 2022 ffmpegagent
                     ` (20 preceding siblings ...)
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 21/26] fftools/ffmpeg: Introduce subtitle filtering and new frame-based subtitle encoding ffmpegagent
@ 2022-01-20  2:48   ` ffmpegagent
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 23/26] avcodec/webvttenc: convert hard-space tags to &nbsp; ffmpegagent
                     ` (4 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  2:48 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

The \h tag in ASS/SSA is indicating a non-breaking space. See
https://github.com/Aegisub/aegisite/blob/master/source/docs/3.2/
ASS_Tags.html.md

The ass_split implementation is used by almost all text subtitle
encoders and it didn't handle this tag. Interestingly, several
tests are testing for \h parsing and had incorrect reference data
for those tests.

The \h tag is specific to ASS and doesn't have any meaning outside
of ASS.
Still, the reference data for ttmlenc, textenc and webvttenc were
full of \h tags even though this tag doesn't have a meaning there.

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavutil/ass_split.c            |  7 +++++++
 tests/ref/fate/.gitattributes    |  3 +++
 tests/ref/fate/mov-mp4-ttml-dfxp |  8 ++++----
 tests/ref/fate/mov-mp4-ttml-stpp |  8 ++++----
 tests/ref/fate/sub-textenc       | 10 +++++-----
 tests/ref/fate/sub-ttmlenc       |  8 ++++----
 tests/ref/fate/sub-webvttenc     | 10 +++++-----
 7 files changed, 32 insertions(+), 22 deletions(-)
 create mode 100644 tests/ref/fate/.gitattributes

diff --git a/libavutil/ass_split.c b/libavutil/ass_split.c
index c5963351fc..30512dfc74 100644
--- a/libavutil/ass_split.c
+++ b/libavutil/ass_split.c
@@ -484,6 +484,7 @@ int avpriv_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *pr
     while (buf && *buf) {
         if (text && callbacks->text &&
             (sscanf(buf, "\\%1[nN]", new_line) == 1 ||
+             sscanf(buf, "\\%1[hH]", new_line) == 1 ||
              !strncmp(buf, "{\\", 2))) {
             callbacks->text(priv, text, text_len);
             text = NULL;
@@ -492,6 +493,12 @@ int avpriv_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *pr
             if (callbacks->new_line)
                 callbacks->new_line(priv, new_line[0] == 'N');
             buf += 2;
+        } else if (sscanf(buf, "\\%1[hH]", new_line) == 1) {
+            if (callbacks->hard_space)
+                callbacks->hard_space(priv);
+            else if (callbacks->text)
+                callbacks->text(priv, " ", 1);
+            buf += 2;
         } else if (!strncmp(buf, "{\\", 2)) {
             buf++;
             while (*buf == '\\') {
diff --git a/tests/ref/fate/.gitattributes b/tests/ref/fate/.gitattributes
new file mode 100644
index 0000000000..19be64d085
--- /dev/null
+++ b/tests/ref/fate/.gitattributes
@@ -0,0 +1,3 @@
+sub-textenc -diff
+sub-ttmlenc -diff
+sub-webvttenc -diff
diff --git a/tests/ref/fate/mov-mp4-ttml-dfxp b/tests/ref/fate/mov-mp4-ttml-dfxp
index e24b5d618b..e565ffa1f6 100644
--- a/tests/ref/fate/mov-mp4-ttml-dfxp
+++ b/tests/ref/fate/mov-mp4-ttml-dfxp
@@ -1,9 +1,9 @@
-2e7e01c821c111466e7a2844826b7f6d *tests/data/fate/mov-mp4-ttml-dfxp.mp4
-8519 tests/data/fate/mov-mp4-ttml-dfxp.mp4
+658884e1b789e75c454b25bdf71283c9 *tests/data/fate/mov-mp4-ttml-dfxp.mp4
+8486 tests/data/fate/mov-mp4-ttml-dfxp.mp4
 #tb 0: 1/1000
 #media_type 0: data
 #codec_id 0: none
-0,          0,          0,    68500,     7866, 0x456c36b7
+0,          0,          0,    68500,     7833, 0x31b22193
 {
     "packets": [
         {
@@ -15,7 +15,7 @@
             "dts_time": "0.000000",
             "duration": 68500,
             "duration_time": "68.500000",
-            "size": "7866",
+            "size": "7833",
             "pos": "44",
             "flags": "K_"
         }
diff --git a/tests/ref/fate/mov-mp4-ttml-stpp b/tests/ref/fate/mov-mp4-ttml-stpp
index 77bd23b7bf..f25b5b2d28 100644
--- a/tests/ref/fate/mov-mp4-ttml-stpp
+++ b/tests/ref/fate/mov-mp4-ttml-stpp
@@ -1,9 +1,9 @@
-cbd2c7ff864a663b0d893deac5a0caec *tests/data/fate/mov-mp4-ttml-stpp.mp4
-8547 tests/data/fate/mov-mp4-ttml-stpp.mp4
+c9570de0ccebc858b0c662a7e449582c *tests/data/fate/mov-mp4-ttml-stpp.mp4
+8514 tests/data/fate/mov-mp4-ttml-stpp.mp4
 #tb 0: 1/1000
 #media_type 0: data
 #codec_id 0: none
-0,          0,          0,    68500,     7866, 0x456c36b7
+0,          0,          0,    68500,     7833, 0x31b22193
 {
     "packets": [
         {
@@ -15,7 +15,7 @@ cbd2c7ff864a663b0d893deac5a0caec *tests/data/fate/mov-mp4-ttml-stpp.mp4
             "dts_time": "0.000000",
             "duration": 68500,
             "duration_time": "68.500000",
-            "size": "7866",
+            "size": "7833",
             "pos": "44",
             "flags": "K_"
         }
diff --git a/tests/ref/fate/sub-textenc b/tests/ref/fate/sub-textenc
index 3ea56b38f0..910ca3d6e3 100644
--- a/tests/ref/fate/sub-textenc
+++ b/tests/ref/fate/sub-textenc
@@ -160,18 +160,18 @@ but show this: {normal text}
 \ N is a forced line break
 \ h is a hard space
 Normal spaces at the start and at the end of the line are trimmed while hard spaces are not trimmed.
-The\hline\hwill\hnever\hbreak\hautomatically\hright\hbefore\hor\hafter\ha\hhard\hspace.\h:-D
+The line will never break automatically right before or after a hard space. :-D
 
 31
 00:00:54,501 --> 00:00:56,500
 
-\h\h\h\h\hA (05 hard spaces followed by a letter)
+     A (05 hard spaces followed by a letter)
 A (Normal  spaces followed by a letter)
 A (No hard spaces followed by a letter)
 
 32
 00:00:56,501 --> 00:00:58,500
-\h\h\h\h\hA (05 hard spaces followed by a letter)
+     A (05 hard spaces followed by a letter)
 A (Normal  spaces followed by a letter)
 A (No hard spaces followed by a letter)
 Show this: \TEST and this: \-)
@@ -179,10 +179,10 @@ Show this: \TEST and this: \-)
 33
 00:00:58,501 --> 00:01:00,500
 
-A letter followed by 05 hard spaces: A\h\h\h\h\h
+A letter followed by 05 hard spaces: A     
 A letter followed by normal  spaces: A
 A letter followed by no hard spaces: A
-05 hard  spaces between letters: A\h\h\h\h\hA
+05 hard  spaces between letters: A     A
 5 normal spaces between letters: A     A
 
 ^--Forced line break
diff --git a/tests/ref/fate/sub-ttmlenc b/tests/ref/fate/sub-ttmlenc
index 4df8f8796f..aea09bb31e 100644
--- a/tests/ref/fate/sub-ttmlenc
+++ b/tests/ref/fate/sub-ttmlenc
@@ -109,16 +109,16 @@
         end="00:00:54.500"><span region="Default">Hide these tags:<br/>also hide these tags:<br/>but show this: {normal text}</span></p>
       <p
         begin="00:00:54.501"
-        end="00:01:00.500"><span region="Default"><br/>\ N is a forced line break<br/>\ h is a hard space<br/>Normal spaces at the start and at the end of the line are trimmed while hard spaces are not trimmed.<br/>The\hline\hwill\hnever\hbreak\hautomatically\hright\hbefore\hor\hafter\ha\hhard\hspace.\h:-D</span></p>
+        end="00:01:00.500"><span region="Default"><br/>\ N is a forced line break<br/>\ h is a hard space<br/>Normal spaces at the start and at the end of the line are trimmed while hard spaces are not trimmed.<br/>The line will never break automatically right before or after a hard space. :-D</span></p>
       <p
         begin="00:00:54.501"
-        end="00:00:56.500"><span region="Default"><br/>\h\h\h\h\hA (05 hard spaces followed by a letter)<br/>A (Normal  spaces followed by a letter)<br/>A (No hard spaces followed by a letter)</span></p>
+        end="00:00:56.500"><span region="Default"><br/>     A (05 hard spaces followed by a letter)<br/>A (Normal  spaces followed by a letter)<br/>A (No hard spaces followed by a letter)</span></p>
       <p
         begin="00:00:56.501"
-        end="00:00:58.500"><span region="Default">\h\h\h\h\hA (05 hard spaces followed by a letter)<br/>A (Normal  spaces followed by a letter)<br/>A (No hard spaces followed by a letter)<br/>Show this: \TEST and this: \-)</span></p>
+        end="00:00:58.500"><span region="Default">     A (05 hard spaces followed by a letter)<br/>A (Normal  spaces followed by a letter)<br/>A (No hard spaces followed by a letter)<br/>Show this: \TEST and this: \-)</span></p>
       <p
         begin="00:00:58.501"
-        end="00:01:00.500"><span region="Default"><br/>A letter followed by 05 hard spaces: A\h\h\h\h\h<br/>A letter followed by normal  spaces: A<br/>A letter followed by no hard spaces: A<br/>05 hard  spaces between letters: A\h\h\h\h\hA<br/>5 normal spaces between letters: A     A<br/><br/>^--Forced line break</span></p>
+        end="00:01:00.500"><span region="Default"><br/>A letter followed by 05 hard spaces: A     <br/>A letter followed by normal  spaces: A<br/>A letter followed by no hard spaces: A<br/>05 hard  spaces between letters: A     A<br/>5 normal spaces between letters: A     A<br/><br/>^--Forced line break</span></p>
       <p
         begin="00:01:00.501"
         end="00:01:02.500"><span region="Default">Both line should be strikethrough,<br/>yes.<br/>Correctly closed tags<br/>should be hidden.</span></p>
diff --git a/tests/ref/fate/sub-webvttenc b/tests/ref/fate/sub-webvttenc
index 45ae0b6131..f4172dcc84 100644
--- a/tests/ref/fate/sub-webvttenc
+++ b/tests/ref/fate/sub-webvttenc
@@ -132,26 +132,26 @@ but show this: {normal text}
 \ N is a forced line break
 \ h is a hard space
 Normal spaces at the start and at the end of the line are trimmed while hard spaces are not trimmed.
-The\hline\hwill\hnever\hbreak\hautomatically\hright\hbefore\hor\hafter\ha\hhard\hspace.\h:-D
+The line will never break automatically right before or after a hard space. :-D
 
 00:54.501 --> 00:56.500
 
-\h\h\h\h\hA (05 hard spaces followed by a letter)
+     A (05 hard spaces followed by a letter)
 A (Normal  spaces followed by a letter)
 A (No hard spaces followed by a letter)
 
 00:56.501 --> 00:58.500
-\h\h\h\h\hA (05 hard spaces followed by a letter)
+     A (05 hard spaces followed by a letter)
 A (Normal  spaces followed by a letter)
 A (No hard spaces followed by a letter)
 Show this: \TEST and this: \-)
 
 00:58.501 --> 01:00.500
 
-A letter followed by 05 hard spaces: A\h\h\h\h\h
+A letter followed by 05 hard spaces: A     
 A letter followed by normal  spaces: A
 A letter followed by no hard spaces: A
-05 hard  spaces between letters: A\h\h\h\h\hA
+05 hard  spaces between letters: A     A
 5 normal spaces between letters: A     A
 
 ^--Forced line break
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v2 23/26] avcodec/webvttenc: convert hard-space tags to &nbsp;
  2022-01-20  2:48 ` [FFmpeg-devel] [PATCH v2 00/26] Subtitle Filtering 2022 ffmpegagent
                     ` (21 preceding siblings ...)
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 22/26] avutil/ass_split: Add parsing of hard-space tags (\h) ffmpegagent
@ 2022-01-20  2:48   ` ffmpegagent
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 24/26] doc/APIchanges: update for subtitle filtering changes ffmpegagent
                     ` (3 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  2:48 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/webvttenc.c       |  6 ++++++
 tests/ref/fate/sub-webvttenc | 10 +++++-----
 2 files changed, 11 insertions(+), 5 deletions(-)

diff --git a/libavcodec/webvttenc.c b/libavcodec/webvttenc.c
index c0436f5739..48945dcb8e 100644
--- a/libavcodec/webvttenc.c
+++ b/libavcodec/webvttenc.c
@@ -123,6 +123,11 @@ static void webvtt_new_line_cb(void *priv, int forced)
     webvtt_print(priv, "\n");
 }
 
+static void webvtt_hard_space_cb(void *priv)
+{
+    webvtt_print(priv, "&nbsp;");
+}
+
 static void webvtt_style_cb(void *priv, char style, int close)
 {
     if (style == 's') // strikethrough unsupported
@@ -147,6 +152,7 @@ static void webvtt_end_cb(void *priv)
 static const ASSCodesCallbacks webvtt_callbacks = {
     .text             = webvtt_text_cb,
     .new_line         = webvtt_new_line_cb,
+    .hard_space       = webvtt_hard_space_cb,
     .style            = webvtt_style_cb,
     .color            = NULL,
     .font_name        = NULL,
diff --git a/tests/ref/fate/sub-webvttenc b/tests/ref/fate/sub-webvttenc
index f4172dcc84..ee9de2859e 100644
--- a/tests/ref/fate/sub-webvttenc
+++ b/tests/ref/fate/sub-webvttenc
@@ -132,26 +132,26 @@ but show this: {normal text}
 \ N is a forced line break
 \ h is a hard space
 Normal spaces at the start and at the end of the line are trimmed while hard spaces are not trimmed.
-The line will never break automatically right before or after a hard space. :-D
+The&nbsp;line&nbsp;will&nbsp;never&nbsp;break&nbsp;automatically&nbsp;right&nbsp;before&nbsp;or&nbsp;after&nbsp;a&nbsp;hard&nbsp;space.&nbsp;:-D
 
 00:54.501 --> 00:56.500
 
-     A (05 hard spaces followed by a letter)
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;A (05 hard spaces followed by a letter)
 A (Normal  spaces followed by a letter)
 A (No hard spaces followed by a letter)
 
 00:56.501 --> 00:58.500
-     A (05 hard spaces followed by a letter)
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;A (05 hard spaces followed by a letter)
 A (Normal  spaces followed by a letter)
 A (No hard spaces followed by a letter)
 Show this: \TEST and this: \-)
 
 00:58.501 --> 01:00.500
 
-A letter followed by 05 hard spaces: A     
+A letter followed by 05 hard spaces: A&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 A letter followed by normal  spaces: A
 A letter followed by no hard spaces: A
-05 hard  spaces between letters: A     A
+05 hard  spaces between letters: A&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;A
 5 normal spaces between letters: A     A
 
 ^--Forced line break
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v2 24/26] doc/APIchanges: update for subtitle filtering changes
  2022-01-20  2:48 ` [FFmpeg-devel] [PATCH v2 00/26] Subtitle Filtering 2022 ffmpegagent
                     ` (22 preceding siblings ...)
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 23/26] avcodec/webvttenc: convert hard-space tags to &nbsp; ffmpegagent
@ 2022-01-20  2:48   ` ffmpegagent
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 25/26] avcodec/webvttenc: Don't encode drawing codes and empty lines ffmpegagent
                     ` (2 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  2:48 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 doc/APIchanges       | 24 ++++++++++++++++++++++++
 libavcodec/version.h |  2 +-
 libavutil/version.h  |  2 +-
 3 files changed, 26 insertions(+), 2 deletions(-)

diff --git a/doc/APIchanges b/doc/APIchanges
index 8df0364e4c..c8238fb008 100644
--- a/doc/APIchanges
+++ b/doc/APIchanges
@@ -14,6 +14,30 @@ libavutil:     2021-04-27
 
 API changes, most recent first:
 
+2021-12-05 - xxxxxxxxxx - lavc 59.15.100 - avcodec.h
+  Deprecate avcodec_encode_subtitle(), use regular encode api now
+
+2021-12-05 - xxxxxxxxxx - lavc 59.15.100 - codec_desc.h
+  Add avcodec_descriptor_get_subtitle_format()
+
+2021-12-05 - xxxxxxxxxx - lavc 59.15.100 - avcodec.h
+  Deprecate avsubtitle_free()
+  Deprecate avcodec_decode_subtitle2(), use regular decode api now
+
+2021-12-05 - xxxxxxxxxx - lavu 57.11.100 - frame.h
+  Add AVMediaType field to AVFrame
+  Add Fields for carrying subtitle data to AVFrame
+  (subtitle_areas, subtitle_header, subtitle_pts, start/end time, etc.)
+  Add av_frame_get_buffer2() and deprecate av_frame_get_buffer()
+
+2021-12-05 - xxxxxxxxxx - lavu 57.11.100 - subfmt.h
+  Add struct AVSubtitleArea (replaces AVSubtitle)
+  Add av_get_subtitle_fmt_name() and av_get_subtitle_fmt()
+
+2021-12-05 - xxxxxxxxxx - lavu 57.11.100 - subfmt.h
+  Add enum AVSubtitleType (moved from lavc), add new values, deprecate existing
+
+2021-11-xx - xxxxxxxxxx - lavfi 8.19.100 - avfilter.h
 2022-01-04 - 78dc21b123e - lavu 57.16.100 - frame.h
   Add AV_FRAME_DATA_DOVI_METADATA.
 
diff --git a/libavcodec/version.h b/libavcodec/version.h
index a46fb05f1a..b5867ad041 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -28,7 +28,7 @@
 #include "libavutil/version.h"
 
 #define LIBAVCODEC_VERSION_MAJOR  59
-#define LIBAVCODEC_VERSION_MINOR  20
+#define LIBAVCODEC_VERSION_MINOR  21
 #define LIBAVCODEC_VERSION_MICRO 100
 
 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
diff --git a/libavutil/version.h b/libavutil/version.h
index 5bf48f6304..168e24f410 100644
--- a/libavutil/version.h
+++ b/libavutil/version.h
@@ -79,7 +79,7 @@
  */
 
 #define LIBAVUTIL_VERSION_MAJOR  57
-#define LIBAVUTIL_VERSION_MINOR  18
+#define LIBAVUTIL_VERSION_MINOR  19
 #define LIBAVUTIL_VERSION_MICRO 100
 
 #define LIBAVUTIL_VERSION_INT   AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v2 25/26] avcodec/webvttenc: Don't encode drawing codes and empty lines
  2022-01-20  2:48 ` [FFmpeg-devel] [PATCH v2 00/26] Subtitle Filtering 2022 ffmpegagent
                     ` (23 preceding siblings ...)
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 24/26] doc/APIchanges: update for subtitle filtering changes ffmpegagent
@ 2022-01-20  2:48   ` ffmpegagent
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 26/26] avcodec/dvbsubdec: Fix conditions for fallback to default resolution ffmpegagent
  2022-01-20  3:25   ` [FFmpeg-devel] [PATCH v3 00/26] Subtitle Filtering 2022 ffmpegagent
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  2:48 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/webvttenc.c | 31 +++++++++++++++++++++++++++----
 1 file changed, 27 insertions(+), 4 deletions(-)

diff --git a/libavcodec/webvttenc.c b/libavcodec/webvttenc.c
index 48945dcb8e..62c4aa7ffd 100644
--- a/libavcodec/webvttenc.c
+++ b/libavcodec/webvttenc.c
@@ -39,6 +39,8 @@ typedef struct {
     int count;
     char stack[WEBVTT_STACK_SIZE];
     int stack_ptr;
+    int has_text;
+    int drawing_scale;
 } WebVTTContext;
 
 #ifdef __GNUC__
@@ -115,17 +117,24 @@ static void webvtt_style_apply(WebVTTContext *s, const char *style)
 static void webvtt_text_cb(void *priv, const char *text, int len)
 {
     WebVTTContext *s = priv;
-    av_bprint_append_data(&s->buffer, text, len);
+    if (!s->drawing_scale) {
+        av_bprint_append_data(&s->buffer, text, len);
+        s->has_text = 1;
+    }
 }
 
 static void webvtt_new_line_cb(void *priv, int forced)
 {
-    webvtt_print(priv, "\n");
+    WebVTTContext *s = priv;
+    if (!s->drawing_scale)
+        webvtt_print(priv, "\n");
 }
 
 static void webvtt_hard_space_cb(void *priv)
 {
-    webvtt_print(priv, "&nbsp;");
+    WebVTTContext *s = priv;
+    if (!s->drawing_scale)
+        webvtt_print(priv, "&nbsp;");
 }
 
 static void webvtt_style_cb(void *priv, char style, int close)
@@ -149,6 +158,12 @@ static void webvtt_end_cb(void *priv)
     webvtt_stack_push_pop(priv, 0, 1);
 }
 
+static void dialog_drawing_mode_cb(void *priv, int scale)
+{
+    WebVTTContext *s = priv;
+    s->drawing_scale = scale;
+}
+
 static const ASSCodesCallbacks webvtt_callbacks = {
     .text             = webvtt_text_cb,
     .new_line         = webvtt_new_line_cb,
@@ -161,6 +176,7 @@ static const ASSCodesCallbacks webvtt_callbacks = {
     .cancel_overrides = webvtt_cancel_overrides_cb,
     .move             = NULL,
     .end              = webvtt_end_cb,
+    .drawing_mode     = dialog_drawing_mode_cb,
 };
 
 static void ensure_ass_context(WebVTTContext* s, const AVFrame* frame)
@@ -211,16 +227,23 @@ static int webvtt_encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
         }
 
         if (ass) {
+            const unsigned saved_len = s->buffer.len;
 
-            if (i > 0)
+            if (i > 0 && s->buffer.len > 0)
                 webvtt_new_line_cb(s, 0);
 
+            s->drawing_scale = 0;
+            s->has_text = 0;
+
             dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
             if (!dialog)
                 return AVERROR(ENOMEM);
             webvtt_style_apply(s, dialog->style);
             avpriv_ass_split_override_codes(&webvtt_callbacks, s, dialog->text);
             avpriv_ass_free_dialog(&dialog);
+
+            if (!s->has_text)
+                s->buffer.len = saved_len;
         }
     }
 
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v2 26/26] avcodec/dvbsubdec: Fix conditions for fallback to default resolution
  2022-01-20  2:48 ` [FFmpeg-devel] [PATCH v2 00/26] Subtitle Filtering 2022 ffmpegagent
                     ` (24 preceding siblings ...)
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 25/26] avcodec/webvttenc: Don't encode drawing codes and empty lines ffmpegagent
@ 2022-01-20  2:48   ` ffmpegagent
  2022-01-20  3:25   ` [FFmpeg-devel] [PATCH v3 00/26] Subtitle Filtering 2022 ffmpegagent
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  2:48 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

The previous code expected a segment of type CLUT definition to exist
in order to accept a set of segments to be complete.
This was an incorrect assumption as the presence of a CLUT segment
is not mandatory.
(version 1.6.1 of the spec is probably a bit more clear about this
than earlier versions: https://www.etsi.org/deliver/etsi_en/
300700_300799/300743/01.06.01_20/en_300743v010601a.pdf)

The flawed condition prevented proper fallback to using the default
resolution for the decoding context.

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/dvbsubdec.c | 51 +++++++++++++++++++++++++-----------------
 1 file changed, 30 insertions(+), 21 deletions(-)

diff --git a/libavcodec/dvbsubdec.c b/libavcodec/dvbsubdec.c
index 0d64c6e71c..3a6259101c 100644
--- a/libavcodec/dvbsubdec.c
+++ b/libavcodec/dvbsubdec.c
@@ -33,7 +33,7 @@
 #define DVBSUB_CLUT_SEGMENT     0x12
 #define DVBSUB_OBJECT_SEGMENT   0x13
 #define DVBSUB_DISPLAYDEFINITION_SEGMENT 0x14
-#define DVBSUB_DISPLAY_SEGMENT  0x80
+#define DVBSUB_END_DISPLAY_SEGMENT  0x80
 
 #define cm (ff_crop_tab + MAX_NEG_CROP)
 
@@ -1620,8 +1620,12 @@ static int dvbsub_decode(AVCodecContext *avctx,
     int segment_length;
     int i;
     int ret = 0;
-    int got_segment = 0;
-    int got_dds = 0;
+    //int got_segment = 0;
+    int got_page = 0;
+    int got_region = 0;
+    int got_object = 0;
+    int got_end_display = 0;
+    int got_displaydef = 0;
 
     ff_dlog(avctx, "DVB sub packet:\n");
 
@@ -1666,34 +1670,28 @@ static int dvbsub_decode(AVCodecContext *avctx,
             switch (segment_type) {
             case DVBSUB_PAGE_SEGMENT:
                 ret = dvbsub_parse_page_segment(avctx, p, segment_length, sub, got_sub_ptr);
-                got_segment |= 1;
+                got_page = 1;
                 break;
             case DVBSUB_REGION_SEGMENT:
                 ret = dvbsub_parse_region_segment(avctx, p, segment_length);
-                got_segment |= 2;
+                got_region = 1;
                 break;
             case DVBSUB_CLUT_SEGMENT:
                 ret = dvbsub_parse_clut_segment(avctx, p, segment_length);
                 if (ret < 0) goto end;
-                got_segment |= 4;
                 break;
             case DVBSUB_OBJECT_SEGMENT:
                 ret = dvbsub_parse_object_segment(avctx, p, segment_length);
-                got_segment |= 8;
+                got_object = 1;
                 break;
             case DVBSUB_DISPLAYDEFINITION_SEGMENT:
                 ret = dvbsub_parse_display_definition_segment(avctx, p,
                                                               segment_length);
-                got_dds = 1;
+                got_displaydef = 1;
                 break;
-            case DVBSUB_DISPLAY_SEGMENT:
+            case DVBSUB_END_DISPLAY_SEGMENT:
                 ret = dvbsub_display_end_segment(avctx, p, segment_length, sub, got_sub_ptr);
-                if (got_segment == 15 && !got_dds && !avctx->width && !avctx->height) {
-                    // Default from ETSI EN 300 743 V1.3.1 (7.2.1)
-                    avctx->width  = 720;
-                    avctx->height = 576;
-                }
-                got_segment |= 16;
+                got_end_display = 1;
                 break;
             default:
                 ff_dlog(avctx, "Subtitling segment type 0x%x, page id %d, length %d\n",
@@ -1706,13 +1704,24 @@ static int dvbsub_decode(AVCodecContext *avctx,
 
         p += segment_length;
     }
-    // Some streams do not send a display segment but if we have all the other
-    // segments then we need no further data.
-    if (got_segment == 15) {
-        av_log(avctx, AV_LOG_DEBUG, "Missing display_end_segment, emulating\n");
-        dvbsub_display_end_segment(avctx, p, 0, sub, got_sub_ptr);
-    }
 
+    // Even though not mandated by the spec, we're imposing a minimum requirement
+    // for a useful packet to have at least one page, region and object segment.
+    if (got_page && got_region && got_object && got_end_display) {
+
+        if (!got_displaydef && !avctx->width && !avctx->height) {
+            // Default from ETSI EN 300 743 V1.3.1 (7.2.1)
+            avctx->width  = 720;
+            avctx->height = 576;
+        }
+
+        // Some streams do not send an end-of-display segment but if we have all the other
+        // segments then we need no further data.
+        if (!got_end_display) {
+            av_log(avctx, AV_LOG_DEBUG, "Missing display_end_segment, emulating\n");
+            dvbsub_display_end_segment(avctx, p, 0, sub, got_sub_ptr);
+        }
+    }
 end:
     if (ret < 0) {
         return ret;
-- 
ffmpeg-codebot
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v3 00/26] Subtitle Filtering 2022
  2022-01-20  2:48 ` [FFmpeg-devel] [PATCH v2 00/26] Subtitle Filtering 2022 ffmpegagent
                     ` (25 preceding siblings ...)
  2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 26/26] avcodec/dvbsubdec: Fix conditions for fallback to default resolution ffmpegagent
@ 2022-01-20  3:25   ` ffmpegagent
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 01/26] avcodec, avutil: Move enum AVSubtitleType to avutil, add new and deprecate old values ffmpegagent
                       ` (26 more replies)
  26 siblings, 27 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  3:25 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt


Subtitle Filtering 2022
=======================

This is a substantial update to the earlier subtitle filtering patch series.
A primary goal has been to address others' concerns as much as possible on
one side and to provide more clarity and control over the way things are
working. Clarity is is specifically important to allow for a better
understanding of the need for a subtitle start pts value that can be
different from the frame's pts value. This is done by refactoring the
subtitle timing fields in AVFrame, adding a frame field to indicate repeated
subtitle frames, and finally the full removal of the heartbeat
functionality, replaced by a new 'subfeed' filter that provides different
modes for arbitrating subtitle frames in a filter graph. Finally, each
subtitle filter's documentation has been amended by a section describing the
filter's timeline behavior (in v3 update).

The update also includes major improvements to graphicsub2text and lots of
other details.

Versioning is restarting at v1 due to the new submission procedure.


v3 - Rebase
===========

due to merge conflicts - apologies.


Changes in v2
=============

 * added .gitattributes file to enforce binary diffs for the test refs that
   cannot be applied when being sent via e-mail
 * perform filter graph re-init due to subtitle "frame size" change only
   when the size was unknown before and not set via -canvas_size
 * overlaytextsubs: Make sure to request frames on the subtitle input
 * avfilter/splitcc: Start parsing cc data on key frames only
 * avcodec/webvttenc: Don't encode ass drawing codes and empty lines
 * stripstyles: fix mem leak
 * gs2t: improve color detection
 * gs2t: empty frames must not be skipped
 * subfeed: fix name
 * textmod: preserve margins
 * added .gitattributes file to enforce binary diffs for the test refs that
   cannot be applied when being sent via e-mail
 * perform filter graph re-init due to subtitle "frame size" change only
   when the size was unknown before and not set via -canvas_size
 * avcodec/dvbsubdec: Fix conditions for fallback to default resolution
 * Made changes suggested by Andreas
 * Fixed failing command line reported by Michael

Changes from previous version v24:


AVFrame
=======

 * Removed sub_start_time The start time is now added to the subtitle
   start_pts during decoding The sub_end_time field is adjusted accordingly
 * Renamed sub_end_time to duration which it is effectively after removing
   the start_time
 * Added a sub-struct 'subtitle_timing' to av frame Contains subtitle_pts
   renamed to 'subtitle_timing.start_pts' and 'subtitle_timing.duration'
 * Change both fields to (fixed) time_base AV_TIMEBASE
 * add repeat_sub field provides a clear indication whether a subtitle frame
   is an actual subtitle event or a repeated subtitle frame in a filter
   graph


Heartbeat Removal
=================

 * completely removed the earlier heartbeat implementation
 * filtering arbitration is now implemented in a new filter: 'subfeed'
 * subfeed will be auto-inserted for compatiblity with sub2video command
   lines
 * the new behavior is not exactly identical to the earlier behavior, but it
   basically allows to achieve the same results


New 'subfeed' Filter
====================

 * a versatile filter for solving all kinds of problems with subtile frame
   flow in filter graphs
 * Can be inserted at any position in a graph
 * Auto-inserted for sub2video command lines (in repeat-mode)
 * Allows duration fixup delay input frames with unknown duration and infer
   duration from start of subsequent frame
 * Provides multiple modes of operation:
   * repeat mode (default) Queues input frames Outputs frames at a fixed
     (configurable) rate Either sends a matching input frame (repeatedly) or
     empty frames otherwise
   * scatter mode similar to repeat mode, but splits input frames by
     duration into small segments with same content
   * forward mode No fixed output rate Useful in combination with duration
     fixup or overlap fixup


ffmpeg Tool Changes
===================

 * delay subtitle output stream initialization (like for audio and video)
   This is needed for example when a format header depends on having
   received an initial frame to derive certain header values from
 * decoding: set subtitle frame size from decoding context
 * re-init graph when subtitle size changes
 * always insert subscale filter for sub2video command lines (to ensure
   correct scaling)


Subtitle Encoding
=================

 * ignore repeated frames for encoding based on repeat_sub field in AVFrame
 * support multi-area encoding for text subtitles Subtitle OCR can create
   multiple areas at different positions. Previously, the texts were always
   squashed into a single area ('subtitle rect'), which was not ideal.
   Multiple text areas are now generally supported:
   * ASS Encoder Changed to use the 'receive_packet' encoding API A single
     frame with multiple text areas will create multiple packets now
   * All other text subtitle encoders A newline is inserted between the text
     from multiple areas


graphicsub2text (OCR)
=====================

 * enhanced preprocessing
   * using elbg algorithm for color quantization
   * detection and removal of text outlines
   * map-based identification of colors per word (text, outline, background)
 * add option for duration fixup
 * add option to dump preprocessing bitmaps
 * Recognize formatting and apply as ASS inline styles
   * per word(!)
   * paragraph alignment
   * positioning
   * font names
   * font size
   * font style (italic, underline, bold)
   * text color, outline color


Other Filter Changes
====================

 * all: Make sure to forward all link properties (time base, frame rate, w,
   h) where appropriate
 * overlaytextsubs: request frames on the subtitle input
 * overlaytextsubs: disable read-order checking
 * overlaytextsubs: improve implementation of render_latest_only
 * overlaytextsubs: ensure equal in/out video formats
 * splitcc: derive framerate from realtime_latency
 * graphicsub2video: implement caching of converted frames
 * graphicsub2video: use 1x1 output frame size as long as subtitle size is
   unknown (0x0)

Plus a dozen of things I forgot..

softworkz (26):
  avcodec,avutil: Move enum AVSubtitleType to avutil, add new and
    deprecate old values
  avutil/frame: Prepare AVFrame for subtitle handling
  avcodec/subtitles: Introduce new frame-based subtitle decoding API
  avfilter/subtitles: Update vf_subtitles to use new decoding api
  avcodec,avutil: Move ass helper functions to avutil as avpriv_ and
    extend ass dialog parsing
  avcodec/subtitles: Replace deprecated enum values
  fftools/play,probe: Adjust for subtitle changes
  avfilter/subtitles: Add subtitles.c for subtitle frame allocation
  avfilter/avfilter: Handle subtitle frames
  avfilter/avfilter: Fix hardcoded input index
  avfilter/sbuffer: Add sbuffersrc and sbuffersink filters
  avfilter/overlaygraphicsubs: Add overlaygraphicsubs and
    graphicsub2video filters
  avfilter/overlaytextsubs: Add overlaytextsubs and textsubs2video
    filters
  avfilter/textmod: Add textmod, censor and show_speaker filters
  avfilter/stripstyles: Add stripstyles filter
  avfilter/splitcc: Add splitcc filter for closed caption handling
  avfilter/graphicsub2text: Add new graphicsub2text filter (OCR)
  avfilter/subscale: Add filter for scaling and/or re-arranging
    graphical subtitles
  avfilter/subfeed: add subtitle feed filter
  avcodec/subtitles: Migrate subtitle encoders to frame-based API
  fftools/ffmpeg: Introduce subtitle filtering and new frame-based
    subtitle encoding
  avutil/ass_split: Add parsing of hard-space tags (\h)
  avcodec/webvttenc: convert hard-space tags to &nbsp;
  doc/APIchanges: update for subtitle filtering changes
  avcodec/webvttenc: Don't encode drawing codes and empty lines
  avcodec/dvbsubdec: Fix conditions for fallback to default resolution

 configure                                     |    7 +-
 doc/APIchanges                                |   24 +
 doc/filters.texi                              |  756 ++++++++++
 fftools/ffmpeg.c                              |  501 +++----
 fftools/ffmpeg.h                              |   13 +-
 fftools/ffmpeg_filter.c                       |  235 +++-
 fftools/ffmpeg_hw.c                           |    2 +-
 fftools/ffmpeg_opt.c                          |    3 +-
 fftools/ffplay.c                              |  102 +-
 fftools/ffprobe.c                             |   47 +-
 libavcodec/Makefile                           |   56 +-
 libavcodec/ass.h                              |  144 +-
 libavcodec/assdec.c                           |    4 +-
 libavcodec/assenc.c                           |  191 ++-
 libavcodec/avcodec.h                          |   32 +-
 libavcodec/ccaption_dec.c                     |   19 +-
 libavcodec/codec_desc.c                       |   11 +
 libavcodec/codec_desc.h                       |    8 +
 libavcodec/decode.c                           |   60 +-
 libavcodec/dvbsubdec.c                        |   53 +-
 libavcodec/dvbsubenc.c                        |   96 +-
 libavcodec/dvdsubdec.c                        |    2 +-
 libavcodec/dvdsubenc.c                        |  102 +-
 libavcodec/encode.c                           |   61 +-
 libavcodec/internal.h                         |   16 +
 libavcodec/jacosubdec.c                       |    2 +-
 libavcodec/libaribb24.c                       |    2 +-
 libavcodec/libzvbi-teletextdec.c              |   14 +-
 libavcodec/microdvddec.c                      |    7 +-
 libavcodec/movtextdec.c                       |    3 +-
 libavcodec/movtextenc.c                       |  126 +-
 libavcodec/mpl2dec.c                          |    2 +-
 libavcodec/pgssubdec.c                        |    2 +-
 libavcodec/realtextdec.c                      |    2 +-
 libavcodec/samidec.c                          |    2 +-
 libavcodec/srtdec.c                           |    2 +-
 libavcodec/srtenc.c                           |  116 +-
 libavcodec/subviewerdec.c                     |    2 +-
 libavcodec/tests/avcodec.c                    |    2 -
 libavcodec/textdec.c                          |    4 +-
 libavcodec/ttmlenc.c                          |  114 +-
 libavcodec/utils.c                            |  184 +++
 libavcodec/version.h                          |    2 +-
 libavcodec/webvttdec.c                        |    2 +-
 libavcodec/webvttenc.c                        |  127 +-
 libavcodec/xsubdec.c                          |    2 +-
 libavcodec/xsubenc.c                          |   88 +-
 libavfilter/Makefile                          |   16 +
 libavfilter/allfilters.c                      |   16 +-
 libavfilter/avfilter.c                        |   30 +-
 libavfilter/avfilter.h                        |   11 +
 libavfilter/avfiltergraph.c                   |    5 +
 libavfilter/buffersink.c                      |   54 +
 libavfilter/buffersink.h                      |    7 +
 libavfilter/buffersrc.c                       |   72 +
 libavfilter/buffersrc.h                       |    1 +
 libavfilter/formats.c                         |   22 +
 libavfilter/formats.h                         |    3 +
 libavfilter/internal.h                        |   19 +-
 libavfilter/sf_graphicsub2text.c              | 1132 +++++++++++++++
 libavfilter/sf_splitcc.c                      |  395 ++++++
 libavfilter/sf_stripstyles.c                  |  211 +++
 libavfilter/sf_subfeed.c                      |  366 +++++
 libavfilter/sf_subscale.c                     |  884 ++++++++++++
 libavfilter/sf_textmod.c                      |  710 ++++++++++
 libavfilter/subtitles.c                       |   63 +
 libavfilter/subtitles.h                       |   44 +
 libavfilter/vf_overlaygraphicsubs.c           |  765 ++++++++++
 libavfilter/vf_overlaytextsubs.c              |  678 +++++++++
 libavfilter/vf_subtitles.c                    |   56 +-
 libavutil/Makefile                            |    4 +
 {libavcodec => libavutil}/ass.c               |   91 +-
 libavutil/ass_internal.h                      |  135 ++
 {libavcodec => libavutil}/ass_split.c         |   37 +-
 .../ass_split_internal.h                      |   32 +-
 libavutil/frame.c                             |  211 ++-
 libavutil/frame.h                             |   85 +-
 libavutil/subfmt.c                            |   45 +
 libavutil/subfmt.h                            |  115 ++
 libavutil/version.h                           |    3 +-
 tests/ref/fate/.gitattributes                 |    3 +
 tests/ref/fate/filter-overlay-dvdsub-2397     |  182 +--
 tests/ref/fate/mov-mp4-ttml-dfxp              |    8 +-
 tests/ref/fate/mov-mp4-ttml-stpp              |    8 +-
 tests/ref/fate/sub-dvb                        |  162 ++-
 tests/ref/fate/sub-textenc                    |   10 +-
 tests/ref/fate/sub-ttmlenc                    |    8 +-
 tests/ref/fate/sub-webvttenc                  |   10 +-
 tests/ref/fate/sub2video                      | 1091 ++++++++++++++-
 tests/ref/fate/sub2video_basic                | 1239 +++++++++++++++--
 tests/ref/fate/sub2video_time_limited         |   78 +-
 91 files changed, 11077 insertions(+), 1392 deletions(-)
 create mode 100644 libavfilter/sf_graphicsub2text.c
 create mode 100644 libavfilter/sf_splitcc.c
 create mode 100644 libavfilter/sf_stripstyles.c
 create mode 100644 libavfilter/sf_subfeed.c
 create mode 100644 libavfilter/sf_subscale.c
 create mode 100644 libavfilter/sf_textmod.c
 create mode 100644 libavfilter/subtitles.c
 create mode 100644 libavfilter/subtitles.h
 create mode 100644 libavfilter/vf_overlaygraphicsubs.c
 create mode 100644 libavfilter/vf_overlaytextsubs.c
 rename {libavcodec => libavutil}/ass.c (65%)
 create mode 100644 libavutil/ass_internal.h
 rename {libavcodec => libavutil}/ass_split.c (93%)
 rename libavcodec/ass_split.h => libavutil/ass_split_internal.h (86%)
 create mode 100644 libavutil/subfmt.c
 create mode 100644 libavutil/subfmt.h
 create mode 100644 tests/ref/fate/.gitattributes


base-commit: dd17c86aa11feae2b86de054dd0679cc5f88ebab
Published-As: https://github.com/ffstaging/FFmpeg/releases/tag/pr-ffstaging-18%2Fsoftworkz%2Fsubmit_subfiltering-v3
Fetch-It-Via: git fetch https://github.com/ffstaging/FFmpeg pr-ffstaging-18/softworkz/submit_subfiltering-v3
Pull-Request: https://github.com/ffstaging/FFmpeg/pull/18

Range-diff vs v2:

  1:  13b9a26b25 =  1:  7767933235 avcodec,avutil: Move enum AVSubtitleType to avutil, add new and deprecate old values
  2:  54ed1290d2 =  2:  e922f141ba avutil/frame: Prepare AVFrame for subtitle handling
  3:  840683a6e1 =  3:  ec262914b0 avcodec/subtitles: Introduce new frame-based subtitle decoding API
  4:  e326e96492 =  4:  77bd67ee37 avfilter/subtitles: Update vf_subtitles to use new decoding api
  5:  20dc27a6c6 =  5:  26bad0c088 avcodec,avutil: Move ass helper functions to avutil as avpriv_ and extend ass dialog parsing
  6:  3de0be7980 =  6:  c6fb78e038 avcodec/subtitles: Replace deprecated enum values
  7:  05e3f2a6fe =  7:  3d8673919f fftools/play,probe: Adjust for subtitle changes
  8:  a86457f868 =  8:  ba8b675326 avfilter/subtitles: Add subtitles.c for subtitle frame allocation
  9:  9fec875c51 =  9:  eb09db0e00 avfilter/avfilter: Handle subtitle frames
 10:  50bf6c36be = 10:  b31a991320 avfilter/avfilter: Fix hardcoded input index
 11:  d1ef5004aa = 11:  59abea7693 avfilter/sbuffer: Add sbuffersrc and sbuffersink filters
 12:  f4af184a7b ! 12:  867b60c60d avfilter/overlaygraphicsubs: Add overlaygraphicsubs and graphicsub2video filters
     @@ libavfilter/Makefile: OBJS-$(CONFIG_GBLUR_FILTER)                  += vf_gblur.o
       OBJS-$(CONFIG_GRAYWORLD_FILTER)              += vf_grayworld.o
       OBJS-$(CONFIG_GREYEDGE_FILTER)               += vf_colorconstancy.o
      @@ libavfilter/Makefile: OBJS-$(CONFIG_OVERLAY_OPENCL_FILTER)         += vf_overlay_opencl.o opencl.o \
     -                                                 opencl/overlay.o framesync.o
       OBJS-$(CONFIG_OVERLAY_QSV_FILTER)            += vf_overlay_qsv.o framesync.o
     + OBJS-$(CONFIG_OVERLAY_VAAPI_FILTER)          += vf_overlay_vaapi.o framesync.o vaapi_vpp.o
       OBJS-$(CONFIG_OVERLAY_VULKAN_FILTER)         += vf_overlay_vulkan.o vulkan.o vulkan_filter.o
      +OBJS-$(CONFIG_OVERLAYGRAPHICSUBS_FILTER)     += vf_overlaygraphicsubs.o framesync.o
       OBJS-$(CONFIG_OWDENOISE_FILTER)              += vf_owdenoise.o
     @@ libavfilter/allfilters.c: extern const AVFilter ff_vf_oscilloscope;
       extern const AVFilter ff_vf_overlay_opencl;
       extern const AVFilter ff_vf_overlay_qsv;
      +extern const AVFilter ff_vf_overlaygraphicsubs;
     + extern const AVFilter ff_vf_overlay_vaapi;
       extern const AVFilter ff_vf_overlay_vulkan;
       extern const AVFilter ff_vf_overlay_cuda;
     - extern const AVFilter ff_vf_owdenoise;
      @@ libavfilter/allfilters.c: extern const AVFilter ff_avf_showvolume;
       extern const AVFilter ff_avf_showwaves;
       extern const AVFilter ff_avf_showwavespic;
 13:  47b045ad74 ! 13:  f88a5667e1 avfilter/overlaytextsubs: Add overlaytextsubs and textsubs2video filters
     @@ Commit message
          Signed-off-by: softworkz <softworkz@hotmail.com>
      
       ## configure ##
     -@@ configure: overlay_opencl_filter_deps="opencl"
     - overlay_qsv_filter_deps="libmfx"
     +@@ configure: overlay_qsv_filter_deps="libmfx"
       overlay_qsv_filter_select="qsvvpp"
     + overlay_vaapi_filter_deps="vaapi VAProcPipelineCaps_blend_flags"
       overlay_vulkan_filter_deps="vulkan spirv_compiler"
      +overlaytextsubs_filter_deps="avcodec libass"
       owdenoise_filter_deps="gpl"
     @@ doc/filters.texi: Overlay PGS subtitles
       @chapter Multimedia Filters
      
       ## libavfilter/Makefile ##
     -@@ libavfilter/Makefile: OBJS-$(CONFIG_OVERLAY_OPENCL_FILTER)         += vf_overlay_opencl.o opencl.o \
     - OBJS-$(CONFIG_OVERLAY_QSV_FILTER)            += vf_overlay_qsv.o framesync.o
     +@@ libavfilter/Makefile: OBJS-$(CONFIG_OVERLAY_QSV_FILTER)            += vf_overlay_qsv.o framesync.o
     + OBJS-$(CONFIG_OVERLAY_VAAPI_FILTER)          += vf_overlay_vaapi.o framesync.o vaapi_vpp.o
       OBJS-$(CONFIG_OVERLAY_VULKAN_FILTER)         += vf_overlay_vulkan.o vulkan.o vulkan_filter.o
       OBJS-$(CONFIG_OVERLAYGRAPHICSUBS_FILTER)     += vf_overlaygraphicsubs.o framesync.o
      +OBJS-$(CONFIG_OVERLAYTEXTSUBS_FILTER)        += vf_overlaytextsubs.o
     @@ libavfilter/allfilters.c: extern const AVFilter ff_vf_oscilloscope;
       extern const AVFilter ff_vf_overlay_opencl;
       extern const AVFilter ff_vf_overlay_qsv;
      -extern const AVFilter ff_vf_overlaygraphicsubs;
     +-extern const AVFilter ff_vf_overlay_vaapi;
       extern const AVFilter ff_vf_overlay_vulkan;
       extern const AVFilter ff_vf_overlay_cuda;
      +extern const AVFilter ff_vf_overlaygraphicsubs;
      +extern const AVFilter ff_vf_overlaytextsubs;
     ++extern const AVFilter ff_vf_overlay_vaapi;
       extern const AVFilter ff_vf_owdenoise;
       extern const AVFilter ff_vf_pad;
       extern const AVFilter ff_vf_pad_opencl;
 14:  6f200be0c3 = 14:  25cda21970 avfilter/textmod: Add textmod, censor and show_speaker filters
 15:  ab8736ac43 = 15:  296f1f697b avfilter/stripstyles: Add stripstyles filter
 16:  5369aca080 = 16:  509d6c67ba avfilter/splitcc: Add splitcc filter for closed caption handling
 17:  43a20d1024 = 17:  1d7acba39f avfilter/graphicsub2text: Add new graphicsub2text filter (OCR)
 18:  0d3c46a68f = 18:  244dd6de33 avfilter/subscale: Add filter for scaling and/or re-arranging graphical subtitles
 19:  2c20886389 = 19:  a87812bd3c avfilter/subfeed: add subtitle feed filter
 20:  64ce976c19 = 20:  aea8acb057 avcodec/subtitles: Migrate subtitle encoders to frame-based API
 21:  56a162b3a4 = 21:  314d1da505 fftools/ffmpeg: Introduce subtitle filtering and new frame-based subtitle encoding
 22:  1a0c6e01f3 = 22:  d3cdd2a0e2 avutil/ass_split: Add parsing of hard-space tags (\h)
 23:  44b4e203d8 = 23:  5fca566749 avcodec/webvttenc: convert hard-space tags to &nbsp;
 24:  5773f2a1ff = 24:  93b469b8d0 doc/APIchanges: update for subtitle filtering changes
 25:  7ab6dedf80 = 25:  0c279550d6 avcodec/webvttenc: Don't encode drawing codes and empty lines
 26:  217d96c39d = 26:  03e1a98e08 avcodec/dvbsubdec: Fix conditions for fallback to default resolution

-- 
ffmpeg-codebot
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v3 01/26] avcodec, avutil: Move enum AVSubtitleType to avutil, add new and deprecate old values
  2022-01-20  3:25   ` [FFmpeg-devel] [PATCH v3 00/26] Subtitle Filtering 2022 ffmpegagent
@ 2022-01-20  3:25     ` ffmpegagent
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 02/26] avutil/frame: Prepare AVFrame for subtitle handling ffmpegagent
                       ` (25 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  3:25 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/avcodec.h | 19 +------------
 libavutil/Makefile   |  1 +
 libavutil/subfmt.h   | 68 ++++++++++++++++++++++++++++++++++++++++++++
 libavutil/version.h  |  1 +
 4 files changed, 71 insertions(+), 18 deletions(-)
 create mode 100644 libavutil/subfmt.h

diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index ec1a0566a4..fe5a83cf85 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -35,6 +35,7 @@
 #include "libavutil/frame.h"
 #include "libavutil/log.h"
 #include "libavutil/pixfmt.h"
+#include "libavutil/subfmt.h"
 #include "libavutil/rational.h"
 
 #include "codec.h"
@@ -2238,24 +2239,6 @@ typedef struct AVHWAccel {
  * @}
  */
 
-enum AVSubtitleType {
-    SUBTITLE_NONE,
-
-    SUBTITLE_BITMAP,                ///< A bitmap, pict will be set
-
-    /**
-     * Plain text, the text field must be set by the decoder and is
-     * authoritative. ass and pict fields may contain approximations.
-     */
-    SUBTITLE_TEXT,
-
-    /**
-     * Formatted text, the ass field must be set by the decoder and is
-     * authoritative. pict and text fields may contain approximations.
-     */
-    SUBTITLE_ASS,
-};
-
 #define AV_SUBTITLE_FLAG_FORCED 0x00000001
 
 typedef struct AVSubtitleRect {
diff --git a/libavutil/Makefile b/libavutil/Makefile
index d17876df1a..ce644f4d48 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -74,6 +74,7 @@ HEADERS = adler32.h                                                     \
           sha512.h                                                      \
           spherical.h                                                   \
           stereo3d.h                                                    \
+          subfmt.h                                                      \
           threadmessage.h                                               \
           time.h                                                        \
           timecode.h                                                    \
diff --git a/libavutil/subfmt.h b/libavutil/subfmt.h
new file mode 100644
index 0000000000..791b45519f
--- /dev/null
+++ b/libavutil/subfmt.h
@@ -0,0 +1,68 @@
+/*
+ * 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
+ */
+
+#ifndef AVUTIL_SUBFMT_H
+#define AVUTIL_SUBFMT_H
+
+#include "version.h"
+
+enum AVSubtitleType {
+
+    /**
+     * Subtitle format unknown.
+     */
+    AV_SUBTITLE_FMT_NONE = -1,
+
+    /**
+     * Subtitle format unknown.
+     */
+    AV_SUBTITLE_FMT_UNKNOWN = 0,
+#if FF_API_OLD_SUBTITLES
+    SUBTITLE_NONE = 0,          ///< Deprecated, use AV_SUBTITLE_FMT_NONE instead.
+#endif
+
+    /**
+     * Bitmap area in AVSubtitleRect.data, pixfmt AV_PIX_FMT_PAL8.
+     */
+    AV_SUBTITLE_FMT_BITMAP = 1,
+#if FF_API_OLD_SUBTITLES
+    SUBTITLE_BITMAP = 1,        ///< Deprecated, use AV_SUBTITLE_FMT_BITMAP instead.
+#endif
+
+    /**
+     * Plain text in AVSubtitleRect.text.
+     */
+    AV_SUBTITLE_FMT_TEXT = 2,
+#if FF_API_OLD_SUBTITLES
+    SUBTITLE_TEXT = 2,          ///< Deprecated, use AV_SUBTITLE_FMT_TEXT instead.
+#endif
+
+    /**
+     * Text Formatted as per ASS specification, contained AVSubtitleRect.ass.
+     */
+    AV_SUBTITLE_FMT_ASS = 3,
+#if FF_API_OLD_SUBTITLES
+    SUBTITLE_ASS = 3,           ///< Deprecated, use AV_SUBTITLE_FMT_ASS instead.
+#endif
+
+    AV_SUBTITLE_FMT_NB,         ///< number of subtitle formats, DO NOT USE THIS if you want to link with shared libav* because the number of formats might differ between versions.
+};
+
+#endif /* AVUTIL_SUBFMT_H */
diff --git a/libavutil/version.h b/libavutil/version.h
index 953aac9d94..5bf48f6304 100644
--- a/libavutil/version.h
+++ b/libavutil/version.h
@@ -110,6 +110,7 @@
 #define FF_API_COLORSPACE_NAME          (LIBAVUTIL_VERSION_MAJOR < 58)
 #define FF_API_AV_MALLOCZ_ARRAY         (LIBAVUTIL_VERSION_MAJOR < 58)
 #define FF_API_FIFO_PEEK2               (LIBAVUTIL_VERSION_MAJOR < 58)
+#define FF_API_OLD_SUBTITLES            (LIBAVUTIL_VERSION_MAJOR < 58)
 
 /**
  * @}
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v3 02/26] avutil/frame: Prepare AVFrame for subtitle handling
  2022-01-20  3:25   ` [FFmpeg-devel] [PATCH v3 00/26] Subtitle Filtering 2022 ffmpegagent
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 01/26] avcodec, avutil: Move enum AVSubtitleType to avutil, add new and deprecate old values ffmpegagent
@ 2022-01-20  3:25     ` ffmpegagent
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 03/26] avcodec/subtitles: Introduce new frame-based subtitle decoding API ffmpegagent
                       ` (24 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  3:25 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Root commit for adding subtitle filtering capabilities.
In detail:

- Add type (AVMediaType) field to AVFrame
  Replaces previous way of distinction which was based on checking
  width and height to determine whether a frame is audio or video
- Add subtitle fields to AVFrame
- Add new struct AVSubtitleArea, similar to AVSubtitleRect, but
  different allocation logic. Cannot and must not be used
  interchangeably, hence the new struct

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavutil/Makefile |   1 +
 libavutil/frame.c  | 211 ++++++++++++++++++++++++++++++++++++++++-----
 libavutil/frame.h  |  85 +++++++++++++++++-
 libavutil/subfmt.c |  45 ++++++++++
 libavutil/subfmt.h |  47 ++++++++++
 5 files changed, 364 insertions(+), 25 deletions(-)
 create mode 100644 libavutil/subfmt.c

diff --git a/libavutil/Makefile b/libavutil/Makefile
index ce644f4d48..8bc0a14942 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -160,6 +160,7 @@ OBJS = adler32.o                                                        \
        slicethread.o                                                    \
        spherical.o                                                      \
        stereo3d.o                                                       \
+       subfmt.o                                                         \
        threadmessage.o                                                  \
        time.o                                                           \
        timecode.o                                                       \
diff --git a/libavutil/frame.c b/libavutil/frame.c
index 8997c85e35..2b95830b6f 100644
--- a/libavutil/frame.c
+++ b/libavutil/frame.c
@@ -26,6 +26,7 @@
 #include "imgutils.h"
 #include "mem.h"
 #include "samplefmt.h"
+#include "subfmt.h"
 #include "hwcontext.h"
 
 #define CHECK_CHANNELS_CONSISTENCY(frame) \
@@ -50,6 +51,9 @@ const char *av_get_colorspace_name(enum AVColorSpace val)
     return name[val];
 }
 #endif
+
+static int frame_copy_subtitles(AVFrame *dst, const AVFrame *src, int copy_data);
+
 static void get_frame_defaults(AVFrame *frame)
 {
     memset(frame, 0, sizeof(*frame));
@@ -70,7 +74,12 @@ static void get_frame_defaults(AVFrame *frame)
     frame->colorspace          = AVCOL_SPC_UNSPECIFIED;
     frame->color_range         = AVCOL_RANGE_UNSPECIFIED;
     frame->chroma_location     = AVCHROMA_LOC_UNSPECIFIED;
-    frame->flags               = 0;
+    frame->num_subtitle_areas  = 0;
+    frame->subtitle_areas      = NULL;
+    frame->subtitle_header     = NULL;
+    frame->repeat_sub          = 0;
+    frame->subtitle_timing.start_pts = 0;
+    frame->subtitle_timing.duration  = 0;
 }
 
 static void free_side_data(AVFrameSideData **ptr_sd)
@@ -240,23 +249,55 @@ static int get_audio_buffer(AVFrame *frame, int align)
 
 }
 
+static int get_subtitle_buffer(AVFrame *frame)
+{
+    // Buffers in AVFrame->buf[] are not used in case of subtitle frames.
+    // To accomodate with existing code, checking ->buf[0] to determine
+    // whether a frame is ref-counted or has data, we're adding a 1-byte
+    // buffer here, which marks the subtitle frame to contain data.
+    frame->buf[0] = av_buffer_alloc(1);
+    if (!frame->buf[0]) {
+        av_frame_unref(frame);
+        return AVERROR(ENOMEM);
+    }
+
+    frame->extended_data = frame->data;
+
+    return 0;
+}
+
 int av_frame_get_buffer(AVFrame *frame, int align)
+{
+    if (frame->width > 0 && frame->height > 0)
+        frame->type = AVMEDIA_TYPE_VIDEO;
+    else if (frame->nb_samples > 0 && (frame->channel_layout || frame->channels > 0))
+        frame->type = AVMEDIA_TYPE_AUDIO;
+
+    return av_frame_get_buffer2(frame, align);
+}
+
+int av_frame_get_buffer2(AVFrame *frame, int align)
 {
     if (frame->format < 0)
         return AVERROR(EINVAL);
 
-    if (frame->width > 0 && frame->height > 0)
+    switch(frame->type) {
+    case AVMEDIA_TYPE_VIDEO:
         return get_video_buffer(frame, align);
-    else if (frame->nb_samples > 0 && (frame->channel_layout || frame->channels > 0))
+    case AVMEDIA_TYPE_AUDIO:
         return get_audio_buffer(frame, align);
-
-    return AVERROR(EINVAL);
+    case AVMEDIA_TYPE_SUBTITLE:
+        return get_subtitle_buffer(frame);
+    default:
+        return AVERROR(EINVAL);
+    }
 }
 
 static int frame_copy_props(AVFrame *dst, const AVFrame *src, int force_copy)
 {
     int ret, i;
 
+    dst->type                   = src->type;
     dst->key_frame              = src->key_frame;
     dst->pict_type              = src->pict_type;
     dst->sample_aspect_ratio    = src->sample_aspect_ratio;
@@ -288,6 +329,12 @@ static int frame_copy_props(AVFrame *dst, const AVFrame *src, int force_copy)
     dst->colorspace             = src->colorspace;
     dst->color_range            = src->color_range;
     dst->chroma_location        = src->chroma_location;
+    dst->repeat_sub             = src->repeat_sub;
+    dst->subtitle_timing.start_pts = src->subtitle_timing.start_pts;
+    dst->subtitle_timing.duration  = src->subtitle_timing.duration;
+
+    if ((ret = av_buffer_replace(&dst->subtitle_header, src->subtitle_header)) < 0)
+        return ret;
 
     av_dict_copy(&dst->metadata, src->metadata, 0);
 
@@ -329,6 +376,7 @@ int av_frame_ref(AVFrame *dst, const AVFrame *src)
     av_assert1(dst->width == 0 && dst->height == 0);
     av_assert1(dst->channels == 0);
 
+    dst->type           = src->type;
     dst->format         = src->format;
     dst->width          = src->width;
     dst->height         = src->height;
@@ -342,7 +390,7 @@ int av_frame_ref(AVFrame *dst, const AVFrame *src)
 
     /* duplicate the frame data if it's not refcounted */
     if (!src->buf[0]) {
-        ret = av_frame_get_buffer(dst, 0);
+        ret = av_frame_get_buffer2(dst, 0);
         if (ret < 0)
             goto fail;
 
@@ -364,6 +412,10 @@ int av_frame_ref(AVFrame *dst, const AVFrame *src)
         }
     }
 
+    /* copy subtitle areas */
+    if (src->type == AVMEDIA_TYPE_SUBTITLE)
+        frame_copy_subtitles(dst, src, 0);
+
     if (src->extended_buf) {
         dst->extended_buf = av_calloc(src->nb_extended_buf,
                                       sizeof(*dst->extended_buf));
@@ -434,7 +486,7 @@ AVFrame *av_frame_clone(const AVFrame *src)
 
 void av_frame_unref(AVFrame *frame)
 {
-    int i;
+    unsigned i, n;
 
     if (!frame)
         return;
@@ -453,6 +505,21 @@ void av_frame_unref(AVFrame *frame)
     av_buffer_unref(&frame->opaque_ref);
     av_buffer_unref(&frame->private_ref);
 
+    av_buffer_unref(&frame->subtitle_header);
+
+    for (i = 0; i < frame->num_subtitle_areas; i++) {
+        AVSubtitleArea *area = frame->subtitle_areas[i];
+
+        for (n = 0; n < FF_ARRAY_ELEMS(area->buf); n++)
+            av_buffer_unref(&area->buf[n]);
+
+        av_freep(&area->text);
+        av_freep(&area->ass);
+        av_free(area);
+    }
+
+    av_freep(&frame->subtitle_areas);
+
     if (frame->extended_data != frame->data)
         av_freep(&frame->extended_data);
 
@@ -472,18 +539,28 @@ void av_frame_move_ref(AVFrame *dst, AVFrame *src)
 
 int av_frame_is_writable(AVFrame *frame)
 {
-    int i, ret = 1;
+    AVSubtitleArea *area;
+    int ret = 1;
 
     /* assume non-refcounted frames are not writable */
     if (!frame->buf[0])
         return 0;
 
-    for (i = 0; i < FF_ARRAY_ELEMS(frame->buf); i++)
+    for (unsigned i = 0; i < FF_ARRAY_ELEMS(frame->buf); i++)
         if (frame->buf[i])
             ret &= !!av_buffer_is_writable(frame->buf[i]);
-    for (i = 0; i < frame->nb_extended_buf; i++)
+    for (unsigned i = 0; i < frame->nb_extended_buf; i++)
         ret &= !!av_buffer_is_writable(frame->extended_buf[i]);
 
+    for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+        area = frame->subtitle_areas[i];
+        if (area) {
+            for (unsigned n = 0; n < FF_ARRAY_ELEMS(area->buf); n++)
+                if (area->buf[n])
+                    ret &= !!av_buffer_is_writable(area->buf[n]);
+            }
+    }
+
     return ret;
 }
 
@@ -499,6 +576,7 @@ int av_frame_make_writable(AVFrame *frame)
         return 0;
 
     memset(&tmp, 0, sizeof(tmp));
+    tmp.type           = frame->type;
     tmp.format         = frame->format;
     tmp.width          = frame->width;
     tmp.height         = frame->height;
@@ -509,7 +587,7 @@ int av_frame_make_writable(AVFrame *frame)
     if (frame->hw_frames_ctx)
         ret = av_hwframe_get_buffer(frame->hw_frames_ctx, &tmp, 0);
     else
-        ret = av_frame_get_buffer(&tmp, 0);
+        ret = av_frame_get_buffer2(&tmp, 0);
     if (ret < 0)
         return ret;
 
@@ -544,14 +622,22 @@ AVBufferRef *av_frame_get_plane_buffer(AVFrame *frame, int plane)
     uint8_t *data;
     int planes, i;
 
-    if (frame->nb_samples) {
-        int channels = frame->channels;
-        if (!channels)
-            return NULL;
-        CHECK_CHANNELS_CONSISTENCY(frame);
-        planes = av_sample_fmt_is_planar(frame->format) ? channels : 1;
-    } else
+    switch(frame->type) {
+    case AVMEDIA_TYPE_VIDEO:
         planes = 4;
+        break;
+    case AVMEDIA_TYPE_AUDIO:
+        {
+            int channels = frame->channels;
+            if (!channels)
+                return NULL;
+            CHECK_CHANNELS_CONSISTENCY(frame);
+            planes = av_sample_fmt_is_planar(frame->format) ? channels : 1;
+            break;
+        }
+    default:
+        return NULL;
+    }
 
     if (plane < 0 || plane >= planes || !frame->extended_data[plane])
         return NULL;
@@ -675,17 +761,98 @@ static int frame_copy_audio(AVFrame *dst, const AVFrame *src)
     return 0;
 }
 
+static int av_subtitle_area2area(AVSubtitleArea *dst, const AVSubtitleArea *src, int copy_data)
+{
+    dst->x         =  src->x;
+    dst->y         =  src->y;
+    dst->w         =  src->w;
+    dst->h         =  src->h;
+    dst->nb_colors =  src->nb_colors;
+    dst->type      =  src->type;
+    dst->flags     =  src->flags;
+
+    switch (dst->type) {
+    case AV_SUBTITLE_FMT_BITMAP:
+
+        for (unsigned i = 0; i < AV_NUM_BUFFER_POINTERS; i++) {
+            if (src->h > 0 && src->w > 0 && src->buf[i]) {
+                dst->buf[0] = av_buffer_ref(src->buf[i]);
+                if (!dst->buf[i])
+                    return AVERROR(ENOMEM);
+
+                if (copy_data) {
+                    const int ret = av_buffer_make_writable(&dst->buf[i]);
+                    if (ret < 0)
+                        return ret;
+                }
+
+                dst->linesize[i] = src->linesize[i];
+            }
+        }
+
+        memcpy(&dst->pal[0], &src->pal[0], sizeof(src->pal[0]) * 256);
+
+        break;
+    case AV_SUBTITLE_FMT_TEXT:
+
+        if (src->text) {
+            dst->text = av_strdup(src->text);
+            if (!dst->text)
+                return AVERROR(ENOMEM);
+        }
+
+        break;
+    case AV_SUBTITLE_FMT_ASS:
+
+        if (src->ass) {
+            dst->ass = av_strdup(src->ass);
+            if (!dst->ass)
+                return AVERROR(ENOMEM);
+        }
+
+        break;
+    default:
+
+        av_log(NULL, AV_LOG_ERROR, "Subtitle area has invalid format: %d", dst->type);
+        return AVERROR(ENOMEM);
+    }
+
+    return 0;
+}
+
+static int frame_copy_subtitles(AVFrame *dst, const AVFrame *src, int copy_data)
+{
+    dst->format = src->format;
+
+    dst->num_subtitle_areas = src->num_subtitle_areas;
+
+    if (src->num_subtitle_areas) {
+        dst->subtitle_areas = av_malloc_array(src->num_subtitle_areas, sizeof(AVSubtitleArea *));
+
+        for (unsigned i = 0; i < src->num_subtitle_areas; i++) {
+            dst->subtitle_areas[i] = av_mallocz(sizeof(AVSubtitleArea));
+            av_subtitle_area2area(dst->subtitle_areas[i], src->subtitle_areas[i], copy_data);
+        }
+    }
+
+    return 0;
+}
+
 int av_frame_copy(AVFrame *dst, const AVFrame *src)
 {
     if (dst->format != src->format || dst->format < 0)
         return AVERROR(EINVAL);
 
-    if (dst->width > 0 && dst->height > 0)
+    switch(dst->type) {
+    case AVMEDIA_TYPE_VIDEO:
         return frame_copy_video(dst, src);
-    else if (dst->nb_samples > 0 && dst->channels > 0)
+    case AVMEDIA_TYPE_AUDIO:
         return frame_copy_audio(dst, src);
-
-    return AVERROR(EINVAL);
+    case AVMEDIA_TYPE_SUBTITLE:
+        return frame_copy_subtitles(dst, src, 1);
+    default:
+        return AVERROR(EINVAL);
+    }
 }
 
 void av_frame_remove_side_data(AVFrame *frame, enum AVFrameSideDataType type)
diff --git a/libavutil/frame.h b/libavutil/frame.h
index 18e239f870..ed519c5e2b 100644
--- a/libavutil/frame.h
+++ b/libavutil/frame.h
@@ -25,7 +25,6 @@
 #ifndef AVUTIL_FRAME_H
 #define AVUTIL_FRAME_H
 
-#include <stddef.h>
 #include <stdint.h>
 
 #include "avutil.h"
@@ -34,6 +33,7 @@
 #include "rational.h"
 #include "samplefmt.h"
 #include "pixfmt.h"
+#include "subfmt.h"
 #include "version.h"
 
 
@@ -285,7 +285,7 @@ typedef struct AVRegionOfInterest {
 } AVRegionOfInterest;
 
 /**
- * This structure describes decoded (raw) audio or video data.
+ * This structure describes decoded (raw) audio, video or subtitle data.
  *
  * AVFrame must be allocated using av_frame_alloc(). Note that this only
  * allocates the AVFrame itself, the buffers for the data must be managed
@@ -399,7 +399,7 @@ typedef struct AVFrame {
     /**
      * format of the frame, -1 if unknown or unset
      * Values correspond to enum AVPixelFormat for video frames,
-     * enum AVSampleFormat for audio)
+     * enum AVSampleFormat for audio, AVSubtitleType for subtitles)
      */
     int format;
 
@@ -681,6 +681,53 @@ typedef struct AVFrame {
      * for the target frame's private_ref field.
      */
     AVBufferRef *private_ref;
+
+    /**
+     * Media type of the frame (audio, video, subtitles..)
+     *
+     * See AVMEDIA_TYPE_xxx
+     */
+    enum AVMediaType type;
+
+    /**
+     * Number of items in the @ref subtitle_areas array.
+     */
+    unsigned num_subtitle_areas;
+
+    /**
+     * Array of subtitle areas, may be empty.
+     */
+    AVSubtitleArea **subtitle_areas;
+
+    /**
+     * Header containing style information for text subtitles.
+     */
+    AVBufferRef *subtitle_header;
+
+    /**
+     * Indicates that a subtitle frame is a repeated frame for arbitrating flow
+     * in a filter graph.
+     * The field subtitle_timing.start_pts always indicates the original presentation
+     * time, while the frame's pts field may be different.
+     */
+    int repeat_sub;
+
+    struct SubtitleTiming
+    {
+        /**
+         * The display start time, in AV_TIME_BASE.
+         *
+         * For subtitle frames, AVFrame.pts is populated from the packet pts value,
+         * which is not always the same as this value.
+         */
+        int64_t start_pts;
+
+        /**
+         * Display duration, in AV_TIME_BASE.
+         */
+        int64_t duration;
+
+    } subtitle_timing;
 } AVFrame;
 
 
@@ -757,6 +804,8 @@ void av_frame_move_ref(AVFrame *dst, AVFrame *src);
 /**
  * Allocate new buffer(s) for audio or video data.
  *
+ * Note: For subtitle data, use av_frame_get_buffer2
+ *
  * The following fields must be set on frame before calling this function:
  * - format (pixel format for video, sample format for audio)
  * - width and height for video
@@ -776,9 +825,39 @@ void av_frame_move_ref(AVFrame *dst, AVFrame *src);
  *              recommended to pass 0 here unless you know what you are doing.
  *
  * @return 0 on success, a negative AVERROR on error.
+ *
+ * @deprecated Use @ref av_frame_get_buffer2 instead and set @ref AVFrame.type
+ * before calling.
  */
+attribute_deprecated
 int av_frame_get_buffer(AVFrame *frame, int align);
 
+/**
+ * Allocate new buffer(s) for audio, video or subtitle data.
+ *
+ * The following fields must be set on frame before calling this function:
+ * - format (pixel format for video, sample format for audio)
+ * - width and height for video
+ * - nb_samples and channel_layout for audio
+ * - type (AVMediaType)
+ *
+ * This function will fill AVFrame.data and AVFrame.buf arrays and, if
+ * necessary, allocate and fill AVFrame.extended_data and AVFrame.extended_buf.
+ * For planar formats, one buffer will be allocated for each plane.
+ *
+ * @warning: if frame already has been allocated, calling this function will
+ *           leak memory. In addition, undefined behavior can occur in certain
+ *           cases.
+ *
+ * @param frame frame in which to store the new buffers.
+ * @param align Required buffer size alignment. If equal to 0, alignment will be
+ *              chosen automatically for the current CPU. It is highly
+ *              recommended to pass 0 here unless you know what you are doing.
+ *
+ * @return 0 on success, a negative AVERROR on error.
+ */
+int av_frame_get_buffer2(AVFrame *frame, int align);
+
 /**
  * Check if the frame data is writable.
  *
diff --git a/libavutil/subfmt.c b/libavutil/subfmt.c
new file mode 100644
index 0000000000..c72ebe2a43
--- /dev/null
+++ b/libavutil/subfmt.c
@@ -0,0 +1,45 @@
+/*
+ * 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
+ */
+
+#include <string.h>
+#include "common.h"
+#include "subfmt.h"
+
+static const char sub_fmt_info[AV_SUBTITLE_FMT_NB][24] = {
+    [AV_SUBTITLE_FMT_UNKNOWN] = "Unknown subtitle format",
+    [AV_SUBTITLE_FMT_BITMAP]  = "Graphical subtitles",
+    [AV_SUBTITLE_FMT_TEXT]    = "Text subtitles (plain)",
+    [AV_SUBTITLE_FMT_ASS]     = "Text subtitles (ass)",
+};
+
+const char *av_get_subtitle_fmt_name(enum AVSubtitleType sub_fmt)
+{
+    if (sub_fmt < 0 || sub_fmt >= AV_SUBTITLE_FMT_NB)
+        return NULL;
+    return sub_fmt_info[sub_fmt];
+}
+
+enum AVSubtitleType av_get_subtitle_fmt(const char *name)
+{
+    for (int i = 0; i < AV_SUBTITLE_FMT_NB; i++)
+        if (!strcmp(sub_fmt_info[i], name))
+            return i;
+    return AV_SUBTITLE_FMT_NONE;
+}
diff --git a/libavutil/subfmt.h b/libavutil/subfmt.h
index 791b45519f..3b8a0613b2 100644
--- a/libavutil/subfmt.h
+++ b/libavutil/subfmt.h
@@ -21,6 +21,9 @@
 #ifndef AVUTIL_SUBFMT_H
 #define AVUTIL_SUBFMT_H
 
+#include <stdint.h>
+
+#include "buffer.h"
 #include "version.h"
 
 enum AVSubtitleType {
@@ -65,4 +68,48 @@ enum AVSubtitleType {
     AV_SUBTITLE_FMT_NB,         ///< number of subtitle formats, DO NOT USE THIS if you want to link with shared libav* because the number of formats might differ between versions.
 };
 
+typedef struct AVSubtitleArea {
+#define AV_NUM_BUFFER_POINTERS 1
+
+    enum AVSubtitleType type;
+    int flags;
+
+    int x;         ///< top left corner  of area.
+    int y;         ///< top left corner  of area.
+    int w;         ///< width            of area.
+    int h;         ///< height           of area.
+    int nb_colors; ///< number of colors in bitmap palette (@ref pal).
+
+    /**
+     * Buffers and line sizes for the bitmap of this subtitle.
+     *
+     * @{
+     */
+    AVBufferRef *buf[AV_NUM_BUFFER_POINTERS];
+    int linesize[AV_NUM_BUFFER_POINTERS];
+    /**
+     * @}
+     */
+
+    uint32_t pal[256]; ///< RGBA palette for the bitmap.
+
+    char *text;        ///< 0-terminated plain UTF-8 text
+    char *ass;         ///< 0-terminated ASS/SSA compatible event line.
+
+} AVSubtitleArea;
+
+/**
+ * Return the name of sub_fmt, or NULL if sub_fmt is not
+ * recognized.
+ */
+const char *av_get_subtitle_fmt_name(enum AVSubtitleType sub_fmt);
+
+/**
+ * Return a subtitle format corresponding to name, or AV_SUBTITLE_FMT_NONE
+ * on error.
+ *
+ * @param name Subtitle format name.
+ */
+enum AVSubtitleType av_get_subtitle_fmt(const char *name);
+
 #endif /* AVUTIL_SUBFMT_H */
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v3 03/26] avcodec/subtitles: Introduce new frame-based subtitle decoding API
  2022-01-20  3:25   ` [FFmpeg-devel] [PATCH v3 00/26] Subtitle Filtering 2022 ffmpegagent
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 01/26] avcodec, avutil: Move enum AVSubtitleType to avutil, add new and deprecate old values ffmpegagent
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 02/26] avutil/frame: Prepare AVFrame for subtitle handling ffmpegagent
@ 2022-01-20  3:25     ` ffmpegagent
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 04/26] avfilter/subtitles: Update vf_subtitles to use new decoding api ffmpegagent
                       ` (23 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  3:25 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

- Modify avcodec_send_packet() to support subtitles via the regular
  frame based decoding API
- Add decode_subtitle_shim() which takes subtitle frames,
  and serves as a compatibility shim to the legacy subtitle decoding
  API until all subtitle decoders are migrated to the frame-based API
- Add additional methods for conversion between old and new API

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/avcodec.h    |   8 +-
 libavcodec/codec_desc.c |  11 +++
 libavcodec/codec_desc.h |   8 ++
 libavcodec/decode.c     |  60 +++++++++++--
 libavcodec/internal.h   |  16 ++++
 libavcodec/utils.c      | 184 ++++++++++++++++++++++++++++++++++++++++
 6 files changed, 278 insertions(+), 9 deletions(-)

diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index fe5a83cf85..9d59f6e840 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -1675,7 +1675,7 @@ typedef struct AVCodecContext {
 
     /**
      * Header containing style information for text subtitles.
-     * For SUBTITLE_ASS subtitle type, it should contain the whole ASS
+     * For AV_SUBTITLE_FMT_ASS subtitle type, it should contain the whole ASS
      * [Script Info] and [V4+ Styles] section, plus the [Events] line and
      * the Format line following. It shouldn't include any Dialogue line.
      * - encoding: Set/allocated/freed by user (before avcodec_open2())
@@ -2415,7 +2415,10 @@ int avcodec_close(AVCodecContext *avctx);
  * Free all allocated data in the given subtitle struct.
  *
  * @param sub AVSubtitle to free.
+ *
+ * @deprecated Use the regular frame based encode and decode APIs instead.
  */
+attribute_deprecated
 void avsubtitle_free(AVSubtitle *sub);
 
 /**
@@ -2508,7 +2511,10 @@ enum AVChromaLocation avcodec_chroma_pos_to_enum(int xpos, int ypos);
  *                 must be freed with avsubtitle_free if *got_sub_ptr is set.
  * @param[in,out] got_sub_ptr Zero if no subtitle could be decompressed, otherwise, it is nonzero.
  * @param[in] avpkt The input AVPacket containing the input buffer.
+ *
+ * @deprecated Use the new decode API (avcodec_send_packet, avcodec_receive_frame) instead.
  */
+attribute_deprecated
 int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub,
                             int *got_sub_ptr,
                             AVPacket *avpkt);
diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
index 0974ee03de..e48e4532ba 100644
--- a/libavcodec/codec_desc.c
+++ b/libavcodec/codec_desc.c
@@ -3548,3 +3548,14 @@ enum AVMediaType avcodec_get_type(enum AVCodecID codec_id)
     const AVCodecDescriptor *desc = avcodec_descriptor_get(codec_id);
     return desc ? desc->type : AVMEDIA_TYPE_UNKNOWN;
 }
+
+enum AVSubtitleType avcodec_descriptor_get_subtitle_format(const AVCodecDescriptor *codec_descriptor)
+{
+    if(codec_descriptor->props & AV_CODEC_PROP_BITMAP_SUB)
+        return AV_SUBTITLE_FMT_BITMAP;
+
+    if(codec_descriptor->props & AV_CODEC_PROP_TEXT_SUB)
+        return AV_SUBTITLE_FMT_ASS;
+
+    return AV_SUBTITLE_FMT_UNKNOWN;
+}
diff --git a/libavcodec/codec_desc.h b/libavcodec/codec_desc.h
index 126b52df47..ba68d24e0e 100644
--- a/libavcodec/codec_desc.h
+++ b/libavcodec/codec_desc.h
@@ -121,6 +121,14 @@ const AVCodecDescriptor *avcodec_descriptor_next(const AVCodecDescriptor *prev);
  */
 const AVCodecDescriptor *avcodec_descriptor_get_by_name(const char *name);
 
+/**
+ * Return subtitle format from a codec descriptor
+ *
+ * @param codec_descriptor codec descriptor
+ * @return                 the subtitle type (e.g. bitmap, text)
+ */
+enum AVSubtitleType avcodec_descriptor_get_subtitle_format(const AVCodecDescriptor *codec_descriptor);
+
 /**
  * @}
  */
diff --git a/libavcodec/decode.c b/libavcodec/decode.c
index 0912f86a14..e3cf1cfa3d 100644
--- a/libavcodec/decode.c
+++ b/libavcodec/decode.c
@@ -576,6 +576,39 @@ static int decode_receive_frame_internal(AVCodecContext *avctx, AVFrame *frame)
     return ret;
 }
 
+static int decode_subtitle2_priv(AVCodecContext *avctx, AVSubtitle *sub,
+                                 int *got_sub_ptr, AVPacket *avpkt);
+
+static int decode_subtitle_shim(AVCodecContext *avctx, AVFrame *frame, AVPacket *avpkt)
+{
+    int ret, got_sub_ptr = 0;
+    AVSubtitle subtitle = { 0 };
+
+    if (frame->buf[0])
+        return AVERROR(EAGAIN);
+
+    av_frame_unref(frame);
+
+    ret = decode_subtitle2_priv(avctx, &subtitle, &got_sub_ptr, avpkt);
+
+    if (ret >= 0 && got_sub_ptr) {
+        frame->type = AVMEDIA_TYPE_SUBTITLE;
+        frame->format = subtitle.format;
+        ret = av_frame_get_buffer2(frame, 0);
+
+        if (ret >= 0)
+            ret = ff_frame_put_subtitle(frame, &subtitle);
+
+        frame->width = avctx->width;
+        frame->height = avctx->height;
+        frame->pkt_dts = avpkt->dts;
+    }
+
+    avsubtitle_free(&subtitle);
+
+    return ret;
+}
+
 int attribute_align_arg avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt)
 {
     AVCodecInternal *avci = avctx->internal;
@@ -590,6 +623,13 @@ int attribute_align_arg avcodec_send_packet(AVCodecContext *avctx, const AVPacke
     if (avpkt && !avpkt->size && avpkt->data)
         return AVERROR(EINVAL);
 
+    if (avctx->codec_type == AVMEDIA_TYPE_SUBTITLE)
+		// this does not exactly implement the avcodec_send_packet/avcodec_receive_frame API 
+	    // but we know that no subtitle decoder produces multiple AVSubtitles per packet through
+		// the legacy API, and this will be changed when migrating the subtitle decoders
+		// to the frame based decoding api
+        return decode_subtitle_shim(avctx, avci->buffer_frame, avpkt);
+
     av_packet_unref(avci->buffer_pkt);
     if (avpkt && (avpkt->data || avpkt->side_data_elems)) {
         ret = av_packet_ref(avci->buffer_pkt, avpkt);
@@ -651,7 +691,9 @@ int attribute_align_arg avcodec_receive_frame(AVCodecContext *avctx, AVFrame *fr
 
     if (avci->buffer_frame->buf[0]) {
         av_frame_move_ref(frame, avci->buffer_frame);
-    } else {
+    } else if (avctx->codec_type == AVMEDIA_TYPE_SUBTITLE)
+        return AVERROR(EAGAIN);
+    else {
         ret = decode_receive_frame_internal(avctx, frame);
         if (ret < 0)
             return ret;
@@ -802,9 +844,8 @@ static int utf8_check(const uint8_t *str)
     return 1;
 }
 
-int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub,
-                             int *got_sub_ptr,
-                             AVPacket *avpkt)
+static int decode_subtitle2_priv(AVCodecContext *avctx, AVSubtitle *sub,
+                             int *got_sub_ptr, AVPacket *avpkt)
 {
     int ret = 0;
 
@@ -850,10 +891,7 @@ int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub,
                                                  avctx->pkt_timebase, ms);
         }
 
-        if (avctx->codec_descriptor->props & AV_CODEC_PROP_BITMAP_SUB)
-            sub->format = 0;
-        else if (avctx->codec_descriptor->props & AV_CODEC_PROP_TEXT_SUB)
-            sub->format = 1;
+        sub->format = avcodec_descriptor_get_subtitle_format(avctx->codec_descriptor);
 
         for (unsigned i = 0; i < sub->num_rects; i++) {
             if (avctx->sub_charenc_mode != FF_SUB_CHARENC_MODE_IGNORE &&
@@ -874,6 +912,12 @@ int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub,
     return ret;
 }
 
+int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub,
+                             int *got_sub_ptr, AVPacket *avpkt)
+{
+    return decode_subtitle2_priv(avctx, sub, got_sub_ptr, avpkt);
+}
+
 enum AVPixelFormat avcodec_default_get_format(struct AVCodecContext *avctx,
                                               const enum AVPixelFormat *fmt)
 {
diff --git a/libavcodec/internal.h b/libavcodec/internal.h
index 72ca1553f6..d2ef58474f 100644
--- a/libavcodec/internal.h
+++ b/libavcodec/internal.h
@@ -363,4 +363,20 @@ int ff_int_from_list_or_default(void *ctx, const char * val_name, int val,
 
 void ff_dvdsub_parse_palette(uint32_t *palette, const char *p);
 
+/**
+ * Copies subtitle data from AVSubtitle to AVFrame.
+ *
+ * @deprecated This is a compatibility method for interoperability with
+ * the legacy subtitle API.
+ */
+int ff_frame_put_subtitle(AVFrame* frame, const AVSubtitle* sub);
+
+/**
+ * Copies subtitle data from AVFrame to AVSubtitle.
+ *
+ * @deprecated This is a compatibility method for interoperability with
+ * the legacy subtitle API.
+ */
+int ff_frame_get_subtitle(AVSubtitle* sub, AVFrame* frame);
+
 #endif /* AVCODEC_INTERNAL_H */
diff --git a/libavcodec/utils.c b/libavcodec/utils.c
index b19befef21..72c742c176 100644
--- a/libavcodec/utils.c
+++ b/libavcodec/utils.c
@@ -813,6 +813,190 @@ int av_get_audio_frame_duration(AVCodecContext *avctx, int frame_bytes)
     return FFMAX(0, duration);
 }
 
+static int subtitle_area2rect(AVSubtitleRect *dst, const AVSubtitleArea *src)
+{
+    dst->x         =  src->x;
+    dst->y         =  src->y;
+    dst->w         =  src->w;
+    dst->h         =  src->h;
+    dst->nb_colors =  src->nb_colors;
+    dst->type      =  src->type;
+    dst->flags     =  src->flags;
+
+    switch (dst->type) {
+    case AV_SUBTITLE_FMT_BITMAP:
+
+        if (src->h > 0 && src->w > 0 && src->buf[0]) {
+            uint32_t *pal;
+            AVBufferRef *buf = src->buf[0];
+            dst->data[0] = av_mallocz(buf->size);
+            memcpy(dst->data[0], buf->data, buf->size);
+            dst->linesize[0] = src->linesize[0];
+
+            dst->data[1] = av_mallocz(256 * 4);
+            pal = (uint32_t *)dst->data[1];
+
+            for (unsigned i = 0; i < 256; i++) {
+                pal[i] = src->pal[i];
+            }
+        }
+
+        break;
+    case AV_SUBTITLE_FMT_TEXT:
+
+        if (src->text)
+            dst->text = av_strdup(src->text);
+        else
+            dst->text = av_strdup("");
+
+        if (!dst->text)
+            return AVERROR(ENOMEM);
+
+        break;
+    case AV_SUBTITLE_FMT_ASS:
+
+        if (src->ass)
+            dst->ass = av_strdup(src->ass);
+        else
+            dst->ass = av_strdup("");
+
+        if (!dst->ass)
+            return AVERROR(ENOMEM);
+
+        break;
+    default:
+
+        av_log(NULL, AV_LOG_ERROR, "Subtitle rect has invalid format: %d", dst->type);
+        return AVERROR(EINVAL);
+    }
+
+    return 0;
+}
+
+static int subtitle_rect2area(AVSubtitleArea *dst, const AVSubtitleRect *src)
+{
+    dst->x         =  src->x;
+    dst->y         =  src->y;
+    dst->w         =  src->w;
+    dst->h         =  src->h;
+    dst->nb_colors =  src->nb_colors;
+    dst->type      =  src->type;
+    dst->flags     =  src->flags;
+
+    switch (dst->type) {
+    case AV_SUBTITLE_FMT_BITMAP:
+
+        if (src->h > 0 && src->w > 0 && src->data[0]) {
+            AVBufferRef *buf = av_buffer_allocz(src->h * src->linesize[0]);
+            memcpy(buf->data, src->data[0], buf->size);
+
+            dst->buf[0] = buf;
+            dst->linesize[0] = src->linesize[0];
+        }
+
+        if (src->data[1]) {
+            uint32_t *pal = (uint32_t *)src->data[1];
+
+            for (unsigned i = 0; i < 256; i++) {
+                dst->pal[i] = pal[i];
+            }
+        }
+
+        break;
+    case AV_SUBTITLE_FMT_TEXT:
+
+        if (src->text) {
+            dst->text = av_strdup(src->text);
+            if (!dst->text)
+                return AVERROR(ENOMEM);
+        }
+
+        break;
+    case AV_SUBTITLE_FMT_ASS:
+
+        if (src->ass) {
+            dst->ass = av_strdup(src->ass);
+            if (!dst->ass)
+                return AVERROR(ENOMEM);
+        }
+
+        break;
+    default:
+
+        av_log(NULL, AV_LOG_ERROR, "Subtitle area has invalid format: %d", dst->type);
+        return AVERROR(EINVAL);
+    }
+
+    return 0;
+}
+
+/**
+ * Copies subtitle data from AVSubtitle (deprecated) to AVFrame
+ *
+ * @note This is a compatibility method for conversion to the legacy API
+ */
+int ff_frame_put_subtitle(AVFrame *frame, const AVSubtitle *sub)
+{
+    frame->format = sub->format;
+    frame->subtitle_timing.start_pts = sub->pts;
+    frame->subtitle_timing.start_pts += av_rescale_q(sub->start_display_time, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q);
+    frame->subtitle_timing.duration = av_rescale_q(sub->end_display_time - sub->start_display_time, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q);
+
+    if (sub->num_rects) {
+        frame->subtitle_areas = av_malloc_array(sub->num_rects, sizeof(AVSubtitleArea*));
+        if (!frame->subtitle_areas)
+            return AVERROR(ENOMEM);
+
+        for (unsigned i = 0; i < sub->num_rects; i++) {
+            int ret;
+            frame->subtitle_areas[i] = av_mallocz(sizeof(AVSubtitleArea));
+            if (!frame->subtitle_areas[i])
+                return AVERROR(ENOMEM);
+            ret = subtitle_rect2area(frame->subtitle_areas[i], sub->rects[i]);
+            if (ret < 0) {
+                frame->num_subtitle_areas = i;
+                return ret;
+            }
+        }
+    }
+
+    frame->num_subtitle_areas = sub->num_rects;
+    return 0;
+}
+
+/**
+ * Copies subtitle data from AVFrame to AVSubtitle (deprecated)
+ *
+ * @note This is a compatibility method for conversion to the legacy API
+ */
+int ff_frame_get_subtitle(AVSubtitle *sub, AVFrame *frame)
+{
+    const int64_t duration_ms = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, (AVRational){ 1, 1000 });
+
+    sub->start_display_time = 0;
+    sub->end_display_time = (int32_t)duration_ms;
+    sub->pts = frame->subtitle_timing.start_pts;
+
+    if (frame->num_subtitle_areas) {
+        sub->rects = av_malloc_array(frame->num_subtitle_areas, sizeof(AVSubtitleRect*));
+        if (!sub->rects)
+            return AVERROR(ENOMEM);
+
+        for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+            int ret;
+            sub->rects[i] = av_mallocz(sizeof(AVSubtitleRect));
+            ret = subtitle_area2rect(sub->rects[i], frame->subtitle_areas[i]);
+            if (ret < 0) {
+                sub->num_rects = i;
+                return ret;
+            }
+        }
+    }
+
+    sub->num_rects = frame->num_subtitle_areas;
+    return 0;
+}
+
 int av_get_audio_frame_duration2(AVCodecParameters *par, int frame_bytes)
 {
     int duration = get_audio_frame_duration(par->codec_id, par->sample_rate,
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v3 04/26] avfilter/subtitles: Update vf_subtitles to use new decoding api
  2022-01-20  3:25   ` [FFmpeg-devel] [PATCH v3 00/26] Subtitle Filtering 2022 ffmpegagent
                       ` (2 preceding siblings ...)
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 03/26] avcodec/subtitles: Introduce new frame-based subtitle decoding API ffmpegagent
@ 2022-01-20  3:25     ` ffmpegagent
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 05/26] avcodec, avutil: Move ass helper functions to avutil as avpriv_ and extend ass dialog parsing ffmpegagent
                       ` (22 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  3:25 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavfilter/vf_subtitles.c | 56 +++++++++++++++++++++++++++++---------
 1 file changed, 43 insertions(+), 13 deletions(-)

diff --git a/libavfilter/vf_subtitles.c b/libavfilter/vf_subtitles.c
index 3fc4eeb63d..25e217e845 100644
--- a/libavfilter/vf_subtitles.c
+++ b/libavfilter/vf_subtitles.c
@@ -35,14 +35,12 @@
 # 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"
 
 typedef struct AssContext {
     const AVClass *class;
@@ -292,6 +290,29 @@ static int attachment_is_font(AVStream * st)
     return 0;
 }
 
+static int decode(AVCodecContext *avctx, AVFrame *frame, int *got_frame, AVPacket *pkt)
+{
+    int ret;
+
+    *got_frame = 0;
+
+    if (pkt) {
+        ret = avcodec_send_packet(avctx, pkt);
+        // In particular, we don't expect AVERROR(EAGAIN), because we read all
+        // decoded frames with avcodec_receive_frame() until done.
+        if (ret < 0 && ret != AVERROR_EOF)
+            return ret;
+    }
+
+    ret = avcodec_receive_frame(avctx, frame);
+    if (ret < 0 && ret != AVERROR(EAGAIN))
+        return ret;
+    if (ret >= 0)
+        *got_frame = 1;
+
+    return 0;
+}
+
 AVFILTER_DEFINE_CLASS(subtitles);
 
 static av_cold int init_subtitles(AVFilterContext *ctx)
@@ -306,6 +327,7 @@ static av_cold int init_subtitles(AVFilterContext *ctx)
     AVStream *st;
     AVPacket pkt;
     AssContext *ass = ctx->priv;
+    enum AVSubtitleType subtitle_format;
 
     /* Init libass */
     ret = init(ctx);
@@ -386,13 +408,17 @@ static av_cold int init_subtitles(AVFilterContext *ctx)
         ret = AVERROR_DECODER_NOT_FOUND;
         goto end;
     }
+
     dec_desc = avcodec_descriptor_get(st->codecpar->codec_id);
-    if (dec_desc && !(dec_desc->props & AV_CODEC_PROP_TEXT_SUB)) {
+    subtitle_format = avcodec_descriptor_get_subtitle_format(dec_desc);
+
+    if (subtitle_format != AV_SUBTITLE_FMT_ASS) {
         av_log(ctx, AV_LOG_ERROR,
-               "Only text based subtitles are currently supported\n");
-        ret = AVERROR_PATCHWELCOME;
+               "Only text based subtitles are supported by this filter\n");
+        ret = AVERROR_INVALIDDATA;
         goto end;
     }
+
     if (ass->charenc)
         av_dict_set(&codec_opts, "sub_charenc", ass->charenc, 0);
 
@@ -448,27 +474,31 @@ static av_cold int init_subtitles(AVFilterContext *ctx)
                                   dec_ctx->subtitle_header_size);
     while (av_read_frame(fmt, &pkt) >= 0) {
         int i, got_subtitle;
-        AVSubtitle sub = {0};
+        AVFrame *sub = av_frame_alloc();
+        if (!sub) {
+            ret = AVERROR(ENOMEM);
+            goto end;
+        }
 
         if (pkt.stream_index == sid) {
-            ret = avcodec_decode_subtitle2(dec_ctx, &sub, &got_subtitle, &pkt);
+            ret = decode(dec_ctx, sub, &got_subtitle, &pkt);
             if (ret < 0) {
                 av_log(ctx, AV_LOG_WARNING, "Error decoding: %s (ignored)\n",
                        av_err2str(ret));
             } else if (got_subtitle) {
-                const int64_t start_time = av_rescale_q(sub.pts, AV_TIME_BASE_Q, av_make_q(1, 1000));
-                const int64_t duration   = sub.end_display_time;
-                for (i = 0; i < sub.num_rects; i++) {
-                    char *ass_line = sub.rects[i]->ass;
+                const int64_t start_time = av_rescale_q(sub->subtitle_timing.start_pts, AV_TIME_BASE_Q, av_make_q(1, 1000));
+                const int64_t duration   = av_rescale_q(sub->subtitle_timing.duration, AV_TIME_BASE_Q, av_make_q(1, 1000));
+                for (i = 0; i < sub->num_subtitle_areas; i++) {
+                    char *ass_line = sub->subtitle_areas[i]->ass;
                     if (!ass_line)
-                        break;
+                        continue;
                     ass_process_chunk(ass->track, ass_line, strlen(ass_line),
                                       start_time, duration);
                 }
             }
         }
         av_packet_unref(&pkt);
-        avsubtitle_free(&sub);
+        av_frame_free(&sub);
     }
 
 end:
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v3 05/26] avcodec, avutil: Move ass helper functions to avutil as avpriv_ and extend ass dialog parsing
  2022-01-20  3:25   ` [FFmpeg-devel] [PATCH v3 00/26] Subtitle Filtering 2022 ffmpegagent
                       ` (3 preceding siblings ...)
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 04/26] avfilter/subtitles: Update vf_subtitles to use new decoding api ffmpegagent
@ 2022-01-20  3:25     ` ffmpegagent
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 06/26] avcodec/subtitles: Replace deprecated enum values ffmpegagent
                       ` (21 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  3:25 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Also add

- hard_space callback (for upcoming fix)
- extensible callback (for future extension)

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/Makefile                           |  56 +++----
 libavcodec/ass.h                              | 144 ++++++------------
 libavcodec/assdec.c                           |   2 +-
 libavcodec/assenc.c                           |   2 +-
 libavcodec/ccaption_dec.c                     |  19 +--
 libavcodec/jacosubdec.c                       |   2 +-
 libavcodec/libaribb24.c                       |   2 +-
 libavcodec/libzvbi-teletextdec.c              |  14 +-
 libavcodec/microdvddec.c                      |   7 +-
 libavcodec/movtextdec.c                       |   3 +-
 libavcodec/movtextenc.c                       |  20 +--
 libavcodec/mpl2dec.c                          |   2 +-
 libavcodec/realtextdec.c                      |   2 +-
 libavcodec/samidec.c                          |   2 +-
 libavcodec/srtdec.c                           |   2 +-
 libavcodec/srtenc.c                           |  16 +-
 libavcodec/subviewerdec.c                     |   2 +-
 libavcodec/textdec.c                          |   4 +-
 libavcodec/ttmlenc.c                          |  15 +-
 libavcodec/webvttdec.c                        |   2 +-
 libavcodec/webvttenc.c                        |  16 +-
 libavutil/Makefile                            |   2 +
 {libavcodec => libavutil}/ass.c               |  91 +++++------
 libavutil/ass_internal.h                      | 135 ++++++++++++++++
 {libavcodec => libavutil}/ass_split.c         |  30 ++--
 .../ass_split_internal.h                      |  32 ++--
 26 files changed, 355 insertions(+), 269 deletions(-)
 rename {libavcodec => libavutil}/ass.c (65%)
 create mode 100644 libavutil/ass_internal.h
 rename {libavcodec => libavutil}/ass_split.c (94%)
 rename libavcodec/ass_split.h => libavutil/ass_split_internal.h (86%)

diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index cfc70a3eaf..80bf8ff2d2 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -215,10 +215,10 @@ OBJS-$(CONFIG_APNG_DECODER)            += png.o pngdec.o pngdsp.o
 OBJS-$(CONFIG_APNG_ENCODER)            += png.o pngenc.o
 OBJS-$(CONFIG_ARBC_DECODER)            += arbc.o
 OBJS-$(CONFIG_ARGO_DECODER)            += argo.o
-OBJS-$(CONFIG_SSA_DECODER)             += assdec.o ass.o
-OBJS-$(CONFIG_SSA_ENCODER)             += assenc.o ass.o
-OBJS-$(CONFIG_ASS_DECODER)             += assdec.o ass.o
-OBJS-$(CONFIG_ASS_ENCODER)             += assenc.o ass.o
+OBJS-$(CONFIG_SSA_DECODER)             += assdec.o
+OBJS-$(CONFIG_SSA_ENCODER)             += assenc.o
+OBJS-$(CONFIG_ASS_DECODER)             += assdec.o
+OBJS-$(CONFIG_ASS_ENCODER)             += assenc.o
 OBJS-$(CONFIG_ASV1_DECODER)            += asvdec.o asv.o mpeg12data.o
 OBJS-$(CONFIG_ASV1_ENCODER)            += asvenc.o asv.o mpeg12data.o
 OBJS-$(CONFIG_ASV2_DECODER)            += asvdec.o asv.o mpeg12data.o
@@ -259,7 +259,7 @@ OBJS-$(CONFIG_BRENDER_PIX_DECODER)     += brenderpix.o
 OBJS-$(CONFIG_C93_DECODER)             += c93.o
 OBJS-$(CONFIG_CAVS_DECODER)            += cavs.o cavsdec.o cavsdsp.o \
                                           cavsdata.o
-OBJS-$(CONFIG_CCAPTION_DECODER)        += ccaption_dec.o ass.o
+OBJS-$(CONFIG_CCAPTION_DECODER)        += ccaption_dec.o
 OBJS-$(CONFIG_CDGRAPHICS_DECODER)      += cdgraphics.o
 OBJS-$(CONFIG_CDTOONS_DECODER)         += cdtoons.o
 OBJS-$(CONFIG_CDXL_DECODER)            += cdxl.o
@@ -434,7 +434,7 @@ OBJS-$(CONFIG_INTERPLAY_ACM_DECODER)   += interplayacm.o
 OBJS-$(CONFIG_INTERPLAY_DPCM_DECODER)  += dpcm.o
 OBJS-$(CONFIG_INTERPLAY_VIDEO_DECODER) += interplayvideo.o
 OBJS-$(CONFIG_IPU_DECODER)             += mpeg12dec.o mpeg12.o mpeg12data.o
-OBJS-$(CONFIG_JACOSUB_DECODER)         += jacosubdec.o ass.o
+OBJS-$(CONFIG_JACOSUB_DECODER)         += jacosubdec.o
 OBJS-$(CONFIG_JPEG2000_ENCODER)        += j2kenc.o mqcenc.o mqc.o jpeg2000.o \
                                           jpeg2000dwt.o
 OBJS-$(CONFIG_JPEG2000_DECODER)        += jpeg2000dec.o jpeg2000.o jpeg2000dsp.o \
@@ -456,7 +456,7 @@ OBJS-$(CONFIG_MAGICYUV_ENCODER)        += magicyuvenc.o
 OBJS-$(CONFIG_MDEC_DECODER)            += mdec.o mpeg12.o mpeg12data.o
 OBJS-$(CONFIG_METASOUND_DECODER)       += metasound.o metasound_data.o \
                                           twinvq.o
-OBJS-$(CONFIG_MICRODVD_DECODER)        += microdvddec.o ass.o
+OBJS-$(CONFIG_MICRODVD_DECODER)        += microdvddec.o
 OBJS-$(CONFIG_MIMIC_DECODER)           += mimic.o
 OBJS-$(CONFIG_MJPEG_DECODER)           += mjpegdec.o mjpegdec_common.o
 OBJS-$(CONFIG_MJPEG_QSV_DECODER)       += qsvdec.o
@@ -471,8 +471,8 @@ OBJS-$(CONFIG_MLP_ENCODER)             += mlpenc.o mlp.o
 OBJS-$(CONFIG_MMVIDEO_DECODER)         += mmvideo.o
 OBJS-$(CONFIG_MOBICLIP_DECODER)        += mobiclip.o
 OBJS-$(CONFIG_MOTIONPIXELS_DECODER)    += motionpixels.o
-OBJS-$(CONFIG_MOVTEXT_DECODER)         += movtextdec.o ass.o
-OBJS-$(CONFIG_MOVTEXT_ENCODER)         += movtextenc.o ass_split.o
+OBJS-$(CONFIG_MOVTEXT_DECODER)         += movtextdec.o
+OBJS-$(CONFIG_MOVTEXT_ENCODER)         += movtextenc.o
 OBJS-$(CONFIG_MP1_DECODER)             += mpegaudiodec_fixed.o
 OBJS-$(CONFIG_MP1FLOAT_DECODER)        += mpegaudiodec_float.o
 OBJS-$(CONFIG_MP2_DECODER)             += mpegaudiodec_fixed.o
@@ -513,7 +513,7 @@ OBJS-$(CONFIG_MPEG4_MEDIACODEC_DECODER) += mediacodecdec.o
 OBJS-$(CONFIG_MPEG4_OMX_ENCODER)       += omx.o
 OBJS-$(CONFIG_MPEG4_V4L2M2M_DECODER)   += v4l2_m2m_dec.o
 OBJS-$(CONFIG_MPEG4_V4L2M2M_ENCODER)   += v4l2_m2m_enc.o
-OBJS-$(CONFIG_MPL2_DECODER)            += mpl2dec.o ass.o
+OBJS-$(CONFIG_MPL2_DECODER)            += mpl2dec.o
 OBJS-$(CONFIG_MSA1_DECODER)            += mss3.o
 OBJS-$(CONFIG_MSCC_DECODER)            += mscc.o
 OBJS-$(CONFIG_MSMPEG4V1_DECODER)       += msmpeg4dec.o msmpeg4.o msmpeg4data.o
@@ -566,7 +566,7 @@ OBJS-$(CONFIG_PGX_DECODER)             += pgxdec.o
 OBJS-$(CONFIG_PHOTOCD_DECODER)         += photocd.o
 OBJS-$(CONFIG_PICTOR_DECODER)          += pictordec.o cga_data.o
 OBJS-$(CONFIG_PIXLET_DECODER)          += pixlet.o
-OBJS-$(CONFIG_PJS_DECODER)             += textdec.o ass.o
+OBJS-$(CONFIG_PJS_DECODER)             += textdec.o
 OBJS-$(CONFIG_PNG_DECODER)             += png.o pngdec.o pngdsp.o
 OBJS-$(CONFIG_PNG_ENCODER)             += png.o pngenc.o
 OBJS-$(CONFIG_PPM_DECODER)             += pnmdec.o pnm.o
@@ -599,7 +599,7 @@ OBJS-$(CONFIG_RALF_DECODER)            += ralf.o
 OBJS-$(CONFIG_RASC_DECODER)            += rasc.o
 OBJS-$(CONFIG_RAWVIDEO_DECODER)        += rawdec.o
 OBJS-$(CONFIG_RAWVIDEO_ENCODER)        += rawenc.o
-OBJS-$(CONFIG_REALTEXT_DECODER)        += realtextdec.o ass.o
+OBJS-$(CONFIG_REALTEXT_DECODER)        += realtextdec.o
 OBJS-$(CONFIG_RL2_DECODER)             += rl2.o
 OBJS-$(CONFIG_ROQ_DECODER)             += roqvideodec.o roqvideo.o
 OBJS-$(CONFIG_ROQ_ENCODER)             += roqvideoenc.o roqvideo.o elbg.o
@@ -614,7 +614,7 @@ OBJS-$(CONFIG_RV20_DECODER)            += rv10.o
 OBJS-$(CONFIG_RV20_ENCODER)            += rv20enc.o
 OBJS-$(CONFIG_RV30_DECODER)            += rv30.o rv34.o rv30dsp.o
 OBJS-$(CONFIG_RV40_DECODER)            += rv40.o rv34.o rv40dsp.o
-OBJS-$(CONFIG_SAMI_DECODER)            += samidec.o ass.o htmlsubtitles.o
+OBJS-$(CONFIG_SAMI_DECODER)            += samidec.o htmlsubtitles.o
 OBJS-$(CONFIG_S302M_DECODER)           += s302m.o
 OBJS-$(CONFIG_S302M_ENCODER)           += s302menc.o
 OBJS-$(CONFIG_SANM_DECODER)            += sanm.o
@@ -649,13 +649,13 @@ OBJS-$(CONFIG_SPEEDHQ_ENCODER)         += speedhq.o mpeg12data.o mpeg12enc.o spe
 OBJS-$(CONFIG_SPEEX_DECODER)           += speexdec.o
 OBJS-$(CONFIG_SP5X_DECODER)            += sp5xdec.o
 OBJS-$(CONFIG_SRGC_DECODER)            += mscc.o
-OBJS-$(CONFIG_SRT_DECODER)             += srtdec.o ass.o htmlsubtitles.o
-OBJS-$(CONFIG_SRT_ENCODER)             += srtenc.o ass_split.o
-OBJS-$(CONFIG_STL_DECODER)             += textdec.o ass.o
-OBJS-$(CONFIG_SUBRIP_DECODER)          += srtdec.o ass.o htmlsubtitles.o
-OBJS-$(CONFIG_SUBRIP_ENCODER)          += srtenc.o ass_split.o
-OBJS-$(CONFIG_SUBVIEWER1_DECODER)      += textdec.o ass.o
-OBJS-$(CONFIG_SUBVIEWER_DECODER)       += subviewerdec.o ass.o
+OBJS-$(CONFIG_SRT_DECODER)             += srtdec.o htmlsubtitles.o
+OBJS-$(CONFIG_SRT_ENCODER)             += srtenc.o
+OBJS-$(CONFIG_STL_DECODER)             += textdec.o
+OBJS-$(CONFIG_SUBRIP_DECODER)          += srtdec.o htmlsubtitles.o
+OBJS-$(CONFIG_SUBRIP_ENCODER)          += srtenc.o
+OBJS-$(CONFIG_SUBVIEWER1_DECODER)      += textdec.o
+OBJS-$(CONFIG_SUBVIEWER_DECODER)       += subviewerdec.o
 OBJS-$(CONFIG_SUNRAST_DECODER)         += sunrast.o
 OBJS-$(CONFIG_SUNRAST_ENCODER)         += sunrastenc.o
 OBJS-$(CONFIG_LIBRSVG_DECODER)         += librsvgdec.o
@@ -665,8 +665,8 @@ OBJS-$(CONFIG_SVQ1_DECODER)            += svq1dec.o svq1.o h263data.o
 OBJS-$(CONFIG_SVQ1_ENCODER)            += svq1enc.o svq1.o  h263data.o  \
                                           h263.o ituh263enc.o
 OBJS-$(CONFIG_SVQ3_DECODER)            += svq3.o mpegutils.o h264data.o
-OBJS-$(CONFIG_TEXT_DECODER)            += textdec.o ass.o
-OBJS-$(CONFIG_TEXT_ENCODER)            += srtenc.o ass_split.o
+OBJS-$(CONFIG_TEXT_DECODER)            += textdec.o
+OBJS-$(CONFIG_TEXT_ENCODER)            += srtenc.o
 OBJS-$(CONFIG_TAK_DECODER)             += takdec.o tak.o takdsp.o
 OBJS-$(CONFIG_TARGA_DECODER)           += targa.o
 OBJS-$(CONFIG_TARGA_ENCODER)           += targaenc.o rle.o
@@ -686,7 +686,7 @@ OBJS-$(CONFIG_TSCC_DECODER)            += tscc.o msrledec.o
 OBJS-$(CONFIG_TSCC2_DECODER)           += tscc2.o
 OBJS-$(CONFIG_TTA_DECODER)             += tta.o ttadata.o ttadsp.o
 OBJS-$(CONFIG_TTA_ENCODER)             += ttaenc.o ttaencdsp.o ttadata.o
-OBJS-$(CONFIG_TTML_ENCODER)            += ttmlenc.o ass_split.o
+OBJS-$(CONFIG_TTML_ENCODER)            += ttmlenc.o
 OBJS-$(CONFIG_TWINVQ_DECODER)          += twinvqdec.o twinvq.o metasound_data.o
 OBJS-$(CONFIG_TXD_DECODER)             += txd.o
 OBJS-$(CONFIG_ULTI_DECODER)            += ulti.o
@@ -741,15 +741,15 @@ OBJS-$(CONFIG_VP9_MEDIACODEC_DECODER)  += mediacodecdec.o
 OBJS-$(CONFIG_VP9_RKMPP_DECODER)       += rkmppdec.o
 OBJS-$(CONFIG_VP9_VAAPI_ENCODER)       += vaapi_encode_vp9.o
 OBJS-$(CONFIG_VP9_QSV_ENCODER)         += qsvenc_vp9.o
-OBJS-$(CONFIG_VPLAYER_DECODER)         += textdec.o ass.o
+OBJS-$(CONFIG_VPLAYER_DECODER)         += textdec.o
 OBJS-$(CONFIG_VP9_V4L2M2M_DECODER)     += v4l2_m2m_dec.o
 OBJS-$(CONFIG_VQA_DECODER)             += vqavideo.o
 OBJS-$(CONFIG_WAVPACK_DECODER)         += wavpack.o wavpackdata.o dsd.o
 OBJS-$(CONFIG_WAVPACK_ENCODER)         += wavpackdata.o wavpackenc.o
 OBJS-$(CONFIG_WCMV_DECODER)            += wcmv.o
 OBJS-$(CONFIG_WEBP_DECODER)            += webp.o
-OBJS-$(CONFIG_WEBVTT_DECODER)          += webvttdec.o ass.o
-OBJS-$(CONFIG_WEBVTT_ENCODER)          += webvttenc.o ass_split.o
+OBJS-$(CONFIG_WEBVTT_DECODER)          += webvttdec.o
+OBJS-$(CONFIG_WEBVTT_ENCODER)          += webvttenc.o
 OBJS-$(CONFIG_WMALOSSLESS_DECODER)     += wmalosslessdec.o wma_common.o
 OBJS-$(CONFIG_WMAPRO_DECODER)          += wmaprodec.o wma.o wma_common.o
 OBJS-$(CONFIG_WMAV1_DECODER)           += wmadec.o wma.o wma_common.o aactab.o
@@ -1040,7 +1040,7 @@ OBJS-$(CONFIG_PCM_ALAW_AT_ENCODER)        += audiotoolboxenc.o
 OBJS-$(CONFIG_PCM_MULAW_AT_ENCODER)       += audiotoolboxenc.o
 OBJS-$(CONFIG_LIBAOM_AV1_DECODER)         += libaomdec.o
 OBJS-$(CONFIG_LIBAOM_AV1_ENCODER)         += libaomenc.o
-OBJS-$(CONFIG_LIBARIBB24_DECODER)         += libaribb24.o ass.o
+OBJS-$(CONFIG_LIBARIBB24_DECODER)         += libaribb24.o
 OBJS-$(CONFIG_LIBCELT_DECODER)            += libcelt_dec.o
 OBJS-$(CONFIG_LIBCODEC2_DECODER)          += libcodec2.o
 OBJS-$(CONFIG_LIBCODEC2_ENCODER)          += libcodec2.o
@@ -1091,7 +1091,7 @@ OBJS-$(CONFIG_LIBX265_ENCODER)            += libx265.o
 OBJS-$(CONFIG_LIBXAVS_ENCODER)            += libxavs.o
 OBJS-$(CONFIG_LIBXAVS2_ENCODER)           += libxavs2.o
 OBJS-$(CONFIG_LIBXVID_ENCODER)            += libxvid.o
-OBJS-$(CONFIG_LIBZVBI_TELETEXT_DECODER)   += libzvbi-teletextdec.o ass.o
+OBJS-$(CONFIG_LIBZVBI_TELETEXT_DECODER)   += libzvbi-teletextdec.o
 
 # parsers
 OBJS-$(CONFIG_AAC_LATM_PARSER)         += latm_parser.o
diff --git a/libavcodec/ass.h b/libavcodec/ass.h
index 2c260e4e78..8bc13d7ab8 100644
--- a/libavcodec/ass.h
+++ b/libavcodec/ass.h
@@ -23,117 +23,73 @@
 #define AVCODEC_ASS_H
 
 #include "avcodec.h"
-#include "libavutil/bprint.h"
-
-#define ASS_DEFAULT_PLAYRESX 384
-#define ASS_DEFAULT_PLAYRESY 288
-
-/**
- * @name Default values for ASS style
- * @{
- */
-#define ASS_DEFAULT_FONT        "Arial"
-#define ASS_DEFAULT_FONT_SIZE   16
-#define ASS_DEFAULT_COLOR       0xffffff
-#define ASS_DEFAULT_BACK_COLOR  0
-#define ASS_DEFAULT_BOLD        0
-#define ASS_DEFAULT_ITALIC      0
-#define ASS_DEFAULT_UNDERLINE   0
-#define ASS_DEFAULT_ALIGNMENT   2
-#define ASS_DEFAULT_BORDERSTYLE 1
-/** @} */
+#include "libavutil/ass_internal.h"
 
 typedef struct FFASSDecoderContext {
     int readorder;
 } FFASSDecoderContext;
 
-/**
- * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS.
- * Can specify all fields explicitly
- *
- * @param avctx pointer to the AVCodecContext
- * @param play_res_x subtitle frame width
- * @param play_res_y subtitle frame height
- * @param font name of the default font face to use
- * @param font_size default font size to use
- * @param primary_color default text color to use (ABGR)
- * @param secondary_color default secondary text color to use (ABGR)
- * @param outline_color default outline color to use (ABGR)
- * @param back_color default background color to use (ABGR)
- * @param bold 1 for bold text, 0 for normal text
- * @param italic 1 for italic text, 0 for normal text
- * @param underline 1 for underline text, 0 for normal text
- * @param border_style 1 for outline, 3 for opaque box
- * @param alignment position of the text (left, center, top...), defined after
- *                  the layout of the numpad (1-3 sub, 4-6 mid, 7-9 top)
- * @return >= 0 on success otherwise an error code <0
- */
-int ff_ass_subtitle_header_full(AVCodecContext *avctx,
+static inline int ff_ass_subtitle_header_full(AVCodecContext *avctx,
                                 int play_res_x, int play_res_y,
                                 const char *font, int font_size,
                                 int primary_color, int secondary_color,
                                 int outline_color, int back_color,
                                 int bold, int italic, int underline,
-                                int border_style, int alignment);
-/**
- * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS.
- *
- * @param avctx pointer to the AVCodecContext
- * @param font name of the default font face to use
- * @param font_size default font size to use
- * @param color default text color to use (ABGR)
- * @param back_color default background color to use (ABGR)
- * @param bold 1 for bold text, 0 for normal text
- * @param italic 1 for italic text, 0 for normal text
- * @param underline 1 for underline text, 0 for normal text
- * @param alignment position of the text (left, center, top...), defined after
- *                  the layout of the numpad (1-3 sub, 4-6 mid, 7-9 top)
- * @return >= 0 on success otherwise an error code <0
- */
-int ff_ass_subtitle_header(AVCodecContext *avctx,
-                           const char *font, int font_size,
-                           int color, int back_color,
-                           int bold, int italic, int underline,
-                           int border_style, int alignment);
+                                int border_style, int alignment)
+{
+    avctx->subtitle_header = (uint8_t *)avpriv_ass_get_subtitle_header_full(
+                                play_res_x, play_res_y, font, font_size,
+                                primary_color, secondary_color, outline_color,
+                                back_color, bold,italic,underline,border_style,alignment,
+                                !(avctx->flags & AV_CODEC_FLAG_BITEXACT));
 
-/**
- * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS
- * with default style.
- *
- * @param avctx pointer to the AVCodecContext
- * @return >= 0 on success otherwise an error code <0
- */
-int ff_ass_subtitle_header_default(AVCodecContext *avctx);
+    if (!avctx->subtitle_header)
+        return AVERROR(ENOMEM);
+    avctx->subtitle_header_size = strlen((char *)avctx->subtitle_header);
+    return 0;
+}
 
-/**
- * Craft an ASS dialog string.
- */
-char *ff_ass_get_dialog(int readorder, int layer, const char *style,
-                        const char *speaker, const char *text);
+static inline int ff_ass_subtitle_header_default(AVCodecContext *avctx)
+{
+    avctx->subtitle_header = (uint8_t *)avpriv_ass_get_subtitle_header_default(!(avctx->flags & AV_CODEC_FLAG_BITEXACT));
+
+    if (!avctx->subtitle_header)
+        return AVERROR(ENOMEM);
+    avctx->subtitle_header_size = strlen((char *)avctx->subtitle_header);
+    return 0;
+}
+
+static inline void ff_ass_decoder_flush(AVCodecContext *avctx)
+{
+    FFASSDecoderContext *s = avctx->priv_data;
+    if (!(avctx->flags2 & AV_CODEC_FLAG2_RO_FLUSH_NOOP))
+        s->readorder = 0;
+}
 
 /**
  * Add an ASS dialog to a subtitle.
  */
-int ff_ass_add_rect(AVSubtitle *sub, const char *dialog,
+static inline int avpriv_ass_add_rect(AVSubtitle *sub, const char *dialog,
                     int readorder, int layer, const char *style,
-                    const char *speaker);
+                    const char *speaker)
+{
+    char *ass_str;
+    AVSubtitleRect **rects;
 
-/**
- * Helper to flush a text subtitles decoder making use of the
- * FFASSDecoderContext.
- */
-void ff_ass_decoder_flush(AVCodecContext *avctx);
+    rects = av_realloc_array(sub->rects, sub->num_rects+1, sizeof(*sub->rects));
+    if (!rects)
+        return AVERROR(ENOMEM);
+    sub->rects = rects;
+    rects[sub->num_rects]       = av_mallocz(sizeof(*rects[0]));
+    if (!rects[sub->num_rects])
+        return AVERROR(ENOMEM);
+    rects[sub->num_rects]->type = SUBTITLE_ASS;
+    ass_str = avpriv_ass_get_dialog(readorder, layer, style, speaker, dialog);
+    if (!ass_str)
+        return AVERROR(ENOMEM);
+    rects[sub->num_rects]->ass = ass_str;
+    sub->num_rects++;
+    return 0;
+}
 
-/**
- * Escape a text subtitle using ASS syntax into an AVBPrint buffer.
- * Newline characters will be escaped to \N.
- *
- * @param buf pointer to an initialized AVBPrint buffer
- * @param p source text
- * @param size size of the source text
- * @param linebreaks additional newline chars, which will be escaped to \N
- * @param keep_ass_markup braces and backslash will not be escaped if set
- */
-void ff_ass_bprint_text_event(AVBPrint *buf, const char *p, int size,
-                             const char *linebreaks, int keep_ass_markup);
 #endif /* AVCODEC_ASS_H */
diff --git a/libavcodec/assdec.c b/libavcodec/assdec.c
index 319279490c..7802a44e71 100644
--- a/libavcodec/assdec.c
+++ b/libavcodec/assdec.c
@@ -22,7 +22,7 @@
 #include <string.h>
 
 #include "avcodec.h"
-#include "ass.h"
+#include "libavutil/ass_internal.h"
 #include "internal.h"
 #include "libavutil/internal.h"
 #include "libavutil/mem.h"
diff --git a/libavcodec/assenc.c b/libavcodec/assenc.c
index a6d107ded2..b0e475834b 100644
--- a/libavcodec/assenc.c
+++ b/libavcodec/assenc.c
@@ -22,7 +22,7 @@
 #include <string.h>
 
 #include "avcodec.h"
-#include "ass.h"
+#include "libavutil/ass_internal.h"
 #include "internal.h"
 #include "libavutil/avstring.h"
 #include "libavutil/internal.h"
diff --git a/libavcodec/ccaption_dec.c b/libavcodec/ccaption_dec.c
index 27c61527f6..27eef75657 100644
--- a/libavcodec/ccaption_dec.c
+++ b/libavcodec/ccaption_dec.c
@@ -272,15 +272,12 @@ static av_cold int init_decoder(AVCodecContext *avctx)
     ctx->bg_color = CCCOL_BLACK;
     ctx->rollup = 2;
     ctx->cursor_row = 10;
-    ret = ff_ass_subtitle_header(avctx, "Monospace",
+    ret = ff_ass_subtitle_header_full(avctx, ASS_DEFAULT_PLAYRESX, ASS_DEFAULT_PLAYRESY, "Monospace",
                                  ASS_DEFAULT_FONT_SIZE,
-                                 ASS_DEFAULT_COLOR,
-                                 ASS_DEFAULT_BACK_COLOR,
-                                 ASS_DEFAULT_BOLD,
-                                 ASS_DEFAULT_ITALIC,
-                                 ASS_DEFAULT_UNDERLINE,
-                                 3,
-                                 ASS_DEFAULT_ALIGNMENT);
+                                 ASS_DEFAULT_COLOR, ASS_DEFAULT_COLOR,
+                                 ASS_DEFAULT_BACK_COLOR, ASS_DEFAULT_BACK_COLOR,
+                                 ASS_DEFAULT_BOLD, ASS_DEFAULT_ITALIC, ASS_DEFAULT_UNDERLINE,
+                                 3, ASS_DEFAULT_ALIGNMENT);
     if (ret < 0) {
         return ret;
     }
@@ -886,7 +883,7 @@ static int decode(AVCodecContext *avctx, void *data, int *got_sub, AVPacket *avp
                                                      AV_TIME_BASE_Q, ms_tb);
             else
                 sub->end_display_time = -1;
-            ret = ff_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL);
+            ret = avpriv_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL);
             if (ret < 0)
                 return ret;
             ctx->last_real_time = sub->pts;
@@ -896,7 +893,7 @@ static int decode(AVCodecContext *avctx, void *data, int *got_sub, AVPacket *avp
 
     if (!bptr && !ctx->real_time && ctx->buffer[!ctx->buffer_index].str[0]) {
         bidx = !ctx->buffer_index;
-        ret = ff_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL);
         if (ret < 0)
             return ret;
         sub->pts = ctx->buffer_time[1];
@@ -914,7 +911,7 @@ static int decode(AVCodecContext *avctx, void *data, int *got_sub, AVPacket *avp
         capture_screen(ctx);
         ctx->buffer_changed = 0;
 
-        ret = ff_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL);
         if (ret < 0)
             return ret;
         sub->end_display_time = -1;
diff --git a/libavcodec/jacosubdec.c b/libavcodec/jacosubdec.c
index 698895a86b..6a53ec3e34 100644
--- a/libavcodec/jacosubdec.c
+++ b/libavcodec/jacosubdec.c
@@ -183,7 +183,7 @@ static int jacosub_decode_frame(AVCodecContext *avctx,
 
         av_bprint_init(&buffer, JSS_MAX_LINESIZE, JSS_MAX_LINESIZE);
         jacosub_to_ass(avctx, &buffer, ptr);
-        ret = ff_ass_add_rect(sub, buffer.str, s->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, buffer.str, s->readorder++, 0, NULL, NULL);
         av_bprint_finalize(&buffer, NULL);
         if (ret < 0)
             return ret;
diff --git a/libavcodec/libaribb24.c b/libavcodec/libaribb24.c
index 0766c0079d..3fb7e5f16e 100644
--- a/libavcodec/libaribb24.c
+++ b/libavcodec/libaribb24.c
@@ -273,7 +273,7 @@ next_region:
         av_log(avctx, AV_LOG_DEBUG, "Styled ASS line: %s\n",
                buf.str);
 
-        ret = ff_ass_add_rect(sub, buf.str, b24->read_order++,
+        ret = avpriv_ass_add_rect(sub, buf.str, b24->read_order++,
                               0, NULL, NULL);
     }
 
diff --git a/libavcodec/libzvbi-teletextdec.c b/libavcodec/libzvbi-teletextdec.c
index 1073d6a0bd..bd9edc34d7 100644
--- a/libavcodec/libzvbi-teletextdec.c
+++ b/libavcodec/libzvbi-teletextdec.c
@@ -152,12 +152,12 @@ static char *create_ass_text(TeletextContext *ctx, const char *text)
     AVBPrint buf;
 
     av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
-    ff_ass_bprint_text_event(&buf, text, strlen(text), "", 0);
+    avpriv_ass_bprint_text_event(&buf, text, strlen(text), "", 0);
     if (!av_bprint_is_complete(&buf)) {
         av_bprint_finalize(&buf, NULL);
         return NULL;
     }
-    dialog = ff_ass_get_dialog(ctx->readorder++, 0, NULL, NULL, buf.str);
+    dialog = avpriv_ass_get_dialog(ctx->readorder++, 0, NULL, NULL, buf.str);
     av_bprint_finalize(&buf, NULL);
     return dialog;
 }
@@ -224,7 +224,7 @@ static int gen_sub_text(TeletextContext *ctx, AVSubtitleRect *sub_rect, vbi_page
         }
         av_log(ctx, AV_LOG_DEBUG, "subtext:%s:txetbus\n", sub_rect->ass);
     } else {
-        sub_rect->type = SUBTITLE_NONE;
+        sub_rect->type = AV_SUBTITLE_FMT_NONE;
     }
     av_bprint_finalize(&buf, NULL);
     return 0;
@@ -394,7 +394,7 @@ static int gen_sub_ass(TeletextContext *ctx, AVSubtitleRect *sub_rect, vbi_page
 
     if (buf.len) {
         sub_rect->type = SUBTITLE_ASS;
-        sub_rect->ass = ff_ass_get_dialog(ctx->readorder++, 0, is_subtitle_page ? "Subtitle" : "Teletext", NULL, buf.str);
+        sub_rect->ass = avpriv_ass_get_dialog(ctx->readorder++, 0, is_subtitle_page ? "Subtitle" : "Teletext", NULL, buf.str);
 
         if (!sub_rect->ass) {
             av_bprint_finalize(&buf, NULL);
@@ -402,7 +402,7 @@ static int gen_sub_ass(TeletextContext *ctx, AVSubtitleRect *sub_rect, vbi_page
         }
         av_log(ctx, AV_LOG_DEBUG, "subtext:%s:txetbus\n", sub_rect->ass);
     } else {
-        sub_rect->type = SUBTITLE_NONE;
+        sub_rect->type = AV_SUBTITLE_FMT_NONE;
     }
     av_bprint_finalize(&buf, NULL);
     return 0;
@@ -462,7 +462,7 @@ static int gen_sub_bitmap(TeletextContext *ctx, AVSubtitleRect *sub_rect, vbi_pa
 
     if (vc >= vcend) {
         av_log(ctx, AV_LOG_DEBUG, "dropping empty page %3x\n", page->pgno);
-        sub_rect->type = SUBTITLE_NONE;
+        sub_rect->type = AV_SUBTITLE_FMT_NONE;
         return 0;
     }
 
@@ -695,7 +695,7 @@ static int teletext_decode_frame(AVCodecContext *avctx, void *data, int *got_sub
         sub->num_rects = 0;
         sub->pts = ctx->pages->pts;
 
-        if (ctx->pages->sub_rect->type != SUBTITLE_NONE) {
+        if (ctx->pages->sub_rect->type != AV_SUBTITLE_FMT_NONE) {
             sub->rects = av_malloc(sizeof(*sub->rects));
             if (sub->rects) {
                 sub->num_rects = 1;
diff --git a/libavcodec/microdvddec.c b/libavcodec/microdvddec.c
index c45fe043bf..fc09db8997 100644
--- a/libavcodec/microdvddec.c
+++ b/libavcodec/microdvddec.c
@@ -310,7 +310,7 @@ static int microdvd_decode_frame(AVCodecContext *avctx,
         }
     }
     if (new_line.len) {
-        int ret = ff_ass_add_rect(sub, new_line.str, s->readorder++, 0, NULL, NULL);
+        int ret = avpriv_ass_add_rect(sub, new_line.str, s->readorder++, 0, NULL, NULL);
         av_bprint_finalize(&new_line, NULL);
         if (ret < 0)
             return ret;
@@ -363,8 +363,9 @@ static int microdvd_init(AVCodecContext *avctx)
             }
         }
     }
-    return ff_ass_subtitle_header(avctx, font_buf.str, font_size, color,
-                                  ASS_DEFAULT_BACK_COLOR, bold, italic,
+    return ff_ass_subtitle_header_full(avctx, ASS_DEFAULT_PLAYRESX, ASS_DEFAULT_PLAYRESY,
+                                  font_buf.str, font_size, color, color,
+                                  ASS_DEFAULT_BACK_COLOR, ASS_DEFAULT_BACK_COLOR, bold, italic,
                                   underline, ASS_DEFAULT_BORDERSTYLE,
                                   alignment);
 }
diff --git a/libavcodec/movtextdec.c b/libavcodec/movtextdec.c
index 825632ca9b..9b17bac9ea 100644
--- a/libavcodec/movtextdec.c
+++ b/libavcodec/movtextdec.c
@@ -22,7 +22,6 @@
 #include "avcodec.h"
 #include "ass.h"
 #include "libavutil/opt.h"
-#include "libavutil/avstring.h"
 #include "libavutil/common.h"
 #include "libavutil/bprint.h"
 #include "libavutil/intreadwrite.h"
@@ -554,7 +553,7 @@ static int mov_text_decode_frame(AVCodecContext *avctx,
     } else
         text_to_ass(&buf, ptr, end, avctx);
 
-    ret = ff_ass_add_rect(sub, buf.str, m->readorder++, 0, NULL, NULL);
+    ret = avpriv_ass_add_rect(sub, buf.str, m->readorder++, 0, NULL, NULL);
     av_bprint_finalize(&buf, NULL);
     if (ret < 0)
         return ret;
diff --git a/libavcodec/movtextenc.c b/libavcodec/movtextenc.c
index 221cd76fea..d506ed5c37 100644
--- a/libavcodec/movtextenc.c
+++ b/libavcodec/movtextenc.c
@@ -26,8 +26,8 @@
 #include "libavutil/intreadwrite.h"
 #include "libavutil/mem.h"
 #include "libavutil/common.h"
-#include "ass_split.h"
-#include "ass.h"
+#include "libavutil/ass_split_internal.h"
+#include "libavutil/ass_internal.h"
 #include "bytestream.h"
 #include "internal.h"
 
@@ -167,7 +167,7 @@ static int mov_text_encode_close(AVCodecContext *avctx)
 {
     MovTextContext *s = avctx->priv_data;
 
-    ff_ass_split_free(s->ass_ctx);
+    avpriv_ass_split_free(s->ass_ctx);
     av_freep(&s->style_attributes);
     av_freep(&s->fonts);
     av_bprint_finalize(&s->buffer, NULL);
@@ -222,7 +222,7 @@ static int encode_sample_description(AVCodecContext *avctx)
     else
         s->font_scale_factor = 1;
 
-    style = ff_ass_style_get(s->ass_ctx, "Default");
+    style = avpriv_ass_style_get(s->ass_ctx, "Default");
     if (!style && ass->styles_count) {
         style = &ass->styles[0];
     }
@@ -329,7 +329,7 @@ static av_cold int mov_text_encode_init(AVCodecContext *avctx)
 
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
 
-    s->ass_ctx = ff_ass_split(avctx->subtitle_header);
+    s->ass_ctx = avpriv_ass_split(avctx->subtitle_header);
     if (!s->ass_ctx)
         return AVERROR_INVALIDDATA;
     ret = encode_sample_description(avctx);
@@ -566,7 +566,7 @@ static void mov_text_ass_style_set(MovTextContext *s, ASSStyle *style)
 
 static void mov_text_dialog(MovTextContext *s, ASSDialog *dialog)
 {
-    ASSStyle *style = ff_ass_style_get(s->ass_ctx, dialog->style);
+    ASSStyle *style = avpriv_ass_style_get(s->ass_ctx, dialog->style);
 
     s->ass_dialog_style = style;
     mov_text_ass_style_set(s, style);
@@ -580,7 +580,7 @@ static void mov_text_cancel_overrides_cb(void *priv, const char *style_name)
     if (!style_name || !*style_name)
         style = s->ass_dialog_style;
     else
-        style= ff_ass_style_get(s->ass_ctx, style_name);
+        style= avpriv_ass_style_get(s->ass_ctx, style_name);
 
     mov_text_ass_style_set(s, style);
 }
@@ -652,12 +652,12 @@ static int mov_text_encode_frame(AVCodecContext *avctx, unsigned char *buf,
             return AVERROR(EINVAL);
         }
 
-        dialog = ff_ass_split_dialog(s->ass_ctx, ass);
+        dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
         if (!dialog)
             return AVERROR(ENOMEM);
         mov_text_dialog(s, dialog);
-        ff_ass_split_override_codes(&mov_text_callbacks, s, dialog->text);
-        ff_ass_free_dialog(&dialog);
+        avpriv_ass_split_override_codes(&mov_text_callbacks, s, dialog->text);
+        avpriv_ass_free_dialog(&dialog);
     }
 
     if (s->buffer.len > UINT16_MAX)
diff --git a/libavcodec/mpl2dec.c b/libavcodec/mpl2dec.c
index 61e47050ec..fe806b4927 100644
--- a/libavcodec/mpl2dec.c
+++ b/libavcodec/mpl2dec.c
@@ -74,7 +74,7 @@ static int mpl2_decode_frame(AVCodecContext *avctx, void *data,
 
     av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
     if (ptr && avpkt->size > 0 && *ptr && !mpl2_event_to_ass(&buf, ptr))
-        ret = ff_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
     av_bprint_finalize(&buf, NULL);
     if (ret < 0)
         return ret;
diff --git a/libavcodec/realtextdec.c b/libavcodec/realtextdec.c
index 11b586d493..acd548b6b5 100644
--- a/libavcodec/realtextdec.c
+++ b/libavcodec/realtextdec.c
@@ -67,7 +67,7 @@ static int realtext_decode_frame(AVCodecContext *avctx,
 
     av_bprint_init(&buf, 0, 4096);
     if (ptr && avpkt->size > 0 && !rt_event_to_ass(&buf, ptr))
-        ret = ff_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
     av_bprint_finalize(&buf, NULL);
     if (ret < 0)
         return ret;
diff --git a/libavcodec/samidec.c b/libavcodec/samidec.c
index 32d07447b4..1249b629d6 100644
--- a/libavcodec/samidec.c
+++ b/libavcodec/samidec.c
@@ -144,7 +144,7 @@ static int sami_decode_frame(AVCodecContext *avctx,
         if (ret < 0)
             return ret;
         // TODO: pass escaped sami->encoded_source.str as source
-        ret = ff_ass_add_rect(sub, sami->full.str, sami->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, sami->full.str, sami->readorder++, 0, NULL, NULL);
         if (ret < 0)
             return ret;
     }
diff --git a/libavcodec/srtdec.c b/libavcodec/srtdec.c
index 4f16226b83..847352eb37 100644
--- a/libavcodec/srtdec.c
+++ b/libavcodec/srtdec.c
@@ -78,7 +78,7 @@ static int srt_decode_frame(AVCodecContext *avctx,
 
     ret = srt_to_ass(avctx, &buffer, avpkt->data, x1, y1, x2, y2);
     if (ret >= 0)
-        ret = ff_ass_add_rect(sub, buffer.str, s->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, buffer.str, s->readorder++, 0, NULL, NULL);
     av_bprint_finalize(&buffer, NULL);
     if (ret < 0)
         return ret;
diff --git a/libavcodec/srtenc.c b/libavcodec/srtenc.c
index 2e3ac55770..a7c5fccefe 100644
--- a/libavcodec/srtenc.c
+++ b/libavcodec/srtenc.c
@@ -23,8 +23,8 @@
 #include "avcodec.h"
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
-#include "ass_split.h"
-#include "ass.h"
+#include "libavutil/ass_split_internal.h"
+#include "libavutil/ass_internal.h"
 #include "internal.h"
 
 
@@ -94,7 +94,7 @@ static void srt_stack_push_pop(SRTContext *s, const char c, int close)
 
 static void srt_style_apply(SRTContext *s, const char *style)
 {
-    ASSStyle *st = ff_ass_style_get(s->ass_ctx, style);
+    ASSStyle *st = avpriv_ass_style_get(s->ass_ctx, style);
     if (st) {
         int c = st->primary_color & 0xFFFFFF;
         if (st->font_name && strcmp(st->font_name, ASS_DEFAULT_FONT) ||
@@ -135,7 +135,7 @@ static av_cold int srt_encode_init(AVCodecContext *avctx)
 {
     SRTContext *s = avctx->priv_data;
     s->avctx = avctx;
-    s->ass_ctx = ff_ass_split(avctx->subtitle_header);
+    s->ass_ctx = avpriv_ass_split(avctx->subtitle_header);
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
     return s->ass_ctx ? 0 : AVERROR_INVALIDDATA;
 }
@@ -245,14 +245,14 @@ static int encode_frame(AVCodecContext *avctx,
             return AVERROR(EINVAL);
         }
 
-        dialog = ff_ass_split_dialog(s->ass_ctx, ass);
+        dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
         if (!dialog)
             return AVERROR(ENOMEM);
         s->alignment_applied = 0;
         if (avctx->codec_id == AV_CODEC_ID_SUBRIP)
             srt_style_apply(s, dialog->style);
-        ff_ass_split_override_codes(cb, s, dialog->text);
-        ff_ass_free_dialog(&dialog);
+        avpriv_ass_split_override_codes(cb, s, dialog->text);
+        avpriv_ass_free_dialog(&dialog);
     }
 
     if (!av_bprint_is_complete(&s->buffer))
@@ -284,7 +284,7 @@ static int text_encode_frame(AVCodecContext *avctx,
 static int srt_encode_close(AVCodecContext *avctx)
 {
     SRTContext *s = avctx->priv_data;
-    ff_ass_split_free(s->ass_ctx);
+    avpriv_ass_split_free(s->ass_ctx);
     av_bprint_finalize(&s->buffer, NULL);
     return 0;
 }
diff --git a/libavcodec/subviewerdec.c b/libavcodec/subviewerdec.c
index 5c650d0cde..04e9ae7c09 100644
--- a/libavcodec/subviewerdec.c
+++ b/libavcodec/subviewerdec.c
@@ -58,7 +58,7 @@ static int subviewer_decode_frame(AVCodecContext *avctx,
 
     av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
     if (ptr && avpkt->size > 0 && !subviewer_event_to_ass(&buf, ptr))
-        ret = ff_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
     av_bprint_finalize(&buf, NULL);
     if (ret < 0)
         return ret;
diff --git a/libavcodec/textdec.c b/libavcodec/textdec.c
index 308553660a..7556deefe8 100644
--- a/libavcodec/textdec.c
+++ b/libavcodec/textdec.c
@@ -54,8 +54,8 @@ static int text_decode_frame(AVCodecContext *avctx, void *data,
 
     av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
     if (ptr && avpkt->size > 0 && *ptr) {
-        ff_ass_bprint_text_event(&buf, ptr, avpkt->size, text->linebreaks, text->keep_ass_markup);
-        ret = ff_ass_add_rect(sub, buf.str, text->readorder++, 0, NULL, NULL);
+        avpriv_ass_bprint_text_event(&buf, ptr, avpkt->size, text->linebreaks, text->keep_ass_markup);
+        ret = avpriv_ass_add_rect(sub, buf.str, text->readorder++, 0, NULL, NULL);
     }
     av_bprint_finalize(&buf, NULL);
     if (ret < 0)
diff --git a/libavcodec/ttmlenc.c b/libavcodec/ttmlenc.c
index ad2eddfdd5..083f2dd67a 100644
--- a/libavcodec/ttmlenc.c
+++ b/libavcodec/ttmlenc.c
@@ -32,8 +32,7 @@
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
 #include "libavutil/internal.h"
-#include "ass_split.h"
-#include "ass.h"
+#include "libavutil/ass_split_internal.h"
 #include "ttmlenc.h"
 
 typedef struct {
@@ -95,7 +94,7 @@ static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf,
             return AVERROR(EINVAL);
         }
 
-        dialog = ff_ass_split_dialog(s->ass_ctx, ass);
+        dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
         if (!dialog)
             return AVERROR(ENOMEM);
 
@@ -107,7 +106,7 @@ static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf,
             av_bprintf(&s->buffer, "\">");
         }
 
-        ret = ff_ass_split_override_codes(&ttml_callbacks, s, dialog->text);
+        ret = avpriv_ass_split_override_codes(&ttml_callbacks, s, dialog->text);
         if (ret < 0) {
             int log_level = (ret != AVERROR_INVALIDDATA ||
                              avctx->err_recognition & AV_EF_EXPLODE) ?
@@ -118,7 +117,7 @@ static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf,
                    av_err2str(ret));
 
             if (log_level == AV_LOG_ERROR) {
-                ff_ass_free_dialog(&dialog);
+                avpriv_ass_free_dialog(&dialog);
                 return ret;
             }
         }
@@ -126,7 +125,7 @@ static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf,
         if (dialog->style)
             av_bprintf(&s->buffer, "</span>");
 
-        ff_ass_free_dialog(&dialog);
+        avpriv_ass_free_dialog(&dialog);
     }
 
     if (!av_bprint_is_complete(&s->buffer))
@@ -148,7 +147,7 @@ static av_cold int ttml_encode_close(AVCodecContext *avctx)
 {
     TTMLContext *s = avctx->priv_data;
 
-    ff_ass_split_free(s->ass_ctx);
+    avpriv_ass_split_free(s->ass_ctx);
 
     av_bprint_finalize(&s->buffer, NULL);
 
@@ -372,7 +371,7 @@ static av_cold int ttml_encode_init(AVCodecContext *avctx)
 
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
 
-    if (!(s->ass_ctx = ff_ass_split(avctx->subtitle_header))) {
+    if (!(s->ass_ctx = avpriv_ass_split(avctx->subtitle_header))) {
         return AVERROR_INVALIDDATA;
     }
 
diff --git a/libavcodec/webvttdec.c b/libavcodec/webvttdec.c
index 0093f328fa..d304edc705 100644
--- a/libavcodec/webvttdec.c
+++ b/libavcodec/webvttdec.c
@@ -91,7 +91,7 @@ static int webvtt_decode_frame(AVCodecContext *avctx,
 
     av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
     if (ptr && avpkt->size > 0 && !webvtt_event_to_ass(&buf, ptr))
-        ret = ff_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
     av_bprint_finalize(&buf, NULL);
     if (ret < 0)
         return ret;
diff --git a/libavcodec/webvttenc.c b/libavcodec/webvttenc.c
index 89b49e42bf..761099b69a 100644
--- a/libavcodec/webvttenc.c
+++ b/libavcodec/webvttenc.c
@@ -24,8 +24,8 @@
 #include "avcodec.h"
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
-#include "ass_split.h"
-#include "ass.h"
+#include "libavutil/ass_split_internal.h"
+#include "libavutil/ass_internal.h"
 #include "internal.h"
 
 #define WEBVTT_STACK_SIZE 64
@@ -93,7 +93,7 @@ static void webvtt_stack_push_pop(WebVTTContext *s, const char c, int close)
 
 static void webvtt_style_apply(WebVTTContext *s, const char *style)
 {
-    ASSStyle *st = ff_ass_style_get(s->ass_ctx, style);
+    ASSStyle *st = avpriv_ass_style_get(s->ass_ctx, style);
     if (st) {
         if (st->bold != ASS_DEFAULT_BOLD) {
             webvtt_print(s, "<b>");
@@ -172,12 +172,12 @@ static int webvtt_encode_frame(AVCodecContext *avctx,
             return AVERROR(EINVAL);
         }
 
-        dialog = ff_ass_split_dialog(s->ass_ctx, ass);
+        dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
         if (!dialog)
             return AVERROR(ENOMEM);
         webvtt_style_apply(s, dialog->style);
-        ff_ass_split_override_codes(&webvtt_callbacks, s, dialog->text);
-        ff_ass_free_dialog(&dialog);
+        avpriv_ass_split_override_codes(&webvtt_callbacks, s, dialog->text);
+        avpriv_ass_free_dialog(&dialog);
     }
 
     if (!av_bprint_is_complete(&s->buffer))
@@ -197,7 +197,7 @@ static int webvtt_encode_frame(AVCodecContext *avctx,
 static int webvtt_encode_close(AVCodecContext *avctx)
 {
     WebVTTContext *s = avctx->priv_data;
-    ff_ass_split_free(s->ass_ctx);
+    avpriv_ass_split_free(s->ass_ctx);
     av_bprint_finalize(&s->buffer, NULL);
     return 0;
 }
@@ -206,7 +206,7 @@ static av_cold int webvtt_encode_init(AVCodecContext *avctx)
 {
     WebVTTContext *s = avctx->priv_data;
     s->avctx = avctx;
-    s->ass_ctx = ff_ass_split(avctx->subtitle_header);
+    s->ass_ctx = avpriv_ass_split(avctx->subtitle_header);
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
     return s->ass_ctx ? 0 : AVERROR_INVALIDDATA;
 }
diff --git a/libavutil/Makefile b/libavutil/Makefile
index 8bc0a14942..7d4c4793b1 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -101,6 +101,8 @@ BUILT_HEADERS = avconfig.h                                              \
 OBJS = adler32.o                                                        \
        aes.o                                                            \
        aes_ctr.o                                                        \
+       ass.o                                                            \
+       ass_split.o                                                      \
        audio_fifo.o                                                     \
        avstring.o                                                       \
        avsscanf.o                                                       \
diff --git a/libavcodec/ass.c b/libavutil/ass.c
similarity index 65%
rename from libavcodec/ass.c
rename to libavutil/ass.c
index 725e4d42ba..9eeaa38ba9 100644
--- a/libavcodec/ass.c
+++ b/libavutil/ass.c
@@ -19,21 +19,22 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-#include "avcodec.h"
-#include "ass.h"
+#include "ass_internal.h"
+
+#include "subfmt.h"
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
 #include "libavutil/common.h"
 
-int ff_ass_subtitle_header_full(AVCodecContext *avctx,
-                                int play_res_x, int play_res_y,
-                                const char *font, int font_size,
-                                int primary_color, int secondary_color,
-                                int outline_color, int back_color,
-                                int bold, int italic, int underline,
-                                int border_style, int alignment)
+char* avpriv_ass_get_subtitle_header_full(int play_res_x, int play_res_y,
+                                    const char *font, int font_size,
+                                    int primary_color, int secondary_color,
+                                    int outline_color, int back_color,
+                                    int bold, int italic, int underline,
+                                    int border_style, int alignment,
+                                    int print_av_version)
 {
-    avctx->subtitle_header = av_asprintf(
+    char* header = av_asprintf(
              "[Script Info]\r\n"
              "; Script generated by FFmpeg/Lavc%s\r\n"
              "ScriptType: v4.00+\r\n"
@@ -68,34 +69,31 @@ int ff_ass_subtitle_header_full(AVCodecContext *avctx,
              "\r\n"
              "[Events]\r\n"
              "Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\r\n",
-             !(avctx->flags & AV_CODEC_FLAG_BITEXACT) ? AV_STRINGIFY(LIBAVCODEC_VERSION) : "",
+             print_av_version ? AV_STRINGIFY(LIBAVCODEC_VERSION) : "",
              play_res_x, play_res_y, font, font_size,
              primary_color, secondary_color, outline_color, back_color,
              -bold, -italic, -underline, border_style, alignment);
 
-    if (!avctx->subtitle_header)
-        return AVERROR(ENOMEM);
-    avctx->subtitle_header_size = strlen(avctx->subtitle_header);
-    return 0;
+    return header;
 }
 
-int ff_ass_subtitle_header(AVCodecContext *avctx,
-                           const char *font, int font_size,
+char* avpriv_ass_get_subtitle_header(const char *font, int font_size,
                            int color, int back_color,
                            int bold, int italic, int underline,
-                           int border_style, int alignment)
+                           int border_style, int alignment,
+                           int print_av_version)
 {
-    return ff_ass_subtitle_header_full(avctx,
-                               ASS_DEFAULT_PLAYRESX, ASS_DEFAULT_PLAYRESY,
-                               font, font_size, color, color,
-                               back_color, back_color,
-                               bold, italic, underline,
-                               border_style, alignment);
+    return avpriv_ass_get_subtitle_header_full(ASS_DEFAULT_PLAYRESX, ASS_DEFAULT_PLAYRESY,
+                                       font, font_size, color, color,
+                                       back_color, back_color,
+                                       bold, italic, underline,
+                                       border_style, alignment,
+                                       print_av_version);
 }
 
-int ff_ass_subtitle_header_default(AVCodecContext *avctx)
+char* avpriv_ass_get_subtitle_header_default(int print_av_version)
 {
-    return ff_ass_subtitle_header(avctx, ASS_DEFAULT_FONT,
+    return avpriv_ass_get_subtitle_header(ASS_DEFAULT_FONT,
                                ASS_DEFAULT_FONT_SIZE,
                                ASS_DEFAULT_COLOR,
                                ASS_DEFAULT_BACK_COLOR,
@@ -103,10 +101,11 @@ int ff_ass_subtitle_header_default(AVCodecContext *avctx)
                                ASS_DEFAULT_ITALIC,
                                ASS_DEFAULT_UNDERLINE,
                                ASS_DEFAULT_BORDERSTYLE,
-                               ASS_DEFAULT_ALIGNMENT);
+                               ASS_DEFAULT_ALIGNMENT,
+                               print_av_version);
 }
 
-char *ff_ass_get_dialog(int readorder, int layer, const char *style,
+char *avpriv_ass_get_dialog(int readorder, int layer, const char *style,
                         const char *speaker, const char *text)
 {
     return av_asprintf("%d,%d,%s,%s,0,0,0,,%s",
@@ -114,37 +113,17 @@ char *ff_ass_get_dialog(int readorder, int layer, const char *style,
                        speaker ? speaker : "", text);
 }
 
-int ff_ass_add_rect(AVSubtitle *sub, const char *dialog,
-                    int readorder, int layer, const char *style,
-                    const char *speaker)
+char *avpriv_ass_get_dialog_ex(int readorder, int layer, const char *style,
+                        const char *speaker, int margin_l, int margin_r,
+                        int margin_v, const char *text)
 {
-    AVSubtitleRect **rects, *rect;
-    char *ass_str;
-
-    rects = av_realloc_array(sub->rects, sub->num_rects+1, sizeof(*sub->rects));
-    if (!rects)
-        return AVERROR(ENOMEM);
-    sub->rects = rects;
-    rect       = av_mallocz(sizeof(*rect));
-    if (!rect)
-        return AVERROR(ENOMEM);
-    rects[sub->num_rects++] = rect;
-    rect->type = SUBTITLE_ASS;
-    ass_str = ff_ass_get_dialog(readorder, layer, style, speaker, dialog);
-    if (!ass_str)
-        return AVERROR(ENOMEM);
-    rect->ass = ass_str;
-    return 0;
-}
-
-void ff_ass_decoder_flush(AVCodecContext *avctx)
-{
-    FFASSDecoderContext *s = avctx->priv_data;
-    if (!(avctx->flags2 & AV_CODEC_FLAG2_RO_FLUSH_NOOP))
-        s->readorder = 0;
+    return av_asprintf("%d,%d,%s,%s,%d,%d,%d,,%s",
+                       readorder, layer, style ? style : "Default",
+                       speaker ? speaker : "", margin_l, margin_r,
+                       margin_v, text);
 }
 
-void ff_ass_bprint_text_event(AVBPrint *buf, const char *p, int size,
+void avpriv_ass_bprint_text_event(AVBPrint *buf, const char *p, int size,
                              const char *linebreaks, int keep_ass_markup)
 {
     const char *p_end = p + size;
diff --git a/libavutil/ass_internal.h b/libavutil/ass_internal.h
new file mode 100644
index 0000000000..cde5561cd3
--- /dev/null
+++ b/libavutil/ass_internal.h
@@ -0,0 +1,135 @@
+/*
+ * SSA/ASS common functions
+ * Copyright (c) 2010  Aurelien Jacobs <aurel@gnuage.org>
+ *
+ * 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
+ */
+
+#ifndef AVUTIL_ASS_INTERNAL_H
+#define AVUTIL_ASS_INTERNAL_H
+
+#include "subfmt.h"
+#include "libavutil/bprint.h"
+
+#define ASS_DEFAULT_PLAYRESX 384
+#define ASS_DEFAULT_PLAYRESY 288
+
+/**
+ * @name Default values for ASS style
+ * @{
+ */
+#define ASS_DEFAULT_FONT        "Arial"
+#define ASS_DEFAULT_FONT_SIZE   16
+#define ASS_DEFAULT_COLOR       0xffffff
+#define ASS_DEFAULT_BACK_COLOR  0
+#define ASS_DEFAULT_BOLD        0
+#define ASS_DEFAULT_ITALIC      0
+#define ASS_DEFAULT_UNDERLINE   0
+#define ASS_DEFAULT_ALIGNMENT   2
+#define ASS_DEFAULT_BORDERSTYLE 1
+/** @} */
+
+/**
+ * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS.
+ * Can specify all fields explicitly
+ *
+ * @param play_res_x subtitle frame width
+ * @param play_res_y subtitle frame height
+ * @param font name of the default font face to use
+ * @param font_size default font size to use
+ * @param primary_color default text color to use (ABGR)
+ * @param secondary_color default secondary text color to use (ABGR)
+ * @param outline_color default outline color to use (ABGR)
+ * @param back_color default background color to use (ABGR)
+ * @param bold 1 for bold text, 0 for normal text
+ * @param italic 1 for italic text, 0 for normal text
+ * @param underline 1 for underline text, 0 for normal text
+ * @param border_style 1 for outline, 3 for opaque box
+ * @param alignment position of the text (left, center, top...), defined after
+ *                  the layout of the numpad (1-3 sub, 4-6 mid, 7-9 top)
+ * @param print_av_version include library version in header
+ * @return a string containing the subtitle header that needs
+ *         to be released via av_free()
+ */
+char* avpriv_ass_get_subtitle_header_full(int play_res_x, int play_res_y,
+                                  const char *font, int font_size,
+                                  int primary_color, int secondary_color,
+                                  int outline_color, int back_color,
+                                  int bold, int italic, int underline,
+                                  int border_style, int alignment,
+                                  int print_av_version);
+
+/**
+ * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS.
+ *
+ * @param font name of the default font face to use
+ * @param font_size default font size to use
+ * @param color default text color to use (ABGR)
+ * @param back_color default background color to use (ABGR)
+ * @param bold 1 for bold text, 0 for normal text
+ * @param italic 1 for italic text, 0 for normal text
+ * @param underline 1 for underline text, 0 for normal text
+ * @param border_style 1 for outline, 3 for opaque box
+ * @param alignment position of the text (left, center, top...), defined after
+ *                  the layout of the numpad (1-3 sub, 4-6 mid, 7-9 top)
+ * @param print_av_version include library version in header
+ * @return a string containing the subtitle header that needs
+ *         to be released via av_free()
+ */
+char* avpriv_ass_get_subtitle_header(const char *font, int font_size,
+                                int color, int back_color,
+                                int bold, int italic, int underline,
+                                int border_style, int alignment,
+                                int print_av_version);
+
+/**
+ * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS
+ * with default style.
+ *
+ * @param print_av_version include library version in header
+ * @return a string containing the subtitle header that needs
+ *         to be released via av_free()
+ */
+char* avpriv_ass_get_subtitle_header_default(int print_av_version);
+
+/**
+ * Craft an ASS dialog string.
+ */
+char *avpriv_ass_get_dialog(int readorder, int layer, const char *style,
+                        const char *speaker, const char *text);
+
+/**
+ * Craft an ASS dialog string.
+ */
+char *avpriv_ass_get_dialog_ex(int readorder, int layer, const char *style,
+                        const char *speaker, int margin_l, int margin_r,
+                        int margin_v, const char *text);
+
+/**
+ * Escape a text subtitle using ASS syntax into an AVBPrint buffer.
+ * Newline characters will be escaped to \N.
+ *
+ * @param buf pointer to an initialized AVBPrint buffer
+ * @param p source text
+ * @param size size of the source text
+ * @param linebreaks additional newline chars, which will be escaped to \N
+ * @param keep_ass_markup braces and backslash will not be escaped if set
+ */
+void avpriv_ass_bprint_text_event(AVBPrint *buf, const char *p, int size,
+                             const char *linebreaks, int keep_ass_markup);
+
+#endif /* AVUTIL_ASS_INTERNAL_H */
diff --git a/libavcodec/ass_split.c b/libavutil/ass_split.c
similarity index 94%
rename from libavcodec/ass_split.c
rename to libavutil/ass_split.c
index 05c5453e53..c5963351fc 100644
--- a/libavcodec/ass_split.c
+++ b/libavutil/ass_split.c
@@ -22,7 +22,7 @@
 #include "libavutil/common.h"
 #include "libavutil/error.h"
 #include "libavutil/mem.h"
-#include "ass_split.h"
+#include "ass_split_internal.h"
 
 typedef enum {
     ASS_STR,
@@ -373,7 +373,7 @@ static int ass_split(ASSSplitContext *ctx, const char *buf)
     return buf ? 0 : AVERROR_INVALIDDATA;
 }
 
-ASSSplitContext *ff_ass_split(const char *buf)
+ASSSplitContext *avpriv_ass_split(const char *buf)
 {
     ASSSplitContext *ctx = av_mallocz(sizeof(*ctx));
     if (!ctx)
@@ -382,7 +382,7 @@ ASSSplitContext *ff_ass_split(const char *buf)
         buf += 3;
     ctx->current_section = -1;
     if (ass_split(ctx, buf) < 0) {
-        ff_ass_split_free(ctx);
+        avpriv_ass_split_free(ctx);
         return NULL;
     }
     return ctx;
@@ -412,7 +412,7 @@ static void free_section(ASSSplitContext *ctx, const ASSSection *section)
         av_freep((uint8_t *)&ctx->ass + section->offset);
 }
 
-void ff_ass_free_dialog(ASSDialog **dialogp)
+void avpriv_ass_free_dialog(ASSDialog **dialogp)
 {
     ASSDialog *dialog = *dialogp;
     if (!dialog)
@@ -424,7 +424,7 @@ void ff_ass_free_dialog(ASSDialog **dialogp)
     av_freep(dialogp);
 }
 
-ASSDialog *ff_ass_split_dialog(ASSSplitContext *ctx, const char *buf)
+ASSDialog *avpriv_ass_split_dialog(ASSSplitContext *ctx, const char *buf)
 {
     int i;
     static const ASSFields fields[] = {
@@ -451,7 +451,7 @@ ASSDialog *ff_ass_split_dialog(ASSSplitContext *ctx, const char *buf)
         buf = skip_space(buf);
         len = last ? strlen(buf) : strcspn(buf, ",");
         if (len >= INT_MAX) {
-            ff_ass_free_dialog(&dialog);
+            avpriv_ass_free_dialog(&dialog);
             return NULL;
         }
         convert_func[type](ptr, buf, len);
@@ -461,7 +461,7 @@ ASSDialog *ff_ass_split_dialog(ASSSplitContext *ctx, const char *buf)
     return dialog;
 }
 
-void ff_ass_split_free(ASSSplitContext *ctx)
+void avpriv_ass_split_free(ASSSplitContext *ctx)
 {
     if (ctx) {
         int i;
@@ -474,7 +474,7 @@ void ff_ass_split_free(ASSSplitContext *ctx)
 }
 
 
-int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
+int avpriv_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
                                 const char *buf)
 {
     const char *text = NULL;
@@ -497,8 +497,8 @@ int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
             while (*buf == '\\') {
                 char style[2], c[2], sep[2], c_num[2] = "0", tmp[128] = {0};
                 unsigned int color = 0xFFFFFFFF;
-                int len, size = -1, an = -1, alpha = -1;
-                int x1, y1, x2, y2, t1 = -1, t2 = -1;
+                int len, size = -1, an = -1, alpha = -1, scale = 0;
+                int x1, y1, x2, y2, t1 = -1, t2 = -1, accel = 1;
                 if (sscanf(buf, "\\%1[bisu]%1[01\\}]%n", style, c, &len) > 1) {
                     int close = c[0] == '0' ? 1 : c[0] == '1' ? 0 : -1;
                     len += close != -1;
@@ -546,6 +546,14 @@ int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
                 } else if (sscanf(buf, "\\org(%d,%d)%1[\\}]%n", &x1, &y1, sep, &len) > 2) {
                     if (callbacks->origin)
                         callbacks->origin(priv, x1, y1);
+                } else if (sscanf(buf, "\\t(%d,%d,%1[\\}]%n", &t1, &t2, sep, &len) > 2 ||
+                           sscanf(buf, "\\t(%d,%d,%d,%1[\\}]%n", &t1, &t2, &accel, sep, &len) > 3) {
+                    if (callbacks->animate)
+                        callbacks->animate(priv, t1, t2, accel, tmp);
+                } else if (sscanf(buf, "\\p%1[\\}]%n", sep, &len) > 0 ||
+                           sscanf(buf, "\\p%u%1[\\}]%n", &scale, sep, &len) > 1) {
+                    if (callbacks->drawing_mode)
+                        callbacks->drawing_mode(priv, scale);
                 } else {
                     len = strcspn(buf+1, "\\}") + 2;  /* skip unknown code */
                 }
@@ -569,7 +577,7 @@ int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
     return 0;
 }
 
-ASSStyle *ff_ass_style_get(ASSSplitContext *ctx, const char *style)
+ASSStyle *avpriv_ass_style_get(ASSSplitContext *ctx, const char *style)
 {
     ASS *ass = &ctx->ass;
     int i;
diff --git a/libavcodec/ass_split.h b/libavutil/ass_split_internal.h
similarity index 86%
rename from libavcodec/ass_split.h
rename to libavutil/ass_split_internal.h
index a45fb9b8a1..eee49ef0f5 100644
--- a/libavcodec/ass_split.h
+++ b/libavutil/ass_split_internal.h
@@ -19,8 +19,8 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-#ifndef AVCODEC_ASS_SPLIT_H
-#define AVCODEC_ASS_SPLIT_H
+#ifndef AVUTIL_ASS_SPLIT_INTERNAL_H
+#define AVUTIL_ASS_SPLIT_INTERNAL_H
 
 /**
  * fields extracted from the [Script Info] section
@@ -81,7 +81,7 @@ typedef struct {
     char *effect;
     char *text;     /**< actual text which will be displayed as a subtitle,
                          can include style override control codes (see
-                         ff_ass_split_override_codes()) */
+                         avpriv_ass_split_override_codes()) */
 } ASSDialog;
 
 /**
@@ -107,12 +107,12 @@ typedef struct ASSSplitContext ASSSplitContext;
  * @param buf String containing the ASS formatted data.
  * @return Newly allocated struct containing split data.
  */
-ASSSplitContext *ff_ass_split(const char *buf);
+ASSSplitContext *avpriv_ass_split(const char *buf);
 
 /**
- * Free a dialogue obtained from ff_ass_split_dialog().
+ * Free a dialogue obtained from avpriv_ass_split_dialog().
  */
-void ff_ass_free_dialog(ASSDialog **dialogp);
+void avpriv_ass_free_dialog(ASSDialog **dialogp);
 
 /**
  * Split one ASS Dialogue line from a string buffer.
@@ -121,14 +121,14 @@ void ff_ass_free_dialog(ASSDialog **dialogp);
  * @param buf String containing the ASS "Dialogue" line.
  * @return Pointer to the split ASSDialog. Must be freed with ff_ass_free_dialog()
  */
-ASSDialog *ff_ass_split_dialog(ASSSplitContext *ctx, const char *buf);
+ASSDialog *avpriv_ass_split_dialog(ASSSplitContext *ctx, const char *buf);
 
 /**
  * Free all the memory allocated for an ASSSplitContext.
  *
  * @param ctx Context previously initialized by ff_ass_split().
  */
-void ff_ass_split_free(ASSSplitContext *ctx);
+void avpriv_ass_split_free(ASSSplitContext *ctx);
 
 
 /**
@@ -141,6 +141,7 @@ typedef struct {
      * @{
      */
     void (*text)(void *priv, const char *text, int len);
+    void (*hard_space)(void *priv);
     void (*new_line)(void *priv, int forced);
     void (*style)(void *priv, char style, int close);
     void (*color)(void *priv, unsigned int /* color */, unsigned int color_id);
@@ -156,7 +157,16 @@ typedef struct {
      * @{
      */
     void (*move)(void *priv, int x1, int y1, int x2, int y2, int t1, int t2);
+    void (*animate)(void *priv, int t1, int t2, int accel, char *style);
     void (*origin)(void *priv, int x, int y);
+    void (*drawing_mode)(void *priv, int scale);
+    /** @} */
+
+    /**
+     * @defgroup ass_ext    ASS extensible parsing callback
+     * @{
+     */
+    void (*ext)(void *priv, int ext_id, const char *text, int p1, int p2);
     /** @} */
 
     /**
@@ -176,7 +186,7 @@ typedef struct {
  * @param buf The ASS "Dialogue" Text field to split.
  * @return >= 0 on success otherwise an error code <0
  */
-int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
+int avpriv_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
                                 const char *buf);
 
 /**
@@ -186,6 +196,6 @@ int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
  * @param style name of the style to search for.
  * @return the ASSStyle corresponding to style, or NULL if style can't be found
  */
-ASSStyle *ff_ass_style_get(ASSSplitContext *ctx, const char *style);
+ASSStyle *avpriv_ass_style_get(ASSSplitContext *ctx, const char *style);
 
-#endif /* AVCODEC_ASS_SPLIT_H */
+#endif /* AVUTIL_ASS_SPLIT_INTERNAL_H */
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v3 06/26] avcodec/subtitles: Replace deprecated enum values
  2022-01-20  3:25   ` [FFmpeg-devel] [PATCH v3 00/26] Subtitle Filtering 2022 ffmpegagent
                       ` (4 preceding siblings ...)
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 05/26] avcodec, avutil: Move ass helper functions to avutil as avpriv_ and extend ass dialog parsing ffmpegagent
@ 2022-01-20  3:25     ` ffmpegagent
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 07/26] fftools/play, probe: Adjust for subtitle changes ffmpegagent
                       ` (20 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  3:25 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/ass.h       | 2 +-
 libavcodec/assdec.c    | 2 +-
 libavcodec/dvbsubdec.c | 2 +-
 libavcodec/dvdsubdec.c | 2 +-
 libavcodec/dvdsubenc.c | 2 +-
 libavcodec/pgssubdec.c | 2 +-
 libavcodec/xsubdec.c   | 2 +-
 7 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/libavcodec/ass.h b/libavcodec/ass.h
index 8bc13d7ab8..43c5ad651a 100644
--- a/libavcodec/ass.h
+++ b/libavcodec/ass.h
@@ -83,7 +83,7 @@ static inline int avpriv_ass_add_rect(AVSubtitle *sub, const char *dialog,
     rects[sub->num_rects]       = av_mallocz(sizeof(*rects[0]));
     if (!rects[sub->num_rects])
         return AVERROR(ENOMEM);
-    rects[sub->num_rects]->type = SUBTITLE_ASS;
+    rects[sub->num_rects]->type = AV_SUBTITLE_FMT_ASS;
     ass_str = avpriv_ass_get_dialog(readorder, layer, style, speaker, dialog);
     if (!ass_str)
         return AVERROR(ENOMEM);
diff --git a/libavcodec/assdec.c b/libavcodec/assdec.c
index 7802a44e71..fd321e7004 100644
--- a/libavcodec/assdec.c
+++ b/libavcodec/assdec.c
@@ -54,7 +54,7 @@ static int ass_decode_frame(AVCodecContext *avctx, void *data, int *got_sub_ptr,
     if (!sub->rects[0])
         return AVERROR(ENOMEM);
     sub->num_rects = 1;
-    sub->rects[0]->type = SUBTITLE_ASS;
+    sub->rects[0]->type = AV_SUBTITLE_FMT_ASS;
     sub->rects[0]->ass  = av_strdup(avpkt->data);
     if (!sub->rects[0]->ass)
         return AVERROR(ENOMEM);
diff --git a/libavcodec/dvbsubdec.c b/libavcodec/dvbsubdec.c
index bc741a1de6..0d64c6e71c 100644
--- a/libavcodec/dvbsubdec.c
+++ b/libavcodec/dvbsubdec.c
@@ -795,7 +795,7 @@ static int save_subtitle_set(AVCodecContext *avctx, AVSubtitle *sub, int *got_ou
             rect->w = region->width;
             rect->h = region->height;
             rect->nb_colors = (1 << region->depth);
-            rect->type      = SUBTITLE_BITMAP;
+            rect->type      = AV_SUBTITLE_FMT_BITMAP;
             rect->linesize[0] = region->width;
 
             clut = get_clut(ctx, region->clut);
diff --git a/libavcodec/dvdsubdec.c b/libavcodec/dvdsubdec.c
index 52259f0730..b39b3d1838 100644
--- a/libavcodec/dvdsubdec.c
+++ b/libavcodec/dvdsubdec.c
@@ -406,7 +406,7 @@ static int decode_dvd_subtitles(DVDSubContext *ctx, AVSubtitle *sub_header,
                 sub_header->rects[0]->y = y1;
                 sub_header->rects[0]->w = w;
                 sub_header->rects[0]->h = h;
-                sub_header->rects[0]->type = SUBTITLE_BITMAP;
+                sub_header->rects[0]->type = AV_SUBTITLE_FMT_BITMAP;
                 sub_header->rects[0]->linesize[0] = w;
                 sub_header->rects[0]->flags = is_menu ? AV_SUBTITLE_FLAG_FORCED : 0;
             }
diff --git a/libavcodec/dvdsubenc.c b/libavcodec/dvdsubenc.c
index ff4fbed39d..943a7466d9 100644
--- a/libavcodec/dvdsubenc.c
+++ b/libavcodec/dvdsubenc.c
@@ -268,7 +268,7 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
     if (rects == 0 || !h->rects)
         return AVERROR(EINVAL);
     for (i = 0; i < rects; i++)
-        if (h->rects[i]->type != SUBTITLE_BITMAP) {
+        if (h->rects[i]->type != AV_SUBTITLE_FMT_BITMAP) {
             av_log(avctx, AV_LOG_ERROR, "Bitmap subtitle required\n");
             return AVERROR(EINVAL);
         }
diff --git a/libavcodec/pgssubdec.c b/libavcodec/pgssubdec.c
index bdd20c914b..22b6616f9b 100644
--- a/libavcodec/pgssubdec.c
+++ b/libavcodec/pgssubdec.c
@@ -535,7 +535,7 @@ static int display_end_segment(AVCodecContext *avctx, void *data,
         if (!rect)
             return AVERROR(ENOMEM);
         sub->rects[sub->num_rects++] = rect;
-        rect->type = SUBTITLE_BITMAP;
+        rect->type = AV_SUBTITLE_FMT_BITMAP;
 
         /* Process bitmap */
         object = find_object(ctx->presentation.objects[i].id, &ctx->objects);
diff --git a/libavcodec/xsubdec.c b/libavcodec/xsubdec.c
index 85cd7d1c20..a4be18a1d8 100644
--- a/libavcodec/xsubdec.c
+++ b/libavcodec/xsubdec.c
@@ -107,7 +107,7 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_sub_ptr,
     sub->num_rects = 1;
     rect->x = x; rect->y = y;
     rect->w = w; rect->h = h;
-    rect->type = SUBTITLE_BITMAP;
+    rect->type = AV_SUBTITLE_FMT_BITMAP;
     rect->linesize[0] = w;
     rect->data[0] = av_malloc(w * h);
     rect->nb_colors = 4;
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v3 07/26] fftools/play, probe: Adjust for subtitle changes
  2022-01-20  3:25   ` [FFmpeg-devel] [PATCH v3 00/26] Subtitle Filtering 2022 ffmpegagent
                       ` (5 preceding siblings ...)
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 06/26] avcodec/subtitles: Replace deprecated enum values ffmpegagent
@ 2022-01-20  3:25     ` ffmpegagent
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 08/26] avfilter/subtitles: Add subtitles.c for subtitle frame allocation ffmpegagent
                       ` (19 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  3:25 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 fftools/ffplay.c  | 102 +++++++++++++++++++++-------------------------
 fftools/ffprobe.c |  47 +++++++++++++--------
 2 files changed, 77 insertions(+), 72 deletions(-)

diff --git a/fftools/ffplay.c b/fftools/ffplay.c
index e7b20be76b..94286eb678 100644
--- a/fftools/ffplay.c
+++ b/fftools/ffplay.c
@@ -152,7 +152,6 @@ typedef struct Clock {
 /* Common struct for handling all types of decoded data and allocated render buffers. */
 typedef struct Frame {
     AVFrame *frame;
-    AVSubtitle sub;
     int serial;
     double pts;           /* presentation timestamp for the frame */
     double duration;      /* estimated duration of the frame */
@@ -586,7 +585,7 @@ static int decoder_init(Decoder *d, AVCodecContext *avctx, PacketQueue *queue, S
     return 0;
 }
 
-static int decoder_decode_frame(Decoder *d, AVFrame *frame, AVSubtitle *sub) {
+static int decoder_decode_frame(Decoder *d, AVFrame *frame) {
     int ret = AVERROR(EAGAIN);
 
     for (;;) {
@@ -620,6 +619,9 @@ static int decoder_decode_frame(Decoder *d, AVFrame *frame, AVSubtitle *sub) {
                             }
                         }
                         break;
+                    case AVMEDIA_TYPE_SUBTITLE:
+                        ret = avcodec_receive_frame(d->avctx, frame);
+                        break;
                 }
                 if (ret == AVERROR_EOF) {
                     d->finished = d->pkt_serial;
@@ -652,25 +654,11 @@ static int decoder_decode_frame(Decoder *d, AVFrame *frame, AVSubtitle *sub) {
             av_packet_unref(d->pkt);
         } while (1);
 
-        if (d->avctx->codec_type == AVMEDIA_TYPE_SUBTITLE) {
-            int got_frame = 0;
-            ret = avcodec_decode_subtitle2(d->avctx, sub, &got_frame, d->pkt);
-            if (ret < 0) {
-                ret = AVERROR(EAGAIN);
-            } else {
-                if (got_frame && !d->pkt->data) {
-                    d->packet_pending = 1;
-                }
-                ret = got_frame ? 0 : (d->pkt->data ? AVERROR(EAGAIN) : AVERROR_EOF);
-            }
-            av_packet_unref(d->pkt);
+        if (avcodec_send_packet(d->avctx, d->pkt) == AVERROR(EAGAIN)) {
+            av_log(d->avctx, AV_LOG_ERROR, "Receive_frame and send_packet both returned EAGAIN, which is an API violation.\n");
+            d->packet_pending = 1;
         } else {
-            if (avcodec_send_packet(d->avctx, d->pkt) == AVERROR(EAGAIN)) {
-                av_log(d->avctx, AV_LOG_ERROR, "Receive_frame and send_packet both returned EAGAIN, which is an API violation.\n");
-                d->packet_pending = 1;
-            } else {
-                av_packet_unref(d->pkt);
-            }
+            av_packet_unref(d->pkt);
         }
     }
 }
@@ -683,7 +671,6 @@ static void decoder_destroy(Decoder *d) {
 static void frame_queue_unref_item(Frame *vp)
 {
     av_frame_unref(vp->frame);
-    avsubtitle_free(&vp->sub);
 }
 
 static int frame_queue_init(FrameQueue *f, PacketQueue *pktq, int max_size, int keep_last)
@@ -981,7 +968,7 @@ static void video_image_display(VideoState *is)
         if (frame_queue_nb_remaining(&is->subpq) > 0) {
             sp = frame_queue_peek(&is->subpq);
 
-            if (vp->pts >= sp->pts + ((float) sp->sub.start_display_time / 1000)) {
+            if (vp->pts >= sp->pts) {
                 if (!sp->uploaded) {
                     uint8_t* pixels[4];
                     int pitch[4];
@@ -993,25 +980,27 @@ static void video_image_display(VideoState *is)
                     if (realloc_texture(&is->sub_texture, SDL_PIXELFORMAT_ARGB8888, sp->width, sp->height, SDL_BLENDMODE_BLEND, 1) < 0)
                         return;
 
-                    for (i = 0; i < sp->sub.num_rects; i++) {
-                        AVSubtitleRect *sub_rect = sp->sub.rects[i];
+                    for (i = 0; i < sp->frame->num_subtitle_areas; i++) {
+                        AVSubtitleArea *area = sp->frame->subtitle_areas[i];
+                        SDL_Rect sdl_rect = { .x = area->x, .y = area->y, .w = area->w, .h = area->h };
 
-                        sub_rect->x = av_clip(sub_rect->x, 0, sp->width );
-                        sub_rect->y = av_clip(sub_rect->y, 0, sp->height);
-                        sub_rect->w = av_clip(sub_rect->w, 0, sp->width  - sub_rect->x);
-                        sub_rect->h = av_clip(sub_rect->h, 0, sp->height - sub_rect->y);
+                        area->x = av_clip(area->x, 0, sp->width );
+                        area->y = av_clip(area->y, 0, sp->height);
+                        area->w = av_clip(area->w, 0, sp->width  - area->x);
+                        area->h = av_clip(area->h, 0, sp->height - area->y);
 
                         is->sub_convert_ctx = sws_getCachedContext(is->sub_convert_ctx,
-                            sub_rect->w, sub_rect->h, AV_PIX_FMT_PAL8,
-                            sub_rect->w, sub_rect->h, AV_PIX_FMT_BGRA,
+                            area->w, area->h, AV_PIX_FMT_PAL8,
+                            area->w, area->h, AV_PIX_FMT_BGRA,
                             0, NULL, NULL, NULL);
                         if (!is->sub_convert_ctx) {
                             av_log(NULL, AV_LOG_FATAL, "Cannot initialize the conversion context\n");
                             return;
                         }
-                        if (!SDL_LockTexture(is->sub_texture, (SDL_Rect *)sub_rect, (void **)pixels, pitch)) {
-                            sws_scale(is->sub_convert_ctx, (const uint8_t * const *)sub_rect->data, sub_rect->linesize,
-                                      0, sub_rect->h, pixels, pitch);
+                        if (!SDL_LockTexture(is->sub_texture, &sdl_rect, (void **)pixels, pitch)) {
+                            const uint8_t* data[2] = { area->buf[0]->data, (uint8_t *)&area->pal };
+                            sws_scale(is->sub_convert_ctx, data, area->linesize,
+                                      0, area->h, pixels, pitch);
                             SDL_UnlockTexture(is->sub_texture);
                         }
                     }
@@ -1038,16 +1027,18 @@ static void video_image_display(VideoState *is)
 #if USE_ONEPASS_SUBTITLE_RENDER
         SDL_RenderCopy(renderer, is->sub_texture, NULL, &rect);
 #else
-        int i;
+        unsigned i;
         double xratio = (double)rect.w / (double)sp->width;
         double yratio = (double)rect.h / (double)sp->height;
-        for (i = 0; i < sp->sub.num_rects; i++) {
-            SDL_Rect *sub_rect = (SDL_Rect*)sp->sub.rects[i];
-            SDL_Rect target = {.x = rect.x + sub_rect->x * xratio,
-                               .y = rect.y + sub_rect->y * yratio,
-                               .w = sub_rect->w * xratio,
-                               .h = sub_rect->h * yratio};
-            SDL_RenderCopy(renderer, is->sub_texture, sub_rect, &target);
+        for (i = 0; i < sp->frame->num_subtitle_areas; i++) {
+            AVSubtitleArea *area = sp->frame->subtitle_areas[i];
+            SDL_Rect sub_rect = { .x = area->x, .y = area->y,
+                                  .w = area->w, .h = area->h};
+            SDL_Rect target = {.x = rect.x + sub_rect.x * xratio,
+                               .y = rect.y + sub_rect.y * yratio,
+                               .w = sub_rect.w * xratio,
+                               .h = sub_rect.h * yratio};
+            SDL_RenderCopy(renderer, is->sub_texture, &sub_rect, &target);
         }
 #endif
     }
@@ -1651,19 +1642,20 @@ retry:
                         sp2 = NULL;
 
                     if (sp->serial != is->subtitleq.serial
-                            || (is->vidclk.pts > (sp->pts + ((float) sp->sub.end_display_time / 1000)))
-                            || (sp2 && is->vidclk.pts > (sp2->pts + ((float) sp2->sub.start_display_time / 1000))))
+                            || (is->vidclk.pts > (sp->pts + ((float) sp->frame->subtitle_timing.duration / AV_TIME_BASE)))
+                            || (sp2 && is->vidclk.pts > (sp2->pts)))
                     {
                         if (sp->uploaded) {
                             int i;
-                            for (i = 0; i < sp->sub.num_rects; i++) {
-                                AVSubtitleRect *sub_rect = sp->sub.rects[i];
+                            for (i = 0; i < sp->frame->num_subtitle_areas; i++) {
+                                AVSubtitleArea *area = sp->frame->subtitle_areas[i];
+                                SDL_Rect sdl_rect = { .x = area->x, .y = area->y, .w = area->w, .h = area->h };
                                 uint8_t *pixels;
                                 int pitch, j;
 
-                                if (!SDL_LockTexture(is->sub_texture, (SDL_Rect *)sub_rect, (void **)&pixels, &pitch)) {
-                                    for (j = 0; j < sub_rect->h; j++, pixels += pitch)
-                                        memset(pixels, 0, sub_rect->w << 2);
+                                if (!SDL_LockTexture(is->sub_texture, &sdl_rect, (void **)&pixels, &pitch)) {
+                                    for (j = 0; j < area->h; j++, pixels += pitch)
+                                        memset(pixels, 0, area->w << 2);
                                     SDL_UnlockTexture(is->sub_texture);
                                 }
                             }
@@ -1774,7 +1766,7 @@ static int get_video_frame(VideoState *is, AVFrame *frame)
 {
     int got_picture;
 
-    if ((got_picture = decoder_decode_frame(&is->viddec, frame, NULL)) < 0)
+    if ((got_picture = decoder_decode_frame(&is->viddec, frame)) < 0)
         return -1;
 
     if (got_picture) {
@@ -2048,7 +2040,7 @@ static int audio_thread(void *arg)
         return AVERROR(ENOMEM);
 
     do {
-        if ((got_frame = decoder_decode_frame(&is->auddec, frame, NULL)) < 0)
+        if ((got_frame = decoder_decode_frame(&is->auddec, frame)) < 0)
             goto the_end;
 
         if (got_frame) {
@@ -2246,14 +2238,14 @@ static int subtitle_thread(void *arg)
         if (!(sp = frame_queue_peek_writable(&is->subpq)))
             return 0;
 
-        if ((got_subtitle = decoder_decode_frame(&is->subdec, NULL, &sp->sub)) < 0)
+        if ((got_subtitle = decoder_decode_frame(&is->subdec, sp->frame)) < 0)
             break;
 
         pts = 0;
 
-        if (got_subtitle && sp->sub.format == 0) {
-            if (sp->sub.pts != AV_NOPTS_VALUE)
-                pts = sp->sub.pts / (double)AV_TIME_BASE;
+        if (got_subtitle && sp->frame->format == AV_SUBTITLE_FMT_BITMAP) {
+            if (sp->frame->subtitle_timing.start_pts != AV_NOPTS_VALUE)
+                pts = (double)sp->frame->subtitle_timing.start_pts / (double)AV_TIME_BASE;
             sp->pts = pts;
             sp->serial = is->subdec.pkt_serial;
             sp->width = is->subdec.avctx->width;
@@ -2263,7 +2255,7 @@ static int subtitle_thread(void *arg)
             /* now we can update the picture count */
             frame_queue_push(&is->subpq);
         } else if (got_subtitle) {
-            avsubtitle_free(&sp->sub);
+            av_frame_free(&sp->frame);
         }
     }
     return 0;
diff --git a/fftools/ffprobe.c b/fftools/ffprobe.c
index 20582ca7ac..a969faec1d 100644
--- a/fftools/ffprobe.c
+++ b/fftools/ffprobe.c
@@ -2375,22 +2375,42 @@ static void show_packet(WriterContext *w, InputFile *ifile, AVPacket *pkt, int p
     fflush(stdout);
 }
 
-static void show_subtitle(WriterContext *w, AVSubtitle *sub, AVStream *stream,
+static void show_subtitle(WriterContext *w, AVFrame *sub, AVStream *stream,
                           AVFormatContext *fmt_ctx)
 {
     AVBPrint pbuf;
+    const char *s;
 
     av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
 
     writer_print_section_header(w, SECTION_ID_SUBTITLE);
 
     print_str ("media_type",         "subtitle");
-    print_ts  ("pts",                 sub->pts);
-    print_time("pts_time",            sub->pts, &AV_TIME_BASE_Q);
-    print_int ("format",              sub->format);
-    print_int ("start_display_time",  sub->start_display_time);
-    print_int ("end_display_time",    sub->end_display_time);
-    print_int ("num_rects",           sub->num_rects);
+    print_ts  ("pts",                 sub->subtitle_timing.start_pts);
+    print_time("pts_time",            sub->subtitle_timing.start_pts, &AV_TIME_BASE_Q);
+    print_time("duration",            sub->subtitle_timing.duration, &AV_TIME_BASE_Q);
+
+    // Remain compatible with previous outputs
+    switch (sub->format) {
+    case AV_SUBTITLE_FMT_BITMAP:
+        print_int ("format",         0);
+        break;
+    case AV_SUBTITLE_FMT_TEXT:
+        print_int ("format",         1);
+        break;
+    case AV_SUBTITLE_FMT_ASS:
+        print_int ("format",         1);
+        break;
+    default:
+        print_int ("format",         -1);
+        break;
+    }
+
+    s = av_get_subtitle_fmt_name(sub->format);
+    if (s) print_str    ("format_str", s);
+    else   print_str_opt("format_str", "unknown");
+
+    print_int ("num_subtitle_rects",           sub->num_subtitle_areas);
 
     writer_print_section_footer(w);
 
@@ -2557,7 +2577,6 @@ static av_always_inline int process_frame(WriterContext *w,
     AVFormatContext *fmt_ctx = ifile->fmt_ctx;
     AVCodecContext *dec_ctx = ifile->streams[pkt->stream_index].dec_ctx;
     AVCodecParameters *par = ifile->streams[pkt->stream_index].st->codecpar;
-    AVSubtitle sub;
     int ret = 0, got_frame = 0;
 
     clear_log(1);
@@ -2565,6 +2584,7 @@ static av_always_inline int process_frame(WriterContext *w,
         switch (par->codec_type) {
         case AVMEDIA_TYPE_VIDEO:
         case AVMEDIA_TYPE_AUDIO:
+        case AVMEDIA_TYPE_SUBTITLE:
             if (*packet_new) {
                 ret = avcodec_send_packet(dec_ctx, pkt);
                 if (ret == AVERROR(EAGAIN)) {
@@ -2583,12 +2603,6 @@ static av_always_inline int process_frame(WriterContext *w,
                 }
             }
             break;
-
-        case AVMEDIA_TYPE_SUBTITLE:
-            if (*packet_new)
-                ret = avcodec_decode_subtitle2(dec_ctx, &sub, &got_frame, pkt);
-            *packet_new = 0;
-            break;
         default:
             *packet_new = 0;
         }
@@ -2603,12 +2617,11 @@ static av_always_inline int process_frame(WriterContext *w,
         nb_streams_frames[pkt->stream_index]++;
         if (do_show_frames)
             if (is_sub)
-                show_subtitle(w, &sub, ifile->streams[pkt->stream_index].st, fmt_ctx);
+                show_subtitle(w, frame, ifile->streams[pkt->stream_index].st, fmt_ctx);
             else
                 show_frame(w, frame, ifile->streams[pkt->stream_index].st, fmt_ctx);
-        if (is_sub)
-            avsubtitle_free(&sub);
     }
+
     return got_frame || *packet_new;
 }
 
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v3 08/26] avfilter/subtitles: Add subtitles.c for subtitle frame allocation
  2022-01-20  3:25   ` [FFmpeg-devel] [PATCH v3 00/26] Subtitle Filtering 2022 ffmpegagent
                       ` (6 preceding siblings ...)
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 07/26] fftools/play, probe: Adjust for subtitle changes ffmpegagent
@ 2022-01-20  3:25     ` ffmpegagent
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 09/26] avfilter/avfilter: Handle subtitle frames ffmpegagent
                       ` (18 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  3:25 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Analog to avfilter/video.c and avfilter/audio.c

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavfilter/Makefile    |  1 +
 libavfilter/avfilter.c  |  4 +++
 libavfilter/internal.h  |  1 +
 libavfilter/subtitles.c | 63 +++++++++++++++++++++++++++++++++++++++++
 libavfilter/subtitles.h | 44 ++++++++++++++++++++++++++++
 5 files changed, 113 insertions(+)
 create mode 100644 libavfilter/subtitles.c
 create mode 100644 libavfilter/subtitles.h

diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 282967144b..f1176644c9 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -19,6 +19,7 @@ OBJS = allfilters.o                                                     \
        framequeue.o                                                     \
        graphdump.o                                                      \
        graphparser.o                                                    \
+       subtitles.o                                                      \
        video.o                                                          \
 
 OBJS-$(HAVE_THREADS)                         += pthread.o
diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c
index 7362bcdab5..df5b8f483c 100644
--- a/libavfilter/avfilter.c
+++ b/libavfilter/avfilter.c
@@ -43,6 +43,7 @@
 #include "formats.h"
 #include "framepool.h"
 #include "internal.h"
+#include "subtitles.h"
 
 #include "libavutil/ffversion.h"
 const char av_filter_ffversion[] = "FFmpeg version " FFMPEG_VERSION;
@@ -1475,6 +1476,9 @@ int ff_inlink_make_frame_writable(AVFilterLink *link, AVFrame **rframe)
     case AVMEDIA_TYPE_AUDIO:
         out = ff_get_audio_buffer(link, frame->nb_samples);
         break;
+    case AVMEDIA_TYPE_SUBTITLE:
+        out = ff_get_subtitles_buffer(link, link->format);
+        break;
     default:
         return AVERROR(EINVAL);
     }
diff --git a/libavfilter/internal.h b/libavfilter/internal.h
index 1099b82b4b..fc09ef574c 100644
--- a/libavfilter/internal.h
+++ b/libavfilter/internal.h
@@ -90,6 +90,7 @@ struct AVFilterPad {
     union {
         AVFrame *(*video)(AVFilterLink *link, int w, int h);
         AVFrame *(*audio)(AVFilterLink *link, int nb_samples);
+        AVFrame *(*subtitle)(AVFilterLink *link, int format);
     } get_buffer;
 
     /**
diff --git a/libavfilter/subtitles.c b/libavfilter/subtitles.c
new file mode 100644
index 0000000000..951bfd612c
--- /dev/null
+++ b/libavfilter/subtitles.c
@@ -0,0 +1,63 @@
+/*
+ * 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
+ */
+
+#include "libavutil/common.h"
+
+#include "subtitles.h"
+#include "avfilter.h"
+#include "internal.h"
+
+
+AVFrame *ff_null_get_subtitles_buffer(AVFilterLink *link, int format)
+{
+    return ff_get_subtitles_buffer(link->dst->outputs[0], format);
+}
+
+AVFrame *ff_default_get_subtitles_buffer(AVFilterLink *link, int format)
+{
+    AVFrame *frame;
+
+    frame = av_frame_alloc();
+    if (!frame)
+        return NULL;
+
+    frame->format = format;
+    frame->type = AVMEDIA_TYPE_SUBTITLE;
+
+    if (av_frame_get_buffer2(frame, 0) < 0) {
+        av_frame_free(&frame);
+        return NULL;
+    }
+
+    return frame;
+}
+
+AVFrame *ff_get_subtitles_buffer(AVFilterLink *link, int format)
+{
+    AVFrame *ret = NULL;
+
+    if (link->dstpad->get_buffer.subtitle)
+        ret = link->dstpad->get_buffer.subtitle(link, format);
+
+    if (!ret)
+        ret = ff_default_get_subtitles_buffer(link, format);
+
+    return ret;
+}
diff --git a/libavfilter/subtitles.h b/libavfilter/subtitles.h
new file mode 100644
index 0000000000..4a9115126e
--- /dev/null
+++ b/libavfilter/subtitles.h
@@ -0,0 +1,44 @@
+/*
+ * 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
+ */
+
+#ifndef AVFILTER_SUBTITLES_H
+#define AVFILTER_SUBTITLES_H
+
+#include "avfilter.h"
+#include "internal.h"
+
+/** default handler for get_subtitles_buffer() for subtitle inputs */
+AVFrame *ff_default_get_subtitles_buffer(AVFilterLink *link, int format);
+
+/** get_subtitles_buffer() handler for filters which simply pass subtitles along */
+AVFrame *ff_null_get_subtitles_buffer(AVFilterLink *link, int format);
+
+/**
+ * Request a subtitles frame with a specific set of permissions.
+ *
+ * @param link           the output link to the filter from which the buffer will
+ *                       be requested
+ * @param format         The subtitles format.
+ * @return               A reference to the frame. This must be unreferenced with
+ *                       av_frame_free when you are finished with it.
+*/
+AVFrame *ff_get_subtitles_buffer(AVFilterLink *link, int format);
+
+#endif /* AVFILTER_SUBTITLES_H */
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v3 09/26] avfilter/avfilter: Handle subtitle frames
  2022-01-20  3:25   ` [FFmpeg-devel] [PATCH v3 00/26] Subtitle Filtering 2022 ffmpegagent
                       ` (7 preceding siblings ...)
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 08/26] avfilter/subtitles: Add subtitles.c for subtitle frame allocation ffmpegagent
@ 2022-01-20  3:25     ` ffmpegagent
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 10/26] avfilter/avfilter: Fix hardcoded input index ffmpegagent
                       ` (17 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  3:25 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavfilter/avfilter.c      |  8 +++++---
 libavfilter/avfilter.h      | 11 +++++++++++
 libavfilter/avfiltergraph.c |  5 +++++
 libavfilter/formats.c       | 22 ++++++++++++++++++++++
 libavfilter/formats.h       |  3 +++
 libavfilter/internal.h      | 18 +++++++++++++++---
 6 files changed, 61 insertions(+), 6 deletions(-)

diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c
index df5b8f483c..75d5e86539 100644
--- a/libavfilter/avfilter.c
+++ b/libavfilter/avfilter.c
@@ -56,7 +56,8 @@ static void tlog_ref(void *ctx, AVFrame *ref, int end)
             ref->linesize[0], ref->linesize[1], ref->linesize[2], ref->linesize[3],
             ref->pts, ref->pkt_pos);
 
-    if (ref->width) {
+    switch(ref->type) {
+    case AVMEDIA_TYPE_VIDEO:
         ff_tlog(ctx, " a:%d/%d s:%dx%d i:%c iskey:%d type:%c",
                 ref->sample_aspect_ratio.num, ref->sample_aspect_ratio.den,
                 ref->width, ref->height,
@@ -64,12 +65,13 @@ static void tlog_ref(void *ctx, AVFrame *ref, int end)
                 ref->top_field_first ? 'T' : 'B',    /* Top / Bottom */
                 ref->key_frame,
                 av_get_picture_type_char(ref->pict_type));
-    }
-    if (ref->nb_samples) {
+        break;
+    case AVMEDIA_TYPE_AUDIO:
         ff_tlog(ctx, " cl:%"PRId64"d n:%d r:%d",
                 ref->channel_layout,
                 ref->nb_samples,
                 ref->sample_rate);
+        break;
     }
 
     ff_tlog(ctx, "]%s", end ? "\n" : "");
diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h
index b105dc3159..9f917deb41 100644
--- a/libavfilter/avfilter.h
+++ b/libavfilter/avfilter.h
@@ -45,6 +45,7 @@
 #include "libavutil/log.h"
 #include "libavutil/samplefmt.h"
 #include "libavutil/pixfmt.h"
+#include "libavutil/subfmt.h"
 #include "libavutil/rational.h"
 
 #include "libavfilter/version.h"
@@ -343,6 +344,12 @@ typedef struct AVFilter {
          * and outputs use the same sample rate and channel count/layout.
          */
         const enum AVSampleFormat *samples_list;
+        /**
+         * Analogous to pixels, but delimited by AV_SUBTITLE_FMT_NONE
+         * and restricted to filters that only have AVMEDIA_TYPE_SUBTITLE
+         * inputs and outputs.
+         */
+        const enum AVSubtitleType *subs_list;
         /**
          * Equivalent to { pix_fmt, AV_PIX_FMT_NONE } as pixels_list.
          */
@@ -351,6 +358,10 @@ typedef struct AVFilter {
          * Equivalent to { sample_fmt, AV_SAMPLE_FMT_NONE } as samples_list.
          */
         enum AVSampleFormat sample_fmt;
+        /**
+         * Equivalent to { sub_fmt, AV_SUBTITLE_FMT_NONE } as subs_list.
+         */
+        enum AVSubtitleType sub_fmt;
     } formats;
 
     int priv_size;      ///< size of private data to allocate for the filter
diff --git a/libavfilter/avfiltergraph.c b/libavfilter/avfiltergraph.c
index b8b432e98b..f4987654af 100644
--- a/libavfilter/avfiltergraph.c
+++ b/libavfilter/avfiltergraph.c
@@ -311,6 +311,8 @@ static int filter_link_check_formats(void *log, AVFilterLink *link, AVFilterForm
             return ret;
         break;
 
+    case AVMEDIA_TYPE_SUBTITLE:
+        return 0;
     default:
         av_assert0(!"reached");
     }
@@ -441,6 +443,9 @@ static int query_formats(AVFilterGraph *graph, void *log_ctx)
             if (!link)
                 continue;
 
+            if (link->type == AVMEDIA_TYPE_SUBTITLE)
+                continue;
+
             neg = ff_filter_get_negotiation(link);
             av_assert0(neg);
             for (neg_step = 1; neg_step < neg->nb_mergers; neg_step++) {
diff --git a/libavfilter/formats.c b/libavfilter/formats.c
index ba62f73248..5c972bb183 100644
--- a/libavfilter/formats.c
+++ b/libavfilter/formats.c
@@ -19,6 +19,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
+#include "libavutil/subfmt.h"
 #include "libavutil/avassert.h"
 #include "libavutil/channel_layout.h"
 #include "libavutil/common.h"
@@ -431,6 +432,12 @@ int ff_add_channel_layout(AVFilterChannelLayouts **l, uint64_t channel_layout)
     return 0;
 }
 
+int ff_add_subtitle_type(AVFilterFormats **avff, int64_t fmt)
+{
+    ADD_FORMAT(avff, fmt, ff_formats_unref, int, formats, nb_formats);
+    return 0;
+}
+
 AVFilterFormats *ff_make_formats_list_singleton(int fmt)
 {
     int fmts[2] = { fmt, -1 };
@@ -450,6 +457,13 @@ AVFilterFormats *ff_all_formats(enum AVMediaType type)
                 return NULL;
             fmt++;
         }
+    } else if (type == AVMEDIA_TYPE_SUBTITLE) {
+        if (ff_add_subtitle_type(&ret, AV_SUBTITLE_FMT_BITMAP) < 0)
+            return NULL;
+        if (ff_add_subtitle_type(&ret, AV_SUBTITLE_FMT_ASS) < 0)
+            return NULL;
+        if (ff_add_subtitle_type(&ret, AV_SUBTITLE_FMT_TEXT) < 0)
+            return NULL;
     }
 
     return ret;
@@ -724,6 +738,10 @@ int ff_default_query_formats(AVFilterContext *ctx)
         type    = AVMEDIA_TYPE_AUDIO;
         formats = ff_make_format_list(f->formats.samples_list);
         break;
+    case FF_FILTER_FORMATS_SUBFMTS_LIST:
+        type    = AVMEDIA_TYPE_SUBTITLE;
+        formats = ff_make_format_list(f->formats.subs_list);
+        break;
     case FF_FILTER_FORMATS_SINGLE_PIXFMT:
         type    = AVMEDIA_TYPE_VIDEO;
         formats = ff_make_formats_list_singleton(f->formats.pix_fmt);
@@ -732,6 +750,10 @@ int ff_default_query_formats(AVFilterContext *ctx)
         type    = AVMEDIA_TYPE_AUDIO;
         formats = ff_make_formats_list_singleton(f->formats.sample_fmt);
         break;
+    case FF_FILTER_FORMATS_SINGLE_SUBFMT:
+        type    = AVMEDIA_TYPE_SUBTITLE;
+        formats = ff_make_formats_list_singleton(f->formats.sub_fmt);
+        break;
     default:
         av_assert2(!"Unreachable");
     /* Intended fallthrough */
diff --git a/libavfilter/formats.h b/libavfilter/formats.h
index a884d15213..94754ebd88 100644
--- a/libavfilter/formats.h
+++ b/libavfilter/formats.h
@@ -180,6 +180,9 @@ int ff_set_common_formats_from_list(AVFilterContext *ctx, const int *fmts);
 av_warn_unused_result
 int ff_add_channel_layout(AVFilterChannelLayouts **l, uint64_t channel_layout);
 
+av_warn_unused_result
+int ff_add_subtitle_type(AVFilterFormats **avff, int64_t fmt);
+
 /**
  * Add *ref as a new reference to f.
  */
diff --git a/libavfilter/internal.h b/libavfilter/internal.h
index fc09ef574c..192b0ae196 100644
--- a/libavfilter/internal.h
+++ b/libavfilter/internal.h
@@ -149,9 +149,11 @@ static av_always_inline int ff_filter_execute(AVFilterContext *ctx, avfilter_act
 
 enum FilterFormatsState {
     /**
-     * The default value meaning that this filter supports all formats
-     * and (for audio) sample rates and channel layouts/counts as long
-     * as these properties agree for all inputs and outputs.
+     * The default value meaning that this filter supports
+     * - For video:     all formats
+     * - For audio:     all sample rates and channel layouts/counts
+     * - For subtitles: all subtitle formats
+     * as long as these properties agree for all inputs and outputs.
      * This state is only allowed in case all inputs and outputs actually
      * have the same type.
      * The union is unused in this state.
@@ -162,8 +164,10 @@ enum FilterFormatsState {
     FF_FILTER_FORMATS_QUERY_FUNC,       ///< formats.query active.
     FF_FILTER_FORMATS_PIXFMT_LIST,      ///< formats.pixels_list active.
     FF_FILTER_FORMATS_SAMPLEFMTS_LIST,  ///< formats.samples_list active.
+    FF_FILTER_FORMATS_SUBFMTS_LIST,     ///< formats.subs_list active.
     FF_FILTER_FORMATS_SINGLE_PIXFMT,    ///< formats.pix_fmt active
     FF_FILTER_FORMATS_SINGLE_SAMPLEFMT, ///< formats.sample_fmt active.
+    FF_FILTER_FORMATS_SINGLE_SUBFMT,    ///< formats.sub_fmt active.
 };
 
 #define FILTER_QUERY_FUNC(func)        \
@@ -175,16 +179,24 @@ enum FilterFormatsState {
 #define FILTER_SAMPLEFMTS_ARRAY(array) \
         .formats.samples_list = array, \
         .formats_state        = FF_FILTER_FORMATS_SAMPLEFMTS_LIST
+#define FILTER_SUBFMTS_ARRAY(array) \
+        .formats.subs_list = array, \
+        .formats_state        = FF_FILTER_FORMATS_SUBFMTS_LIST
 #define FILTER_PIXFMTS(...)            \
     FILTER_PIXFMTS_ARRAY(((const enum AVPixelFormat []) { __VA_ARGS__, AV_PIX_FMT_NONE }))
 #define FILTER_SAMPLEFMTS(...)         \
     FILTER_SAMPLEFMTS_ARRAY(((const enum AVSampleFormat[]) { __VA_ARGS__, AV_SAMPLE_FMT_NONE }))
+#define FILTER_SUBFMTS(...)         \
+    FILTER_SUBFMTS_ARRAY(((const enum AVSubtitleType[]) { __VA_ARGS__, AV_SUBTITLE_FMT_NONE }))
 #define FILTER_SINGLE_PIXFMT(pix_fmt_)  \
         .formats.pix_fmt = pix_fmt_,    \
         .formats_state   = FF_FILTER_FORMATS_SINGLE_PIXFMT
 #define FILTER_SINGLE_SAMPLEFMT(sample_fmt_) \
         .formats.sample_fmt = sample_fmt_,   \
         .formats_state      = FF_FILTER_FORMATS_SINGLE_SAMPLEFMT
+#define FILTER_SINGLE_SUBFMT(sub_fmt_) \
+        .formats.sub_fmt = sub_fmt_,   \
+        .formats_state      = FF_FILTER_FORMATS_SINGLE_SUBFMT
 
 #define FILTER_INOUTPADS(inout, array) \
        .inout        = array, \
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v3 10/26] avfilter/avfilter: Fix hardcoded input index
  2022-01-20  3:25   ` [FFmpeg-devel] [PATCH v3 00/26] Subtitle Filtering 2022 ffmpegagent
                       ` (8 preceding siblings ...)
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 09/26] avfilter/avfilter: Handle subtitle frames ffmpegagent
@ 2022-01-20  3:25     ` ffmpegagent
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 11/26] avfilter/sbuffer: Add sbuffersrc and sbuffersink filters ffmpegagent
                       ` (16 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  3:25 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

This fix targets (rare) cases where multiple input pads have a
.filter_frame function. ff_request_frame_to_filter needs
to call ff_request_frame with the correct input pad
instead of the hardcoded first one.

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavfilter/avfilter.c | 18 +++++++++++++-----
 1 file changed, 13 insertions(+), 5 deletions(-)

diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c
index 75d5e86539..aa9aa71f53 100644
--- a/libavfilter/avfilter.c
+++ b/libavfilter/avfilter.c
@@ -463,7 +463,7 @@ static int64_t guess_status_pts(AVFilterContext *ctx, int status, AVRational lin
     return AV_NOPTS_VALUE;
 }
 
-static int ff_request_frame_to_filter(AVFilterLink *link)
+static int ff_request_frame_to_filter(AVFilterLink *link, int input_index)
 {
     int ret = -1;
 
@@ -472,8 +472,8 @@ static int ff_request_frame_to_filter(AVFilterLink *link)
     link->frame_blocked_in = 1;
     if (link->srcpad->request_frame)
         ret = link->srcpad->request_frame(link);
-    else if (link->src->inputs[0])
-        ret = ff_request_frame(link->src->inputs[0]);
+    else if (link->src->inputs[input_index])
+        ret = ff_request_frame(link->src->inputs[input_index]);
     if (ret < 0) {
         if (ret != AVERROR(EAGAIN) && ret != link->status_in)
             ff_avfilter_link_set_in_status(link, ret, guess_status_pts(link->src, ret, link->time_base));
@@ -1172,6 +1172,14 @@ static int forward_status_change(AVFilterContext *filter, AVFilterLink *in)
 {
     unsigned out = 0, progress = 0;
     int ret;
+    int input_index = 0;
+
+    for (int i = 0; i < in->dst->nb_inputs; i++) {
+        if (&in->dst->input_pads[i] == in->dstpad) {
+            input_index = i;
+            break;
+        }
+    }
 
     av_assert0(!in->status_out);
     if (!filter->nb_outputs) {
@@ -1181,7 +1189,7 @@ static int forward_status_change(AVFilterContext *filter, AVFilterLink *in)
     while (!in->status_out) {
         if (!filter->outputs[out]->status_in) {
             progress++;
-            ret = ff_request_frame_to_filter(filter->outputs[out]);
+            ret = ff_request_frame_to_filter(filter->outputs[out], input_index);
             if (ret < 0)
                 return ret;
         }
@@ -1218,7 +1226,7 @@ static int ff_filter_activate_default(AVFilterContext *filter)
     for (i = 0; i < filter->nb_outputs; i++) {
         if (filter->outputs[i]->frame_wanted_out &&
             !filter->outputs[i]->frame_blocked_in) {
-            return ff_request_frame_to_filter(filter->outputs[i]);
+            return ff_request_frame_to_filter(filter->outputs[i], 0);
         }
     }
     return FFERROR_NOT_READY;
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v3 11/26] avfilter/sbuffer: Add sbuffersrc and sbuffersink filters
  2022-01-20  3:25   ` [FFmpeg-devel] [PATCH v3 00/26] Subtitle Filtering 2022 ffmpegagent
                       ` (9 preceding siblings ...)
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 10/26] avfilter/avfilter: Fix hardcoded input index ffmpegagent
@ 2022-01-20  3:25     ` ffmpegagent
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 12/26] avfilter/overlaygraphicsubs: Add overlaygraphicsubs and graphicsub2video filters ffmpegagent
                       ` (15 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  3:25 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 configure                |  2 +-
 libavfilter/allfilters.c |  2 ++
 libavfilter/buffersink.c | 54 ++++++++++++++++++++++++++++++
 libavfilter/buffersink.h |  7 ++++
 libavfilter/buffersrc.c  | 72 ++++++++++++++++++++++++++++++++++++++++
 libavfilter/buffersrc.h  |  1 +
 6 files changed, 137 insertions(+), 1 deletion(-)

diff --git a/configure b/configure
index 52ef214b73..1928a77344 100755
--- a/configure
+++ b/configure
@@ -7856,7 +7856,7 @@ print_enabled_components(){
         fi
     done
     if [ "$name" = "filter_list" ]; then
-        for c in asrc_abuffer vsrc_buffer asink_abuffer vsink_buffer; do
+        for c in asrc_abuffer vsrc_buffer ssrc_sbuffer asink_abuffer vsink_buffer ssink_sbuffer; do
             printf "    &ff_%s,\n" $c >> $TMPH
         done
     fi
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 714468afce..ebb2e374bd 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -557,8 +557,10 @@ extern const AVFilter ff_avsrc_movie;
  * being the same while having different 'types'). */
 extern  const AVFilter ff_asrc_abuffer;
 extern  const AVFilter ff_vsrc_buffer;
+extern  const AVFilter ff_ssrc_sbuffer;
 extern  const AVFilter ff_asink_abuffer;
 extern  const AVFilter ff_vsink_buffer;
+extern  const AVFilter ff_ssink_sbuffer;
 extern const AVFilter ff_af_afifo;
 extern const AVFilter ff_vf_fifo;
 
diff --git a/libavfilter/buffersink.c b/libavfilter/buffersink.c
index c0215669e7..0b268c2fa4 100644
--- a/libavfilter/buffersink.c
+++ b/libavfilter/buffersink.c
@@ -29,6 +29,8 @@
 #include "libavutil/internal.h"
 #include "libavutil/opt.h"
 
+#include "libavcodec/avcodec.h"
+
 #define FF_INTERNAL_FIELDS 1
 #include "framequeue.h"
 
@@ -57,6 +59,10 @@ typedef struct BufferSinkContext {
     int *sample_rates;                  ///< list of accepted sample rates
     int sample_rates_size;
 
+    /* only used for subtitles */
+    enum AVSubtitleType *subtitle_types;     ///< list of accepted subtitle types, must be terminated with -1
+    int subtitle_types_size;
+
     AVFrame *peeked_frame;
 } BufferSinkContext;
 
@@ -305,6 +311,28 @@ static int asink_query_formats(AVFilterContext *ctx)
     return 0;
 }
 
+static int ssink_query_formats(AVFilterContext *ctx)
+{
+    BufferSinkContext *buf = ctx->priv;
+    AVFilterFormats *formats = NULL;
+    unsigned i;
+    int ret;
+
+    CHECK_LIST_SIZE(subtitle_types)
+    if (buf->subtitle_types_size) {
+        for (i = 0; i < NB_ITEMS(buf->subtitle_types); i++)
+            if ((ret = ff_add_subtitle_type(&formats, buf->subtitle_types[i])) < 0)
+                return ret;
+        if ((ret = ff_set_common_formats(ctx, formats)) < 0)
+            return ret;
+    } else {
+        if ((ret = ff_default_query_formats(ctx)) < 0)
+            return ret;
+    }
+
+    return 0;
+}
+
 #define OFFSET(x) offsetof(BufferSinkContext, x)
 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
 static const AVOption buffersink_options[] = {
@@ -322,9 +350,16 @@ static const AVOption abuffersink_options[] = {
     { NULL },
 };
 #undef FLAGS
+#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_SUBTITLE_PARAM
+static const AVOption sbuffersink_options[] = {
+    { "subtitle_types", "set the supported subtitle formats", OFFSET(subtitle_types), AV_OPT_TYPE_BINARY, .flags = FLAGS },
+    { NULL },
+};
+#undef FLAGS
 
 AVFILTER_DEFINE_CLASS(buffersink);
 AVFILTER_DEFINE_CLASS(abuffersink);
+AVFILTER_DEFINE_CLASS(sbuffersink);
 
 static const AVFilterPad avfilter_vsink_buffer_inputs[] = {
     {
@@ -363,3 +398,22 @@ const AVFilter ff_asink_abuffer = {
     .outputs       = NULL,
     FILTER_QUERY_FUNC(asink_query_formats),
 };
+
+static const AVFilterPad avfilter_ssink_sbuffer_inputs[] = {
+    {
+        .name = "default",
+        .type = AVMEDIA_TYPE_SUBTITLE,
+    },
+};
+
+const AVFilter ff_ssink_sbuffer = {
+    .name          = "sbuffersink",
+    .description   = NULL_IF_CONFIG_SMALL("Buffer subtitle frames, and make them available to the end of the filter graph."),
+    .priv_class    = &sbuffersink_class,
+    .priv_size     = sizeof(BufferSinkContext),
+    .init          = common_init,
+    .activate      = activate,
+    FILTER_INPUTS(avfilter_ssink_sbuffer_inputs),
+    .outputs       = NULL,
+    FILTER_QUERY_FUNC(ssink_query_formats),
+};
diff --git a/libavfilter/buffersink.h b/libavfilter/buffersink.h
index 69ed0f29a8..11905abdc5 100644
--- a/libavfilter/buffersink.h
+++ b/libavfilter/buffersink.h
@@ -129,6 +129,13 @@ typedef struct AVABufferSinkParams {
  */
 attribute_deprecated
 AVABufferSinkParams *av_abuffersink_params_alloc(void);
+
+/**
+ * Deprecated and unused struct to use for initializing an sbuffersink context.
+ */
+typedef struct AVSBufferSinkParams {
+    const int *subtitle_type;
+} AVSBufferSinkParams;
 #endif
 
 /**
diff --git a/libavfilter/buffersrc.c b/libavfilter/buffersrc.c
index b0611872f1..d2362999a2 100644
--- a/libavfilter/buffersrc.c
+++ b/libavfilter/buffersrc.c
@@ -39,6 +39,7 @@
 #include "formats.h"
 #include "internal.h"
 #include "video.h"
+#include "libavcodec/avcodec.h"
 
 typedef struct BufferSourceContext {
     const AVClass    *class;
@@ -63,6 +64,9 @@ typedef struct BufferSourceContext {
     uint64_t channel_layout;
     char    *channel_layout_str;
 
+    /* subtitle only */
+    enum AVSubtitleType subtitle_type;
+
     int eof;
 } BufferSourceContext;
 
@@ -130,6 +134,13 @@ int av_buffersrc_parameters_set(AVFilterContext *ctx, AVBufferSrcParameters *par
         if (param->channel_layout)
             s->channel_layout = param->channel_layout;
         break;
+    case AVMEDIA_TYPE_SUBTITLE:
+        s->subtitle_type = param->format;
+        if (param->width > 0)
+            s->w = param->width;
+        if (param->height > 0)
+            s->h = param->height;
+        break;
     default:
         return AVERROR_BUG;
     }
@@ -197,6 +208,8 @@ int attribute_align_arg av_buffersrc_add_frame_flags(AVFilterContext *ctx, AVFra
             CHECK_AUDIO_PARAM_CHANGE(ctx, s, frame->sample_rate, frame->channel_layout,
                                      frame->channels, frame->format, frame->pts);
             break;
+        case AVMEDIA_TYPE_SUBTITLE:
+            break;
         default:
             return AVERROR(EINVAL);
         }
@@ -269,6 +282,7 @@ unsigned av_buffersrc_get_nb_failed_requests(AVFilterContext *buffer_src)
 #define OFFSET(x) offsetof(BufferSourceContext, x)
 #define A AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_AUDIO_PARAM
 #define V AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
+#define S AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_SUBTITLE_PARAM
 
 static const AVOption buffer_options[] = {
     { "width",         NULL,                     OFFSET(w),                AV_OPT_TYPE_INT,      { .i64 = 0 }, 0, INT_MAX, V },
@@ -298,6 +312,16 @@ static const AVOption abuffer_options[] = {
 
 AVFILTER_DEFINE_CLASS(abuffer);
 
+static const AVOption sbuffer_options[] = {
+    { "time_base",     NULL, OFFSET(time_base),            AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, 0, INT_MAX, S },
+    { "subtitle_type", NULL, OFFSET(subtitle_type),        AV_OPT_TYPE_INT,      { .i64 = 0 }, 0, INT_MAX, S },
+    { "width",         NULL, OFFSET(w),                    AV_OPT_TYPE_INT,      { .i64 = 0 }, 0, INT_MAX, V },
+    { "height",        NULL, OFFSET(h),                    AV_OPT_TYPE_INT,      { .i64 = 0 }, 0, INT_MAX, V },
+    { NULL },
+};
+
+AVFILTER_DEFINE_CLASS(sbuffer);
+
 static av_cold int init_audio(AVFilterContext *ctx)
 {
     BufferSourceContext *s = ctx->priv;
@@ -347,6 +371,21 @@ static av_cold int init_audio(AVFilterContext *ctx)
     return ret;
 }
 
+static av_cold int init_subtitle(AVFilterContext *ctx)
+{
+    BufferSourceContext *c = ctx->priv;
+
+    if (c->subtitle_type == AV_SUBTITLE_FMT_BITMAP)
+        av_log(ctx, AV_LOG_VERBOSE, "graphical subtitles - w:%d h:%d tb:%d/%d\n",
+               c->w, c->h, c->time_base.num, c->time_base.den);
+    else
+        av_log(ctx, AV_LOG_VERBOSE, "text subtitles -  w:%d h:%d tb:%d/%d\n",
+               c->w, c->h, c->time_base.num, c->time_base.den);
+
+    return 0;
+}
+
+
 static av_cold void uninit(AVFilterContext *ctx)
 {
     BufferSourceContext *s = ctx->priv;
@@ -381,6 +420,11 @@ static int query_formats(AVFilterContext *ctx)
         if ((ret = ff_set_common_channel_layouts(ctx, channel_layouts)) < 0)
             return ret;
         break;
+    case AVMEDIA_TYPE_SUBTITLE:
+        if ((ret = ff_add_format         (&formats, c->subtitle_type)) < 0 ||
+            (ret = ff_set_common_formats (ctx     , formats   )) < 0)
+            return ret;
+        break;
     default:
         return AVERROR(EINVAL);
     }
@@ -408,6 +452,11 @@ static int config_props(AVFilterLink *link)
         if (!c->channel_layout)
             c->channel_layout = link->channel_layout;
         break;
+    case AVMEDIA_TYPE_SUBTITLE:
+        link->format = c->subtitle_type;
+        link->w = c->w;
+        link->h = c->h;
+        break;
     default:
         return AVERROR(EINVAL);
     }
@@ -472,3 +521,26 @@ const AVFilter ff_asrc_abuffer = {
     FILTER_QUERY_FUNC(query_formats),
     .priv_class = &abuffer_class,
 };
+
+static const AVFilterPad ssrc_sbuffer_outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_SUBTITLE,
+        .request_frame = request_frame,
+        .config_props  = config_props,
+    },
+};
+
+const AVFilter ff_ssrc_sbuffer = {
+    .name          = "sbuffer",
+    .description   = NULL_IF_CONFIG_SMALL("Buffer subtitle frames, and make them accessible to the filterchain."),
+    .priv_size     = sizeof(BufferSourceContext),
+
+    .init      = init_subtitle,
+    .uninit    = uninit,
+
+    .inputs    = NULL,
+    FILTER_OUTPUTS(ssrc_sbuffer_outputs),
+    FILTER_QUERY_FUNC(query_formats),
+    .priv_class = &sbuffer_class,
+};
diff --git a/libavfilter/buffersrc.h b/libavfilter/buffersrc.h
index 08fbd18a47..929a2fa249 100644
--- a/libavfilter/buffersrc.h
+++ b/libavfilter/buffersrc.h
@@ -74,6 +74,7 @@ typedef struct AVBufferSrcParameters {
     /**
      * video: the pixel format, value corresponds to enum AVPixelFormat
      * audio: the sample format, value corresponds to enum AVSampleFormat
+     * subtitles: the subtitle format, value corresponds to enum AVSubtitleType
      */
     int format;
     /**
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v3 12/26] avfilter/overlaygraphicsubs: Add overlaygraphicsubs and graphicsub2video filters
  2022-01-20  3:25   ` [FFmpeg-devel] [PATCH v3 00/26] Subtitle Filtering 2022 ffmpegagent
                       ` (10 preceding siblings ...)
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 11/26] avfilter/sbuffer: Add sbuffersrc and sbuffersink filters ffmpegagent
@ 2022-01-20  3:25     ` ffmpegagent
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 13/26] avfilter/overlaytextsubs: Add overlaytextsubs and textsubs2video filters ffmpegagent
                       ` (14 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  3:25 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

- overlaygraphicsubs (VS -> V)
  Overlay graphic subtitles onto a video stream

- graphicsub2video {S -> V)
  Converts graphic subtitles to video frames (with alpha)
  Gets auto-inserted for retaining compatibility with
  sub2video command lines

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 doc/filters.texi                    | 118 +++++
 libavfilter/Makefile                |   2 +
 libavfilter/allfilters.c            |   2 +
 libavfilter/vf_overlaygraphicsubs.c | 765 ++++++++++++++++++++++++++++
 4 files changed, 887 insertions(+)
 create mode 100644 libavfilter/vf_overlaygraphicsubs.c

diff --git a/doc/filters.texi b/doc/filters.texi
index 248c09caf8..2bd3737cf5 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -25764,6 +25764,124 @@ tools.
 
 @c man end VIDEO SINKS
 
+@chapter Subtitle Filters
+@c man begin SUBTITLE FILTERS
+
+When you configure your FFmpeg build, you can disable any of the
+existing filters using @code{--disable-filters}.
+
+Below is a description of the currently available subtitle filters.
+
+@section graphicsub2video
+
+Renders graphic subtitles as video frames.
+
+This filter replaces the previous "sub2video" hack which did the conversion implicitly and up-front as subtitle filtering wasn't possible at that time.
+To retain compatibility with earlier sub2video command lines, this filter is being auto-inserted in those cases.
+
+For overlaying graphicsal subtitles it is recommended to use the 'overlay_graphicsubs' filter which is more efficient and takes less processing resources.
+
+This filter is still useful in cases where the overlay is done with hardware acceleration (e.g. overlay_qsv, overlay_vaapi, overlay_cuda) for preparing the overlay frames.
+
+Inputs:
+@itemize
+@item 0: Subtitles [BITMAP]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Video [RGB32]
+@end itemize
+
+
+It accepts the following parameters:
+
+@table @option
+@item size, s
+Set the size of the output video frame.
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Overlay PGS subtitles
+(not recommended - better use overlay_graphicsubs)
+@example
+ffmpeg -i "https://streams.videolan.org/samples/sub/PGS/Girl_With_The_Dragon_Tattoo_2%3A23%3A56.mkv" -filter_complex "[0:1]graphicsub2video[subs];[0:0][subs]overlay" output.mp4
+@end example
+
+@item
+Overlay PGS subtitles implicitly
+The graphicsub2video is inserted automatically for compatibility with legacy command lines.
+@example
+ffmpeg -i "https://streams.videolan.org/samples/sub/PGS/Girl_With_The_Dragon_Tattoo_2%3A23%3A56.mkv" -filter_complex "[0:0][0:1]overlay" output.mp4
+@end example
+@end itemize
+
+@section overlaygraphicsubs
+
+Overlay graphic subtitles onto a video stream.
+
+This filter can blend graphical subtitles on a video stream directly, i.e. without creating full-size alpha images first.
+The blending operation is limited to the area of the subtitle rectangles, which also means that no processing is done at times where no subtitles are to be displayed.
+
+Inputs:
+@itemize
+@item 0: Video [YUV420P, YUV422P, YUV444P, ARGB, RGBA, ABGR, BGRA, RGB24, BGR24]
+@item 1: Subtitles [BITMAP]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Video (same as input)
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+@item x
+@item y
+Set the expression for the x and y coordinates of the overlaid video
+on the main video. Default value is "0" for both expressions. In case
+the expression is invalid, it is set to a huge value (meaning that the
+overlay will not be displayed within the output visible area).
+
+@item eof_action
+See @ref{framesync}.
+
+@item eval
+Set when the expressions for @option{x}, and @option{y} are evaluated.
+
+It accepts the following values:
+@table @samp
+@item init
+only evaluate expressions once during the filter initialization or
+when a command is processed
+
+@item frame
+evaluate expressions for each incoming frame
+@end table
+
+Default value is @samp{frame}.
+
+@item shortest
+See @ref{framesync}.
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Overlay PGS subtitles
+@example
+ffmpeg -i "https://streams.videolan.org/samples/sub/PGS/Girl_With_The_Dragon_Tattoo_2%3A23%3A56.mkv" -filter_complex "[0:0][0:1]overlaygraphicsubs" output.mp4
+@end example
+@end itemize
+@c man end SUBTITLE FILTERS
+
 @chapter Multimedia Filters
 @c man begin MULTIMEDIA FILTERS
 
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index f1176644c9..c326b8d32b 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -298,6 +298,7 @@ OBJS-$(CONFIG_GBLUR_FILTER)                  += vf_gblur.o
 OBJS-$(CONFIG_GBLUR_VULKAN_FILTER)           += vf_gblur_vulkan.o vulkan.o vulkan_filter.o
 OBJS-$(CONFIG_GEQ_FILTER)                    += vf_geq.o
 OBJS-$(CONFIG_GRADFUN_FILTER)                += vf_gradfun.o
+OBJS-$(CONFIG_GRAPHICSUB2VIDEO_FILTER)       += vf_overlaygraphicsubs.o framesync.o
 OBJS-$(CONFIG_GRAPHMONITOR_FILTER)           += f_graphmonitor.o
 OBJS-$(CONFIG_GRAYWORLD_FILTER)              += vf_grayworld.o
 OBJS-$(CONFIG_GREYEDGE_FILTER)               += vf_colorconstancy.o
@@ -379,6 +380,7 @@ OBJS-$(CONFIG_OVERLAY_OPENCL_FILTER)         += vf_overlay_opencl.o opencl.o \
 OBJS-$(CONFIG_OVERLAY_QSV_FILTER)            += vf_overlay_qsv.o framesync.o
 OBJS-$(CONFIG_OVERLAY_VAAPI_FILTER)          += vf_overlay_vaapi.o framesync.o vaapi_vpp.o
 OBJS-$(CONFIG_OVERLAY_VULKAN_FILTER)         += vf_overlay_vulkan.o vulkan.o vulkan_filter.o
+OBJS-$(CONFIG_OVERLAYGRAPHICSUBS_FILTER)     += vf_overlaygraphicsubs.o framesync.o
 OBJS-$(CONFIG_OWDENOISE_FILTER)              += vf_owdenoise.o
 OBJS-$(CONFIG_PAD_FILTER)                    += vf_pad.o
 OBJS-$(CONFIG_PAD_OPENCL_FILTER)             += vf_pad_opencl.o opencl.o opencl/pad.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index ebb2e374bd..aeb3f0d846 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -358,6 +358,7 @@ extern const AVFilter ff_vf_oscilloscope;
 extern const AVFilter ff_vf_overlay;
 extern const AVFilter ff_vf_overlay_opencl;
 extern const AVFilter ff_vf_overlay_qsv;
+extern const AVFilter ff_vf_overlaygraphicsubs;
 extern const AVFilter ff_vf_overlay_vaapi;
 extern const AVFilter ff_vf_overlay_vulkan;
 extern const AVFilter ff_vf_overlay_cuda;
@@ -546,6 +547,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_svf_graphicsub2video;
 
 /* multimedia sources */
 extern const AVFilter ff_avsrc_amovie;
diff --git a/libavfilter/vf_overlaygraphicsubs.c b/libavfilter/vf_overlaygraphicsubs.c
new file mode 100644
index 0000000000..7b26d8ef37
--- /dev/null
+++ b/libavfilter/vf_overlaygraphicsubs.c
@@ -0,0 +1,765 @@
+/*
+ * Copyright (c) 2021 softworkz (derived from vf_overlay)
+ * Copyright (c) 2010 Stefano Sabatini
+ * Copyright (c) 2010 Baptiste Coudurier
+ * Copyright (c) 2007 Bobby Bingham
+ *
+ * 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
+ * overlay graphical subtitles on top of a video frame
+ */
+
+#include "libavutil/eval.h"
+#include "libavutil/imgutils.h"
+#include "libavutil/opt.h"
+#include "internal.h"
+#include "drawutils.h"
+#include "framesync.h"
+
+enum var_name {
+    VAR_MAIN_W,    VAR_MW,
+    VAR_MAIN_H,    VAR_MH,
+    VAR_OVERLAY_W, VAR_OW,
+    VAR_OVERLAY_H, VAR_OH,
+    VAR_HSUB,
+    VAR_VSUB,
+    VAR_X,
+    VAR_Y,
+    VAR_N,
+    VAR_POS,
+    VAR_T,
+    VAR_VARS_NB
+};
+
+typedef struct OverlaySubsContext {
+    const AVClass *class;
+    int x, y;                   ///< position of overlaid picture
+    int w, h;
+    AVFrame *outpicref;
+
+    int main_is_packed_rgb;
+    uint8_t main_rgba_map[4];
+    int main_has_alpha;
+    uint8_t overlay_rgba_map[4];
+    int eval_mode;              ///< EvalMode
+    int use_caching;
+    AVFrame *cache_frame;
+
+    FFFrameSync fs;
+
+    int main_pix_step[4];       ///< steps per pixel for each plane of the main output
+    int hsub, vsub;             ///< chroma subsampling values
+    const AVPixFmtDescriptor *main_desc; ///< format descriptor for main input
+
+    double var_values[VAR_VARS_NB];
+    char *x_expr, *y_expr;
+
+    AVExpr *x_pexpr, *y_pexpr;
+
+    int pic_counter;
+} OverlaySubsContext;
+
+static const char *const var_names[] = {
+    "main_w",    "W", ///< width  of the main    video
+    "main_h",    "H", ///< height of the main    video
+    "overlay_w", "w", ///< width  of the overlay video
+    "overlay_h", "h", ///< height of the overlay video
+    "hsub",
+    "vsub",
+    "x",
+    "y",
+    "n",            ///< number of frame
+    "pos",          ///< position in the file
+    "t",            ///< timestamp expressed in seconds
+    NULL
+};
+
+#define MAIN    0
+#define OVERLAY 1
+
+#define R 0
+#define G 1
+#define B 2
+#define A 3
+
+#define Y 0
+#define U 1
+#define V 2
+
+enum EvalMode {
+    EVAL_MODE_INIT,
+    EVAL_MODE_FRAME,
+    EVAL_MODE_NB
+};
+
+static av_cold void overlay_graphicsubs_uninit(AVFilterContext *ctx)
+{
+    OverlaySubsContext *s = ctx->priv;
+
+    av_frame_free(&s->cache_frame);
+    ff_framesync_uninit(&s->fs);
+    av_expr_free(s->x_pexpr); s->x_pexpr = NULL;
+    av_expr_free(s->y_pexpr); s->y_pexpr = NULL;
+}
+
+static inline int normalize_xy(double d, int chroma_sub)
+{
+    if (isnan(d))
+        return INT_MAX;
+    return (int)d & ~((1 << chroma_sub) - 1);
+}
+
+static void eval_expr(AVFilterContext *ctx)
+{
+    OverlaySubsContext *s = ctx->priv;
+
+    s->var_values[VAR_X] = av_expr_eval(s->x_pexpr, s->var_values, NULL);
+    s->var_values[VAR_Y] = av_expr_eval(s->y_pexpr, s->var_values, NULL);
+    /* It is necessary if x is expressed from y  */
+    s->var_values[VAR_X] = av_expr_eval(s->x_pexpr, s->var_values, NULL);
+    s->x = normalize_xy(s->var_values[VAR_X], s->hsub);
+    s->y = normalize_xy(s->var_values[VAR_Y], s->vsub);
+}
+
+static int set_expr(AVExpr **pexpr, const char *expr, const char *option, void *log_ctx)
+{
+    int ret;
+    AVExpr *old = NULL;
+
+    if (*pexpr)
+        old = *pexpr;
+    ret = av_expr_parse(pexpr, expr, var_names,
+                        NULL, NULL, NULL, NULL, 0, log_ctx);
+    if (ret < 0) {
+        av_log(log_ctx, AV_LOG_ERROR,
+               "Error when evaluating the expression '%s' for %s\n",
+               expr, option);
+        *pexpr = old;
+        return ret;
+    }
+
+    av_expr_free(old);
+    return 0;
+}
+
+static int overlay_graphicsubs_query_formats(AVFilterContext *ctx)
+{
+    AVFilterFormats *formats;
+    AVFilterLink *inlink0 = ctx->inputs[0];
+    AVFilterLink *inlink1 = ctx->inputs[1];
+    AVFilterLink *outlink = ctx->outputs[0];
+    int ret;
+    static const enum AVSubtitleType subtitle_fmts[] = { AV_SUBTITLE_FMT_BITMAP, AV_SUBTITLE_FMT_NONE };
+    static const enum AVPixelFormat supported_pix_fmts[] = {
+        AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV444P,
+        AV_PIX_FMT_ARGB,  AV_PIX_FMT_RGBA,
+        AV_PIX_FMT_ABGR,  AV_PIX_FMT_BGRA,
+        AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24,
+        AV_PIX_FMT_NONE
+    };
+
+    /* set input0 video formats */
+    formats = ff_make_format_list(supported_pix_fmts);
+    if ((ret = ff_formats_ref(formats, &inlink0->outcfg.formats)) < 0)
+        return ret;
+
+    /* set output0 video formats */
+    if ((ret = ff_formats_ref(formats, &outlink->incfg.formats)) < 0)
+        return ret;
+
+    /* set input1 subtitle formats */
+    formats = ff_make_format_list(subtitle_fmts);
+    if ((ret = ff_formats_ref(formats, &inlink1->outcfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    OverlaySubsContext *s = ctx->priv;
+    int ret;
+
+    if ((ret = ff_framesync_init_dualinput(&s->fs, ctx)) < 0)
+        return ret;
+
+    outlink->w = ctx->inputs[MAIN]->w;
+    outlink->h = ctx->inputs[MAIN]->h;
+    outlink->time_base = ctx->inputs[MAIN]->time_base;
+    outlink->frame_rate = ctx->inputs[MAIN]->frame_rate;
+
+    return ff_framesync_configure(&s->fs);
+}
+
+// divide by 255 and round to nearest
+// apply a fast variant: (X+127)/255 = ((X+127)*257+257)>>16 = ((X+128)*257)>>16
+#define FAST_DIV255(x) ((((x) + 128) * 257) >> 16)
+
+// calculate the non-pre-multiplied alpha, applying the general equation:
+// alpha = alpha_overlay / ( (alpha_main + alpha_overlay) - (alpha_main * alpha_overlay) )
+// (((x) << 16) - ((x) << 9) + (x)) is a faster version of: 255 * 255 * x
+// ((((x) + (y)) << 8) - ((x) + (y)) - (y) * (x)) is a faster version of: 255 * (x + y)
+#define UNPREMULTIPLY_ALPHA(x, y) ((((x) << 16) - ((x) << 9) + (x)) / ((((x) + (y)) << 8) - ((x) + (y)) - (y) * (x)))
+
+/**
+ * Blend image in src to destination buffer dst at position (x, y).
+ */
+static av_always_inline void blend_packed_rgb(const AVFilterContext *ctx,
+    const AVFrame *dst, const AVSubtitleArea *src,
+    int x, int y,
+    int is_straight)
+{
+    OverlaySubsContext *s = ctx->priv;
+    int i, imax, j, jmax;
+    const int src_w = src->w;
+    const int src_h = src->h;
+    const int dst_w = dst->width;
+    const int dst_h = dst->height;
+    uint8_t alpha;          ///< the amount of overlay to blend on to main
+    const int dr = s->main_rgba_map[R];
+    const int dg = s->main_rgba_map[G];
+    const int db = s->main_rgba_map[B];
+    const int da = s->main_rgba_map[A];
+    const int dstep = s->main_pix_step[0];
+    const int sr = s->overlay_rgba_map[R];
+    const int sg = s->overlay_rgba_map[G];
+    const int sb = s->overlay_rgba_map[B];
+    const int sa = s->overlay_rgba_map[A];
+    int slice_start, slice_end;
+    uint8_t *S, *sp, *d, *dp;
+
+    i = FFMAX(-y, 0);
+    imax = FFMIN3(-y + dst_h, FFMIN(src_h, dst_h), y + src_h);
+
+    slice_start = i;
+    slice_end = i + imax;
+
+    sp = src->buf[0]->data + slice_start       * src->linesize[0];
+    dp = dst->data[0] + (slice_start + y) * dst->linesize[0];
+
+    for (i = slice_start; i < slice_end; i++) {
+        j = FFMAX(-x, 0);
+        S = sp + j;
+        d = dp + ((x + j) * dstep);
+
+        for (jmax = FFMIN(-x + dst_w, src_w); j < jmax; j++) {
+            uint32_t val = src->pal[*S];
+            const uint8_t *sval = (uint8_t *)&val;
+            alpha = sval[sa];
+
+            // if the main channel has an alpha channel, alpha has to be calculated
+            // to create an un-premultiplied (straight) alpha value
+            if (s->main_has_alpha && alpha != 0 && alpha != 255) {
+                const uint8_t alpha_d = d[da];
+                alpha = UNPREMULTIPLY_ALPHA(alpha, alpha_d);
+            }
+
+            switch (alpha) {
+            case 0:
+                break;
+            case 255:
+                d[dr] = sval[sr];
+                d[dg] = sval[sg];
+                d[db] = sval[sb];
+                break;
+            default:
+                // main_value = main_value * (1 - alpha) + overlay_value * alpha
+                // since alpha is in the range 0-255, the result must divided by 255
+                d[dr] = is_straight ? FAST_DIV255(d[dr] * (255 - alpha) + sval[sr] * alpha) :
+                        FFMIN(FAST_DIV255(d[dr] * (255 - alpha)) + sval[sr], 255);
+                d[dg] = is_straight ? FAST_DIV255(d[dg] * (255 - alpha) + sval[sg] * alpha) :
+                        FFMIN(FAST_DIV255(d[dg] * (255 - alpha)) + sval[sg], 255);
+                d[db] = is_straight ? FAST_DIV255(d[db] * (255 - alpha) + sval[sb] * alpha) :
+                        FFMIN(FAST_DIV255(d[db] * (255 - alpha)) + sval[sb], 255);
+            }
+
+            if (s->main_has_alpha) {
+                switch (alpha) {
+                case 0:
+                    break;
+                case 255:
+                    d[da] = sval[sa];
+                    break;
+                default:
+                    // apply alpha compositing: main_alpha += (1-main_alpha) * overlay_alpha
+                    d[da] += FAST_DIV255((255 - d[da]) * S[sa]);
+                }
+            }
+            d += dstep;
+            S += 1;
+        }
+        dp += dst->linesize[0];
+        sp += src->linesize[0];
+    }
+}
+
+static av_always_inline void blend_plane_8_8bits(const AVFilterContext *ctx, const AVFrame *dst, const AVSubtitleArea *area,
+    const uint32_t *yuv_pal, int src_w, int src_h, int dst_w, int dst_h, int plane, int hsub, int vsub,
+    int x, int y, int dst_plane, int dst_offset, int dst_step)
+{
+    const int src_wp = AV_CEIL_RSHIFT(src_w, hsub);
+    const int src_hp = AV_CEIL_RSHIFT(src_h, vsub);
+    const int dst_wp = AV_CEIL_RSHIFT(dst_w, hsub);
+    const int dst_hp = AV_CEIL_RSHIFT(dst_h, vsub);
+    const int yp = y >> vsub;
+    const int xp = x >> hsub;
+    uint8_t *s, *sp, *d, *dp, *dap;
+    int imax, i, j, jmax;
+    int slice_start, slice_end;
+
+    i = FFMAX(-yp, 0);                                                                                     \
+    imax = FFMIN3(-yp + dst_hp, FFMIN(src_hp, dst_hp), yp + src_hp);                                       \
+
+    slice_start = i;
+    slice_end = i + imax;
+
+    sp = area->buf[0]->data + (slice_start << vsub) * area->linesize[0];
+    dp = dst->data[dst_plane] + (yp + slice_start) * dst->linesize[dst_plane] + dst_offset;
+
+    dap = dst->data[3] + ((yp + slice_start) << vsub) * dst->linesize[3];
+
+    for (i = slice_start; i < slice_end; i++) {
+        j = FFMAX(-xp, 0);
+        d = dp + (xp + j) * dst_step;
+        s = sp + (j << hsub);
+        jmax = FFMIN(-xp + dst_wp, src_wp);
+
+        for (; j < jmax; j++) {
+            uint32_t val = yuv_pal[*s];
+            const uint8_t *sval = (uint8_t *)&val;
+            const int alpha = sval[3];
+            const int max = 255, mid = 128;
+            const int d_int = *d;
+            const int sval_int = sval[plane];
+
+            switch (alpha) {
+            case 0:
+                break;
+            case 255:
+                *d = sval[plane];
+                break;
+            default:
+                if (plane > 0)
+                    *d = av_clip(FAST_DIV255((d_int - mid) * (max - alpha) + (sval_int - mid) * alpha) , -mid, mid) + mid;
+                else
+                    *d = FAST_DIV255(d_int * (max - alpha) + sval_int * alpha);
+                break;
+            }
+
+            d += dst_step;
+            s += 1 << hsub;
+        }
+        dp += dst->linesize[dst_plane];
+        sp +=  (1 << vsub) * area->linesize[0];
+        dap += (1 << vsub) * dst->linesize[3];
+    }
+}
+
+#define RGB2Y(r, g, b) (uint8_t)(((66 * (r) + 129 * (g) +  25 * (b) + 128) >> 8) +  16)
+#define RGB2U(r, g, b) (uint8_t)(((-38 * (r) - 74 * (g) + 112 * (b) + 128) >> 8) + 128)
+#define RGB2V(r, g, b) (uint8_t)(((112 * (r) - 94 * (g) -  18 * (b) + 128) >> 8) + 128)
+/* Converts R8 G8 B8 color to YUV. */
+static av_always_inline void rgb_2_yuv(uint8_t r, uint8_t g, uint8_t b, uint8_t* y, uint8_t* u, uint8_t* v)
+{
+    *y = RGB2Y((int)r, (int)g, (int)b);
+    *u = RGB2U((int)r, (int)g, (int)b);
+    *v = RGB2V((int)r, (int)g, (int)b);
+}
+
+
+static av_always_inline void blend_yuv_8_8bits(AVFilterContext *ctx, AVFrame *dst, const AVSubtitleArea *area, int hsub, int vsub, int x, int y)
+{
+    OverlaySubsContext *s = ctx->priv;
+    const int src_w = area->w;
+    const int src_h = area->h;
+    const int dst_w = dst->width;
+    const int dst_h = dst->height;
+    const int sr = s->overlay_rgba_map[R];
+    const int sg = s->overlay_rgba_map[G];
+    const int sb = s->overlay_rgba_map[B];
+    const int sa = s->overlay_rgba_map[A];
+    uint32_t yuvpal[256];
+
+    for (int i = 0; i < 256; ++i) {
+        const uint8_t *rgba = (const uint8_t *)&area->pal[i];
+        uint8_t *yuva = (uint8_t *)&yuvpal[i];
+        rgb_2_yuv(rgba[sr], rgba[sg], rgba[sb], &yuva[Y], &yuva[U], &yuva[V]);
+        yuva[3] = rgba[sa];
+    }
+
+    blend_plane_8_8bits(ctx, dst, area, yuvpal, src_w, src_h, dst_w, dst_h, Y, 0,    0,    x, y, s->main_desc->comp[Y].plane, s->main_desc->comp[Y].offset, s->main_desc->comp[Y].step);
+    blend_plane_8_8bits(ctx, dst, area, yuvpal, src_w, src_h, dst_w, dst_h, U, hsub, vsub, x, y, s->main_desc->comp[U].plane, s->main_desc->comp[U].offset, s->main_desc->comp[U].step);
+    blend_plane_8_8bits(ctx, dst, area, yuvpal, src_w, src_h, dst_w, dst_h, V, hsub, vsub, x, y, s->main_desc->comp[V].plane, s->main_desc->comp[V].offset, s->main_desc->comp[V].step);
+}
+
+static int config_input_main(AVFilterLink *inlink)
+{
+    int ret;
+    AVFilterContext *ctx  = inlink->dst;
+    OverlaySubsContext *s = inlink->dst->priv;
+    const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(inlink->format);
+
+    av_image_fill_max_pixsteps(s->main_pix_step,    NULL, pix_desc);
+    ff_fill_rgba_map(s->overlay_rgba_map, AV_PIX_FMT_RGB32); // it's actually AV_PIX_FMT_PAL8);
+
+    s->hsub = pix_desc->log2_chroma_w;
+    s->vsub = pix_desc->log2_chroma_h;
+
+    s->main_desc = pix_desc;
+
+    s->main_is_packed_rgb = ff_fill_rgba_map(s->main_rgba_map, inlink->format) >= 0;
+    s->main_has_alpha = !!(pix_desc->flags & AV_PIX_FMT_FLAG_ALPHA);
+
+    /* Finish the configuration by evaluating the expressions
+       now when both inputs are configured. */
+    s->var_values[VAR_MAIN_W   ] = s->var_values[VAR_MW] = ctx->inputs[MAIN   ]->w;
+    s->var_values[VAR_MAIN_H   ] = s->var_values[VAR_MH] = ctx->inputs[MAIN   ]->h;
+    s->var_values[VAR_OVERLAY_W] = s->var_values[VAR_OW] = ctx->inputs[OVERLAY]->w;
+    s->var_values[VAR_OVERLAY_H] = s->var_values[VAR_OH] = ctx->inputs[OVERLAY]->h;
+    s->var_values[VAR_HSUB]  = 1<<pix_desc->log2_chroma_w;
+    s->var_values[VAR_VSUB]  = 1<<pix_desc->log2_chroma_h;
+    s->var_values[VAR_X]     = NAN;
+    s->var_values[VAR_Y]     = NAN;
+    s->var_values[VAR_N]     = 0;
+    s->var_values[VAR_T]     = NAN;
+    s->var_values[VAR_POS]   = NAN;
+
+    if ((ret = set_expr(&s->x_pexpr,      s->x_expr,      "x",      ctx)) < 0 ||
+        (ret = set_expr(&s->y_pexpr,      s->y_expr,      "y",      ctx)) < 0)
+        return ret;
+
+    if (s->eval_mode == EVAL_MODE_INIT) {
+        eval_expr(ctx);
+        av_log(ctx, AV_LOG_VERBOSE, "x:%f xi:%d y:%f yi:%d\n",
+               s->var_values[VAR_X], s->x,
+               s->var_values[VAR_Y], s->y);
+    }
+
+    av_log(ctx, AV_LOG_VERBOSE,
+           "main w:%d h:%d fmt:%s overlay w:%d h:%d fmt:%s\n",
+           ctx->inputs[MAIN]->w, ctx->inputs[MAIN]->h,
+           av_get_pix_fmt_name(ctx->inputs[MAIN]->format),
+           ctx->inputs[OVERLAY]->w, ctx->inputs[OVERLAY]->h,
+           av_get_pix_fmt_name(ctx->inputs[OVERLAY]->format));
+    return 0;
+}
+
+static int do_blend(FFFrameSync *fs)
+{
+    AVFilterContext *ctx = fs->parent;
+    AVFrame *mainpic, *second;
+    OverlaySubsContext *s = ctx->priv;
+    AVFilterLink *inlink = ctx->inputs[0];
+    unsigned i;
+    int ret;
+
+    ret = ff_framesync_dualinput_get_writable(fs, &mainpic, &second);
+    if (ret < 0)
+        return ret;
+    if (!second)
+        return ff_filter_frame(ctx->outputs[0], mainpic);
+
+    if (s->eval_mode == EVAL_MODE_FRAME) {
+        int64_t pos = mainpic->pkt_pos;
+
+        s->var_values[VAR_N] = (double)inlink->frame_count_out;
+        s->var_values[VAR_T] = mainpic->pts == AV_NOPTS_VALUE ?
+            NAN :(double)mainpic->pts * av_q2d(inlink->time_base);
+        s->var_values[VAR_POS] = pos == -1 ? NAN : (double)pos;
+
+        s->var_values[VAR_OVERLAY_W] = s->var_values[VAR_OW] = second->width;
+        s->var_values[VAR_OVERLAY_H] = s->var_values[VAR_OH] = second->height;
+        s->var_values[VAR_MAIN_W   ] = s->var_values[VAR_MW] = mainpic->width;
+        s->var_values[VAR_MAIN_H   ] = s->var_values[VAR_MH] = mainpic->height;
+
+        eval_expr(ctx);
+        av_log(ctx, AV_LOG_DEBUG, "n:%f t:%f pos:%f x:%f xi:%d y:%f yi:%d\n",
+               s->var_values[VAR_N], s->var_values[VAR_T], s->var_values[VAR_POS],
+               s->var_values[VAR_X], s->x,
+               s->var_values[VAR_Y], s->y);
+    }
+
+    for (i = 0; i < second->num_subtitle_areas; i++) {
+        const AVSubtitleArea *sub_area = second->subtitle_areas[i];
+
+        if (sub_area->type != AV_SUBTITLE_FMT_BITMAP) {
+            av_log(NULL, AV_LOG_WARNING, "overlay_graphicsubs: non-bitmap subtitle\n");
+            return AVERROR_INVALIDDATA;
+        }
+
+        switch (inlink->format) {
+        case AV_PIX_FMT_YUV420P:
+            blend_yuv_8_8bits(ctx, mainpic, sub_area, 1, 1, sub_area->x + s->x, sub_area->y + s->y);
+            break;
+        case AV_PIX_FMT_YUV422P:
+            blend_yuv_8_8bits(ctx, mainpic, sub_area, 1, 0, sub_area->x + s->x, sub_area->y + s->y);
+            break;
+        case AV_PIX_FMT_YUV444P:
+            blend_yuv_8_8bits(ctx, mainpic, sub_area, 0, 0, sub_area->x + s->x, sub_area->y + s->y);
+            break;
+        case AV_PIX_FMT_RGB24:
+        case AV_PIX_FMT_BGR24:
+        case AV_PIX_FMT_ARGB:
+        case AV_PIX_FMT_RGBA:
+        case AV_PIX_FMT_BGRA:
+        case AV_PIX_FMT_ABGR:
+            blend_packed_rgb(ctx, mainpic, sub_area, sub_area->x + s->x, sub_area->y + s->y, 1);
+            break;
+        default:
+            av_log(NULL, AV_LOG_ERROR, "Unsupported input pix fmt: %d\n", inlink->format);
+            return AVERROR(EINVAL);
+        }
+    }
+
+    return ff_filter_frame(ctx->outputs[0], mainpic);
+}
+
+static av_cold int overlay_graphicsubs_init(AVFilterContext *ctx)
+{
+    OverlaySubsContext *s = ctx->priv;
+
+    s->fs.on_event = do_blend;
+    return 0;
+}
+
+static int overlay_graphicsubs_activate(AVFilterContext *ctx)
+{
+    OverlaySubsContext *s = ctx->priv;
+    return ff_framesync_activate(&s->fs);
+}
+
+static int graphicsub2video_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_BITMAP, AV_SUBTITLE_FMT_NONE };
+    static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_RGB32, AV_PIX_FMT_NONE };
+    int ret;
+
+    /* set input subtitle formats */
+    formats = ff_make_format_list(subtitle_fmts);
+    if ((ret = ff_formats_ref(formats, &inlink->outcfg.formats)) < 0)
+        return ret;
+
+    /* set output video formats */
+    formats = ff_make_format_list(pix_fmts);
+    if ((ret = ff_formats_ref(formats, &outlink->incfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int graphicsub2video_config_input(AVFilterLink *inlink)
+{
+    AVFilterContext *ctx = inlink->dst;
+    OverlaySubsContext *s = ctx->priv;
+
+    if (s->w <= 0 || s->h <= 0) {
+        s->w = inlink->w;
+        s->h = inlink->h;
+    }
+    return 0;
+}
+
+static int graphicsub2video_config_output(AVFilterLink *outlink)
+{
+    const AVFilterContext *ctx  = outlink->src;
+    OverlaySubsContext *s = ctx->priv;
+    const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(outlink->format);
+
+    outlink->w = s->w;
+    outlink->h = s->h;
+
+    if (outlink->w == 0 && outlink->h == 0) {
+        outlink->w = 1;
+        outlink->h = 1;
+    }
+    outlink->sample_aspect_ratio = (AVRational){1,1};
+    outlink->time_base = ctx->inputs[0]->time_base;
+    outlink->frame_rate = ctx->inputs[0]->frame_rate;
+
+    av_image_fill_max_pixsteps(s->main_pix_step, NULL, pix_desc);
+    ff_fill_rgba_map(s->overlay_rgba_map, AV_PIX_FMT_RGB32);
+
+    s->hsub = pix_desc->log2_chroma_w;
+    s->vsub = pix_desc->log2_chroma_h;
+
+    s->main_desc = pix_desc;
+
+    s->main_is_packed_rgb = ff_fill_rgba_map(s->main_rgba_map, outlink->format) >= 0;
+    s->main_has_alpha = !!(pix_desc->flags & AV_PIX_FMT_FLAG_ALPHA);
+
+    return 0;
+}
+
+static int graphicsub2video_filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFilterLink *outlink = inlink->dst->outputs[0];
+    const AVFilterContext *ctx  = outlink->src;
+    OverlaySubsContext *s = ctx->priv;
+    AVFrame *out;
+    const unsigned num_rects = frame->num_subtitle_areas;
+    unsigned int i;
+    int ret;
+
+    if (s->use_caching && s->cache_frame && frame->repeat_sub
+        && s->cache_frame->subtitle_timing.start_pts == frame->subtitle_timing.start_pts) {
+        out = av_frame_clone(s->cache_frame);
+        if (!out)
+            return AVERROR(ENOMEM);
+
+        ret = av_frame_copy_props(out, frame);
+        if (ret < 0)
+            return ret;
+
+        av_log(inlink->dst, AV_LOG_DEBUG, "graphicsub2video CACHED - size %dx%d  pts: %"PRId64"  areas: %d\n", frame->width, frame->height, frame->subtitle_timing.start_pts, frame->num_subtitle_areas);
+        av_frame_free(&frame);
+        return ff_filter_frame(outlink, out);
+    }
+
+
+    out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
+    if (!out) {
+        av_frame_free(&frame);
+        return AVERROR(ENOMEM);
+    }
+
+    memset(out->data[0], 0, (size_t)out->linesize[0] * out->height);
+
+    out->pts = out->pkt_dts = out->best_effort_timestamp = frame->pts;
+    out->coded_picture_number = out->display_picture_number = s->pic_counter++;
+
+    for (i = 0; i < num_rects; i++) {
+        const AVSubtitleArea  *sub_rect = frame->subtitle_areas[i];
+
+        if (sub_rect->type != AV_SUBTITLE_FMT_BITMAP) {
+            av_log(NULL, AV_LOG_WARNING, "graphicsub2video: non-bitmap subtitle\n");
+            av_frame_free(&frame);
+            return AVERROR_INVALIDDATA;
+        }
+
+        blend_packed_rgb(inlink->dst, out, sub_rect, sub_rect->x, sub_rect->y, 1);
+    }
+
+    av_log(inlink->dst, AV_LOG_DEBUG, "graphicsub2video output - size %dx%d  pts: %"PRId64"  areas: %d\n", out->width, out->height, out->pts, frame->num_subtitle_areas);
+
+    if (s->use_caching) {
+        av_frame_free(&s->cache_frame);
+        s->cache_frame = av_frame_clone(out);
+    }
+
+    av_frame_free(&frame);
+    return ff_filter_frame(outlink, out);
+}
+
+#define OFFSET(x) offsetof(OverlaySubsContext, x)
+#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption overlaygraphicsubs_options[] = {
+    { "x", "set the x expression", OFFSET(x_expr), AV_OPT_TYPE_STRING, {.str = "0"}, 0, 0, .flags = FLAGS },
+    { "y", "set the y expression", OFFSET(y_expr), AV_OPT_TYPE_STRING, {.str = "0"}, 0, 0, .flags = FLAGS },
+    { "eof_action", "Action to take when encountering EOF from secondary input ",
+        OFFSET(fs.opt_eof_action), AV_OPT_TYPE_INT, { .i64 = EOF_ACTION_REPEAT },
+        EOF_ACTION_REPEAT, EOF_ACTION_PASS, .flags = FLAGS, "eof_action" },
+        { "repeat", "Repeat the previous frame.",   0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_REPEAT }, .flags = FLAGS, "eof_action" },
+        { "endall", "End both streams.",            0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_ENDALL }, .flags = FLAGS, "eof_action" },
+        { "pass",   "Pass through the main input.", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_PASS },   .flags = FLAGS, "eof_action" },
+    { "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, {.i64 = EVAL_MODE_FRAME}, 0, EVAL_MODE_NB-1, FLAGS, "eval" },
+         { "init",  "eval expressions once during initialization", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_INIT},  .flags = FLAGS, .unit = "eval" },
+         { "frame", "eval expressions per-frame",                  0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_FRAME}, .flags = FLAGS, .unit = "eval" },
+    { "shortest", "force termination when the shortest input terminates", OFFSET(fs.opt_shortest), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, .flags = FLAGS },
+    { "repeatlast", "repeat overlay of the last overlay frame", OFFSET(fs.opt_repeatlast), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, .flags = FLAGS },
+    { NULL }
+};
+
+static const AVOption graphicsub2video_options[] = {
+    { "size",        "set output frame size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, .flags = FLAGS },
+    { "s",           "set output frame size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, .flags = FLAGS },
+    { "use_caching", "Cache output frames",   OFFSET(use_caching), AV_OPT_TYPE_BOOL, { .i64 = 1}, 0, 1, .flags = FLAGS },
+    { NULL }
+};
+
+FRAMESYNC_DEFINE_CLASS(overlaygraphicsubs, OverlaySubsContext, fs);
+
+static const AVFilterPad overlaygraphicsubs_inputs[] = {
+    {
+        .name         = "main",
+        .type         = AVMEDIA_TYPE_VIDEO,
+        .config_props = config_input_main,
+        .flags        = AVFILTERPAD_FLAG_NEEDS_WRITABLE,
+    },
+    {
+        .name         = "overlay",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+    },
+};
+
+static const AVFilterPad overlaygraphicsubs_outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_VIDEO,
+        .config_props  = config_output,
+    },
+};
+
+const AVFilter ff_vf_overlaygraphicsubs = {
+    .name          = "overlaygraphicsubs",
+    .description   = NULL_IF_CONFIG_SMALL("Overlay graphical subtitles on top of the input."),
+    .preinit       = overlaygraphicsubs_framesync_preinit,
+    .init          = overlay_graphicsubs_init,
+    .uninit        = overlay_graphicsubs_uninit,
+    .priv_size     = sizeof(OverlaySubsContext),
+    .priv_class    = &overlaygraphicsubs_class,
+    .activate      = overlay_graphicsubs_activate,
+    FILTER_INPUTS(overlaygraphicsubs_inputs),
+    FILTER_OUTPUTS(overlaygraphicsubs_outputs),
+    FILTER_QUERY_FUNC(overlay_graphicsubs_query_formats),
+};
+
+AVFILTER_DEFINE_CLASS(graphicsub2video);
+
+static const AVFilterPad graphicsub2video_inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .filter_frame = graphicsub2video_filter_frame,
+        .config_props = graphicsub2video_config_input,
+    },
+};
+
+static const AVFilterPad graphicsub2video_outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_VIDEO,
+        .config_props  = graphicsub2video_config_output,
+    },
+};
+
+const AVFilter ff_svf_graphicsub2video = {
+    .name          = "graphicsub2video",
+    .description   = NULL_IF_CONFIG_SMALL("Convert graphical subtitles to video"),
+    .priv_size     = sizeof(OverlaySubsContext),
+    .priv_class    = &graphicsub2video_class,
+    FILTER_INPUTS(graphicsub2video_inputs),
+    FILTER_OUTPUTS(graphicsub2video_outputs),
+    FILTER_QUERY_FUNC(graphicsub2video_query_formats),
+};
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v3 13/26] avfilter/overlaytextsubs: Add overlaytextsubs and textsubs2video filters
  2022-01-20  3:25   ` [FFmpeg-devel] [PATCH v3 00/26] Subtitle Filtering 2022 ffmpegagent
                       ` (11 preceding siblings ...)
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 12/26] avfilter/overlaygraphicsubs: Add overlaygraphicsubs and graphicsub2video filters ffmpegagent
@ 2022-01-20  3:25     ` ffmpegagent
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 14/26] avfilter/textmod: Add textmod, censor and show_speaker filters ffmpegagent
                       ` (13 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  3:25 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

- overlaytextsubs {VS -> V)
  Overlay text subtitles onto a video stream.

- textsubs2video {S -> V)
  Converts text subtitles to video frames

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 configure                        |   2 +
 doc/filters.texi                 | 113 ++++++
 libavfilter/Makefile             |   2 +
 libavfilter/allfilters.c         |   6 +-
 libavfilter/vf_overlaytextsubs.c | 678 +++++++++++++++++++++++++++++++
 5 files changed, 799 insertions(+), 2 deletions(-)
 create mode 100644 libavfilter/vf_overlaytextsubs.c

diff --git a/configure b/configure
index 1928a77344..7b66e315f2 100755
--- a/configure
+++ b/configure
@@ -3692,6 +3692,7 @@ overlay_qsv_filter_deps="libmfx"
 overlay_qsv_filter_select="qsvvpp"
 overlay_vaapi_filter_deps="vaapi VAProcPipelineCaps_blend_flags"
 overlay_vulkan_filter_deps="vulkan spirv_compiler"
+overlaytextsubs_filter_deps="avcodec libass"
 owdenoise_filter_deps="gpl"
 pad_opencl_filter_deps="opencl"
 pan_filter_deps="swresample"
@@ -3736,6 +3737,7 @@ superequalizer_filter_deps="avcodec"
 superequalizer_filter_select="rdft"
 surround_filter_deps="avcodec"
 surround_filter_select="rdft"
+textsub2video_filter_deps="avcodec libass"
 tinterlace_filter_deps="gpl"
 tinterlace_merge_test_deps="tinterlace_filter"
 tinterlace_pad_test_deps="tinterlace_filter"
diff --git a/doc/filters.texi b/doc/filters.texi
index 2bd3737cf5..02a854851c 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -25880,6 +25880,119 @@ Overlay PGS subtitles
 ffmpeg -i "https://streams.videolan.org/samples/sub/PGS/Girl_With_The_Dragon_Tattoo_2%3A23%3A56.mkv" -filter_complex "[0:0][0:1]overlaygraphicsubs" output.mp4
 @end example
 @end itemize
+
+@section overlaytextsubs
+
+Overlay text subtitles onto a video stream.
+
+This filter supersedes the classic @ref{subtitles} filter opposed to which it does no longer require to open and access the source stream separately, which is often causing problems or doesn't even work for non-local or slow sources.
+
+Inputs:
+@itemize
+@item 0: Video [YUV420P, YUV422P, YUV444P, ARGB, RGBA, ABGR, BGRA, RGB24, BGR24]
+@item 1: Subtitles [TEXT]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Video (same as input)
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+
+@item alpha
+Process alpha channel, by default alpha channel is untouched.
+
+@item fonts_dir
+Set a directory path containing fonts that can be used by the filter.
+These fonts will be used in addition to whatever the font provider uses.
+
+@item default_font_path
+Path to a font file to be used as the default font.
+
+@item font_size
+Set the default font size.
+
+@item fontconfig_file
+Path to ASS fontconfig configuration file.
+
+@item force_style
+Override default style or script info parameters of the subtitles. It accepts a
+string containing ASS style format @code{KEY=VALUE} couples separated by ",".
+
+@item margin
+Set the rendering margin in pixels.
+
+@item render_latest_only
+For rendering, alway use the latest event only, which is covering the given point in time
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Overlay ASS subtitles with animations:
+@example
+ffmpeg -i "http://streams.videolan.org/samples/sub/SSA/subtitle_testing_complex.mkv" -filter_complex "[0:v]overlaytextsubs" -map 0 -y out.mkv
+@end example
+@end itemize
+
+@section textsub2video
+
+Converts text subtitles to video frames.
+
+For overlaying text subtitles onto video frames it is recommended to use the overlay_textsubs filter.
+The textsub2video is useful for for creating transparent text-frames when overlay is done via hw acceleration
+
+Inputs:
+@itemize
+@item 0: Subtitles [TEXT]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Video [RGB32]
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+
+@item rate, r
+Set the framerate for updating overlay frames.
+Normally, overlay frames will only be updated each time when the subtitles to display are changing.
+In cases where subtitles include advanced features (like animation), this parameter determines the frequency by which the overlay frames should be updated.
+
+@item size, s
+Set the output frame size.
+Allows to override the size of output video frames.
+
+@item fonts_dir
+Set a directory path containing fonts that can be used by the filter.
+These fonts will be used in addition to whatever the font provider uses.
+
+@item default_font_path
+Path to a font file to be used as the default font.
+
+@item font_size
+Set the default font size.
+
+@item fontconfig_file
+Path to ASS fontconfig configuration file.
+
+@item force_style
+Override default style or script info parameters of the subtitles. It accepts a
+string containing ASS style format @code{KEY=VALUE} couples separated by ",".
+
+@item margin
+Set the rendering margin in pixels.
+
+@item render_latest_only
+For rendering, alway use the latest event only, which is covering the given point in time.
+@end table
+
 @c man end SUBTITLE FILTERS
 
 @chapter Multimedia Filters
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index c326b8d32b..a3f8a07131 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -381,6 +381,7 @@ OBJS-$(CONFIG_OVERLAY_QSV_FILTER)            += vf_overlay_qsv.o framesync.o
 OBJS-$(CONFIG_OVERLAY_VAAPI_FILTER)          += vf_overlay_vaapi.o framesync.o vaapi_vpp.o
 OBJS-$(CONFIG_OVERLAY_VULKAN_FILTER)         += vf_overlay_vulkan.o vulkan.o vulkan_filter.o
 OBJS-$(CONFIG_OVERLAYGRAPHICSUBS_FILTER)     += vf_overlaygraphicsubs.o framesync.o
+OBJS-$(CONFIG_OVERLAYTEXTSUBS_FILTER)        += vf_overlaytextsubs.o
 OBJS-$(CONFIG_OWDENOISE_FILTER)              += vf_owdenoise.o
 OBJS-$(CONFIG_PAD_FILTER)                    += vf_pad.o
 OBJS-$(CONFIG_PAD_OPENCL_FILTER)             += vf_pad_opencl.o opencl.o opencl/pad.o
@@ -470,6 +471,7 @@ OBJS-$(CONFIG_SWAPRECT_FILTER)               += vf_swaprect.o
 OBJS-$(CONFIG_SWAPUV_FILTER)                 += vf_swapuv.o
 OBJS-$(CONFIG_TBLEND_FILTER)                 += vf_blend.o framesync.o
 OBJS-$(CONFIG_TELECINE_FILTER)               += vf_telecine.o
+OBJS-$(CONFIG_TEXTSUB2VIDEO_FILTER)          += vf_overlaytextsubs.o
 OBJS-$(CONFIG_THISTOGRAM_FILTER)             += vf_histogram.o
 OBJS-$(CONFIG_THRESHOLD_FILTER)              += vf_threshold.o framesync.o
 OBJS-$(CONFIG_THUMBNAIL_FILTER)              += vf_thumbnail.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index aeb3f0d846..5f068a152d 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -358,10 +358,11 @@ extern const AVFilter ff_vf_oscilloscope;
 extern const AVFilter ff_vf_overlay;
 extern const AVFilter ff_vf_overlay_opencl;
 extern const AVFilter ff_vf_overlay_qsv;
-extern const AVFilter ff_vf_overlaygraphicsubs;
-extern const AVFilter ff_vf_overlay_vaapi;
 extern const AVFilter ff_vf_overlay_vulkan;
 extern const AVFilter ff_vf_overlay_cuda;
+extern const AVFilter ff_vf_overlaygraphicsubs;
+extern const AVFilter ff_vf_overlaytextsubs;
+extern const AVFilter ff_vf_overlay_vaapi;
 extern const AVFilter ff_vf_owdenoise;
 extern const AVFilter ff_vf_pad;
 extern const AVFilter ff_vf_pad_opencl;
@@ -548,6 +549,7 @@ extern const AVFilter ff_avf_showwaves;
 extern const AVFilter ff_avf_showwavespic;
 extern const AVFilter ff_vaf_spectrumsynth;
 extern const AVFilter ff_svf_graphicsub2video;
+extern const AVFilter ff_svf_textsub2video;
 
 /* multimedia sources */
 extern const AVFilter ff_avsrc_amovie;
diff --git a/libavfilter/vf_overlaytextsubs.c b/libavfilter/vf_overlaytextsubs.c
new file mode 100644
index 0000000000..173e7149bb
--- /dev/null
+++ b/libavfilter/vf_overlaytextsubs.c
@@ -0,0 +1,678 @@
+/*
+ * 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
+ * overlay text subtitles on top of a video frame
+ */
+
+#include <ass/ass.h>
+#include "libavutil/ass_internal.h"
+#include "libavutil/thread.h"
+
+#include "drawutils.h"
+#include "filters.h"
+
+#include "libavutil/opt.h"
+#include "libavutil/pixdesc.h"
+
+typedef struct TextSubsContext {
+    const AVClass *class;
+    AVMutex mutex;
+    int is_mutex_initialized;
+
+    ASS_Library   *library;
+    ASS_Renderer  *renderer;
+    ASS_Track     *track;
+
+    char *default_font_path;
+    char *fonts_dir;
+    char *fc_file;
+    double font_size;
+    char *force_style;
+    char *language;
+    int margin;
+    int render_latest_only;
+
+    int alpha;
+    FFDrawContext draw;
+
+    int got_header;
+    int out_w, out_h;
+    AVRational frame_rate;
+    AVFrame *last_frame;
+    int need_frame;
+    int eof;
+} TextSubsContext;
+
+/* libass supports a log level ranging from 0 to 7 */
+static const int ass_libavfilter_log_level_map[] = {
+    AV_LOG_QUIET,               /* 0 */
+    AV_LOG_PANIC,               /* 1 */
+    AV_LOG_FATAL,               /* 2 */
+    AV_LOG_ERROR,               /* 3 */
+    AV_LOG_WARNING,             /* 4 */
+    AV_LOG_INFO,                /* 5 */
+    AV_LOG_VERBOSE,             /* 6 */
+    AV_LOG_DEBUG,               /* 7 */
+};
+
+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 void uninit(AVFilterContext *ctx)
+{
+    TextSubsContext *s = ctx->priv;
+
+    if (s->track)
+        ass_free_track(s->track);
+    if (s->renderer)
+        ass_renderer_done(s->renderer);
+    if (s->library)
+        ass_library_done(s->library);
+
+    s->track = NULL;
+    s->renderer = NULL;
+    s->library = NULL;
+
+    if (s->is_mutex_initialized) {
+        ff_mutex_destroy(&s->mutex);
+        s->is_mutex_initialized = 0;
+    }
+
+    av_frame_free(&s->last_frame);
+}
+
+static int overlay_textsubs_query_formats(AVFilterContext *ctx)
+{
+    AVFilterFormats *formats;
+    AVFilterLink *inlink0 = ctx->inputs[0];
+    AVFilterLink *inlink1 = ctx->inputs[1];
+    AVFilterLink *outlink = ctx->outputs[0];
+    static const enum AVSubtitleType subtitle_fmts[] = { AV_SUBTITLE_FMT_ASS, AV_SUBTITLE_FMT_NONE };
+    int ret;
+
+    /* set input0 and output0 video formats */
+    formats = ff_draw_supported_pixel_formats(0);
+    if ((ret = ff_formats_ref(formats, &inlink0->outcfg.formats)) < 0)
+        return ret;
+    if ((ret = ff_formats_ref(formats, &outlink->incfg.formats)) < 0)
+        return ret;
+
+    /* set input1 subtitle formats */
+    formats = ff_make_format_list(subtitle_fmts);
+    if ((ret = ff_formats_ref(formats, &inlink1->outcfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+
+    outlink->w = ctx->inputs[0]->w;
+    outlink->h = ctx->inputs[0]->h;
+    outlink->time_base = ctx->inputs[0]->time_base;
+    outlink->frame_rate = ctx->inputs[0]->frame_rate;
+
+    return 0;
+}
+
+static int config_input_main(AVFilterLink *inlink)
+{
+    AVFilterContext *ctx  = inlink->dst;
+    TextSubsContext *s = inlink->dst->priv;
+    int ret;
+
+    ret = ff_draw_init(&s->draw, inlink->format, s->alpha ? FF_DRAW_PROCESS_ALPHA : 0);
+    if (ret < 0) {
+        av_log(ctx, AV_LOG_ERROR, "Could not initialize ff_draw.\n");
+        return ret;
+    }
+
+    ass_set_frame_size  (s->renderer, inlink->w, inlink->h);
+    ass_set_pixel_aspect(s->renderer, av_q2d(inlink->sample_aspect_ratio));
+
+    av_log(ctx, AV_LOG_VERBOSE, "Subtitle screen: %dx%d\n\n\n\n", inlink->w, inlink->h);
+
+    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(TextSubsContext *s, 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(&s->draw, &color, rgba_color);
+        ff_blend_mask(&s->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 void process_header(AVFilterContext *link, AVFrame *frame)
+{
+    TextSubsContext *s = link->priv;
+    ASS_Track *track = s->track;
+    ASS_Style *style;
+    int sid = 0;
+
+    if (!track)
+        return;
+
+    if (frame && frame->subtitle_header) {
+        char *subtitle_header = (char *)frame->subtitle_header->data;
+        ass_process_codec_private(s->track, subtitle_header, strlen(subtitle_header));
+    }
+    else {
+        char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+        if (!subtitle_header)
+            return;
+
+        ass_process_codec_private(s->track, subtitle_header, strlen(subtitle_header));
+        av_free(subtitle_header);
+    }
+
+    if (s->language)
+        s->track->Language = av_strdup(s->language);
+
+    if (!s->track->event_format) {
+        s->track->event_format = av_strdup("ReadOrder, Layer, Style, Name, MarginL, MarginR, MarginV, Effect, Text");
+    }
+
+    if (s->track->n_styles == 0) {
+        sid = ass_alloc_style(track);
+        style = &s->track->styles[sid];
+        style->Name             = av_strdup("Default");
+        style->PrimaryColour    = 0xffffff00;
+        style->SecondaryColour  = 0x00ffff00;
+        style->OutlineColour    = 0x00000000;
+        style->BackColour       = 0x00000080;
+        style->Bold             = 200;
+        style->ScaleX           = 1.0;
+        style->ScaleY           = 1.0;
+        style->Spacing          = 0;
+        style->BorderStyle      = 1;
+        style->Outline          = 2;
+        style->Shadow           = 3;
+        style->Alignment        = 2;
+    }
+    else
+        style = &s->track->styles[sid];
+
+    style->FontSize         = s->font_size;
+    style->MarginL = style->MarginR = style->MarginV = s->margin;
+
+    track->default_style = sid;
+
+    s->got_header = 1;
+}
+
+static int filter_video_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFilterContext *ctx = inlink->dst;
+    TextSubsContext *s = ctx->priv;
+    int detect_change = 0;
+    ASS_Image *image;
+
+
+    int64_t time_ms  = (int64_t)((double)frame->pts * av_q2d(inlink->time_base) * 1000);
+    int64_t time_ms1 = (int64_t)((double)ctx->inputs[1]->current_pts * av_q2d(ctx->inputs[1]->time_base) * 1000);
+
+    if (time_ms1 < time_ms + 1000)
+        ff_request_frame(ctx->inputs[1]);
+
+    av_log(ctx, AV_LOG_DEBUG, "filter_video_frame - video: %"PRId64"ms  sub: %"PRId64"ms  rel %d\n", time_ms, time_ms1, (time_ms1 < time_ms));
+
+    ff_mutex_lock(&s->mutex);
+    image = ass_render_frame(s->renderer, s->track, time_ms, &detect_change);
+    ff_mutex_unlock(&s->mutex);
+
+    if (detect_change)
+        av_log(ctx, AV_LOG_DEBUG, "Change happened at time ms:%"PRId64"\n", time_ms);
+
+    overlay_ass_image(s, frame, image);
+
+    return ff_filter_frame(ctx->outputs[0], frame);
+}
+
+static int filter_subtitle_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFilterContext *ctx = inlink->dst;
+    TextSubsContext *s = ctx->priv;
+    const int64_t start_time = av_rescale_q(frame->subtitle_timing.start_pts, AV_TIME_BASE_Q, av_make_q(1, 1000));
+    const int64_t duration   = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, av_make_q(1, 1000));
+    const int64_t frame_time = (int64_t)((double)frame->pts * av_q2d(inlink->time_base) * 1000);
+
+    // Postpone header processing until we receive a frame with content
+    if (!s->got_header && frame->num_subtitle_areas > 0)
+        process_header(ctx, frame);
+
+    av_log(ctx, AV_LOG_DEBUG, "filter_subtitle_frame dur: %"PRId64"ms frame: %"PRId64"ms  sub: %"PRId64"ms  repeat_sub %d\n", duration, frame_time, start_time, frame->repeat_sub);
+
+    if (frame->repeat_sub)
+        goto exit;
+
+    ff_mutex_lock(&s->mutex);
+
+    if (s->render_latest_only && s->track->n_events > 0) {
+        const int64_t previous_start_time = s->track->events[s->track->n_events - 1].Start;
+        const int64_t diff = start_time - previous_start_time;
+        for (int i = s->track->n_events - 1; i >= 0; i--) {
+            if (previous_start_time != s->track->events[i].Start)
+                break;
+
+            if (s->track->events[i].Duration > diff)
+                s->track->events[i].Duration = diff;
+        }
+    }
+
+    for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+        char *ass_line = frame->subtitle_areas[i]->ass;
+        if (!ass_line)
+            continue;
+
+        ass_process_chunk(s->track, ass_line, strlen(ass_line), start_time, duration);
+    }
+
+    ff_mutex_unlock(&s->mutex);
+
+exit:
+    av_frame_free(&frame);
+    return 0;
+}
+
+static av_cold int init(AVFilterContext *ctx)
+{
+    int ret;
+    TextSubsContext *s = ctx->priv;
+
+    s->library = ass_library_init();
+
+    if (!s->library) {
+        av_log(ctx, AV_LOG_ERROR, "Could not initialize libass.\n");
+        return AVERROR(EINVAL);
+    }
+
+    ass_set_message_cb(s->library, ass_log, ctx);
+
+    /* Initialize fonts */
+    if (s->fonts_dir)
+        ass_set_fonts_dir(s->library, s->fonts_dir);
+
+    ass_set_extract_fonts(s->library, 1);
+
+    s->renderer = ass_renderer_init(s->library);
+    if (!s->renderer) {
+        av_log(ctx, AV_LOG_ERROR, "Could not initialize libass renderer.\n");
+        return AVERROR(EINVAL);
+    }
+
+    s->track = ass_new_track(s->library);
+    if (!s->track) {
+        av_log(ctx, AV_LOG_ERROR, "ass_new_track() failed!\n");
+        return AVERROR(EINVAL);
+    }
+
+    ass_set_check_readorder(s->track, 0);
+
+    ass_set_fonts(s->renderer, s->default_font_path, NULL, 1, s->fc_file, 1);
+
+    if (s->force_style) {
+        char **list = NULL;
+        char *temp = NULL;
+        char *ptr = av_strtok(s->force_style, ",", &temp);
+        int i = 0;
+        while (ptr) {
+            av_dynarray_add(&list, &i, ptr);
+            if (!list) {
+                return AVERROR(ENOMEM);
+            }
+            ptr = av_strtok(NULL, ",", &temp);
+        }
+        av_dynarray_add(&list, &i, NULL);
+        if (!list) {
+            return AVERROR(ENOMEM);
+        }
+        ass_set_style_overrides(s->library, list);
+        av_free(list);
+    }
+
+    ret = ff_mutex_init(&s->mutex, NULL);
+    if (ret) {
+        av_log(ctx, AV_LOG_ERROR, "mutex initialiuzation failed! Error code: %d\n", ret);
+        return AVERROR(EINVAL);
+    }
+
+    s->is_mutex_initialized = 1;
+
+    return ret;
+}
+
+static int textsub2video_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 input0 subtitle format */
+    formats = ff_make_format_list(subtitle_fmts);
+    if ((ret = ff_formats_ref(formats, &inlink->outcfg.formats)) < 0)
+        return ret;
+
+    /* set output0 video format */
+    formats = ff_draw_supported_pixel_formats(AV_PIX_FMT_FLAG_ALPHA);
+    if ((ret = ff_formats_ref(formats, &outlink->incfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int textsub2video_config_input(AVFilterLink *inlink)
+{
+    AVFilterContext *ctx = inlink->dst;
+    TextSubsContext *s = ctx->priv;
+
+    if (s->out_w <= 0 || s->out_h <= 0) {
+        s->out_w = inlink->w;
+        s->out_h = inlink->h;
+    }
+
+    return 0;
+}
+
+static int textsub2video_config_output(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    TextSubsContext *s = ctx->priv;
+    int ret;
+
+    ret = ff_draw_init(&s->draw, outlink->format, FF_DRAW_PROCESS_ALPHA);
+    if (ret < 0) {
+        av_log(ctx, AV_LOG_ERROR, "Could not initialize ff_draw.\n");
+        return ret;
+    }
+
+    if (s->out_w <= 0 || s->out_h <= 0) {
+        av_log(ctx, AV_LOG_ERROR, "No output image size set.\n");
+        return AVERROR(EINVAL);
+    }
+
+    ass_set_frame_size  (s->renderer, s->out_w, s->out_h);
+
+    outlink->w = s->out_w;
+    outlink->h = s->out_h;
+    outlink->sample_aspect_ratio = (AVRational){1,1};
+    outlink->frame_rate = s->frame_rate;
+
+    return 0;
+}
+
+static int textsub2video_request_frame(AVFilterLink *outlink)
+{
+    TextSubsContext *s = outlink->src->priv;
+    AVFilterLink *inlink = outlink->src->inputs[0];
+    int64_t last_pts = outlink->current_pts;
+    int64_t next_pts, time_ms;
+    int i, detect_change = 0, status;
+    AVFrame *out;
+    ASS_Image *image;
+
+    status = ff_outlink_get_status(inlink);
+    if (status == AVERROR_EOF)
+        return AVERROR_EOF;
+
+    if (s->eof)
+        return AVERROR_EOF;
+
+    if (inlink->current_pts == AV_NOPTS_VALUE) { // || outlink->current_pts > inlink->current_pts) {
+        int ret = ff_request_frame(inlink);
+        if (ret == AVERROR_EOF) {
+            s->eof = 1;
+        }
+
+        if (ret != 0)
+            av_log(outlink->src, AV_LOG_DEBUG, "ff_request_frame returned: %d\n", ret);
+
+        s->need_frame = 1;
+        return 0;
+    }
+
+    if (last_pts == AV_NOPTS_VALUE)
+        next_pts = last_pts = inlink->current_pts * av_q2d(inlink->time_base) / av_q2d(outlink->time_base);
+    else
+        next_pts = last_pts + (int64_t)(1.0 / av_q2d(outlink->frame_rate) / av_q2d(outlink->time_base));
+
+    time_ms = (int64_t)((double)next_pts * av_q2d(outlink->time_base) * 1000);
+
+    ff_mutex_lock(&s->mutex);
+    image = ass_render_frame(s->renderer, s->track, time_ms, &detect_change);
+    ff_mutex_unlock(&s->mutex);
+
+    if (detect_change)
+        av_log(outlink->src, AV_LOG_VERBOSE, "Change happened at time ms:%"PRId64" pts:%"PRId64"\n", time_ms, next_pts);
+    else if (s->last_frame) {
+        out = av_frame_clone(s->last_frame);
+        if (!out)
+            return AVERROR(ENOMEM);
+
+        out->pts = out->pkt_dts = out->best_effort_timestamp = next_pts;
+        return ff_filter_frame(outlink, out);
+    }
+
+    out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
+    if (!out)
+        return AVERROR(ENOMEM);
+
+    for (i = 0; i < AV_NUM_DATA_POINTERS; i++) {
+        if (out->buf[i] && i != 1)
+            memset(out->buf[i]->data, 0, out->buf[i]->size);
+    }
+
+    out->pts = out->pkt_dts = out->best_effort_timestamp = next_pts;
+
+    if (image)
+        overlay_ass_image(s, out, image);
+
+    av_frame_free(&s->last_frame);
+
+    s->last_frame = av_frame_clone(out);
+
+    return ff_filter_frame(outlink, out);
+}
+
+static int textsub2video_filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFilterContext *ctx = inlink->dst;
+    TextSubsContext *s = ctx->priv;
+    const int64_t start_time = av_rescale_q(frame->subtitle_timing.start_pts, AV_TIME_BASE_Q, av_make_q(1, 1000));
+    const int64_t duration   = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, av_make_q(1, 1000));
+
+    av_log(ctx, AV_LOG_VERBOSE, "textsub2video_filter_frame num_subtitle_rects: %d, start_time_ms: %"PRId64"\n", frame->num_subtitle_areas, start_time);
+
+    if (!s->got_header && frame->num_subtitle_areas > 0)
+        process_header(ctx, frame);
+
+    if (frame->repeat_sub)
+        goto exit;
+
+    ff_mutex_lock(&s->mutex);
+
+    if (s->render_latest_only && s->track->n_events > 0) {
+        const int64_t previous_start_time = s->track->events[s->track->n_events - 1].Start;
+        const int64_t diff = start_time - previous_start_time;
+        for (int i = s->track->n_events - 1; i >= 0; i--) {
+            if (previous_start_time != s->track->events[i].Start)
+                break;
+
+            if (s->track->events[i].Duration > diff)
+                s->track->events[i].Duration = diff;
+
+        }
+    }
+
+    for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+        char *ass_line = frame->subtitle_areas[i]->ass;
+        if (!ass_line)
+            continue;
+
+        ass_process_chunk(s->track, ass_line, strlen(ass_line), start_time, duration);
+    }
+
+
+    ff_mutex_unlock(&s->mutex);
+
+exit:
+    av_frame_free(&frame);
+
+    if (s->need_frame) {
+        s->need_frame = 0;
+        return textsub2video_request_frame(ctx->outputs[0]);
+    }
+
+    return 0;
+}
+
+#define OFFSET(x) offsetof(TextSubsContext, x)
+#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption overlaytextsubs_options[] = {
+    {"alpha",              "enable processing of alpha channel", OFFSET(alpha),              AV_OPT_TYPE_BOOL,   {.i64 = 0   }, 0,         1,        .flags =  FLAGS},
+    {"font_size",          "default font size",                  OFFSET(font_size),          AV_OPT_TYPE_DOUBLE, {.dbl = 18.0}, 0.0,       100.0,    .flags =  FLAGS},
+    {"force_style",        "force subtitle style",               OFFSET(force_style),        AV_OPT_TYPE_STRING, {.str = NULL}, 0,         0,        .flags =  FLAGS},
+    {"margin",             "default margin",                     OFFSET(margin),             AV_OPT_TYPE_INT,    {.i64 = 20  }, 0,         INT_MAX,  .flags =  FLAGS},
+    {"default_font_path",  "path to default font",               OFFSET(default_font_path),  AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"fonts_dir",          "directory to scan for fonts",        OFFSET(fonts_dir),          AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"fontsdir",           "directory to scan for fonts",        OFFSET(fonts_dir),          AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"fontconfig_file",    "fontconfig file to load",            OFFSET(fc_file),            AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"language",           "default language",                   OFFSET(language),           AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"render_latest_only", "newest sub event for each time",     OFFSET(render_latest_only), AV_OPT_TYPE_BOOL,   {.i64 = 0   }, 0,         1,        .flags =  FLAGS},
+    { .name = NULL }
+};
+
+static const AVOption textsub2video_options[] = {
+    {"rate",               "set frame rate",                   OFFSET(frame_rate),         AV_OPT_TYPE_VIDEO_RATE, {.str="8"},   0,         INT_MAX,   .flags =  FLAGS},
+    {"r",                  "set frame rate",                   OFFSET(frame_rate),         AV_OPT_TYPE_VIDEO_RATE, {.str="8"},   0,         INT_MAX,   .flags =  FLAGS},
+    {"size",               "set video size",                   OFFSET(out_w),              AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0,         0,        .flags =  FLAGS},
+    {"s",                  "set video size",                   OFFSET(out_w),              AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0,         0,        .flags =  FLAGS},
+    {"font_size",          "default font size",                OFFSET(font_size),          AV_OPT_TYPE_DOUBLE,     {.dbl = 18.0}, 0.0,       100.0,    .flags =  FLAGS},
+    {"force_style",        "force subtitle style",             OFFSET(force_style),        AV_OPT_TYPE_STRING,     {.str = NULL}, 0,         0,        .flags =  FLAGS},
+    {"margin",             "default margin",                   OFFSET(margin),             AV_OPT_TYPE_INT,        {.i64 = 20  }, 0,         INT_MAX,  .flags =  FLAGS},
+    {"default_font_path",  "path to default font",             OFFSET(default_font_path),  AV_OPT_TYPE_STRING,     {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"fonts_dir",          "directory to scan for fonts",      OFFSET(fonts_dir),          AV_OPT_TYPE_STRING,     {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"fontsdir",           "directory to scan for fonts",      OFFSET(fonts_dir),          AV_OPT_TYPE_STRING,     {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"fontconfig_file",    "fontconfig file to load",          OFFSET(fc_file),            AV_OPT_TYPE_STRING,     {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"language",           "default language",                 OFFSET(language),           AV_OPT_TYPE_STRING,     {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"render_latest_only", "newest sub event for each time",   OFFSET(render_latest_only), AV_OPT_TYPE_BOOL,       {.i64 = 0   }, 0,         1,        .flags =  FLAGS},
+    { .name = NULL }
+};
+
+#if CONFIG_OVERLAYTEXTSUBS_FILTER
+
+AVFILTER_DEFINE_CLASS(overlaytextsubs);
+
+static const AVFilterPad overlaytextsubs_inputs[] = {
+    {
+        .name         = "main",
+        .type         = AVMEDIA_TYPE_VIDEO,
+        .config_props = config_input_main,
+        .flags        = AVFILTERPAD_FLAG_NEEDS_WRITABLE,
+        .filter_frame = filter_video_frame,
+    },
+    {
+        .name         = "overlay",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .filter_frame = filter_subtitle_frame,
+    },
+};
+
+static const AVFilterPad overlaytextsubs_outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_VIDEO,
+        .config_props  = config_output,
+    },
+};
+
+const AVFilter ff_vf_overlaytextsubs = {
+    .name          = "overlaytextsubs",
+    .description   = NULL_IF_CONFIG_SMALL("Overlay textual subtitles on top of the input."),
+    .init          = init,
+    .uninit        = uninit,
+    .priv_size     = sizeof(TextSubsContext),
+    .priv_class    = &overlaytextsubs_class,
+    FILTER_INPUTS(overlaytextsubs_inputs),
+    FILTER_OUTPUTS(overlaytextsubs_outputs),
+    FILTER_QUERY_FUNC(overlay_textsubs_query_formats),
+};
+#endif
+
+#if CONFIG_TEXTSUB2VIDEO_FILTER
+
+AVFILTER_DEFINE_CLASS(textsub2video);
+
+static const AVFilterPad textsub2video_inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .config_props = textsub2video_config_input,
+        .filter_frame = textsub2video_filter_frame,
+    },
+};
+
+static const AVFilterPad textsub2video_outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_VIDEO,
+        .config_props  = textsub2video_config_output,
+        .request_frame = textsub2video_request_frame,
+    },
+};
+
+const AVFilter ff_svf_textsub2video = {
+    .name          = "textsub2video",
+    .description   = NULL_IF_CONFIG_SMALL("Convert textual subtitles to video frames"),
+    .init          = init,
+    .uninit        = uninit,
+    .priv_size     = sizeof(TextSubsContext),
+    .priv_class    = &textsub2video_class,
+    FILTER_INPUTS(textsub2video_inputs),
+    FILTER_OUTPUTS(textsub2video_outputs),
+    FILTER_QUERY_FUNC(textsub2video_query_formats),
+};
+#endif
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v3 14/26] avfilter/textmod: Add textmod, censor and show_speaker filters
  2022-01-20  3:25   ` [FFmpeg-devel] [PATCH v3 00/26] Subtitle Filtering 2022 ffmpegagent
                       ` (12 preceding siblings ...)
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 13/26] avfilter/overlaytextsubs: Add overlaytextsubs and textsubs2video filters ffmpegagent
@ 2022-01-20  3:25     ` ffmpegagent
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 15/26] avfilter/stripstyles: Add stripstyles filter ffmpegagent
                       ` (12 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  3:25 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

- textmod {S -> S)
  Modify subtitle text in a number of ways

- censor {S -> S)
  Censor subtitles using a word list

- show_speaker {S -> S)
  Prepend speaker names from ASS subtitles to the visible text lines

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 doc/filters.texi         | 206 ++++++++++++
 libavfilter/Makefile     |   5 +
 libavfilter/allfilters.c |   3 +
 libavfilter/sf_textmod.c | 710 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 924 insertions(+)
 create mode 100644 libavfilter/sf_textmod.c

diff --git a/doc/filters.texi b/doc/filters.texi
index 02a854851c..6559f9d101 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -25772,6 +25772,145 @@ existing filters using @code{--disable-filters}.
 
 Below is a description of the currently available subtitle filters.
 
+
+@section censor
+
+Censor selected words in text subtitles.
+
+Inputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+@item mode
+The censoring mode to apply.
+
+Supported censoring modes are:
+
+@table @var
+@item 0, keep_first_last
+Replace all characters with the 'censor_char' except the first and the last character of a word.
+For words with less than 4 characters, the last character will be replaced as well.
+For words with less than 3 characters, the first character will be replaced as well.
+@item 1, keep_first
+Replace all characters with the 'censor_char' except the first character of a word.
+For words with less than 3 characters, the first character will be replaced as well.
+@item 2, all
+Replace all characters with the 'censor_char'.
+@end table
+
+@item words
+A list of words to censor, separated by 'separator'.
+
+@item words_file
+Specify a file from which to load the contents for the 'words' parameter.
+
+@item censor_char
+Single character used as replacement for censoring.
+
+@item separator
+Delimiter character for words. Used with replace_words and remove_words- Must be a single character.
+The default is '.'.
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Censor a few given words with a pound character.
+@example
+ffmpeg -i "http://streams.videolan.org/samples/sub/SSA/subtitle_testing_complex.mkv" -filter_complex "[0:1]censor=words='diss,louder,hope,beam,word':censor_char='#'" -map 0 -y output.mkv
+@end example
+@end itemize
+
+
+@section textmod
+
+Modify subtitle text in a number of ways.
+
+Inputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+@item mode
+The kind of text modification to apply
+
+Supported operation modes are:
+
+@table @var
+@item 0, leet
+Convert subtitle text to 'leet speak'. It's primarily useful for testing as the modification will be visible with almost all text lines.
+@item 1, to_upper
+Change all text to upper case. Might improve readability.
+@item 2, to_lower
+Change all text to lower case.
+@item 3, replace_chars
+Replace one or more characters. Requires the find and replace parameters to be specified.
+Both need to be equal in length.
+The first char in find is replaced by the first char in replace, same for all subsequent chars.
+@item 4, remove_chars
+Remove certain characters. Requires the find parameter to be specified.
+All chars in the find parameter string will be removed from all subtitle text.
+@item 5, replace_words
+Replace one or more words. Requires the find and replace parameters to be specified. Multiple words must be separated by the delimiter char specified vie the separator parameter (default: ',').
+The number of words in the find and replace parameters needs to be equal.
+The first word in find is replaced by the first word in replace, same for all subsequent words
+@item 6, remove_words
+Remove certain words. Requires the find parameter to be specified. Multiple words must be separated by the delimiter char specified vie the separator parameter (default: ',').
+All words in the find parameter string will be removed from all subtitle text.
+@end table
+
+@item find
+Required for replace_chars, remove_chars, replace_words and remove_words.
+
+@item find_file
+Specify a file from which to load the contents for the 'find' parameter.
+
+@item replace
+Required for replace_chars and replace_words.
+
+@item replace_file
+Specify a file from which to load the contents for the 'replace' parameter.
+
+@item separator
+Delimiter character for words. Used with replace_words and remove_words- Must be a single character.
+The default is '.'.
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Change all characters to upper case while keeping all styles and animations:
+@example
+ffmpeg -i "https://streams.videolan.org/ffmpeg/mkv_subtitles.mkv" -filter_complex "[0:s]textmod=mode=to_upper" -map 0 -y out.mkv
+@end example
+@item
+Remove a set of symbol characters for am improved and smoother visual apperance:
+@example
+ffmpeg -i "https://streams.videolan.org/ffmpeg/mkv_subtitles.mkv" -filter_complex "[0:s]textmod=mode=remove_chars:find='$&#@*§'" -map 0 -y out.mkv
+@end example
+@end itemize
+
 @section graphicsub2video
 
 Renders graphic subtitles as video frames.
@@ -25939,6 +26078,73 @@ ffmpeg -i "http://streams.videolan.org/samples/sub/SSA/subtitle_testing_complex.
 @end example
 @end itemize
 
+@section showspeaker
+
+Prepend speaker names to subtitle lines (when available).
+
+Subtitles in ASS/SSA format are often including the names of the persons
+or character for each subtitle line. The showspeaker filter adds those names
+to the actual subtitle text to make it visible on playback.
+
+Inputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+@item format
+The format for prepending speaker names. Default is 'square_brackets'.
+
+Supported operation modes are:
+
+@table @var
+@item 0, square_brackets
+Enclose the speaker name in square brackets, followed by space ('[speaker] text').
+@item 1, round_brackets
+Enclose the speaker name in round brackets, followed by space ('(speaker) text').
+@item 2, colon
+Separate the speaker name with a colon and space ('speaker: text').
+@item 3, plain
+Separate the speaker name with a space only ('speaker text').
+@end table
+
+@item line_break
+Set thís parameter to insert a line break between speaker name and text instead of the space character.
+
+@item style
+Allows to set a specific style for the speaker name text.
+
+This can be either a named style that exists in the ass subtitle header script (e.g. 'Default') or an ass style override code.
+Example:  @{\\c&HDD0000&\\be1\\i1\\bord10@}
+This sets the color to blue, enables edge blurring, italic font and a border of size 10.
+
+The behavior is as follows:
+
+- When the style parameter is not provided, the filter will find the first position in the event string that is actual text.
+  The speaker name will be inserted at this position. This allows to have the speaker name shown in the same style like the
+  regular text, in case the string would start with a sequence of style codes.
+- When the style parameter is provided, everything will be prepended to the original text:
+  Style Code or Style name >> Speaker Name >> Style Reset Code >> Original Text
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Prepend speaker names with blue text, smooth edges and blend/overlay that onto the video.
+@example
+ffmpeg -i INPUT -filter_complex "showspeaker=format=colon:style='@{\\c&HDD0000&\\be1@}',[0:v]overlay_textsubs"
+@end example
+@end itemize
+
 @section textsub2video
 
 Converts text subtitles to video frames.
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index a3f8a07131..4579b7c8c0 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -562,6 +562,11 @@ OBJS-$(CONFIG_YUVTESTSRC_FILTER)             += vsrc_testsrc.o
 
 OBJS-$(CONFIG_NULLSINK_FILTER)               += vsink_nullsink.o
 
+# subtitle filters
+OBJS-$(CONFIG_CENSOR_FILTER)                 += sf_textmod.o
+OBJS-$(CONFIG_SHOW_SPEAKER_FILTER)           += sf_textmod.o
+OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
+
 # multimedia filters
 OBJS-$(CONFIG_ABITSCOPE_FILTER)              += avf_abitscope.o
 OBJS-$(CONFIG_ADRAWGRAPH_FILTER)             += f_drawgraph.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 5f068a152d..9c489fdc66 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -548,6 +548,9 @@ 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_censor;
+extern const AVFilter ff_sf_showspeaker;
+extern const AVFilter ff_sf_textmod;
 extern const AVFilter ff_svf_graphicsub2video;
 extern const AVFilter ff_svf_textsub2video;
 
diff --git a/libavfilter/sf_textmod.c b/libavfilter/sf_textmod.c
new file mode 100644
index 0000000000..c75244cfb2
--- /dev/null
+++ b/libavfilter/sf_textmod.c
@@ -0,0 +1,710 @@
+/*
+ * 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 allows to modify subtitle text in several ways
+ */
+
+#include <libavutil/ass_internal.h>
+
+#include "libavutil/opt.h"
+#include "internal.h"
+#include "libavutil/ass_split_internal.h"
+#include "libavutil/bprint.h"
+#include "libavutil/file.h"
+
+static const char* leet_src = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+static const char* leet_dst = "abcd3f6#1jklmn0pq257uvwxyzAB(D3F6#1JKLMN0PQ257UVWXYZ";
+
+enum TextModFilterType {
+    TM_TEXTMOD,
+    TM_CENSOR,
+    TM_SHOW_SPEAKER,
+};
+
+enum TextModOperation {
+    OP_LEET,
+    OP_TO_UPPER,
+    OP_TO_LOWER,
+    OP_REPLACE_CHARS,
+    OP_REMOVE_CHARS,
+    OP_REPLACE_WORDS,
+    OP_REMOVE_WORDS,
+    NB_OPS,
+};
+
+enum CensorMode {
+    CM_KEEP_FIRST_LAST,
+    CM_KEEP_FIRST,
+    CM_ALL,
+};
+
+enum ShowSpeakerMode {
+    SM_SQUARE_BRACKETS,
+    SM_ROUND_BRACKETS,
+    SM_COLON,
+    SM_PLAIN,
+};
+
+typedef struct TextModContext {
+    const AVClass *class;
+    enum AVSubtitleType format;
+    enum TextModFilterType filter_type;
+    enum TextModOperation operation;
+    enum CensorMode censor_mode;
+    enum ShowSpeakerMode speaker_mode;
+    char *find;
+    char *find_file;
+    char *style;
+    char *replace;
+    char *replace_file;
+    char *separator;
+    char *censor_char;
+    char **find_list;
+    int  line_break;
+    int  nb_find_list;
+    char **replace_list;
+    int  nb_replace_list;
+} TextModContext;
+
+static char **split_string(char *source, int *nb_elems, const char *delim)
+{
+    char **list = NULL;
+    char *temp = NULL;
+    char *ptr = av_strtok(source, delim, &temp);
+
+    while (ptr) {
+        if (strlen(ptr)) {
+            av_dynarray_add(&list, nb_elems, ptr);
+            if (!list)
+                return NULL;
+        }
+
+        ptr = av_strtok(NULL, delim, &temp);
+    }
+
+    if (!list)
+        return NULL;
+
+    for (int i = 0; i < *nb_elems; i++) {
+        list[i] = av_strdup(list[i]);
+        if (!list[i]) {
+            for (int n = 0; n < i; n++)
+                av_free(list[n]);
+            av_free(list);
+            return NULL;
+        }
+    }
+
+    return list;
+}
+
+static int load_text_from_file(AVFilterContext *ctx, const char *file_name, char **text, char separator)
+{
+    int err;
+    uint8_t *textbuf;
+    char *tmp;
+    size_t textbuf_size;
+    int offset = 0;
+
+    if ((err = av_file_map(file_name, &textbuf, &textbuf_size, 0, ctx)) < 0) {
+        av_log(ctx, AV_LOG_ERROR, "The text file '%s' could not be read or is empty\n", file_name);
+        return err;
+    }
+
+    if (textbuf_size > 1 &&
+        (textbuf[0] == 0xFF && textbuf[1] == 0xFE
+        || textbuf[0] == 0xFE && textbuf[1] == 0xFF)) {
+        av_log(ctx, AV_LOG_ERROR, "UTF text files are not supported. File: %s\n", file_name);
+        return AVERROR(EINVAL);
+    }
+
+    if (textbuf_size > 2 && textbuf[0] == 0xEF && textbuf[1] == 0xBB && textbuf[2] == 0xBF)
+        offset = 3; // UTF-8
+
+    if (textbuf_size > SIZE_MAX - 1 || !((tmp = av_strndup((char *)textbuf + offset, textbuf_size - offset)))) {
+        av_file_unmap(textbuf, textbuf_size);
+        return AVERROR(ENOMEM);
+    }
+
+    av_file_unmap(textbuf, textbuf_size);
+
+    for (size_t i = 0; i < strlen(tmp); i++) {
+        switch (tmp[i]) {
+        case '\n':
+        case '\r':
+        case '\f':
+        case '\v':
+            tmp[i] = separator;
+        }
+    }
+
+    *text = tmp;
+
+    return 0;
+}
+
+static int load_files(AVFilterContext *ctx)
+{
+    TextModContext *s = ctx->priv;
+    int ret;
+
+    if (!s->separator || strlen(s->separator) != 1) {
+        av_log(ctx, AV_LOG_ERROR, "A single character needs to be specified for the separator parameter.\n");
+        return AVERROR(EINVAL);
+    }
+
+    if (s->find_file && strlen(s->find_file)) {
+        ret = load_text_from_file(ctx, s->find_file, &s->find, s->separator[0]);
+        if (ret < 0 )
+            return ret;
+    }
+
+    if (s->replace_file && strlen(s->replace_file)) {
+        ret = load_text_from_file(ctx, s->replace_file, &s->replace, s->separator[0]);
+        if (ret < 0 )
+            return ret;
+    }
+
+    return 0;
+}
+
+static int init_censor(AVFilterContext *ctx)
+{
+    TextModContext *s = ctx->priv;
+    int ret;
+
+    s->filter_type = TM_CENSOR;
+    s->operation = OP_REPLACE_WORDS;
+
+    ret = load_files(ctx);
+    if (ret < 0 )
+        return ret;
+
+    if (!s->find || !strlen(s->find)) {
+        av_log(ctx, AV_LOG_ERROR, "Either the 'words' or the 'words_file' parameter needs to be specified\n");
+        return AVERROR(EINVAL);
+    }
+
+    if (!s->censor_char || strlen(s->censor_char) != 1) {
+        av_log(ctx, AV_LOG_ERROR, "A single character needs to be specified for the censor_char parameter\n");
+        return AVERROR(EINVAL);
+    }
+
+    s->find_list = split_string(s->find, &s->nb_find_list, s->separator);
+    if (!s->find_list)
+        return AVERROR(ENOMEM);
+
+    s->replace_list = av_calloc(s->nb_find_list, sizeof(char *));
+    if (!s->replace_list)
+        return AVERROR(ENOMEM);
+
+    for (int i = 0; i < s->nb_find_list; i++) {
+        size_t len, start = 0, end;
+        char *item = av_strdup(s->find_list[i]);
+        if (!item)
+            return AVERROR(ENOMEM);
+
+        len = end = strlen(item);
+
+        switch (s->censor_mode) {
+        case CM_KEEP_FIRST_LAST:
+
+            if (len > 2)
+                start = 1;
+            if (len > 3)
+                end--;
+
+            break;
+        case CM_KEEP_FIRST:
+
+            if (len > 2)
+                start = 1;
+
+            break;
+        }
+
+        for (size_t n = start; n < end; n++)
+            item[n] = s->censor_char[0];
+
+        s->replace_list[i] = item;
+    }
+
+    return 0;
+}
+
+static int init_showspeaker(AVFilterContext *ctx)
+{
+    TextModContext *s = ctx->priv;
+    s->filter_type = TM_SHOW_SPEAKER;
+
+    return 0;
+}
+
+static int init(AVFilterContext *ctx)
+{
+    TextModContext *s = ctx->priv;
+    int ret;
+
+    ret = load_files(ctx);
+    if (ret < 0 )
+        return ret;
+
+    switch (s->operation) {
+    case OP_REPLACE_CHARS:
+    case OP_REMOVE_CHARS:
+    case OP_REPLACE_WORDS:
+    case OP_REMOVE_WORDS:
+        if (!s->find || !strlen(s->find)) {
+            av_log(ctx, AV_LOG_ERROR, "Selected mode requires the 'find' parameter to be specified\n");
+            return AVERROR(EINVAL);
+        }
+        break;
+    }
+
+    switch (s->operation) {
+    case OP_REPLACE_CHARS:
+    case OP_REPLACE_WORDS:
+        if (!s->replace || !strlen(s->replace)) {
+            av_log(ctx, AV_LOG_ERROR, "Selected mode requires the 'replace' parameter to be specified\n");
+            return AVERROR(EINVAL);
+        }
+        break;
+    }
+
+    if (s->operation == OP_REPLACE_CHARS && strlen(s->find) != strlen(s->replace)) {
+        av_log(ctx, AV_LOG_ERROR, "Selected mode requires the 'find' and 'replace' parameters to have the same length\n");
+        return AVERROR(EINVAL);
+    }
+
+    if (s->operation == OP_REPLACE_WORDS || s->operation == OP_REMOVE_WORDS) {
+        if (!s->separator || strlen(s->separator) != 1) {
+            av_log(ctx, AV_LOG_ERROR, "Selected mode requires a single separator char to be specified\n");
+            return AVERROR(EINVAL);
+        }
+
+        s->find_list = split_string(s->find, &s->nb_find_list, s->separator);
+        if (!s->find_list)
+            return AVERROR(ENOMEM);
+
+        if (s->operation == OP_REPLACE_WORDS) {
+
+            s->replace_list = split_string(s->replace, &s->nb_replace_list, s->separator);
+            if (!s->replace_list)
+                return AVERROR(ENOMEM);
+
+            if (s->nb_find_list != s->nb_replace_list) {
+                av_log(ctx, AV_LOG_ERROR, "The number of words in 'find' and 'replace' needs to be equal\n");
+                return AVERROR(EINVAL);
+            }
+        }
+    }
+
+    return 0;
+}
+
+static void uninit(AVFilterContext *ctx)
+{
+    TextModContext *s = ctx->priv;
+
+    for (int i = 0; i < s->nb_find_list; i++)
+        av_freep(&s->find_list[i]);
+
+    s->nb_find_list = 0;
+    av_freep(&s->find_list);
+
+    for (int i = 0; i < s->nb_replace_list; i++)
+        av_freep(&s->replace_list[i]);
+
+    s->nb_replace_list = 0;
+    av_freep(&s->replace_list);
+}
+
+static char *process_text(TextModContext *s, char *text)
+{
+    const char *char_src = s->find;
+    const char *char_dst = s->replace;
+    char *result         = NULL;
+    int escape_level     = 0, k = 0;
+
+    switch (s->operation) {
+    case OP_LEET:
+    case OP_REPLACE_CHARS:
+
+        if (s->operation == OP_LEET) {
+            char_src = leet_src;
+            char_dst = leet_dst;
+        }
+
+        result = av_strdup(text);
+        if (!result)
+            return NULL;
+
+        for (size_t n = 0; n < strlen(result); n++) {
+            if (result[n] == '{')
+                escape_level++;
+
+            if (!escape_level) {
+                size_t len = strlen(char_src);
+                for (size_t t = 0; t < len; t++) {
+                    if (result[n] == char_src[t]) {
+                        result[n] = char_dst[t];
+                        break;
+                    }
+                }
+            }
+
+            if (result[n] == '}')
+                escape_level--;
+        }
+
+        break;
+    case OP_TO_UPPER:
+    case OP_TO_LOWER:
+
+        result = av_strdup(text);
+        if (!result)
+            return NULL;
+
+        for (size_t n = 0; n < strlen(result); n++) {
+            if (result[n] == '{')
+                escape_level++;
+            if (!escape_level)
+                result[n] = s->operation == OP_TO_LOWER ? av_tolower(result[n]) : av_toupper(result[n]);
+            if (result[n] == '}')
+                escape_level--;
+        }
+
+        break;
+    case OP_REMOVE_CHARS:
+
+        result = av_strdup(text);
+        if (!result)
+            return NULL;
+
+        for (size_t n = 0; n < strlen(result); n++) {
+            int skip_char = 0;
+
+            if (result[n] == '{')
+                escape_level++;
+
+            if (!escape_level) {
+                size_t len = strlen(char_src);
+                for (size_t t = 0; t < len; t++) {
+                    if (result[n] == char_src[t]) {
+                        skip_char = 1;
+                        break;
+                    }
+                }
+            }
+
+            if (!skip_char)
+                result[k++] = result[n];
+
+            if (result[n] == '}')
+                escape_level--;
+        }
+
+        result[k] = 0;
+
+        break;
+    case OP_REPLACE_WORDS:
+    case OP_REMOVE_WORDS:
+
+        result = av_strdup(text);
+        if (!result)
+            return NULL;
+
+        for (int n = 0; n < s->nb_find_list; n++) {
+            char *tmp           = result;
+            const char *replace = (s->operation == OP_REPLACE_WORDS) ? s->replace_list[n] : "";
+
+            result = av_strireplace(result, s->find_list[n], replace);
+            if (!result)
+                return NULL;
+
+            av_free(tmp);
+        }
+
+        break;
+    }
+
+    return result;
+}
+
+static char *process_dialog_show_speaker(TextModContext *s, char *ass_line)
+{
+    ASSDialog *dialog = avpriv_ass_split_dialog(NULL, ass_line);
+    int escape_level = 0;
+    unsigned pos = 0, len;
+    char *result, *text;
+    AVBPrint pbuf;
+
+    if (!dialog)
+        return NULL;
+
+    text = process_text(s, dialog->text);
+    if (!text)
+        return NULL;
+
+    if (!dialog->name || !strlen(dialog->name) || !dialog->text || !strlen(dialog->text))
+        return av_strdup(ass_line);
+
+    // Find insertion point in case the line starts with style codes
+    len = (unsigned)strlen(dialog->text);
+    for (unsigned i = 0; i < len; i++) {
+
+        if (dialog->text[i] == '{')
+            escape_level++;
+
+        if (dialog->text[i] == '}')
+            escape_level--;
+
+        if (escape_level == 0) {
+            pos = i;
+            break;
+        }
+    }
+
+    if (s->style && strlen(s->style))
+        // When a style is specified reset the insertion point
+        // (always add speaker plus style at the start in that case)
+        pos = 0;
+
+    if (pos >= len - 1)
+        return av_strdup(ass_line);
+
+    av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
+
+    if (pos > 0) {
+        av_bprint_append_data(&pbuf, dialog->text, pos);
+    }
+
+    if (s->style && strlen(s->style)) {
+        if (s->style[0] == '{')
+            // Assume complete and valid style code, e.g. {\c&HFF0000&}
+            av_bprintf(&pbuf, "%s", s->style);
+        else
+            // Otherwise it must be a style name
+            av_bprintf(&pbuf, "{\\r%s}", s->style);
+    }
+
+    switch (s->speaker_mode) {
+    case SM_SQUARE_BRACKETS:
+        av_bprintf(&pbuf, "[%s]", dialog->name);
+        break;
+    case SM_ROUND_BRACKETS:
+        av_bprintf(&pbuf, "(%s)", dialog->name);
+        break;
+    case SM_COLON:
+        av_bprintf(&pbuf, "%s:", dialog->name);
+        break;
+    case SM_PLAIN:
+        av_bprintf(&pbuf, "%s", dialog->name);
+        break;
+    }
+
+    if (s->style && strlen(s->style)) {
+        // Reset line style
+        if (dialog->style && strlen(dialog->style) && !av_strcasecmp(dialog->style, "default"))
+            av_bprintf(&pbuf, "{\\r%s}", dialog->style);
+        else
+            av_bprintf(&pbuf, "{\\r}");
+    }
+
+    if (s->line_break)
+        av_bprintf(&pbuf, "\\N");
+    else
+        av_bprintf(&pbuf, " ");
+
+    av_bprint_append_data(&pbuf, dialog->text + pos, len - pos);
+
+    av_bprint_finalize(&pbuf, &text);
+
+    result = avpriv_ass_get_dialog_ex(dialog->readorder, dialog->layer, dialog->style, dialog->name, dialog->margin_l, dialog->margin_r, dialog->margin_v, text);
+
+    av_free(text);
+    avpriv_ass_free_dialog(&dialog);
+    return result;
+}
+
+static char *process_dialog(TextModContext *s, char *ass_line)
+{
+    ASSDialog *dialog;
+    char *result, *text;
+
+    if (s->filter_type == TM_SHOW_SPEAKER)
+        return process_dialog_show_speaker(s, ass_line);
+
+    dialog = avpriv_ass_split_dialog(NULL, ass_line);
+    if (!dialog)
+        return NULL;
+
+    text = process_text(s, dialog->text);
+    if (!text)
+        return NULL;
+
+    result = avpriv_ass_get_dialog_ex(dialog->readorder, dialog->layer, dialog->style, dialog->name, dialog->margin_l, dialog->margin_r, dialog->margin_v, text);
+
+    av_free(text);
+    avpriv_ass_free_dialog(&dialog);
+    return result;
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    AVFilterLink *inlink = outlink->src->inputs[0];
+
+    outlink->w = inlink->w;
+    outlink->h = inlink->h;
+    outlink->time_base = inlink->time_base;
+    outlink->frame_rate = inlink->frame_rate;
+
+    return 0;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    TextModContext *s = inlink->dst->priv;
+    AVFilterLink *outlink = inlink->dst->outputs[0];
+    int ret;
+
+    outlink->format = inlink->format;
+
+    ret = av_frame_make_writable(frame);
+    if (ret < 0)
+        return ret;
+
+    for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+
+        AVSubtitleArea *area = frame->subtitle_areas[i];
+
+        if (area->ass) {
+            char *tmp = area->ass;
+            area->ass = process_dialog(s, area->ass);
+            av_free(tmp);
+            if (!area->ass)
+                return AVERROR(ENOMEM);
+        }
+    }
+
+    return ff_filter_frame(outlink, frame);
+}
+
+#define OFFSET(x) offsetof(TextModContext, x)
+#define FLAGS (AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption textmod_options[] = {
+    { "mode",             "set operation mode",              OFFSET(operation),    AV_OPT_TYPE_INT,    {.i64=OP_LEET},          OP_LEET, NB_OPS-1, FLAGS, "mode" },
+    {   "leet",           "convert text to 'leet speak'",    0,                    AV_OPT_TYPE_CONST,  {.i64=OP_LEET},          0,       0,        FLAGS, "mode" },
+    {   "to_upper",       "change to upper case",            0,                    AV_OPT_TYPE_CONST,  {.i64=OP_TO_UPPER},      0,       0,        FLAGS, "mode" },
+    {   "to_lower",       "change to lower case",            0,                    AV_OPT_TYPE_CONST,  {.i64=OP_TO_LOWER},      0,       0,        FLAGS, "mode" },
+    {   "replace_chars",  "replace characters",              0,                    AV_OPT_TYPE_CONST,  {.i64=OP_REPLACE_CHARS}, 0,       0,        FLAGS, "mode" },
+    {   "remove_chars",   "remove characters",               0,                    AV_OPT_TYPE_CONST,  {.i64=OP_REMOVE_CHARS},  0,       0,        FLAGS, "mode" },
+    {   "replace_words",  "replace words",                   0,                    AV_OPT_TYPE_CONST,  {.i64=OP_REPLACE_WORDS}, 0,       0,        FLAGS, "mode" },
+    {   "remove_words",   "remove words",                    0,                    AV_OPT_TYPE_CONST,  {.i64=OP_REMOVE_WORDS},  0,       0,        FLAGS, "mode" },
+    { "find",             "chars/words to find or remove",   OFFSET(find),         AV_OPT_TYPE_STRING, {.str = NULL},           0,       0,        FLAGS, NULL   },
+    { "find_file",        "load find param from file",       OFFSET(find_file),    AV_OPT_TYPE_STRING, {.str = NULL},           0,       0,        FLAGS, NULL   },
+    { "replace",          "chars/words to replace",          OFFSET(replace),      AV_OPT_TYPE_STRING, {.str = NULL},           0,       0,        FLAGS, NULL   },
+    { "replace_file",     "load replace param from file",    OFFSET(replace_file), AV_OPT_TYPE_STRING, {.str = NULL},           0,       0,        FLAGS, NULL   },
+    { "separator",        "word separator",                  OFFSET(separator),    AV_OPT_TYPE_STRING, {.str = ","},            0,       0,        FLAGS, NULL   },
+    { NULL },
+};
+
+
+static const AVOption censor_options[] = {
+    { "mode",               "set censoring mode",        OFFSET(censor_mode), AV_OPT_TYPE_INT,    {.i64=CM_KEEP_FIRST_LAST}, 0, 2, FLAGS, "mode" },
+    {   "keep_first_last",  "censor inner chars",        0,                   AV_OPT_TYPE_CONST,  {.i64=CM_KEEP_FIRST_LAST}, 0, 0, FLAGS, "mode" },
+    {   "keep_first",       "censor all but first char", 0,                   AV_OPT_TYPE_CONST,  {.i64=CM_KEEP_FIRST},      0, 0, FLAGS, "mode" },
+    {   "all",              "censor all chars",          0,                   AV_OPT_TYPE_CONST,  {.i64=CM_ALL},             0, 0, FLAGS, "mode" },
+    { "words",              "list of words to censor",   OFFSET(find),        AV_OPT_TYPE_STRING, {.str = NULL},             0, 0, FLAGS, NULL   },
+    { "words_file",         "path to word list file",    OFFSET(find_file),   AV_OPT_TYPE_STRING, {.str = NULL},             0, 0, FLAGS, NULL   },
+    { "separator",          "word separator",            OFFSET(separator),   AV_OPT_TYPE_STRING, {.str = ","},              0, 0, FLAGS, NULL   },
+    { "censor_char",        "replacement character",     OFFSET(censor_char), AV_OPT_TYPE_STRING, {.str = "*"},              0, 0, FLAGS, NULL   },
+    { NULL },
+};
+
+static const AVOption showspeaker_options[] = {
+    { "format",             "speaker name formatting",        OFFSET(speaker_mode), AV_OPT_TYPE_INT,    {.i64=SM_SQUARE_BRACKETS}, 0, 2, FLAGS, "format" },
+    {   "square_brackets",  "[speaker] text",                 0,                    AV_OPT_TYPE_CONST,  {.i64=SM_SQUARE_BRACKETS}, 0, 0, FLAGS, "format" },
+    {   "round_brackets",   "(speaker) text",                 0,                    AV_OPT_TYPE_CONST,  {.i64=SM_ROUND_BRACKETS},  0, 0, FLAGS, "format" },
+    {   "colon",            "speaker: text",                  0,                    AV_OPT_TYPE_CONST,  {.i64=SM_COLON},           0, 0, FLAGS, "format" },
+    {   "plain",            "speaker text",                   0,                    AV_OPT_TYPE_CONST,  {.i64=SM_PLAIN},           0, 0, FLAGS, "format" },
+    { "line_break",         "insert line break",              OFFSET(line_break),   AV_OPT_TYPE_BOOL,   {.i64=0},                  0, 1, FLAGS, NULL     },
+    { "style",              "ass type name or style code",    OFFSET(style),        AV_OPT_TYPE_STRING, {.str = NULL},             0, 0, FLAGS, NULL     },
+    { NULL },
+};
+
+AVFILTER_DEFINE_CLASS(textmod);
+AVFILTER_DEFINE_CLASS(censor);
+AVFILTER_DEFINE_CLASS(showspeaker);
+
+static const AVFilterPad inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .filter_frame = filter_frame,
+    },
+};
+
+static const AVFilterPad outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_SUBTITLE,
+        .config_props  = config_output,
+    },
+};
+
+const AVFilter ff_sf_textmod = {
+    .name          = "textmod",
+    .description   = NULL_IF_CONFIG_SMALL("Modify subtitle text in several ways"),
+    .init          = init,
+    .uninit        = uninit,
+    .priv_size     = sizeof(TextModContext),
+    .priv_class    = &textmod_class,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_SINGLE_SUBFMT(AV_SUBTITLE_FMT_ASS),
+};
+
+const AVFilter ff_sf_censor = {
+    .name          = "censor",
+    .description   = NULL_IF_CONFIG_SMALL("Censor words in subtitle text"),
+    .init          = init_censor,
+    .uninit        = uninit,
+    .priv_size     = sizeof(TextModContext),
+    .priv_class    = &censor_class,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_SINGLE_SUBFMT(AV_SUBTITLE_FMT_ASS),
+};
+
+const AVFilter ff_sf_showspeaker = {
+    .name          = "showspeaker",
+    .description   = NULL_IF_CONFIG_SMALL("Prepend speaker names to text subtitles (when available)"),
+    .init          = init_showspeaker,
+    .uninit        = uninit,
+    .priv_size     = sizeof(TextModContext),
+    .priv_class    = &showspeaker_class,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_SINGLE_SUBFMT(AV_SUBTITLE_FMT_ASS),
+};
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v3 15/26] avfilter/stripstyles: Add stripstyles filter
  2022-01-20  3:25   ` [FFmpeg-devel] [PATCH v3 00/26] Subtitle Filtering 2022 ffmpegagent
                       ` (13 preceding siblings ...)
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 14/26] avfilter/textmod: Add textmod, censor and show_speaker filters ffmpegagent
@ 2022-01-20  3:25     ` ffmpegagent
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 16/26] avfilter/splitcc: Add splitcc filter for closed caption handling ffmpegagent
                       ` (11 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  3:25 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

- stripstyles {S -> S)
  Remove all inline styles from subtitle events

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 doc/filters.texi             |  37 ++++++
 libavfilter/Makefile         |   1 +
 libavfilter/allfilters.c     |   1 +
 libavfilter/sf_stripstyles.c | 211 +++++++++++++++++++++++++++++++++++
 4 files changed, 250 insertions(+)
 create mode 100644 libavfilter/sf_stripstyles.c

diff --git a/doc/filters.texi b/doc/filters.texi
index 6559f9d101..2f0e560ca0 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -25832,6 +25832,43 @@ ffmpeg -i "http://streams.videolan.org/samples/sub/SSA/subtitle_testing_complex.
 @end example
 @end itemize
 
+@section stripstyles
+
+Remove all inline styles from subtitle events.
+
+Inputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+@item remove_animated
+Also remove text which is subject to animation (default: true)
+Usually, animated text elements are used used in addition to static subtitle lines for creating effects, so in most cases it is safe to remove the animation content.
+If subtitle text is missing, try setting this to false.
+
+@item select_layer
+Process only ASS subtitle events from a specific layer. This allows to filter out certain effects where an ASS author duplicates the text onto multiple layers.
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Remove styles and animations from ASS subtitles and output events from ass layer 0 only. Then convert asn save as SRT stream:
+@example
+ffmpeg -i "https://streams.videolan.org/samples/sub/SSA/subtitle_testing_complex.mkv" -filter_complex "[0:1]stripstyles=select_layer=0" -map 0 -c:s srt output.mkv
+@end example
+@end itemize
+
 
 @section textmod
 
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 4579b7c8c0..9c512e3527 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -566,6 +566,7 @@ OBJS-$(CONFIG_NULLSINK_FILTER)               += vsink_nullsink.o
 OBJS-$(CONFIG_CENSOR_FILTER)                 += sf_textmod.o
 OBJS-$(CONFIG_SHOW_SPEAKER_FILTER)           += sf_textmod.o
 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 9c489fdc66..23f65533a1 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -550,6 +550,7 @@ extern const AVFilter ff_avf_showwavespic;
 extern const AVFilter ff_vaf_spectrumsynth;
 extern const AVFilter ff_sf_censor;
 extern const AVFilter ff_sf_showspeaker;
+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..bc3c5d1441
--- /dev/null
+++ b/libavfilter/sf_stripstyles.c
@@ -0,0 +1,211 @@
+/*
+ * 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 "libavutil/opt.h"
+#include "internal.h"
+#include "libavutil/ass_split_internal.h"
+#include "libavutil/bprint.h"
+
+typedef struct StripStylesContext {
+    const AVClass *class;
+    enum AVSubtitleType format;
+    int remove_animated;
+    int select_layer;
+} StripStylesContext;
+
+typedef struct DialogContext {
+    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;
+
+    av_log(s->ss_ctx, AV_LOG_DEBUG, "dialog_text_cb: %s\n", text);
+
+    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 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)
+{
+    DialogContext dlg_ctx = { .ss_ctx = s };
+    ASSDialog *dialog = avpriv_ass_split_dialog(NULL, ass_line);
+    char *result = NULL;
+
+    if (!dialog)
+        return NULL;
+
+    if (s->select_layer >= 0 && dialog->layer != s->select_layer) {
+        avpriv_ass_free_dialog(&dialog);
+        return NULL;
+    }
+
+    dlg_ctx.ss_ctx = s;
+
+    av_bprint_init(&dlg_ctx.buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
+
+    avpriv_ass_split_override_codes(&dialog_callbacks, &dlg_ctx, dialog->text);
+
+    if (av_bprint_is_complete(&dlg_ctx.buffer)
+        && dlg_ctx.buffer.len > 0)
+        result = ass_get_line(dialog->readorder, dialog->layer, dialog->style, dialog->name, dialog->effect, dlg_ctx.buffer.str);
+
+    av_bprint_finalize(&dlg_ctx.buffer, NULL);
+    avpriv_ass_free_dialog(&dialog);
+
+    return result;
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    AVFilterLink *inlink = outlink->src->inputs[0];
+
+    outlink->w = inlink->w;
+    outlink->h = inlink->h;
+    outlink->time_base = inlink->time_base;
+    outlink->frame_rate = inlink->frame_rate;
+
+    return 0;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    StripStylesContext *s = inlink->dst->priv;
+    AVFilterLink *outlink = inlink->dst->outputs[0];
+    int ret;
+
+    outlink->format = inlink->format;
+
+    ret = av_frame_make_writable(frame);
+    if (ret <0 ) {
+        av_frame_free(&frame);
+        return AVERROR(ENOMEM);
+    }
+
+    for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+
+        AVSubtitleArea *area = frame->subtitle_areas[i];
+
+        if (area->ass) {
+            char *tmp = area->ass;
+            area->ass = process_dialog(s, area->ass);
+
+            if (area->ass) {
+                av_log(inlink->dst, AV_LOG_INFO, "original: %d %s\n", i, tmp);
+                av_log(inlink->dst, AV_LOG_INFO, "stripped: %d %s\n", i, area->ass);
+            }
+            else
+                area->ass = NULL;
+
+            av_free(tmp);
+        }
+    }
+
+    return ff_filter_frame(outlink, frame);
+}
+
+#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 },
+    { "select_layer", "process a specific ass layer only",   OFFSET(remove_animated),  AV_OPT_TYPE_INT, {.i64 = -1 }, -1, INT_MAX, 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,
+        .config_props  = config_output,
+    },
+};
+
+const AVFilter ff_sf_stripstyles = {
+    .name          = "stripstyles",
+    .description   = NULL_IF_CONFIG_SMALL("Strip subtitle inline styles"),
+    .priv_size     = sizeof(StripStylesContext),
+    .priv_class    = &stripstyles_class,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_SINGLE_SUBFMT(AV_SUBTITLE_FMT_ASS),
+};
+
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v3 16/26] avfilter/splitcc: Add splitcc filter for closed caption handling
  2022-01-20  3:25   ` [FFmpeg-devel] [PATCH v3 00/26] Subtitle Filtering 2022 ffmpegagent
                       ` (14 preceding siblings ...)
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 15/26] avfilter/stripstyles: Add stripstyles filter ffmpegagent
@ 2022-01-20  3:25     ` ffmpegagent
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 17/26] avfilter/graphicsub2text: Add new graphicsub2text filter (OCR) ffmpegagent
                       ` (10 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  3:25 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

- splitcc {V -> VS)
  Extract closed-caption (A53) data from video
  frames as subtitle Frames

ffmpeg -y -loglevel verbose -i "https://streams.videolan.org/streams
/ts/CC/NewsStream-608-ac3.ts" -filter_complex "[0:v]splitcc[vid1],
textmod=mode=remove_chars:find='@',[vid1]overlay_textsubs" output.mkv

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 configure                |   1 +
 doc/filters.texi         |  63 +++++++
 libavfilter/Makefile     |   1 +
 libavfilter/allfilters.c |   1 +
 libavfilter/sf_splitcc.c | 395 +++++++++++++++++++++++++++++++++++++++
 5 files changed, 461 insertions(+)
 create mode 100644 libavfilter/sf_splitcc.c

diff --git a/configure b/configure
index 7b66e315f2..2a561b6f75 100755
--- a/configure
+++ b/configure
@@ -3730,6 +3730,7 @@ spp_filter_select="fft idctdsp fdctdsp me_cmp pixblockdsp"
 sr_filter_deps="avformat swscale"
 sr_filter_select="dnn"
 stereo3d_filter_deps="gpl"
+splitcc_filter_deps="avcodec"
 subtitles_filter_deps="avformat avcodec libass"
 super2xsai_filter_deps="gpl"
 pixfmts_super2xsai_test_deps="super2xsai_filter"
diff --git a/doc/filters.texi b/doc/filters.texi
index 2f0e560ca0..c7a11a0896 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -26182,6 +26182,69 @@ ffmpeg -i INPUT -filter_complex "showspeaker=format=colon:style='@{\\c&HDD0000&\
 @end example
 @end itemize
 
+
+@section splitcc
+
+Split-out closed-caption/A53 subtitles from video frame side data.
+
+This filter provides an input and an output for video frames, which are just passed through without modification.
+The second out provides subtitle frames which are extracted from video frame side data.
+
+Inputs:
+@itemize
+@item 0: Video [ALL]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Video (same as input)
+@item 1: Subtitles [TEXT]
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+
+@item use_cc_styles
+Emit closed caption style header.
+This will make closed captions appear in white font with a black rectangle background.
+
+@item real_time
+Emit subtitle events as they are decoded for real-time display.
+
+@item real_time_latency_msec
+Minimum elapsed time between emitting real-time subtitle events.
+Only applies to real_time mode.
+
+@item data_field
+Select data field. Possible values:
+
+@table @samp
+@item auto
+Pick first one that appears.
+@item first
+@item second
+@end table
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Extract closed captions as text subtitle stream and overlay it onto the video in cc style (black bar background):
+@example
+ffmpeg -i "https://streams.videolan.org/streams/ts/CC/NewsStream-608-ac3.ts" -filter_complex  "[0:v:0]splitcc=use_cc_styles=1[vid1][sub1];[vid1][sub1]overlaytextsubs" output.mkv
+@end example
+
+@item
+A nicer variant, using realtime output from cc_dec and rendering it with the render_latest_only parameter from overlaytextsubs to avoid ghosting by timely overlap.
+@example
+ffmpeg -i "https://streams.videolan.org/streams/ts/CC/NewsStream-608-ac3.ts" -filter_complex  "[0:v:0]splitcc=real_time=1:real_time_latency_msec=200[vid1][sub1];[vid1][sub1]overlaytextsubs=render_latest_only=1" output.mkv
+@end example
+@end itemize
+
+
 @section textsub2video
 
 Converts text subtitles to video frames.
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 9c512e3527..b2e715cf8a 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -566,6 +566,7 @@ OBJS-$(CONFIG_NULLSINK_FILTER)               += vsink_nullsink.o
 OBJS-$(CONFIG_CENSOR_FILTER)                 += sf_textmod.o
 OBJS-$(CONFIG_SHOW_SPEAKER_FILTER)           += sf_textmod.o
 OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
+OBJS-$(CONFIG_SPLITCC_FILTER)                += sf_splitcc.o
 OBJS-$(CONFIG_STRIPSTYLES_FILTER)            += sf_stripstyles.o
 
 # multimedia filters
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 23f65533a1..4996d3d27a 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -550,6 +550,7 @@ extern const AVFilter ff_avf_showwavespic;
 extern const AVFilter ff_vaf_spectrumsynth;
 extern const AVFilter ff_sf_censor;
 extern const AVFilter ff_sf_showspeaker;
+extern const AVFilter ff_sf_splitcc;
 extern const AVFilter ff_sf_stripstyles;
 extern const AVFilter ff_sf_textmod;
 extern const AVFilter ff_svf_graphicsub2video;
diff --git a/libavfilter/sf_splitcc.c b/libavfilter/sf_splitcc.c
new file mode 100644
index 0000000000..14235e822c
--- /dev/null
+++ b/libavfilter/sf_splitcc.c
@@ -0,0 +1,395 @@
+/*
+ * 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
+ * subtitle filter for splitting out closed-caption/A53 subtitles from video frame side data
+ */
+
+#include "filters.h"
+#include "libavutil/opt.h"
+#include "subtitles.h"
+#include "libavcodec/avcodec.h"
+
+static const AVRational ms_tb = {1, 1000};
+
+typedef struct SplitCaptionsContext {
+    const AVClass *class;
+    enum AVSubtitleType format;
+    AVCodecContext *cc_dec;
+    int eof;
+    AVFrame *next_sub_frame;
+    AVFrame *empty_sub_frame;
+    int new_frame;
+    int64_t next_repetition_pts;
+    int had_keyframe;
+    AVBufferRef *subtitle_header;
+    int use_cc_styles;
+    int real_time;
+    int real_time_latency_msec;
+    int data_field;
+    int scatter_realtime_output;
+} SplitCaptionsContext;
+
+static int64_t ms_to_avtb(int64_t ms)
+{
+    return av_rescale_q(ms, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q);
+}
+
+static int init(AVFilterContext *ctx)
+{
+    SplitCaptionsContext *s = ctx->priv;
+    AVDictionary *options = NULL;
+
+    int ret;
+    const AVCodec *codec = avcodec_find_decoder(AV_CODEC_ID_EIA_608);
+    if (!codec) {
+        av_log(ctx, AV_LOG_ERROR, "failed to find EIA-608/708 decoder\n");
+        return AVERROR_DECODER_NOT_FOUND;
+    }
+
+    if (!((s->cc_dec = avcodec_alloc_context3(codec)))) {
+        av_log(ctx, AV_LOG_ERROR, "failed to allocate EIA-608/708 decoder\n");
+        return AVERROR(ENOMEM);
+    }
+
+    av_dict_set_int(&options, "real_time", s->real_time, 0);
+    av_dict_set_int(&options, "real_time_latency_msec", s->real_time_latency_msec, 0);
+    av_dict_set_int(&options, "data_field", s->data_field, 0);
+
+    if ((ret = avcodec_open2(s->cc_dec, codec, &options)) < 0) {
+        av_log(ctx, AV_LOG_ERROR, "failed to open EIA-608/708 decoder: %i\n", ret);
+        return ret;
+    }
+
+    if (s->use_cc_styles && s->cc_dec->subtitle_header && s->cc_dec->subtitle_header[0] != 0) {
+        char* subtitle_header =  av_strdup((char *)s->cc_dec->subtitle_header);
+        if (!subtitle_header)
+            return AVERROR(ENOMEM);
+        s->subtitle_header = av_buffer_create((uint8_t *)subtitle_header, strlen(subtitle_header) + 1, NULL, NULL, 0);
+        if (!s->subtitle_header) {
+            av_free(subtitle_header);
+            return AVERROR(ENOMEM);
+        }
+    }
+
+    return 0;
+}
+
+static void uninit(AVFilterContext *ctx)
+{
+    SplitCaptionsContext *s = ctx->priv;
+    av_frame_free(&s->next_sub_frame);
+    av_frame_free(&s->empty_sub_frame);
+    av_buffer_unref(&s->subtitle_header);
+}
+
+static int config_input(AVFilterLink *link)
+{
+    const SplitCaptionsContext *context = link->dst->priv;
+
+    if (context->cc_dec)
+        context->cc_dec->pkt_timebase = link->time_base;
+
+    return 0;
+}
+
+static int query_formats(AVFilterContext *ctx)
+{
+    AVFilterFormats *formats;
+    AVFilterLink *inlink0 = ctx->inputs[0];
+    AVFilterLink *outlink0 = ctx->outputs[0];
+    AVFilterLink *outlink1 = ctx->outputs[1];
+    static const enum AVSubtitleType subtitle_fmts[] = { AV_SUBTITLE_FMT_ASS, AV_SUBTITLE_FMT_NONE };
+    int ret;
+
+    /* set input0 video formats */
+    formats = ff_all_formats(AVMEDIA_TYPE_VIDEO);
+    if ((ret = ff_formats_ref(formats, &inlink0->outcfg.formats)) < 0)
+        return ret;
+
+    /* set output0 video formats */
+    if ((ret = ff_formats_ref(formats, &outlink0->incfg.formats)) < 0)
+        return ret;
+
+    /* set output1 subtitle formats */
+    formats = ff_make_format_list(subtitle_fmts);
+    if ((ret = ff_formats_ref(formats, &outlink1->incfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int config_video_output(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    const AVFilterLink *inlink = outlink->src->inputs[0];
+
+    outlink->w = ctx->inputs[0]->w;
+    outlink->h = ctx->inputs[0]->h;
+    outlink->frame_rate = ctx->inputs[0]->frame_rate;
+    outlink->time_base = ctx->inputs[0]->time_base;
+    outlink->sample_aspect_ratio = ctx->inputs[0]->sample_aspect_ratio;
+
+    if (inlink->hw_frames_ctx)
+        outlink->hw_frames_ctx = av_buffer_ref(inlink->hw_frames_ctx);
+
+    return 0;
+}
+
+static int config_sub_output(AVFilterLink *outlink)
+{
+    SplitCaptionsContext *s = outlink->src->priv;
+    const AVFilterLink *inlink = outlink->src->inputs[0];
+
+    outlink->time_base = inlink->time_base;
+    outlink->format = AV_SUBTITLE_FMT_ASS;
+    outlink->frame_rate = (AVRational){1000, s->real_time_latency_msec};
+
+    return 0;
+}
+
+static int request_sub_frame(AVFilterLink *outlink)
+{
+    SplitCaptionsContext *s = outlink->src->priv;
+    int status;
+    int64_t pts;
+
+    if (!s->empty_sub_frame) {
+        s->empty_sub_frame = ff_get_subtitles_buffer(outlink, outlink->format);
+        if (!s->empty_sub_frame)
+            return AVERROR(ENOMEM);
+    }
+
+    if (!s->eof && ff_inlink_acknowledge_status(outlink->src->inputs[0], &status, &pts)) {
+        if (status == AVERROR_EOF)
+            s->eof = 1;
+    }
+
+    if (s->eof)
+        return AVERROR_EOF;
+
+    if (s->next_sub_frame) {
+
+        AVFrame *out = NULL;
+        s->next_sub_frame->pts++;
+
+        if (s->new_frame)
+            out = av_frame_clone(s->next_sub_frame);
+        else if (s->empty_sub_frame) {
+            s->empty_sub_frame->pts = s->next_sub_frame->pts;
+            out = av_frame_clone(s->empty_sub_frame);
+            av_frame_copy_props(out, s->next_sub_frame);
+            out->repeat_sub = 1;
+        }
+
+        if (!out)
+            return AVERROR(ENOMEM);
+
+        out->subtitle_timing.start_pts = av_rescale_q(s->next_sub_frame->pts, outlink->time_base, AV_TIME_BASE_Q);
+        s->new_frame = 0;
+
+        return ff_filter_frame(outlink, out);
+    }
+
+    return 0;
+}
+
+static int decode(AVCodecContext *avctx, AVFrame *frame, int *got_frame, AVPacket *pkt)
+{
+    int ret;
+
+    *got_frame = 0;
+
+    if (pkt) {
+        ret = avcodec_send_packet(avctx, pkt);
+        // In particular, we don't expect AVERROR(EAGAIN), because we read all
+        // decoded frames with avcodec_receive_frame() until done.
+        if (ret < 0 && ret != AVERROR_EOF)
+            return ret;
+    }
+
+    ret = avcodec_receive_frame(avctx, frame);
+    if (ret < 0 && ret != AVERROR(EAGAIN))
+        return ret;
+    if (ret >= 0)
+        *got_frame = 1;
+
+    return 0;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFrameSideData *sd;
+    SplitCaptionsContext *s = inlink->dst->priv;
+    AVFilterLink *outlink0 = inlink->dst->outputs[0];
+    AVFilterLink *outlink1 = inlink->dst->outputs[1];
+    AVPacket *pkt = NULL;
+    AVFrame *sub_out = NULL;
+
+    int ret;
+
+    outlink0->format = inlink->format;
+
+    sd = av_frame_get_side_data(frame, AV_FRAME_DATA_A53_CC);
+
+    if (sd && (s->had_keyframe || frame->key_frame)) {
+        int got_output = 0;
+
+        s->had_keyframe = 1;
+        pkt = av_packet_alloc();
+        pkt->buf = av_buffer_ref(sd->buf);
+        if (!pkt->buf) {
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
+
+        pkt->data = pkt->buf->data;
+        pkt->size = pkt->buf->size;
+        pkt->pts  = av_rescale_q(frame->pts, inlink->time_base, AV_TIME_BASE_Q);
+
+        sub_out = ff_get_subtitles_buffer(outlink1, AV_SUBTITLE_FMT_ASS);
+        if (!sub_out) {
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
+
+        if ((ret = av_buffer_replace(&sub_out->subtitle_header, s->subtitle_header)) < 0)
+            goto fail;
+
+        ret = decode(s->cc_dec, sub_out, &got_output, pkt);
+
+        ////if (got_output) {
+        ////    av_log(inlink->dst, AV_LOG_INFO, "CC Packet PTS: %"PRId64" got_output: %d  out_frame_pts: %"PRId64"  out_sub_pts: %"PRId64"\n", pkt->pts, got_output, sub_out->pts, sub_out->subtitle_pts);
+        ////}
+
+        av_packet_free(&pkt);
+
+        if (ret < 0) {
+            av_log(inlink->dst, AV_LOG_ERROR, "Decode error: %d \n", ret);
+            goto fail;
+        }
+
+        if (got_output) {
+            sub_out->pts = frame->pts;
+            av_frame_free(&s->next_sub_frame);
+            s->next_sub_frame = sub_out;
+            sub_out = NULL;
+            s->new_frame = 1;
+            s->next_sub_frame->pts = frame->pts;
+
+            if ((ret = av_buffer_replace(&s->next_sub_frame->subtitle_header, s->subtitle_header)) < 0)
+                goto fail;
+
+            if (s->real_time && s->scatter_realtime_output) {
+                if (s->next_repetition_pts)
+                    s->next_sub_frame->pts = s->next_repetition_pts;
+
+                s->next_sub_frame->subtitle_timing.duration = ms_to_avtb(s->real_time_latency_msec);
+                s->next_repetition_pts = s->next_sub_frame->pts + av_rescale_q(s->real_time_latency_msec, ms_tb, inlink->time_base);
+            }
+
+            ret = request_sub_frame(outlink1);
+            if (ret < 0)
+                goto fail;
+        }
+    }
+
+    if (s->real_time && s->scatter_realtime_output && !s->new_frame && s->next_repetition_pts > 0 && frame->pts > s->next_repetition_pts) {
+        s->new_frame = 1;
+        s->next_sub_frame->pts = s->next_repetition_pts;
+        s->next_repetition_pts = s->next_sub_frame->pts + av_rescale_q(s->real_time_latency_msec, ms_tb, inlink->time_base);
+    }
+
+    if (!s->next_sub_frame) {
+        s->next_sub_frame = ff_get_subtitles_buffer(outlink1, outlink1->format);
+        if (!s->next_sub_frame) {
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
+
+        s->next_sub_frame->subtitle_timing.duration = ms_to_avtb(s->real_time_latency_msec);
+        s->next_sub_frame->pts = frame->pts;
+        s->new_frame = 1;
+
+        if ((ret = av_buffer_replace(&s->next_sub_frame->subtitle_header, s->subtitle_header)) < 0)
+            goto fail;
+    }
+
+    ret = ff_filter_frame(outlink0, frame);
+
+fail:
+    av_packet_free(&pkt);
+    av_frame_free(&sub_out);
+    return ret;
+}
+
+#define OFFSET(x) offsetof(SplitCaptionsContext, x)
+#define FLAGS (AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption split_cc_options[] = {
+    { "use_cc_styles",    "Emit closed caption style header", OFFSET(use_cc_styles),  AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, NULL },
+    { "real_time", "emit subtitle events as they are decoded for real-time display", OFFSET(real_time), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS },
+    { "real_time_latency_msec", "minimum elapsed time between emitting real-time subtitle events", OFFSET(real_time_latency_msec), AV_OPT_TYPE_INT, { .i64 = 200 }, 0, 500, FLAGS },
+    { "scatter_realtime_output", "scatter output events to a duration of real_time_latency_msec", OFFSET(scatter_realtime_output), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS },
+    { "data_field", "select data field", OFFSET(data_field), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1, FLAGS, "data_field" },
+    { "auto",   "pick first one that appears", 0, AV_OPT_TYPE_CONST, { .i64 =-1 }, 0, 0, FLAGS, "data_field" },
+    { "first",  NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, FLAGS, "data_field" },
+    { "second", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, FLAGS, "data_field" },
+    { NULL },
+};
+
+AVFILTER_DEFINE_CLASS(split_cc);
+
+static const AVFilterPad inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_VIDEO,
+        .filter_frame = filter_frame,
+        .config_props = config_input,
+    },
+};
+
+static const AVFilterPad outputs[] = {
+    {
+        .name          = "video_passthrough",
+        .type          = AVMEDIA_TYPE_VIDEO,
+        .config_props  = config_video_output,
+    },
+    {
+        .name          = "subtitles",
+        .type          = AVMEDIA_TYPE_SUBTITLE,
+        .request_frame = request_sub_frame,
+        .config_props  = config_sub_output,
+    },
+};
+
+const AVFilter ff_sf_splitcc = {
+    .name           = "splitcc",
+    .description    = NULL_IF_CONFIG_SMALL("Extract closed-caption (A53) data from video as subtitle stream."),
+    .init           = init,
+    .uninit         = uninit,
+    .priv_size      = sizeof(SplitCaptionsContext),
+    .priv_class     = &split_cc_class,
+    .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_QUERY_FUNC(query_formats),
+};
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v3 17/26] avfilter/graphicsub2text: Add new graphicsub2text filter (OCR)
  2022-01-20  3:25   ` [FFmpeg-devel] [PATCH v3 00/26] Subtitle Filtering 2022 ffmpegagent
                       ` (15 preceding siblings ...)
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 16/26] avfilter/splitcc: Add splitcc filter for closed caption handling ffmpegagent
@ 2022-01-20  3:25     ` ffmpegagent
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 18/26] avfilter/subscale: Add filter for scaling and/or re-arranging graphical subtitles ffmpegagent
                       ` (9 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  3:25 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 configure                        |    1 +
 doc/filters.texi                 |   55 ++
 libavfilter/Makefile             |    2 +
 libavfilter/allfilters.c         |    1 +
 libavfilter/sf_graphicsub2text.c | 1132 ++++++++++++++++++++++++++++++
 5 files changed, 1191 insertions(+)
 create mode 100644 libavfilter/sf_graphicsub2text.c

diff --git a/configure b/configure
index 2a561b6f75..59d3bb86f0 100755
--- a/configure
+++ b/configure
@@ -3665,6 +3665,7 @@ frei0r_filter_deps="frei0r"
 frei0r_src_filter_deps="frei0r"
 fspp_filter_deps="gpl"
 gblur_vulkan_filter_deps="vulkan spirv_compiler"
+graphicsub2text_filter_deps="libtesseract"
 hflip_vulkan_filter_deps="vulkan spirv_compiler"
 histeq_filter_deps="gpl"
 hqdn3d_filter_deps="gpl"
diff --git a/doc/filters.texi b/doc/filters.texi
index c7a11a0896..1c0a00c7ae 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -25948,6 +25948,61 @@ ffmpeg -i "https://streams.videolan.org/ffmpeg/mkv_subtitles.mkv" -filter_comple
 @end example
 @end itemize
 
+@section graphicsub2text
+
+Converts graphic subtitles to text subtitles by performing OCR.
+
+For this filter to be available, ffmpeg needs to be compiled with libtesseract (see https://github.com/tesseract-ocr/tesseract).
+Language models need to be downloaded from https://github.com/tesseract-ocr/tessdata and put into as subfolder named 'tessdata' or into a folder specified via the environment variable 'TESSDATA_PREFIX'.
+The path can also be specified via filter option (see below).
+
+Note: These models are including the data for both OCR modes.
+
+Inputs:
+- 0: Subtitles [bitmap]
+
+Outputs:
+- 0: Subtitles [text]
+
+It accepts the following parameters:
+
+@table @option
+@item ocr_mode
+The character recognition mode to use.
+
+Supported OCR modes are:
+
+@table @var
+@item 0, tesseract
+This is the classic libtesseract operation mode. It is fast but less accurate than LSTM.
+@item 1, lstm
+Newer OCR implementation based on ML models. Provides usually better results, requires more processing resources.
+@item 2, both
+Use a combination of both modes.
+@end table
+
+@item tessdata_path
+The path to a folder containing the language models to be used.
+
+@item language
+The recognition language. It needs to match the first three characters of a  language model file in the tessdata path.
+
+@end table
+
+
+@subsection Examples
+
+@itemize
+@item
+Convert DVB graphic subtitles to ASS (text) subtitles
+
+Note: For this to work, you need to have the data file 'eng.traineddata' in a 'tessdata' subfolder (see above).
+@example
+ffmpeg -loglevel verbose -i "https://streams.videolan.org/streams/ts/video_subs_ttxt%2Bdvbsub.ts" -filter_complex "[0:13]graphicsub2text=delay_when_no_duration=1" -c:s ass -y output.mkv
+@end example
+@end itemize
+
+
 @section graphicsub2video
 
 Renders graphic subtitles as video frames.
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index b2e715cf8a..1397351373 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -299,6 +299,8 @@ OBJS-$(CONFIG_GBLUR_VULKAN_FILTER)           += vf_gblur_vulkan.o vulkan.o vulka
 OBJS-$(CONFIG_GEQ_FILTER)                    += vf_geq.o
 OBJS-$(CONFIG_GRADFUN_FILTER)                += vf_gradfun.o
 OBJS-$(CONFIG_GRAPHICSUB2VIDEO_FILTER)       += vf_overlaygraphicsubs.o framesync.o
+OBJS-$(CONFIG_GRAPHICSUB2TEXT_FILTER)        += sf_graphicsub2text.o
+OBJS-$(CONFIG_GRAPHICSUB2VIDEO_FILTER)       += vf_overlaygraphicsubs.o framesync.o
 OBJS-$(CONFIG_GRAPHMONITOR_FILTER)           += f_graphmonitor.o
 OBJS-$(CONFIG_GRAYWORLD_FILTER)              += vf_grayworld.o
 OBJS-$(CONFIG_GREYEDGE_FILTER)               += vf_colorconstancy.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 4996d3d27a..544eca616a 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -549,6 +549,7 @@ extern const AVFilter ff_avf_showwaves;
 extern const AVFilter ff_avf_showwavespic;
 extern const AVFilter ff_vaf_spectrumsynth;
 extern const AVFilter ff_sf_censor;
+extern const AVFilter ff_sf_graphicsub2text;
 extern const AVFilter ff_sf_showspeaker;
 extern const AVFilter ff_sf_splitcc;
 extern const AVFilter ff_sf_stripstyles;
diff --git a/libavfilter/sf_graphicsub2text.c b/libavfilter/sf_graphicsub2text.c
new file mode 100644
index 0000000000..9b413d314e
--- /dev/null
+++ b/libavfilter/sf_graphicsub2text.c
@@ -0,0 +1,1132 @@
+/*
+ * 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
+ * subtitle filter to convert graphical subs to text subs via OCR
+ */
+
+#include <tesseract/capi.h>
+#include <libavutil/ass_internal.h>
+
+#include "drawutils.h"
+#include "libavutil/opt.h"
+#include "subtitles.h"
+
+#include "libavcodec/elbg.h"
+
+enum {
+    RFLAGS_NONE         = 0,
+    RFLAGS_HALIGN       = 1 << 0,
+    RFLAGS_VALIGN       = 1 << 1,
+    RFLAGS_FBOLD        = 1 << 2,
+    RFLAGS_FITALIC      = 1 << 3,
+    RFLAGS_FUNDERLINE   = 1 << 4,
+    RFLAGS_FONT         = 1 << 5,
+    RFLAGS_FONTSIZE     = 1 << 6,
+    RFLAGS_COLOR        = 1 << 7,
+    RFLAGS_OUTLINECOLOR = 1 << 8,
+    RFLAGS_ALL = RFLAGS_HALIGN | RFLAGS_VALIGN | RFLAGS_FBOLD | RFLAGS_FITALIC | RFLAGS_FUNDERLINE |
+                RFLAGS_FONT | RFLAGS_FONTSIZE | RFLAGS_COLOR | RFLAGS_OUTLINECOLOR,
+};
+
+typedef struct SubOcrContext {
+    const AVClass *class;
+    int w, h;
+
+    TessBaseAPI *tapi;
+    TessOcrEngineMode ocr_mode;
+    char *tessdata_path;
+    char *language;
+    int preprocess_images;
+    int dump_bitmaps;
+    int delay_when_no_duration;
+    int recognize;
+    double font_size_factor;
+
+    int readorder_counter;
+
+    AVFrame *pending_frame;
+    AVBufferRef *subtitle_header;
+    AVBPrint buffer;
+
+    // Color Quantization Fields
+    struct ELBGContext *ctx;
+    AVLFG lfg;
+    int *codeword;
+    int *codeword_closest_codebook_idxs;
+    int *codebook;
+    int r_idx, g_idx, b_idx, a_idx;
+    int64_t last_subtitle_pts;
+} SubOcrContext;
+
+typedef struct OcrImageProps {
+    int background_color_index;
+    int fill_color_index;
+
+} OcrImageProps;
+
+static int64_t ms_to_avtb(int64_t ms)
+{
+    return av_rescale_q(ms, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q);
+}
+
+static int create_ass_header(AVFilterContext* ctx)
+{
+    SubOcrContext* s = ctx->priv;
+
+    if (!(s->w && s->h)) {
+        av_log(ctx, AV_LOG_WARNING, "create_ass_header: no width and height specified!\n");
+        s->w = ASS_DEFAULT_PLAYRESX;
+        s->h = ASS_DEFAULT_PLAYRESY;
+    }
+
+    char* subtitle_header_text = avpriv_ass_get_subtitle_header_full(s->w, s->h, ASS_DEFAULT_FONT, ASS_DEFAULT_FONT_SIZE,
+        ASS_DEFAULT_COLOR, ASS_DEFAULT_COLOR, ASS_DEFAULT_BACK_COLOR, ASS_DEFAULT_BACK_COLOR, ASS_DEFAULT_BOLD,
+        ASS_DEFAULT_ITALIC, ASS_DEFAULT_UNDERLINE, ASS_DEFAULT_BORDERSTYLE, ASS_DEFAULT_ALIGNMENT, 0);
+
+    if (!subtitle_header_text)
+        return AVERROR(ENOMEM);
+
+    s->subtitle_header = av_buffer_create((uint8_t*)subtitle_header_text, strlen(subtitle_header_text) + 1, NULL, NULL, 0);
+
+    if (!s->subtitle_header)
+        return AVERROR(ENOMEM);
+
+    return 0;
+}
+
+static int init(AVFilterContext *ctx)
+{
+    SubOcrContext *s = ctx->priv;
+    const char* tver = TessVersion();
+    uint8_t rgba_map[4];
+    int ret;
+
+    s->tapi = TessBaseAPICreate();
+
+    if (!s->tapi || !tver || !strlen(tver)) {
+        av_log(ctx, AV_LOG_ERROR, "Failed to access libtesseract\n");
+        return AVERROR(ENOSYS);
+    }
+
+    av_log(ctx, AV_LOG_VERBOSE, "Initializing libtesseract, version: %s\n", tver);
+
+    ret = TessBaseAPIInit4(s->tapi, s->tessdata_path, s->language, s->ocr_mode, NULL, 0, NULL, NULL, 0, 1);
+    if (ret < 0 ) {
+        av_log(ctx, AV_LOG_ERROR, "Failed to initialize libtesseract. Error: %d\n", ret);
+        return AVERROR(ENOSYS);
+    }
+
+    ret = TessBaseAPISetVariable(s->tapi, "tessedit_char_blacklist", "|");
+    if (ret < 0 ) {
+        av_log(ctx, AV_LOG_ERROR, "Failed to set 'tessedit_char_blacklist'. Error: %d\n", ret);
+        return AVERROR(EINVAL);
+    }
+
+    av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
+
+    ff_fill_rgba_map(&rgba_map[0], AV_PIX_FMT_RGB32);
+
+    s->r_idx = rgba_map[0]; // R
+    s->g_idx = rgba_map[1]; // G
+    s->b_idx = rgba_map[2]; // B
+    s->a_idx = rgba_map[3]; // A
+
+    av_lfg_init(&s->lfg, 123456789);
+
+    return 0;
+}
+
+static void uninit(AVFilterContext *ctx)
+{
+    SubOcrContext *s = ctx->priv;
+
+    av_buffer_unref(&s->subtitle_header);
+    av_bprint_finalize(&s->buffer, NULL);
+
+    if (s->tapi) {
+        TessBaseAPIEnd(s->tapi);
+        TessBaseAPIDelete(s->tapi);
+    }
+
+    avpriv_elbg_free(&s->ctx);
+}
+
+static int query_formats(AVFilterContext *ctx)
+{
+    AVFilterFormats *formats, *formats2;
+    AVFilterLink *inlink = ctx->inputs[0];
+    AVFilterLink *outlink = ctx->outputs[0];
+    static const enum AVSubtitleType in_fmts[] = { AV_SUBTITLE_FMT_BITMAP, AV_SUBTITLE_FMT_NONE };
+    static const enum AVSubtitleType out_fmts[] = { AV_SUBTITLE_FMT_ASS, AV_SUBTITLE_FMT_NONE };
+    int ret;
+
+    /* set input format */
+    formats = ff_make_format_list(in_fmts);
+    if ((ret = ff_formats_ref(formats, &inlink->outcfg.formats)) < 0)
+        return ret;
+
+    /* set output format */
+    formats2 = ff_make_format_list(out_fmts);
+    if ((ret = ff_formats_ref(formats2, &outlink->incfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int config_input(AVFilterLink *inlink)
+{
+    AVFilterContext *ctx = inlink->dst;
+    SubOcrContext *s = ctx->priv;
+
+    if (s->w <= 0 || s->h <= 0) {
+        s->w = inlink->w;
+        s->h = inlink->h;
+    }
+
+    return create_ass_header(ctx);
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    AVFilterLink *inlink = outlink->src->inputs[0];
+    const AVFilterContext *ctx  = outlink->src;
+    SubOcrContext *s = ctx->priv;
+
+    outlink->format = AV_SUBTITLE_FMT_ASS;
+    outlink->w = s->w;
+    outlink->h = s->h;
+    outlink->time_base = inlink->time_base;
+    outlink->frame_rate = inlink->frame_rate;
+
+    return 0;
+}
+
+static void free_subtitle_area(AVSubtitleArea *area)
+{
+    for (unsigned n = 0; n < FF_ARRAY_ELEMS(area->buf); n++)
+        av_buffer_unref(&area->buf[n]);
+
+    av_freep(&area->text);
+    av_freep(&area->ass);
+    av_free(area);
+
+}
+
+static AVSubtitleArea *copy_subtitle_area(const AVSubtitleArea *src)
+{
+    AVSubtitleArea *dst = av_mallocz(sizeof(AVSubtitleArea));
+
+    if (!dst)
+        return NULL;
+
+    dst->x         =  src->x;
+    dst->y         =  src->y;
+    dst->w         =  src->w;
+    dst->h         =  src->h;
+    dst->nb_colors =  src->nb_colors;
+    dst->type      =  src->type;
+    dst->flags     =  src->flags;
+
+    for (unsigned i = 0; i < AV_NUM_BUFFER_POINTERS; i++) {
+        if (src->h > 0 && src->w > 0 && src->buf[i]) {
+            dst->buf[0] = av_buffer_ref(src->buf[i]);
+            if (!dst->buf[i])
+                return NULL;
+
+            const int ret = av_buffer_make_writable(&dst->buf[i]);
+            if (ret < 0)
+                return NULL;
+
+            dst->linesize[i] = src->linesize[i];
+        }
+    }
+
+    memcpy(&dst->pal[0], &src->pal[0], sizeof(src->pal[0]) * 256);
+
+
+    return dst;
+}
+
+static int quantize_image_colors(SubOcrContext *const s, AVSubtitleArea *subtitle_area)
+{
+    const int num_quantized_colors = 3;
+    int k, ret;
+    const int codeword_length = subtitle_area->w * subtitle_area->h;
+    uint8_t *src_data = subtitle_area->buf[0]->data;
+
+    if (subtitle_area->nb_colors <= num_quantized_colors) {
+        av_log(s, AV_LOG_DEBUG, "No need to quantize colors. Color count: %d\n", subtitle_area->nb_colors);
+        return 0;
+    }
+
+    // Convert palette to grayscale
+    for (int i = 0; i < subtitle_area->nb_colors; i++) {
+        uint8_t *color        = (uint8_t *)&subtitle_area->pal[i];
+        const uint8_t average = (uint8_t)(((int)color[s->r_idx] + color[s->g_idx] + color[s->b_idx]) / 3);
+        color[s->b_idx]       = average;
+        color[s->g_idx]       = average;
+        color[s->r_idx]       = average;
+    }
+
+    /* Re-Initialize */
+    s->codeword = av_realloc_f(s->codeword, codeword_length, 4 * sizeof(*s->codeword));
+    if (!s->codeword)
+        return AVERROR(ENOMEM);
+
+    s->codeword_closest_codebook_idxs = av_realloc_f(s->codeword_closest_codebook_idxs,
+        codeword_length, sizeof(*s->codeword_closest_codebook_idxs));
+    if (!s->codeword_closest_codebook_idxs)
+        return AVERROR(ENOMEM);
+
+    s->codebook = av_realloc_f(s->codebook, num_quantized_colors, 4 * sizeof(*s->codebook));
+    if (!s->codebook)
+        return AVERROR(ENOMEM);
+
+    /* build the codeword */
+    k = 0;
+    for (int i = 0; i < subtitle_area->h; i++) {
+        uint8_t *p = src_data;
+        for (int j = 0; j < subtitle_area->w; j++) {
+            const uint8_t *color = (uint8_t *)&subtitle_area->pal[*p];
+            s->codeword[k++] = color[s->b_idx];
+            s->codeword[k++] = color[s->g_idx];
+            s->codeword[k++] = color[s->r_idx];
+            s->codeword[k++] = color[s->a_idx];
+            p++;
+        }
+        src_data += subtitle_area->linesize[0];
+    }
+
+    /* compute the codebook */
+    ret = avpriv_elbg_do(&s->ctx, s->codeword, 4, codeword_length, s->codebook,
+        num_quantized_colors, 1, s->codeword_closest_codebook_idxs, &s->lfg, 0);
+    if (ret < 0)
+        return ret;
+
+    /* Write Palette */
+    for (int i = 0; i < num_quantized_colors; i++) {
+        subtitle_area->pal[i] = s->codebook[i*4+3] << 24  |
+                    (s->codebook[i*4+2] << 16) |
+                    (s->codebook[i*4+1] <<  8) |
+                    (s->codebook[i*4  ] <<  0);
+    }
+
+
+    av_log(s, AV_LOG_DEBUG, "Quantized colors from %d to %d\n", subtitle_area->nb_colors, num_quantized_colors);
+
+    subtitle_area->nb_colors = num_quantized_colors;
+    src_data = subtitle_area->buf[0]->data;
+
+    /* Write Image */
+    k = 0;
+    for (int i = 0; i < subtitle_area->h; i++) {
+        uint8_t *p = src_data;
+        for (int j = 0; j < subtitle_area->w; j++, p++) {
+            p[0] = (uint8_t)s->codeword_closest_codebook_idxs[k++];
+        }
+
+        src_data += subtitle_area->linesize[0];
+    }
+
+    return ret;
+}
+
+#define MEASURE_LINE_COUNT 6
+
+static uint8_t get_background_color_index(SubOcrContext *const s, const AVSubtitleArea *subtitle_area)
+{
+    const int linesize = subtitle_area->linesize[0];
+    int index_counts[256] = {0};
+    const unsigned int line_offsets[MEASURE_LINE_COUNT] = {
+        0,
+        linesize,
+        2 * linesize,
+        (subtitle_area->h - 3) * linesize,
+        (subtitle_area->h - 2) * linesize,
+        (subtitle_area->h - 1) * linesize
+    };
+
+    const uint8_t *src_data = subtitle_area->buf[0]->data;
+    const uint8_t tl = src_data[0];
+    const uint8_t tr = src_data[subtitle_area->w - 1];
+    const uint8_t bl = src_data[(subtitle_area->h - 1) * linesize + 0];
+    const uint8_t br = src_data[(subtitle_area->h - 1) * linesize + subtitle_area->w - 1];
+    uint8_t max_index = 0;
+    int max_count;
+
+    // When all corner pixels are equal, assume that as background color
+    if (tl == tr == bl == br || subtitle_area->h < 6)
+        return tl;
+
+    for (unsigned int i = 0; i < MEASURE_LINE_COUNT; i++) {
+        uint8_t *p = subtitle_area->buf[0]->data + line_offsets[i];
+        for (int k = 0; k < subtitle_area->w; k++)
+            index_counts[p[k]]++;
+    }
+
+    max_count = index_counts[0];
+
+    for (uint8_t i = 1; i < subtitle_area->nb_colors; i++) {
+        if (index_counts[i] > max_count) {
+            max_count = index_counts[i];
+            max_index = i;
+        }
+    }
+
+    return max_index;
+}
+
+static uint8_t get_text_color_index(SubOcrContext *const s, const AVSubtitleArea *subtitle_area, const uint8_t bg_color_index, uint8_t *outline_color_index)
+{
+    const int linesize = subtitle_area->linesize[0];
+    int index_counts[256] = {0};
+    uint8_t last_index = bg_color_index;
+    int max_count, min_req_count;
+    uint8_t max_index = 0;
+
+    for (int i = 3; i < subtitle_area->h - 3; i += 5) {
+        const uint8_t *p = subtitle_area->buf[0]->data + ((ptrdiff_t)linesize * i);
+        for (int k = 0; k < subtitle_area->w; k++) {
+            const uint8_t cur_index = p[k];
+
+            // When color hasn't changed, continue
+            if (cur_index == last_index)
+                continue;
+
+            if (cur_index != bg_color_index)
+                index_counts[cur_index]++;
+
+            last_index = cur_index;
+        }
+    }
+
+    max_count = index_counts[0];
+
+    for (uint8_t i = 1; i < subtitle_area->nb_colors; i++) {
+        if (index_counts[i] > max_count) {
+            max_count = index_counts[i];
+            max_index = i;
+        }
+    }
+
+    min_req_count = max_count / 3;
+
+    for (uint8_t i = 1; i < subtitle_area->nb_colors; i++) {
+        if (index_counts[i] < min_req_count)
+            index_counts[i] = 0;
+    }
+
+    *outline_color_index = max_index;
+
+    index_counts[max_index] = 0;
+    max_count = 0;
+
+    for (uint8_t i = 0; i < subtitle_area->nb_colors; i++) {
+        if (index_counts[i] > max_count) {
+            max_count = index_counts[i];
+            max_index = i;
+        }
+    }
+
+    if (*outline_color_index == max_index)
+        *outline_color_index = 255;
+
+    return max_index;
+}
+
+static void make_image_binary(SubOcrContext *const s, AVSubtitleArea *subtitle_area, const uint8_t text_color_index)
+{
+    for (int i = 0; i < subtitle_area->nb_colors; i++) {
+
+        if (i != text_color_index)
+            subtitle_area->pal[i] = 0xffffffff;
+        else
+            subtitle_area->pal[i] = 0xff000000;
+    }
+}
+
+static int get_crop_region(SubOcrContext *const s, const AVSubtitleArea *subtitle_area, uint8_t text_color_index, int *x, int *y, int *w, int *h)
+{
+    const int linesize = subtitle_area->linesize[0];
+    int max_y = 0, max_x = 0;
+    int min_y = subtitle_area->h - 1, min_x = subtitle_area->w - 1;
+
+    for (int i = 0; i < subtitle_area->h; i += 3) {
+        const uint8_t *p = subtitle_area->buf[0]->data + ((ptrdiff_t)linesize * i);
+        for (int k = 0; k < subtitle_area->w; k += 2) {
+            if (p[k] == text_color_index) {
+                min_y = FFMIN(min_y, i);
+                min_x = FFMIN(min_x, k);
+                max_y = FFMAX(max_y, i);
+                max_x = FFMAX(max_x, k);
+            }
+        }
+    }
+
+    if (max_y <= min_y || max_x <= min_x) {
+        av_log(s, AV_LOG_WARNING, "Unable to detect crop region\n");
+        *x = 0;
+        *y = 0;
+        *w = subtitle_area->w;
+        *h = subtitle_area->h;
+    }    else {
+        *x = FFMAX(min_x - 10, 0);
+        *y = FFMAX(min_y - 10, 0);
+        *w = FFMIN(max_x + 10 - *x, (subtitle_area->w - *x));
+        *h = FFMIN(max_y + 10 - *y, (subtitle_area->h - *y));
+    }
+
+    return 0;
+}
+
+static int crop_area_bitmap(SubOcrContext *const s, AVSubtitleArea *subtitle_area, int x, int y, int w, int h)
+{
+    const int linesize = subtitle_area->linesize[0];
+    AVBufferRef *dst = av_buffer_allocz(h * w);
+    uint8_t *d;
+
+    if (!dst)
+        return AVERROR(ENOMEM);
+
+    d = dst->data;
+
+    for (int i = y; i < y + h; i++) {
+        const uint8_t *p = subtitle_area->buf[0]->data + ((ptrdiff_t)linesize * i);
+        for (int k = x; k < x + w; k++) {
+            *d = p[k];
+            d++;
+        }
+    }
+
+    subtitle_area->w = w;
+    subtitle_area->h = h;
+    subtitle_area->x += x;
+    subtitle_area->y += y;
+    subtitle_area->linesize[0] = w;
+    av_buffer_replace(&subtitle_area->buf[0], dst);
+
+    av_buffer_unref(&dst);
+    return 0;
+}
+
+#define R 0
+#define G 1
+#define B 2
+#define A 3
+
+static int print_code(AVBPrint *buf, int in_code, const char *fmt, ...)
+{
+    va_list vl;
+
+    if (!in_code)
+        av_bprint_chars(buf, '{', 1);
+
+    va_start(vl, fmt);
+    av_vbprintf(buf, fmt, vl);
+    va_end(vl);
+
+    return 1;
+}
+
+static int end_code(AVBPrint *buf, int in_code)
+{
+    if (in_code)
+        av_bprint_chars(buf, '}', 1);
+    return 0;
+}
+
+static uint8_t* create_grayscale_image(AVFilterContext *ctx, AVSubtitleArea *area, int invert)
+{
+    uint8_t gray_pal[256];
+    const size_t img_size = area->buf[0]->size;
+    const uint8_t* img    = area->buf[0]->data;
+    uint8_t* gs_img       = av_malloc(img_size);
+
+    if (!gs_img)
+        return NULL;
+
+    for (unsigned i = 0; i < 256; i++) {
+        const uint8_t *col = (uint8_t*)&area->pal[i];
+        const int val      = (int)col[3] * FFMAX3(col[0], col[1], col[2]);
+        gray_pal[i]        = (uint8_t)(val >> 8);
+    }
+
+    if (invert)
+        for (unsigned i = 0; i < img_size; i++)
+            gs_img[i]   = 255 - gray_pal[img[i]];
+    else
+        for (unsigned i = 0; i < img_size; i++)
+            gs_img[i]   = gray_pal[img[i]];
+
+    return gs_img;
+}
+
+static uint8_t* create_bitmap_image(AVFilterContext *ctx, AVSubtitleArea *area, const uint8_t text_color_index)
+{
+    const size_t img_size = area->buf[0]->size;
+    const uint8_t* img    = area->buf[0]->data;
+    uint8_t* gs_img       = av_malloc(img_size);
+
+    if (!gs_img)
+        return NULL;
+
+    for (unsigned i = 0; i < img_size; i++) {
+        if (img[i] == text_color_index)
+            gs_img[i]   = 0;
+        else
+            gs_img[i]   = 255;
+    }
+
+    return gs_img;
+}
+
+static void png_save(AVFilterContext *ctx, const char *filename, AVSubtitleArea *area)
+{
+    int x, y;
+    int v;
+    FILE *f;
+    char fname[40];
+    const uint8_t *data = area->buf[0]->data;
+
+    snprintf(fname, sizeof(fname), "%s.ppm", filename);
+
+    f = fopen(fname, "wb");
+    if (!f) {
+        perror(fname);
+        return;
+    }
+    fprintf(f, "P6\n"
+            "%d %d\n"
+            "%d\n",
+            area->w, area->h, 255);
+    for(y = 0; y < area->h; y++) {
+        for(x = 0; x < area->w; x++) {
+            const uint8_t index = data[y * area->linesize[0] + x];
+            v = (int)area->pal[index];
+            putc(v >> 16 & 0xff, f);
+            putc(v >> 8 & 0xff, f);
+            putc(v >> 0 & 0xff, f);
+        }
+    }
+
+    fclose(f);
+}
+
+static int get_max_index(int score[256])
+{
+    int max_val = 0, max_index = 0;
+
+    for (int i = 0; i < 256; i++) {
+        if (score[i] > max_val) {
+            max_val = score[i];
+            max_index = i;
+        }
+    }
+
+    return max_index;
+}
+
+static int get_word_colors(AVFilterContext *ctx, TessResultIterator* ri, const AVSubtitleArea* area, const AVSubtitleArea* original_area,
+                           uint8_t bg_color_index, uint8_t text_color_index, uint8_t outline_color_index,
+                           uint32_t* bg_color, uint32_t* text_color, uint32_t* outline_color)
+{
+    int left = 0, top = 0, right = 0, bottom = 0, ret;
+    int bg_score[256] = {0}, text_score[256] = {0}, outline_score[256] = {0};
+    int max_index;
+
+    ret = TessPageIteratorBoundingBox((TessPageIterator*)ri, RIL_WORD, &left, &top, &right, &bottom);
+    if (ret < 0) {
+        av_log(ctx, AV_LOG_WARNING, "get_word_colors: IteratorBoundingBox failed: %d\n", ret);
+        return  ret;
+    }
+
+    if (left >= area->w || right >= area->w || top >= area->h || bottom >= area->h) {
+        av_log(ctx, AV_LOG_WARNING, "get_word_colors: word bounding box (l: %d, t: %d r: %d, b: %d) out of image bounds (%dx%d)\n", left,top, right, bottom, area->w, area->h);
+        return  AVERROR(EINVAL);
+    }
+
+    for (int y = top; y < bottom; y += 3) {
+        uint8_t *p = area->buf[0]->data + (y * area->linesize[0]) + left;
+        uint8_t *porig = original_area->buf[0]->data + (y * original_area->linesize[0]) + left;
+        uint8_t current_index = 255;
+
+        for (int x = left; x < right; x++, p++, porig++) {
+
+            if (*p == current_index) {
+                if (*p == bg_color_index)
+                    bg_score[*porig]++;
+                if (*p == text_color_index)
+                    text_score[*porig]++;
+                if (*p == outline_color_index)
+                    outline_score[*porig]++;
+            }
+
+            current_index = *p;
+        }
+    }
+
+    max_index = get_max_index(bg_score);
+    if (bg_score[max_index] > 0)
+        *bg_color = original_area->pal[max_index];
+
+    max_index = get_max_index(text_score);
+    if (text_score[max_index] > 0)
+        *text_color = original_area->pal[max_index];
+
+    max_index = get_max_index(outline_score);
+    if (outline_score[max_index] > 0)
+        *outline_color = original_area->pal[max_index];
+
+    return 0;
+}
+
+static int convert_area(AVFilterContext *ctx, AVSubtitleArea *area, const AVFrame *frame, const unsigned area_index, int *margin_v)
+{
+    SubOcrContext *s = ctx->priv;
+    char *ocr_text = NULL;
+    int ret = 0;
+    uint8_t *gs_img;
+    uint8_t bg_color_index;
+    uint8_t text_color_index = 255;
+    uint8_t outline_color_index = 255;
+    char filename[32];
+    AVSubtitleArea *original_area = copy_subtitle_area(area);
+
+    if (!original_area)
+        return AVERROR(ENOMEM);
+
+    if (area->w < 6 || area->h < 6) {
+        area->ass = NULL;
+        goto exit;
+    }
+
+    if (s->dump_bitmaps) {
+        snprintf(filename, sizeof(filename), "graphicsub2text_%"PRId64"_%d_original", frame->subtitle_timing.start_pts, area_index);
+        png_save(ctx, filename, area);
+    }
+
+    if (s->preprocess_images) {
+        ret = quantize_image_colors(s, area);
+        if (ret < 0)
+            goto exit;
+        if (s->dump_bitmaps && original_area->nb_colors != area->nb_colors) {
+            snprintf(filename, sizeof(filename), "graphicsub2text_%"PRId64"_%d_quantized", frame->subtitle_timing.start_pts, area_index);
+            png_save(ctx, filename, area);
+        }
+    }
+
+    bg_color_index = get_background_color_index(s, area);
+
+    if (s->preprocess_images) {
+        int x, y, w, h;
+
+        for (int i = 0; i < area->nb_colors; ++i) {
+            av_log(s, AV_LOG_DEBUG, "Color #%d: %0.8X\n", i, area->pal[i]);
+        }
+
+        text_color_index = get_text_color_index(s, area, bg_color_index, &outline_color_index);
+
+        get_crop_region(s, area, text_color_index, &x, &y, &w, &h);
+
+        if ((ret = crop_area_bitmap(s, area, x, y, w, h) < 0))
+            goto exit;
+
+        if ((ret = crop_area_bitmap(s, original_area, x, y, w, h) < 0))
+            goto exit;
+
+        make_image_binary(s, area, text_color_index);
+
+        if (s->dump_bitmaps) {
+            snprintf(filename, sizeof(filename), "graphicsub2text_%"PRId64"_%d_preprocessed", frame->subtitle_timing.start_pts, area_index);
+            png_save(ctx, filename, area);
+        }
+
+        gs_img = create_bitmap_image(ctx, area, text_color_index);
+    } else
+        gs_img = create_grayscale_image(ctx, area, 1);
+
+    if (!gs_img) {
+        ret = AVERROR(ENOMEM);
+        goto exit;
+    }
+
+    area->type = AV_SUBTITLE_FMT_ASS;
+    TessBaseAPISetImage(s->tapi, gs_img, area->w, area->h, 1, area->linesize[0]);
+
+    TessBaseAPISetSourceResolution(s->tapi, 72);
+
+    ret = TessBaseAPIRecognize(s->tapi, NULL);
+    if (ret == 0)
+        ocr_text = TessBaseAPIGetUTF8Text(s->tapi);
+
+    if (!ocr_text || !strlen(ocr_text)) {
+        av_log(ctx, AV_LOG_WARNING, "OCR didn't return a text. ret=%d\n", ret);
+        area->ass = NULL;
+
+        goto exit;
+    }
+
+    const size_t len = strlen(ocr_text);
+    if (len > 0 && ocr_text[len - 1] == '\n')
+        ocr_text[len - 1] = 0;
+
+    av_log(ctx, AV_LOG_VERBOSE, "OCR Result: %s\n", ocr_text);
+
+    area->ass = av_strdup(ocr_text);
+    TessDeleteText(ocr_text);
+
+    // End of simple recognition
+
+    if (s->recognize != RFLAGS_NONE) {
+        TessResultIterator* ri = 0;
+        const TessPageIteratorLevel level = RIL_WORD;
+        int cur_is_bold = 0, cur_is_italic = 0, cur_is_underlined = 0, cur_pointsize = 0;
+        uint32_t cur_text_color = 0, cur_bg_color = 0, cur_outline_color = 0;
+
+        char *cur_font_name = NULL;
+        int valign = 0; // 0: bottom, 4: top, 8 middle
+        int halign = 2; // 1: left, 2: center, 3: right
+        int in_code = 0;
+        double font_factor = (0.000666 * (s->h - 480) + 1) * s->font_size_factor;
+
+        av_freep(&area->ass);
+        av_bprint_clear(&s->buffer);
+
+        ri = TessBaseAPIGetIterator(s->tapi);
+
+        // Horizontal Alignment
+        if (s->w && s->recognize & RFLAGS_HALIGN) {
+            int left_margin = area->x;
+            int right_margin = s->w - area->x - area->w;
+            double relative_diff = ((double)left_margin - right_margin) / s->w;
+
+            if (FFABS(relative_diff) < 0.1)
+                halign = 2; // center
+            else if (relative_diff > 0)
+                halign = 3; // right
+            else
+                halign = 1; // left
+        }
+
+        // Vertical Alignment
+        if (s->h && frame->height && s->recognize & RFLAGS_VALIGN) {
+            int left = 0, top = 0, right = 0, bottom = 0;
+
+            TessPageIteratorBoundingBox((TessPageIterator*)ri, RIL_TEXTLINE, &left, &top, &right, &bottom);
+            av_log(s, AV_LOG_DEBUG, "RIL_TEXTLINE - TOP: %d  BOTTOM: %d HEIGHT: %d\n", top, bottom, bottom - top);
+
+            TessPageIteratorBoundingBox((TessPageIterator*)ri, RIL_BLOCK, &left, &top, &right, &bottom);
+
+            const int vertical_pos = area->y + area->h / 2;
+            if (vertical_pos < s->h / 3) {
+                *margin_v = area->y + top;
+                valign = 4;
+            }
+            else if (vertical_pos < s->h / 3 * 2) {
+                *margin_v = 0;
+                valign = 8;
+            } else {
+                *margin_v = frame->height - area->y - area->h;
+                valign = 0;
+            }
+        }
+
+        if (*margin_v < 0)
+            *margin_v = 0;
+
+        // Set alignment when not default (2)
+        if ((valign | halign) != 2)
+            in_code = print_code(&s->buffer, in_code, "\\a%d", valign | halign);
+
+        do {
+            int is_bold, is_italic, is_underlined, is_monospace, is_serif, is_smallcaps, pointsize, font_id;
+            char* word;
+            const char *font_name = TessResultIteratorWordFontAttributes(ri, &is_bold, &is_italic, &is_underlined, &is_monospace, &is_serif, &is_smallcaps, &pointsize, &font_id);
+            uint32_t text_color = 0, bg_color = 0, outline_color = 0;
+
+            if (cur_is_underlined && !is_underlined && s->recognize & RFLAGS_FUNDERLINE)
+                in_code = print_code(&s->buffer, in_code, "\\u0");
+
+            if (cur_is_bold && !is_bold && s->recognize & RFLAGS_FBOLD)
+                in_code = print_code(&s->buffer, in_code, "\\b0");
+
+            if (cur_is_italic && !is_italic && s->recognize & RFLAGS_FITALIC)
+                in_code = print_code(&s->buffer, in_code, "\\i0");
+
+
+            if (TessPageIteratorIsAtBeginningOf((TessPageIterator*)ri, RIL_TEXTLINE) && !TessPageIteratorIsAtBeginningOf((TessPageIterator*)ri, RIL_BLOCK)) {
+                in_code = end_code(&s->buffer, in_code);
+                av_bprintf(&s->buffer, "\\N");
+            }
+
+            if (get_word_colors(ctx, ri, area, original_area, bg_color_index, text_color_index, outline_color_index, &bg_color, &text_color, &outline_color) == 0) {
+
+                if (text_color > 0 && cur_text_color != text_color && s->recognize & RFLAGS_COLOR) {
+                    const uint8_t* tval = (uint8_t*)&text_color;
+                    const int color = (int)tval[R] << 16 | (int)tval[G] << 8 | tval[B];
+
+                    in_code = print_code(&s->buffer, in_code, "\\1c&H%0.6X&", color);
+                    if (tval[A] != 255)
+                        in_code = print_code(&s->buffer, in_code, "\\1a&H%0.2X&", 255 - tval[A]);
+                }
+
+                if (outline_color > 0 && cur_outline_color != outline_color && s->recognize & RFLAGS_OUTLINECOLOR) {
+                    const uint8_t* tval = (uint8_t*)&outline_color;
+                    const int color = (int)tval[R] << 16 | (int)tval[G] << 8 | tval[B];
+
+                    in_code = print_code(&s->buffer, in_code, "\\3c&H%0.6X&\\bord2", color);
+                    in_code = print_code(&s->buffer, in_code, "\\3a&H%0.2X&", FFMIN(255 - tval[A], 30));
+                }
+
+                cur_text_color = text_color;
+                cur_outline_color = outline_color;
+            }
+
+            if (font_name && strlen(font_name) && s->recognize & RFLAGS_FONT) {
+                if (!cur_font_name || !strlen(cur_font_name) || strcmp(cur_font_name, font_name) != 0) {
+                    char *sanitized_font_name = av_strireplace(font_name, "_", " ");
+                    if (!sanitized_font_name) {
+                        ret = AVERROR(ENOMEM);
+                        goto exit;
+                    }
+
+                    in_code = print_code(&s->buffer, in_code, "\\fn%s", sanitized_font_name);
+                    av_freep(&sanitized_font_name);
+
+                    if (cur_font_name)
+                        av_freep(&cur_font_name);
+                    cur_font_name = av_strdup(font_name);
+                    if (!cur_font_name) {
+                        ret = AVERROR(ENOMEM);
+                        goto exit;
+                    }
+                }
+            }
+
+            if (pointsize != cur_pointsize && s->recognize & RFLAGS_FONTSIZE) {
+                av_log(s, AV_LOG_DEBUG, "pointsize - pointsize: %d\n", pointsize);
+                in_code = print_code(&s->buffer, in_code, "\\fs%d", (int)(pointsize * font_factor));
+                cur_pointsize = pointsize;
+            }
+
+            if (is_italic && !cur_is_italic && s->recognize & RFLAGS_FITALIC)
+                in_code = print_code(&s->buffer, in_code, "\\i1");
+
+            if (is_bold && !cur_is_bold && s->recognize & RFLAGS_FBOLD)
+                in_code = print_code(&s->buffer, in_code, "\\b1");
+
+            if (is_underlined && !cur_is_underlined && s->recognize & RFLAGS_FUNDERLINE)
+                in_code = print_code(&s->buffer, in_code, "\\u1");
+
+            in_code = end_code(&s->buffer, in_code);
+
+            cur_is_underlined = is_underlined;
+            cur_is_bold = is_bold;
+            cur_is_italic = is_italic;
+
+            if (!TessPageIteratorIsAtBeginningOf((TessPageIterator*)ri, RIL_TEXTLINE))
+                av_bprint_chars(&s->buffer, ' ', 1);
+
+            word = TessResultIteratorGetUTF8Text(ri, level);
+            av_bprint_append_data(&s->buffer, word, strlen(word));
+            TessDeleteText(word);
+
+        } while (TessResultIteratorNext(ri, level));
+
+        if (!av_bprint_is_complete(&s->buffer))
+            ret = AVERROR(ENOMEM);
+        else {
+            av_log(ctx, AV_LOG_VERBOSE, "ASS Result: %s\n", s->buffer.str);
+            area->ass = av_strdup(s->buffer.str);
+        }
+
+        TessResultIteratorDelete(ri);
+        av_freep(&cur_font_name);
+    }
+
+exit:
+    free_subtitle_area(original_area);
+    av_freep(&gs_img);
+    av_buffer_unref(&area->buf[0]);
+    area->type = AV_SUBTITLE_FMT_ASS;
+
+    return ret;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFilterContext *ctx = inlink->dst;
+    SubOcrContext *s = ctx->priv;
+    AVFilterLink *outlink = inlink->dst->outputs[0];
+    int ret, frame_sent = 0;
+
+    if (s->pending_frame && !frame->repeat_sub) {
+        const int64_t pts_diff = frame->subtitle_timing.start_pts - s->pending_frame->subtitle_timing.start_pts;
+
+        if (pts_diff == 0) {
+            // This is just a repetition of the previous frame, ignore it
+            av_frame_free(&frame);
+            return 0;
+        }
+
+        s->pending_frame->subtitle_timing.duration = pts_diff;
+
+        if ((ret = av_buffer_replace(&s->pending_frame->subtitle_header, s->subtitle_header)) < 0)
+            return ret;
+
+        ret = ff_filter_frame(outlink, s->pending_frame);
+        s->pending_frame = NULL;
+        if (ret < 0)
+            return  ret;
+
+        frame_sent = 1;
+        s->last_subtitle_pts = frame->subtitle_timing.start_pts;
+    }
+
+    if (frame->repeat_sub) {
+        // Ignore repeated frame
+        av_frame_free(&frame);
+        return 0;
+    }
+
+    s->last_subtitle_pts = frame->subtitle_timing.start_pts;
+
+    ret = av_frame_make_writable(frame);
+
+    if (ret < 0) {
+        av_frame_free(&frame);
+        return ret;
+    }
+
+    frame->format = AV_SUBTITLE_FMT_ASS;
+
+    av_log(ctx, AV_LOG_VERBOSE, "filter_frame sub_pts: %"PRIu64", duration: %"PRIu64", num_areas: %d\n",
+        frame->subtitle_timing.start_pts, frame->subtitle_timing.duration, frame->num_subtitle_areas);
+
+    if (frame->num_subtitle_areas > 1 &&
+        frame->subtitle_areas[0]->y > frame->subtitle_areas[frame->num_subtitle_areas - 1]->y) {
+
+        for (unsigned i = 0; i < frame->num_subtitle_areas / 2; i++)
+            FFSWAP(AVSubtitleArea*, frame->subtitle_areas[i], frame->subtitle_areas[frame->num_subtitle_areas - i - 1]);
+    }
+
+    for (int i = 0; i < frame->num_subtitle_areas; i++) {
+        AVSubtitleArea *area = frame->subtitle_areas[i];
+        int margin_v = 0;
+
+        ret = convert_area(ctx, area, frame, i, &margin_v);
+        if (ret < 0)
+            return ret;
+
+        if (area->ass && area->ass[0] != '\0') {
+
+            const int layer = s->recognize ? i : 0;
+            char *tmp = area->ass;
+            area->ass = avpriv_ass_get_dialog_ex(s->readorder_counter++, layer, "Default", NULL, 0, 0, margin_v, tmp);
+            av_free(tmp);
+        }
+    }
+
+    // When decoders can't determine the end time, they are setting it either to UINT32_NAX
+    // or 30s (dvbsub).
+    if (s->delay_when_no_duration && frame->subtitle_timing.duration >= ms_to_avtb(29000)) {
+        // Can't send it without end time, wait for the next frame to determine the end_display time
+        s->pending_frame = frame;
+
+        if (frame_sent)
+            return 0;
+
+        // To keep all going, send an empty frame instead
+        frame = ff_get_subtitles_buffer(outlink, AV_SUBTITLE_FMT_ASS);
+        if (!frame)
+            return AVERROR(ENOMEM);
+
+        av_frame_copy_props(frame, s->pending_frame);
+        frame->subtitle_timing.start_pts = 0;
+        frame->subtitle_timing.duration = 1;
+        frame->repeat_sub = 1;
+    }
+
+    if ((ret = av_buffer_replace(&frame->subtitle_header, s->subtitle_header)) < 0)
+        return ret;
+
+    return ff_filter_frame(outlink, frame);
+}
+
+#define OFFSET(x) offsetof(SubOcrContext, x)
+#define FLAGS (AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption graphicsub2text_options[] = {
+    { "delay_when_no_duration", "delay output when duration is unknown", OFFSET(delay_when_no_duration), AV_OPT_TYPE_BOOL,   { .i64 = 0 },                         0,                  1,       FLAGS, NULL },
+    { "dump_bitmaps",           "save processed bitmaps as .ppm",        OFFSET(dump_bitmaps),           AV_OPT_TYPE_BOOL,   { .i64 = 0 },                         0,                  1,       FLAGS, NULL },
+    { "font_size_factor",       "font size adjustment factor",           OFFSET(font_size_factor),       AV_OPT_TYPE_DOUBLE, { .dbl = 1.0 },                       0.2,                5,       FLAGS, NULL },
+    { "language",               "ocr language",                          OFFSET(language),               AV_OPT_TYPE_STRING, { .str = "eng" },                     0,                  0,       FLAGS, NULL },
+    { "ocr_mode",               "set ocr mode",                          OFFSET(ocr_mode),               AV_OPT_TYPE_INT,    { .i64=OEM_TESSERACT_ONLY },          OEM_TESSERACT_ONLY, 2,       FLAGS, "ocr_mode" },
+    {   "tesseract",            "classic tesseract ocr",                 0,                              AV_OPT_TYPE_CONST,  { .i64=OEM_TESSERACT_ONLY },          0,                  0,       FLAGS, "ocr_mode" },
+    {   "lstm",                 "lstm (ML based)",                       0,                              AV_OPT_TYPE_CONST,  { .i64=OEM_LSTM_ONLY},                0,                  0,       FLAGS, "ocr_mode" },
+    {   "both",                 "use both models combined",              0,                              AV_OPT_TYPE_CONST,  { .i64=OEM_TESSERACT_LSTM_COMBINED }, 0,                  0,       FLAGS, "ocr_mode" },
+    { "preprocess_images",      "reduce colors, remove outlines",        OFFSET(preprocess_images),      AV_OPT_TYPE_BOOL,   { .i64 = 1 },                         0,                  1,       FLAGS, NULL },
+    { "recognize",              "detect fonts, styles and colors",       OFFSET(recognize),              AV_OPT_TYPE_FLAGS,  { .i64 = RFLAGS_ALL},                  0,                  INT_MAX, FLAGS, "reco_flags" },
+        { "none",         "no format detection",  0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_NONE         }, 0, 0, FLAGS, "reco_flags" },
+        { "halign",       "horizontal alignment", 0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_HALIGN       }, 0, 0, FLAGS, "reco_flags" },
+        { "valign",       "vertical alignment",   0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_VALIGN       }, 0, 0, FLAGS, "reco_flags" },
+        { "bold",         "font bold",            0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_FBOLD        }, 0, 0, FLAGS, "reco_flags" },
+        { "italic",       "font italic",          0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_FITALIC      }, 0, 0, FLAGS, "reco_flags" },
+        { "underline",    "font underline",       0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_FUNDERLINE   }, 0, 0, FLAGS, "reco_flags" },
+        { "font",         "font name",            0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_FONT         }, 0, 0, FLAGS, "reco_flags" },
+        { "fontsize",     "font size",            0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_FONTSIZE     }, 0, 0, FLAGS, "reco_flags" },
+        { "color",        "font color",           0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_COLOR        }, 0, 0, FLAGS, "reco_flags" },
+        { "outlinecolor", "outline color",        0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_OUTLINECOLOR }, 0, 0, FLAGS, "reco_flags" },
+    { "tessdata_path",          "path to tesseract data",                OFFSET(tessdata_path),          AV_OPT_TYPE_STRING, { .str = NULL },                      0,                  0,       FLAGS, NULL },
+    { NULL },
+};
+
+AVFILTER_DEFINE_CLASS(graphicsub2text);
+
+static const AVFilterPad inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .filter_frame = filter_frame,
+        .config_props = config_input,
+    },
+};
+
+static const AVFilterPad outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_SUBTITLE,
+        .config_props  = config_output,
+    },
+};
+
+const AVFilter ff_sf_graphicsub2text = {
+    .name          = "graphicsub2text",
+    .description   = NULL_IF_CONFIG_SMALL("Convert graphical subtitles to text subtitles via OCR"),
+    .init          = init,
+    .uninit        = uninit,
+    .priv_size     = sizeof(SubOcrContext),
+    .priv_class    = &graphicsub2text_class,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_QUERY_FUNC(query_formats),
+};
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v3 18/26] avfilter/subscale: Add filter for scaling and/or re-arranging graphical subtitles
  2022-01-20  3:25   ` [FFmpeg-devel] [PATCH v3 00/26] Subtitle Filtering 2022 ffmpegagent
                       ` (16 preceding siblings ...)
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 17/26] avfilter/graphicsub2text: Add new graphicsub2text filter (OCR) ffmpegagent
@ 2022-01-20  3:25     ` ffmpegagent
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 19/26] avfilter/subfeed: add subtitle feed filter ffmpegagent
                       ` (8 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  3:25 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 configure                 |   1 +
 doc/filters.texi          | 164 +++++++
 libavfilter/Makefile      |   1 +
 libavfilter/allfilters.c  |   1 +
 libavfilter/sf_subscale.c | 884 ++++++++++++++++++++++++++++++++++++++
 5 files changed, 1051 insertions(+)
 create mode 100644 libavfilter/sf_subscale.c

diff --git a/configure b/configure
index 59d3bb86f0..acb788edfc 100755
--- a/configure
+++ b/configure
@@ -3732,6 +3732,7 @@ sr_filter_deps="avformat swscale"
 sr_filter_select="dnn"
 stereo3d_filter_deps="gpl"
 splitcc_filter_deps="avcodec"
+subscale_filter_deps="swscale avcodec"
 subtitles_filter_deps="avformat avcodec libass"
 super2xsai_filter_deps="gpl"
 pixfmts_super2xsai_test_deps="super2xsai_filter"
diff --git a/doc/filters.texi b/doc/filters.texi
index 1c0a00c7ae..c9a1616d84 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -26354,6 +26354,170 @@ Set the rendering margin in pixels.
 For rendering, alway use the latest event only, which is covering the given point in time.
 @end table
 
+@section subscale
+
+Provides high-quality scaling and rearranging functionality for graphical subtitles.
+
+The subscale filter provides multiple approaches for manipulating
+the size and position of graphical subtitle rectangles wich can
+be combined or used separately.
+Scaling is performed by converting the palettized subtitle bitmaps
+to RGBA and re-quantization to palette colors afterwards via elbg algorithm.
+
+The two major operations are 'scale' and 're-arrange' with the
+latter being separated as 'arrange_h' and 'arrange_v'.
+
+
+Inputs:
+- 0: Subtitles [bitmap]
+
+Outputs:
+- 0: Subtitles [bitmap]
+
+It accepts the following parameters:
+
+@table @option
+
+@item w, width
+Set the width of the output.
+Width and height in case of graphical subtitles are just indicating
+a virtual size for which the output (consisting of 0-n bitmap rectangles)
+is intended to be displayed on.
+
+@item h, height
+Set the height of the output.
+
+@item margin_h
+Sets a horizontal margin to be preserverved when using any
+of the arrange modes.
+
+@item margin_v
+Sets a vertical margin to be preserverved when using any
+of the arrange modes.
+
+@item force_original_aspect_ratio
+Enable decreasing or increasing output video width or height if necessary to
+keep the original aspect ratio. Possible values:
+
+@table @samp
+@item disable
+Scale the video as specified and disable this feature.
+
+@item decrease
+The output video dimensions will automatically be decreased if needed.
+
+@item increase
+The output video dimensions will automatically be increased if needed.
+
+@end table
+
+
+@item scale_mode
+Specifies how subtitle bitmaps should be scaled.
+The scale factor is determined by the the factor between input
+and output size.
+
+@table @samp
+@item none
+Do not apply any common scaling.
+
+@item uniform
+Uniformly scale all subtitle bitmaps including their positions.
+
+@item uniform_no_reposition
+Uniformly scale all subtitle bitmaps without changing positions.
+
+@end table
+
+
+@item arrange_h
+Specifies how subtitle bitmaps should be arranged horizontally.
+
+@item arrange_v
+Specifies how subtitle bitmaps should be arranged vertically.
+
+
+@table @samp
+@item none
+Do not rearrange subtitle bitmaps.
+
+@item margin_no_scale
+Move subtitle bitmaps to be positioned inside the specified
+margin (margin_h or margin_v) when possible and without scaling.
+
+@item margin_and_scale
+Move subtitle bitmaps to be positioned inside the specified
+margin (margin_h or margin_v) and scale in case it doesn't fit.
+
+@item snapalign_no_scale
+Categorize subtitle bitmap positions as one of left/center/right
+or top/bottom/middle based on original positioning and apply
+these alignments for the target positioning.
+No scaling will be applied.
+
+@item snapalign_and_scale
+Categorize subtitle bitmap positions as one of left/center/right
+or top/bottom/middle based on original positioning and apply
+these alignments for the target positioning.
+Bitmaps that do not fit inside the margins borders are
+scaled to fit.
+@end table
+
+@item eval
+Set evaluation mode for the expressions (@option{width}, @option{height}).
+
+It accepts the following values:
+@table @samp
+@item init
+Evaluate expressions only once during the filter initialization.
+
+@item frame
+Evaluate expressions for each incoming frame. This is way slower than the
+@samp{init} mode since it requires all the scalers to be re-computed, but it
+allows advanced dynamic expressions.
+@end table
+
+Default value is @samp{init}.
+
+
+@item num_colors
+Set the number of palette colors for output images.
+Choose the maximum (256) when further processing is done (e.g.
+overlaying on a video).
+When subtitles will be encoded as bitmap subtitles (e.g. dvbsub),
+a smaller number of palette colors (e.g. 4-16) might need to be used, depending
+on the target format and codec.
+
+@item bitmap_width_align
+@item bitmap_height_align
+Make sure that subtitle bitmap sizes are a multiple of this
+value. Default is 2.
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Uniformly scale down video and bitmap subtitles and encode
+subtitles as dvbsub.
+@example
+ffmpeg -y -loglevel verbose -ss 32 -i "https://streams.videolan.org/samples/sub/largeres_vobsub.mkv" -filter_complex "[0:0]scale=480x270[vid1];[0:10]subscale=w=480:h=270:num_colors=16[sub1]" -map [vid1] -map [sub1] -c:s dvbsub -c:v libx265 output.ts
+@end example
+@item
+Squeeze video vertically and arrange subtitle bitmaps
+inside the video area without scaling, then overlay
+subtitles onto the video.
+@example
+ffmpeg -y -loglevel verbose -ss 32 -i "https://streams.videolan.org/samples/sub/largeres_vobsub.mkv" -filter_complex "[0:0]scale=1920x400,setsar=1[vid1];[0:10]subscale=w=1920:h=400:scale_mode=none:margin_h=50:arrange_h=margin_no_scale:arrange_v=margin_no_scale:margin_v=50:num_colors=256[sub1];[vid1][sub1]overlay_graphicsubs" -c:v libx265 output.ts
+@end example
+@item
+Scale both, a video and its embedded VOB subs, then encode them as separate streams into an MKV.
+@example
+ffmpeg -y -y -loglevel verbose -i "https://streams.videolan.org/samples/sub/largeres_vobsub.mkv" -filter_complex "[0:0]scale=720x576[vid1];[0:7]subscale=w=720:h=576:num_colors=16[sub1]" -map [vid1] -map [sub1] -c:s dvdsub out.mkv
+@end example
+@end itemize
+
 @c man end SUBTITLE FILTERS
 
 @chapter Multimedia Filters
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 1397351373..94160d1429 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -570,6 +570,7 @@ OBJS-$(CONFIG_SHOW_SPEAKER_FILTER)           += sf_textmod.o
 OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
 OBJS-$(CONFIG_SPLITCC_FILTER)                += sf_splitcc.o
 OBJS-$(CONFIG_STRIPSTYLES_FILTER)            += sf_stripstyles.o
+OBJS-$(CONFIG_SUBSCALE_FILTER)               += sf_subscale.o
 
 # multimedia filters
 OBJS-$(CONFIG_ABITSCOPE_FILTER)              += avf_abitscope.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 544eca616a..487e2bd6a8 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -553,6 +553,7 @@ extern const AVFilter ff_sf_graphicsub2text;
 extern const AVFilter ff_sf_showspeaker;
 extern const AVFilter ff_sf_splitcc;
 extern const AVFilter ff_sf_stripstyles;
+extern const AVFilter ff_sf_subscale;
 extern const AVFilter ff_sf_textmod;
 extern const AVFilter ff_svf_graphicsub2video;
 extern const AVFilter ff_svf_textsub2video;
diff --git a/libavfilter/sf_subscale.c b/libavfilter/sf_subscale.c
new file mode 100644
index 0000000000..04f0f16c27
--- /dev/null
+++ b/libavfilter/sf_subscale.c
@@ -0,0 +1,884 @@
+/*
+ * 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
+ * scale graphical subtitles filter
+ */
+
+#include <string.h>
+
+#include "drawutils.h"
+#include "internal.h"
+#include "scale_eval.h"
+#include "libavutil/eval.h"
+#include "libavutil/opt.h"
+#include "libswscale/swscale.h"
+
+#include "libavcodec/elbg.h"
+
+static const char *const var_names[] = {
+    "in_w",   "iw",
+    "in_h",   "ih",
+    "out_w",  "ow",
+    "out_h",  "oh",
+    "a",
+    "sar",
+    "dar",
+    "margin_h",
+    "margin_v",
+    NULL
+};
+
+enum var_name {
+    VAR_IN_W,   VAR_IW,
+    VAR_IN_H,   VAR_IH,
+    VAR_OUT_W,  VAR_OW,
+    VAR_OUT_H,  VAR_OH,
+    VAR_A,
+    VAR_SAR,
+    VAR_DAR,
+    VARS_B_H,
+    VARS_B_V,
+    VARS_NB
+};
+
+enum EvalMode {
+    EVAL_MODE_INIT,
+    EVAL_MODE_FRAME,
+    EVAL_MODE_NB
+};
+
+enum SubScaleMode {
+    SSM_NONE,
+    SSM_UNIFORM,
+    SSM_UNIFORM_NO_REPOSITION,
+};
+
+enum SubArrangeMode {
+    SAM_NONE,
+    SAM_ENSUREMARGIN_NO_SCALE,
+    SAM_ENSUREMARGIN_AND_SCALE,
+    SAM_SNAPALIGNMENT_NO_SCALE,
+    SAM_SNAPALIGNMENT_AND_SCALE,
+};
+
+typedef struct SubScaleContext {
+    const AVClass *class;
+    struct SwsContext *sws;
+    AVDictionary *opts;
+
+    int w, h;
+
+    char *w_expr;               ///< width  expression string
+    char *h_expr;               ///< height expression string
+    AVExpr *w_pexpr;
+    AVExpr *h_pexpr;
+    double var_values[VARS_NB];
+
+    int force_original_aspect_ratio;
+    int eval_mode;               ///< expression evaluation mode
+
+    int use_caching;
+
+    // Scale Options
+    enum SubScaleMode scale_mode;
+
+    // Arrange Options
+    enum SubArrangeMode arrange_mode_h;
+    enum SubArrangeMode arrange_mode_v;
+    int margin_h;
+    int margin_v;
+    char *margin_h_expr;
+    char *margin_v_expr;
+    AVExpr *margin_h_pexpr;
+    AVExpr *margin_v_pexpr;
+
+    // Bitmap Options
+    int num_output_colors;
+    int bitmap_width_align;
+    int bitmap_height_align;
+
+    // Color Quantization Fields
+    struct ELBGContext *ctx;
+    AVLFG lfg;
+    int *codeword;
+    int *codeword_closest_codebook_idxs;
+    int *codebook;
+    int r_idx, g_idx, b_idx, a_idx;
+    AVFrame *cache_frame;
+} SubScaleContext;
+
+
+static int config_output(AVFilterLink *outlink);
+
+static int check_exprs(AVFilterContext *ctx)
+{
+    const SubScaleContext *s = ctx->priv;
+    unsigned vars_w[VARS_NB] = { 0 }, vars_h[VARS_NB] = { 0 };
+
+    if (!s->w_pexpr && !s->h_pexpr)
+        return AVERROR(EINVAL);
+
+    if (s->w_pexpr)
+        av_expr_count_vars(s->w_pexpr, vars_w, VARS_NB);
+    if (s->h_pexpr)
+        av_expr_count_vars(s->h_pexpr, vars_h, VARS_NB);
+
+    if (vars_w[VAR_OUT_W] || vars_w[VAR_OW]) {
+        av_log(ctx, AV_LOG_ERROR, "Width expression cannot be self-referencing: '%s'.\n", s->w_expr);
+        return AVERROR(EINVAL);
+    }
+
+    if (vars_h[VAR_OUT_H] || vars_h[VAR_OH]) {
+        av_log(ctx, AV_LOG_ERROR, "Height expression cannot be self-referencing: '%s'.\n", s->h_expr);
+        return AVERROR(EINVAL);
+    }
+
+    if ((vars_w[VAR_OUT_H] || vars_w[VAR_OH]) &&
+        (vars_h[VAR_OUT_W] || vars_h[VAR_OW])) {
+        av_log(ctx, AV_LOG_WARNING, "Circular references detected for width '%s' and height '%s' - possibly invalid.\n", s->w_expr, s->h_expr);
+    }
+
+    if (s->margin_h_pexpr)
+        av_expr_count_vars(s->margin_h_pexpr, vars_w, VARS_NB);
+    if (s->margin_v_pexpr)
+        av_expr_count_vars(s->margin_v_pexpr, vars_h, VARS_NB);
+
+    return 0;
+}
+
+static int scale_parse_expr(AVFilterContext *ctx, char *str_expr, AVExpr **pexpr_ptr, const char *var, const char *args)
+{
+    SubScaleContext *s = ctx->priv;
+    int ret, is_inited = 0;
+    char *old_str_expr = NULL;
+    AVExpr *old_pexpr = NULL;
+
+    if (str_expr) {
+        old_str_expr = av_strdup(str_expr);
+        if (!old_str_expr)
+            return AVERROR(ENOMEM);
+        av_opt_set(s, var, args, 0);
+    }
+
+    if (*pexpr_ptr) {
+        old_pexpr = *pexpr_ptr;
+        *pexpr_ptr = NULL;
+        is_inited = 1;
+    }
+
+    ret = av_expr_parse(pexpr_ptr, args, var_names,
+                        NULL, NULL, NULL, NULL, 0, ctx);
+    if (ret < 0) {
+        av_log(ctx, AV_LOG_ERROR, "Cannot parse expression for %s: '%s'\n", var, args);
+        goto revert;
+    }
+
+    ret = check_exprs(ctx);
+    if (ret < 0)
+        goto revert;
+
+    if (is_inited && (ret = config_output(ctx->outputs[0])) < 0)
+        goto revert;
+
+    av_expr_free(old_pexpr);
+    av_freep(&old_str_expr);
+
+    return 0;
+
+revert:
+    av_expr_free(*pexpr_ptr);
+    *pexpr_ptr = NULL;
+    if (old_str_expr) {
+        av_opt_set(s, var, old_str_expr, 0);
+        av_free(old_str_expr);
+    }
+    if (old_pexpr)
+        *pexpr_ptr = old_pexpr;
+
+    return ret;
+}
+
+static av_cold int init_dict(AVFilterContext *ctx, AVDictionary **opts)
+{
+    SubScaleContext *s = ctx->priv;
+    uint8_t rgba_map[4];
+    int ret;
+
+    if (!s->w_expr)
+        av_opt_set(s, "w", "iw", 0);
+    if (!s->h_expr)
+        av_opt_set(s, "h", "ih", 0);
+
+    ret = scale_parse_expr(ctx, NULL, &s->w_pexpr, "width", s->w_expr);
+    if (ret < 0)
+        return ret;
+
+    ret = scale_parse_expr(ctx, NULL, &s->h_pexpr, "height", s->h_expr);
+    if (ret < 0)
+        return ret;
+
+    av_log(ctx, AV_LOG_VERBOSE, "w:%s h:%s\n",
+           s->w_expr, s->h_expr);
+
+    if (!s->margin_h_expr)
+        av_opt_set(s, "margin_h", "0", 0);
+    if (!s->margin_v_expr)
+        av_opt_set(s, "margin_v", "0", 0);
+
+    ret = scale_parse_expr(ctx, NULL, &s->margin_h_pexpr, "margin_h", s->margin_h_expr);
+    if (ret < 0)
+        return ret;
+
+    ret = scale_parse_expr(ctx, NULL, &s->margin_v_pexpr, "margin_v", s->margin_v_expr);
+    if (ret < 0)
+        return ret;
+
+    s->opts = *opts;
+    *opts = NULL;
+
+    ff_fill_rgba_map(&rgba_map[0], AV_PIX_FMT_RGB32);
+
+    s->r_idx = rgba_map[0]; // R
+    s->g_idx = rgba_map[1]; // G
+    s->b_idx = rgba_map[2]; // B
+    s->a_idx = rgba_map[3]; // A
+
+    av_lfg_init(&s->lfg, 123456789);
+
+
+    return 0;
+}
+
+static av_cold void uninit(AVFilterContext *ctx)
+{
+    SubScaleContext *s = ctx->priv;
+
+    av_frame_free(&s->cache_frame);
+
+    av_expr_free(s->w_pexpr);
+    av_expr_free(s->h_pexpr);
+    s->w_pexpr = s->h_pexpr = NULL;
+
+    av_expr_free(s->margin_h_pexpr);
+    av_expr_free(s->margin_v_pexpr);
+    s->margin_h_pexpr = s->margin_v_pexpr = NULL;
+
+    sws_freeContext(s->sws);
+    s->sws = NULL;
+    av_dict_free(&s->opts);
+
+    avpriv_elbg_free(&s->ctx);
+
+    av_freep(&s->codebook);
+    av_freep(&s->codeword);
+    av_freep(&s->codeword_closest_codebook_idxs);
+}
+
+static int config_input(AVFilterLink *inlink)
+{
+    ////const AVFilterContext *ctx = inlink->dst;
+    ////SubScaleContext *s = ctx->priv;
+
+    ////if (s->w <= 0 || s->h <= 0) {
+    ////    s->w = inlink->w;
+    ////    s->h = inlink->h;
+    ////}
+    return 0;
+}
+
+static int scale_eval_dimensions(AVFilterContext *ctx)
+{
+    SubScaleContext *s = ctx->priv;
+    const AVFilterLink *inlink = ctx->inputs[0];
+    char *expr;
+    int eval_w, eval_h, margin_h, margin_v;
+    int ret;
+    double res;
+
+    s->var_values[VAR_IN_W]  = s->var_values[VAR_IW] = inlink->w;
+    s->var_values[VAR_IN_H]  = s->var_values[VAR_IH] = inlink->h;
+    s->var_values[VAR_OUT_W] = s->var_values[VAR_OW] = NAN;
+    s->var_values[VAR_OUT_H] = s->var_values[VAR_OH] = NAN;
+    s->var_values[VARS_B_H]  = s->var_values[VARS_B_V] = 0;
+    s->var_values[VAR_A]     = (double) inlink->w / inlink->h;
+    s->var_values[VAR_SAR]   = inlink->sample_aspect_ratio.num ?
+        (double) inlink->sample_aspect_ratio.num / inlink->sample_aspect_ratio.den : 1;
+    s->var_values[VAR_DAR]   = s->var_values[VAR_A] * s->var_values[VAR_SAR];
+
+    res = av_expr_eval(s->w_pexpr, s->var_values, NULL);
+    s->var_values[VAR_OUT_W] = s->var_values[VAR_OW] = (int) res == 0 ? inlink->w : (int) res;
+
+    res = av_expr_eval(s->h_pexpr, s->var_values, NULL);
+    if (isnan(res)) {
+        expr = s->h_expr;
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+    eval_h = s->var_values[VAR_OUT_H] = s->var_values[VAR_OH] = (int) res == 0 ? inlink->h : (int) res;
+
+    res = av_expr_eval(s->w_pexpr, s->var_values, NULL);
+    if (isnan(res)) {
+        expr = s->w_expr;
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+    eval_w = s->var_values[VAR_OUT_W] = s->var_values[VAR_OW] = (int) res == 0 ? inlink->w : (int) res;
+
+    s->w = eval_w;
+    s->h = eval_h;
+
+    res = av_expr_eval(s->margin_h_pexpr, s->var_values, NULL);
+    s->var_values[VARS_B_H] = (int)res;
+
+    res = av_expr_eval(s->margin_v_pexpr, s->var_values, NULL);
+    if (isnan(res)) {
+        expr = s->margin_v_expr;
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+    margin_v = s->var_values[VARS_B_V] = (int)res;
+
+    res = av_expr_eval(s->margin_h_pexpr, s->var_values, NULL);
+    if (isnan(res)) {
+        expr = s->margin_h_expr;
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+    margin_h = s->var_values[VARS_B_H] = (int)res;
+
+    s->margin_h = margin_h;
+    s->margin_v = margin_v;
+
+    return 0;
+
+fail:
+    av_log(ctx, AV_LOG_ERROR,
+           "Error when evaluating the expression '%s'.\n", expr);
+    return ret;
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    AVFilterLink *inlink  = outlink->src->inputs[0];
+    SubScaleContext *s = ctx->priv;
+    int ret;
+
+    outlink->format = AV_SUBTITLE_FMT_BITMAP;
+    outlink->time_base = ctx->inputs[0]->time_base;
+    outlink->frame_rate = ctx->inputs[0]->frame_rate;
+
+    if ((ret = scale_eval_dimensions(ctx)) < 0)
+        goto fail;
+
+    ff_scale_adjust_dimensions(inlink, &s->w, &s->h,
+                               s->force_original_aspect_ratio, 2);
+
+    if (s->w > INT_MAX ||
+        s->h > INT_MAX ||
+        (s->h * inlink->w) > INT_MAX ||
+        (s->w * inlink->h) > INT_MAX)
+        av_log(ctx, AV_LOG_ERROR, "Rescaled value for width or height is too big.\n");
+
+    outlink->w = s->w;
+    outlink->h = s->h;
+
+    if (s->sws)
+        sws_freeContext(s->sws);
+
+    s->sws = sws_alloc_context();
+    if (!s->sws)
+        return AVERROR(ENOMEM);
+
+    av_opt_set_pixel_fmt(s->sws, "src_format", AV_PIX_FMT_PAL8, 0);
+    av_opt_set_int(s->sws, "dst_format", AV_PIX_FMT_RGB32, 0);
+    av_opt_set_int(s->sws, "threads", ff_filter_get_nb_threads(ctx), 0);
+
+    if (s->opts) {
+        const AVDictionaryEntry *e = NULL;
+        while ((e = av_dict_get(s->opts, "", e, AV_DICT_IGNORE_SUFFIX))) {
+            if ((ret = av_opt_set(s->sws, e->key, e->value, 0)) < 0)
+                return ret;
+        }
+    }
+
+    if ((ret = sws_init_context(s->sws, NULL, NULL)) < 0)
+        return ret;
+
+    if (inlink->sample_aspect_ratio.num){
+        outlink->sample_aspect_ratio = av_mul_q((AVRational){outlink->h * inlink->w, outlink->w * inlink->h}, inlink->sample_aspect_ratio);
+    } else
+        outlink->sample_aspect_ratio = inlink->sample_aspect_ratio;
+
+    av_log(ctx, AV_LOG_VERBOSE, "Output size set to %dx%d.\n", outlink->w, outlink->h);
+
+    return 0;
+fail:
+    return ret;
+}
+
+static int palettize_image(SubScaleContext *const s, const int w, const int h, const int src_linesize, uint8_t *src_data,
+                          int dst_linesize, uint8_t *dst_data, uint32_t *dst_pal)
+{
+    int k, ret;
+    const int codeword_length = w * h;
+
+    /* Re-Initialize */
+    s->codeword = av_realloc_f(s->codeword, codeword_length, 4 * sizeof(*s->codeword));
+    if (!s->codeword)
+        return AVERROR(ENOMEM);
+
+    s->codeword_closest_codebook_idxs =
+        av_realloc_f(s->codeword_closest_codebook_idxs, codeword_length,
+                     sizeof(*s->codeword_closest_codebook_idxs));
+    if (!s->codeword_closest_codebook_idxs)
+        return AVERROR(ENOMEM);
+
+    s->codebook = av_realloc_f(s->codebook, s->num_output_colors, 4 * sizeof(*s->codebook));
+    if (!s->codebook)
+        return AVERROR(ENOMEM);
+
+    /* build the codeword */
+    k = 0;
+    for (int i = 0; i < h; i++) {
+        uint8_t *p = src_data;
+        for (int j = 0; j < w; j++) {
+            s->codeword[k++] = p[s->b_idx];
+            s->codeword[k++] = p[s->g_idx];
+            s->codeword[k++] = p[s->r_idx];
+            s->codeword[k++] = p[s->a_idx];
+            p += 4;
+        }
+        src_data += src_linesize;
+    }
+
+    /* compute the codebook */
+    ret = avpriv_elbg_do(&s->ctx, s->codeword, 4,
+        codeword_length, s->codebook,
+        s->num_output_colors, 1,
+        s->codeword_closest_codebook_idxs, &s->lfg, 0);
+
+    if (ret < 0)
+        return ret;
+
+    /* Write Palette */
+    for (int i = 0; i < s->num_output_colors; i++) {
+        dst_pal[i] = s->codebook[i*4+3] << 24  |
+                    (s->codebook[i*4+2] << 16) |
+                    (s->codebook[i*4+1] <<  8) |
+                     s->codebook[i*4  ];
+    }
+
+    /* Write Image */
+    k = 0;
+    for (int i = 0; i < h; i++) {
+        uint8_t *p = dst_data;
+        for (int j = 0; j < w; j++, p++) {
+            p[0] = s->codeword_closest_codebook_idxs[k++];
+        }
+
+        dst_data += dst_linesize;
+    }
+
+    return ret;
+}
+
+static int rescale_size(int64_t a, AVRational factor)
+{
+    const int64_t res = av_rescale_rnd(a, factor.num, factor.den, AV_ROUND_NEAR_INF);
+    if (res > INT32_MAX || res < 0)
+        return 0;
+
+    return (int)res;
+}
+
+
+static int scale_area(AVFilterLink *link, AVSubtitleArea *area, const int target_width, const int target_height)
+{
+    const AVFilterContext *ctx = link->dst;
+    SubScaleContext *s = ctx->priv;
+    int ret;
+
+    AVBufferRef *dst_buffer;
+    const uint8_t* data[2]    = { area->buf[0]->data, (uint8_t *)&area->pal };
+    const int dstW            = FFALIGN(target_width, s->bitmap_width_align);
+    const int dstH            = FFALIGN(target_height, s->bitmap_height_align);
+    const int tmp_linesize[2] = { FFALIGN(dstW * 4, 32), 0 };
+    const int dst_linesize[2] = { dstW, 0 };
+    uint8_t* tmp[2] = { 0, 0 };
+
+    AVBufferRef *tmp_buffer = av_buffer_allocz(tmp_linesize[0] * dstH);
+    if (!tmp_buffer)
+        return AVERROR(ENOMEM);
+
+    if (!s->sws)
+        return 0;
+
+    tmp[0] = tmp_buffer->data;
+
+    s->sws = sws_getCachedContext(s->sws, area->w, area->h, AV_PIX_FMT_PAL8,
+        dstW, dstH, AV_PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL);
+    if (!s->sws) {
+        av_log(NULL, AV_LOG_FATAL, "Cannot initialize the conversion context. dstW=%d dstH=%d\n", dstW, dstH);
+        return AVERROR(EINVAL);
+    }
+
+    // Rescale to ARGB
+    ret = sws_scale(s->sws, data, area->linesize, 0, area->h, tmp, tmp_linesize);
+    if (ret < 0) {
+        av_buffer_unref(&tmp_buffer);
+        return ret;
+    }
+
+    // Alloc output buffer
+    dst_buffer = av_buffer_allocz(dst_linesize[0] * dstH);
+    if (!dst_buffer) {
+        av_buffer_unref(&tmp_buffer);
+        return AVERROR(ENOMEM);
+    }
+
+    // Quantize to palettized image
+    ret = palettize_image(s, dstW, dstH, tmp_linesize[0], tmp[0], dst_linesize[0], dst_buffer->data, area->pal);
+    av_buffer_unref(&tmp_buffer);
+
+    if (ret < 0) {
+        av_buffer_unref(&dst_buffer);
+        return ret;
+    }
+
+    av_buffer_unref(&area->buf[0]);
+    ret = av_buffer_replace(&area->buf[0], dst_buffer);
+    if (ret < 0) {
+        av_buffer_unref(&dst_buffer);
+        return ret;
+    }
+
+    area->w = dstW;
+    area->h = dstH;
+    area->linesize[0] = dst_linesize[0];
+    area->nb_colors = s->num_output_colors;
+
+    return ret;
+}
+
+static int process_area(AVFilterLink *inlink, AVSubtitleArea *area, AVRational x_factor, AVRational y_factor)
+{
+    AVFilterContext *ctx     = inlink->dst;
+    const SubScaleContext *s = ctx->priv;
+    int target_w, target_h, target_x, target_y;
+    const int border_l = s->margin_h;
+    const int border_r = s->w - s->margin_h;
+    const int border_t = s->margin_v;
+    const int border_b = s->h - s->margin_v;
+
+    av_log(ctx, AV_LOG_DEBUG, "process_area -  start: x/y: (%d:%d) size: %dx%d scale_mode: %d x-factor: %d:%d y-factor: %d:%d\n",
+        area->x, area->y, area->w, area->h, s->scale_mode, x_factor.num, x_factor.den, y_factor.num, y_factor.den);
+
+    switch (s->scale_mode) {
+    case SSM_NONE:
+        target_w = area->w;
+        target_h = area->h;
+        target_x = area->x;
+        target_y = area->y;
+        break;
+    case SSM_UNIFORM:
+        target_w = rescale_size(area->w, x_factor);
+        target_h = rescale_size(area->h, y_factor);
+        target_x = rescale_size(area->x, x_factor);
+        target_y = rescale_size(area->y, y_factor);
+        break;
+    case SSM_UNIFORM_NO_REPOSITION:
+        target_w = rescale_size(area->w, x_factor);
+        target_h = rescale_size(area->h, y_factor);
+        target_x = area->x;
+        target_y = area->y;
+        break;
+    default:
+        av_log(ctx, AV_LOG_ERROR, "Invalid scale_mode: %d\n", s->scale_mode);
+        return AVERROR(EINVAL);
+    }
+
+    av_log(ctx, AV_LOG_DEBUG, "process_area - scaled: x/y: (%d:%d) size: %dx%d.\n", target_x, target_y, target_w, target_h);
+
+
+    switch (s->arrange_mode_h) {
+    case SAM_ENSUREMARGIN_AND_SCALE:
+    case SAM_SNAPALIGNMENT_AND_SCALE:
+        {
+            // IF it doesn't fit - scale it
+            int max_width = s->w - 2 * s->margin_h;
+
+            if (max_width < 2)
+                max_width = 2;
+
+            if (target_w > max_width) {
+                target_h = (int)av_rescale(target_h, max_width, target_w);
+                target_w = max_width;
+                target_x = s->margin_h;
+            }
+        }
+        break;
+    }
+
+    switch (s->arrange_mode_h) {
+    case SAM_NONE:
+        break;
+    case SAM_ENSUREMARGIN_NO_SCALE:
+    case SAM_ENSUREMARGIN_AND_SCALE:
+        // Left border
+        if (target_x < border_l)
+            target_x = border_l;
+
+        // Right border
+        if (target_x + target_w > border_r)
+            target_x = border_r - target_w;
+
+        break;
+    case SAM_SNAPALIGNMENT_NO_SCALE:
+    case SAM_SNAPALIGNMENT_AND_SCALE:
+        {
+            // Use original values to detect alignment
+            const int left_margin          = area->x;
+            const int right_margin         = inlink->w - area->x - area->w;
+            const AVRational diff_factor_r = { left_margin - right_margin, area->w };
+            const float diff_factor        = (float)av_q2d(diff_factor_r);
+
+            if (diff_factor > 0.2f) {
+                // Right aligned
+                target_x = border_r - target_w;
+            } else if (diff_factor < -0.2f) {
+                // Left aligned
+                target_x = border_l;
+            } else {
+                // Centered
+                target_x = (inlink->w - area->w) / 2;
+            }
+        }
+
+        break;
+    default:
+        av_log(ctx, AV_LOG_ERROR, "Invalid arrange_mode_h: %d\n", s->arrange_mode_h);
+        return AVERROR(EINVAL);
+    }
+
+    av_log(ctx, AV_LOG_DEBUG, "process_area -  arr_h: x/y: (%d:%d) size: %dx%d.\n", target_x, target_y, target_w, target_h);
+
+
+    switch (s->arrange_mode_v) {
+    case SAM_ENSUREMARGIN_AND_SCALE:
+    case SAM_SNAPALIGNMENT_AND_SCALE:
+        {
+            // IF it doesn't fit - scale it
+            int max_height = s->h - 2 * s->margin_v;
+
+            if (max_height < 2)
+                max_height = 2;
+
+            if (target_h > max_height) {
+                target_w = (int)av_rescale(target_w, max_height, target_h);
+                target_h = max_height;
+                target_y = s->margin_v;
+            }
+        }
+        break;
+    }
+
+    switch (s->arrange_mode_v) {
+    case SAM_NONE:
+        break;
+    case SAM_ENSUREMARGIN_NO_SCALE:
+    case SAM_ENSUREMARGIN_AND_SCALE:
+        // Top border
+        if (target_y < border_t)
+            target_y = border_t;
+
+        // Bottom border
+        if (target_y + target_h > border_b)
+            target_y = border_b - target_h;
+
+        break;
+    case SAM_SNAPALIGNMENT_NO_SCALE:
+    case SAM_SNAPALIGNMENT_AND_SCALE:
+        {
+            // Use original values to detect alignment
+            const int top_margin           = area->y;
+            const int bottom_margin        = inlink->h - area->y - area->h;
+            const AVRational diff_factor_r = { top_margin - bottom_margin, area->h };
+            const float diff_factor        = (float)av_q2d(diff_factor_r);
+
+            if (diff_factor > 0.2f) {
+                // Bottom aligned
+                target_y = border_b - target_h;
+            } else if (diff_factor < -0.2f) {
+                // Top aligned
+                target_y = border_t;
+            } else {
+                // Centered
+                target_y = (inlink->h - area->h) / 2;
+            }
+        }
+
+        break;
+    default:
+        av_log(ctx, AV_LOG_ERROR, "Invalid arrange_mode_v: %d\n", s->arrange_mode_v);
+        return AVERROR(EINVAL);
+    }
+
+    av_log(ctx, AV_LOG_VERBOSE, "process_area -  arr_v: x/y: (%d:%d) size: %dx%d.\n", target_x, target_y, target_w, target_h);
+
+    area->x = target_x;
+    area->y = target_y;
+
+    if (area->w != target_w || area->h != target_h)
+        return scale_area(inlink, area, target_w, target_h);
+
+    return 0;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    const AVFilterContext *ctx = inlink->dst;
+    SubScaleContext *s = ctx->priv;
+    AVFilterLink *outlink = ctx->outputs[0];
+    int ret;
+
+    // just forward empty frames
+    if (frame->num_subtitle_areas == 0) {
+        av_frame_free(&s->cache_frame);
+        return ff_filter_frame(outlink, frame);
+    }
+
+    if (s->use_caching && s->cache_frame && frame->repeat_sub
+        && s->cache_frame->subtitle_timing.start_pts == frame->subtitle_timing.start_pts) {
+        AVFrame *out = av_frame_clone(s->cache_frame);
+        if (!out)
+            return AVERROR(ENOMEM);
+
+        ret = av_frame_copy_props(out, frame);
+        if (ret < 0)
+            return ret;
+
+        av_log(inlink->dst, AV_LOG_DEBUG, "subscale CACHED - size %dx%d  pts: %"PRId64"  areas: %d\n", frame->width, frame->height, frame->subtitle_timing.start_pts, frame->num_subtitle_areas);
+        av_frame_free(&frame);
+        return ff_filter_frame(outlink, out);
+    }
+
+    ret = av_frame_make_writable(frame);
+    if (ret >= 0) {
+        const AVRational x_factor = { .num = outlink->w, .den = inlink->w} ;
+        const AVRational y_factor = { .num = outlink->h, .den = inlink->h} ;
+
+        for (unsigned i = 0; i < frame->num_subtitle_areas; ++i) {
+            AVSubtitleArea *area = frame->subtitle_areas[i];
+
+            ret = process_area(inlink, area, x_factor, y_factor);
+            if (ret < 0)
+                return ret;
+        }
+
+        av_log(inlink->dst, AV_LOG_DEBUG, "subscale output - size %dx%d  pts: %"PRId64"  areas: %d\n", frame->width, frame->height, frame->subtitle_timing.start_pts, frame->num_subtitle_areas);
+
+        if (s->use_caching) {
+            av_frame_free(&s->cache_frame);
+            s->cache_frame = av_frame_clone(frame);
+        }
+
+        return ff_filter_frame(outlink, frame);
+    }
+
+    return ret;
+}
+
+#define OFFSET(x) offsetof(SubScaleContext, x)
+#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption subscale_options[] = {
+    { "margin_h", "horizontal border",        OFFSET(margin_h_expr),    AV_OPT_TYPE_STRING,        .flags = FLAGS },
+    { "margin_v", "vertical border",          OFFSET(margin_v_expr),    AV_OPT_TYPE_STRING,        .flags = FLAGS },
+    { "w",     "Output video width",          OFFSET(w_expr),    AV_OPT_TYPE_STRING,        .flags = FLAGS },
+    { "width", "Output video width",          OFFSET(w_expr),    AV_OPT_TYPE_STRING,        .flags = FLAGS },
+    { "h",     "Output video height",         OFFSET(h_expr),    AV_OPT_TYPE_STRING,        .flags = FLAGS },
+    { "height","Output video height",         OFFSET(h_expr),    AV_OPT_TYPE_STRING,        .flags = FLAGS },
+    { "force_original_aspect_ratio", "decrease or increase w/h if necessary to keep the original AR", OFFSET(force_original_aspect_ratio), AV_OPT_TYPE_INT, { .i64 = 0}, 0, 2, FLAGS, "force_oar" },
+    { "disable",  NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, 0, 0, FLAGS, "force_oar" },
+    { "decrease", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 1 }, 0, 0, FLAGS, "force_oar" },
+    { "increase", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 2 }, 0, 0, FLAGS, "force_oar" },
+    { "scale_mode", "specify how to scale subtitles", OFFSET(scale_mode), AV_OPT_TYPE_INT, {.i64 = SSM_UNIFORM}, 0, SSM_UNIFORM_NO_REPOSITION, FLAGS, "scale_mode" },
+         { "none",  "no common scaling", 0, AV_OPT_TYPE_CONST, {.i64=SSM_NONE},  .flags = FLAGS, .unit = "scale_mode" },
+         { "uniform",  "uniformly scale and reposition", 0, AV_OPT_TYPE_CONST, {.i64=SSM_UNIFORM},  .flags = FLAGS, .unit = "scale_mode" },
+         { "uniform_no_reposition",  "uniformly scale but keep positions", 0, AV_OPT_TYPE_CONST, {.i64=SSM_UNIFORM_NO_REPOSITION},  .flags = FLAGS, .unit = "scale_mode" },
+    { "use_caching", "Cache output frames",   OFFSET(use_caching), AV_OPT_TYPE_BOOL, { .i64 = 1}, 0, 1, .flags = FLAGS },
+
+    { "arrange_h", "specify how to arrange subtitles horizontally", OFFSET(arrange_mode_h), AV_OPT_TYPE_INT, {.i64 = SAM_NONE}, 0, SAM_SNAPALIGNMENT_AND_SCALE, FLAGS, "arrange" },
+    { "arrange_v", "specify how to arrange subtitles vertically", OFFSET(arrange_mode_v), AV_OPT_TYPE_INT, {.i64 = SAM_NONE}, 0, SAM_SNAPALIGNMENT_AND_SCALE, FLAGS, "arrange" },
+         { "none",  "no repositioning", 0, AV_OPT_TYPE_CONST, {.i64=SAM_NONE},  .flags = FLAGS, .unit = "arrange" },
+         { "margin_no_scale",  "move subs inside border when possible", 0, AV_OPT_TYPE_CONST, {.i64=SAM_ENSUREMARGIN_NO_SCALE,},  .flags = FLAGS, .unit = "arrange" },
+         { "margin_and_scale",  "move subs inside border and scale as needed", 0, AV_OPT_TYPE_CONST, {.i64=SAM_ENSUREMARGIN_AND_SCALE,},  .flags = FLAGS, .unit = "arrange" },
+         { "snapalign_no_scale",  "snap subs to near/far/center when possible", 0, AV_OPT_TYPE_CONST, {.i64=SAM_SNAPALIGNMENT_NO_SCALE,},  .flags = FLAGS, .unit = "arrange" },
+         { "snapalign_and_scale", "snap subs to near/far/center and scale as needed", 0, AV_OPT_TYPE_CONST, {.i64=SAM_SNAPALIGNMENT_AND_SCALE,}, .flags = FLAGS, .unit = "arrange" },
+
+    { "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, {.i64 = EVAL_MODE_INIT}, 0, EVAL_MODE_NB-1, FLAGS, "eval" },
+         { "init",  "eval expressions once during initialization", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_INIT},  .flags = FLAGS, .unit = "eval" },
+         { "frame", "eval expressions during initialization and per-frame", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_FRAME}, .flags = FLAGS, .unit = "eval" },
+    { "num_colors",     "number of palette colors in output", OFFSET(num_output_colors),  AV_OPT_TYPE_INT, {.i64 = 256 }, 2, 256, .flags = FLAGS },
+    { "bitmap_width_align",     "Bitmap width alignment", OFFSET(bitmap_width_align),  AV_OPT_TYPE_INT, {.i64 = 2 }, 1, 256, .flags = FLAGS },
+    { "bitmap_height_align",     "Bitmap height alignment", OFFSET(bitmap_height_align),  AV_OPT_TYPE_INT, {.i64 = 2 }, 1, 256, .flags = FLAGS },
+    { .name =  NULL }
+};
+
+static const AVClass subscale_class = {
+    .class_name       = "subscale",
+    .item_name        = av_default_item_name,
+    .option           = subscale_options,
+    .version          = LIBAVUTIL_VERSION_INT,
+    .category         = AV_CLASS_CATEGORY_FILTER,
+};
+
+static const AVFilterPad inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .filter_frame = filter_frame,
+        .config_props = config_input,
+    },
+};
+
+static const AVFilterPad outputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .config_props = config_output,
+    },
+};
+
+const AVFilter ff_sf_subscale = {
+    .name            = "subscale",
+    .description     = NULL_IF_CONFIG_SMALL("Scale graphical subtitles."),
+    .init_dict       = init_dict,
+    .uninit          = uninit,
+    .priv_size       = sizeof(SubScaleContext),
+    .priv_class      = &subscale_class,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_SINGLE_SUBFMT(AV_SUBTITLE_FMT_BITMAP),
+};
+
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v3 19/26] avfilter/subfeed: add subtitle feed filter
  2022-01-20  3:25   ` [FFmpeg-devel] [PATCH v3 00/26] Subtitle Filtering 2022 ffmpegagent
                       ` (17 preceding siblings ...)
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 18/26] avfilter/subscale: Add filter for scaling and/or re-arranging graphical subtitles ffmpegagent
@ 2022-01-20  3:25     ` ffmpegagent
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 20/26] avcodec/subtitles: Migrate subtitle encoders to frame-based API ffmpegagent
                       ` (7 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  3:25 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavfilter/Makefile     |   1 +
 libavfilter/allfilters.c |   1 +
 libavfilter/sf_subfeed.c | 366 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 368 insertions(+)
 create mode 100644 libavfilter/sf_subfeed.c

diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 94160d1429..ad8a5a9d18 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -571,6 +571,7 @@ OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
 OBJS-$(CONFIG_SPLITCC_FILTER)                += sf_splitcc.o
 OBJS-$(CONFIG_STRIPSTYLES_FILTER)            += sf_stripstyles.o
 OBJS-$(CONFIG_SUBSCALE_FILTER)               += sf_subscale.o
+OBJS-$(CONFIG_SUBFEED_FILTER)                += sf_subfeed.o
 
 # multimedia filters
 OBJS-$(CONFIG_ABITSCOPE_FILTER)              += avf_abitscope.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 487e2bd6a8..fe1aec093b 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -554,6 +554,7 @@ extern const AVFilter ff_sf_showspeaker;
 extern const AVFilter ff_sf_splitcc;
 extern const AVFilter ff_sf_stripstyles;
 extern const AVFilter ff_sf_subscale;
+extern const AVFilter ff_sf_subfeed;
 extern const AVFilter ff_sf_textmod;
 extern const AVFilter ff_svf_graphicsub2video;
 extern const AVFilter ff_svf_textsub2video;
diff --git a/libavfilter/sf_subfeed.c b/libavfilter/sf_subfeed.c
new file mode 100644
index 0000000000..7df6641f6a
--- /dev/null
+++ b/libavfilter/sf_subfeed.c
@@ -0,0 +1,366 @@
+/*
+ * 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
+ * subtitle filter for feeding subtitle frames into a filtergraph in a contiguous way
+ *
+ *
+ * also supports
+ *   - duration fixup
+ *     delaying a subtitle event with unknown duration and infer duration from the
+ *     start time of the subsequent subtitle
+ *   - scattering
+ *     splitting a subtitle event with unknown duration into multiple ones with
+ *     a short and fixed duration
+ *
+ */
+
+#include "filters.h"
+#include "libavutil/opt.h"
+#include "subtitles.h"
+#include "libavutil/avassert.h"
+
+enum SubFeedMode {
+    FM_REPEAT,
+    FM_SCATTER,
+    FM_FORWARD,
+};
+
+typedef struct SubFeedContext {
+    const AVClass *class;
+    enum AVSubtitleType format;
+    enum SubFeedMode mode;
+
+    AVRational frame_rate;
+    int fix_durations;
+    int fix_overlap;
+
+    int current_frame_isnew;
+    int eof;
+    int got_first_input;
+    int need_frame;
+    int64_t next_pts_offset;
+    int64_t recent_subtitle_pts;
+
+    int64_t counter;
+
+    /**
+     * Queue of frames waiting to be filtered.
+     */
+    FFFrameQueue fifo;
+
+} SubFeedContext;
+
+static int64_t ms_to_avtb(int64_t ms)
+{
+    return av_rescale_q(ms, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q);
+}
+
+static int64_t avtb_to_ms(int64_t avtb)
+{
+    return av_rescale_q(avtb, AV_TIME_BASE_Q, (AVRational){ 1, 1000 });
+}
+
+static int init(AVFilterContext *ctx)
+{
+    SubFeedContext *s = ctx->priv;
+
+    ff_framequeue_init(&s->fifo, NULL);
+
+    return 0;
+}
+
+static void uninit(AVFilterContext *ctx)
+{
+    SubFeedContext *s = ctx->priv;
+    ff_framequeue_free(&s->fifo);
+}
+
+static int config_input(AVFilterLink *link)
+{
+    ////const subfeedContext *context = link->dst->priv;
+
+    return 0;
+}
+
+static int query_formats(AVFilterContext *ctx)
+{
+    AVFilterFormats *formats;
+    AVFilterLink *inlink0 = ctx->inputs[0];
+    AVFilterLink *outlink0 = ctx->outputs[0];
+    static const enum AVSubtitleType subtitle_fmts[] = { AV_SUBTITLE_FMT_BITMAP, AV_SUBTITLE_FMT_ASS, AV_SUBTITLE_FMT_NB };
+    int ret;
+
+    formats = ff_make_format_list(subtitle_fmts);
+
+    if ((ret = ff_formats_ref(formats, &inlink0->outcfg.formats)) < 0)
+        return ret;
+
+    if ((ret = ff_formats_ref(formats, &outlink0->incfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    SubFeedContext *s = outlink->src->priv;
+    const AVFilterLink *inlink = outlink->src->inputs[0];
+
+    outlink->time_base = AV_TIME_BASE_Q;
+    outlink->format = inlink->format;
+    outlink->w = inlink->w;
+    outlink->h = inlink->h;
+
+    if (s->mode == FM_FORWARD)
+        outlink->frame_rate = (AVRational) { 1, 0 };
+    else
+        outlink->frame_rate = s->frame_rate;
+
+    return 0;
+}
+
+static int request_frame(AVFilterLink *outlink)
+{
+    SubFeedContext *s = outlink->src->priv;
+    AVFilterLink *inlink = outlink->src->inputs[0];
+    int64_t last_pts = outlink->current_pts;
+    int64_t next_pts;
+    int64_t interval = ms_to_avtb((int64_t)(av_q2d(av_inv_q(outlink->frame_rate)) * 1000));
+    AVFrame *out;
+    int status;
+
+    if (s->mode == FM_FORWARD)
+        return ff_request_frame(inlink);
+
+    s->counter++;
+    if (interval == 0)
+        interval =  ms_to_avtb(200);
+
+    status = ff_outlink_get_status(inlink);
+    if (status == AVERROR_EOF)
+        s->eof = 1;
+
+    if (s->eof)
+        return AVERROR_EOF;
+
+    if (!s->got_first_input && inlink->current_pts != AV_NOPTS_VALUE) {
+
+        s->got_first_input = 1;
+        next_pts = av_rescale_q(inlink->current_pts, inlink->time_base, AV_TIME_BASE_Q);
+        if (next_pts < last_pts)
+            next_pts = last_pts + interval;
+
+    } else if (last_pts == AV_NOPTS_VALUE)
+        next_pts = av_rescale_q(inlink->current_pts, inlink->time_base, AV_TIME_BASE_Q);
+    else
+        next_pts = last_pts + interval;
+
+    if (next_pts == AV_NOPTS_VALUE)
+        next_pts = 0;
+
+    if (s->next_pts_offset) {
+        next_pts -= s->next_pts_offset;
+        s->next_pts_offset = 0;
+    }
+
+retry:
+    if (ff_framequeue_queued_frames(&s->fifo) && !s->current_frame_isnew) {
+
+        const AVFrame *current_frame = ff_framequeue_peek(&s->fifo, 0);
+        const int64_t sub_end_time   = current_frame->subtitle_timing.start_pts + current_frame->subtitle_timing.duration;
+
+        if (next_pts > sub_end_time) {
+            AVFrame *remove_frame = ff_framequeue_take(&s->fifo);
+            av_frame_free(&remove_frame);
+
+            if (ff_framequeue_queued_frames(&s->fifo)) {
+                s->current_frame_isnew = 1;
+                goto retry;
+            }
+        }
+    }
+
+    if (ff_framequeue_queued_frames(&s->fifo)) {
+        AVFrame *current_frame = ff_framequeue_peek(&s->fifo, 0);
+
+        if (current_frame && current_frame->subtitle_timing.start_pts <= next_pts + interval) {
+            if (!s->current_frame_isnew)
+                current_frame->repeat_sub++;
+
+            out = av_frame_clone(current_frame);
+
+            if (!out)
+                return AVERROR(ENOMEM);
+
+            if (!s->current_frame_isnew) {
+                out->pts = next_pts;
+            } else {
+                out->pts = out->subtitle_timing.start_pts;
+
+                if (out->pts < next_pts)
+                    out->pts = next_pts;
+
+                s->next_pts_offset = (out->pts - next_pts) % interval;
+            }
+
+            if (s->mode == FM_SCATTER) {
+                const int64_t sub_end_time  = current_frame->subtitle_timing.start_pts + current_frame->subtitle_timing.duration;
+
+                if (s->current_frame_isnew == 1 && current_frame->subtitle_timing.start_pts < out->pts) {
+                    const int64_t diff = out->pts - current_frame->subtitle_timing.start_pts;
+                    current_frame->subtitle_timing.duration -= diff;
+                }
+
+                out->repeat_sub = 0;
+                out->subtitle_timing.start_pts = out->pts;
+                out->subtitle_timing.duration = interval;
+                av_assert1(out->pts >= next_pts);
+                av_assert1(out->pts < next_pts + interval);
+                av_assert1(out->pts < sub_end_time);
+
+                if (out->pts > next_pts)
+                    out->subtitle_timing.duration -= out->pts - next_pts;
+
+                if (sub_end_time < next_pts + interval) {
+                    const int64_t diff = next_pts + interval - sub_end_time;
+                    av_assert1(diff <= out->subtitle_timing.duration);
+                    out->subtitle_timing.duration -= diff;
+                }
+            }
+
+            s->current_frame_isnew = 0;
+            s->recent_subtitle_pts = out->subtitle_timing.start_pts;
+
+            return ff_filter_frame(outlink, out);
+        }
+    }
+
+    if (ff_framequeue_queued_frames(&s->fifo) == 0) {
+        status = ff_request_frame(inlink);
+        if (status == AVERROR_EOF) {
+            s->eof = 1;
+            return status;
+        }
+
+        if (s->counter > 1 && s->counter % 2)
+            return 0;
+    }
+
+    out = ff_get_subtitles_buffer(outlink, outlink->format);
+    out->pts = next_pts;
+    out->repeat_sub = 1;
+    out->subtitle_timing.start_pts = s->recent_subtitle_pts;
+
+    return ff_filter_frame(outlink, out);
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFilterContext *ctx        = inlink->dst;
+    SubFeedContext *s           = inlink->dst->priv;
+    AVFilterLink *outlink       = inlink->dst->outputs[0];
+    const int64_t index         = (int64_t)ff_framequeue_queued_frames(&s->fifo) - 1;
+    int ret = 0;
+
+    frame->pts = av_rescale_q(frame->pts, inlink->time_base, AV_TIME_BASE_Q);
+
+    if (index < 0) {
+        s->current_frame_isnew = 1;
+    } else if (s->fix_durations || s->fix_overlap) {
+        AVFrame *previous_frame = ff_framequeue_peek(&s->fifo, index);
+        const int64_t pts_diff = frame->subtitle_timing.start_pts - previous_frame->subtitle_timing.start_pts;
+
+        if (s->fix_durations && pts_diff > 0 && previous_frame->subtitle_timing.duration > ms_to_avtb(29000))
+                    previous_frame->subtitle_timing.duration = frame->subtitle_timing.start_pts - previous_frame->subtitle_timing.start_pts;
+
+        if (s->fix_overlap && pts_diff > 0 && previous_frame->subtitle_timing.duration > pts_diff)
+                    previous_frame->subtitle_timing.duration = pts_diff;
+    }
+
+    ff_framequeue_add(&s->fifo, frame);
+
+    if (index > 3)
+        av_log(ctx, AV_LOG_WARNING, "frame queue count: %d\n", (int)index);
+
+    if (s->mode == FM_FORWARD && ff_framequeue_queued_frames(&s->fifo)) {
+
+        AVFrame *first_frame = ff_framequeue_peek(&s->fifo, 0);
+
+        if (s->fix_overlap && ff_framequeue_queued_frames(&s->fifo) < 2)
+            return 0;
+
+        if (s->fix_durations && first_frame->subtitle_timing.duration > ms_to_avtb(29000))
+            return 0;
+
+        first_frame = ff_framequeue_take(&s->fifo);
+        return ff_filter_frame(outlink, first_frame);
+    }
+
+    return ret;
+}
+
+#define OFFSET(x) offsetof(SubFeedContext, x)
+#define FLAGS (AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption subfeed_options[] = {
+    { "fix_durations", "delay output and determine duration from next frame", OFFSET(fix_durations),   AV_OPT_TYPE_BOOL,   { .i64=1 },                  0, 1, FLAGS, NULL     },
+    { "fix_overlap", "delay output and adjust durations to prevent overlap", OFFSET(fix_overlap),   AV_OPT_TYPE_BOOL,   { .i64=0 },                  0, 1, FLAGS, NULL     },
+    { "mode",       "set feed mode",         OFFSET(mode),      AV_OPT_TYPE_INT,                 { .i64=FM_REPEAT },  FM_REPEAT, FM_FORWARD,  FLAGS, "mode" },
+    {   "repeat",     "repeat recent while valid, send empty otherwise",   0, AV_OPT_TYPE_CONST, { .i64=FM_REPEAT },  0,                  0,  FLAGS, "mode" },
+    {   "scatter",    "subdivide subtitles into 1/framerate segments",     0, AV_OPT_TYPE_CONST, { .i64=FM_SCATTER }, 0,                  0,  FLAGS, "mode" },
+    {   "forward",    "forward only (clears output framerate)",            0, AV_OPT_TYPE_CONST, { .i64=FM_FORWARD }, 0,                  0,  FLAGS, "mode" },
+    { "rate",       "output frame rate",     OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE,         { .str = "5"},       0,         INT_MAX,     FLAGS, NULL },\
+    { "r",          "output frame rate",     OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE,         { .str = "5"},       0,         INT_MAX,     FLAGS, NULL },\
+    { NULL },
+};
+
+AVFILTER_DEFINE_CLASS(subfeed);
+
+static const AVFilterPad inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .filter_frame = filter_frame,
+        .config_props = config_input,
+    },
+};
+
+static const AVFilterPad outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_SUBTITLE,
+        .request_frame = request_frame,
+        .config_props  = config_output,
+    },
+};
+
+const AVFilter ff_sf_subfeed = {
+    .name           = "subfeed",
+    .description    = NULL_IF_CONFIG_SMALL("Control subtitle frame timing and flow in a filtergraph"),
+    .init           = init,
+    .uninit         = uninit,
+    .priv_size      = sizeof(SubFeedContext),
+    .priv_class     = &subfeed_class,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_QUERY_FUNC(query_formats),
+};
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v3 20/26] avcodec/subtitles: Migrate subtitle encoders to frame-based API
  2022-01-20  3:25   ` [FFmpeg-devel] [PATCH v3 00/26] Subtitle Filtering 2022 ffmpegagent
                       ` (18 preceding siblings ...)
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 19/26] avfilter/subfeed: add subtitle feed filter ffmpegagent
@ 2022-01-20  3:25     ` ffmpegagent
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 21/26] fftools/ffmpeg: Introduce subtitle filtering and new frame-based subtitle encoding ffmpegagent
                       ` (6 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  3:25 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

and provide a compatibility shim for the legacy api

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/assenc.c        | 189 ++++++++++++++++++++++++++++++-------
 libavcodec/avcodec.h       |   5 +-
 libavcodec/dvbsubenc.c     |  96 ++++++++++---------
 libavcodec/dvdsubenc.c     | 102 ++++++++++++--------
 libavcodec/encode.c        |  61 +++++++++++-
 libavcodec/movtextenc.c    | 114 ++++++++++++++++------
 libavcodec/srtenc.c        | 108 ++++++++++++++-------
 libavcodec/tests/avcodec.c |   2 -
 libavcodec/ttmlenc.c       | 101 +++++++++++++++-----
 libavcodec/webvttenc.c     |  86 ++++++++++++-----
 libavcodec/xsubenc.c       |  88 ++++++++++-------
 11 files changed, 687 insertions(+), 265 deletions(-)

diff --git a/libavcodec/assenc.c b/libavcodec/assenc.c
index b0e475834b..e1401b1ac5 100644
--- a/libavcodec/assenc.c
+++ b/libavcodec/assenc.c
@@ -22,70 +22,195 @@
 #include <string.h>
 
 #include "avcodec.h"
+#include "encode.h"
 #include "libavutil/ass_internal.h"
 #include "internal.h"
 #include "libavutil/avstring.h"
 #include "libavutil/internal.h"
 #include "libavutil/mem.h"
 
+typedef struct {
+    AVCodecContext *avctx;
+    AVFrame* current_frame;
+    int have_frame;
+    int current_area;
+} AssEncContext;
+
+static void check_write_header(AVCodecContext* avctx, const AVFrame* frame)
+{
+    if (avctx->extradata_size)
+        return;
+
+    if (frame->subtitle_header && frame->subtitle_header->size > 0) {
+        const char* subtitle_header = (char*)frame->subtitle_header->data;
+        avctx->extradata_size = strlen(subtitle_header);
+        avctx->extradata = av_mallocz(frame->subtitle_header->size + 1);
+        memcpy(avctx->extradata, subtitle_header, avctx->extradata_size);
+        avctx->extradata[avctx->extradata_size] = 0;
+    }
+
+    if (!avctx->extradata_size) {
+        const char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+        if (!subtitle_header)
+            return;
+
+        avctx->extradata_size = strlen(subtitle_header);
+        avctx->extradata = av_mallocz(avctx->extradata_size + 1);
+        memcpy(avctx->extradata, subtitle_header, avctx->extradata_size);
+        avctx->extradata[avctx->extradata_size] = 0;
+        av_freep(&subtitle_header);
+    }
+}
+
 static av_cold int ass_encode_init(AVCodecContext *avctx)
 {
-    avctx->extradata = av_malloc(avctx->subtitle_header_size + 1);
-    if (!avctx->extradata)
-        return AVERROR(ENOMEM);
-    memcpy(avctx->extradata, avctx->subtitle_header, avctx->subtitle_header_size);
-    avctx->extradata_size = avctx->subtitle_header_size;
-    avctx->extradata[avctx->extradata_size] = 0;
+    AssEncContext *s = avctx->priv_data;
+
+    if (avctx->subtitle_header_size) {
+        avctx->extradata = av_malloc(avctx->subtitle_header_size + 1);
+        if (!avctx->extradata)
+            return AVERROR(ENOMEM);
+        memcpy(avctx->extradata, avctx->subtitle_header, avctx->subtitle_header_size);
+        avctx->extradata_size                   = avctx->subtitle_header_size;
+        avctx->extradata[avctx->extradata_size] = 0;
+    }
+
+    s->current_frame = av_frame_alloc();
+    return 0;
+}
+
+static av_cold int ass_encode_close(AVCodecContext *avctx)
+{
+    AssEncContext *s = avctx->priv_data;
+    av_frame_free(&s->current_frame);
     return 0;
 }
 
-static int ass_encode_frame(AVCodecContext *avctx,
-                            unsigned char *buf, int bufsize,
-                            const AVSubtitle *sub)
+////static int ass_encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+////                            const AVFrame* frame, int* got_packet)
+////{
+////    int ret;
+////    size_t req_len = 0, total_len = 0;
+////
+////    check_write_header(avctx, frame);
+////
+////    for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+////        const char *ass = frame->subtitle_areas[i]->ass;
+////
+////        if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) {
+////            av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
+////            return AVERROR(EINVAL);
+////        }
+////
+////        if (ass)
+////            req_len += strlen(ass);
+////    }
+////
+////    ret = ff_get_encode_buffer(avctx, avpkt, req_len + 1, 0);
+////    if (ret < 0) {
+////        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+////        return ret;
+////    }
+////
+////    for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+////        const char *ass = frame->subtitle_areas[i]->ass;
+////
+////        if (ass) {
+////            size_t len = av_strlcpy((char *)avpkt->data + total_len, ass, avpkt->size - total_len);
+////            total_len += len;
+////        }
+////    }
+////
+////    avpkt->size = total_len;
+////    *got_packet = total_len > 0;
+////
+////    return 0;
+////}
+
+static int ass_receive_packet(AVCodecContext *avctx, AVPacket *avpkt)
 {
-    int i, len, total_len = 0;
+    AssEncContext *s = avctx->priv_data;
+    int ret;
+
+    if (!s->have_frame) {
+        s->current_area = 0;
+        ret = ff_encode_get_frame(avctx, s->current_frame);
+
+        if (ret < 0) {
+            av_frame_unref(s->current_frame);
+            return ret;
+        }
+
+        s->have_frame = 1;
+    }
 
-    for (i=0; i<sub->num_rects; i++) {
-        const char *ass = sub->rects[i]->ass;
+    check_write_header(avctx, s->current_frame);
 
-        if (sub->rects[i]->type != SUBTITLE_ASS) {
-            av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
+    if (s->current_frame->repeat_sub) {
+        av_frame_unref(s->current_frame);
+        s->have_frame = 0;
+        return AVERROR(EAGAIN);
+    }
+
+    if (s->current_area < s->current_frame->num_subtitle_areas) {
+        const AVSubtitleArea *area = s->current_frame->subtitle_areas[s->current_area];
+        const char *ass = area->ass;
+
+        if (area->type != AV_SUBTITLE_FMT_ASS) {
+            av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
             return AVERROR(EINVAL);
         }
 
-        len = av_strlcpy(buf+total_len, ass, bufsize-total_len);
+        if (ass) {
+            size_t len = strlen(ass);
+
+            ret = ff_get_encode_buffer(avctx, avpkt, len + 1, 0);
+            if (ret < 0) {
+                av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+                return ret;
+            }
+
+            len = av_strlcpy((char *)avpkt->data, ass, avpkt->size);
 
-        if (len > bufsize-total_len-1) {
-            av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n");
-            return AVERROR_BUFFER_TOO_SMALL;
+            avpkt->size = len;
         }
 
-        total_len += len;
+        s->current_area++;
     }
 
-    return total_len;
+    if (s->current_area < s->current_frame->num_subtitle_areas)
+        return 0;
+
+    av_frame_unref(s->current_frame);
+    s->have_frame = 0;
+
+    return 0;
 }
 
 #if CONFIG_SSA_ENCODER
 const AVCodec ff_ssa_encoder = {
-    .name         = "ssa",
-    .long_name    = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"),
-    .type         = AVMEDIA_TYPE_SUBTITLE,
-    .id           = AV_CODEC_ID_ASS,
-    .init         = ass_encode_init,
-    .encode_sub   = ass_encode_frame,
+    .name           = "ssa",
+    .long_name      = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"),
+    .type           = AVMEDIA_TYPE_SUBTITLE,
+    .id             = AV_CODEC_ID_ASS,
+    .priv_data_size = sizeof(AssEncContext),
+    .init           = ass_encode_init,
+    .close          = ass_encode_close,
+    .receive_packet = ass_receive_packet,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
 };
 #endif
 
 #if CONFIG_ASS_ENCODER
 const AVCodec ff_ass_encoder = {
-    .name         = "ass",
-    .long_name    = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"),
-    .type         = AVMEDIA_TYPE_SUBTITLE,
-    .id           = AV_CODEC_ID_ASS,
-    .init         = ass_encode_init,
-    .encode_sub   = ass_encode_frame,
+    .name           = "ass",
+    .long_name      = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"),
+    .type           = AVMEDIA_TYPE_SUBTITLE,
+    .priv_data_size = sizeof(AssEncContext),
+    .id             = AV_CODEC_ID_ASS,
+    .init           = ass_encode_init,
+    .close          = ass_encode_close,
+    .receive_packet = ass_receive_packet,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
 };
 #endif
diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index 9d59f6e840..93063dc6e9 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -2995,10 +2995,13 @@ void av_parser_close(AVCodecParserContext *s);
  * @{
  */
 
+ /**
+  * @deprecated Use @ref avcodec_encode_subtitle2() instead.
+  */
+attribute_deprecated
 int avcodec_encode_subtitle(AVCodecContext *avctx, uint8_t *buf, int buf_size,
                             const AVSubtitle *sub);
 
-
 /**
  * @}
  */
diff --git a/libavcodec/dvbsubenc.c b/libavcodec/dvbsubenc.c
index 322fc27cb4..3b5a76daa7 100644
--- a/libavcodec/dvbsubenc.c
+++ b/libavcodec/dvbsubenc.c
@@ -20,6 +20,7 @@
  */
 #include "avcodec.h"
 #include "bytestream.h"
+#include "encode.h"
 #include "libavutil/colorspace.h"
 
 typedef struct DVBSubtitleContext {
@@ -268,21 +269,29 @@ static int dvb_encode_rle8(uint8_t **pq, int buf_size,
     return len;
 }
 
-static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
-                         const AVSubtitle *h)
+static int dvbsub_encode(AVCodecContext* avctx, AVPacket* avpkt,
+                         const AVFrame* frame, int* got_packet)
 {
     DVBSubtitleContext *s = avctx->priv_data;
     uint8_t *q, *pseg_len;
     int page_id, region_id, clut_id, object_id, i, bpp_index, page_state;
-
-
-    q = outbuf;
+    size_t buf_size;
+    int ret;
 
     page_id = 1;
 
-    if (h->num_rects && !h->rects)
+    if (frame->num_subtitle_areas && !frame->subtitle_areas)
         return AVERROR(EINVAL);
 
+    ret = ff_get_encode_buffer(avctx, avpkt, 1024 * 1024, 0);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+        return ret;
+    }
+
+    buf_size = avpkt->size;
+    q = avpkt->data;
+
     if (avctx->width > 0 && avctx->height > 0) {
         if (buf_size < 11)
             return AVERROR_BUFFER_TOO_SMALL;
@@ -301,7 +310,7 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
 
     /* page composition segment */
 
-    if (buf_size < 8 + h->num_rects * 6)
+    if (buf_size < 8 + frame->num_subtitle_areas * 6)
         return AVERROR_BUFFER_TOO_SMALL;
     *q++ = 0x0f; /* sync_byte */
     *q++ = 0x10; /* segment_type */
@@ -313,30 +322,30 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
     /* page_version = 0 + page_state */
     *q++ = (s->object_version << 4) | (page_state << 2) | 3;
 
-    for (region_id = 0; region_id < h->num_rects; region_id++) {
+    for (region_id = 0; region_id < frame->num_subtitle_areas; region_id++) {
         *q++ = region_id;
         *q++ = 0xff; /* reserved */
-        bytestream_put_be16(&q, h->rects[region_id]->x); /* left pos */
-        bytestream_put_be16(&q, h->rects[region_id]->y); /* top pos */
+        bytestream_put_be16(&q, frame->subtitle_areas[region_id]->x); /* left pos */
+        bytestream_put_be16(&q, frame->subtitle_areas[region_id]->y); /* top pos */
     }
 
     bytestream_put_be16(&pseg_len, q - pseg_len - 2);
-    buf_size -= 8 + h->num_rects * 6;
+    buf_size -= 8 + frame->num_subtitle_areas * 6;
 
-    if (h->num_rects) {
-        for (clut_id = 0; clut_id < h->num_rects; clut_id++) {
-            if (buf_size < 6 + h->rects[clut_id]->nb_colors * 6)
+    if (frame->num_subtitle_areas) {
+        for (clut_id = 0; clut_id < frame->num_subtitle_areas; clut_id++) {
+            if (buf_size < 6 + frame->subtitle_areas[clut_id]->nb_colors * 6)
                 return AVERROR_BUFFER_TOO_SMALL;
 
             /* CLUT segment */
 
-            if (h->rects[clut_id]->nb_colors <= 4) {
+            if (frame->subtitle_areas[clut_id]->nb_colors <= 4) {
                 /* 2 bpp, some decoders do not support it correctly */
                 bpp_index = 0;
-            } else if (h->rects[clut_id]->nb_colors <= 16) {
+            } else if (frame->subtitle_areas[clut_id]->nb_colors <= 16) {
                 /* 4 bpp, standard encoding */
                 bpp_index = 1;
-            } else if (h->rects[clut_id]->nb_colors <= 256) {
+            } else if (frame->subtitle_areas[clut_id]->nb_colors <= 256) {
                 /* 8 bpp, standard encoding */
                 bpp_index = 2;
             } else {
@@ -353,12 +362,12 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
             *q++ = clut_id;
             *q++ = (0 << 4) | 0xf; /* version = 0 */
 
-            for(i = 0; i < h->rects[clut_id]->nb_colors; i++) {
+            for(i = 0; i < frame->subtitle_areas[clut_id]->nb_colors; i++) {
                 *q++ = i; /* clut_entry_id */
                 *q++ = (1 << (7 - bpp_index)) | (0xf << 1) | 1; /* 2 bits/pixel full range */
                 {
                     int a, r, g, b;
-                    uint32_t x= ((uint32_t*)h->rects[clut_id]->data[1])[i];
+                    uint32_t x= ((uint32_t*)frame->subtitle_areas[clut_id]->pal)[i];
                     a = (x >> 24) & 0xff;
                     r = (x >> 16) & 0xff;
                     g = (x >>  8) & 0xff;
@@ -372,22 +381,22 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
             }
 
             bytestream_put_be16(&pseg_len, q - pseg_len - 2);
-            buf_size -= 6 + h->rects[clut_id]->nb_colors * 6;
+            buf_size -= 6 + frame->subtitle_areas[clut_id]->nb_colors * 6;
         }
 
-        if (buf_size < h->num_rects * 22)
+        if (buf_size < frame->num_subtitle_areas * 22)
             return AVERROR_BUFFER_TOO_SMALL;
-        for (region_id = 0; region_id < h->num_rects; region_id++) {
+        for (region_id = 0; region_id < frame->num_subtitle_areas; region_id++) {
 
             /* region composition segment */
 
-            if (h->rects[region_id]->nb_colors <= 4) {
+            if (frame->subtitle_areas[region_id]->nb_colors <= 4) {
                 /* 2 bpp, some decoders do not support it correctly */
                 bpp_index = 0;
-            } else if (h->rects[region_id]->nb_colors <= 16) {
+            } else if (frame->subtitle_areas[region_id]->nb_colors <= 16) {
                 /* 4 bpp, standard encoding */
                 bpp_index = 1;
-            } else if (h->rects[region_id]->nb_colors <= 256) {
+            } else if (frame->subtitle_areas[region_id]->nb_colors <= 256) {
                 /* 8 bpp, standard encoding */
                 bpp_index = 2;
             } else {
@@ -401,8 +410,8 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
             q += 2; /* segment length */
             *q++ = region_id;
             *q++ = (s->object_version << 4) | (0 << 3) | 0x07; /* version , no fill */
-            bytestream_put_be16(&q, h->rects[region_id]->w); /* region width */
-            bytestream_put_be16(&q, h->rects[region_id]->h); /* region height */
+            bytestream_put_be16(&q, frame->subtitle_areas[region_id]->w); /* region width */
+            bytestream_put_be16(&q, frame->subtitle_areas[region_id]->h); /* region height */
             *q++ = ((1 + bpp_index) << 5) | ((1 + bpp_index) << 2) | 0x03;
             *q++ = region_id; /* clut_id == region_id */
             *q++ = 0; /* 8 bit fill colors */
@@ -416,9 +425,9 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
 
             bytestream_put_be16(&pseg_len, q - pseg_len - 2);
         }
-        buf_size -= h->num_rects * 22;
+        buf_size -= frame->num_subtitle_areas * 22;
 
-        for (object_id = 0; object_id < h->num_rects; object_id++) {
+        for (object_id = 0; object_id < frame->num_subtitle_areas; object_id++) {
             int (*dvb_encode_rle)(uint8_t **pq, int buf_size,
                                   const uint8_t *bitmap, int linesize,
                                   int w, int h);
@@ -427,13 +436,13 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
                 return AVERROR_BUFFER_TOO_SMALL;
 
             /* bpp_index maths */
-            if (h->rects[object_id]->nb_colors <= 4) {
+            if (frame->subtitle_areas[object_id]->nb_colors <= 4) {
                 /* 2 bpp, some decoders do not support it correctly */
                 dvb_encode_rle = dvb_encode_rle2;
-            } else if (h->rects[object_id]->nb_colors <= 16) {
+            } else if (frame->subtitle_areas[object_id]->nb_colors <= 16) {
                 /* 4 bpp, standard encoding */
                 dvb_encode_rle = dvb_encode_rle4;
-            } else if (h->rects[object_id]->nb_colors <= 256) {
+            } else if (frame->subtitle_areas[object_id]->nb_colors <= 256) {
                 /* 8 bpp, standard encoding */
                 dvb_encode_rle = dvb_encode_rle8;
             } else {
@@ -463,19 +472,19 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
 
                 top_ptr = q;
                 ret = dvb_encode_rle(&q, buf_size,
-                                     h->rects[object_id]->data[0],
-                                     h->rects[object_id]->w * 2,
-                                     h->rects[object_id]->w,
-                                     h->rects[object_id]->h >> 1);
+                                     frame->subtitle_areas[object_id]->buf[0]->data,
+                                     frame->subtitle_areas[object_id]->w * 2,
+                                     frame->subtitle_areas[object_id]->w,
+                                     frame->subtitle_areas[object_id]->h >> 1);
                 if (ret < 0)
                     return ret;
                 buf_size -= ret;
                 bottom_ptr = q;
                 ret = dvb_encode_rle(&q, buf_size,
-                                     h->rects[object_id]->data[0] + h->rects[object_id]->w,
-                                     h->rects[object_id]->w * 2,
-                                     h->rects[object_id]->w,
-                                     h->rects[object_id]->h >> 1);
+                                     frame->subtitle_areas[object_id]->buf[0]->data + frame->subtitle_areas[object_id]->w,
+                                     frame->subtitle_areas[object_id]->w * 2,
+                                     frame->subtitle_areas[object_id]->w,
+                                     frame->subtitle_areas[object_id]->h >> 1);
                 if (ret < 0)
                     return ret;
                 buf_size -= ret;
@@ -502,7 +511,10 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
     buf_size -= 6;
 
     s->object_version = (s->object_version + 1) & 0xf;
-    return q - outbuf;
+    avpkt->size = q - avpkt->data;
+    *got_packet = 1;
+
+    return 0;
 }
 
 const AVCodec ff_dvbsub_encoder = {
@@ -511,5 +523,5 @@ const AVCodec ff_dvbsub_encoder = {
     .type           = AVMEDIA_TYPE_SUBTITLE,
     .id             = AV_CODEC_ID_DVB_SUBTITLE,
     .priv_data_size = sizeof(DVBSubtitleContext),
-    .encode_sub     = dvbsub_encode,
+    .encode2        = dvbsub_encode,
 };
diff --git a/libavcodec/dvdsubenc.c b/libavcodec/dvdsubenc.c
index 943a7466d9..ff21e75ef3 100644
--- a/libavcodec/dvdsubenc.c
+++ b/libavcodec/dvdsubenc.c
@@ -20,6 +20,7 @@
  */
 #include "avcodec.h"
 #include "bytestream.h"
+#include "encode.h"
 #include "internal.h"
 #include "libavutil/avassert.h"
 #include "libavutil/bprint.h"
@@ -114,15 +115,14 @@ static int color_distance(uint32_t a, uint32_t b)
  * Count colors used in a rectangle, quantizing alpha and grouping by
  * nearest global palette entry.
  */
-static void count_colors(AVCodecContext *avctx, unsigned hits[33],
-                         const AVSubtitleRect *r)
+static void count_colors(const AVCodecContext *avctx, unsigned hits[33],
+                         const AVSubtitleArea *r)
 {
     DVDSubtitleContext *dvdc = avctx->priv_data;
     unsigned count[256] = { 0 };
-    uint32_t *palette = (uint32_t *)r->data[1];
     uint32_t color;
     int x, y, i, j, match, d, best_d, av_uninit(best_j);
-    uint8_t *p = r->data[0];
+    uint8_t *p = r->buf[0]->data;
 
     for (y = 0; y < r->h; y++) {
         for (x = 0; x < r->w; x++)
@@ -132,7 +132,7 @@ static void count_colors(AVCodecContext *avctx, unsigned hits[33],
     for (i = 0; i < 256; i++) {
         if (!count[i]) /* avoid useless search */
             continue;
-        color = palette[i];
+        color = r->pal[i];
         /* 0: transparent, 1-16: semi-transparent, 17-33 opaque */
         match = color < 0x33000000 ? 0 : color < 0xCC000000 ? 1 : 17;
         if (match) {
@@ -232,13 +232,13 @@ static void build_color_map(AVCodecContext *avctx, int cmap[],
     }
 }
 
-static void copy_rectangle(AVSubtitleRect *dst, AVSubtitleRect *src, int cmap[])
+static void copy_rectangle(AVSubtitleArea*dst, AVSubtitleArea *src, int cmap[])
 {
     int x, y;
     uint8_t *p, *q;
 
-    p = src->data[0];
-    q = dst->data[0] + (src->x - dst->x) +
+    p = src->buf[0]->data;
+    q = dst->buf[0]->data + (src->x - dst->x) +
                             (src->y - dst->y) * dst->linesize[0];
     for (y = 0; y < src->h; y++) {
         for (x = 0; x < src->w; x++)
@@ -248,51 +248,57 @@ static void copy_rectangle(AVSubtitleRect *dst, AVSubtitleRect *src, int cmap[])
     }
 }
 
-static int encode_dvd_subtitles(AVCodecContext *avctx,
-                                uint8_t *outbuf, int outbuf_size,
-                                const AVSubtitle *h)
+static int encode_dvd_subtitles(AVCodecContext* avctx, AVPacket* avpkt,
+                                const AVFrame* frame, int* got_packet)
 {
     DVDSubtitleContext *dvdc = avctx->priv_data;
     uint8_t *q, *qq;
     int offset1, offset2;
-    int i, rects = h->num_rects, ret;
+    int ret = 0;
+    unsigned i, rects = frame->num_subtitle_areas;
     unsigned global_palette_hits[33] = { 0 };
     int cmap[256];
     int out_palette[4];
     int out_alpha[4];
-    AVSubtitleRect vrect;
-    uint8_t *vrect_data = NULL;
+    AVSubtitleArea vrect;
+    uint8_t* vrect_data = NULL, *outbuf;
     int x2, y2;
     int forced = 0;
+    int outbuf_size;
+    int64_t duration_ms;
 
-    if (rects == 0 || !h->rects)
+    if (rects == 0)
+        return 0;
+
+    if (!frame->subtitle_areas)
         return AVERROR(EINVAL);
+
     for (i = 0; i < rects; i++)
-        if (h->rects[i]->type != AV_SUBTITLE_FMT_BITMAP) {
+        if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_BITMAP) {
             av_log(avctx, AV_LOG_ERROR, "Bitmap subtitle required\n");
             return AVERROR(EINVAL);
         }
     /* Mark this subtitle forced if any of the rectangles is forced. */
     for (i = 0; i < rects; i++)
-        if ((h->rects[i]->flags & AV_SUBTITLE_FLAG_FORCED) != 0) {
+        if ((frame->subtitle_areas[i]->flags & AV_SUBTITLE_FLAG_FORCED) != 0) {
             forced = 1;
             break;
         }
 
-    vrect = *h->rects[0];
+    vrect = *frame->subtitle_areas[0];
 
     if (rects > 1) {
         /* DVD subtitles can have only one rectangle: build a virtual
            rectangle containing all actual rectangles.
            The data of the rectangles will be copied later, when the palette
            is decided, because the rectangles may have different palettes. */
-        int xmin = h->rects[0]->x, xmax = xmin + h->rects[0]->w;
-        int ymin = h->rects[0]->y, ymax = ymin + h->rects[0]->h;
+        int xmin = frame->subtitle_areas[0]->x, xmax = xmin + frame->subtitle_areas[0]->w;
+        int ymin = frame->subtitle_areas[0]->y, ymax = ymin + frame->subtitle_areas[0]->h;
         for (i = 1; i < rects; i++) {
-            xmin = FFMIN(xmin, h->rects[i]->x);
-            ymin = FFMIN(ymin, h->rects[i]->y);
-            xmax = FFMAX(xmax, h->rects[i]->x + h->rects[i]->w);
-            ymax = FFMAX(ymax, h->rects[i]->y + h->rects[i]->h);
+            xmin = FFMIN(xmin, frame->subtitle_areas[i]->x);
+            ymin = FFMIN(ymin, frame->subtitle_areas[i]->y);
+            xmax = FFMAX(xmax, frame->subtitle_areas[i]->x + frame->subtitle_areas[i]->w);
+            ymax = FFMAX(ymax, frame->subtitle_areas[i]->y + frame->subtitle_areas[i]->h);
         }
         vrect.x = xmin;
         vrect.y = ymin;
@@ -304,27 +310,29 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
         /* Count pixels outside the virtual rectangle as transparent */
         global_palette_hits[0] = vrect.w * vrect.h;
         for (i = 0; i < rects; i++)
-            global_palette_hits[0] -= h->rects[i]->w * h->rects[i]->h;
+            global_palette_hits[0] -= frame->subtitle_areas[i]->w * frame->subtitle_areas[i]->h;
     }
 
     for (i = 0; i < rects; i++)
-        count_colors(avctx, global_palette_hits, h->rects[i]);
+        count_colors(avctx, global_palette_hits, frame->subtitle_areas[i]);
     select_palette(avctx, out_palette, out_alpha, global_palette_hits);
 
     if (rects > 1) {
-        if (!(vrect_data = av_calloc(vrect.w, vrect.h)))
+
+        vrect.buf[0] = av_buffer_allocz((size_t)vrect.w * vrect.h);
+        if (!vrect.buf[0])
             return AVERROR(ENOMEM);
-        vrect.data    [0] = vrect_data;
+
         vrect.linesize[0] = vrect.w;
         for (i = 0; i < rects; i++) {
-            build_color_map(avctx, cmap, (uint32_t *)h->rects[i]->data[1],
+            build_color_map(avctx, cmap, frame->subtitle_areas[i]->pal,
                             out_palette, out_alpha);
-            copy_rectangle(&vrect, h->rects[i], cmap);
+            copy_rectangle(&vrect, frame->subtitle_areas[i], cmap);
         }
         for (i = 0; i < 4; i++)
             cmap[i] = i;
     } else {
-        build_color_map(avctx, cmap, (uint32_t *)h->rects[0]->data[1],
+        build_color_map(avctx, cmap, frame->subtitle_areas[0]->pal,
                         out_palette, out_alpha);
     }
 
@@ -335,6 +343,16 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
                out_palette[i], out_alpha[i] >> 4);
     av_log(avctx, AV_LOG_DEBUG, "\n");
 
+
+    ret = ff_get_encode_buffer(avctx, avpkt, 4 + vrect.w * vrect.h / 2 + 17 + 21, 0);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+        return ret;
+    }
+
+    outbuf_size = avpkt->size;
+    outbuf = avpkt->data;
+
     // encode data block
     q = outbuf + 4;
     offset1 = q - outbuf;
@@ -344,10 +362,10 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
         ret = AVERROR_BUFFER_TOO_SMALL;
         goto fail;
     }
-    dvd_encode_rle(&q, vrect.data[0], vrect.w * 2,
+    dvd_encode_rle(&q, vrect.buf[0]->data, vrect.w * 2,
                    vrect.w, (vrect.h + 1) >> 1, cmap);
     offset2 = q - outbuf;
-    dvd_encode_rle(&q, vrect.data[0] + vrect.w, vrect.w * 2,
+    dvd_encode_rle(&q, vrect.buf[0]->data + vrect.w, vrect.w * 2,
                    vrect.w, vrect.h >> 1, cmap);
 
     if (dvdc->even_rows_fix && (vrect.h & 1)) {
@@ -362,7 +380,7 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
     bytestream_put_be16(&qq, q - outbuf);
 
     // send start display command
-    bytestream_put_be16(&q, (h->start_display_time*90) >> 10);
+    bytestream_put_be16(&q, 0);
     bytestream_put_be16(&q, (q - outbuf) /*- 2 */ + 8 + 12 + 2);
     *q++ = 0x03; // palette - 4 nibbles
     *q++ = (out_palette[3] << 4) | out_palette[2];
@@ -394,7 +412,8 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
     *q++ = 0xff; // terminating command
 
     // send stop display command last
-    bytestream_put_be16(&q, (h->end_display_time*90) >> 10);
+    duration_ms = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, (AVRational){ 1, 1000 });
+    bytestream_put_be16(&q, (duration_ms*90) >> 10);
     bytestream_put_be16(&q, (q - outbuf) - 2 /*+ 4*/);
     *q++ = 0x02; // set end
     *q++ = 0xff; // terminating command
@@ -403,7 +422,9 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
     bytestream_put_be16(&qq, q - outbuf);
 
     av_log(NULL, AV_LOG_DEBUG, "subtitle_packet size=%"PTRDIFF_SPECIFIER"\n", q - outbuf);
-    ret = q - outbuf;
+    avpkt->size = q - outbuf;
+    ret = 0;
+    *got_packet = 1;
 
 fail:
     av_free(vrect_data);
@@ -467,14 +488,13 @@ static int dvdsub_init(AVCodecContext *avctx)
     return 0;
 }
 
-static int dvdsub_encode(AVCodecContext *avctx,
-                         unsigned char *buf, int buf_size,
-                         const AVSubtitle *sub)
+static int dvdsub_encode(struct AVCodecContext* avctx, struct AVPacket* avpkt,
+                         const struct AVFrame* frame, int* got_packet)
 {
     //DVDSubtitleContext *s = avctx->priv_data;
     int ret;
 
-    ret = encode_dvd_subtitles(avctx, buf, buf_size, sub);
+    ret = encode_dvd_subtitles(avctx, avpkt, frame, got_packet);
     return ret;
 }
 
@@ -499,7 +519,7 @@ const AVCodec ff_dvdsub_encoder = {
     .type           = AVMEDIA_TYPE_SUBTITLE,
     .id             = AV_CODEC_ID_DVD_SUBTITLE,
     .init           = dvdsub_init,
-    .encode_sub     = dvdsub_encode,
+    .encode2        = dvdsub_encode,
     .priv_class     = &dvdsubenc_class,
     .priv_data_size = sizeof(DVDSubtitleContext),
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
diff --git a/libavcodec/encode.c b/libavcodec/encode.c
index 618be0573d..da195bb77a 100644
--- a/libavcodec/encode.c
+++ b/libavcodec/encode.c
@@ -140,17 +140,70 @@ fail:
     return ret;
 }
 
-int avcodec_encode_subtitle(AVCodecContext *avctx, uint8_t *buf, int buf_size,
-                            const AVSubtitle *sub)
+int avcodec_encode_subtitle(AVCodecContext *avctx, uint8_t *buf, int buf_size, const AVSubtitle *sub)
 {
-    int ret;
+    int ret = 0, got_packet = 0;
+    AVFrame *frame = NULL;
+    AVPacket* avpkt = NULL;
+
     if (sub->start_display_time) {
         av_log(avctx, AV_LOG_ERROR, "start_display_time must be 0.\n");
         return -1;
     }
 
-    ret = avctx->codec->encode_sub(avctx, buf, buf_size, sub);
+    memset(buf, 0, buf_size);
+    // Create a temporary frame for calling the regular api:
+    frame = av_frame_alloc();
+    if (!frame) {
+        ret = AVERROR(ENOMEM);
+        goto exit;
+    }
+
+    frame->format = sub->format;
+    frame->type = AVMEDIA_TYPE_SUBTITLE;
+    ret = av_frame_get_buffer2(frame, 0);
+    if (ret < 0)
+        goto exit;
+
+    // Create a temporary packet
+    avpkt = av_packet_alloc();
+    if (!avpkt) {
+        ret = AVERROR(ENOMEM);
+        goto exit;
+    }
+
+    // Copy legacy subtitle data to temp frame
+    ret = ff_frame_put_subtitle(frame, sub);
+    if (ret < 0)
+        goto exit;
+
+    ret = avcodec_send_frame(avctx, frame);
+    if (ret < 0)
+        goto exit;
+
+    ret = avcodec_receive_packet(avctx, avpkt);
+
+    if (ret < 0 && ret != AVERROR(EAGAIN))
+        goto exit;
+
+    //ret = avctx->codec->encode2(avctx, avpkt, frame, &got_packet);
+
     avctx->frame_number++;
+
+    if (avpkt->size) {
+        if (avpkt->size > buf_size) {
+            ret = AVERROR_BUFFER_TOO_SMALL;
+            goto exit;
+        }
+
+        memcpy(buf, avpkt->data, avpkt->size);
+        ret = avpkt->size;
+    }
+
+exit:
+
+    av_packet_free(&avpkt);
+    av_frame_free(&frame);
     return ret;
 }
 
diff --git a/libavcodec/movtextenc.c b/libavcodec/movtextenc.c
index d506ed5c37..6383d694a8 100644
--- a/libavcodec/movtextenc.c
+++ b/libavcodec/movtextenc.c
@@ -29,6 +29,7 @@
 #include "libavutil/ass_split_internal.h"
 #include "libavutil/ass_internal.h"
 #include "bytestream.h"
+#include "encode.h"
 #include "internal.h"
 
 #define STYLE_FLAG_BOLD         (1<<0)
@@ -73,6 +74,7 @@ typedef struct {
     AVCodecContext *avctx;
 
     ASSSplitContext *ass_ctx;
+    int is_default_ass_context;
     ASSStyle *ass_dialog_style;
     StyleBox *style_attributes;
     unsigned  count;
@@ -329,7 +331,7 @@ static av_cold int mov_text_encode_init(AVCodecContext *avctx)
 
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
 
-    s->ass_ctx = avpriv_ass_split(avctx->subtitle_header);
+    s->ass_ctx = avpriv_ass_split((char*)avctx->subtitle_header);
     if (!s->ass_ctx)
         return AVERROR_INVALIDDATA;
     ret = encode_sample_description(avctx);
@@ -633,56 +635,112 @@ static const ASSCodesCallbacks mov_text_callbacks = {
     .end              = mov_text_end_cb,
 };
 
-static int mov_text_encode_frame(AVCodecContext *avctx, unsigned char *buf,
-                                 int bufsize, const AVSubtitle *sub)
+static void ensure_ass_context(AVCodecContext* avctx, const AVFrame* frame)
+{
+    MovTextContext* s = avctx->priv_data;
+    int ret;
+
+    if (s->ass_ctx && !s->is_default_ass_context)
+        // We already have a (non-default context)
+        return;
+
+    if (!frame->num_subtitle_areas)
+        // Don't need ass context for processing empty subtitle frames
+        return;
+
+    // The frame has content, so we need to set up a context
+    if (frame->subtitle_header && frame->subtitle_header->size > 0) {
+        const char* subtitle_header = (char*)frame->subtitle_header->data;
+        avpriv_ass_split_free(s->ass_ctx);
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 0;
+    }
+    else if (!s->ass_ctx) {
+        char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+        if (!subtitle_header)
+            return;
+
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 1;
+        av_free(subtitle_header);
+    }
+
+    if (s->ass_ctx && !avctx->extradata_size) {
+        ret = encode_sample_description(avctx);
+        if (ret < 0)
+            av_log(avctx, AV_LOG_ERROR, "Error during encode_sample_description().\n");
+    }
+}
+
+static int mov_text_encode_frame(AVCodecContext *avctx, AVPacket *avpkt,
+                                 const AVFrame *frame, int *got_packet)
 {
     MovTextContext *s = avctx->priv_data;
     ASSDialog *dialog;
-    int i, length;
+    int i, ret = 0;
+    size_t j;
+    uint8_t* buf;
+
+    ensure_ass_context(avctx, frame);
 
     s->text_pos = 0;
     s->count = 0;
     s->box_flags = 0;
     av_bprint_clear(&s->buffer);
-    for (i = 0; i < sub->num_rects; i++) {
-        const char *ass = sub->rects[i]->ass;
+    for (i = 0; i < frame->num_subtitle_areas; i++) {
+        const char *ass = frame->subtitle_areas[i]->ass;
 
-        if (sub->rects[i]->type != SUBTITLE_ASS) {
-            av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
+        if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) {
+            av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
             return AVERROR(EINVAL);
         }
 
-        dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
-        if (!dialog)
-            return AVERROR(ENOMEM);
-        mov_text_dialog(s, dialog);
-        avpriv_ass_split_override_codes(&mov_text_callbacks, s, dialog->text);
-        avpriv_ass_free_dialog(&dialog);
+        if (ass) {
+
+            if (i > 0)
+                mov_text_new_line_cb(s, 0);
+
+            dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
+            if (!dialog)
+                return AVERROR(ENOMEM);
+            mov_text_dialog(s, dialog);
+            avpriv_ass_split_override_codes(&mov_text_callbacks, s, dialog->text);
+            avpriv_ass_free_dialog(&dialog);
+
+        }
     }
 
-    if (s->buffer.len > UINT16_MAX)
-        return AVERROR(ERANGE);
-    AV_WB16(buf, s->buffer.len);
-    buf += 2;
+    if (!av_bprint_is_complete(&s->buffer)) {
+        return AVERROR(ENOMEM);
+    }
 
-    for (size_t j = 0; j < box_count; j++)
+    for (j = 0; j < box_count; j++) {
         box_types[j].encode(s);
+    }
 
-    if (!av_bprint_is_complete(&s->buffer))
-        return AVERROR(ENOMEM);
+    ret = ff_get_encode_buffer(avctx, avpkt, s->buffer.len + 3, 0);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+        return ret;
+    }
 
-    if (!s->buffer.len)
-        return 0;
+    buf = avpkt->data;
 
-    if (s->buffer.len > bufsize - 3) {
+    AV_WB16(buf, s->buffer.len);
+    buf += 2;
+
+    if (s->buffer.len > avpkt->size - 3) {
         av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n");
-        return AVERROR_BUFFER_TOO_SMALL;
+        ret = AVERROR_BUFFER_TOO_SMALL;
+        goto exit;
     }
 
     memcpy(buf, s->buffer.str, s->buffer.len);
-    length = s->buffer.len + 2;
+    avpkt->size = s->buffer.len + 2;
+    *got_packet = 1;
 
-    return length;
+exit:
+    return ret;
 }
 
 #define OFFSET(x) offsetof(MovTextContext, x)
@@ -707,7 +765,7 @@ const AVCodec ff_movtext_encoder = {
     .priv_data_size = sizeof(MovTextContext),
     .priv_class     = &mov_text_encoder_class,
     .init           = mov_text_encode_init,
-    .encode_sub     = mov_text_encode_frame,
+    .encode2        = mov_text_encode_frame,
     .close          = mov_text_encode_close,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP,
 };
diff --git a/libavcodec/srtenc.c b/libavcodec/srtenc.c
index a7c5fccefe..ebe42ef817 100644
--- a/libavcodec/srtenc.c
+++ b/libavcodec/srtenc.c
@@ -21,6 +21,7 @@
 
 #include <stdarg.h>
 #include "avcodec.h"
+#include "encode.h"
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
 #include "libavutil/ass_split_internal.h"
@@ -33,6 +34,7 @@
 typedef struct {
     AVCodecContext *avctx;
     ASSSplitContext *ass_ctx;
+    int is_default_ass_context;
     AVBPrint buffer;
     char stack[SRT_STACK_SIZE];
     int stack_ptr;
@@ -130,14 +132,13 @@ static void srt_style_apply(SRTContext *s, const char *style)
     }
 }
 
-
 static av_cold int srt_encode_init(AVCodecContext *avctx)
 {
     SRTContext *s = avctx->priv_data;
     s->avctx = avctx;
-    s->ass_ctx = avpriv_ass_split(avctx->subtitle_header);
+    s->ass_ctx = avpriv_ass_split((char *)avctx->subtitle_header);
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
-    return s->ass_ctx ? 0 : AVERROR_INVALIDDATA;
+    return 0;
 }
 
 static void srt_text_cb(void *priv, const char *text, int len)
@@ -227,58 +228,95 @@ static const ASSCodesCallbacks text_callbacks = {
     .new_line         = srt_new_line_cb,
 };
 
-static int encode_frame(AVCodecContext *avctx,
-                        unsigned char *buf, int bufsize, const AVSubtitle *sub,
-                        const ASSCodesCallbacks *cb)
+static void ensure_ass_context(SRTContext* s, const AVFrame* frame)
+{
+    if (s->ass_ctx && !s->is_default_ass_context)
+        // We already have a (non-default context)
+        return;
+
+    if (!frame->num_subtitle_areas)
+        // Don't need ass context for processing empty subtitle frames
+        return;
+
+    // The frame has content, so we need to set up a context
+    if (frame->subtitle_header && frame->subtitle_header->size > 0) {
+        const char* subtitle_header = (char*)frame->subtitle_header->data;
+        avpriv_ass_split_free(s->ass_ctx);
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 0;
+    }
+    else if (!s->ass_ctx) {
+        char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+        if (!subtitle_header)
+            return;
+
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 1;
+        av_free(subtitle_header);
+    }
+}
+
+static int encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+                        const AVFrame* frame, int* got_packet, const ASSCodesCallbacks* cb)
 {
     SRTContext *s = avctx->priv_data;
     ASSDialog *dialog;
-    int i;
+    int i, ret;
+
+    ensure_ass_context(s, frame);
 
     av_bprint_clear(&s->buffer);
 
-    for (i=0; i<sub->num_rects; i++) {
-        const char *ass = sub->rects[i]->ass;
+    for (i=0; i< frame->num_subtitle_areas; i++) {
+        const char *ass = frame->subtitle_areas[i]->ass;
 
-        if (sub->rects[i]->type != SUBTITLE_ASS) {
-            av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
+        if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) {
+            av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
             return AVERROR(EINVAL);
         }
 
-        dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
-        if (!dialog)
-            return AVERROR(ENOMEM);
-        s->alignment_applied = 0;
-        if (avctx->codec_id == AV_CODEC_ID_SUBRIP)
-            srt_style_apply(s, dialog->style);
-        avpriv_ass_split_override_codes(cb, s, dialog->text);
-        avpriv_ass_free_dialog(&dialog);
+        if (ass) {
+
+            if (i > 0)
+                srt_new_line_cb(s, 0);
+
+            dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
+            if (!dialog)
+                return AVERROR(ENOMEM);
+            s->alignment_applied = 0;
+            if (avctx->codec_id == AV_CODEC_ID_SUBRIP)
+                srt_style_apply(s, dialog->style);
+            avpriv_ass_split_override_codes(cb, s, dialog->text);
+            avpriv_ass_free_dialog(&dialog);
+        }
     }
 
     if (!av_bprint_is_complete(&s->buffer))
         return AVERROR(ENOMEM);
-    if (!s->buffer.len)
-        return 0;
 
-    if (s->buffer.len > bufsize) {
-        av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n");
-        return AVERROR_BUFFER_TOO_SMALL;
+    ret = ff_get_encode_buffer(avctx, avpkt, s->buffer.len + 1, 0);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+        return ret;
     }
-    memcpy(buf, s->buffer.str, s->buffer.len);
 
-    return s->buffer.len;
+    memcpy(avpkt->data, s->buffer.str, s->buffer.len);
+    avpkt->size = s->buffer.len;
+    *got_packet = 1;
+
+    return 0;
 }
 
-static int srt_encode_frame(AVCodecContext *avctx,
-                               unsigned char *buf, int bufsize, const AVSubtitle *sub)
+static int srt_encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+                            const AVFrame* frame, int* got_packet)
 {
-    return encode_frame(avctx, buf, bufsize, sub, &srt_callbacks);
+    return encode_frame(avctx, avpkt, frame, got_packet, &srt_callbacks);
 }
 
-static int text_encode_frame(AVCodecContext *avctx,
-                             unsigned char *buf, int bufsize, const AVSubtitle *sub)
+static int text_encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+                             const AVFrame* frame, int* got_packet)
 {
-    return encode_frame(avctx, buf, bufsize, sub, &text_callbacks);
+    return encode_frame(avctx, avpkt, frame, got_packet, &text_callbacks);
 }
 
 static int srt_encode_close(AVCodecContext *avctx)
@@ -298,7 +336,7 @@ const AVCodec ff_srt_encoder = {
     .id             = AV_CODEC_ID_SUBRIP,
     .priv_data_size = sizeof(SRTContext),
     .init           = srt_encode_init,
-    .encode_sub     = srt_encode_frame,
+    .encode2        = srt_encode_frame,
     .close          = srt_encode_close,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
 };
@@ -312,7 +350,7 @@ const AVCodec ff_subrip_encoder = {
     .id             = AV_CODEC_ID_SUBRIP,
     .priv_data_size = sizeof(SRTContext),
     .init           = srt_encode_init,
-    .encode_sub     = srt_encode_frame,
+    .encode2        = srt_encode_frame,
     .close          = srt_encode_close,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
 };
@@ -326,7 +364,7 @@ const AVCodec ff_text_encoder = {
     .id             = AV_CODEC_ID_TEXT,
     .priv_data_size = sizeof(SRTContext),
     .init           = srt_encode_init,
-    .encode_sub     = text_encode_frame,
+    .encode2        = text_encode_frame,
     .close          = srt_encode_close,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
 };
diff --git a/libavcodec/tests/avcodec.c b/libavcodec/tests/avcodec.c
index 5d0ff9432c..bd979b2184 100644
--- a/libavcodec/tests/avcodec.c
+++ b/libavcodec/tests/avcodec.c
@@ -107,8 +107,6 @@ int main(void){
             continue;
         }
         if (is_encoder) {
-            if (codec->type == AVMEDIA_TYPE_SUBTITLE ^ !!codec->encode_sub)
-                ERR("Encoder %s is both subtitle encoder and not subtitle encoder.");
             if (!!codec->encode_sub + !!codec->encode2 + !!codec->receive_packet != 1)
                 ERR("Encoder %s does not implement exactly one encode API.\n");
             if (codec->update_thread_context || codec->update_thread_context_for_user || codec->bsfs)
diff --git a/libavcodec/ttmlenc.c b/libavcodec/ttmlenc.c
index 083f2dd67a..e6d8a01a73 100644
--- a/libavcodec/ttmlenc.c
+++ b/libavcodec/ttmlenc.c
@@ -33,11 +33,17 @@
 #include "libavutil/bprint.h"
 #include "libavutil/internal.h"
 #include "libavutil/ass_split_internal.h"
+#include "libavutil/ass_internal.h"
 #include "ttmlenc.h"
 
+#include "encode.h"
+
+
 typedef struct {
     AVCodecContext *avctx;
     ASSSplitContext *ass_ctx;
+    int is_default_ass_context;
+    int extradata_written;
     AVBPrint buffer;
 } TTMLContext;
 
@@ -76,28 +82,75 @@ static const ASSCodesCallbacks ttml_callbacks = {
     .new_line         = ttml_new_line_cb,
 };
 
-static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf,
-                             int bufsize, const AVSubtitle *sub)
+static int ttml_write_header_content(AVCodecContext* avctx);
+
+static void ensure_ass_context(AVCodecContext* avctx, const AVFrame* frame)
+{
+    TTMLContext* s = avctx->priv_data;
+    int ret;
+
+    if (s->ass_ctx && !s->is_default_ass_context)
+        // We already have a (non-default context)
+        return;
+
+    if (!frame->num_subtitle_areas)
+        // Don't need ass context for processing empty subtitle frames
+        return;
+
+    // The frame has content, so we need to set up a context
+    if (frame->subtitle_header && frame->subtitle_header->size > 0) {
+        const char* subtitle_header = (char*)frame->subtitle_header->data;
+        avpriv_ass_split_free(s->ass_ctx);
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 0;
+    }
+    else if (!s->ass_ctx) {
+        char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+        if (!subtitle_header)
+            return;
+
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 1;
+        av_free(subtitle_header);
+    }
+
+    if (s->ass_ctx && !s->extradata_written) {
+        s->extradata_written = 1;
+        if ((ret = ttml_write_header_content(avctx)) < 0) {
+            av_log(avctx, AV_LOG_ERROR, "Error writing header content.\n");
+        }
+    }
+}
+
+static int ttml_encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+                             const AVFrame* frame, int* got_packet)
 {
     TTMLContext *s = avctx->priv_data;
     ASSDialog *dialog;
-    int i;
+    int i, ret;
+
+    ensure_ass_context(avctx, frame);
 
     av_bprint_clear(&s->buffer);
 
-    for (i=0; i<sub->num_rects; i++) {
-        const char *ass = sub->rects[i]->ass;
-        int ret;
+    for (i=0; i< frame->num_subtitle_areas; i++) {
+        const char *ass = frame->subtitle_areas[i]->ass;
 
-        if (sub->rects[i]->type != SUBTITLE_ASS) {
-            av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
+        if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) {
+            av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
             return AVERROR(EINVAL);
         }
 
+        if (!ass)
+            continue;
+
         dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
         if (!dialog)
             return AVERROR(ENOMEM);
 
+        if (i > 0)
+            ttml_new_line_cb(s, 0);
+
         if (dialog->style) {
             av_bprintf(&s->buffer, "<span region=\"");
             av_bprint_escape(&s->buffer, dialog->style, NULL,
@@ -130,17 +183,19 @@ static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf,
 
     if (!av_bprint_is_complete(&s->buffer))
         return AVERROR(ENOMEM);
-    if (!s->buffer.len)
-        return 0;
-
-    // force null-termination, so in case our destination buffer is
-    // too small, the return value is larger than bufsize minus null.
-    if (av_strlcpy(buf, s->buffer.str, bufsize) > bufsize - 1) {
-        av_log(avctx, AV_LOG_ERROR, "Buffer too small for TTML event.\n");
-        return AVERROR_BUFFER_TOO_SMALL;
+
+    ret = ff_get_encode_buffer(avctx, avpkt, s->buffer.len + 3, 0);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+        return ret;
     }
 
-    return s->buffer.len;
+    av_strlcpy(avpkt->data, s->buffer.str, avpkt->size);
+
+    avpkt->size = s->buffer.len;
+    *got_packet = 1;
+
+    return 0;
 }
 
 static av_cold int ttml_encode_close(AVCodecContext *avctx)
@@ -370,13 +425,13 @@ static av_cold int ttml_encode_init(AVCodecContext *avctx)
     s->avctx   = avctx;
 
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
+    s->ass_ctx = avpriv_ass_split((char*)avctx->subtitle_header);
 
-    if (!(s->ass_ctx = avpriv_ass_split(avctx->subtitle_header))) {
-        return AVERROR_INVALIDDATA;
-    }
+    if (s->ass_ctx) {
+        if (ret = ttml_write_header_content(avctx) < 0)
+            return ret;
 
-    if ((ret = ttml_write_header_content(avctx)) < 0) {
-        return ret;
+        s->extradata_written = 1;
     }
 
     return 0;
@@ -389,7 +444,7 @@ const AVCodec ff_ttml_encoder = {
     .id             = AV_CODEC_ID_TTML,
     .priv_data_size = sizeof(TTMLContext),
     .init           = ttml_encode_init,
-    .encode_sub     = ttml_encode_frame,
+    .encode2        = ttml_encode_frame,
     .close          = ttml_encode_close,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP,
 };
diff --git a/libavcodec/webvttenc.c b/libavcodec/webvttenc.c
index 761099b69a..c0436f5739 100644
--- a/libavcodec/webvttenc.c
+++ b/libavcodec/webvttenc.c
@@ -22,6 +22,7 @@
 
 #include <stdarg.h>
 #include "avcodec.h"
+#include "encode.h"
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
 #include "libavutil/ass_split_internal.h"
@@ -32,6 +33,7 @@
 typedef struct {
     AVCodecContext *avctx;
     ASSSplitContext *ass_ctx;
+    int is_default_ass_context;
     AVBPrint buffer;
     unsigned timestamp_end;
     int count;
@@ -155,43 +157,81 @@ static const ASSCodesCallbacks webvtt_callbacks = {
     .end              = webvtt_end_cb,
 };
 
-static int webvtt_encode_frame(AVCodecContext *avctx,
-                               unsigned char *buf, int bufsize, const AVSubtitle *sub)
+static void ensure_ass_context(WebVTTContext* s, const AVFrame* frame)
+{
+    if (s->ass_ctx && !s->is_default_ass_context)
+        // We already have a (non-default context)
+        return;
+
+    if (!frame->num_subtitle_areas)
+        // Don't need ass context for processing empty subtitle frames
+        return;
+
+    // The frame has content, so we need to set up a context
+    if (frame->subtitle_header && frame->subtitle_header->size > 0) {
+        avpriv_ass_split_free(s->ass_ctx);
+        const char* subtitle_header = (char*)frame->subtitle_header->data;
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 0;
+    }
+    else if (!s->ass_ctx) {
+        char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+        if (!subtitle_header)
+            return;
+
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 1;
+        av_free(subtitle_header);
+    }
+}
+
+static int webvtt_encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+                               const AVFrame* frame, int* got_packet)
 {
     WebVTTContext *s = avctx->priv_data;
     ASSDialog *dialog;
-    int i;
+    int ret, i;
+
+    ensure_ass_context(s, frame);
 
     av_bprint_clear(&s->buffer);
 
-    for (i=0; i<sub->num_rects; i++) {
-        const char *ass = sub->rects[i]->ass;
+    for (i=0; i< frame->num_subtitle_areas; i++) {
+        const char *ass = frame->subtitle_areas[i]->ass;
 
-        if (sub->rects[i]->type != SUBTITLE_ASS) {
-            av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
+        if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) {
+            av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
             return AVERROR(EINVAL);
         }
 
-        dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
-        if (!dialog)
-            return AVERROR(ENOMEM);
-        webvtt_style_apply(s, dialog->style);
-        avpriv_ass_split_override_codes(&webvtt_callbacks, s, dialog->text);
-        avpriv_ass_free_dialog(&dialog);
+        if (ass) {
+
+            if (i > 0)
+                webvtt_new_line_cb(s, 0);
+
+            dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
+            if (!dialog)
+                return AVERROR(ENOMEM);
+            webvtt_style_apply(s, dialog->style);
+            avpriv_ass_split_override_codes(&webvtt_callbacks, s, dialog->text);
+            avpriv_ass_free_dialog(&dialog);
+        }
     }
 
     if (!av_bprint_is_complete(&s->buffer))
         return AVERROR(ENOMEM);
-    if (!s->buffer.len)
-        return 0;
 
-    if (s->buffer.len > bufsize) {
-        av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n");
-        return AVERROR_BUFFER_TOO_SMALL;
+    ret = ff_get_encode_buffer(avctx, avpkt, s->buffer.len + 1, 0);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+        return ret;
     }
-    memcpy(buf, s->buffer.str, s->buffer.len);
 
-    return s->buffer.len;
+    memcpy(avpkt->data, s->buffer.str, s->buffer.len);
+    avpkt->size = s->buffer.len;
+    *got_packet = s->buffer.len > 0;
+
+    return 0;
 }
 
 static int webvtt_encode_close(AVCodecContext *avctx)
@@ -206,9 +246,9 @@ static av_cold int webvtt_encode_init(AVCodecContext *avctx)
 {
     WebVTTContext *s = avctx->priv_data;
     s->avctx = avctx;
-    s->ass_ctx = avpriv_ass_split(avctx->subtitle_header);
+    s->ass_ctx = avpriv_ass_split((char*)avctx->subtitle_header);
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
-    return s->ass_ctx ? 0 : AVERROR_INVALIDDATA;
+    return 0;
 }
 
 const AVCodec ff_webvtt_encoder = {
@@ -218,7 +258,7 @@ const AVCodec ff_webvtt_encoder = {
     .id             = AV_CODEC_ID_WEBVTT,
     .priv_data_size = sizeof(WebVTTContext),
     .init           = webvtt_encode_init,
-    .encode_sub     = webvtt_encode_frame,
+    .encode2        = webvtt_encode_frame,
     .close          = webvtt_encode_close,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
 };
diff --git a/libavcodec/xsubenc.c b/libavcodec/xsubenc.c
index 03d0dc2d86..ef804f21f4 100644
--- a/libavcodec/xsubenc.c
+++ b/libavcodec/xsubenc.c
@@ -22,6 +22,7 @@
 
 #include "avcodec.h"
 #include "bytestream.h"
+#include "encode.h"
 #include "internal.h"
 #include "put_bits.h"
 
@@ -111,39 +112,56 @@ static int make_tc(uint64_t ms, int *tc)
     return ms > 99;
 }
 
-static int xsub_encode(AVCodecContext *avctx, unsigned char *buf,
-                       int bufsize, const AVSubtitle *h)
+static int xsub_encode(AVCodecContext* avctx, AVPacket* avpkt,
+                       const AVFrame* frame, int* got_packet)
 {
-    uint64_t startTime = h->pts / 1000; // FIXME: need better solution...
-    uint64_t endTime = startTime + h->end_display_time - h->start_display_time;
+    const int64_t duration_ms = (int64_t)((double)frame->subtitle_timing.duration * av_q2d(AV_TIME_BASE_Q) * 1000);
+    const uint64_t startTime = (int64_t)((double)frame->subtitle_timing.start_pts * av_q2d(AV_TIME_BASE_Q) * 1000);
+    const uint64_t endTime   = startTime + duration_ms;
     int start_tc[4], end_tc[4];
-    uint8_t *hdr = buf + 27; // Point behind the timestamp
+    uint8_t *hdr;
     uint8_t *rlelenptr;
     uint16_t width, height;
-    int i;
+    int i, ret;
     PutBitContext pb;
+    uint8_t* buf;
+    int64_t req_size;
 
-    if (bufsize < 27 + 7*2 + 4*3) {
-        av_log(avctx, AV_LOG_ERROR, "Buffer too small for XSUB header.\n");
-        return AVERROR_BUFFER_TOO_SMALL;
+    if (!frame->num_subtitle_areas) {
+        // Don't encode empty sub events
+        return 0;
     }
 
+    // Estimate size (timestamp 27, header 7*2 + 4*3, padding 10)
+    req_size = 27 + 7*2 + 4*3 + 10;
+    req_size += frame->subtitle_areas[0]->linesize[0] * frame->subtitle_areas[0]->h;
+    req_size += 256; // Palette
+
+    ret = ff_get_encode_buffer(avctx, avpkt, req_size, 0);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+        return ret;
+    }
+
+    buf = avpkt->data;
+    hdr = avpkt->data + 27; // Point behind the timestamp
+
     // TODO: support multiple rects
-    if (h->num_rects != 1)
-        av_log(avctx, AV_LOG_WARNING, "Only single rects supported (%d in subtitle.)\n", h->num_rects);
+    if (frame->num_subtitle_areas != 1)
+        av_log(avctx, AV_LOG_WARNING, "Only single rects supported (%d in subtitle.)\n", frame->num_subtitle_areas);
 
     // TODO: render text-based subtitles into bitmaps
-    if (!h->rects[0]->data[0] || !h->rects[0]->data[1]) {
+    if (!frame->subtitle_areas[0]->buf[0]->data || !frame->subtitle_areas[0]->pal) {
         av_log(avctx, AV_LOG_WARNING, "No subtitle bitmap available.\n");
         return AVERROR(EINVAL);
     }
 
     // TODO: color reduction, similar to dvdsub encoder
-    if (h->rects[0]->nb_colors > 4)
-        av_log(avctx, AV_LOG_WARNING, "No more than 4 subtitle colors supported (%d found.)\n", h->rects[0]->nb_colors);
+    if (frame->subtitle_areas[0]->nb_colors > 4)
+        av_log(avctx, AV_LOG_WARNING, "No more than 4 subtitle colors supported (%d found.)\n", frame->subtitle_areas[0]->nb_colors);
 
     // TODO: Palette swapping if color zero is not transparent
-    if (((uint32_t *)h->rects[0]->data[1])[0] & 0xff000000)
+    if (((uint32_t *)frame->subtitle_areas[0]->pal)[0] & 0xff000000)
         av_log(avctx, AV_LOG_WARNING, "Color index 0 is not transparent. Transparency will be messed up.\n");
 
     if (make_tc(startTime, start_tc) || make_tc(endTime, end_tc)) {
@@ -151,7 +169,7 @@ static int xsub_encode(AVCodecContext *avctx, unsigned char *buf,
         return AVERROR(EINVAL);
     }
 
-    snprintf(buf, 28,
+    snprintf((char *)avpkt->data, 28,
         "[%02d:%02d:%02d.%03d-%02d:%02d:%02d.%03d]",
         start_tc[3], start_tc[2], start_tc[1], start_tc[0],
         end_tc[3],   end_tc[2],   end_tc[1],   end_tc[0]);
@@ -160,45 +178,47 @@ static int xsub_encode(AVCodecContext *avctx, unsigned char *buf,
     // 2 pixels required on either side of subtitle.
     // Possibly due to limitations of hardware renderers.
     // TODO: check if the bitmap is already padded
-    width  = FFALIGN(h->rects[0]->w, 2) + PADDING * 2;
-    height = FFALIGN(h->rects[0]->h, 2);
+    width  = FFALIGN(frame->subtitle_areas[0]->w, 2) + PADDING * 2;
+    height = FFALIGN(frame->subtitle_areas[0]->h, 2);
 
     bytestream_put_le16(&hdr, width);
     bytestream_put_le16(&hdr, height);
-    bytestream_put_le16(&hdr, h->rects[0]->x);
-    bytestream_put_le16(&hdr, h->rects[0]->y);
-    bytestream_put_le16(&hdr, h->rects[0]->x + width -1);
-    bytestream_put_le16(&hdr, h->rects[0]->y + height -1);
+    bytestream_put_le16(&hdr, frame->subtitle_areas[0]->x);
+    bytestream_put_le16(&hdr, frame->subtitle_areas[0]->y);
+    bytestream_put_le16(&hdr, frame->subtitle_areas[0]->x + width -1);
+    bytestream_put_le16(&hdr, frame->subtitle_areas[0]->y + height -1);
 
     rlelenptr = hdr; // Will store length of first field here later.
     hdr+=2;
 
     // Palette
     for (i=0; i<4; i++)
-        bytestream_put_be24(&hdr, ((uint32_t *)h->rects[0]->data[1])[i]);
+        bytestream_put_be24(&hdr, ((uint32_t *)frame->subtitle_areas[0]->pal)[i]);
 
     // Bitmap
     // RLE buffer. Reserve 2 bytes for possible padding after the last row.
-    init_put_bits(&pb, hdr, bufsize - (hdr - buf) - 2);
-    if (xsub_encode_rle(&pb, h->rects[0]->data[0],
-                        h->rects[0]->linesize[0] * 2,
-                        h->rects[0]->w, (h->rects[0]->h + 1) >> 1))
+    init_put_bits(&pb, hdr, avpkt->size - (hdr - buf) - 2);
+    if (xsub_encode_rle(&pb, frame->subtitle_areas[0]->buf[0]->data,
+                        frame->subtitle_areas[0]->linesize[0] * 2,
+                        frame->subtitle_areas[0]->w, (frame->subtitle_areas[0]->h + 1) >> 1))
         return AVERROR_BUFFER_TOO_SMALL;
     bytestream_put_le16(&rlelenptr, put_bytes_count(&pb, 0)); // Length of first field
 
-    if (xsub_encode_rle(&pb, h->rects[0]->data[0] + h->rects[0]->linesize[0],
-                        h->rects[0]->linesize[0] * 2,
-                        h->rects[0]->w, h->rects[0]->h >> 1))
+    if (xsub_encode_rle(&pb, frame->subtitle_areas[0]->buf[0]->data + frame->subtitle_areas[0]->linesize[0],
+                        frame->subtitle_areas[0]->linesize[0] * 2,
+                        frame->subtitle_areas[0]->w, frame->subtitle_areas[0]->h >> 1))
         return AVERROR_BUFFER_TOO_SMALL;
 
     // Enforce total height to be a multiple of 2
-    if (h->rects[0]->h & 1) {
-        put_xsub_rle(&pb, h->rects[0]->w, PADDING_COLOR);
+    if (frame->subtitle_areas[0]->h & 1) {
+        put_xsub_rle(&pb, frame->subtitle_areas[0]->w, PADDING_COLOR);
     }
 
     flush_put_bits(&pb);
 
-    return hdr - buf + put_bytes_output(&pb);
+    avpkt->size = hdr - buf + put_bytes_output(&pb);
+    *got_packet = 1;
+    return 0;
 }
 
 static av_cold int xsub_encoder_init(AVCodecContext *avctx)
@@ -217,6 +237,6 @@ const AVCodec ff_xsub_encoder = {
     .type       = AVMEDIA_TYPE_SUBTITLE,
     .id         = AV_CODEC_ID_XSUB,
     .init       = xsub_encoder_init,
-    .encode_sub = xsub_encode,
+    .encode2    = xsub_encode,
     .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE,
 };
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v3 21/26] fftools/ffmpeg: Introduce subtitle filtering and new frame-based subtitle encoding
  2022-01-20  3:25   ` [FFmpeg-devel] [PATCH v3 00/26] Subtitle Filtering 2022 ffmpegagent
                       ` (19 preceding siblings ...)
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 20/26] avcodec/subtitles: Migrate subtitle encoders to frame-based API ffmpegagent
@ 2022-01-20  3:25     ` ffmpegagent
  2022-01-20 10:06       ` Michael Niedermayer
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 22/26] avutil/ass_split: Add parsing of hard-space tags (\h) ffmpegagent
                       ` (5 subsequent siblings)
  26 siblings, 1 reply; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  3:25 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

This commit actually enables subtitle filtering in ffmpeg by
sending and receiving subtitle frames to and from a filtergraph.

The heartbeat functionality from the previous sub2video implementation
is removed and now provided by the 'subfeed' filter.
The other part of sub2video functionality is retained by
auto-insertion of the new graphicsub2video filter.

Justification for changed test refs:

- sub2video
  The new results are identical excepting the last frame which
  is due to the implementation changes

- sub2video_basic
  The previous results had some incorrect output because multiple
  frames had the same dts
  The non-empty content frames are visually identical, the different
  CRC is due to the different blending algorithm that is being used.

- sub2video_time_limited
  The third frame in the previous ref was a repetition, which doesn't
  happen anymore with the new subtitle filtering.

- sub-dvb
  Running ffprobe -show_frames on the source file shows that there
  are 7 subtitle frames with 0 rects in the source at the start
  and 2 at the end. This translates to the 14 and 4 additional
  entries in the new test results.

- filter-overlay-dvdsub-2397
  Overlay results have slightly different CRCs due to different
  blending implementation

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 fftools/ffmpeg.c                          |  501 ++++-----
 fftools/ffmpeg.h                          |   13 +-
 fftools/ffmpeg_filter.c                   |  235 ++--
 fftools/ffmpeg_hw.c                       |    2 +-
 fftools/ffmpeg_opt.c                      |    3 +-
 tests/ref/fate/filter-overlay-dvdsub-2397 |  182 +--
 tests/ref/fate/sub-dvb                    |  162 +--
 tests/ref/fate/sub2video                  | 1091 +++++++++++++++++-
 tests/ref/fate/sub2video_basic            | 1239 +++++++++++++++++++--
 tests/ref/fate/sub2video_time_limited     |   78 +-
 10 files changed, 2837 insertions(+), 669 deletions(-)

diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c
index 5d134b025f..e916f0aaad 100644
--- a/fftools/ffmpeg.c
+++ b/fftools/ffmpeg.c
@@ -143,8 +143,6 @@ static int want_sdp = 1;
 static BenchmarkTimeStamps current_time;
 AVIOContext *progress_avio = NULL;
 
-static uint8_t *subtitle_out;
-
 InputStream **input_streams = NULL;
 int        nb_input_streams = 0;
 InputFile   **input_files   = NULL;
@@ -169,163 +167,6 @@ static int restore_tty;
 static void free_input_threads(void);
 #endif
 
-/* sub2video hack:
-   Convert subtitles to video with alpha to insert them in filter graphs.
-   This is a temporary solution until libavfilter gets real subtitles support.
- */
-
-static int sub2video_get_blank_frame(InputStream *ist)
-{
-    int ret;
-    AVFrame *frame = ist->sub2video.frame;
-
-    av_frame_unref(frame);
-    ist->sub2video.frame->width  = ist->dec_ctx->width  ? ist->dec_ctx->width  : ist->sub2video.w;
-    ist->sub2video.frame->height = ist->dec_ctx->height ? ist->dec_ctx->height : ist->sub2video.h;
-    ist->sub2video.frame->format = AV_PIX_FMT_RGB32;
-    if ((ret = av_frame_get_buffer(frame, 0)) < 0)
-        return ret;
-    memset(frame->data[0], 0, frame->height * frame->linesize[0]);
-    return 0;
-}
-
-static void sub2video_copy_rect(uint8_t *dst, int dst_linesize, int w, int h,
-                                AVSubtitleRect *r)
-{
-    uint32_t *pal, *dst2;
-    uint8_t *src, *src2;
-    int x, y;
-
-    if (r->type != SUBTITLE_BITMAP) {
-        av_log(NULL, AV_LOG_WARNING, "sub2video: non-bitmap subtitle\n");
-        return;
-    }
-    if (r->x < 0 || r->x + r->w > w || r->y < 0 || r->y + r->h > h) {
-        av_log(NULL, AV_LOG_WARNING, "sub2video: rectangle (%d %d %d %d) overflowing %d %d\n",
-            r->x, r->y, r->w, r->h, w, h
-        );
-        return;
-    }
-
-    dst += r->y * dst_linesize + r->x * 4;
-    src = r->data[0];
-    pal = (uint32_t *)r->data[1];
-    for (y = 0; y < r->h; y++) {
-        dst2 = (uint32_t *)dst;
-        src2 = src;
-        for (x = 0; x < r->w; x++)
-            *(dst2++) = pal[*(src2++)];
-        dst += dst_linesize;
-        src += r->linesize[0];
-    }
-}
-
-static void sub2video_push_ref(InputStream *ist, int64_t pts)
-{
-    AVFrame *frame = ist->sub2video.frame;
-    int i;
-    int ret;
-
-    av_assert1(frame->data[0]);
-    ist->sub2video.last_pts = frame->pts = pts;
-    for (i = 0; i < ist->nb_filters; i++) {
-        ret = av_buffersrc_add_frame_flags(ist->filters[i]->filter, frame,
-                                           AV_BUFFERSRC_FLAG_KEEP_REF |
-                                           AV_BUFFERSRC_FLAG_PUSH);
-        if (ret != AVERROR_EOF && ret < 0)
-            av_log(NULL, AV_LOG_WARNING, "Error while add the frame to buffer source(%s).\n",
-                   av_err2str(ret));
-    }
-}
-
-void sub2video_update(InputStream *ist, int64_t heartbeat_pts, AVSubtitle *sub)
-{
-    AVFrame *frame = ist->sub2video.frame;
-    int8_t *dst;
-    int     dst_linesize;
-    int num_rects, i;
-    int64_t pts, end_pts;
-
-    if (!frame)
-        return;
-    if (sub) {
-        pts       = av_rescale_q(sub->pts + sub->start_display_time * 1000LL,
-                                 AV_TIME_BASE_Q, ist->st->time_base);
-        end_pts   = av_rescale_q(sub->pts + sub->end_display_time   * 1000LL,
-                                 AV_TIME_BASE_Q, ist->st->time_base);
-        num_rects = sub->num_rects;
-    } else {
-        /* If we are initializing the system, utilize current heartbeat
-           PTS as the start time, and show until the following subpicture
-           is received. Otherwise, utilize the previous subpicture's end time
-           as the fall-back value. */
-        pts       = ist->sub2video.initialize ?
-                    heartbeat_pts : ist->sub2video.end_pts;
-        end_pts   = INT64_MAX;
-        num_rects = 0;
-    }
-    if (sub2video_get_blank_frame(ist) < 0) {
-        av_log(ist->dec_ctx, AV_LOG_ERROR,
-               "Impossible to get a blank canvas.\n");
-        return;
-    }
-    dst          = frame->data    [0];
-    dst_linesize = frame->linesize[0];
-    for (i = 0; i < num_rects; i++)
-        sub2video_copy_rect(dst, dst_linesize, frame->width, frame->height, sub->rects[i]);
-    sub2video_push_ref(ist, pts);
-    ist->sub2video.end_pts = end_pts;
-    ist->sub2video.initialize = 0;
-}
-
-static void sub2video_heartbeat(InputStream *ist, int64_t pts)
-{
-    InputFile *infile = input_files[ist->file_index];
-    int i, j, nb_reqs;
-    int64_t pts2;
-
-    /* When a frame is read from a file, examine all sub2video streams in
-       the same file and send the sub2video frame again. Otherwise, decoded
-       video frames could be accumulating in the filter graph while a filter
-       (possibly overlay) is desperately waiting for a subtitle frame. */
-    for (i = 0; i < infile->nb_streams; i++) {
-        InputStream *ist2 = input_streams[infile->ist_index + i];
-        if (!ist2->sub2video.frame)
-            continue;
-        /* subtitles seem to be usually muxed ahead of other streams;
-           if not, subtracting a larger time here is necessary */
-        pts2 = av_rescale_q(pts, ist->st->time_base, ist2->st->time_base) - 1;
-        /* do not send the heartbeat frame if the subtitle is already ahead */
-        if (pts2 <= ist2->sub2video.last_pts)
-            continue;
-        if (pts2 >= ist2->sub2video.end_pts || ist2->sub2video.initialize)
-            /* if we have hit the end of the current displayed subpicture,
-               or if we need to initialize the system, update the
-               overlayed subpicture and its start/end times */
-            sub2video_update(ist2, pts2 + 1, NULL);
-        for (j = 0, nb_reqs = 0; j < ist2->nb_filters; j++)
-            nb_reqs += av_buffersrc_get_nb_failed_requests(ist2->filters[j]->filter);
-        if (nb_reqs)
-            sub2video_push_ref(ist2, pts2);
-    }
-}
-
-static void sub2video_flush(InputStream *ist)
-{
-    int i;
-    int ret;
-
-    if (ist->sub2video.end_pts < INT64_MAX)
-        sub2video_update(ist, INT64_MAX, NULL);
-    for (i = 0; i < ist->nb_filters; i++) {
-        ret = av_buffersrc_add_frame(ist->filters[i]->filter, NULL);
-        if (ret != AVERROR_EOF && ret < 0)
-            av_log(NULL, AV_LOG_WARNING, "Flush the frame error.\n");
-    }
-}
-
-/* end of sub2video hack */
-
 static void term_exit_sigsafe(void)
 {
 #if HAVE_TERMIOS_H
@@ -526,7 +367,6 @@ static void ffmpeg_cleanup(int ret)
         avfilter_graph_free(&fg->graph);
         for (j = 0; j < fg->nb_inputs; j++) {
             InputFilter *ifilter = fg->inputs[j];
-            struct InputStream *ist = ifilter->ist;
 
             while (av_fifo_size(ifilter->frame_queue)) {
                 AVFrame *frame;
@@ -536,15 +376,6 @@ static void ffmpeg_cleanup(int ret)
             }
             av_fifo_freep(&ifilter->frame_queue);
             av_freep(&ifilter->displaymatrix);
-            if (ist->sub2video.sub_queue) {
-                while (av_fifo_size(ist->sub2video.sub_queue)) {
-                    AVSubtitle sub;
-                    av_fifo_generic_read(ist->sub2video.sub_queue,
-                                         &sub, sizeof(sub), NULL);
-                    avsubtitle_free(&sub);
-                }
-                av_fifo_freep(&ist->sub2video.sub_queue);
-            }
             av_buffer_unref(&ifilter->hw_frames_ctx);
             av_freep(&ifilter->name);
             av_freep(&fg->inputs[j]);
@@ -564,7 +395,7 @@ static void ffmpeg_cleanup(int ret)
     }
     av_freep(&filtergraphs);
 
-    av_freep(&subtitle_out);
+    ////av_freep(&subtitle_out);
 
     /* close files */
     for (i = 0; i < nb_output_files; i++) {
@@ -632,12 +463,12 @@ static void ffmpeg_cleanup(int ret)
         av_frame_free(&ist->decoded_frame);
         av_packet_free(&ist->pkt);
         av_dict_free(&ist->decoder_opts);
-        avsubtitle_free(&ist->prev_sub.subtitle);
-        av_frame_free(&ist->sub2video.frame);
+        av_frame_free(&ist->prev_sub.subtitle);
         av_freep(&ist->filters);
         av_freep(&ist->hwaccel_device);
         av_freep(&ist->dts_buffer);
 
+        av_buffer_unref(&ist->subtitle_header);
         avcodec_free_context(&ist->dec_ctx);
 
         av_freep(&input_streams[i]);
@@ -1055,33 +886,76 @@ error:
     exit_program(1);
 }
 
-static void do_subtitle_out(OutputFile *of,
-                            OutputStream *ost,
-                            AVSubtitle *sub)
+static void encode_subtitle_frame(OutputFile *of, OutputStream *ost, AVFrame *frame, AVPacket *pkt, int64_t pts_offset)
 {
-    int subtitle_out_max_size = 1024 * 1024;
-    int subtitle_out_size, nb, i;
+    AVCodecContext *enc = ost->enc_ctx;
+    int ret;
+
+        ost->frames_encoded++;
+
+        ret = avcodec_send_frame(enc, frame);
+        if (ret < 0)
+            goto error;
+
+        while (1) {
+            ret = avcodec_receive_packet(enc, pkt);
+            update_benchmark("encode_subtitles %d.%d", ost->file_index, ost->index);
+            if (ret == AVERROR(EAGAIN))
+                break;
+            if (ret < 0)
+                goto error;
+
+            if (debug_ts) {
+                av_log(NULL, AV_LOG_INFO, "encoder -> type:subtitles "
+                       "pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s\n",
+                       av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &enc->time_base),
+                       av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &enc->time_base));
+            }
+
+            pkt->pts  = av_rescale_q(frame->subtitle_timing.start_pts, AV_TIME_BASE_Q, ost->mux_timebase);
+            pkt->duration = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, ost->mux_timebase);
+            pkt->pts += pts_offset;
+
+            pkt->dts = pkt->pts;
+            output_packet(of, pkt, ost, 0);
+        }
+        ost->sync_opts++;
+        ost->frame_number++;
+
+    return;
+error:
+    av_log(NULL, AV_LOG_FATAL, "Subtitle encoding failed - Error code: %d\n", ret);
+    exit_program(1);
+}
+
+static void do_subtitle_out(OutputFile *of, OutputStream *ost, AVFrame *frame)
+{
+    int nb, i;
     AVCodecContext *enc;
     AVPacket *pkt = ost->pkt;
     int64_t pts;
 
-    if (sub->pts == AV_NOPTS_VALUE) {
+    if (!frame)
+        return;
+
+    av_log(NULL, AV_LOG_DEBUG, "do_subtitle_out: sub->pts: %"PRId64"  frame->pts: %"PRId64"\n", frame->subtitle_timing.start_pts, frame->pts);
+
+    if (frame->subtitle_timing.start_pts == AV_NOPTS_VALUE) {
         av_log(NULL, AV_LOG_ERROR, "Subtitle packets must have a pts\n");
         if (exit_on_error)
             exit_program(1);
         return;
     }
 
-    enc = ost->enc_ctx;
-
-    if (!subtitle_out) {
-        subtitle_out = av_malloc(subtitle_out_max_size);
-        if (!subtitle_out) {
-            av_log(NULL, AV_LOG_FATAL, "Failed to allocate subtitle_out\n");
-            exit_program(1);
-        }
+    if (frame->repeat_sub) {
+        av_log(NULL, AV_LOG_WARNING, "Ignoring repeated subtitle frame\n");
+        return;
     }
 
+    init_output_stream_wrapper(ost, frame, 1);
+
+    enc = ost->enc_ctx;
+
     /* Note: DVB subtitle need one packet to draw them and one other
        packet to clear them */
     /* XXX: signal it in the codec context ? */
@@ -1091,50 +965,38 @@ static void do_subtitle_out(OutputFile *of,
         nb = 1;
 
     /* shift timestamp to honor -ss and make check_recording_time() work with -t */
-    pts = sub->pts;
+    pts = frame->subtitle_timing.start_pts;
     if (output_files[ost->file_index]->start_time != AV_NOPTS_VALUE)
         pts -= output_files[ost->file_index]->start_time;
-    for (i = 0; i < nb; i++) {
-        unsigned save_num_rects = sub->num_rects;
 
-        ost->sync_opts = av_rescale_q(pts, AV_TIME_BASE_Q, enc->time_base);
-        if (!check_recording_time(ost))
-            return;
+    ost->sync_opts = av_rescale_q(pts, AV_TIME_BASE_Q, enc->time_base);
+    if (!check_recording_time(ost))
+        return;
 
-        sub->pts = pts;
-        // start_display_time is required to be 0
-        sub->pts               += av_rescale_q(sub->start_display_time, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q);
-        sub->end_display_time  -= sub->start_display_time;
-        sub->start_display_time = 0;
-        if (i == 1)
-            sub->num_rects = 0;
+    frame->subtitle_timing.start_pts = pts;
+
+    for (i = 0; i < nb; i++) {
+        const unsigned save_num_rects = frame->num_subtitle_areas;
+        int64_t pts_offset = 0;
 
         ost->frames_encoded++;
 
-        subtitle_out_size = avcodec_encode_subtitle(enc, subtitle_out,
-                                                    subtitle_out_max_size, sub);
         if (i == 1)
-            sub->num_rects = save_num_rects;
-        if (subtitle_out_size < 0) {
-            av_log(NULL, AV_LOG_FATAL, "Subtitle encoding failed\n");
-            exit_program(1);
-        }
+            frame->num_subtitle_areas = 0;
 
-        av_packet_unref(pkt);
-        pkt->data = subtitle_out;
-        pkt->size = subtitle_out_size;
-        pkt->pts  = av_rescale_q(sub->pts, AV_TIME_BASE_Q, ost->mux_timebase);
-        pkt->duration = av_rescale_q(sub->end_display_time, (AVRational){ 1, 1000 }, ost->mux_timebase);
         if (enc->codec_id == AV_CODEC_ID_DVB_SUBTITLE) {
             /* XXX: the pts correction is handled here. Maybe handling
                it in the codec would be better */
             if (i == 0)
-                pkt->pts += av_rescale_q(sub->start_display_time, (AVRational){ 1, 1000 }, ost->mux_timebase);
+                pts_offset = 0;
             else
-                pkt->pts += av_rescale_q(sub->end_display_time, (AVRational){ 1, 1000 }, ost->mux_timebase);
+                pts_offset = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, ost->mux_timebase);
         }
-        pkt->dts = pkt->pts;
-        output_packet(of, pkt, ost, 0);
+
+        encode_subtitle_frame(of, ost, frame, pkt, pts_offset);
+
+        if (i == 1)
+            frame->num_subtitle_areas = save_num_rects;
     }
 }
 
@@ -1544,8 +1406,26 @@ static int reap_filters(int flush)
                 }
                 do_audio_out(of, ost, filtered_frame);
                 break;
+            case AVMEDIA_TYPE_SUBTITLE:
+
+                if (filtered_frame->format == AV_SUBTITLE_FMT_ASS && !enc->subtitle_header
+                    && filtered_frame->subtitle_header) {
+                    const char *subtitle_header = (char *)filtered_frame->subtitle_header->data;
+                    enc->subtitle_header = (uint8_t *)av_strdup(subtitle_header);
+                    if (!enc->subtitle_header)
+                        return AVERROR(ENOMEM);
+                    enc->subtitle_header_size = strlen(subtitle_header);
+                }
+
+                if ((ost->enc_ctx->width == 0 || ost->enc_ctx->height == 0)
+                    && filter->inputs[0]->w > 0 && filter->inputs[0]->h > 0 ) {
+                    ost->enc_ctx->width = filter->inputs[0]->w;
+                    ost->enc_ctx->height = filter->inputs[0]->h;
+                }
+
+                do_subtitle_out(of, ost, filtered_frame);
+                break;
             default:
-                // TODO support subtitle filters
                 av_assert0(0);
             }
 
@@ -2138,7 +2018,8 @@ static int ifilter_has_all_input_formats(FilterGraph *fg)
     int i;
     for (i = 0; i < fg->nb_inputs; i++) {
         if (fg->inputs[i]->format < 0 && (fg->inputs[i]->type == AVMEDIA_TYPE_AUDIO ||
-                                          fg->inputs[i]->type == AVMEDIA_TYPE_VIDEO))
+                                          fg->inputs[i]->type == AVMEDIA_TYPE_VIDEO ||
+                                          fg->inputs[i]->type == AVMEDIA_TYPE_SUBTITLE))
             return 0;
     }
     return 1;
@@ -2166,6 +2047,18 @@ static int ifilter_send_frame(InputFilter *ifilter, AVFrame *frame, int keep_ref
     case AVMEDIA_TYPE_VIDEO:
         need_reinit |= ifilter->width  != frame->width ||
                        ifilter->height != frame->height;
+
+        if (need_reinit)
+            need_reinit = 1;
+        break;
+    case AVMEDIA_TYPE_SUBTITLE:
+        need_reinit |= ifilter->width  != frame->width ||
+                       ifilter->height != frame->height;
+
+        need_reinit &= (ifilter->width == 0 || ifilter->height == 0);
+
+        if (need_reinit)
+            need_reinit = 1;
         break;
     }
 
@@ -2243,7 +2136,7 @@ static int ifilter_send_eof(InputFilter *ifilter, int64_t pts)
         // the filtergraph was never configured
         if (ifilter->format < 0)
             ifilter_parameters_from_codecpar(ifilter, ifilter->ist->st->codecpar);
-        if (ifilter->format < 0 && (ifilter->type == AVMEDIA_TYPE_AUDIO || ifilter->type == AVMEDIA_TYPE_VIDEO)) {
+        if (ifilter->format < 0 && (ifilter->type == AVMEDIA_TYPE_AUDIO || ifilter->type == AVMEDIA_TYPE_VIDEO || ifilter->type == AVMEDIA_TYPE_SUBTITLE)) {
             av_log(NULL, AV_LOG_ERROR, "Cannot determine format of input stream %d:%d after EOF\n", ifilter->ist->file_index, ifilter->ist->st->index);
             return AVERROR_INVALIDDATA;
         }
@@ -2281,7 +2174,7 @@ static int decode(AVCodecContext *avctx, AVFrame *frame, int *got_frame, AVPacke
 
 static int send_frame_to_filters(InputStream *ist, AVFrame *decoded_frame)
 {
-    int i, ret;
+    int i, ret = 0;
 
     av_assert1(ist->nb_filters > 0); /* ensure ret is initialized */
     for (i = 0; i < ist->nb_filters; i++) {
@@ -2482,81 +2375,114 @@ fail:
     return err < 0 ? err : ret;
 }
 
-static int transcode_subtitles(InputStream *ist, AVPacket *pkt, int *got_output,
+static InputStream *get_input_stream(OutputStream *ost)
+{
+    if (ost->source_index >= 0)
+        return input_streams[ost->source_index];
+    return NULL;
+}
+
+static int decode_subtitles(InputStream *ist, AVPacket *pkt, int *got_output,
                                int *decode_failed)
 {
-    AVSubtitle subtitle;
-    int free_sub = 1;
-    int i, ret = avcodec_decode_subtitle2(ist->dec_ctx,
-                                          &subtitle, got_output, pkt);
+    AVFrame *decoded_frame;
+    AVCodecContext *avctx = ist->dec_ctx;
+    int i = 0, ret = 0, err = 0;
+
+    if (!ist->decoded_frame && !(ist->decoded_frame = av_frame_alloc()))
+        return AVERROR(ENOMEM);
+    decoded_frame = ist->decoded_frame;
+    decoded_frame->type = AVMEDIA_TYPE_SUBTITLE;
+    decoded_frame->format = avcodec_descriptor_get_subtitle_format(avctx->codec_descriptor);
 
-    check_decode_result(NULL, got_output, ret);
+    if (!ist->subtitle_header && avctx->subtitle_header && avctx->subtitle_header_size > 0) {
+        ist->subtitle_header = av_buffer_allocz(avctx->subtitle_header_size + 1);
+        if (!ist->subtitle_header)
+            return AVERROR(ENOMEM);
+        memcpy(ist->subtitle_header->data, avctx->subtitle_header, avctx->subtitle_header_size);
+    }
+
+    ret = decode(avctx, decoded_frame, got_output, pkt);
+
+    if (ret != AVERROR_EOF)
+        check_decode_result(NULL, got_output, ret);
 
     if (ret < 0 || !*got_output) {
         *decode_failed = 1;
-        if (!pkt->size)
-            sub2video_flush(ist);
+        if (!pkt->size) {
+            // Flush
+            for (i = 0; i < ist->nb_filters; i++) {
+                ret = av_buffersrc_add_frame(ist->filters[i]->filter, NULL);
+                if (ret != AVERROR_EOF && ret < 0)
+                    av_log(NULL, AV_LOG_WARNING, "Flush the frame error.\n");
+            }
+        }
         return ret;
     }
 
     if (ist->fix_sub_duration) {
-        int end = 1;
-        if (ist->prev_sub.got_output) {
-            end = av_rescale(subtitle.pts - ist->prev_sub.subtitle.pts,
-                             1000, AV_TIME_BASE);
-            if (end < ist->prev_sub.subtitle.end_display_time) {
-                av_log(ist->dec_ctx, AV_LOG_DEBUG,
-                       "Subtitle duration reduced from %"PRId32" to %d%s\n",
-                       ist->prev_sub.subtitle.end_display_time, end,
-                       end <= 0 ? ", dropping it" : "");
-                ist->prev_sub.subtitle.end_display_time = end;
+        int64_t end = 1;
+        if (ist->prev_sub.got_output && ist->prev_sub.subtitle) {
+
+            const int64_t duration = ist->prev_sub.subtitle->subtitle_timing.duration;
+            end = decoded_frame->subtitle_timing.start_pts - ist->prev_sub.subtitle->subtitle_timing.start_pts;
+
+            if (end < duration) {
+                av_log(avctx, AV_LOG_DEBUG, "Subtitle duration reduced from %"PRId64" to %"PRId64"%s\n",
+                    duration, end, end <= 0 ? ", dropping it" : "");
+                ist->prev_sub.subtitle->subtitle_timing.duration = end;
             }
         }
-        FFSWAP(int,        *got_output, ist->prev_sub.got_output);
-        FFSWAP(int,        ret,         ist->prev_sub.ret);
-        FFSWAP(AVSubtitle, subtitle,    ist->prev_sub.subtitle);
+        FFSWAP(int,        *got_output,   ist->prev_sub.got_output);
+        FFSWAP(int,        ret,           ist->prev_sub.ret);
+        FFSWAP(AVFrame*,   decoded_frame, ist->prev_sub.subtitle);
         if (end <= 0)
-            goto out;
+            return (int)end;
     }
 
-    if (!*got_output)
+    if (!*got_output || !decoded_frame)
         return ret;
 
-    if (ist->sub2video.frame) {
-        sub2video_update(ist, INT64_MIN, &subtitle);
-    } else if (ist->nb_filters) {
-        if (!ist->sub2video.sub_queue)
-            ist->sub2video.sub_queue = av_fifo_alloc(8 * sizeof(AVSubtitle));
-        if (!ist->sub2video.sub_queue)
-            exit_program(1);
-        if (!av_fifo_space(ist->sub2video.sub_queue)) {
-            ret = av_fifo_realloc2(ist->sub2video.sub_queue, 2 * av_fifo_size(ist->sub2video.sub_queue));
-            if (ret < 0)
-                exit_program(1);
+    decoded_frame->type = AVMEDIA_TYPE_SUBTITLE;
+    decoded_frame->format = avcodec_descriptor_get_subtitle_format(avctx->codec_descriptor);
+
+    if ((ret = av_buffer_replace(&decoded_frame->subtitle_header, ist->subtitle_header)) < 0)
+        return ret;
+
+    decoded_frame->pts = av_rescale_q(decoded_frame->subtitle_timing.start_pts, AV_TIME_BASE_Q, ist->st->time_base);
+
+    if (ist->nb_filters > 0) {
+        AVFrame *filter_frame = av_frame_clone(decoded_frame);
+        if (!filter_frame) {
+            err = AVERROR(ENOMEM);
+            goto end;
         }
-        av_fifo_generic_write(ist->sub2video.sub_queue, &subtitle, sizeof(subtitle), NULL);
-        free_sub = 0;
-    }
 
-    if (!subtitle.num_rects)
-        goto out;
+        err = send_frame_to_filters(ist, filter_frame);
+        av_frame_free(&filter_frame);
+    }
 
-    ist->frames_decoded++;
+    if (err >= 0) {
+        for (i = 0; i < nb_output_streams; i++) {
+            OutputStream *ost = output_streams[i];
+            InputStream *ist_src = get_input_stream(ost);
 
-    for (i = 0; i < nb_output_streams; i++) {
-        OutputStream *ost = output_streams[i];
+            if (!ist_src || !check_output_constraints(ist, ost)
+                || ist_src != ist
+                || !ost->encoding_needed
+                || ost->enc->type != AVMEDIA_TYPE_SUBTITLE)
+                continue;
 
-        if (!check_output_constraints(ist, ost) || !ost->encoding_needed
-            || ost->enc->type != AVMEDIA_TYPE_SUBTITLE)
-            continue;
+            if (ost->filter && ost->filter->filter->nb_inputs > 0)
+                continue;
 
-        do_subtitle_out(output_files[ost->file_index], ost, &subtitle);
+            do_subtitle_out(output_files[ost->file_index], ost, decoded_frame);
+        }
     }
 
-out:
-    if (free_sub)
-        avsubtitle_free(&subtitle);
-    return ret;
+end:
+    av_frame_unref(decoded_frame);
+    return err < 0 ? err : ret;
 }
 
 static int send_filter_eof(InputStream *ist)
@@ -2660,7 +2586,7 @@ static int process_input_packet(InputStream *ist, const AVPacket *pkt, int no_eo
         case AVMEDIA_TYPE_SUBTITLE:
             if (repeating)
                 break;
-            ret = transcode_subtitles(ist, avpkt, &got_output, &decode_failed);
+            ret = decode_subtitles(ist, avpkt, &got_output, &decode_failed);
             if (!pkt && ret >= 0)
                 ret = AVERROR_EOF;
             av_packet_unref(avpkt);
@@ -2928,13 +2854,6 @@ FF_ENABLE_DEPRECATION_WARNINGS
     return 0;
 }
 
-static InputStream *get_input_stream(OutputStream *ost)
-{
-    if (ost->source_index >= 0)
-        return input_streams[ost->source_index];
-    return NULL;
-}
-
 static int compare_int64(const void *a, const void *b)
 {
     return FFDIFFSIGN(*(const int64_t *)a, *(const int64_t *)b);
@@ -3411,7 +3330,7 @@ static int init_output_stream_encode(OutputStream *ost, AVFrame *frame)
         break;
     case AVMEDIA_TYPE_SUBTITLE:
         enc_ctx->time_base = AV_TIME_BASE_Q;
-        if (!enc_ctx->width) {
+        if (!enc_ctx->width && ost->source_index >= 0) {
             enc_ctx->width     = input_streams[ost->source_index]->st->codecpar->width;
             enc_ctx->height    = input_streams[ost->source_index]->st->codecpar->height;
         }
@@ -3464,19 +3383,14 @@ static int init_output_stream(OutputStream *ost, AVFrame *frame,
         }
 
         if (ist && ist->dec->type == AVMEDIA_TYPE_SUBTITLE && ost->enc->type == AVMEDIA_TYPE_SUBTITLE) {
-            int input_props = 0, output_props = 0;
-            AVCodecDescriptor const *input_descriptor =
-                avcodec_descriptor_get(dec->codec_id);
-            AVCodecDescriptor const *output_descriptor =
-                avcodec_descriptor_get(ost->enc_ctx->codec_id);
-            if (input_descriptor)
-                input_props = input_descriptor->props & (AV_CODEC_PROP_TEXT_SUB | AV_CODEC_PROP_BITMAP_SUB);
-            if (output_descriptor)
-                output_props = output_descriptor->props & (AV_CODEC_PROP_TEXT_SUB | AV_CODEC_PROP_BITMAP_SUB);
-            if (input_props && output_props && input_props != output_props) {
-                snprintf(error, error_len,
-                         "Subtitle encoding currently only possible from text to text "
-                         "or bitmap to bitmap");
+            AVCodecDescriptor const *input_descriptor     = avcodec_descriptor_get(dec->codec_id);
+            AVCodecDescriptor const *output_descriptor    = avcodec_descriptor_get(ost->enc_ctx->codec_id);
+            const enum AVSubtitleType in_subtitle_format  = output_descriptor ? avcodec_descriptor_get_subtitle_format(input_descriptor) : AV_SUBTITLE_FMT_UNKNOWN;
+            const enum AVSubtitleType out_subtitle_format = output_descriptor ? avcodec_descriptor_get_subtitle_format(output_descriptor) : AV_SUBTITLE_FMT_UNKNOWN;
+
+            if (ist->nb_filters == 0 && in_subtitle_format != AV_SUBTITLE_FMT_UNKNOWN && out_subtitle_format != AV_SUBTITLE_FMT_UNKNOWN
+                && in_subtitle_format != out_subtitle_format) {
+                snprintf(error, error_len, "Subtitle encoding is only possible from text to text or bitmap to bitmap");
                 return AVERROR_INVALIDDATA;
             }
         }
@@ -3640,7 +3554,8 @@ static int transcode_init(void)
     for (i = 0; i < nb_output_streams; i++) {
         if (!output_streams[i]->stream_copy &&
             (output_streams[i]->enc_ctx->codec_type == AVMEDIA_TYPE_VIDEO ||
-             output_streams[i]->enc_ctx->codec_type == AVMEDIA_TYPE_AUDIO))
+             output_streams[i]->enc_ctx->codec_type == AVMEDIA_TYPE_AUDIO ||
+             output_streams[i]->enc_ctx->codec_type == AVMEDIA_TYPE_SUBTITLE))
             continue;
 
         ret = init_output_stream_wrapper(output_streams[i], NULL, 0);
@@ -4477,8 +4392,6 @@ static int process_input(int file_index)
                av_ts2timestr(input_files[ist->file_index]->ts_offset, &AV_TIME_BASE_Q));
     }
 
-    sub2video_heartbeat(ist, pkt->pts);
-
     process_input_packet(ist, pkt, 0);
 
 discard_packet:
diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h
index 9b200b806a..5ebd01c14e 100644
--- a/fftools/ffmpeg.h
+++ b/fftools/ffmpeg.h
@@ -349,17 +349,10 @@ typedef struct InputStream {
     struct { /* previous decoded subtitle and related variables */
         int got_output;
         int ret;
-        AVSubtitle subtitle;
+        AVFrame *subtitle;
     } prev_sub;
 
-    struct sub2video {
-        int64_t last_pts;
-        int64_t end_pts;
-        AVFifoBuffer *sub_queue;    ///< queue of AVSubtitle* before filter init
-        AVFrame *frame;
-        int w, h;
-        unsigned int initialize; ///< marks if sub2video_update should force an initialization
-    } sub2video;
+    AVBufferRef *subtitle_header;
 
     /* decoded data from this stream goes into all those filters
      * currently video and audio only */
@@ -659,8 +652,6 @@ int filtergraph_is_simple(FilterGraph *fg);
 int init_simple_filtergraph(InputStream *ist, OutputStream *ost);
 int init_complex_filtergraph(FilterGraph *fg);
 
-void sub2video_update(InputStream *ist, int64_t heartbeat_pts, AVSubtitle *sub);
-
 int ifilter_parameters_from_frame(InputFilter *ifilter, const AVFrame *frame);
 
 int ffmpeg_parse_options(int argc, char **argv);
diff --git a/fftools/ffmpeg_filter.c b/fftools/ffmpeg_filter.c
index 1f6cba2c04..17ce79cfaa 100644
--- a/fftools/ffmpeg_filter.c
+++ b/fftools/ffmpeg_filter.c
@@ -22,6 +22,8 @@
 
 #include "ffmpeg.h"
 
+#include "libavutil/ass_split_internal.h"
+
 #include "libavfilter/avfilter.h"
 #include "libavfilter/buffersink.h"
 #include "libavfilter/buffersrc.h"
@@ -30,11 +32,9 @@
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
 #include "libavutil/channel_layout.h"
-#include "libavutil/display.h"
 #include "libavutil/opt.h"
 #include "libavutil/pixdesc.h"
 #include "libavutil/pixfmt.h"
-#include "libavutil/imgutils.h"
 #include "libavutil/samplefmt.h"
 
 // FIXME: YUV420P etc. are actually supported with full color range,
@@ -215,9 +215,8 @@ static void init_input_filter(FilterGraph *fg, AVFilterInOut *in)
     InputFilter *ifilter;
     int i;
 
-    // TODO: support other filter types
-    if (type != AVMEDIA_TYPE_VIDEO && type != AVMEDIA_TYPE_AUDIO) {
-        av_log(NULL, AV_LOG_FATAL, "Only video and audio filters supported "
+    if (type != AVMEDIA_TYPE_VIDEO && type != AVMEDIA_TYPE_AUDIO && type != AVMEDIA_TYPE_SUBTITLE) {
+        av_log(NULL, AV_LOG_FATAL, "Only video, audio and subtitle filters supported "
                "currently.\n");
         exit_program(1);
     }
@@ -238,8 +237,9 @@ static void init_input_filter(FilterGraph *fg, AVFilterInOut *in)
         for (i = 0; i < s->nb_streams; i++) {
             enum AVMediaType stream_type = s->streams[i]->codecpar->codec_type;
             if (stream_type != type &&
-                !(stream_type == AVMEDIA_TYPE_SUBTITLE &&
-                  type == AVMEDIA_TYPE_VIDEO /* sub2video hack */))
+                // in the followng case we auto-insert the graphicsub2video conversion filter
+                // for retaining compatibility with the previous sub2video hack
+                !(stream_type == AVMEDIA_TYPE_SUBTITLE && type == AVMEDIA_TYPE_VIDEO))
                 continue;
             if (check_stream_specifier(s, s->streams[i], *p == ':' ? p + 1 : p) == 1) {
                 st = s->streams[i];
@@ -286,6 +286,17 @@ static void init_input_filter(FilterGraph *fg, AVFilterInOut *in)
     ifilter->type   = ist->st->codecpar->codec_type;
     ifilter->name   = describe_filter_link(fg, in, 1);
 
+    if (ist->st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE && ist->dec_ctx) {
+        const AVCodecDescriptor *codec_descriptor = ist->dec_ctx->codec_descriptor;
+        if (!codec_descriptor)
+            codec_descriptor = avcodec_descriptor_get(ist->dec_ctx->codec_id);
+
+        // For subtitles, we need to set the format here. Would we leave the format
+        // at -1, it would delay processing (ifilter_has_all_input_formats()) until
+        // the first subtile frame arrives, which could never happen in the worst case
+        fg->inputs[fg->nb_inputs - 1]->format = avcodec_descriptor_get_subtitle_format(codec_descriptor);
+    }
+
     ifilter->frame_queue = av_fifo_alloc(8 * sizeof(AVFrame*));
     if (!ifilter->frame_queue)
         exit_program(1);
@@ -405,6 +416,39 @@ static int insert_filter(AVFilterContext **last_filter, int *pad_idx,
     return 0;
 }
 
+static int configure_output_subtitle_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out)
+{
+    OutputStream *ost = ofilter->ost;
+    AVFilterContext *last_filter = out->filter_ctx;
+    int pad_idx = out->pad_idx;
+    int ret;
+    char name[255];
+
+    snprintf(name, sizeof(name), "out_%d_%d", ost->file_index, ost->index);
+    ret = avfilter_graph_create_filter(&ofilter->filter,
+                                       avfilter_get_by_name("sbuffersink"),
+                                       name, NULL, NULL, fg->graph);
+
+    if (ret < 0) {
+        av_log(NULL, AV_LOG_ERROR, "Unable to create filter sbuffersink\n");
+        return ret;
+    }
+
+    ////snprintf(name, sizeof(name), "trim_out_%d_%d",
+    ////         ost->file_index, ost->index);
+    ////ret = insert_trim(of->start_time, of->recording_time,
+    ////                  &last_filter, &pad_idx, name);
+    ////if (ret < 0)
+    ////    return ret;
+
+    ////ost->st->codecpar->codec_tag = MKTAG('a', 's', 's', 's');
+
+    if ((ret = avfilter_link(last_filter, pad_idx, ofilter->filter, 0)) < 0)
+        return ret;
+
+    return 0;
+}
+
 static int configure_output_video_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out)
 {
     OutputStream *ost = ofilter->ost;
@@ -585,7 +629,8 @@ static int configure_output_audio_filter(FilterGraph *fg, OutputFilter *ofilter,
         int i;
 
         for (i=0; i<of->ctx->nb_streams; i++)
-            if (of->ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
+            if (of->ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ||
+                of->ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)
                 break;
 
         if (i<of->ctx->nb_streams) {
@@ -619,6 +664,7 @@ static int configure_output_filter(FilterGraph *fg, OutputFilter *ofilter,
     switch (avfilter_pad_get_type(out->filter_ctx->output_pads, out->pad_idx)) {
     case AVMEDIA_TYPE_VIDEO: return configure_output_video_filter(fg, ofilter, out);
     case AVMEDIA_TYPE_AUDIO: return configure_output_audio_filter(fg, ofilter, out);
+    case AVMEDIA_TYPE_SUBTITLE: return configure_output_subtitle_filter(fg, ofilter, out);
     default: av_assert0(0); return 0;
     }
 }
@@ -638,51 +684,126 @@ void check_filter_outputs(void)
     }
 }
 
-static int sub2video_prepare(InputStream *ist, InputFilter *ifilter)
+static int configure_input_subtitle_filter(FilterGraph *fg, InputFilter *ifilter,
+                                        AVFilterInOut *in)
 {
-    AVFormatContext *avf = input_files[ist->file_index]->ctx;
-    int i, w, h;
+    AVFilterContext *last_filter;
+    const AVFilter *buffer_filt = avfilter_get_by_name("sbuffer");
+    InputStream *ist = ifilter->ist;
+    AVBPrint args;
+    char name[255];
+    int ret, pad_idx = 0;
+    int w, h;
+    AVBufferSrcParameters *par = av_buffersrc_parameters_alloc();
+    enum AVMediaType media_type;
+
+    if (!par)
+        return AVERROR(ENOMEM);
+
+    par->format = AV_PIX_FMT_NONE;
+
+    if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) {
+        av_log(NULL, AV_LOG_ERROR, "Cannot connect subtitle filter to audio input\n");
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+
+    if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO) {
+        av_log(NULL, AV_LOG_ERROR, "Cannot connect subtitle filter to video input\n");
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
 
-    /* Compute the size of the canvas for the subtitles stream.
-       If the subtitles codecpar has set a size, use it. Otherwise use the
-       maximum dimensions of the video streams in the same file. */
     w = ifilter->width;
     h = ifilter->height;
+
     if (!(w && h)) {
-        for (i = 0; i < avf->nb_streams; i++) {
-            if (avf->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
-                w = FFMAX(w, avf->streams[i]->codecpar->width);
-                h = FFMAX(h, avf->streams[i]->codecpar->height);
-            }
-        }
-        if (!(w && h)) {
-            w = FFMAX(w, 720);
-            h = FFMAX(h, 576);
-        }
-        av_log(avf, AV_LOG_INFO, "sub2video: using %dx%d canvas\n", w, h);
+        w = ist->dec_ctx->width;
+        h = ist->dec_ctx->height;
     }
-    ist->sub2video.w = ifilter->width  = w;
-    ist->sub2video.h = ifilter->height = h;
 
-    ifilter->width  = ist->dec_ctx->width  ? ist->dec_ctx->width  : ist->sub2video.w;
-    ifilter->height = ist->dec_ctx->height ? ist->dec_ctx->height : ist->sub2video.h;
+    if (!(w && h) && ist->dec_ctx->subtitle_header) {
+        ASSSplitContext *ass_ctx = avpriv_ass_split((char *)ist->dec_ctx->subtitle_header);
+        ASS *ass = (ASS *)ass_ctx;
+        w = ass->script_info.play_res_x;
+        h = ass->script_info.play_res_y;
+        avpriv_ass_split_free(ass_ctx);
+    }
 
-    /* rectangles are AV_PIX_FMT_PAL8, but we have no guarantee that the
-       palettes for all rectangles are identical or compatible */
-    ifilter->format = AV_PIX_FMT_RGB32;
+    ifilter->width = w;
+    ifilter->height = h;
+    ist->dec_ctx->width = w;
+    ist->dec_ctx->height = h;
 
-    ist->sub2video.frame = av_frame_alloc();
-    if (!ist->sub2video.frame)
-        return AVERROR(ENOMEM);
-    ist->sub2video.last_pts = INT64_MIN;
-    ist->sub2video.end_pts  = INT64_MIN;
+    snprintf(name, sizeof(name), "graph %d subtitle input from stream %d:%d", fg->index,
+             ist->file_index, ist->st->index);
+
+
+    av_bprint_init(&args, 0, AV_BPRINT_SIZE_AUTOMATIC);
+    av_bprintf(&args,
+             "subtitle_type=%d:width=%d:height=%d:time_base=%d/%d:",
+             ifilter->format, ifilter->width, ifilter->height,
+             ist->st->time_base.num, ist->st->time_base.den);
+    if ((ret = avfilter_graph_create_filter(&ifilter->filter, buffer_filt, name,
+                                            args.str, NULL, fg->graph)) < 0)
+        goto fail;
+
+    par->hw_frames_ctx = ifilter->hw_frames_ctx;
+    par->format = ifilter->format;
+    par->width = ifilter->width;
+    par->height = ifilter->height;
+
+    ret = av_buffersrc_parameters_set(ifilter->filter, par);
+    if (ret < 0)
+        goto fail;
+    av_freep(&par);
+    last_filter = ifilter->filter;
+
+    // This is for sub2video compatibility
+    media_type = avfilter_pad_get_type(in->filter_ctx->input_pads, in->pad_idx);
+    if (media_type == AVMEDIA_TYPE_VIDEO) {
+        int subscale_w = w, subscale_h = h;
+
+        av_log(NULL, AV_LOG_INFO, "Auto-inserting subfeed filter\n");
+        ret = insert_filter(&last_filter, &pad_idx, "subfeed", NULL);
+        if (ret < 0)
+            return ret;
+
+        if (!(subscale_w && subscale_h)) {
+            // If the subtitle frame size is unknown, try to find a video input
+            // and use its size for adding a subscale filter
+            for (int i = 0; i < fg->nb_inputs; i++) {
+                InputFilter *input = fg->inputs[i];
+                if (input->type == AVMEDIA_TYPE_VIDEO && input->width && input->height) {
+                    subscale_w = input->width;
+                    subscale_h = input->height;
+                    break;
+                }
+            }
+        }
+
+        if (subscale_w && subscale_h) {
+            char subscale_params[64];
+            snprintf(subscale_params, sizeof(subscale_params), "w=%d:h=%d", subscale_w, subscale_h);
+            ret = insert_filter(&last_filter, &pad_idx, "subscale", subscale_params);
+            if (ret < 0)
+                return ret;
+        }
 
-    /* sub2video structure has been (re-)initialized.
-       Mark it as such so that the system will be
-       initialized with the first received heartbeat. */
-    ist->sub2video.initialize = 1;
+        av_log(NULL, AV_LOG_INFO, "Auto-inserting graphicsub2video filter\n");
+        ret = insert_filter(&last_filter, &pad_idx, "graphicsub2video", NULL);
+        if (ret < 0)
+            return ret;
+    }
+
+    if ((ret = avfilter_link(last_filter, 0, in->filter_ctx, in->pad_idx)) < 0)
+        return ret;
 
     return 0;
+fail:
+    av_freep(&par);
+
+    return ret;
 }
 
 static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter,
@@ -701,8 +822,15 @@ static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter,
     char name[255];
     int ret, pad_idx = 0;
     int64_t tsoffset = 0;
-    AVBufferSrcParameters *par = av_buffersrc_parameters_alloc();
+    AVBufferSrcParameters *par;
 
+    if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_SUBTITLE) {
+        // Automatically insert conversion filter to retain compatibility
+        // with sub2video command lines
+        return configure_input_subtitle_filter(fg, ifilter, in);
+    }
+
+    par = av_buffersrc_parameters_alloc();
     if (!par)
         return AVERROR(ENOMEM);
     memset(par, 0, sizeof(*par));
@@ -717,12 +845,6 @@ static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter,
     if (!fr.num)
         fr = av_guess_frame_rate(input_files[ist->file_index]->ctx, ist->st, NULL);
 
-    if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_SUBTITLE) {
-        ret = sub2video_prepare(ist, ifilter);
-        if (ret < 0)
-            goto fail;
-    }
-
     sar = ifilter->sample_aspect_ratio;
     if(!sar.den)
         sar = (AVRational){0,1};
@@ -734,7 +856,7 @@ static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter,
              tb.num, tb.den, sar.num, sar.den);
     if (fr.num && fr.den)
         av_bprintf(&args, ":frame_rate=%d/%d", fr.num, fr.den);
-    snprintf(name, sizeof(name), "graph %d input from stream %d:%d", fg->index,
+    snprintf(name, sizeof(name), "graph %d video input from stream %d:%d", fg->index,
              ist->file_index, ist->st->index);
 
 
@@ -932,6 +1054,7 @@ static int configure_input_filter(FilterGraph *fg, InputFilter *ifilter,
     switch (avfilter_pad_get_type(in->filter_ctx->input_pads, in->pad_idx)) {
     case AVMEDIA_TYPE_VIDEO: return configure_input_video_filter(fg, ifilter, in);
     case AVMEDIA_TYPE_AUDIO: return configure_input_audio_filter(fg, ifilter, in);
+    case AVMEDIA_TYPE_SUBTITLE: return configure_input_subtitle_filter(fg, ifilter, in);
     default: av_assert0(0); return 0;
     }
 }
@@ -1125,19 +1248,6 @@ int configure_filtergraph(FilterGraph *fg)
         }
     }
 
-    /* process queued up subtitle packets */
-    for (i = 0; i < fg->nb_inputs; i++) {
-        InputStream *ist = fg->inputs[i]->ist;
-        if (ist->sub2video.sub_queue && ist->sub2video.frame) {
-            while (av_fifo_size(ist->sub2video.sub_queue)) {
-                AVSubtitle tmp;
-                av_fifo_generic_read(ist->sub2video.sub_queue, &tmp, sizeof(tmp), NULL);
-                sub2video_update(ist, INT64_MIN, &tmp);
-                avsubtitle_free(&tmp);
-            }
-        }
-    }
-
     return 0;
 
 fail:
@@ -1160,6 +1270,7 @@ int ifilter_parameters_from_frame(InputFilter *ifilter, const AVFrame *frame)
     ifilter->sample_rate         = frame->sample_rate;
     ifilter->channels            = frame->channels;
     ifilter->channel_layout      = frame->channel_layout;
+    ifilter->type                = frame->type;
 
     av_freep(&ifilter->displaymatrix);
     sd = av_frame_get_side_data(frame, AV_FRAME_DATA_DISPLAYMATRIX);
diff --git a/fftools/ffmpeg_hw.c b/fftools/ffmpeg_hw.c
index 14e702bd92..be69d54aaf 100644
--- a/fftools/ffmpeg_hw.c
+++ b/fftools/ffmpeg_hw.c
@@ -449,7 +449,7 @@ int hw_device_setup_for_encode(OutputStream *ost)
     AVBufferRef *frames_ref = NULL;
     int i;
 
-    if (ost->filter) {
+    if (ost->filter && ost->filter->filter) {
         frames_ref = av_buffersink_get_hw_frames_ctx(ost->filter->filter);
         if (frames_ref &&
             ((AVHWFramesContext*)frames_ref->data)->format ==
diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c
index 9c820ab73f..476ef628e4 100644
--- a/fftools/ffmpeg_opt.c
+++ b/fftools/ffmpeg_opt.c
@@ -2211,8 +2211,9 @@ static void init_output_filter(OutputFilter *ofilter, OptionsContext *o,
     switch (ofilter->type) {
     case AVMEDIA_TYPE_VIDEO: ost = new_video_stream(o, oc, -1); break;
     case AVMEDIA_TYPE_AUDIO: ost = new_audio_stream(o, oc, -1); break;
+    case AVMEDIA_TYPE_SUBTITLE: ost = new_subtitle_stream(o, oc, -1); break;
     default:
-        av_log(NULL, AV_LOG_FATAL, "Only video and audio filters are supported "
+        av_log(NULL, AV_LOG_FATAL, "Only video, audio and subtitle filters are supported "
                "currently.\n");
         exit_program(1);
     }
diff --git a/tests/ref/fate/filter-overlay-dvdsub-2397 b/tests/ref/fate/filter-overlay-dvdsub-2397
index 483e5fa4e0..42042b967c 100644
--- a/tests/ref/fate/filter-overlay-dvdsub-2397
+++ b/tests/ref/fate/filter-overlay-dvdsub-2397
@@ -490,368 +490,368 @@
 1,       3877,       3877,       10,     2013, 0x95a39f9c
 1,       3887,       3887,       10,     2013, 0x4f7ea123
 1,       3897,       3897,       10,     2013, 0x9efb9ba1
-0,        117,        117,        1,   518400, 0xbf8523da
+0,        117,        117,        1,   518400, 0xc44e1d4c
 1,       3907,       3907,       10,     2013, 0xf395b2cd
 1,       3917,       3917,       10,     2013, 0x261a881e
 1,       3927,       3927,       10,     2013, 0x7f2d9f72
 1,       3937,       3937,       10,     2013, 0x0105b38d
-0,        118,        118,        1,   518400, 0x41890ed6
+0,        118,        118,        1,   518400, 0xad1a084c
 1,       3952,       3952,       10,     2013, 0x0e5db67e
 1,       3962,       3962,       10,     2013, 0xfc9baf97
-0,        119,        119,        1,   518400, 0x588534fc
+0,        119,        119,        1,   518400, 0x52d52e73
 1,       3972,       3972,       10,     2013, 0x8e02a1b1
 1,       3982,       3982,       10,     2013, 0x6eecaac8
 1,       3992,       3992,       10,     2013, 0xf5558f0c
 1,       4002,       4002,       10,     2013, 0x512ba99b
-0,        120,        120,        1,   518400, 0x2145ebc1
+0,        120,        120,        1,   518400, 0xc732e546
 1,       4012,       4012,       10,     2013, 0x932b9932
 1,       4022,       4022,       10,     2013, 0xc01ea987
-0,        121,        121,        1,   518400, 0x28bca595
+0,        121,        121,        1,   518400, 0xc36f9f14
 1,       4038,       4038,       10,     2013, 0x10879cf7
 1,       4048,       4048,       10,     2013, 0x90679338
 1,       4058,       4058,       10,     2013, 0x077d8a9e
 1,       4068,       4068,       10,     2013, 0x969fa57c
-0,        122,        122,        1,   518400, 0x77dc951e
+0,        122,        122,        1,   518400, 0x78428e8b
 1,       4078,       4078,       10,     2013, 0xe049ab07
 1,       4088,       4088,       10,     2013, 0xf535b3b3
 1,       4098,       4098,       10,     2013, 0xfe76bd37
-0,        123,        123,        1,   518400, 0xe8924c17
+0,        123,        123,        1,   518400, 0xf0f8458d
 1,       4108,       4108,       10,     2013, 0xde79ad8c
 1,       4123,       4123,       10,     2013, 0xe89b9c47
 1,       4133,       4133,       10,     2013, 0xc570b0f0
-0,        124,        124,        1,   518400, 0xadb4cccc
+0,        124,        124,        1,   518400, 0x7083c653
 1,       4143,       4143,       10,     2013, 0xee709cd9
 1,       4153,       4153,       10,     2013, 0xcfe5afab
 1,       4163,       4163,       10,     2013, 0x98ff8ce4
-0,        125,        125,        1,   518400, 0x1d7b56ac
+0,        125,        125,        1,   518400, 0xa105502c
 1,       4173,       4173,       10,     2013, 0x9d19b44c
 1,       4183,       4183,       10,     2013, 0x4349917a
 1,       4193,       4193,       10,     2013, 0xbf54a59a
-0,        126,        126,        1,   518400, 0xad5739a4
+0,        126,        126,        1,   518400, 0xd411331a
 1,       4208,       4208,       10,     2013, 0xc4a399e0
 1,       4218,       4218,       10,     2013, 0x1bf58ff0
 1,       4228,       4228,       10,     2013, 0x3518ac56
-0,        127,        127,        1,   518400, 0x2733d35a
+0,        127,        127,        1,   518400, 0x83b0ccdb
 1,       4238,       4238,       10,     2013, 0xcd38c1de
 1,       4248,       4248,       10,     2013, 0xbe7d9c4d
 1,       4258,       4258,       10,     2013, 0xe113a306
 1,       4268,       4268,       10,     2013, 0x083197ea
-0,        128,        128,        1,   518400, 0x78e76da2
+0,        128,        128,        1,   518400, 0xa9be671a
 1,       4278,       4278,       10,     2013, 0x1929b1eb
 1,       4294,       4294,       10,     2013, 0x5d6ea5af
 1,       4304,       4304,       10,     2013, 0x05519d53
-0,        129,        129,        1,   518400, 0x6c076013
+0,        129,        129,        1,   518400, 0xaeb75983
 1,       4314,       4314,       10,     2013, 0x5773b380
 1,       4324,       4324,       10,     2013, 0xaa70a8f5
 1,       4334,       4334,       10,     2013, 0x990db0ec
-0,        130,        130,        1,   518400, 0x7854f2b1
+0,        130,        130,        1,   518400, 0x81f8ec13
 1,       4344,       4344,       10,     2013, 0x91d3a623
 1,       4354,       4354,       10,     2013, 0xc91f9824
 1,       4364,       4364,       10,     2013, 0x1d058abf
-0,        131,        131,        1,   518400, 0xd2ae1ecd
+0,        131,        131,        1,   518400, 0x8aaa1839
 1,       4379,       4379,       10,     2013, 0x8de1b8d5
 1,       4389,       4389,       10,     2013, 0x7872b06b
 1,       4399,       4399,       10,     2013, 0xa084c203
-0,        132,        132,        1,   518400, 0xf5eab38d
+0,        132,        132,        1,   518400, 0xc98bacf5
 1,       4409,       4409,       10,     2013, 0xff90ae8d
 1,       4419,       4419,       10,     2013, 0x61dead8e
 1,       4429,       4429,       10,     2013, 0xee76b284
-0,        133,        133,        1,   518400, 0x994d3e9c
+0,        133,        133,        1,   518400, 0x31083804
 1,       4439,       4439,       10,     2013, 0xe888af7f
 1,       4449,       4449,       10,     2013, 0x5d57b115
 1,       4464,       4464,       10,     2013, 0xcdbfb1d0
-0,        134,        134,        1,   518400, 0x95ab705a
+0,        134,        134,        1,   518400, 0x540a69dc
 1,       4474,       4474,       10,     2013, 0x2e28a952
 1,       4484,       4484,       10,     2013, 0x4795a994
 1,       4494,       4494,       10,     2013, 0x7e7ea304
 1,       4504,       4504,       10,     2013, 0x9502c1e1
-0,        135,        135,        1,   518400, 0x3c83c5ce
+0,        135,        135,        1,   518400, 0x80d3bf46
 1,       4514,       4514,       10,     2013, 0xf7c78ab2
 1,       4524,       4524,       10,     2013, 0x24049816
 1,       4534,       4534,       10,     2013, 0x52089dcf
-0,        136,        136,        1,   518400, 0xfa22c508
+0,        136,        136,        1,   518400, 0x2967be7f
 1,       4550,       4550,       10,     2013, 0x2150a0b1
 1,       4560,       4560,       10,     2013, 0x3c2e9b93
 1,       4570,       4570,       10,     2013, 0x491f932b
-0,        137,        137,        1,   518400, 0xddda1712
+0,        137,        137,        1,   518400, 0x5a3b1092
 1,       4580,       4580,       10,     2013, 0x31359cf8
 1,       4590,       4590,       10,     2013, 0x1b00ac3f
 1,       4600,       4600,       10,     2013, 0x8d7ab3cb
-0,        138,        138,        1,   518400, 0x985a3b93
+0,        138,        138,        1,   518400, 0x8741350b
 1,       4610,       4610,       10,     2013, 0xb2c2a4de
 1,       4620,       4620,       10,     2013, 0x80a4abf2
 1,       4635,       4635,       10,     2013, 0x0701a4ee
-0,        139,        139,        1,   518400, 0xea63c5e7
+0,        139,        139,        1,   518400, 0xd5a9bf60
 1,       4645,       4645,       10,     2013, 0xdc1ba5bc
 1,       4655,       4655,       10,     2013, 0x6083a8a4
 1,       4665,       4665,       10,     2013, 0x6226ad45
-0,        140,        140,        1,   518400, 0xef64983d
+0,        140,        140,        1,   518400, 0xc05f91ba
 1,       4675,       4675,       10,     2013, 0x2732a205
 1,       4685,       4685,       10,     2013, 0x0f62a0d3
 1,       4695,       4695,       10,     2013, 0xc1799249
-0,        141,        141,        1,   518400, 0x747bb193
+0,        141,        141,        1,   518400, 0x3fdaab0b
 1,       4705,       4705,       10,     2013, 0xbccfa9c8
 1,       4720,       4720,       10,     2013, 0xded096e7
 1,       4730,       4730,       10,     2013, 0x7f0daf43
-0,        142,        142,        1,   518400, 0xb8748862
+0,        142,        142,        1,   518400, 0xab7281d9
 1,       4740,       4740,       10,     2013, 0xc47ea682
 1,       4750,       4750,       10,     2013, 0x5a72b07a
 1,       4760,       4760,       10,     2013, 0x386faa8c
 1,       4770,       4770,       10,     2013, 0xf9919a91
-0,        143,        143,        1,   518400, 0xaab55a5f
+0,        143,        143,        1,   518400, 0xc80053d6
 1,       4780,       4780,       10,     2013, 0x4908897e
 1,       4790,       4790,       10,     2013, 0x4882b594
-0,        144,        144,        1,   518400, 0x7b468add
+0,        144,        144,        1,   518400, 0x6526845c
 1,       4806,       4806,       10,     2013, 0x113e98d1
 1,       4816,       4816,       10,     2013, 0x5098b30d
 1,       4826,       4826,       10,     2013, 0x0ef7b857
 1,       4836,       4836,       10,     2013, 0x216ea176
-0,        145,        145,        1,   518400, 0xf2078707
+0,        145,        145,        1,   518400, 0x1b788089
 1,       4846,       4846,       10,     2013, 0xf906944a
 1,       4856,       4856,       10,     2013, 0xee9b92fb
 1,       4866,       4866,       10,     2013, 0xd6029209
-0,        146,        146,        1,   518400, 0x6a2d931e
+0,        146,        146,        1,   518400, 0xfa8e8ca9
 1,       4876,       4876,       10,     2013, 0x2256a12e
 1,       4891,       4891,       10,     2013, 0x89de8e4a
 1,       4901,       4901,       10,     2013, 0x0bf0a584
-0,        147,        147,        1,   518400, 0xbbe3c417
+0,        147,        147,        1,   518400, 0xb278bda1
 1,       4911,       4911,       10,     2013, 0x6a5ebd58
 1,       4921,       4921,       10,     2013, 0x3edd9aa4
 1,       4931,       4931,       10,     2013, 0xbd66ac26
-0,        148,        148,        1,   518400, 0x6294e449
+0,        148,        148,        1,   518400, 0xb0c3ddca
 1,       4941,       4941,       10,     2013, 0x313896ea
 1,       4951,       4951,       10,     2013, 0x6b83a6a0
 1,       4961,       4961,       10,     2013, 0x9aafb109
-0,        149,        149,        1,   518400, 0xa05721e7
+0,        149,        149,        1,   518400, 0x10351b53
 1,       4976,       4976,       10,     2013, 0x5192a85a
 1,       4986,       4986,       10,     2013, 0x1f919f79
 1,       4996,       4996,       10,     2013, 0xc0799c40
-0,        150,        150,        1,   518400, 0x37749183
+0,        150,        150,        1,   518400, 0xc1408aee
 1,       5006,       5006,       10,     2013, 0x2988bcd8
 1,       5016,       5016,       10,     2013, 0x1482913a
 1,       5026,       5026,       10,     2013, 0x74da9a94
 1,       5036,       5036,       10,     2013, 0x763eb709
-0,        151,        151,        1,   518400, 0xf9d9dca0
+0,        151,        151,        1,   518400, 0xf016d615
 1,       5046,       5046,       10,     2013, 0x1285b405
 1,       5062,       5062,       10,     2013, 0xb6ab9dfc
-0,        152,        152,        1,   518400, 0x5f8ccf08
+0,        152,        152,        1,   518400, 0xa768c892
 1,       5072,       5072,       10,     2013, 0xe4c8bf19
 1,       5082,       5082,       10,     2013, 0xabbbade8
 1,       5092,       5092,       10,     2013, 0xf8b69d89
 1,       5102,       5102,       10,     2013, 0xce04a866
-0,        153,        153,        1,   518400, 0x7303f77b
+0,        153,        153,        1,   518400, 0x11c3f11e
 1,       5112,       5112,       10,     2013, 0x07528abf
 1,       5122,       5122,       10,     2013, 0x74fb98bf
 1,       5132,       5132,       10,     2013, 0x579fb1c9
-0,        154,        154,        1,   518400, 0x22b0513f
+0,        154,        154,        1,   518400, 0xcd9a4ac4
 1,       5147,       5147,       10,     2013, 0x7ddea2ed
 1,       5157,       5157,       10,     2013, 0x296caa2c
 1,       5167,       5167,       10,     2013, 0x346d9c4f
-0,        155,        155,        1,   518400, 0x330485d2
+0,        155,        155,        1,   518400, 0x4ade7f5e
 1,       5177,       5177,       10,     2013, 0x3e1fba15
 1,       5187,       5187,       10,     2013, 0x48a2908f
 1,       5197,       5197,       10,     2013, 0xc1938d09
-0,        156,        156,        1,   518400, 0x7f83daea
+0,        156,        156,        1,   518400, 0x655dd46b
 1,       5207,       5207,       10,     2013, 0x0e96a060
 1,       5217,       5217,       10,     2013, 0x7b6a9e06
 1,       5232,       5232,       10,     2013, 0x5b779d28
-0,        157,        157,        1,   518400, 0xee19f2df
+0,        157,        157,        1,   518400, 0x5ab5ec61
 1,       5242,       5242,       10,     2013, 0xf600aca1
 1,       5252,       5252,       10,     2013, 0x3a6c9e68
 1,       5262,       5262,       10,     2013, 0x0c8dc1b0
-0,        158,        158,        1,   518400, 0xb71b1c77
+0,        158,        158,        1,   518400, 0x45dc15e6
 1,       5272,       5272,       10,     2013, 0x26beb245
 1,       5282,       5282,       10,     2013, 0x2bc09557
 1,       5292,       5292,       10,     2013, 0x27fc8845
 1,       5302,       5302,       10,     2013, 0x1025aa47
-0,        159,        159,        1,   518400, 0xbffc1856
+0,        159,        159,        1,   518400, 0x201911d3
 1,       5318,       5318,       10,     2013, 0xc2e69baa
 1,       5328,       5328,       10,     2013, 0xdb249b92
 1,       5338,       5338,       10,     2013, 0x6ccda29e
-0,        160,        160,        1,   518400, 0xabc125aa
+0,        160,        160,        1,   518400, 0x0fbc1f46
 1,       5348,       5348,       10,     2013, 0xeaf6a1cf
 1,       5358,       5358,       10,     2013, 0x509ba397
 1,       5368,       5368,       10,     2013, 0xfaf8a2df
-0,        161,        161,        1,   518400, 0x5ee467f8
+0,        161,        161,        1,   518400, 0x7e316179
 1,       5378,       5378,       10,     2013, 0x41388f28
 1,       5388,       5388,       10,     2013, 0xfe5eab39
 1,       5403,       5403,       10,     2013, 0xd5ffa066
-0,        162,        162,        1,   518400, 0x6c2cf168
+0,        162,        162,        1,   518400, 0x73bbeaed
 1,       5413,       5413,       10,     2013, 0x6813a30a
 1,       5423,       5423,       10,     2013, 0x9be89718
 1,       5433,       5433,       10,     2013, 0xaec3a27b
-0,        163,        163,        1,   518400, 0x63996b26
+0,        163,        163,        1,   518400, 0x3a7c648a
 1,       5446,       5446,       10,     2013, 0x579a983e
 1,       5456,       5456,       10,     2013, 0x98cea21f
 1,       5466,       5466,       10,     2013, 0xca77a58a
-0,        164,        164,        1,   518400, 0xb34d789a
+0,        164,        164,        1,   518400, 0x9f707209
 1,       5476,       5476,       10,     2013, 0xcbc3b1ee
 1,       5486,       5486,       10,     2013, 0xf3bb8f07
 1,       5496,       5496,       10,     2013, 0x6aeebd92
-0,        165,        165,        1,   518400, 0xf49c030f
+0,        165,        165,        1,   518400, 0x9f25fc5c
 1,       5506,       5506,       10,     2013, 0xe955a449
 1,       5516,       5516,       10,     2013, 0x9436aa5b
 1,       5531,       5531,       10,     2013, 0x4f0a8f9f
-0,        166,        166,        1,   518400, 0x092dc41a
+0,        166,        166,        1,   518400, 0x2ed8bd75
 1,       5541,       5541,       10,     2013, 0x3551b22d
 1,       5551,       5551,       10,     2013, 0x0959a3d4
 1,       5561,       5561,       10,     2013, 0x2ed5a11b
 1,       5571,       5571,       10,     2013, 0x8f52a5c3
-0,        167,        167,        1,   518400, 0x4134c577
+0,        167,        167,        1,   518400, 0xb493becb
 1,       5581,       5581,       10,     2013, 0x6552978d
 1,       5591,       5591,       10,     2013, 0x7dcca0c1
 1,       5601,       5601,       10,     2013, 0xbcd4a3c9
-0,        168,        168,        1,   518400, 0x261de1ed
+0,        168,        168,        1,   518400, 0x7df6db57
 1,       5616,       5616,       10,     2013, 0xfe41a8d8
 1,       5626,       5626,       10,     2013, 0xc85aae14
 1,       5636,       5636,       10,     2013, 0x1185b346
-0,        169,        169,        1,   518400, 0xcbc8566a
+0,        169,        169,        1,   518400, 0x1cb94fca
 1,       5646,       5646,       10,     2013, 0xf7429a0d
 1,       5656,       5656,       10,     2013, 0x48c2a160
 1,       5666,       5666,       10,     2013, 0x9d85a85d
-0,        170,        170,        1,   518400, 0x407a5c76
+0,        170,        170,        1,   518400, 0x70db55d8
 1,       5676,       5676,       10,     2013, 0xbbe89fe9
 1,       5686,       5686,       10,     2013, 0xea429fe2
 1,       5702,       5702,       10,     2013, 0x221ca1d4
-0,        171,        171,        1,   518400, 0x1ed73bb2
+0,        171,        171,        1,   518400, 0xc1d9351b
 1,       5712,       5712,       10,     2013, 0x394b925b
 1,       5722,       5722,       10,     2013, 0x556dc26f
 1,       5732,       5732,       10,     2013, 0xce21a5e1
-0,        172,        172,        1,   518400, 0x8467ddb5
+0,        172,        172,        1,   518400, 0xa4b0d717
 1,       5742,       5742,       10,     2013, 0xbc87c0a8
 1,       5752,       5752,       10,     2013, 0xbac4ac07
 1,       5762,       5762,       10,     2013, 0xdeefa4aa
 1,       5772,       5772,       10,     2013, 0x1f15b362
-0,        173,        173,        1,   518400, 0x0523dc73
+0,        173,        173,        1,   518400, 0x3730d5e9
 1,       5787,       5787,       10,     2013, 0x6406b7b2
 1,       5797,       5797,       10,     2013, 0x8030a03d
-0,        174,        174,        1,   518400, 0x81f5e895
+0,        174,        174,        1,   518400, 0x9673e1ec
 1,       5807,       5807,       10,     2013, 0x0373a5b1
 1,       5817,       5817,       10,     2013, 0x34ef93da
 1,       5827,       5827,       10,     2013, 0x94c198fe
 1,       5837,       5837,       10,     2013, 0xfefcabad
-0,        175,        175,        1,   518400, 0xfc74608d
+0,        175,        175,        1,   518400, 0x877959d5
 1,       5847,       5847,       10,     2013, 0x8755b3ec
 1,       5857,       5857,       10,     2013, 0xe436a6fd
 1,       5872,       5872,       10,     2013, 0x9cf5a11e
-0,        176,        176,        1,   518400, 0xc4e0dae0
+0,        176,        176,        1,   518400, 0x04f3d421
 1,       5882,       5882,       10,     2013, 0x03b8a98c
 1,       5892,       5892,       10,     2013, 0x6216a138
 1,       5902,       5902,       10,     2013, 0xd87b9f12
-0,        177,        177,        1,   518400, 0x98367f5b
+0,        177,        177,        1,   518400, 0x4f3078bc
 1,       5912,       5912,       10,     2013, 0x4ce99653
 1,       5922,       5922,       10,     2013, 0x6c2ea9e2
 1,       5932,       5932,       10,     2013, 0x918cae4c
-0,        178,        178,        1,   518400, 0x0f1a869d
+0,        178,        178,        1,   518400, 0x8a127ff8
 1,       5942,       5942,       10,     2013, 0xd19fa5f2
 1,       5958,       5958,       10,     2013, 0x0bdda7c6
 1,       5968,       5968,       10,     2013, 0x0f9ab0ca
-0,        179,        179,        1,   518400, 0x45b6ccf2
+0,        179,        179,        1,   518400, 0x5864c64f
 1,       5978,       5978,       10,     2013, 0x410a92b1
 1,       5988,       5988,       10,     2013, 0xcfbe9d1c
 1,       5998,       5998,       10,     2013, 0x59ed9d15
-0,        180,        180,        1,   518400, 0x5f9ccb77
+0,        180,        180,        1,   518400, 0xdaccc4c0
 1,       6008,       6008,       10,     2013, 0x4e129e27
 1,       6018,       6018,       10,     2013, 0x7bb9ac0a
 1,       6028,       6028,       10,     2013, 0x826ca82b
-0,        181,        181,        1,   518400, 0x5f15ea31
+0,        181,        181,        1,   518400, 0xd999e376
 1,       6043,       6043,       10,     2013, 0x9ad5a74b
 1,       6053,       6053,       10,     2013, 0x6c5f969a
 1,       6063,       6063,       10,     2013, 0x8479a0e5
-0,        182,        182,        1,   518400, 0x86369f27
+0,        182,        182,        1,   518400, 0x8af39876
 1,       6073,       6073,       10,     2013, 0x165298ef
 1,       6083,       6083,       10,     2013, 0xdcadb4a1
 1,       6093,       6093,       10,     2013, 0xa90e987c
 1,       6103,       6103,       10,     2013, 0x1ac5b510
-0,        183,        183,        1,   518400, 0x2e27f9fa
+0,        183,        183,        1,   518400, 0x5e72f33d
 1,       6113,       6113,       10,     2013, 0x66728d85
 1,       6128,       6128,       10,     2013, 0xe4859fc5
 1,       6138,       6138,       10,     2013, 0x9901786e
-0,        184,        184,        1,   518400, 0xc029a44d
+0,        184,        184,        1,   518400, 0x14af9d92
 1,       6148,       6148,       10,     2013, 0x6aebb406
 1,       6158,       6158,       10,     2013, 0x7d13a2cc
 1,       6168,       6168,       10,     2013, 0x99b7a8cc
-0,        185,        185,        1,   518400, 0xebee33b0
+0,        185,        185,        1,   518400, 0x50b82d10
 1,       6178,       6178,       10,     2013, 0x80b8a624
 1,       6188,       6188,       10,     2013, 0xbb6aa271
 1,       6198,       6198,       10,     2013, 0x17af9e4a
-0,        186,        186,        1,   518400, 0x19e5494f
+0,        186,        186,        1,   518400, 0xc068429c
 1,       6214,       6214,       10,     2013, 0xfaf0a8f1
 1,       6224,       6224,       10,     2013, 0xd6849b93
 1,       6234,       6234,       10,     2013, 0xe9829669
-0,        187,        187,        1,   518400, 0xf697bd7c
+0,        187,        187,        1,   518400, 0x8934b6d1
 1,       6244,       6244,       10,     2013, 0x7ec98944
 1,       6254,       6254,       10,     2013, 0x2b2099a4
 1,       6264,       6264,       10,     2013, 0x1033a82f
-0,        188,        188,        1,   518400, 0x82569002
+0,        188,        188,        1,   518400, 0x11d08947
 1,       6274,       6274,       10,     2013, 0x5ec88990
 1,       6284,       6284,       10,     2013, 0xd2a19b3d
 1,       6299,       6299,       10,     2013, 0xa377b268
-0,        189,        189,        1,   518400, 0xfcb6d707
+0,        189,        189,        1,   518400, 0x8a27d041
 1,       6309,       6309,       10,     2013, 0xfa859901
 1,       6319,       6319,       10,     2013, 0x1713955a
 1,       6329,       6329,       10,     2013, 0x70aab0da
 1,       6339,       6339,       10,     2013, 0xcdaea422
-0,        190,        190,        1,   518400, 0x82a9662b
+0,        190,        190,        1,   518400, 0xab265f7d
 1,       6349,       6349,       10,     2013, 0x65c3bf80
 1,       6359,       6359,       10,     2013, 0x1d75a55f
 1,       6369,       6369,       10,     2013, 0xa5bea4de
-0,        191,        191,        1,   518400, 0x212e16ee
+0,        191,        191,        1,   518400, 0xff491040
 1,       6384,       6384,       10,     2013, 0x184db71c
 1,       6394,       6394,       10,     2013, 0x99858ec8
 1,       6404,       6404,       10,     2013, 0xb8f2aee5
-0,        192,        192,        1,   518400, 0x2ca34dca
+0,        192,        192,        1,   518400, 0x822b4704
 1,       6414,       6414,       10,     2013, 0x4435b2ef
 1,       6424,       6424,       10,     2013, 0x8acfa6c7
 1,       6434,       6434,       10,     2013, 0x42b4c01f
-0,        193,        193,        1,   518400, 0xe9ebe0a5
+0,        193,        193,        1,   518400, 0x4523d9f4
 1,       6444,       6444,       10,     2013, 0x6e308c13
 1,       6454,       6454,       10,     2013, 0x8227a0f6
 1,       6470,       6470,       10,     2013, 0x6f12a7a2
-0,        194,        194,        1,   518400, 0x4e6b6917
+0,        194,        194,        1,   518400, 0xfc3c626e
 1,       6480,       6480,       10,     2013, 0x785392be
 1,       6490,       6490,       10,     2013, 0x81849c2b
 1,       6500,       6500,       10,     2013, 0x5cf2af65
-0,        195,        195,        1,   518400, 0x7dcf20ab
+0,        195,        195,        1,   518400, 0x237319e5
 1,       6510,       6510,       10,     2013, 0x0c6ca6b4
 1,       6520,       6520,       10,     2013, 0x412fab9f
 1,       6530,       6530,       10,     2013, 0x08e792b4
-0,        196,        196,        1,   518400, 0xf30fac97
+0,        196,        196,        1,   518400, 0x892ca5d8
 1,       6540,       6540,       10,     2013, 0x407aace3
 1,       6555,       6555,       10,     2013, 0xd26bac16
 1,       6565,       6565,       10,     2013, 0xac8bb295
-0,        197,        197,        1,   518400, 0xcb9fc692
+0,        197,        197,        1,   518400, 0xc4c0bfc7
 1,       6575,       6575,       10,     2013, 0xddd1949c
 1,       6585,       6585,       10,     2013, 0x6b26b868
 1,       6595,       6595,       10,     2013, 0x5eaba587
 1,       6605,       6605,       10,     2013, 0xef0793b9
-0,        198,        198,        1,   518400, 0x5d05601e
+0,        198,        198,        1,   518400, 0x57c85956
 1,       6615,       6615,       10,     2013, 0xdef19bd6
 1,       6625,       6625,       10,     2013, 0xca98a635
-0,        199,        199,        1,   518400, 0x456c1417
+0,        199,        199,        1,   518400, 0xd6300d46
 1,       6640,       6640,       10,     2013, 0x06269a5a
 1,       6650,       6650,       10,     2013, 0x32cb9952
 1,       6660,       6660,       10,     2013, 0xf01fa95a
 1,       6670,       6670,       10,     2013, 0xefab9e55
-0,        200,        200,        1,   518400, 0x9a0fd1ad
+0,        200,        200,        1,   518400, 0xd3dacaec
 1,       6680,       6680,       10,     2013, 0x55a3b63a
 1,       6690,       6690,       10,     2013, 0xcd36a553
 1,       6700,       6700,       10,     2013, 0x2ec19877
-0,        201,        201,        1,   518400, 0x55db9716
+0,        201,        201,        1,   518400, 0x65429052
 1,       6710,       6710,       10,     2013, 0xc18b924c
 1,       6726,       6726,       10,     2013, 0xf132b04c
 1,       6736,       6736,       10,     2013, 0x7975a44d
-0,        202,        202,        1,   518400, 0x1f0d40d6
+0,        202,        202,        1,   518400, 0xec803a15
 1,       6746,       6746,       10,     2013, 0x2aaf94cb
 1,       6756,       6756,       10,     2013, 0x58cfa60f
 1,       6766,       6766,       10,     2013, 0x9757a658
-0,        203,        203,        1,   518400, 0x73695c82
+0,        203,        203,        1,   518400, 0x7a9a55c9
 1,       6776,       6776,       10,     2013, 0x67ebc0d5
 1,       6786,       6786,       10,     2013, 0x3c50a70e
 1,       6796,       6796,       10,     2013, 0x9c5799c6
-0,        204,        204,        1,   518400, 0xb0f10812
+0,        204,        204,        1,   518400, 0xcac30160
 1,       6811,       6811,       10,     2013, 0x018d85b2
 1,       6821,       6821,       10,     2013, 0x5367a956
-0,        205,        205,        1,   518400, 0xdec18505
-0,        208,        208,        1,   518400, 0xb147b947
-0,        240,        240,        1,   518400, 0x9d2e3977
+0,        205,        205,        1,   518400, 0x7e187e4f
+0,        208,        208,        1,   518400, 0x0be0b2a2
+0,        213,        213,        1,   518400, 0x0be0b2a2
diff --git a/tests/ref/fate/sub-dvb b/tests/ref/fate/sub-dvb
index cbd1801d64..8f33c75d70 100644
--- a/tests/ref/fate/sub-dvb
+++ b/tests/ref/fate/sub-dvb
@@ -1,75 +1,93 @@
 #tb 0: 1/1000000
 #media_type 0: subtitle
 #codec_id 0: dvb_subtitle
-0,   15600000,   15600000,   159000,     1168, 0xd0f89d82
-0,   15759000,   15759000,   159000,       14, 0x064900eb
-0,   15760000,   15760000,   239000,     1544, 0xe60f1751
-0,   15999000,   15999000,   239000,       14, 0x0729010b
-0,   16000000,   16000000,   339000,     1658, 0xbe343093
-0,   16339000,   16339000,   339000,       14, 0x0809012b
-0,   16340000,   16340000,   599000,     2343, 0xc68f07ef
-0,   16939000,   16939000,   599000,       14, 0x08e9014b
-0,   16940000,   16940000,   459000,     2568, 0x0ee657b1
-0,   17399000,   17399000,   459000,       14, 0x09c9016b
-0,   17400000,   17400000,   359000,     3422, 0xba5b63ce
-0,   17759000,   17759000,   359000,       14, 0x0aa9018b
-0,   17760000,   17760000,   219000,     5078, 0x95b19902
-0,   17979000,   17979000,   219000,       14, 0x0b8901ab
-0,   17980000,   17980000,   959000,     5808, 0xc9717b89
-0,   18939000,   18939000,   959000,       14, 0x0c6901cb
-0,   18940000,   18940000,   219000,     6015, 0x0becbfac
-0,   19159000,   19159000,   219000,       14, 0x064900eb
-0,   19160000,   19160000,   259000,     6519, 0xfcd24d26
-0,   19419000,   19419000,   259000,       14, 0x0729010b
-0,   19420000,   19420000,    99000,     7061, 0xf0320408
-0,   19519000,   19519000,    99000,       14, 0x0809012b
-0,   19520000,   19520000,   219000,     4773, 0x66c93074
-0,   19739000,   19739000,   219000,       14, 0x08e9014b
-0,   19740000,   19740000,   219000,     5546, 0x06052c81
-0,   19959000,   19959000,   219000,       14, 0x09c9016b
-0,   19960000,   19960000,   239000,     5754, 0x904f7325
-0,   20199000,   20199000,   239000,       14, 0x0aa9018b
-0,   20200000,   20200000,   139000,     6099, 0xe30cde07
-0,   20339000,   20339000,   139000,       14, 0x0b8901ab
-0,   20340000,   20340000,   799000,     6839, 0x770fcb6c
-0,   21139000,   21139000,   799000,       14, 0x0c6901cb
-0,   21140000,   21140000,   239000,     4744, 0xa91e1b41
-0,   21379000,   21379000,   239000,       14, 0x064900eb
-0,   21380000,   21380000,   339000,     5824, 0xcf6d782b
-0,   21719000,   21719000,   339000,       14, 0x0729010b
-0,   21720000,   21720000,  1439000,     6212, 0xabf8f7cf
-0,   23159000,   23159000,  1439000,       14, 0x0809012b
-0,   23160000,   23160000,  1319000,     7082, 0xd7ca10f2
-0,   24479000,   24479000,  1319000,       14, 0x08e9014b
-0,   24480000,   24480000,   219000,     5345, 0x12b2cae0
-0,   24699000,   24699000,   219000,       14, 0x09c9016b
-0,   24700000,   24700000,   219000,     5765, 0xc7d46192
-0,   24919000,   24919000,   219000,       14, 0x0aa9018b
-0,   24920000,   24920000,   599000,     6557, 0xcb995d30
-0,   25519000,   25519000,   599000,       14, 0x0b8901ab
-0,   25520000,   25520000,   219000,     7091, 0xe6ea0559
-0,   25739000,   25739000,   219000,       14, 0x0c6901cb
-0,   25740000,   25740000,   239000,     7305, 0xb66c404e
-0,   25979000,   25979000,   239000,       14, 0x064900eb
-0,   25980000,   25980000,   359000,     7590, 0x0cc2a481
-0,   26339000,   26339000,   359000,       14, 0x0729010b
-0,   26340000,   26340000,   219000,     4629, 0xe18cfea8
-0,   26559000,   26559000,   219000,       14, 0x0809012b
-0,   26560000,   26560000,   719000,     4785, 0x82043fc0
-0,   27279000,   27279000,   719000,       14, 0x08e9014b
-0,   27280000,   27280000,   459000,     6061, 0xbde7d245
-0,   27739000,   27739000,   459000,       14, 0x09c9016b
-0,   27740000,   27740000,   239000,     6301, 0x92d01a51
-0,   27979000,   27979000,   239000,       14, 0x0aa9018b
-0,   27980000,   27980000,    99000,     6736, 0xbd25a134
-0,   28079000,   28079000,    99000,       14, 0x0b8901ab
-0,   28080000,   28080000,   219000,     7214, 0x7ef93c13
-0,   28299000,   28299000,   219000,       14, 0x0c6901cb
-0,   28300000,   28300000,   239000,     7366, 0x5bed7fcd
-0,   28539000,   28539000,   239000,       14, 0x064900eb
-0,   28540000,   28540000,   599000,     4564, 0x7f4c014b
-0,   29139000,   29139000,   599000,       14, 0x0729010b
-0,   29140000,   29140000,   219000,     4637, 0x682626b7
-0,   29359000,   29359000,   219000,       14, 0x0809012b
-0,   29360000,   29360000,  1679000,     5358, 0x29e30c48
-0,   31039000,   31039000,  1679000,       14, 0x08e9014b
+0,          0,          0,   279000,       14, 0x05d900db
+0,     279000,     279000,   279000,       14, 0x064900eb
+0,     280000,     280000,  4999000,       14, 0x06b900fb
+0,    5279000,    5279000,  4999000,       14, 0x0729010b
+0,    5280000,    5280000,  5019000,       14, 0x0799011b
+0,   10299000,   10299000,  5019000,       14, 0x0809012b
+0,   10300000,   10300000,  3599000,       14, 0x0879013b
+0,   13899000,   13899000,  3599000,       14, 0x08e9014b
+0,   13900000,   13900000,   219000,       14, 0x0959015b
+0,   14119000,   14119000,   219000,       14, 0x09c9016b
+0,   14120000,   14120000,  1439000,       14, 0x0a39017b
+0,   15559000,   15559000,  1439000,       14, 0x0aa9018b
+0,   15560000,   15560000,    39000,       14, 0x0b19019b
+0,   15599000,   15599000,    39000,       14, 0x0b8901ab
+0,   15600000,   15600000,   159000,     1168, 0xd69da022
+0,   15759000,   15759000,   159000,       14, 0x0c6901cb
+0,   15760000,   15760000,   239000,     1544, 0xc5f116f1
+0,   15999000,   15999000,   239000,       14, 0x064900eb
+0,   16000000,   16000000,   339000,     1658, 0x73563033
+0,   16339000,   16339000,   339000,       14, 0x0729010b
+0,   16340000,   16340000,   599000,     2343, 0x7ac2078f
+0,   16939000,   16939000,   599000,       14, 0x0809012b
+0,   16940000,   16940000,   459000,     2568, 0x6eaa5751
+0,   17399000,   17399000,   459000,       14, 0x08e9014b
+0,   17400000,   17400000,   359000,     3422, 0xd9d0636e
+0,   17759000,   17759000,   359000,       14, 0x09c9016b
+0,   17760000,   17760000,   219000,     5078, 0x722c9862
+0,   17979000,   17979000,   219000,       14, 0x0aa9018b
+0,   17980000,   17980000,   959000,     5808, 0x38dd7ae9
+0,   18939000,   18939000,   959000,       14, 0x0b8901ab
+0,   18940000,   18940000,   219000,     6015, 0xd4d2c40c
+0,   19159000,   19159000,   219000,       14, 0x0c6901cb
+0,   19160000,   19160000,   259000,     6519, 0x08af4c86
+0,   19419000,   19419000,   259000,       14, 0x064900eb
+0,   19420000,   19420000,    99000,     7061, 0xecf10368
+0,   19519000,   19519000,    99000,       14, 0x0729010b
+0,   19520000,   19520000,   219000,     4773, 0xbee42fd4
+0,   19739000,   19739000,   219000,       14, 0x0809012b
+0,   19740000,   19740000,   219000,     5546, 0xdb822be1
+0,   19959000,   19959000,   219000,       14, 0x08e9014b
+0,   19960000,   19960000,   239000,     5754, 0xfdcc7285
+0,   20199000,   20199000,   239000,       14, 0x09c9016b
+0,   20200000,   20200000,   139000,     6099, 0xa409dd67
+0,   20339000,   20339000,   139000,       14, 0x0aa9018b
+0,   20340000,   20340000,   799000,     6839, 0xc5eecacc
+0,   21139000,   21139000,   799000,       14, 0x0b8901ab
+0,   21140000,   21140000,   239000,     4744, 0x4e451fa1
+0,   21379000,   21379000,   239000,       14, 0x0c6901cb
+0,   21380000,   21380000,   339000,     5824, 0x5299778b
+0,   21719000,   21719000,   339000,       14, 0x064900eb
+0,   21720000,   21720000,  1439000,     6212, 0x6d15f72f
+0,   23159000,   23159000,  1439000,       14, 0x0729010b
+0,   23160000,   23160000,  1319000,     7082, 0xe5c91052
+0,   24479000,   24479000,  1319000,       14, 0x0809012b
+0,   24480000,   24480000,   219000,     5345, 0x2e5eca40
+0,   24699000,   24699000,   219000,       14, 0x08e9014b
+0,   24700000,   24700000,   219000,     5765, 0x118060f2
+0,   24919000,   24919000,   219000,       14, 0x09c9016b
+0,   24920000,   24920000,   599000,     6557, 0x89275c90
+0,   25519000,   25519000,   599000,       14, 0x0aa9018b
+0,   25520000,   25520000,   219000,     7091, 0x996904b9
+0,   25739000,   25739000,   219000,       14, 0x0b8901ab
+0,   25740000,   25740000,   239000,     7305, 0xc23e44ae
+0,   25979000,   25979000,   239000,       14, 0x0c6901cb
+0,   25980000,   25980000,   359000,     7590, 0xc5a3a3e1
+0,   26339000,   26339000,   359000,       14, 0x064900eb
+0,   26340000,   26340000,   219000,     4629, 0x7ad6fe08
+0,   26559000,   26559000,   219000,       14, 0x0729010b
+0,   26560000,   26560000,   719000,     4785, 0xcd3f3f20
+0,   27279000,   27279000,   719000,       14, 0x0809012b
+0,   27280000,   27280000,   459000,     6061, 0x8b04d1a5
+0,   27739000,   27739000,   459000,       14, 0x08e9014b
+0,   27740000,   27740000,   239000,     6301, 0xe7de19b1
+0,   27979000,   27979000,   239000,       14, 0x09c9016b
+0,   27980000,   27980000,    99000,     6736, 0x38b3a094
+0,   28079000,   28079000,    99000,       14, 0x0aa9018b
+0,   28080000,   28080000,   219000,     7214, 0x0b783b73
+0,   28299000,   28299000,   219000,       14, 0x0b8901ab
+0,   28300000,   28300000,   239000,     7366, 0x98bf842d
+0,   28539000,   28539000,   239000,       14, 0x0c6901cb
+0,   28540000,   28540000,   599000,     4564, 0x3d9600ab
+0,   29139000,   29139000,   599000,       14, 0x064900eb
+0,   29140000,   29140000,   219000,     4637, 0x01f02617
+0,   29359000,   29359000,   219000,       14, 0x0729010b
+0,   29360000,   29360000,  1679000,     5358, 0x5b0f0ba8
+0,   31039000,   31039000,  1679000,       14, 0x0809012b
+0,   31040000,   31040000,   359000,       14, 0x0879013b
+0,   31399000,   31399000,   359000,       14, 0x08e9014b
+0,   31400000,   31400000,   479000,       14, 0x0959015b
+0,   31879000,   31879000,   479000,       14, 0x09c9016b
diff --git a/tests/ref/fate/sub2video b/tests/ref/fate/sub2video
index 80abe9c905..6f9f92b8e0 100644
--- a/tests/ref/fate/sub2video
+++ b/tests/ref/fate/sub2video
@@ -58,129 +58,1136 @@
 0,         47,         47,        1,   518400, 0xde69683f
 0,         48,         48,        1,   518400, 0x7df08fba
 0,         49,         49,        1,   518400, 0xbab197ea
+0,         50,         50,        1,   518400, 0xbab197ea
+0,         51,         51,        1,   518400, 0xbab197ea
+0,         52,         52,        1,   518400, 0xbab197ea
+0,         53,         53,        1,   518400, 0xbab197ea
+0,         54,         54,        1,   518400, 0xbab197ea
+0,         55,         55,        1,   518400, 0xbab197ea
+0,         56,         56,        1,   518400, 0xbab197ea
+0,         57,         57,        1,   518400, 0xbab197ea
+0,         58,         58,        1,   518400, 0xbab197ea
+0,         59,         59,        1,   518400, 0xbab197ea
+0,         60,         60,        1,   518400, 0xbab197ea
+0,         61,         61,        1,   518400, 0xbab197ea
+0,         62,         62,        1,   518400, 0xbab197ea
+0,         63,         63,        1,   518400, 0xbab197ea
+0,         64,         64,        1,   518400, 0xbab197ea
+0,         65,         65,        1,   518400, 0xbab197ea
+0,         66,         66,        1,   518400, 0xbab197ea
+0,         67,         67,        1,   518400, 0xbab197ea
+0,         68,         68,        1,   518400, 0xbab197ea
+0,         69,         69,        1,   518400, 0xbab197ea
+0,         70,         70,        1,   518400, 0xbab197ea
+0,         71,         71,        1,   518400, 0xbab197ea
+0,         72,         72,        1,   518400, 0xbab197ea
+0,         73,         73,        1,   518400, 0xbab197ea
+0,         74,         74,        1,   518400, 0xbab197ea
+0,         75,         75,        1,   518400, 0xbab197ea
+0,         76,         76,        1,   518400, 0xbab197ea
 1,   15355000,   15355000,  4733000,     2094, 0x3c171425
 0,         77,         77,        1,   518400, 0x902285d9
-0,        100,        100,        1,   518400, 0xbab197ea
+0,         78,         78,        1,   518400, 0x902285d9
+0,         79,         79,        1,   518400, 0x902285d9
+0,         80,         80,        1,   518400, 0x902285d9
+0,         81,         81,        1,   518400, 0x902285d9
+0,         82,         82,        1,   518400, 0x902285d9
+0,         83,         83,        1,   518400, 0x902285d9
+0,         84,         84,        1,   518400, 0x902285d9
+0,         85,         85,        1,   518400, 0x902285d9
+0,         86,         86,        1,   518400, 0x902285d9
+0,         87,         87,        1,   518400, 0x902285d9
+0,         88,         88,        1,   518400, 0x902285d9
+0,         89,         89,        1,   518400, 0x902285d9
+0,         90,         90,        1,   518400, 0x902285d9
+0,         91,         91,        1,   518400, 0x902285d9
+0,         92,         92,        1,   518400, 0x902285d9
+0,         93,         93,        1,   518400, 0x902285d9
+0,         94,         94,        1,   518400, 0x902285d9
+0,         95,         95,        1,   518400, 0x902285d9
+0,         96,         96,        1,   518400, 0x902285d9
+0,         97,         97,        1,   518400, 0x902285d9
+0,         98,         98,        1,   518400, 0x902285d9
+0,         99,         99,        1,   518400, 0x902285d9
+0,        100,        100,        1,   518400, 0x902285d9
+0,        101,        101,        1,   518400, 0xbab197ea
+0,        102,        102,        1,   518400, 0xbab197ea
+0,        103,        103,        1,   518400, 0xbab197ea
+0,        104,        104,        1,   518400, 0xbab197ea
+0,        105,        105,        1,   518400, 0xbab197ea
+0,        106,        106,        1,   518400, 0xbab197ea
+0,        107,        107,        1,   518400, 0xbab197ea
+0,        108,        108,        1,   518400, 0xbab197ea
+0,        109,        109,        1,   518400, 0xbab197ea
+0,        110,        110,        1,   518400, 0xbab197ea
+0,        111,        111,        1,   518400, 0xbab197ea
+0,        112,        112,        1,   518400, 0xbab197ea
+0,        113,        113,        1,   518400, 0xbab197ea
+0,        114,        114,        1,   518400, 0xbab197ea
+0,        115,        115,        1,   518400, 0xbab197ea
+0,        116,        116,        1,   518400, 0xbab197ea
+0,        117,        117,        1,   518400, 0xbab197ea
+0,        118,        118,        1,   518400, 0xbab197ea
+0,        119,        119,        1,   518400, 0xbab197ea
+0,        120,        120,        1,   518400, 0xbab197ea
+0,        121,        121,        1,   518400, 0xbab197ea
+0,        122,        122,        1,   518400, 0xbab197ea
+0,        123,        123,        1,   518400, 0xbab197ea
+0,        124,        124,        1,   518400, 0xbab197ea
+0,        125,        125,        1,   518400, 0xbab197ea
+0,        126,        126,        1,   518400, 0xbab197ea
+0,        127,        127,        1,   518400, 0xbab197ea
+0,        128,        128,        1,   518400, 0xbab197ea
+0,        129,        129,        1,   518400, 0xbab197ea
+0,        130,        130,        1,   518400, 0xbab197ea
+0,        131,        131,        1,   518400, 0xbab197ea
+0,        132,        132,        1,   518400, 0xbab197ea
+0,        133,        133,        1,   518400, 0xbab197ea
+0,        134,        134,        1,   518400, 0xbab197ea
+0,        135,        135,        1,   518400, 0xbab197ea
+0,        136,        136,        1,   518400, 0xbab197ea
+0,        137,        137,        1,   518400, 0xbab197ea
+0,        138,        138,        1,   518400, 0xbab197ea
+0,        139,        139,        1,   518400, 0xbab197ea
+0,        140,        140,        1,   518400, 0xbab197ea
+0,        141,        141,        1,   518400, 0xbab197ea
+0,        142,        142,        1,   518400, 0xbab197ea
+0,        143,        143,        1,   518400, 0xbab197ea
+0,        144,        144,        1,   518400, 0xbab197ea
+0,        145,        145,        1,   518400, 0xbab197ea
+0,        146,        146,        1,   518400, 0xbab197ea
+0,        147,        147,        1,   518400, 0xbab197ea
+0,        148,        148,        1,   518400, 0xbab197ea
+0,        149,        149,        1,   518400, 0xbab197ea
+0,        150,        150,        1,   518400, 0xbab197ea
+0,        151,        151,        1,   518400, 0xbab197ea
+0,        152,        152,        1,   518400, 0xbab197ea
+0,        153,        153,        1,   518400, 0xbab197ea
+0,        154,        154,        1,   518400, 0xbab197ea
+0,        155,        155,        1,   518400, 0xbab197ea
+0,        156,        156,        1,   518400, 0xbab197ea
+0,        157,        157,        1,   518400, 0xbab197ea
+0,        158,        158,        1,   518400, 0xbab197ea
+0,        159,        159,        1,   518400, 0xbab197ea
+0,        160,        160,        1,   518400, 0xbab197ea
+0,        161,        161,        1,   518400, 0xbab197ea
+0,        162,        162,        1,   518400, 0xbab197ea
+0,        163,        163,        1,   518400, 0xbab197ea
+0,        164,        164,        1,   518400, 0xbab197ea
+0,        165,        165,        1,   518400, 0xbab197ea
+0,        166,        166,        1,   518400, 0xbab197ea
+0,        167,        167,        1,   518400, 0xbab197ea
+0,        168,        168,        1,   518400, 0xbab197ea
+0,        169,        169,        1,   518400, 0xbab197ea
+0,        170,        170,        1,   518400, 0xbab197ea
+0,        171,        171,        1,   518400, 0xbab197ea
+0,        172,        172,        1,   518400, 0xbab197ea
+0,        173,        173,        1,   518400, 0xbab197ea
+0,        174,        174,        1,   518400, 0xbab197ea
+0,        175,        175,        1,   518400, 0xbab197ea
+0,        176,        176,        1,   518400, 0xbab197ea
+0,        177,        177,        1,   518400, 0xbab197ea
+0,        178,        178,        1,   518400, 0xbab197ea
+0,        179,        179,        1,   518400, 0xbab197ea
+0,        180,        180,        1,   518400, 0xbab197ea
+0,        181,        181,        1,   518400, 0xbab197ea
+0,        182,        182,        1,   518400, 0xbab197ea
+0,        183,        183,        1,   518400, 0xbab197ea
+0,        184,        184,        1,   518400, 0xbab197ea
+0,        185,        185,        1,   518400, 0xbab197ea
+0,        186,        186,        1,   518400, 0xbab197ea
+0,        187,        187,        1,   518400, 0xbab197ea
+0,        188,        188,        1,   518400, 0xbab197ea
+0,        189,        189,        1,   518400, 0xbab197ea
+0,        190,        190,        1,   518400, 0xbab197ea
+0,        191,        191,        1,   518400, 0xbab197ea
+0,        192,        192,        1,   518400, 0xbab197ea
+0,        193,        193,        1,   518400, 0xbab197ea
+0,        194,        194,        1,   518400, 0xbab197ea
+0,        195,        195,        1,   518400, 0xbab197ea
+0,        196,        196,        1,   518400, 0xbab197ea
+0,        197,        197,        1,   518400, 0xbab197ea
+0,        198,        198,        1,   518400, 0xbab197ea
+0,        199,        199,        1,   518400, 0xbab197ea
+0,        200,        200,        1,   518400, 0xbab197ea
+0,        201,        201,        1,   518400, 0xbab197ea
+0,        202,        202,        1,   518400, 0xbab197ea
+0,        203,        203,        1,   518400, 0xbab197ea
+0,        204,        204,        1,   518400, 0xbab197ea
+0,        205,        205,        1,   518400, 0xbab197ea
+0,        206,        206,        1,   518400, 0xbab197ea
+0,        207,        207,        1,   518400, 0xbab197ea
+0,        208,        208,        1,   518400, 0xbab197ea
+0,        209,        209,        1,   518400, 0xbab197ea
+0,        210,        210,        1,   518400, 0xbab197ea
+0,        211,        211,        1,   518400, 0xbab197ea
+0,        212,        212,        1,   518400, 0xbab197ea
+0,        213,        213,        1,   518400, 0xbab197ea
+0,        214,        214,        1,   518400, 0xbab197ea
+0,        215,        215,        1,   518400, 0xbab197ea
+0,        216,        216,        1,   518400, 0xbab197ea
+0,        217,        217,        1,   518400, 0xbab197ea
+0,        218,        218,        1,   518400, 0xbab197ea
+0,        219,        219,        1,   518400, 0xbab197ea
+0,        220,        220,        1,   518400, 0xbab197ea
+0,        221,        221,        1,   518400, 0xbab197ea
+0,        222,        222,        1,   518400, 0xbab197ea
+0,        223,        223,        1,   518400, 0xbab197ea
+0,        224,        224,        1,   518400, 0xbab197ea
+0,        225,        225,        1,   518400, 0xbab197ea
+0,        226,        226,        1,   518400, 0xbab197ea
+0,        227,        227,        1,   518400, 0xbab197ea
+0,        228,        228,        1,   518400, 0xbab197ea
+0,        229,        229,        1,   518400, 0xbab197ea
+0,        230,        230,        1,   518400, 0xbab197ea
+0,        231,        231,        1,   518400, 0xbab197ea
+0,        232,        232,        1,   518400, 0xbab197ea
+0,        233,        233,        1,   518400, 0xbab197ea
+0,        234,        234,        1,   518400, 0xbab197ea
+0,        235,        235,        1,   518400, 0xbab197ea
+0,        236,        236,        1,   518400, 0xbab197ea
+0,        237,        237,        1,   518400, 0xbab197ea
+0,        238,        238,        1,   518400, 0xbab197ea
+0,        239,        239,        1,   518400, 0xbab197ea
+0,        240,        240,        1,   518400, 0xbab197ea
+0,        241,        241,        1,   518400, 0xbab197ea
+0,        242,        242,        1,   518400, 0xbab197ea
+0,        243,        243,        1,   518400, 0xbab197ea
 1,   48797000,   48797000,  2560000,     2480, 0x7c0edf21
 0,        244,        244,        1,   518400, 0x7a11c812
-0,        257,        257,        1,   518400, 0xbab197ea
+0,        245,        245,        1,   518400, 0x7a11c812
+0,        246,        246,        1,   518400, 0x7a11c812
+0,        247,        247,        1,   518400, 0x7a11c812
+0,        248,        248,        1,   518400, 0x7a11c812
+0,        249,        249,        1,   518400, 0x7a11c812
+0,        250,        250,        1,   518400, 0x7a11c812
+0,        251,        251,        1,   518400, 0x7a11c812
+0,        252,        252,        1,   518400, 0x7a11c812
+0,        253,        253,        1,   518400, 0x7a11c812
+0,        254,        254,        1,   518400, 0x7a11c812
+0,        255,        255,        1,   518400, 0x7a11c812
+0,        256,        256,        1,   518400, 0x7a11c812
+0,        257,        257,        1,   518400, 0x7a11c812
 1,   51433000,   51433000,  2366000,     3059, 0xc95b8a05
 0,        258,        258,        1,   518400, 0x34cdddee
-0,        269,        269,        1,   518400, 0xbab197ea
+0,        259,        259,        1,   518400, 0x34cdddee
+0,        260,        260,        1,   518400, 0x34cdddee
+0,        261,        261,        1,   518400, 0x34cdddee
+0,        262,        262,        1,   518400, 0x34cdddee
+0,        263,        263,        1,   518400, 0x34cdddee
+0,        264,        264,        1,   518400, 0x34cdddee
+0,        265,        265,        1,   518400, 0x34cdddee
+0,        266,        266,        1,   518400, 0x34cdddee
+0,        267,        267,        1,   518400, 0x34cdddee
+0,        268,        268,        1,   518400, 0x34cdddee
+0,        269,        269,        1,   518400, 0x34cdddee
 1,   53910000,   53910000,  2696000,     2095, 0x61bb15ed
 0,        270,        270,        1,   518400, 0x4db4ce51
-0,        283,        283,        1,   518400, 0xbab197ea
+0,        271,        271,        1,   518400, 0x4db4ce51
+0,        272,        272,        1,   518400, 0x4db4ce51
+0,        273,        273,        1,   518400, 0x4db4ce51
+0,        274,        274,        1,   518400, 0x4db4ce51
+0,        275,        275,        1,   518400, 0x4db4ce51
+0,        276,        276,        1,   518400, 0x4db4ce51
+0,        277,        277,        1,   518400, 0x4db4ce51
+0,        278,        278,        1,   518400, 0x4db4ce51
+0,        279,        279,        1,   518400, 0x4db4ce51
+0,        280,        280,        1,   518400, 0x4db4ce51
+0,        281,        281,        1,   518400, 0x4db4ce51
+0,        282,        282,        1,   518400, 0x4db4ce51
+0,        283,        283,        1,   518400, 0x4db4ce51
 1,   56663000,   56663000,  1262000,     1013, 0xc9ae89b7
 0,        284,        284,        1,   518400, 0xe6bc0ea9
-0,        290,        290,        1,   518400, 0xbab197ea
+0,        285,        285,        1,   518400, 0xe6bc0ea9
+0,        286,        286,        1,   518400, 0xe6bc0ea9
+0,        287,        287,        1,   518400, 0xe6bc0ea9
+0,        288,        288,        1,   518400, 0xe6bc0ea9
+0,        289,        289,        1,   518400, 0xe6bc0ea9
+0,        290,        290,        1,   518400, 0xe6bc0ea9
 1,   58014000,   58014000,  1661000,      969, 0xe01878f0
 0,        291,        291,        1,   518400, 0xa8643af7
-0,        298,        298,        1,   518400, 0xbab197ea
+0,        292,        292,        1,   518400, 0xa8643af7
+0,        293,        293,        1,   518400, 0xa8643af7
+0,        294,        294,        1,   518400, 0xa8643af7
+0,        295,        295,        1,   518400, 0xa8643af7
+0,        296,        296,        1,   518400, 0xa8643af7
+0,        297,        297,        1,   518400, 0xa8643af7
+0,        298,        298,        1,   518400, 0xa8643af7
+0,        299,        299,        1,   518400, 0xbab197ea
+0,        300,        300,        1,   518400, 0xbab197ea
+0,        301,        301,        1,   518400, 0xbab197ea
+0,        302,        302,        1,   518400, 0xbab197ea
+0,        303,        303,        1,   518400, 0xbab197ea
+0,        304,        304,        1,   518400, 0xbab197ea
+0,        305,        305,        1,   518400, 0xbab197ea
+0,        306,        306,        1,   518400, 0xbab197ea
+0,        307,        307,        1,   518400, 0xbab197ea
+0,        308,        308,        1,   518400, 0xbab197ea
+0,        309,        309,        1,   518400, 0xbab197ea
+0,        310,        310,        1,   518400, 0xbab197ea
+0,        311,        311,        1,   518400, 0xbab197ea
+0,        312,        312,        1,   518400, 0xbab197ea
+0,        313,        313,        1,   518400, 0xbab197ea
+0,        314,        314,        1,   518400, 0xbab197ea
+0,        315,        315,        1,   518400, 0xbab197ea
+0,        316,        316,        1,   518400, 0xbab197ea
+0,        317,        317,        1,   518400, 0xbab197ea
+0,        318,        318,        1,   518400, 0xbab197ea
+0,        319,        319,        1,   518400, 0xbab197ea
+0,        320,        320,        1,   518400, 0xbab197ea
+0,        321,        321,        1,   518400, 0xbab197ea
+0,        322,        322,        1,   518400, 0xbab197ea
+0,        323,        323,        1,   518400, 0xbab197ea
+0,        324,        324,        1,   518400, 0xbab197ea
+0,        325,        325,        1,   518400, 0xbab197ea
+0,        326,        326,        1,   518400, 0xbab197ea
+0,        327,        327,        1,   518400, 0xbab197ea
+0,        328,        328,        1,   518400, 0xbab197ea
+0,        329,        329,        1,   518400, 0xbab197ea
+0,        330,        330,        1,   518400, 0xbab197ea
+0,        331,        331,        1,   518400, 0xbab197ea
+0,        332,        332,        1,   518400, 0xbab197ea
+0,        333,        333,        1,   518400, 0xbab197ea
+0,        334,        334,        1,   518400, 0xbab197ea
+0,        335,        335,        1,   518400, 0xbab197ea
+0,        336,        336,        1,   518400, 0xbab197ea
+0,        337,        337,        1,   518400, 0xbab197ea
+0,        338,        338,        1,   518400, 0xbab197ea
 1,   67724000,   67724000,  1365000,      844, 0xe7db4fc1
 0,        339,        339,        1,   518400, 0xb1885c67
-0,        345,        345,        1,   518400, 0xbab197ea
+0,        340,        340,        1,   518400, 0xb1885c67
+0,        341,        341,        1,   518400, 0xb1885c67
+0,        342,        342,        1,   518400, 0xb1885c67
+0,        343,        343,        1,   518400, 0xb1885c67
+0,        344,        344,        1,   518400, 0xb1885c67
+0,        345,        345,        1,   518400, 0xb1885c67
 1,   69175000,   69175000,  1558000,      802, 0xf48531ba
 0,        346,        346,        1,   518400, 0x378e3fd0
-0,        354,        354,        1,   518400, 0xbab197ea
+0,        347,        347,        1,   518400, 0x378e3fd0
+0,        348,        348,        1,   518400, 0x378e3fd0
+0,        349,        349,        1,   518400, 0x378e3fd0
+0,        350,        350,        1,   518400, 0x378e3fd0
+0,        351,        351,        1,   518400, 0x378e3fd0
+0,        352,        352,        1,   518400, 0x378e3fd0
+0,        353,        353,        1,   518400, 0x378e3fd0
+0,        354,        354,        1,   518400, 0x378e3fd0
 1,   70819000,   70819000,  1865000,     1709, 0xb4d5a1bd
 0,        355,        355,        1,   518400, 0xa3782469
-0,        363,        363,        1,   518400, 0xbab197ea
+0,        356,        356,        1,   518400, 0xa3782469
+0,        357,        357,        1,   518400, 0xa3782469
+0,        358,        358,        1,   518400, 0xa3782469
+0,        359,        359,        1,   518400, 0xa3782469
+0,        360,        360,        1,   518400, 0xa3782469
+0,        361,        361,        1,   518400, 0xa3782469
+0,        362,        362,        1,   518400, 0xa3782469
+0,        363,        363,        1,   518400, 0xa3782469
 1,   72762000,   72762000,  1968000,     2438, 0x99d7bc82
 0,        364,        364,        1,   518400, 0xba23a0d5
-0,        374,        374,        1,   518400, 0xbab197ea
+0,        365,        365,        1,   518400, 0xba23a0d5
+0,        366,        366,        1,   518400, 0xba23a0d5
+0,        367,        367,        1,   518400, 0xba23a0d5
+0,        368,        368,        1,   518400, 0xba23a0d5
+0,        369,        369,        1,   518400, 0xba23a0d5
+0,        370,        370,        1,   518400, 0xba23a0d5
+0,        371,        371,        1,   518400, 0xba23a0d5
+0,        372,        372,        1,   518400, 0xba23a0d5
+0,        373,        373,        1,   518400, 0xba23a0d5
+0,        374,        374,        1,   518400, 0xba23a0d5
 1,   74806000,   74806000,  1831000,     2116, 0x96514097
 0,        375,        375,        1,   518400, 0x129de2f8
-0,        383,        383,        1,   518400, 0xbab197ea
+0,        376,        376,        1,   518400, 0x129de2f8
+0,        377,        377,        1,   518400, 0x129de2f8
+0,        378,        378,        1,   518400, 0x129de2f8
+0,        379,        379,        1,   518400, 0x129de2f8
+0,        380,        380,        1,   518400, 0x129de2f8
+0,        381,        381,        1,   518400, 0x129de2f8
+0,        382,        382,        1,   518400, 0x129de2f8
+0,        383,        383,        1,   518400, 0x129de2f8
 1,   76716000,   76716000,  1262000,     1822, 0xefccc72e
 0,        384,        384,        1,   518400, 0x19772f0f
-0,        390,        390,        1,   518400, 0xbab197ea
+0,        385,        385,        1,   518400, 0x19772f0f
+0,        386,        386,        1,   518400, 0x19772f0f
+0,        387,        387,        1,   518400, 0x19772f0f
+0,        388,        388,        1,   518400, 0x19772f0f
+0,        389,        389,        1,   518400, 0x19772f0f
+0,        390,        390,        1,   518400, 0x19772f0f
 1,   78051000,   78051000,  1524000,      987, 0x7b927a27
 0,        391,        391,        1,   518400, 0x56f54e73
-0,        398,        398,        1,   518400, 0xbab197ea
+0,        392,        392,        1,   518400, 0x56f54e73
+0,        393,        393,        1,   518400, 0x56f54e73
+0,        394,        394,        1,   518400, 0x56f54e73
+0,        395,        395,        1,   518400, 0x56f54e73
+0,        396,        396,        1,   518400, 0x56f54e73
+0,        397,        397,        1,   518400, 0x56f54e73
+0,        398,        398,        1,   518400, 0x56f54e73
 1,   79644000,   79644000,  2662000,     2956, 0x190778f7
 0,        399,        399,        1,   518400, 0x300b5247
+0,        400,        400,        1,   518400, 0x300b5247
+0,        401,        401,        1,   518400, 0x300b5247
+0,        402,        402,        1,   518400, 0x300b5247
+0,        403,        403,        1,   518400, 0x300b5247
+0,        404,        404,        1,   518400, 0x300b5247
+0,        405,        405,        1,   518400, 0x300b5247
+0,        406,        406,        1,   518400, 0x300b5247
+0,        407,        407,        1,   518400, 0x300b5247
+0,        408,        408,        1,   518400, 0x300b5247
+0,        409,        409,        1,   518400, 0x300b5247
+0,        410,        410,        1,   518400, 0x300b5247
+0,        411,        411,        1,   518400, 0x300b5247
 1,   82380000,   82380000,  2764000,     3094, 0xc021b7d3
-0,        412,        412,        1,   518400, 0xbab197ea
+0,        412,        412,        1,   518400, 0x300b5247
 0,        413,        413,        1,   518400, 0x6fd028fa
-0,        426,        426,        1,   518400, 0xbab197ea
+0,        414,        414,        1,   518400, 0x6fd028fa
+0,        415,        415,        1,   518400, 0x6fd028fa
+0,        416,        416,        1,   518400, 0x6fd028fa
+0,        417,        417,        1,   518400, 0x6fd028fa
+0,        418,        418,        1,   518400, 0x6fd028fa
+0,        419,        419,        1,   518400, 0x6fd028fa
+0,        420,        420,        1,   518400, 0x6fd028fa
+0,        421,        421,        1,   518400, 0x6fd028fa
+0,        422,        422,        1,   518400, 0x6fd028fa
+0,        423,        423,        1,   518400, 0x6fd028fa
+0,        424,        424,        1,   518400, 0x6fd028fa
+0,        425,        425,        1,   518400, 0x6fd028fa
+0,        426,        426,        1,   518400, 0x6fd028fa
 1,   85225000,   85225000,  2366000,     2585, 0x74d0048f
 0,        427,        427,        1,   518400, 0x01f80e9d
-0,        438,        438,        1,   518400, 0xbab197ea
+0,        428,        428,        1,   518400, 0x01f80e9d
+0,        429,        429,        1,   518400, 0x01f80e9d
+0,        430,        430,        1,   518400, 0x01f80e9d
+0,        431,        431,        1,   518400, 0x01f80e9d
+0,        432,        432,        1,   518400, 0x01f80e9d
+0,        433,        433,        1,   518400, 0x01f80e9d
+0,        434,        434,        1,   518400, 0x01f80e9d
+0,        435,        435,        1,   518400, 0x01f80e9d
+0,        436,        436,        1,   518400, 0x01f80e9d
+0,        437,        437,        1,   518400, 0x01f80e9d
+0,        438,        438,        1,   518400, 0x01f80e9d
 1,   87652000,   87652000,  1831000,      634, 0x8832fda1
 0,        439,        439,        1,   518400, 0xb48d90c0
-0,        447,        447,        1,   518400, 0xbab197ea
+0,        440,        440,        1,   518400, 0xb48d90c0
+0,        441,        441,        1,   518400, 0xb48d90c0
+0,        442,        442,        1,   518400, 0xb48d90c0
+0,        443,        443,        1,   518400, 0xb48d90c0
+0,        444,        444,        1,   518400, 0xb48d90c0
+0,        445,        445,        1,   518400, 0xb48d90c0
+0,        446,        446,        1,   518400, 0xb48d90c0
+0,        447,        447,        1,   518400, 0xb48d90c0
+0,        448,        448,        1,   518400, 0xbab197ea
+0,        449,        449,        1,   518400, 0xbab197ea
+0,        450,        450,        1,   518400, 0xbab197ea
+0,        451,        451,        1,   518400, 0xbab197ea
+0,        452,        452,        1,   518400, 0xbab197ea
+0,        453,        453,        1,   518400, 0xbab197ea
+0,        454,        454,        1,   518400, 0xbab197ea
+0,        455,        455,        1,   518400, 0xbab197ea
+0,        456,        456,        1,   518400, 0xbab197ea
+0,        457,        457,        1,   518400, 0xbab197ea
 1,   91531000,   91531000,  2332000,     2080, 0x97a1146f
 0,        458,        458,        1,   518400, 0xcb5a0173
-0,        469,        469,        1,   518400, 0xbab197ea
+0,        459,        459,        1,   518400, 0xcb5a0173
+0,        460,        460,        1,   518400, 0xcb5a0173
+0,        461,        461,        1,   518400, 0xcb5a0173
+0,        462,        462,        1,   518400, 0xcb5a0173
+0,        463,        463,        1,   518400, 0xcb5a0173
+0,        464,        464,        1,   518400, 0xcb5a0173
+0,        465,        465,        1,   518400, 0xcb5a0173
+0,        466,        466,        1,   518400, 0xcb5a0173
+0,        467,        467,        1,   518400, 0xcb5a0173
+0,        468,        468,        1,   518400, 0xcb5a0173
+0,        469,        469,        1,   518400, 0xcb5a0173
+0,        470,        470,        1,   518400, 0xbab197ea
+0,        471,        471,        1,   518400, 0xbab197ea
+0,        472,        472,        1,   518400, 0xbab197ea
+0,        473,        473,        1,   518400, 0xbab197ea
+0,        474,        474,        1,   518400, 0xbab197ea
+0,        475,        475,        1,   518400, 0xbab197ea
+0,        476,        476,        1,   518400, 0xbab197ea
+0,        477,        477,        1,   518400, 0xbab197ea
 1,   95510000,   95510000,  3299000,     2964, 0x8b8f6684
 0,        478,        478,        1,   518400, 0xb8a323e4
-0,        494,        494,        1,   518400, 0xbab197ea
+0,        479,        479,        1,   518400, 0xb8a323e4
+0,        480,        480,        1,   518400, 0xb8a323e4
+0,        481,        481,        1,   518400, 0xb8a323e4
+0,        482,        482,        1,   518400, 0xb8a323e4
+0,        483,        483,        1,   518400, 0xb8a323e4
+0,        484,        484,        1,   518400, 0xb8a323e4
+0,        485,        485,        1,   518400, 0xb8a323e4
+0,        486,        486,        1,   518400, 0xb8a323e4
+0,        487,        487,        1,   518400, 0xb8a323e4
+0,        488,        488,        1,   518400, 0xb8a323e4
+0,        489,        489,        1,   518400, 0xb8a323e4
+0,        490,        490,        1,   518400, 0xb8a323e4
+0,        491,        491,        1,   518400, 0xb8a323e4
+0,        492,        492,        1,   518400, 0xb8a323e4
+0,        493,        493,        1,   518400, 0xb8a323e4
+0,        494,        494,        1,   518400, 0xb8a323e4
 1,   98872000,   98872000,  2161000,     1875, 0x9002ef71
 0,        495,        495,        1,   518400, 0xc43518ba
-0,        505,        505,        1,   518400, 0xbab197ea
+0,        496,        496,        1,   518400, 0xc43518ba
+0,        497,        497,        1,   518400, 0xc43518ba
+0,        498,        498,        1,   518400, 0xc43518ba
+0,        499,        499,        1,   518400, 0xc43518ba
+0,        500,        500,        1,   518400, 0xc43518ba
+0,        501,        501,        1,   518400, 0xc43518ba
+0,        502,        502,        1,   518400, 0xc43518ba
+0,        503,        503,        1,   518400, 0xc43518ba
+0,        504,        504,        1,   518400, 0xc43518ba
+0,        505,        505,        1,   518400, 0xc43518ba
 1,  101124000,  101124000,  4096000,     3872, 0x20c6ed9c
 0,        506,        506,        1,   518400, 0x04e38692
-0,        526,        526,        1,   518400, 0xbab197ea
+0,        507,        507,        1,   518400, 0x04e38692
+0,        508,        508,        1,   518400, 0x04e38692
+0,        509,        509,        1,   518400, 0x04e38692
+0,        510,        510,        1,   518400, 0x04e38692
+0,        511,        511,        1,   518400, 0x04e38692
+0,        512,        512,        1,   518400, 0x04e38692
+0,        513,        513,        1,   518400, 0x04e38692
+0,        514,        514,        1,   518400, 0x04e38692
+0,        515,        515,        1,   518400, 0x04e38692
+0,        516,        516,        1,   518400, 0x04e38692
+0,        517,        517,        1,   518400, 0x04e38692
+0,        518,        518,        1,   518400, 0x04e38692
+0,        519,        519,        1,   518400, 0x04e38692
+0,        520,        520,        1,   518400, 0x04e38692
+0,        521,        521,        1,   518400, 0x04e38692
+0,        522,        522,        1,   518400, 0x04e38692
+0,        523,        523,        1,   518400, 0x04e38692
+0,        524,        524,        1,   518400, 0x04e38692
+0,        525,        525,        1,   518400, 0x04e38692
+0,        526,        526,        1,   518400, 0x04e38692
 1,  105303000,  105303000,  2730000,     3094, 0xf203a663
 0,        527,        527,        1,   518400, 0x856b0ee5
-0,        540,        540,        1,   518400, 0xbab197ea
+0,        528,        528,        1,   518400, 0x856b0ee5
+0,        529,        529,        1,   518400, 0x856b0ee5
+0,        530,        530,        1,   518400, 0x856b0ee5
+0,        531,        531,        1,   518400, 0x856b0ee5
+0,        532,        532,        1,   518400, 0x856b0ee5
+0,        533,        533,        1,   518400, 0x856b0ee5
+0,        534,        534,        1,   518400, 0x856b0ee5
+0,        535,        535,        1,   518400, 0x856b0ee5
+0,        536,        536,        1,   518400, 0x856b0ee5
+0,        537,        537,        1,   518400, 0x856b0ee5
+0,        538,        538,        1,   518400, 0x856b0ee5
+0,        539,        539,        1,   518400, 0x856b0ee5
+0,        540,        540,        1,   518400, 0x856b0ee5
 1,  108106000,  108106000,  2059000,     2404, 0x41a7b429
 0,        541,        541,        1,   518400, 0x3e5beee2
-0,        551,        551,        1,   518400, 0xbab197ea
+0,        542,        542,        1,   518400, 0x3e5beee2
+0,        543,        543,        1,   518400, 0x3e5beee2
+0,        544,        544,        1,   518400, 0x3e5beee2
+0,        545,        545,        1,   518400, 0x3e5beee2
+0,        546,        546,        1,   518400, 0x3e5beee2
+0,        547,        547,        1,   518400, 0x3e5beee2
+0,        548,        548,        1,   518400, 0x3e5beee2
+0,        549,        549,        1,   518400, 0x3e5beee2
+0,        550,        550,        1,   518400, 0x3e5beee2
+0,        551,        551,        1,   518400, 0x3e5beee2
+0,        552,        552,        1,   518400, 0xbab197ea
+0,        553,        553,        1,   518400, 0xbab197ea
+0,        554,        554,        1,   518400, 0xbab197ea
+0,        555,        555,        1,   518400, 0xbab197ea
+0,        556,        556,        1,   518400, 0xbab197ea
+0,        557,        557,        1,   518400, 0xbab197ea
+0,        558,        558,        1,   518400, 0xbab197ea
+0,        559,        559,        1,   518400, 0xbab197ea
+0,        560,        560,        1,   518400, 0xbab197ea
+0,        561,        561,        1,   518400, 0xbab197ea
+0,        562,        562,        1,   518400, 0xbab197ea
+0,        563,        563,        1,   518400, 0xbab197ea
+0,        564,        564,        1,   518400, 0xbab197ea
+0,        565,        565,        1,   518400, 0xbab197ea
+0,        566,        566,        1,   518400, 0xbab197ea
+0,        567,        567,        1,   518400, 0xbab197ea
+0,        568,        568,        1,   518400, 0xbab197ea
+0,        569,        569,        1,   518400, 0xbab197ea
+0,        570,        570,        1,   518400, 0xbab197ea
+0,        571,        571,        1,   518400, 0xbab197ea
+0,        572,        572,        1,   518400, 0xbab197ea
+0,        573,        573,        1,   518400, 0xbab197ea
+0,        574,        574,        1,   518400, 0xbab197ea
+0,        575,        575,        1,   518400, 0xbab197ea
+0,        576,        576,        1,   518400, 0xbab197ea
+0,        577,        577,        1,   518400, 0xbab197ea
+0,        578,        578,        1,   518400, 0xbab197ea
+0,        579,        579,        1,   518400, 0xbab197ea
+0,        580,        580,        1,   518400, 0xbab197ea
+0,        581,        581,        1,   518400, 0xbab197ea
+0,        582,        582,        1,   518400, 0xbab197ea
+0,        583,        583,        1,   518400, 0xbab197ea
+0,        584,        584,        1,   518400, 0xbab197ea
+0,        585,        585,        1,   518400, 0xbab197ea
+0,        586,        586,        1,   518400, 0xbab197ea
+0,        587,        587,        1,   518400, 0xbab197ea
+0,        588,        588,        1,   518400, 0xbab197ea
+0,        589,        589,        1,   518400, 0xbab197ea
+0,        590,        590,        1,   518400, 0xbab197ea
+0,        591,        591,        1,   518400, 0xbab197ea
+0,        592,        592,        1,   518400, 0xbab197ea
+0,        593,        593,        1,   518400, 0xbab197ea
+0,        594,        594,        1,   518400, 0xbab197ea
+0,        595,        595,        1,   518400, 0xbab197ea
+0,        596,        596,        1,   518400, 0xbab197ea
+0,        597,        597,        1,   518400, 0xbab197ea
+0,        598,        598,        1,   518400, 0xbab197ea
+0,        599,        599,        1,   518400, 0xbab197ea
+0,        600,        600,        1,   518400, 0xbab197ea
+0,        601,        601,        1,   518400, 0xbab197ea
+0,        602,        602,        1,   518400, 0xbab197ea
+0,        603,        603,        1,   518400, 0xbab197ea
+0,        604,        604,        1,   518400, 0xbab197ea
+0,        605,        605,        1,   518400, 0xbab197ea
+0,        606,        606,        1,   518400, 0xbab197ea
+0,        607,        607,        1,   518400, 0xbab197ea
+0,        608,        608,        1,   518400, 0xbab197ea
+0,        609,        609,        1,   518400, 0xbab197ea
+0,        610,        610,        1,   518400, 0xbab197ea
+0,        611,        611,        1,   518400, 0xbab197ea
+0,        612,        612,        1,   518400, 0xbab197ea
+0,        613,        613,        1,   518400, 0xbab197ea
+0,        614,        614,        1,   518400, 0xbab197ea
+0,        615,        615,        1,   518400, 0xbab197ea
+0,        616,        616,        1,   518400, 0xbab197ea
+0,        617,        617,        1,   518400, 0xbab197ea
+0,        618,        618,        1,   518400, 0xbab197ea
+0,        619,        619,        1,   518400, 0xbab197ea
+0,        620,        620,        1,   518400, 0xbab197ea
+0,        621,        621,        1,   518400, 0xbab197ea
+0,        622,        622,        1,   518400, 0xbab197ea
+0,        623,        623,        1,   518400, 0xbab197ea
+0,        624,        624,        1,   518400, 0xbab197ea
+0,        625,        625,        1,   518400, 0xbab197ea
+0,        626,        626,        1,   518400, 0xbab197ea
+0,        627,        627,        1,   518400, 0xbab197ea
+0,        628,        628,        1,   518400, 0xbab197ea
+0,        629,        629,        1,   518400, 0xbab197ea
+0,        630,        630,        1,   518400, 0xbab197ea
+0,        631,        631,        1,   518400, 0xbab197ea
+0,        632,        632,        1,   518400, 0xbab197ea
+0,        633,        633,        1,   518400, 0xbab197ea
+0,        634,        634,        1,   518400, 0xbab197ea
+0,        635,        635,        1,   518400, 0xbab197ea
+0,        636,        636,        1,   518400, 0xbab197ea
+0,        637,        637,        1,   518400, 0xbab197ea
+0,        638,        638,        1,   518400, 0xbab197ea
+0,        639,        639,        1,   518400, 0xbab197ea
+0,        640,        640,        1,   518400, 0xbab197ea
+0,        641,        641,        1,   518400, 0xbab197ea
+0,        642,        642,        1,   518400, 0xbab197ea
+0,        643,        643,        1,   518400, 0xbab197ea
+0,        644,        644,        1,   518400, 0xbab197ea
+0,        645,        645,        1,   518400, 0xbab197ea
+0,        646,        646,        1,   518400, 0xbab197ea
+0,        647,        647,        1,   518400, 0xbab197ea
+0,        648,        648,        1,   518400, 0xbab197ea
+0,        649,        649,        1,   518400, 0xbab197ea
+0,        650,        650,        1,   518400, 0xbab197ea
+0,        651,        651,        1,   518400, 0xbab197ea
+0,        652,        652,        1,   518400, 0xbab197ea
+0,        653,        653,        1,   518400, 0xbab197ea
+0,        654,        654,        1,   518400, 0xbab197ea
+0,        655,        655,        1,   518400, 0xbab197ea
+0,        656,        656,        1,   518400, 0xbab197ea
+0,        657,        657,        1,   518400, 0xbab197ea
+0,        658,        658,        1,   518400, 0xbab197ea
+0,        659,        659,        1,   518400, 0xbab197ea
+0,        660,        660,        1,   518400, 0xbab197ea
+0,        661,        661,        1,   518400, 0xbab197ea
+0,        662,        662,        1,   518400, 0xbab197ea
+0,        663,        663,        1,   518400, 0xbab197ea
+0,        664,        664,        1,   518400, 0xbab197ea
+0,        665,        665,        1,   518400, 0xbab197ea
+0,        666,        666,        1,   518400, 0xbab197ea
+0,        667,        667,        1,   518400, 0xbab197ea
+0,        668,        668,        1,   518400, 0xbab197ea
+0,        669,        669,        1,   518400, 0xbab197ea
+0,        670,        670,        1,   518400, 0xbab197ea
+0,        671,        671,        1,   518400, 0xbab197ea
+0,        672,        672,        1,   518400, 0xbab197ea
+0,        673,        673,        1,   518400, 0xbab197ea
+0,        674,        674,        1,   518400, 0xbab197ea
+0,        675,        675,        1,   518400, 0xbab197ea
+0,        676,        676,        1,   518400, 0xbab197ea
+0,        677,        677,        1,   518400, 0xbab197ea
+0,        678,        678,        1,   518400, 0xbab197ea
+0,        679,        679,        1,   518400, 0xbab197ea
+0,        680,        680,        1,   518400, 0xbab197ea
+0,        681,        681,        1,   518400, 0xbab197ea
+0,        682,        682,        1,   518400, 0xbab197ea
+0,        683,        683,        1,   518400, 0xbab197ea
+0,        684,        684,        1,   518400, 0xbab197ea
+0,        685,        685,        1,   518400, 0xbab197ea
+0,        686,        686,        1,   518400, 0xbab197ea
+0,        687,        687,        1,   518400, 0xbab197ea
+0,        688,        688,        1,   518400, 0xbab197ea
+0,        689,        689,        1,   518400, 0xbab197ea
+0,        690,        690,        1,   518400, 0xbab197ea
+0,        691,        691,        1,   518400, 0xbab197ea
+0,        692,        692,        1,   518400, 0xbab197ea
+0,        693,        693,        1,   518400, 0xbab197ea
+0,        694,        694,        1,   518400, 0xbab197ea
+0,        695,        695,        1,   518400, 0xbab197ea
+0,        696,        696,        1,   518400, 0xbab197ea
+0,        697,        697,        1,   518400, 0xbab197ea
+0,        698,        698,        1,   518400, 0xbab197ea
+0,        699,        699,        1,   518400, 0xbab197ea
+0,        700,        700,        1,   518400, 0xbab197ea
+0,        701,        701,        1,   518400, 0xbab197ea
+0,        702,        702,        1,   518400, 0xbab197ea
+0,        703,        703,        1,   518400, 0xbab197ea
+0,        704,        704,        1,   518400, 0xbab197ea
+0,        705,        705,        1,   518400, 0xbab197ea
+0,        706,        706,        1,   518400, 0xbab197ea
+0,        707,        707,        1,   518400, 0xbab197ea
 1,  141556000,  141556000,  1661000,     1088, 0xde20aa20
 0,        708,        708,        1,   518400, 0xb8bc1365
-0,        716,        716,        1,   518400, 0xbab197ea
+0,        709,        709,        1,   518400, 0xb8bc1365
+0,        710,        710,        1,   518400, 0xb8bc1365
+0,        711,        711,        1,   518400, 0xb8bc1365
+0,        712,        712,        1,   518400, 0xb8bc1365
+0,        713,        713,        1,   518400, 0xb8bc1365
+0,        714,        714,        1,   518400, 0xb8bc1365
+0,        715,        715,        1,   518400, 0xb8bc1365
+0,        716,        716,        1,   518400, 0xb8bc1365
+0,        717,        717,        1,   518400, 0xbab197ea
+0,        718,        718,        1,   518400, 0xbab197ea
+0,        719,        719,        1,   518400, 0xbab197ea
+0,        720,        720,        1,   518400, 0xbab197ea
+0,        721,        721,        1,   518400, 0xbab197ea
+0,        722,        722,        1,   518400, 0xbab197ea
+0,        723,        723,        1,   518400, 0xbab197ea
+0,        724,        724,        1,   518400, 0xbab197ea
+0,        725,        725,        1,   518400, 0xbab197ea
+0,        726,        726,        1,   518400, 0xbab197ea
+0,        727,        727,        1,   518400, 0xbab197ea
+0,        728,        728,        1,   518400, 0xbab197ea
+0,        729,        729,        1,   518400, 0xbab197ea
+0,        730,        730,        1,   518400, 0xbab197ea
+0,        731,        731,        1,   518400, 0xbab197ea
+0,        732,        732,        1,   518400, 0xbab197ea
+0,        733,        733,        1,   518400, 0xbab197ea
+0,        734,        734,        1,   518400, 0xbab197ea
+0,        735,        735,        1,   518400, 0xbab197ea
+0,        736,        736,        1,   518400, 0xbab197ea
+0,        737,        737,        1,   518400, 0xbab197ea
+0,        738,        738,        1,   518400, 0xbab197ea
+0,        739,        739,        1,   518400, 0xbab197ea
+0,        740,        740,        1,   518400, 0xbab197ea
+0,        741,        741,        1,   518400, 0xbab197ea
+0,        742,        742,        1,   518400, 0xbab197ea
+0,        743,        743,        1,   518400, 0xbab197ea
+0,        744,        744,        1,   518400, 0xbab197ea
+0,        745,        745,        1,   518400, 0xbab197ea
+0,        746,        746,        1,   518400, 0xbab197ea
+0,        747,        747,        1,   518400, 0xbab197ea
+0,        748,        748,        1,   518400, 0xbab197ea
+0,        749,        749,        1,   518400, 0xbab197ea
+0,        750,        750,        1,   518400, 0xbab197ea
+0,        751,        751,        1,   518400, 0xbab197ea
+0,        752,        752,        1,   518400, 0xbab197ea
+0,        753,        753,        1,   518400, 0xbab197ea
+0,        754,        754,        1,   518400, 0xbab197ea
+0,        755,        755,        1,   518400, 0xbab197ea
+0,        756,        756,        1,   518400, 0xbab197ea
+0,        757,        757,        1,   518400, 0xbab197ea
+0,        758,        758,        1,   518400, 0xbab197ea
+0,        759,        759,        1,   518400, 0xbab197ea
+0,        760,        760,        1,   518400, 0xbab197ea
+0,        761,        761,        1,   518400, 0xbab197ea
+0,        762,        762,        1,   518400, 0xbab197ea
+0,        763,        763,        1,   518400, 0xbab197ea
+0,        764,        764,        1,   518400, 0xbab197ea
+0,        765,        765,        1,   518400, 0xbab197ea
+0,        766,        766,        1,   518400, 0xbab197ea
+0,        767,        767,        1,   518400, 0xbab197ea
+0,        768,        768,        1,   518400, 0xbab197ea
+0,        769,        769,        1,   518400, 0xbab197ea
+0,        770,        770,        1,   518400, 0xbab197ea
+0,        771,        771,        1,   518400, 0xbab197ea
+0,        772,        772,        1,   518400, 0xbab197ea
+0,        773,        773,        1,   518400, 0xbab197ea
+0,        774,        774,        1,   518400, 0xbab197ea
+0,        775,        775,        1,   518400, 0xbab197ea
+0,        776,        776,        1,   518400, 0xbab197ea
+0,        777,        777,        1,   518400, 0xbab197ea
+0,        778,        778,        1,   518400, 0xbab197ea
+0,        779,        779,        1,   518400, 0xbab197ea
+0,        780,        780,        1,   518400, 0xbab197ea
+0,        781,        781,        1,   518400, 0xbab197ea
+0,        782,        782,        1,   518400, 0xbab197ea
+0,        783,        783,        1,   518400, 0xbab197ea
+0,        784,        784,        1,   518400, 0xbab197ea
+0,        785,        785,        1,   518400, 0xbab197ea
+0,        786,        786,        1,   518400, 0xbab197ea
+0,        787,        787,        1,   518400, 0xbab197ea
+0,        788,        788,        1,   518400, 0xbab197ea
+0,        789,        789,        1,   518400, 0xbab197ea
+0,        790,        790,        1,   518400, 0xbab197ea
+0,        791,        791,        1,   518400, 0xbab197ea
+0,        792,        792,        1,   518400, 0xbab197ea
+0,        793,        793,        1,   518400, 0xbab197ea
+0,        794,        794,        1,   518400, 0xbab197ea
+0,        795,        795,        1,   518400, 0xbab197ea
+0,        796,        796,        1,   518400, 0xbab197ea
+0,        797,        797,        1,   518400, 0xbab197ea
+0,        798,        798,        1,   518400, 0xbab197ea
+0,        799,        799,        1,   518400, 0xbab197ea
+0,        800,        800,        1,   518400, 0xbab197ea
+0,        801,        801,        1,   518400, 0xbab197ea
+0,        802,        802,        1,   518400, 0xbab197ea
+0,        803,        803,        1,   518400, 0xbab197ea
+0,        804,        804,        1,   518400, 0xbab197ea
+0,        805,        805,        1,   518400, 0xbab197ea
+0,        806,        806,        1,   518400, 0xbab197ea
+0,        807,        807,        1,   518400, 0xbab197ea
+0,        808,        808,        1,   518400, 0xbab197ea
+0,        809,        809,        1,   518400, 0xbab197ea
+0,        810,        810,        1,   518400, 0xbab197ea
+0,        811,        811,        1,   518400, 0xbab197ea
+0,        812,        812,        1,   518400, 0xbab197ea
+0,        813,        813,        1,   518400, 0xbab197ea
+0,        814,        814,        1,   518400, 0xbab197ea
+0,        815,        815,        1,   518400, 0xbab197ea
+0,        816,        816,        1,   518400, 0xbab197ea
 0,        817,        817,        1,   518400, 0x83efa32d
 1,  163445000,  163445000,  1331000,      339, 0x8bd186ef
-0,        824,        824,        1,   518400, 0xbab197ea
+0,        818,        818,        1,   518400, 0x83efa32d
+0,        819,        819,        1,   518400, 0x83efa32d
+0,        820,        820,        1,   518400, 0x83efa32d
+0,        821,        821,        1,   518400, 0x83efa32d
+0,        822,        822,        1,   518400, 0x83efa32d
+0,        823,        823,        1,   518400, 0x83efa32d
+0,        824,        824,        1,   518400, 0x83efa32d
+0,        825,        825,        1,   518400, 0xbab197ea
+0,        826,        826,        1,   518400, 0xbab197ea
+0,        827,        827,        1,   518400, 0xbab197ea
+0,        828,        828,        1,   518400, 0xbab197ea
+0,        829,        829,        1,   518400, 0xbab197ea
+0,        830,        830,        1,   518400, 0xbab197ea
+0,        831,        831,        1,   518400, 0xbab197ea
+0,        832,        832,        1,   518400, 0xbab197ea
+0,        833,        833,        1,   518400, 0xbab197ea
+0,        834,        834,        1,   518400, 0xbab197ea
+0,        835,        835,        1,   518400, 0xbab197ea
+0,        836,        836,        1,   518400, 0xbab197ea
+0,        837,        837,        1,   518400, 0xbab197ea
+0,        838,        838,        1,   518400, 0xbab197ea
+0,        839,        839,        1,   518400, 0xbab197ea
 0,        840,        840,        1,   518400, 0x03ea0e90
 1,  168049000,  168049000,  1900000,     1312, 0x0bf20e8d
-0,        850,        850,        1,   518400, 0xbab197ea
+0,        841,        841,        1,   518400, 0x03ea0e90
+0,        842,        842,        1,   518400, 0x03ea0e90
+0,        843,        843,        1,   518400, 0x03ea0e90
+0,        844,        844,        1,   518400, 0x03ea0e90
+0,        845,        845,        1,   518400, 0x03ea0e90
+0,        846,        846,        1,   518400, 0x03ea0e90
+0,        847,        847,        1,   518400, 0x03ea0e90
+0,        848,        848,        1,   518400, 0x03ea0e90
+0,        849,        849,        1,   518400, 0x03ea0e90
+0,        850,        850,        1,   518400, 0x03ea0e90
 1,  170035000,  170035000,  1524000,     1279, 0xb6c2dafe
 0,        851,        851,        1,   518400, 0x8780239e
-0,        858,        858,        1,   518400, 0xbab197ea
+0,        852,        852,        1,   518400, 0x8780239e
+0,        853,        853,        1,   518400, 0x8780239e
+0,        854,        854,        1,   518400, 0x8780239e
+0,        855,        855,        1,   518400, 0x8780239e
+0,        856,        856,        1,   518400, 0x8780239e
+0,        857,        857,        1,   518400, 0x8780239e
+0,        858,        858,        1,   518400, 0x8780239e
+0,        859,        859,        1,   518400, 0xbab197ea
+0,        860,        860,        1,   518400, 0xbab197ea
 0,        861,        861,        1,   518400, 0x6eb72347
 1,  172203000,  172203000,  1695000,     1826, 0x9a1ac769
-0,        869,        869,        1,   518400, 0xbab197ea
+0,        862,        862,        1,   518400, 0x6eb72347
+0,        863,        863,        1,   518400, 0x6eb72347
+0,        864,        864,        1,   518400, 0x6eb72347
+0,        865,        865,        1,   518400, 0x6eb72347
+0,        866,        866,        1,   518400, 0x6eb72347
+0,        867,        867,        1,   518400, 0x6eb72347
+0,        868,        868,        1,   518400, 0x6eb72347
+0,        869,        869,        1,   518400, 0x6eb72347
 1,  173947000,  173947000,  1934000,     1474, 0xa9b03cdc
 0,        870,        870,        1,   518400, 0x9c4a3a3d
-0,        879,        879,        1,   518400, 0xbab197ea
+0,        871,        871,        1,   518400, 0x9c4a3a3d
+0,        872,        872,        1,   518400, 0x9c4a3a3d
+0,        873,        873,        1,   518400, 0x9c4a3a3d
+0,        874,        874,        1,   518400, 0x9c4a3a3d
+0,        875,        875,        1,   518400, 0x9c4a3a3d
+0,        876,        876,        1,   518400, 0x9c4a3a3d
+0,        877,        877,        1,   518400, 0x9c4a3a3d
+0,        878,        878,        1,   518400, 0x9c4a3a3d
+0,        879,        879,        1,   518400, 0x9c4a3a3d
 1,  175957000,  175957000,  1763000,     1019, 0x20409355
 0,        880,        880,        1,   518400, 0xc9ebfa89
-0,        889,        889,        1,   518400, 0xbab197ea
+0,        881,        881,        1,   518400, 0xc9ebfa89
+0,        882,        882,        1,   518400, 0xc9ebfa89
+0,        883,        883,        1,   518400, 0xc9ebfa89
+0,        884,        884,        1,   518400, 0xc9ebfa89
+0,        885,        885,        1,   518400, 0xc9ebfa89
+0,        886,        886,        1,   518400, 0xc9ebfa89
+0,        887,        887,        1,   518400, 0xc9ebfa89
+0,        888,        888,        1,   518400, 0xc9ebfa89
+0,        889,        889,        1,   518400, 0xc9ebfa89
+0,        890,        890,        1,   518400, 0xbab197ea
+0,        891,        891,        1,   518400, 0xbab197ea
+0,        892,        892,        1,   518400, 0xbab197ea
+0,        893,        893,        1,   518400, 0xbab197ea
+0,        894,        894,        1,   518400, 0xbab197ea
+0,        895,        895,        1,   518400, 0xbab197ea
+0,        896,        896,        1,   518400, 0xbab197ea
+0,        897,        897,        1,   518400, 0xbab197ea
+0,        898,        898,        1,   518400, 0xbab197ea
+0,        899,        899,        1,   518400, 0xbab197ea
+0,        900,        900,        1,   518400, 0xbab197ea
+0,        901,        901,        1,   518400, 0xbab197ea
+0,        902,        902,        1,   518400, 0xbab197ea
+0,        903,        903,        1,   518400, 0xbab197ea
+0,        904,        904,        1,   518400, 0xbab197ea
+0,        905,        905,        1,   518400, 0xbab197ea
+0,        906,        906,        1,   518400, 0xbab197ea
+0,        907,        907,        1,   518400, 0xbab197ea
+0,        908,        908,        1,   518400, 0xbab197ea
+0,        909,        909,        1,   518400, 0xbab197ea
+0,        910,        910,        1,   518400, 0xbab197ea
+0,        911,        911,        1,   518400, 0xbab197ea
+0,        912,        912,        1,   518400, 0xbab197ea
+0,        913,        913,        1,   518400, 0xbab197ea
+0,        914,        914,        1,   518400, 0xbab197ea
+0,        915,        915,        1,   518400, 0xbab197ea
+0,        916,        916,        1,   518400, 0xbab197ea
+0,        917,        917,        1,   518400, 0xbab197ea
+0,        918,        918,        1,   518400, 0xbab197ea
+0,        919,        919,        1,   518400, 0xbab197ea
+0,        920,        920,        1,   518400, 0xbab197ea
+0,        921,        921,        1,   518400, 0xbab197ea
+0,        922,        922,        1,   518400, 0xbab197ea
+0,        923,        923,        1,   518400, 0xbab197ea
+0,        924,        924,        1,   518400, 0xbab197ea
+0,        925,        925,        1,   518400, 0xbab197ea
+0,        926,        926,        1,   518400, 0xbab197ea
+0,        927,        927,        1,   518400, 0xbab197ea
+0,        928,        928,        1,   518400, 0xbab197ea
+0,        929,        929,        1,   518400, 0xbab197ea
+0,        930,        930,        1,   518400, 0xbab197ea
+0,        931,        931,        1,   518400, 0xbab197ea
+0,        932,        932,        1,   518400, 0xbab197ea
+0,        933,        933,        1,   518400, 0xbab197ea
+0,        934,        934,        1,   518400, 0xbab197ea
+0,        935,        935,        1,   518400, 0xbab197ea
+0,        936,        936,        1,   518400, 0xbab197ea
+0,        937,        937,        1,   518400, 0xbab197ea
+0,        938,        938,        1,   518400, 0xbab197ea
+0,        939,        939,        1,   518400, 0xbab197ea
+0,        940,        940,        1,   518400, 0xbab197ea
+0,        941,        941,        1,   518400, 0xbab197ea
+0,        942,        942,        1,   518400, 0xbab197ea
+0,        943,        943,        1,   518400, 0xbab197ea
+0,        944,        944,        1,   518400, 0xbab197ea
+0,        945,        945,        1,   518400, 0xbab197ea
 0,        946,        946,        1,   518400, 0xbaf801ef
 1,  189295000,  189295000,  1968000,     1596, 0x408c726e
-0,        956,        956,        1,   518400, 0xbab197ea
+0,        947,        947,        1,   518400, 0xbaf801ef
+0,        948,        948,        1,   518400, 0xbaf801ef
+0,        949,        949,        1,   518400, 0xbaf801ef
+0,        950,        950,        1,   518400, 0xbaf801ef
+0,        951,        951,        1,   518400, 0xbaf801ef
+0,        952,        952,        1,   518400, 0xbaf801ef
+0,        953,        953,        1,   518400, 0xbaf801ef
+0,        954,        954,        1,   518400, 0xbaf801ef
+0,        955,        955,        1,   518400, 0xbaf801ef
+0,        956,        956,        1,   518400, 0xbaf801ef
 1,  191356000,  191356000,  1228000,     1517, 0xae8c5c2b
 0,        957,        957,        1,   518400, 0x59f4e72f
-0,        963,        963,        1,   518400, 0xbab197ea
+0,        958,        958,        1,   518400, 0x59f4e72f
+0,        959,        959,        1,   518400, 0x59f4e72f
+0,        960,        960,        1,   518400, 0x59f4e72f
+0,        961,        961,        1,   518400, 0x59f4e72f
+0,        962,        962,        1,   518400, 0x59f4e72f
+0,        963,        963,        1,   518400, 0x59f4e72f
 1,  192640000,  192640000,  1763000,     2506, 0xa458d6d4
 0,        964,        964,        1,   518400, 0x9d5b9d69
-0,        972,        972,        1,   518400, 0xbab197ea
+0,        965,        965,        1,   518400, 0x9d5b9d69
+0,        966,        966,        1,   518400, 0x9d5b9d69
+0,        967,        967,        1,   518400, 0x9d5b9d69
+0,        968,        968,        1,   518400, 0x9d5b9d69
+0,        969,        969,        1,   518400, 0x9d5b9d69
+0,        970,        970,        1,   518400, 0x9d5b9d69
+0,        971,        971,        1,   518400, 0x9d5b9d69
+0,        972,        972,        1,   518400, 0x9d5b9d69
+0,        973,        973,        1,   518400, 0xbab197ea
+0,        974,        974,        1,   518400, 0xbab197ea
+0,        975,        975,        1,   518400, 0xbab197ea
 1,  195193000,  195193000,  1092000,     1074, 0x397ba9a8
 0,        976,        976,        1,   518400, 0x923d1ce7
-0,        981,        981,        1,   518400, 0xbab197ea
+0,        977,        977,        1,   518400, 0x923d1ce7
+0,        978,        978,        1,   518400, 0x923d1ce7
+0,        979,        979,        1,   518400, 0x923d1ce7
+0,        980,        980,        1,   518400, 0x923d1ce7
+0,        981,        981,        1,   518400, 0x923d1ce7
 1,  196361000,  196361000,  1524000,     1715, 0x695ca41e
 0,        982,        982,        1,   518400, 0x6e652cd2
-0,        989,        989,        1,   518400, 0xbab197ea
+0,        983,        983,        1,   518400, 0x6e652cd2
+0,        984,        984,        1,   518400, 0x6e652cd2
+0,        985,        985,        1,   518400, 0x6e652cd2
+0,        986,        986,        1,   518400, 0x6e652cd2
+0,        987,        987,        1,   518400, 0x6e652cd2
+0,        988,        988,        1,   518400, 0x6e652cd2
+0,        989,        989,        1,   518400, 0x6e652cd2
 1,  197946000,  197946000,  1160000,      789, 0xc63a189e
 0,        990,        990,        1,   518400, 0x25113966
-0,        996,        996,        1,   518400, 0xbab197ea
+0,        991,        991,        1,   518400, 0x25113966
+0,        992,        992,        1,   518400, 0x25113966
+0,        993,        993,        1,   518400, 0x25113966
+0,        994,        994,        1,   518400, 0x25113966
+0,        995,        995,        1,   518400, 0x25113966
+0,        996,        996,        1,   518400, 0x25113966
 1,  199230000,  199230000,  1627000,     1846, 0xeea8c599
 0,        997,        997,        1,   518400, 0x2dc83609
-0,       1004,       1004,        1,   518400, 0xbab197ea
+0,        998,        998,        1,   518400, 0x2dc83609
+0,        999,        999,        1,   518400, 0x2dc83609
+0,       1000,       1000,        1,   518400, 0x2dc83609
+0,       1001,       1001,        1,   518400, 0x2dc83609
+0,       1002,       1002,        1,   518400, 0x2dc83609
+0,       1003,       1003,        1,   518400, 0x2dc83609
+0,       1004,       1004,        1,   518400, 0x2dc83609
 1,  200924000,  200924000,  1763000,      922, 0xd4a87222
 0,       1005,       1005,        1,   518400, 0x90483bc6
-0,       1013,       1013,        1,   518400, 0xbab197ea
+0,       1006,       1006,        1,   518400, 0x90483bc6
+0,       1007,       1007,        1,   518400, 0x90483bc6
+0,       1008,       1008,        1,   518400, 0x90483bc6
+0,       1009,       1009,        1,   518400, 0x90483bc6
+0,       1010,       1010,        1,   518400, 0x90483bc6
+0,       1011,       1011,        1,   518400, 0x90483bc6
+0,       1012,       1012,        1,   518400, 0x90483bc6
+0,       1013,       1013,        1,   518400, 0x90483bc6
+0,       1014,       1014,        1,   518400, 0xbab197ea
+0,       1015,       1015,        1,   518400, 0xbab197ea
+0,       1016,       1016,        1,   518400, 0xbab197ea
+0,       1017,       1017,        1,   518400, 0xbab197ea
+0,       1018,       1018,        1,   518400, 0xbab197ea
+0,       1019,       1019,        1,   518400, 0xbab197ea
+0,       1020,       1020,        1,   518400, 0xbab197ea
+0,       1021,       1021,        1,   518400, 0xbab197ea
+0,       1022,       1022,        1,   518400, 0xbab197ea
+0,       1023,       1023,        1,   518400, 0xbab197ea
+0,       1024,       1024,        1,   518400, 0xbab197ea
+0,       1025,       1025,        1,   518400, 0xbab197ea
+0,       1026,       1026,        1,   518400, 0xbab197ea
+0,       1027,       1027,        1,   518400, 0xbab197ea
+0,       1028,       1028,        1,   518400, 0xbab197ea
+0,       1029,       1029,        1,   518400, 0xbab197ea
+0,       1030,       1030,        1,   518400, 0xbab197ea
+0,       1031,       1031,        1,   518400, 0xbab197ea
+0,       1032,       1032,        1,   518400, 0xbab197ea
+0,       1033,       1033,        1,   518400, 0xbab197ea
+0,       1034,       1034,        1,   518400, 0xbab197ea
+0,       1035,       1035,        1,   518400, 0xbab197ea
+0,       1036,       1036,        1,   518400, 0xbab197ea
+0,       1037,       1037,        1,   518400, 0xbab197ea
+0,       1038,       1038,        1,   518400, 0xbab197ea
+0,       1039,       1039,        1,   518400, 0xbab197ea
+0,       1040,       1040,        1,   518400, 0xbab197ea
+0,       1041,       1041,        1,   518400, 0xbab197ea
+0,       1042,       1042,        1,   518400, 0xbab197ea
+0,       1043,       1043,        1,   518400, 0xbab197ea
+0,       1044,       1044,        1,   518400, 0xbab197ea
+0,       1045,       1045,        1,   518400, 0xbab197ea
+0,       1046,       1046,        1,   518400, 0xbab197ea
+0,       1047,       1047,        1,   518400, 0xbab197ea
+0,       1048,       1048,        1,   518400, 0xbab197ea
+0,       1049,       1049,        1,   518400, 0xbab197ea
+0,       1050,       1050,        1,   518400, 0xbab197ea
+0,       1051,       1051,        1,   518400, 0xbab197ea
+0,       1052,       1052,        1,   518400, 0xbab197ea
 0,       1053,       1053,        1,   518400, 0x3de86ab7
 1,  210600000,  210600000,  1831000,      665, 0x55580135
-0,       1062,       1062,        1,   518400, 0xbab197ea
+0,       1054,       1054,        1,   518400, 0x3de86ab7
+0,       1055,       1055,        1,   518400, 0x3de86ab7
+0,       1056,       1056,        1,   518400, 0x3de86ab7
+0,       1057,       1057,        1,   518400, 0x3de86ab7
+0,       1058,       1058,        1,   518400, 0x3de86ab7
+0,       1059,       1059,        1,   518400, 0x3de86ab7
+0,       1060,       1060,        1,   518400, 0x3de86ab7
+0,       1061,       1061,        1,   518400, 0x3de86ab7
+0,       1062,       1062,        1,   518400, 0x3de86ab7
+0,       1063,       1063,        1,   518400, 0xbab197ea
+0,       1064,       1064,        1,   518400, 0xbab197ea
+0,       1065,       1065,        1,   518400, 0xbab197ea
+0,       1066,       1066,        1,   518400, 0xbab197ea
+0,       1067,       1067,        1,   518400, 0xbab197ea
+0,       1068,       1068,        1,   518400, 0xbab197ea
+0,       1069,       1069,        1,   518400, 0xbab197ea
+0,       1070,       1070,        1,   518400, 0xbab197ea
+0,       1071,       1071,        1,   518400, 0xbab197ea
+0,       1072,       1072,        1,   518400, 0xbab197ea
+0,       1073,       1073,        1,   518400, 0xbab197ea
 1,  214771000,  214771000,  1558000,     1216, 0x50d1f6c5
 0,       1074,       1074,        1,   518400, 0x8c320e68
-0,       1082,       1082,        1,   518400, 0xbab197ea
+0,       1075,       1075,        1,   518400, 0x8c320e68
+0,       1076,       1076,        1,   518400, 0x8c320e68
+0,       1077,       1077,        1,   518400, 0x8c320e68
+0,       1078,       1078,        1,   518400, 0x8c320e68
+0,       1079,       1079,        1,   518400, 0x8c320e68
+0,       1080,       1080,        1,   518400, 0x8c320e68
+0,       1081,       1081,        1,   518400, 0x8c320e68
+0,       1082,       1082,        1,   518400, 0x8c320e68
+0,       1083,       1083,        1,   518400, 0xbab197ea
+0,       1084,       1084,        1,   518400, 0xbab197ea
+0,       1085,       1085,        1,   518400, 0xbab197ea
+0,       1086,       1086,        1,   518400, 0xbab197ea
+0,       1087,       1087,        1,   518400, 0xbab197ea
+0,       1088,       1088,        1,   518400, 0xbab197ea
+0,       1089,       1089,        1,   518400, 0xbab197ea
+0,       1090,       1090,        1,   518400, 0xbab197ea
+0,       1091,       1091,        1,   518400, 0xbab197ea
+0,       1092,       1092,        1,   518400, 0xbab197ea
+0,       1093,       1093,        1,   518400, 0xbab197ea
+0,       1094,       1094,        1,   518400, 0xbab197ea
+0,       1095,       1095,        1,   518400, 0xbab197ea
+0,       1096,       1096,        1,   518400, 0xbab197ea
+0,       1097,       1097,        1,   518400, 0xbab197ea
+0,       1098,       1098,        1,   518400, 0xbab197ea
+0,       1099,       1099,        1,   518400, 0xbab197ea
+0,       1100,       1100,        1,   518400, 0xbab197ea
+0,       1101,       1101,        1,   518400, 0xbab197ea
+0,       1102,       1102,        1,   518400, 0xbab197ea
+0,       1103,       1103,        1,   518400, 0xbab197ea
+0,       1104,       1104,        1,   518400, 0xbab197ea
+0,       1105,       1105,        1,   518400, 0xbab197ea
+0,       1106,       1106,        1,   518400, 0xbab197ea
+0,       1107,       1107,        1,   518400, 0xbab197ea
+0,       1108,       1108,        1,   518400, 0xbab197ea
+0,       1109,       1109,        1,   518400, 0xbab197ea
+0,       1110,       1110,        1,   518400, 0xbab197ea
+0,       1111,       1111,        1,   518400, 0xbab197ea
+0,       1112,       1112,        1,   518400, 0xbab197ea
+0,       1113,       1113,        1,   518400, 0xbab197ea
+0,       1114,       1114,        1,   518400, 0xbab197ea
+0,       1115,       1115,        1,   518400, 0xbab197ea
+0,       1116,       1116,        1,   518400, 0xbab197ea
+0,       1117,       1117,        1,   518400, 0xbab197ea
+0,       1118,       1118,        1,   518400, 0xbab197ea
+0,       1119,       1119,        1,   518400, 0xbab197ea
+0,       1120,       1120,        1,   518400, 0xbab197ea
+0,       1121,       1121,        1,   518400, 0xbab197ea
+0,       1122,       1122,        1,   518400, 0xbab197ea
+0,       1123,       1123,        1,   518400, 0xbab197ea
+0,       1124,       1124,        1,   518400, 0xbab197ea
+0,       1125,       1125,        1,   518400, 0xbab197ea
+0,       1126,       1126,        1,   518400, 0xbab197ea
+0,       1127,       1127,        1,   518400, 0xbab197ea
 0,       1128,       1128,        1,   518400, 0x81e977b2
 1,  225640000,  225640000,  2127000,     2133, 0x670c11a5
-0,       1139,       1139,        1,   518400, 0xbab197ea
+0,       1129,       1129,        1,   518400, 0x81e977b2
+0,       1130,       1130,        1,   518400, 0x81e977b2
+0,       1131,       1131,        1,   518400, 0x81e977b2
+0,       1132,       1132,        1,   518400, 0x81e977b2
+0,       1133,       1133,        1,   518400, 0x81e977b2
+0,       1134,       1134,        1,   518400, 0x81e977b2
+0,       1135,       1135,        1,   518400, 0x81e977b2
+0,       1136,       1136,        1,   518400, 0x81e977b2
+0,       1137,       1137,        1,   518400, 0x81e977b2
+0,       1138,       1138,        1,   518400, 0x81e977b2
+0,       1139,       1139,        1,   518400, 0x81e977b2
 1,  227834000,  227834000,  1262000,     1264, 0xc1d9fc57
 0,       1140,       1140,        1,   518400, 0xb046dd30
-0,       1145,       1145,        1,   518400, 0xbab197ea
diff --git a/tests/ref/fate/sub2video_basic b/tests/ref/fate/sub2video_basic
index 5f72e292c9..3c9fc71b5c 100644
--- a/tests/ref/fate/sub2video_basic
+++ b/tests/ref/fate/sub2video_basic
@@ -1,95 +1,1150 @@
-#tb 0: 1/25
+#tb 0: 1/5
 #media_type 0: video
 #codec_id 0: rawvideo
 #dimensions 0: 720x480
-#sar 0: 0/1
-0,       3312,       3312,        1,  1382400, 0x00000000
-0,       3312,       3312,        1,  1382400, 0x8c93c2ba
-0,       3436,       3436,        1,  1382400, 0x00000000
-0,       3684,       3684,        1,  1382400, 0xb02e32ca
-0,       3802,       3802,        1,  1382400, 0x00000000
-0,       4520,       4520,        1,  1382400, 0x83b71116
-0,       4584,       4584,        1,  1382400, 0x00000000
-0,       4586,       4586,        1,  1382400, 0x85547fd1
-0,       4645,       4645,        1,  1382400, 0x00000000
-0,       4648,       4648,        1,  1382400, 0x00000000
-0,       4648,       4648,        1,  1382400, 0xb6a8f181
-0,       4715,       4715,        1,  1382400, 0x00000000
-0,       4717,       4717,        1,  1382400, 0xb64d1a2c
-0,       4748,       4748,        1,  1382400, 0x00000000
-0,       4750,       4750,        1,  1382400, 0x7b37ecf3
-0,       4792,       4792,        1,  1382400, 0x00000000
-0,       4993,       4993,        1,  1382400, 0xdc025bd1
-0,       5027,       5027,        1,  1382400, 0x00000000
-0,       5029,       5029,        1,  1382400, 0x688b294d
-0,       5068,       5068,        1,  1382400, 0x00000000
-0,       5070,       5070,        1,  1382400, 0xa2b33d1b
-0,       5117,       5117,        1,  1382400, 0x00000000
-0,       5119,       5119,        1,  1382400, 0xb3e525e3
-0,       5168,       5168,        1,  1382400, 0x00000000
-0,       5170,       5170,        1,  1382400, 0xaa8fbdd7
-0,       5216,       5216,        1,  1382400, 0x00000000
-0,       5218,       5218,        1,  1382400, 0x7b7f26dd
-0,       5249,       5249,        1,  1382400, 0x00000000
-0,       5251,       5251,        1,  1382400, 0x15e2f836
-0,       5289,       5289,        1,  1382400, 0x00000000
-0,       5291,       5291,        1,  1382400, 0x0fee9b0c
-0,       5358,       5358,        1,  1382400, 0x00000000
-0,       5360,       5360,        1,  1382400, 0x89d62791
-0,       5429,       5429,        1,  1382400, 0x00000000
-0,       5431,       5431,        1,  1382400, 0xa6a9fd74
-0,       5490,       5490,        1,  1382400, 0x00000000
-0,       5491,       5491,        1,  1382400, 0x7896178d
-0,       5537,       5537,        1,  1382400, 0x00000000
-0,       5588,       5588,        1,  1382400, 0x01751a52
-0,       5647,       5647,        1,  1382400, 0x00000000
-0,       5688,       5688,        1,  1382400, 0xa3959c6f
-0,       5770,       5770,        1,  1382400, 0x00000000
-0,       5772,       5772,        1,  1382400, 0x3d3ea47b
-0,       5826,       5826,        1,  1382400, 0x00000000
-0,       5828,       5828,        1,  1382400, 0x593f8b24
-0,       5931,       5931,        1,  1382400, 0x00000000
-0,       5933,       5933,        1,  1382400, 0x171f05ba
-0,       6001,       6001,        1,  1382400, 0x00000000
-0,       6003,       6003,        1,  1382400, 0xb014cdf1
-0,       6054,       6054,        1,  1382400, 0x00000000
-0,       6839,       6839,        1,  1382400, 0xd918e667
-0,       6880,       6880,        1,  1382400, 0x00000000
-0,       7386,       7386,        1,  1382400, 0xc9406331
-0,       7419,       7419,        1,  1382400, 0x00000000
-0,       7501,       7501,        1,  1382400, 0xaf08b10d
-0,       7549,       7549,        1,  1382400, 0x00000000
-0,       7551,       7551,        1,  1382400, 0x00000000
-0,       7551,       7551,        1,  1382400, 0x853a9d93
-0,       7589,       7589,        1,  1382400, 0x00000000
-0,       7605,       7605,        1,  1382400, 0x7491a87d
-0,       7647,       7647,        1,  1382400, 0x00000000
-0,       7649,       7649,        1,  1382400, 0xf7383c58
-0,       7697,       7697,        1,  1382400, 0x00000000
-0,       7699,       7699,        1,  1382400, 0xe66be411
-0,       7743,       7743,        1,  1382400, 0x00000000
-0,       8032,       8032,        1,  1382400, 0xd6850362
-0,       8082,       8082,        1,  1382400, 0x00000000
-0,       8084,       8084,        1,  1382400, 0x3e1ed109
-0,       8115,       8115,        1,  1382400, 0x00000000
-0,       8116,       8116,        1,  1382400, 0x39c1b7bd
-0,       8160,       8160,        1,  1382400, 0x00000000
-0,       8180,       8180,        1,  1382400, 0x35b85f2e
-0,       8207,       8207,        1,  1382400, 0x00000000
-0,       8209,       8209,        1,  1382400, 0x00000000
-0,       8209,       8209,        1,  1382400, 0x83f103e5
-0,       8247,       8247,        1,  1382400, 0x00000000
-0,       8249,       8249,        1,  1382400, 0xbc1ca9b3
-0,       8278,       8278,        1,  1382400, 0x00000000
-0,       8281,       8281,        1,  1382400, 0x94d4a51e
-0,       8321,       8321,        1,  1382400, 0x00000000
-0,       8323,       8323,        1,  1382400, 0xf88cdfde
-0,       8367,       8367,        1,  1382400, 0x00000000
-0,       8565,       8565,        1,  1382400, 0xdd51423b
-0,       8611,       8611,        1,  1382400, 0x00000000
-0,       8669,       8669,        1,  1382400, 0x08259fa4
-0,       8708,       8708,        1,  1382400, 0x00000000
-0,       8941,       8941,        1,  1382400, 0x1663fa34
-0,       8994,       8994,        1,  1382400, 0x00000000
-0,       8996,       8996,        1,  1382400, 0xda2ceb55
-0,       9027,       9027,        1,  1382400, 0x00000000
+#sar 0: 1/1
+0,          0,          0,        1,  1382400, 0x00000000
+0,        662,        662,        1,  1382400, 0xc637b893
+0,        663,        663,        1,  1382400, 0xc637b893
+0,        664,        664,        1,  1382400, 0xc637b893
+0,        665,        665,        1,  1382400, 0xc637b893
+0,        666,        666,        1,  1382400, 0xc637b893
+0,        667,        667,        1,  1382400, 0xc637b893
+0,        668,        668,        1,  1382400, 0xc637b893
+0,        669,        669,        1,  1382400, 0xc637b893
+0,        670,        670,        1,  1382400, 0xc637b893
+0,        671,        671,        1,  1382400, 0xc637b893
+0,        672,        672,        1,  1382400, 0xc637b893
+0,        673,        673,        1,  1382400, 0xc637b893
+0,        674,        674,        1,  1382400, 0xc637b893
+0,        675,        675,        1,  1382400, 0xc637b893
+0,        676,        676,        1,  1382400, 0xc637b893
+0,        677,        677,        1,  1382400, 0xc637b893
+0,        678,        678,        1,  1382400, 0xc637b893
+0,        679,        679,        1,  1382400, 0xc637b893
+0,        680,        680,        1,  1382400, 0xc637b893
+0,        681,        681,        1,  1382400, 0xc637b893
+0,        682,        682,        1,  1382400, 0xc637b893
+0,        683,        683,        1,  1382400, 0xc637b893
+0,        684,        684,        1,  1382400, 0xc637b893
+0,        685,        685,        1,  1382400, 0xc637b893
+0,        686,        686,        1,  1382400, 0xc637b893
+0,        687,        687,        1,  1382400, 0x00000000
+0,        688,        688,        1,  1382400, 0x00000000
+0,        689,        689,        1,  1382400, 0x00000000
+0,        690,        690,        1,  1382400, 0x00000000
+0,        691,        691,        1,  1382400, 0x00000000
+0,        692,        692,        1,  1382400, 0x00000000
+0,        693,        693,        1,  1382400, 0x00000000
+0,        694,        694,        1,  1382400, 0x00000000
+0,        695,        695,        1,  1382400, 0x00000000
+0,        696,        696,        1,  1382400, 0x00000000
+0,        697,        697,        1,  1382400, 0x00000000
+0,        698,        698,        1,  1382400, 0x00000000
+0,        699,        699,        1,  1382400, 0x00000000
+0,        700,        700,        1,  1382400, 0x00000000
+0,        701,        701,        1,  1382400, 0x00000000
+0,        702,        702,        1,  1382400, 0x00000000
+0,        703,        703,        1,  1382400, 0x00000000
+0,        704,        704,        1,  1382400, 0x00000000
+0,        705,        705,        1,  1382400, 0x00000000
+0,        706,        706,        1,  1382400, 0x00000000
+0,        707,        707,        1,  1382400, 0x00000000
+0,        708,        708,        1,  1382400, 0x00000000
+0,        709,        709,        1,  1382400, 0x00000000
+0,        710,        710,        1,  1382400, 0x00000000
+0,        711,        711,        1,  1382400, 0x00000000
+0,        712,        712,        1,  1382400, 0x00000000
+0,        713,        713,        1,  1382400, 0x00000000
+0,        714,        714,        1,  1382400, 0x00000000
+0,        715,        715,        1,  1382400, 0x00000000
+0,        716,        716,        1,  1382400, 0x00000000
+0,        717,        717,        1,  1382400, 0x00000000
+0,        718,        718,        1,  1382400, 0x00000000
+0,        719,        719,        1,  1382400, 0x00000000
+0,        720,        720,        1,  1382400, 0x00000000
+0,        721,        721,        1,  1382400, 0x00000000
+0,        722,        722,        1,  1382400, 0x00000000
+0,        723,        723,        1,  1382400, 0x00000000
+0,        724,        724,        1,  1382400, 0x00000000
+0,        725,        725,        1,  1382400, 0x00000000
+0,        726,        726,        1,  1382400, 0x00000000
+0,        727,        727,        1,  1382400, 0x00000000
+0,        728,        728,        1,  1382400, 0x00000000
+0,        729,        729,        1,  1382400, 0x00000000
+0,        730,        730,        1,  1382400, 0x00000000
+0,        731,        731,        1,  1382400, 0x00000000
+0,        732,        732,        1,  1382400, 0x00000000
+0,        733,        733,        1,  1382400, 0x00000000
+0,        734,        734,        1,  1382400, 0x00000000
+0,        735,        735,        1,  1382400, 0x00000000
+0,        737,        737,        1,  1382400, 0x4c2960ca
+0,        737,        737,        1,  1382400, 0x4c2960ca
+0,        738,        738,        1,  1382400, 0x4c2960ca
+0,        739,        739,        1,  1382400, 0x4c2960ca
+0,        740,        740,        1,  1382400, 0x4c2960ca
+0,        741,        741,        1,  1382400, 0x4c2960ca
+0,        742,        742,        1,  1382400, 0x4c2960ca
+0,        743,        743,        1,  1382400, 0x4c2960ca
+0,        744,        744,        1,  1382400, 0x4c2960ca
+0,        745,        745,        1,  1382400, 0x4c2960ca
+0,        746,        746,        1,  1382400, 0x4c2960ca
+0,        747,        747,        1,  1382400, 0x4c2960ca
+0,        748,        748,        1,  1382400, 0x4c2960ca
+0,        749,        749,        1,  1382400, 0x4c2960ca
+0,        750,        750,        1,  1382400, 0x4c2960ca
+0,        751,        751,        1,  1382400, 0x4c2960ca
+0,        752,        752,        1,  1382400, 0x4c2960ca
+0,        753,        753,        1,  1382400, 0x4c2960ca
+0,        754,        754,        1,  1382400, 0x4c2960ca
+0,        755,        755,        1,  1382400, 0x4c2960ca
+0,        756,        756,        1,  1382400, 0x4c2960ca
+0,        757,        757,        1,  1382400, 0x4c2960ca
+0,        758,        758,        1,  1382400, 0x4c2960ca
+0,        759,        759,        1,  1382400, 0x4c2960ca
+0,        760,        760,        1,  1382400, 0x00000000
+0,        761,        761,        1,  1382400, 0x00000000
+0,        762,        762,        1,  1382400, 0x00000000
+0,        763,        763,        1,  1382400, 0x00000000
+0,        764,        764,        1,  1382400, 0x00000000
+0,        765,        765,        1,  1382400, 0x00000000
+0,        766,        766,        1,  1382400, 0x00000000
+0,        767,        767,        1,  1382400, 0x00000000
+0,        768,        768,        1,  1382400, 0x00000000
+0,        769,        769,        1,  1382400, 0x00000000
+0,        770,        770,        1,  1382400, 0x00000000
+0,        771,        771,        1,  1382400, 0x00000000
+0,        772,        772,        1,  1382400, 0x00000000
+0,        773,        773,        1,  1382400, 0x00000000
+0,        774,        774,        1,  1382400, 0x00000000
+0,        775,        775,        1,  1382400, 0x00000000
+0,        776,        776,        1,  1382400, 0x00000000
+0,        777,        777,        1,  1382400, 0x00000000
+0,        778,        778,        1,  1382400, 0x00000000
+0,        779,        779,        1,  1382400, 0x00000000
+0,        780,        780,        1,  1382400, 0x00000000
+0,        781,        781,        1,  1382400, 0x00000000
+0,        782,        782,        1,  1382400, 0x00000000
+0,        783,        783,        1,  1382400, 0x00000000
+0,        784,        784,        1,  1382400, 0x00000000
+0,        785,        785,        1,  1382400, 0x00000000
+0,        786,        786,        1,  1382400, 0x00000000
+0,        787,        787,        1,  1382400, 0x00000000
+0,        788,        788,        1,  1382400, 0x00000000
+0,        789,        789,        1,  1382400, 0x00000000
+0,        790,        790,        1,  1382400, 0x00000000
+0,        791,        791,        1,  1382400, 0x00000000
+0,        792,        792,        1,  1382400, 0x00000000
+0,        793,        793,        1,  1382400, 0x00000000
+0,        794,        794,        1,  1382400, 0x00000000
+0,        795,        795,        1,  1382400, 0x00000000
+0,        796,        796,        1,  1382400, 0x00000000
+0,        797,        797,        1,  1382400, 0x00000000
+0,        798,        798,        1,  1382400, 0x00000000
+0,        799,        799,        1,  1382400, 0x00000000
+0,        800,        800,        1,  1382400, 0x00000000
+0,        801,        801,        1,  1382400, 0x00000000
+0,        802,        802,        1,  1382400, 0x00000000
+0,        803,        803,        1,  1382400, 0x00000000
+0,        804,        804,        1,  1382400, 0x00000000
+0,        805,        805,        1,  1382400, 0x00000000
+0,        806,        806,        1,  1382400, 0x00000000
+0,        807,        807,        1,  1382400, 0x00000000
+0,        808,        808,        1,  1382400, 0x00000000
+0,        809,        809,        1,  1382400, 0x00000000
+0,        810,        810,        1,  1382400, 0x00000000
+0,        811,        811,        1,  1382400, 0x00000000
+0,        812,        812,        1,  1382400, 0x00000000
+0,        813,        813,        1,  1382400, 0x00000000
+0,        814,        814,        1,  1382400, 0x00000000
+0,        815,        815,        1,  1382400, 0x00000000
+0,        816,        816,        1,  1382400, 0x00000000
+0,        817,        817,        1,  1382400, 0x00000000
+0,        818,        818,        1,  1382400, 0x00000000
+0,        819,        819,        1,  1382400, 0x00000000
+0,        820,        820,        1,  1382400, 0x00000000
+0,        821,        821,        1,  1382400, 0x00000000
+0,        822,        822,        1,  1382400, 0x00000000
+0,        823,        823,        1,  1382400, 0x00000000
+0,        824,        824,        1,  1382400, 0x00000000
+0,        825,        825,        1,  1382400, 0x00000000
+0,        826,        826,        1,  1382400, 0x00000000
+0,        827,        827,        1,  1382400, 0x00000000
+0,        828,        828,        1,  1382400, 0x00000000
+0,        829,        829,        1,  1382400, 0x00000000
+0,        830,        830,        1,  1382400, 0x00000000
+0,        831,        831,        1,  1382400, 0x00000000
+0,        832,        832,        1,  1382400, 0x00000000
+0,        833,        833,        1,  1382400, 0x00000000
+0,        834,        834,        1,  1382400, 0x00000000
+0,        835,        835,        1,  1382400, 0x00000000
+0,        836,        836,        1,  1382400, 0x00000000
+0,        837,        837,        1,  1382400, 0x00000000
+0,        838,        838,        1,  1382400, 0x00000000
+0,        839,        839,        1,  1382400, 0x00000000
+0,        840,        840,        1,  1382400, 0x00000000
+0,        841,        841,        1,  1382400, 0x00000000
+0,        842,        842,        1,  1382400, 0x00000000
+0,        843,        843,        1,  1382400, 0x00000000
+0,        844,        844,        1,  1382400, 0x00000000
+0,        845,        845,        1,  1382400, 0x00000000
+0,        846,        846,        1,  1382400, 0x00000000
+0,        847,        847,        1,  1382400, 0x00000000
+0,        848,        848,        1,  1382400, 0x00000000
+0,        849,        849,        1,  1382400, 0x00000000
+0,        850,        850,        1,  1382400, 0x00000000
+0,        851,        851,        1,  1382400, 0x00000000
+0,        852,        852,        1,  1382400, 0x00000000
+0,        853,        853,        1,  1382400, 0x00000000
+0,        854,        854,        1,  1382400, 0x00000000
+0,        855,        855,        1,  1382400, 0x00000000
+0,        856,        856,        1,  1382400, 0x00000000
+0,        857,        857,        1,  1382400, 0x00000000
+0,        858,        858,        1,  1382400, 0x00000000
+0,        859,        859,        1,  1382400, 0x00000000
+0,        860,        860,        1,  1382400, 0x00000000
+0,        861,        861,        1,  1382400, 0x00000000
+0,        862,        862,        1,  1382400, 0x00000000
+0,        863,        863,        1,  1382400, 0x00000000
+0,        864,        864,        1,  1382400, 0x00000000
+0,        865,        865,        1,  1382400, 0x00000000
+0,        866,        866,        1,  1382400, 0x00000000
+0,        867,        867,        1,  1382400, 0x00000000
+0,        868,        868,        1,  1382400, 0x00000000
+0,        869,        869,        1,  1382400, 0x00000000
+0,        870,        870,        1,  1382400, 0x00000000
+0,        871,        871,        1,  1382400, 0x00000000
+0,        872,        872,        1,  1382400, 0x00000000
+0,        873,        873,        1,  1382400, 0x00000000
+0,        874,        874,        1,  1382400, 0x00000000
+0,        875,        875,        1,  1382400, 0x00000000
+0,        876,        876,        1,  1382400, 0x00000000
+0,        877,        877,        1,  1382400, 0x00000000
+0,        878,        878,        1,  1382400, 0x00000000
+0,        879,        879,        1,  1382400, 0x00000000
+0,        880,        880,        1,  1382400, 0x00000000
+0,        881,        881,        1,  1382400, 0x00000000
+0,        882,        882,        1,  1382400, 0x00000000
+0,        883,        883,        1,  1382400, 0x00000000
+0,        884,        884,        1,  1382400, 0x00000000
+0,        885,        885,        1,  1382400, 0x00000000
+0,        886,        886,        1,  1382400, 0x00000000
+0,        887,        887,        1,  1382400, 0x00000000
+0,        888,        888,        1,  1382400, 0x00000000
+0,        889,        889,        1,  1382400, 0x00000000
+0,        890,        890,        1,  1382400, 0x00000000
+0,        891,        891,        1,  1382400, 0x00000000
+0,        892,        892,        1,  1382400, 0x00000000
+0,        893,        893,        1,  1382400, 0x00000000
+0,        894,        894,        1,  1382400, 0x00000000
+0,        895,        895,        1,  1382400, 0x00000000
+0,        896,        896,        1,  1382400, 0x00000000
+0,        897,        897,        1,  1382400, 0x00000000
+0,        898,        898,        1,  1382400, 0x00000000
+0,        899,        899,        1,  1382400, 0x00000000
+0,        900,        900,        1,  1382400, 0x00000000
+0,        901,        901,        1,  1382400, 0x00000000
+0,        902,        902,        1,  1382400, 0x00000000
+0,        904,        904,        1,  1382400, 0x5fa18966
+0,        904,        904,        1,  1382400, 0x5fa18966
+0,        905,        905,        1,  1382400, 0x5fa18966
+0,        906,        906,        1,  1382400, 0x5fa18966
+0,        907,        907,        1,  1382400, 0x5fa18966
+0,        908,        908,        1,  1382400, 0x5fa18966
+0,        909,        909,        1,  1382400, 0x5fa18966
+0,        910,        910,        1,  1382400, 0x5fa18966
+0,        911,        911,        1,  1382400, 0x5fa18966
+0,        912,        912,        1,  1382400, 0x5fa18966
+0,        913,        913,        1,  1382400, 0x5fa18966
+0,        914,        914,        1,  1382400, 0x5fa18966
+0,        915,        915,        1,  1382400, 0x5fa18966
+0,        916,        916,        1,  1382400, 0x5fa18966
+0,        917,        917,        1,  1382400, 0x55f4b7b1
+0,        918,        918,        1,  1382400, 0x55f4b7b1
+0,        919,        919,        1,  1382400, 0x55f4b7b1
+0,        920,        920,        1,  1382400, 0x55f4b7b1
+0,        921,        921,        1,  1382400, 0x55f4b7b1
+0,        922,        922,        1,  1382400, 0x55f4b7b1
+0,        923,        923,        1,  1382400, 0x55f4b7b1
+0,        924,        924,        1,  1382400, 0x55f4b7b1
+0,        925,        925,        1,  1382400, 0x55f4b7b1
+0,        926,        926,        1,  1382400, 0x55f4b7b1
+0,        927,        927,        1,  1382400, 0x55f4b7b1
+0,        928,        928,        1,  1382400, 0x55f4b7b1
+0,        929,        929,        1,  1382400, 0x00000000
+0,        930,        930,        1,  1382400, 0xdfa4cf32
+0,        931,        931,        1,  1382400, 0xdfa4cf32
+0,        932,        932,        1,  1382400, 0xdfa4cf32
+0,        933,        933,        1,  1382400, 0xdfa4cf32
+0,        934,        934,        1,  1382400, 0xdfa4cf32
+0,        935,        935,        1,  1382400, 0xdfa4cf32
+0,        936,        936,        1,  1382400, 0xdfa4cf32
+0,        937,        937,        1,  1382400, 0xdfa4cf32
+0,        938,        938,        1,  1382400, 0xdfa4cf32
+0,        939,        939,        1,  1382400, 0xdfa4cf32
+0,        940,        940,        1,  1382400, 0xdfa4cf32
+0,        941,        941,        1,  1382400, 0xdfa4cf32
+0,        942,        942,        1,  1382400, 0xdfa4cf32
+0,        943,        943,        1,  1382400, 0x35023df8
+0,        944,        944,        1,  1382400, 0x35023df8
+0,        945,        945,        1,  1382400, 0x35023df8
+0,        946,        946,        1,  1382400, 0x35023df8
+0,        947,        947,        1,  1382400, 0x35023df8
+0,        948,        948,        1,  1382400, 0x35023df8
+0,        949,        949,        1,  1382400, 0x35023df8
+0,        950,        950,        1,  1382400, 0xed933219
+0,        951,        951,        1,  1382400, 0xed933219
+0,        952,        952,        1,  1382400, 0xed933219
+0,        953,        953,        1,  1382400, 0xed933219
+0,        954,        954,        1,  1382400, 0xed933219
+0,        955,        955,        1,  1382400, 0xed933219
+0,        956,        956,        1,  1382400, 0xed933219
+0,        957,        957,        1,  1382400, 0xed933219
+0,        958,        958,        1,  1382400, 0x00000000
+0,        959,        959,        1,  1382400, 0x00000000
+0,        960,        960,        1,  1382400, 0x00000000
+0,        961,        961,        1,  1382400, 0x00000000
+0,        962,        962,        1,  1382400, 0x00000000
+0,        963,        963,        1,  1382400, 0x00000000
+0,        964,        964,        1,  1382400, 0x00000000
+0,        965,        965,        1,  1382400, 0x00000000
+0,        966,        966,        1,  1382400, 0x00000000
+0,        967,        967,        1,  1382400, 0x00000000
+0,        968,        968,        1,  1382400, 0x00000000
+0,        969,        969,        1,  1382400, 0x00000000
+0,        970,        970,        1,  1382400, 0x00000000
+0,        971,        971,        1,  1382400, 0x00000000
+0,        972,        972,        1,  1382400, 0x00000000
+0,        973,        973,        1,  1382400, 0x00000000
+0,        974,        974,        1,  1382400, 0x00000000
+0,        975,        975,        1,  1382400, 0x00000000
+0,        976,        976,        1,  1382400, 0x00000000
+0,        977,        977,        1,  1382400, 0x00000000
+0,        978,        978,        1,  1382400, 0x00000000
+0,        979,        979,        1,  1382400, 0x00000000
+0,        980,        980,        1,  1382400, 0x00000000
+0,        981,        981,        1,  1382400, 0x00000000
+0,        982,        982,        1,  1382400, 0x00000000
+0,        983,        983,        1,  1382400, 0x00000000
+0,        984,        984,        1,  1382400, 0x00000000
+0,        985,        985,        1,  1382400, 0x00000000
+0,        986,        986,        1,  1382400, 0x00000000
+0,        987,        987,        1,  1382400, 0x00000000
+0,        988,        988,        1,  1382400, 0x00000000
+0,        989,        989,        1,  1382400, 0x00000000
+0,        990,        990,        1,  1382400, 0x00000000
+0,        991,        991,        1,  1382400, 0x00000000
+0,        992,        992,        1,  1382400, 0x00000000
+0,        993,        993,        1,  1382400, 0x00000000
+0,        994,        994,        1,  1382400, 0x00000000
+0,        995,        995,        1,  1382400, 0x00000000
+0,        996,        996,        1,  1382400, 0x00000000
+0,        997,        997,        1,  1382400, 0x00000000
+0,        999,        999,        1,  1382400, 0x1b26389a
+0,        999,        999,        1,  1382400, 0x1b26389a
+0,       1000,       1000,        1,  1382400, 0x1b26389a
+0,       1001,       1001,        1,  1382400, 0x1b26389a
+0,       1002,       1002,        1,  1382400, 0x1b26389a
+0,       1003,       1003,        1,  1382400, 0x1b26389a
+0,       1004,       1004,        1,  1382400, 0x1b26389a
+0,       1006,       1006,        1,  1382400, 0xf0c7028b
+0,       1006,       1006,        1,  1382400, 0xf0c7028b
+0,       1007,       1007,        1,  1382400, 0xf0c7028b
+0,       1008,       1008,        1,  1382400, 0xf0c7028b
+0,       1009,       1009,        1,  1382400, 0xf0c7028b
+0,       1010,       1010,        1,  1382400, 0xf0c7028b
+0,       1011,       1011,        1,  1382400, 0xf0c7028b
+0,       1012,       1012,        1,  1382400, 0xf0c7028b
+0,       1013,       1013,        1,  1382400, 0xf0c7028b
+0,       1014,       1014,        1,  1382400, 0x395f521d
+0,       1015,       1015,        1,  1382400, 0x395f521d
+0,       1016,       1016,        1,  1382400, 0x395f521d
+0,       1017,       1017,        1,  1382400, 0x395f521d
+0,       1018,       1018,        1,  1382400, 0x395f521d
+0,       1019,       1019,        1,  1382400, 0x395f521d
+0,       1020,       1020,        1,  1382400, 0x395f521d
+0,       1021,       1021,        1,  1382400, 0x395f521d
+0,       1022,       1022,        1,  1382400, 0x395f521d
+0,       1024,       1024,        1,  1382400, 0x1ea87415
+0,       1024,       1024,        1,  1382400, 0x1ea87415
+0,       1025,       1025,        1,  1382400, 0x1ea87415
+0,       1026,       1026,        1,  1382400, 0x1ea87415
+0,       1027,       1027,        1,  1382400, 0x1ea87415
+0,       1028,       1028,        1,  1382400, 0x1ea87415
+0,       1029,       1029,        1,  1382400, 0x1ea87415
+0,       1030,       1030,        1,  1382400, 0x1ea87415
+0,       1031,       1031,        1,  1382400, 0x1ea87415
+0,       1032,       1032,        1,  1382400, 0x1ea87415
+0,       1033,       1033,        1,  1382400, 0x1ea87415
+0,       1034,       1034,        1,  1382400, 0xc6effdc1
+0,       1035,       1035,        1,  1382400, 0xc6effdc1
+0,       1036,       1036,        1,  1382400, 0xc6effdc1
+0,       1037,       1037,        1,  1382400, 0xc6effdc1
+0,       1038,       1038,        1,  1382400, 0xc6effdc1
+0,       1039,       1039,        1,  1382400, 0xc6effdc1
+0,       1040,       1040,        1,  1382400, 0xc6effdc1
+0,       1041,       1041,        1,  1382400, 0xc6effdc1
+0,       1042,       1042,        1,  1382400, 0xc6effdc1
+0,       1044,       1044,        1,  1382400, 0xba6846f8
+0,       1044,       1044,        1,  1382400, 0xba6846f8
+0,       1045,       1045,        1,  1382400, 0xba6846f8
+0,       1046,       1046,        1,  1382400, 0xba6846f8
+0,       1047,       1047,        1,  1382400, 0xba6846f8
+0,       1048,       1048,        1,  1382400, 0xba6846f8
+0,       1049,       1049,        1,  1382400, 0xba6846f8
+0,       1050,       1050,        1,  1382400, 0x033c5d5b
+0,       1051,       1051,        1,  1382400, 0x033c5d5b
+0,       1052,       1052,        1,  1382400, 0x033c5d5b
+0,       1053,       1053,        1,  1382400, 0x033c5d5b
+0,       1054,       1054,        1,  1382400, 0x033c5d5b
+0,       1055,       1055,        1,  1382400, 0x033c5d5b
+0,       1056,       1056,        1,  1382400, 0x033c5d5b
+0,       1057,       1057,        1,  1382400, 0x033c5d5b
+0,       1058,       1058,        1,  1382400, 0x00000000
+0,       1059,       1059,        1,  1382400, 0xef5abf66
+0,       1060,       1060,        1,  1382400, 0xef5abf66
+0,       1061,       1061,        1,  1382400, 0xef5abf66
+0,       1062,       1062,        1,  1382400, 0xef5abf66
+0,       1063,       1063,        1,  1382400, 0xef5abf66
+0,       1064,       1064,        1,  1382400, 0xef5abf66
+0,       1065,       1065,        1,  1382400, 0xef5abf66
+0,       1066,       1066,        1,  1382400, 0xef5abf66
+0,       1067,       1067,        1,  1382400, 0xef5abf66
+0,       1068,       1068,        1,  1382400, 0xef5abf66
+0,       1069,       1069,        1,  1382400, 0xef5abf66
+0,       1070,       1070,        1,  1382400, 0xef5abf66
+0,       1071,       1071,        1,  1382400, 0xef5abf66
+0,       1072,       1072,        1,  1382400, 0x00000000
+0,       1073,       1073,        1,  1382400, 0xec747954
+0,       1074,       1074,        1,  1382400, 0xec747954
+0,       1075,       1075,        1,  1382400, 0xec747954
+0,       1076,       1076,        1,  1382400, 0xec747954
+0,       1077,       1077,        1,  1382400, 0xec747954
+0,       1078,       1078,        1,  1382400, 0xec747954
+0,       1079,       1079,        1,  1382400, 0xec747954
+0,       1080,       1080,        1,  1382400, 0xec747954
+0,       1081,       1081,        1,  1382400, 0xec747954
+0,       1082,       1082,        1,  1382400, 0xec747954
+0,       1083,       1083,        1,  1382400, 0xec747954
+0,       1084,       1084,        1,  1382400, 0xec747954
+0,       1085,       1085,        1,  1382400, 0xec747954
+0,       1086,       1086,        1,  1382400, 0x00000000
+0,       1087,       1087,        1,  1382400, 0xfa34bcaf
+0,       1088,       1088,        1,  1382400, 0xfa34bcaf
+0,       1089,       1089,        1,  1382400, 0xfa34bcaf
+0,       1090,       1090,        1,  1382400, 0xfa34bcaf
+0,       1091,       1091,        1,  1382400, 0xfa34bcaf
+0,       1092,       1092,        1,  1382400, 0xfa34bcaf
+0,       1093,       1093,        1,  1382400, 0xfa34bcaf
+0,       1094,       1094,        1,  1382400, 0xfa34bcaf
+0,       1095,       1095,        1,  1382400, 0xfa34bcaf
+0,       1096,       1096,        1,  1382400, 0xfa34bcaf
+0,       1097,       1097,        1,  1382400, 0xfa34bcaf
+0,       1098,       1098,        1,  1382400, 0x00000000
+0,       1099,       1099,        1,  1382400, 0x8b7a709b
+0,       1100,       1100,        1,  1382400, 0x8b7a709b
+0,       1101,       1101,        1,  1382400, 0x8b7a709b
+0,       1102,       1102,        1,  1382400, 0x8b7a709b
+0,       1103,       1103,        1,  1382400, 0x8b7a709b
+0,       1104,       1104,        1,  1382400, 0x8b7a709b
+0,       1105,       1105,        1,  1382400, 0x8b7a709b
+0,       1106,       1106,        1,  1382400, 0x8b7a709b
+0,       1107,       1107,        1,  1382400, 0x00000000
+0,       1108,       1108,        1,  1382400, 0x00000000
+0,       1109,       1109,        1,  1382400, 0x00000000
+0,       1110,       1110,        1,  1382400, 0x00000000
+0,       1111,       1111,        1,  1382400, 0x00000000
+0,       1112,       1112,        1,  1382400, 0x00000000
+0,       1113,       1113,        1,  1382400, 0x00000000
+0,       1114,       1114,        1,  1382400, 0x00000000
+0,       1115,       1115,        1,  1382400, 0x00000000
+0,       1116,       1116,        1,  1382400, 0x00000000
+0,       1118,       1118,        1,  1382400, 0xc333382f
+0,       1118,       1118,        1,  1382400, 0xc333382f
+0,       1119,       1119,        1,  1382400, 0xc333382f
+0,       1120,       1120,        1,  1382400, 0xc333382f
+0,       1121,       1121,        1,  1382400, 0xc333382f
+0,       1122,       1122,        1,  1382400, 0xc333382f
+0,       1123,       1123,        1,  1382400, 0xc333382f
+0,       1124,       1124,        1,  1382400, 0xc333382f
+0,       1125,       1125,        1,  1382400, 0xc333382f
+0,       1126,       1126,        1,  1382400, 0xc333382f
+0,       1127,       1127,        1,  1382400, 0xc333382f
+0,       1128,       1128,        1,  1382400, 0xc333382f
+0,       1129,       1129,        1,  1382400, 0x00000000
+0,       1130,       1130,        1,  1382400, 0x00000000
+0,       1131,       1131,        1,  1382400, 0x00000000
+0,       1132,       1132,        1,  1382400, 0x00000000
+0,       1133,       1133,        1,  1382400, 0x00000000
+0,       1134,       1134,        1,  1382400, 0x00000000
+0,       1135,       1135,        1,  1382400, 0x00000000
+0,       1136,       1136,        1,  1382400, 0x00000000
+0,       1138,       1138,        1,  1382400, 0xabe5dfcf
+0,       1138,       1138,        1,  1382400, 0xabe5dfcf
+0,       1139,       1139,        1,  1382400, 0xabe5dfcf
+0,       1140,       1140,        1,  1382400, 0xabe5dfcf
+0,       1141,       1141,        1,  1382400, 0xabe5dfcf
+0,       1142,       1142,        1,  1382400, 0xabe5dfcf
+0,       1143,       1143,        1,  1382400, 0xabe5dfcf
+0,       1144,       1144,        1,  1382400, 0xabe5dfcf
+0,       1145,       1145,        1,  1382400, 0xabe5dfcf
+0,       1146,       1146,        1,  1382400, 0xabe5dfcf
+0,       1147,       1147,        1,  1382400, 0xabe5dfcf
+0,       1148,       1148,        1,  1382400, 0xabe5dfcf
+0,       1149,       1149,        1,  1382400, 0xabe5dfcf
+0,       1150,       1150,        1,  1382400, 0xabe5dfcf
+0,       1151,       1151,        1,  1382400, 0xabe5dfcf
+0,       1152,       1152,        1,  1382400, 0xabe5dfcf
+0,       1153,       1153,        1,  1382400, 0xabe5dfcf
+0,       1154,       1154,        1,  1382400, 0x56948101
+0,       1155,       1155,        1,  1382400, 0x56948101
+0,       1156,       1156,        1,  1382400, 0x56948101
+0,       1157,       1157,        1,  1382400, 0x56948101
+0,       1158,       1158,        1,  1382400, 0x56948101
+0,       1159,       1159,        1,  1382400, 0x56948101
+0,       1160,       1160,        1,  1382400, 0x56948101
+0,       1161,       1161,        1,  1382400, 0x56948101
+0,       1162,       1162,        1,  1382400, 0x56948101
+0,       1163,       1163,        1,  1382400, 0x56948101
+0,       1164,       1164,        1,  1382400, 0x56948101
+0,       1166,       1166,        1,  1382400, 0xb747834a
+0,       1166,       1166,        1,  1382400, 0xb747834a
+0,       1167,       1167,        1,  1382400, 0xb747834a
+0,       1168,       1168,        1,  1382400, 0xb747834a
+0,       1169,       1169,        1,  1382400, 0xb747834a
+0,       1170,       1170,        1,  1382400, 0xb747834a
+0,       1171,       1171,        1,  1382400, 0xb747834a
+0,       1172,       1172,        1,  1382400, 0xb747834a
+0,       1173,       1173,        1,  1382400, 0xb747834a
+0,       1174,       1174,        1,  1382400, 0xb747834a
+0,       1175,       1175,        1,  1382400, 0xb747834a
+0,       1176,       1176,        1,  1382400, 0xb747834a
+0,       1177,       1177,        1,  1382400, 0xb747834a
+0,       1178,       1178,        1,  1382400, 0xb747834a
+0,       1179,       1179,        1,  1382400, 0xb747834a
+0,       1180,       1180,        1,  1382400, 0xb747834a
+0,       1181,       1181,        1,  1382400, 0xb747834a
+0,       1182,       1182,        1,  1382400, 0xb747834a
+0,       1183,       1183,        1,  1382400, 0xb747834a
+0,       1184,       1184,        1,  1382400, 0xb747834a
+0,       1185,       1185,        1,  1382400, 0xb747834a
+0,       1187,       1187,        1,  1382400, 0x3448baad
+0,       1187,       1187,        1,  1382400, 0x3448baad
+0,       1188,       1188,        1,  1382400, 0x3448baad
+0,       1189,       1189,        1,  1382400, 0x3448baad
+0,       1190,       1190,        1,  1382400, 0x3448baad
+0,       1191,       1191,        1,  1382400, 0x3448baad
+0,       1192,       1192,        1,  1382400, 0x3448baad
+0,       1193,       1193,        1,  1382400, 0x3448baad
+0,       1194,       1194,        1,  1382400, 0x3448baad
+0,       1195,       1195,        1,  1382400, 0x3448baad
+0,       1196,       1196,        1,  1382400, 0x3448baad
+0,       1197,       1197,        1,  1382400, 0x3448baad
+0,       1198,       1198,        1,  1382400, 0x3448baad
+0,       1199,       1199,        1,  1382400, 0x3448baad
+0,       1200,       1200,        1,  1382400, 0x00000000
+0,       1201,       1201,        1,  1382400, 0xaabe4f37
+0,       1202,       1202,        1,  1382400, 0xaabe4f37
+0,       1203,       1203,        1,  1382400, 0xaabe4f37
+0,       1204,       1204,        1,  1382400, 0xaabe4f37
+0,       1205,       1205,        1,  1382400, 0xaabe4f37
+0,       1206,       1206,        1,  1382400, 0xaabe4f37
+0,       1207,       1207,        1,  1382400, 0xaabe4f37
+0,       1208,       1208,        1,  1382400, 0xaabe4f37
+0,       1209,       1209,        1,  1382400, 0xaabe4f37
+0,       1210,       1210,        1,  1382400, 0xaabe4f37
+0,       1211,       1211,        1,  1382400, 0x00000000
+0,       1212,       1212,        1,  1382400, 0x00000000
+0,       1213,       1213,        1,  1382400, 0x00000000
+0,       1214,       1214,        1,  1382400, 0x00000000
+0,       1215,       1215,        1,  1382400, 0x00000000
+0,       1216,       1216,        1,  1382400, 0x00000000
+0,       1217,       1217,        1,  1382400, 0x00000000
+0,       1218,       1218,        1,  1382400, 0x00000000
+0,       1219,       1219,        1,  1382400, 0x00000000
+0,       1220,       1220,        1,  1382400, 0x00000000
+0,       1221,       1221,        1,  1382400, 0x00000000
+0,       1222,       1222,        1,  1382400, 0x00000000
+0,       1223,       1223,        1,  1382400, 0x00000000
+0,       1224,       1224,        1,  1382400, 0x00000000
+0,       1225,       1225,        1,  1382400, 0x00000000
+0,       1226,       1226,        1,  1382400, 0x00000000
+0,       1227,       1227,        1,  1382400, 0x00000000
+0,       1228,       1228,        1,  1382400, 0x00000000
+0,       1229,       1229,        1,  1382400, 0x00000000
+0,       1230,       1230,        1,  1382400, 0x00000000
+0,       1231,       1231,        1,  1382400, 0x00000000
+0,       1232,       1232,        1,  1382400, 0x00000000
+0,       1233,       1233,        1,  1382400, 0x00000000
+0,       1234,       1234,        1,  1382400, 0x00000000
+0,       1235,       1235,        1,  1382400, 0x00000000
+0,       1236,       1236,        1,  1382400, 0x00000000
+0,       1237,       1237,        1,  1382400, 0x00000000
+0,       1238,       1238,        1,  1382400, 0x00000000
+0,       1239,       1239,        1,  1382400, 0x00000000
+0,       1240,       1240,        1,  1382400, 0x00000000
+0,       1241,       1241,        1,  1382400, 0x00000000
+0,       1242,       1242,        1,  1382400, 0x00000000
+0,       1243,       1243,        1,  1382400, 0x00000000
+0,       1244,       1244,        1,  1382400, 0x00000000
+0,       1245,       1245,        1,  1382400, 0x00000000
+0,       1246,       1246,        1,  1382400, 0x00000000
+0,       1247,       1247,        1,  1382400, 0x00000000
+0,       1248,       1248,        1,  1382400, 0x00000000
+0,       1249,       1249,        1,  1382400, 0x00000000
+0,       1250,       1250,        1,  1382400, 0x00000000
+0,       1251,       1251,        1,  1382400, 0x00000000
+0,       1252,       1252,        1,  1382400, 0x00000000
+0,       1253,       1253,        1,  1382400, 0x00000000
+0,       1254,       1254,        1,  1382400, 0x00000000
+0,       1255,       1255,        1,  1382400, 0x00000000
+0,       1256,       1256,        1,  1382400, 0x00000000
+0,       1257,       1257,        1,  1382400, 0x00000000
+0,       1258,       1258,        1,  1382400, 0x00000000
+0,       1259,       1259,        1,  1382400, 0x00000000
+0,       1260,       1260,        1,  1382400, 0x00000000
+0,       1261,       1261,        1,  1382400, 0x00000000
+0,       1262,       1262,        1,  1382400, 0x00000000
+0,       1263,       1263,        1,  1382400, 0x00000000
+0,       1264,       1264,        1,  1382400, 0x00000000
+0,       1265,       1265,        1,  1382400, 0x00000000
+0,       1266,       1266,        1,  1382400, 0x00000000
+0,       1267,       1267,        1,  1382400, 0x00000000
+0,       1268,       1268,        1,  1382400, 0x00000000
+0,       1269,       1269,        1,  1382400, 0x00000000
+0,       1270,       1270,        1,  1382400, 0x00000000
+0,       1271,       1271,        1,  1382400, 0x00000000
+0,       1272,       1272,        1,  1382400, 0x00000000
+0,       1273,       1273,        1,  1382400, 0x00000000
+0,       1274,       1274,        1,  1382400, 0x00000000
+0,       1275,       1275,        1,  1382400, 0x00000000
+0,       1276,       1276,        1,  1382400, 0x00000000
+0,       1277,       1277,        1,  1382400, 0x00000000
+0,       1278,       1278,        1,  1382400, 0x00000000
+0,       1279,       1279,        1,  1382400, 0x00000000
+0,       1280,       1280,        1,  1382400, 0x00000000
+0,       1281,       1281,        1,  1382400, 0x00000000
+0,       1282,       1282,        1,  1382400, 0x00000000
+0,       1283,       1283,        1,  1382400, 0x00000000
+0,       1284,       1284,        1,  1382400, 0x00000000
+0,       1285,       1285,        1,  1382400, 0x00000000
+0,       1286,       1286,        1,  1382400, 0x00000000
+0,       1287,       1287,        1,  1382400, 0x00000000
+0,       1288,       1288,        1,  1382400, 0x00000000
+0,       1289,       1289,        1,  1382400, 0x00000000
+0,       1290,       1290,        1,  1382400, 0x00000000
+0,       1291,       1291,        1,  1382400, 0x00000000
+0,       1292,       1292,        1,  1382400, 0x00000000
+0,       1293,       1293,        1,  1382400, 0x00000000
+0,       1294,       1294,        1,  1382400, 0x00000000
+0,       1295,       1295,        1,  1382400, 0x00000000
+0,       1296,       1296,        1,  1382400, 0x00000000
+0,       1297,       1297,        1,  1382400, 0x00000000
+0,       1298,       1298,        1,  1382400, 0x00000000
+0,       1299,       1299,        1,  1382400, 0x00000000
+0,       1300,       1300,        1,  1382400, 0x00000000
+0,       1301,       1301,        1,  1382400, 0x00000000
+0,       1302,       1302,        1,  1382400, 0x00000000
+0,       1303,       1303,        1,  1382400, 0x00000000
+0,       1304,       1304,        1,  1382400, 0x00000000
+0,       1305,       1305,        1,  1382400, 0x00000000
+0,       1306,       1306,        1,  1382400, 0x00000000
+0,       1307,       1307,        1,  1382400, 0x00000000
+0,       1308,       1308,        1,  1382400, 0x00000000
+0,       1309,       1309,        1,  1382400, 0x00000000
+0,       1310,       1310,        1,  1382400, 0x00000000
+0,       1311,       1311,        1,  1382400, 0x00000000
+0,       1312,       1312,        1,  1382400, 0x00000000
+0,       1313,       1313,        1,  1382400, 0x00000000
+0,       1314,       1314,        1,  1382400, 0x00000000
+0,       1315,       1315,        1,  1382400, 0x00000000
+0,       1316,       1316,        1,  1382400, 0x00000000
+0,       1317,       1317,        1,  1382400, 0x00000000
+0,       1318,       1318,        1,  1382400, 0x00000000
+0,       1319,       1319,        1,  1382400, 0x00000000
+0,       1320,       1320,        1,  1382400, 0x00000000
+0,       1321,       1321,        1,  1382400, 0x00000000
+0,       1322,       1322,        1,  1382400, 0x00000000
+0,       1323,       1323,        1,  1382400, 0x00000000
+0,       1324,       1324,        1,  1382400, 0x00000000
+0,       1325,       1325,        1,  1382400, 0x00000000
+0,       1326,       1326,        1,  1382400, 0x00000000
+0,       1327,       1327,        1,  1382400, 0x00000000
+0,       1328,       1328,        1,  1382400, 0x00000000
+0,       1329,       1329,        1,  1382400, 0x00000000
+0,       1330,       1330,        1,  1382400, 0x00000000
+0,       1331,       1331,        1,  1382400, 0x00000000
+0,       1332,       1332,        1,  1382400, 0x00000000
+0,       1333,       1333,        1,  1382400, 0x00000000
+0,       1334,       1334,        1,  1382400, 0x00000000
+0,       1335,       1335,        1,  1382400, 0x00000000
+0,       1336,       1336,        1,  1382400, 0x00000000
+0,       1337,       1337,        1,  1382400, 0x00000000
+0,       1338,       1338,        1,  1382400, 0x00000000
+0,       1339,       1339,        1,  1382400, 0x00000000
+0,       1340,       1340,        1,  1382400, 0x00000000
+0,       1341,       1341,        1,  1382400, 0x00000000
+0,       1342,       1342,        1,  1382400, 0x00000000
+0,       1343,       1343,        1,  1382400, 0x00000000
+0,       1344,       1344,        1,  1382400, 0x00000000
+0,       1345,       1345,        1,  1382400, 0x00000000
+0,       1346,       1346,        1,  1382400, 0x00000000
+0,       1347,       1347,        1,  1382400, 0x00000000
+0,       1348,       1348,        1,  1382400, 0x00000000
+0,       1349,       1349,        1,  1382400, 0x00000000
+0,       1350,       1350,        1,  1382400, 0x00000000
+0,       1351,       1351,        1,  1382400, 0x00000000
+0,       1352,       1352,        1,  1382400, 0x00000000
+0,       1353,       1353,        1,  1382400, 0x00000000
+0,       1354,       1354,        1,  1382400, 0x00000000
+0,       1355,       1355,        1,  1382400, 0x00000000
+0,       1356,       1356,        1,  1382400, 0x00000000
+0,       1357,       1357,        1,  1382400, 0x00000000
+0,       1358,       1358,        1,  1382400, 0x00000000
+0,       1359,       1359,        1,  1382400, 0x00000000
+0,       1360,       1360,        1,  1382400, 0x00000000
+0,       1361,       1361,        1,  1382400, 0x00000000
+0,       1362,       1362,        1,  1382400, 0x00000000
+0,       1363,       1363,        1,  1382400, 0x00000000
+0,       1364,       1364,        1,  1382400, 0x00000000
+0,       1365,       1365,        1,  1382400, 0x00000000
+0,       1366,       1366,        1,  1382400, 0x00000000
+0,       1368,       1368,        1,  1382400, 0x8a48cd6f
+0,       1368,       1368,        1,  1382400, 0x8a48cd6f
+0,       1369,       1369,        1,  1382400, 0x8a48cd6f
+0,       1370,       1370,        1,  1382400, 0x8a48cd6f
+0,       1371,       1371,        1,  1382400, 0x8a48cd6f
+0,       1372,       1372,        1,  1382400, 0x8a48cd6f
+0,       1373,       1373,        1,  1382400, 0x8a48cd6f
+0,       1374,       1374,        1,  1382400, 0x8a48cd6f
+0,       1375,       1375,        1,  1382400, 0x8a48cd6f
+0,       1376,       1376,        1,  1382400, 0x00000000
+0,       1377,       1377,        1,  1382400, 0x00000000
+0,       1378,       1378,        1,  1382400, 0x00000000
+0,       1379,       1379,        1,  1382400, 0x00000000
+0,       1380,       1380,        1,  1382400, 0x00000000
+0,       1381,       1381,        1,  1382400, 0x00000000
+0,       1382,       1382,        1,  1382400, 0x00000000
+0,       1383,       1383,        1,  1382400, 0x00000000
+0,       1384,       1384,        1,  1382400, 0x00000000
+0,       1385,       1385,        1,  1382400, 0x00000000
+0,       1386,       1386,        1,  1382400, 0x00000000
+0,       1387,       1387,        1,  1382400, 0x00000000
+0,       1388,       1388,        1,  1382400, 0x00000000
+0,       1389,       1389,        1,  1382400, 0x00000000
+0,       1390,       1390,        1,  1382400, 0x00000000
+0,       1391,       1391,        1,  1382400, 0x00000000
+0,       1392,       1392,        1,  1382400, 0x00000000
+0,       1393,       1393,        1,  1382400, 0x00000000
+0,       1394,       1394,        1,  1382400, 0x00000000
+0,       1395,       1395,        1,  1382400, 0x00000000
+0,       1396,       1396,        1,  1382400, 0x00000000
+0,       1397,       1397,        1,  1382400, 0x00000000
+0,       1398,       1398,        1,  1382400, 0x00000000
+0,       1399,       1399,        1,  1382400, 0x00000000
+0,       1400,       1400,        1,  1382400, 0x00000000
+0,       1401,       1401,        1,  1382400, 0x00000000
+0,       1402,       1402,        1,  1382400, 0x00000000
+0,       1403,       1403,        1,  1382400, 0x00000000
+0,       1404,       1404,        1,  1382400, 0x00000000
+0,       1405,       1405,        1,  1382400, 0x00000000
+0,       1406,       1406,        1,  1382400, 0x00000000
+0,       1407,       1407,        1,  1382400, 0x00000000
+0,       1408,       1408,        1,  1382400, 0x00000000
+0,       1409,       1409,        1,  1382400, 0x00000000
+0,       1410,       1410,        1,  1382400, 0x00000000
+0,       1411,       1411,        1,  1382400, 0x00000000
+0,       1412,       1412,        1,  1382400, 0x00000000
+0,       1413,       1413,        1,  1382400, 0x00000000
+0,       1414,       1414,        1,  1382400, 0x00000000
+0,       1415,       1415,        1,  1382400, 0x00000000
+0,       1416,       1416,        1,  1382400, 0x00000000
+0,       1417,       1417,        1,  1382400, 0x00000000
+0,       1418,       1418,        1,  1382400, 0x00000000
+0,       1419,       1419,        1,  1382400, 0x00000000
+0,       1420,       1420,        1,  1382400, 0x00000000
+0,       1421,       1421,        1,  1382400, 0x00000000
+0,       1422,       1422,        1,  1382400, 0x00000000
+0,       1423,       1423,        1,  1382400, 0x00000000
+0,       1424,       1424,        1,  1382400, 0x00000000
+0,       1425,       1425,        1,  1382400, 0x00000000
+0,       1426,       1426,        1,  1382400, 0x00000000
+0,       1427,       1427,        1,  1382400, 0x00000000
+0,       1428,       1428,        1,  1382400, 0x00000000
+0,       1429,       1429,        1,  1382400, 0x00000000
+0,       1430,       1430,        1,  1382400, 0x00000000
+0,       1431,       1431,        1,  1382400, 0x00000000
+0,       1432,       1432,        1,  1382400, 0x00000000
+0,       1433,       1433,        1,  1382400, 0x00000000
+0,       1434,       1434,        1,  1382400, 0x00000000
+0,       1435,       1435,        1,  1382400, 0x00000000
+0,       1436,       1436,        1,  1382400, 0x00000000
+0,       1437,       1437,        1,  1382400, 0x00000000
+0,       1438,       1438,        1,  1382400, 0x00000000
+0,       1439,       1439,        1,  1382400, 0x00000000
+0,       1440,       1440,        1,  1382400, 0x00000000
+0,       1441,       1441,        1,  1382400, 0x00000000
+0,       1442,       1442,        1,  1382400, 0x00000000
+0,       1443,       1443,        1,  1382400, 0x00000000
+0,       1444,       1444,        1,  1382400, 0x00000000
+0,       1445,       1445,        1,  1382400, 0x00000000
+0,       1446,       1446,        1,  1382400, 0x00000000
+0,       1447,       1447,        1,  1382400, 0x00000000
+0,       1448,       1448,        1,  1382400, 0x00000000
+0,       1449,       1449,        1,  1382400, 0x00000000
+0,       1450,       1450,        1,  1382400, 0x00000000
+0,       1451,       1451,        1,  1382400, 0x00000000
+0,       1452,       1452,        1,  1382400, 0x00000000
+0,       1453,       1453,        1,  1382400, 0x00000000
+0,       1454,       1454,        1,  1382400, 0x00000000
+0,       1455,       1455,        1,  1382400, 0x00000000
+0,       1456,       1456,        1,  1382400, 0x00000000
+0,       1457,       1457,        1,  1382400, 0x00000000
+0,       1458,       1458,        1,  1382400, 0x00000000
+0,       1459,       1459,        1,  1382400, 0x00000000
+0,       1460,       1460,        1,  1382400, 0x00000000
+0,       1461,       1461,        1,  1382400, 0x00000000
+0,       1462,       1462,        1,  1382400, 0x00000000
+0,       1463,       1463,        1,  1382400, 0x00000000
+0,       1464,       1464,        1,  1382400, 0x00000000
+0,       1465,       1465,        1,  1382400, 0x00000000
+0,       1466,       1466,        1,  1382400, 0x00000000
+0,       1467,       1467,        1,  1382400, 0x00000000
+0,       1468,       1468,        1,  1382400, 0x00000000
+0,       1469,       1469,        1,  1382400, 0x00000000
+0,       1470,       1470,        1,  1382400, 0x00000000
+0,       1471,       1471,        1,  1382400, 0x00000000
+0,       1472,       1472,        1,  1382400, 0x00000000
+0,       1473,       1473,        1,  1382400, 0x00000000
+0,       1474,       1474,        1,  1382400, 0x00000000
+0,       1475,       1475,        1,  1382400, 0x00000000
+0,       1477,       1477,        1,  1382400, 0x49518c43
+0,       1477,       1477,        1,  1382400, 0x49518c43
+0,       1478,       1478,        1,  1382400, 0x49518c43
+0,       1479,       1479,        1,  1382400, 0x49518c43
+0,       1480,       1480,        1,  1382400, 0x49518c43
+0,       1481,       1481,        1,  1382400, 0x49518c43
+0,       1482,       1482,        1,  1382400, 0x49518c43
+0,       1483,       1483,        1,  1382400, 0x49518c43
+0,       1484,       1484,        1,  1382400, 0x00000000
+0,       1485,       1485,        1,  1382400, 0x00000000
+0,       1486,       1486,        1,  1382400, 0x00000000
+0,       1487,       1487,        1,  1382400, 0x00000000
+0,       1488,       1488,        1,  1382400, 0x00000000
+0,       1489,       1489,        1,  1382400, 0x00000000
+0,       1490,       1490,        1,  1382400, 0x00000000
+0,       1491,       1491,        1,  1382400, 0x00000000
+0,       1492,       1492,        1,  1382400, 0x00000000
+0,       1493,       1493,        1,  1382400, 0x00000000
+0,       1494,       1494,        1,  1382400, 0x00000000
+0,       1495,       1495,        1,  1382400, 0x00000000
+0,       1496,       1496,        1,  1382400, 0x00000000
+0,       1497,       1497,        1,  1382400, 0x00000000
+0,       1498,       1498,        1,  1382400, 0x00000000
+0,       1500,       1500,        1,  1382400, 0x4a72fa21
+0,       1500,       1500,        1,  1382400, 0x4a72fa21
+0,       1501,       1501,        1,  1382400, 0x4a72fa21
+0,       1502,       1502,        1,  1382400, 0x4a72fa21
+0,       1503,       1503,        1,  1382400, 0x4a72fa21
+0,       1504,       1504,        1,  1382400, 0x4a72fa21
+0,       1505,       1505,        1,  1382400, 0x4a72fa21
+0,       1506,       1506,        1,  1382400, 0x4a72fa21
+0,       1507,       1507,        1,  1382400, 0x4a72fa21
+0,       1508,       1508,        1,  1382400, 0x4a72fa21
+0,       1509,       1509,        1,  1382400, 0x4a72fa21
+0,       1510,       1510,        1,  1382400, 0x00000000
+0,       1511,       1511,        1,  1382400, 0xa82f7de8
+0,       1512,       1512,        1,  1382400, 0xa82f7de8
+0,       1513,       1513,        1,  1382400, 0xa82f7de8
+0,       1514,       1514,        1,  1382400, 0xa82f7de8
+0,       1515,       1515,        1,  1382400, 0xa82f7de8
+0,       1516,       1516,        1,  1382400, 0xa82f7de8
+0,       1517,       1517,        1,  1382400, 0xa82f7de8
+0,       1518,       1518,        1,  1382400, 0x00000000
+0,       1519,       1519,        1,  1382400, 0x00000000
+0,       1521,       1521,        1,  1382400, 0xeba0b5f3
+0,       1521,       1521,        1,  1382400, 0xeba0b5f3
+0,       1522,       1522,        1,  1382400, 0xeba0b5f3
+0,       1523,       1523,        1,  1382400, 0xeba0b5f3
+0,       1524,       1524,        1,  1382400, 0xeba0b5f3
+0,       1525,       1525,        1,  1382400, 0xeba0b5f3
+0,       1526,       1526,        1,  1382400, 0xeba0b5f3
+0,       1527,       1527,        1,  1382400, 0xeba0b5f3
+0,       1528,       1528,        1,  1382400, 0xeba0b5f3
+0,       1530,       1530,        1,  1382400, 0xd6a91770
+0,       1530,       1530,        1,  1382400, 0xd6a91770
+0,       1531,       1531,        1,  1382400, 0xd6a91770
+0,       1532,       1532,        1,  1382400, 0xd6a91770
+0,       1533,       1533,        1,  1382400, 0xd6a91770
+0,       1534,       1534,        1,  1382400, 0xd6a91770
+0,       1535,       1535,        1,  1382400, 0xd6a91770
+0,       1536,       1536,        1,  1382400, 0xd6a91770
+0,       1537,       1537,        1,  1382400, 0xd6a91770
+0,       1538,       1538,        1,  1382400, 0xd6a91770
+0,       1539,       1539,        1,  1382400, 0x00000000
+0,       1540,       1540,        1,  1382400, 0x222f827c
+0,       1541,       1541,        1,  1382400, 0x222f827c
+0,       1542,       1542,        1,  1382400, 0x222f827c
+0,       1543,       1543,        1,  1382400, 0x222f827c
+0,       1544,       1544,        1,  1382400, 0x222f827c
+0,       1545,       1545,        1,  1382400, 0x222f827c
+0,       1546,       1546,        1,  1382400, 0x222f827c
+0,       1547,       1547,        1,  1382400, 0x222f827c
+0,       1548,       1548,        1,  1382400, 0x222f827c
+0,       1549,       1549,        1,  1382400, 0x00000000
+0,       1550,       1550,        1,  1382400, 0x00000000
+0,       1551,       1551,        1,  1382400, 0x00000000
+0,       1552,       1552,        1,  1382400, 0x00000000
+0,       1553,       1553,        1,  1382400, 0x00000000
+0,       1554,       1554,        1,  1382400, 0x00000000
+0,       1555,       1555,        1,  1382400, 0x00000000
+0,       1556,       1556,        1,  1382400, 0x00000000
+0,       1557,       1557,        1,  1382400, 0x00000000
+0,       1558,       1558,        1,  1382400, 0x00000000
+0,       1559,       1559,        1,  1382400, 0x00000000
+0,       1560,       1560,        1,  1382400, 0x00000000
+0,       1561,       1561,        1,  1382400, 0x00000000
+0,       1562,       1562,        1,  1382400, 0x00000000
+0,       1563,       1563,        1,  1382400, 0x00000000
+0,       1564,       1564,        1,  1382400, 0x00000000
+0,       1565,       1565,        1,  1382400, 0x00000000
+0,       1566,       1566,        1,  1382400, 0x00000000
+0,       1567,       1567,        1,  1382400, 0x00000000
+0,       1568,       1568,        1,  1382400, 0x00000000
+0,       1569,       1569,        1,  1382400, 0x00000000
+0,       1570,       1570,        1,  1382400, 0x00000000
+0,       1571,       1571,        1,  1382400, 0x00000000
+0,       1572,       1572,        1,  1382400, 0x00000000
+0,       1573,       1573,        1,  1382400, 0x00000000
+0,       1574,       1574,        1,  1382400, 0x00000000
+0,       1575,       1575,        1,  1382400, 0x00000000
+0,       1576,       1576,        1,  1382400, 0x00000000
+0,       1577,       1577,        1,  1382400, 0x00000000
+0,       1578,       1578,        1,  1382400, 0x00000000
+0,       1579,       1579,        1,  1382400, 0x00000000
+0,       1580,       1580,        1,  1382400, 0x00000000
+0,       1581,       1581,        1,  1382400, 0x00000000
+0,       1582,       1582,        1,  1382400, 0x00000000
+0,       1583,       1583,        1,  1382400, 0x00000000
+0,       1584,       1584,        1,  1382400, 0x00000000
+0,       1585,       1585,        1,  1382400, 0x00000000
+0,       1586,       1586,        1,  1382400, 0x00000000
+0,       1587,       1587,        1,  1382400, 0x00000000
+0,       1588,       1588,        1,  1382400, 0x00000000
+0,       1589,       1589,        1,  1382400, 0x00000000
+0,       1590,       1590,        1,  1382400, 0x00000000
+0,       1591,       1591,        1,  1382400, 0x00000000
+0,       1592,       1592,        1,  1382400, 0x00000000
+0,       1593,       1593,        1,  1382400, 0x00000000
+0,       1594,       1594,        1,  1382400, 0x00000000
+0,       1595,       1595,        1,  1382400, 0x00000000
+0,       1596,       1596,        1,  1382400, 0x00000000
+0,       1597,       1597,        1,  1382400, 0x00000000
+0,       1598,       1598,        1,  1382400, 0x00000000
+0,       1599,       1599,        1,  1382400, 0x00000000
+0,       1600,       1600,        1,  1382400, 0x00000000
+0,       1601,       1601,        1,  1382400, 0x00000000
+0,       1602,       1602,        1,  1382400, 0x00000000
+0,       1603,       1603,        1,  1382400, 0x00000000
+0,       1604,       1604,        1,  1382400, 0x00000000
+0,       1606,       1606,        1,  1382400, 0x3270f4ff
+0,       1606,       1606,        1,  1382400, 0x3270f4ff
+0,       1607,       1607,        1,  1382400, 0x3270f4ff
+0,       1608,       1608,        1,  1382400, 0x3270f4ff
+0,       1609,       1609,        1,  1382400, 0x3270f4ff
+0,       1610,       1610,        1,  1382400, 0x3270f4ff
+0,       1611,       1611,        1,  1382400, 0x3270f4ff
+0,       1612,       1612,        1,  1382400, 0x3270f4ff
+0,       1613,       1613,        1,  1382400, 0x3270f4ff
+0,       1614,       1614,        1,  1382400, 0x3270f4ff
+0,       1615,       1615,        1,  1382400, 0x3270f4ff
+0,       1617,       1617,        1,  1382400, 0x40813cb3
+0,       1617,       1617,        1,  1382400, 0x40813cb3
+0,       1618,       1618,        1,  1382400, 0x40813cb3
+0,       1619,       1619,        1,  1382400, 0x40813cb3
+0,       1620,       1620,        1,  1382400, 0x40813cb3
+0,       1621,       1621,        1,  1382400, 0x40813cb3
+0,       1622,       1622,        1,  1382400, 0x40813cb3
+0,       1623,       1623,        1,  1382400, 0x9d8fde41
+0,       1624,       1624,        1,  1382400, 0x9d8fde41
+0,       1625,       1625,        1,  1382400, 0x9d8fde41
+0,       1626,       1626,        1,  1382400, 0x9d8fde41
+0,       1627,       1627,        1,  1382400, 0x9d8fde41
+0,       1628,       1628,        1,  1382400, 0x9d8fde41
+0,       1629,       1629,        1,  1382400, 0x9d8fde41
+0,       1630,       1630,        1,  1382400, 0x9d8fde41
+0,       1631,       1631,        1,  1382400, 0x9d8fde41
+0,       1632,       1632,        1,  1382400, 0x00000000
+0,       1633,       1633,        1,  1382400, 0x00000000
+0,       1634,       1634,        1,  1382400, 0x00000000
+0,       1636,       1636,        1,  1382400, 0xc6d7a701
+0,       1636,       1636,        1,  1382400, 0xc6d7a701
+0,       1637,       1637,        1,  1382400, 0xc6d7a701
+0,       1638,       1638,        1,  1382400, 0xc6d7a701
+0,       1639,       1639,        1,  1382400, 0xc6d7a701
+0,       1640,       1640,        1,  1382400, 0xc6d7a701
+0,       1642,       1642,        1,  1382400, 0x9d45f2dc
+0,       1642,       1642,        1,  1382400, 0x9d45f2dc
+0,       1643,       1643,        1,  1382400, 0x9d45f2dc
+0,       1644,       1644,        1,  1382400, 0x9d45f2dc
+0,       1645,       1645,        1,  1382400, 0x9d45f2dc
+0,       1646,       1646,        1,  1382400, 0x9d45f2dc
+0,       1647,       1647,        1,  1382400, 0x9d45f2dc
+0,       1648,       1648,        1,  1382400, 0x9d45f2dc
+0,       1649,       1649,        1,  1382400, 0x00000000
+0,       1650,       1650,        1,  1382400, 0x8525ee40
+0,       1651,       1651,        1,  1382400, 0x8525ee40
+0,       1652,       1652,        1,  1382400, 0x8525ee40
+0,       1653,       1653,        1,  1382400, 0x8525ee40
+0,       1654,       1654,        1,  1382400, 0x8525ee40
+0,       1655,       1655,        1,  1382400, 0x8525ee40
+0,       1656,       1656,        1,  1382400, 0x5b26b98b
+0,       1657,       1657,        1,  1382400, 0x5b26b98b
+0,       1658,       1658,        1,  1382400, 0x5b26b98b
+0,       1659,       1659,        1,  1382400, 0x5b26b98b
+0,       1660,       1660,        1,  1382400, 0x5b26b98b
+0,       1661,       1661,        1,  1382400, 0x5b26b98b
+0,       1662,       1662,        1,  1382400, 0x5b26b98b
+0,       1663,       1663,        1,  1382400, 0x5b26b98b
+0,       1664,       1664,        1,  1382400, 0x00000000
+0,       1665,       1665,        1,  1382400, 0x51be311f
+0,       1666,       1666,        1,  1382400, 0x51be311f
+0,       1667,       1667,        1,  1382400, 0x51be311f
+0,       1668,       1668,        1,  1382400, 0x51be311f
+0,       1669,       1669,        1,  1382400, 0x51be311f
+0,       1670,       1670,        1,  1382400, 0x51be311f
+0,       1671,       1671,        1,  1382400, 0x51be311f
+0,       1672,       1672,        1,  1382400, 0x51be311f
+0,       1673,       1673,        1,  1382400, 0x00000000
+0,       1674,       1674,        1,  1382400, 0x00000000
+0,       1675,       1675,        1,  1382400, 0x00000000
+0,       1676,       1676,        1,  1382400, 0x00000000
+0,       1677,       1677,        1,  1382400, 0x00000000
+0,       1678,       1678,        1,  1382400, 0x00000000
+0,       1679,       1679,        1,  1382400, 0x00000000
+0,       1680,       1680,        1,  1382400, 0x00000000
+0,       1681,       1681,        1,  1382400, 0x00000000
+0,       1682,       1682,        1,  1382400, 0x00000000
+0,       1683,       1683,        1,  1382400, 0x00000000
+0,       1684,       1684,        1,  1382400, 0x00000000
+0,       1685,       1685,        1,  1382400, 0x00000000
+0,       1686,       1686,        1,  1382400, 0x00000000
+0,       1687,       1687,        1,  1382400, 0x00000000
+0,       1688,       1688,        1,  1382400, 0x00000000
+0,       1689,       1689,        1,  1382400, 0x00000000
+0,       1690,       1690,        1,  1382400, 0x00000000
+0,       1691,       1691,        1,  1382400, 0x00000000
+0,       1692,       1692,        1,  1382400, 0x00000000
+0,       1693,       1693,        1,  1382400, 0x00000000
+0,       1694,       1694,        1,  1382400, 0x00000000
+0,       1695,       1695,        1,  1382400, 0x00000000
+0,       1696,       1696,        1,  1382400, 0x00000000
+0,       1697,       1697,        1,  1382400, 0x00000000
+0,       1698,       1698,        1,  1382400, 0x00000000
+0,       1699,       1699,        1,  1382400, 0x00000000
+0,       1700,       1700,        1,  1382400, 0x00000000
+0,       1701,       1701,        1,  1382400, 0x00000000
+0,       1702,       1702,        1,  1382400, 0x00000000
+0,       1703,       1703,        1,  1382400, 0x00000000
+0,       1704,       1704,        1,  1382400, 0x00000000
+0,       1705,       1705,        1,  1382400, 0x00000000
+0,       1706,       1706,        1,  1382400, 0x00000000
+0,       1707,       1707,        1,  1382400, 0x00000000
+0,       1708,       1708,        1,  1382400, 0x00000000
+0,       1709,       1709,        1,  1382400, 0x00000000
+0,       1710,       1710,        1,  1382400, 0x00000000
+0,       1711,       1711,        1,  1382400, 0x00000000
+0,       1713,       1713,        1,  1382400, 0x00a4f2a3
+0,       1713,       1713,        1,  1382400, 0x00a4f2a3
+0,       1714,       1714,        1,  1382400, 0x00a4f2a3
+0,       1715,       1715,        1,  1382400, 0x00a4f2a3
+0,       1716,       1716,        1,  1382400, 0x00a4f2a3
+0,       1717,       1717,        1,  1382400, 0x00a4f2a3
+0,       1718,       1718,        1,  1382400, 0x00a4f2a3
+0,       1719,       1719,        1,  1382400, 0x00a4f2a3
+0,       1720,       1720,        1,  1382400, 0x00a4f2a3
+0,       1721,       1721,        1,  1382400, 0x00a4f2a3
+0,       1722,       1722,        1,  1382400, 0x00000000
+0,       1723,       1723,        1,  1382400, 0x00000000
+0,       1724,       1724,        1,  1382400, 0x00000000
+0,       1725,       1725,        1,  1382400, 0x00000000
+0,       1726,       1726,        1,  1382400, 0x00000000
+0,       1727,       1727,        1,  1382400, 0x00000000
+0,       1728,       1728,        1,  1382400, 0x00000000
+0,       1729,       1729,        1,  1382400, 0x00000000
+0,       1730,       1730,        1,  1382400, 0x00000000
+0,       1731,       1731,        1,  1382400, 0x00000000
+0,       1732,       1732,        1,  1382400, 0x00000000
+0,       1734,       1734,        1,  1382400, 0x40a445e8
+0,       1734,       1734,        1,  1382400, 0x40a445e8
+0,       1735,       1735,        1,  1382400, 0x40a445e8
+0,       1736,       1736,        1,  1382400, 0x40a445e8
+0,       1737,       1737,        1,  1382400, 0x40a445e8
+0,       1738,       1738,        1,  1382400, 0x40a445e8
+0,       1739,       1739,        1,  1382400, 0x40a445e8
+0,       1740,       1740,        1,  1382400, 0x40a445e8
+0,       1741,       1741,        1,  1382400, 0x40a445e8
+0,       1742,       1742,        1,  1382400, 0x00000000
+0,       1743,       1743,        1,  1382400, 0x00000000
+0,       1744,       1744,        1,  1382400, 0x00000000
+0,       1745,       1745,        1,  1382400, 0x00000000
+0,       1746,       1746,        1,  1382400, 0x00000000
+0,       1747,       1747,        1,  1382400, 0x00000000
+0,       1748,       1748,        1,  1382400, 0x00000000
+0,       1749,       1749,        1,  1382400, 0x00000000
+0,       1750,       1750,        1,  1382400, 0x00000000
+0,       1751,       1751,        1,  1382400, 0x00000000
+0,       1752,       1752,        1,  1382400, 0x00000000
+0,       1753,       1753,        1,  1382400, 0x00000000
+0,       1754,       1754,        1,  1382400, 0x00000000
+0,       1755,       1755,        1,  1382400, 0x00000000
+0,       1756,       1756,        1,  1382400, 0x00000000
+0,       1757,       1757,        1,  1382400, 0x00000000
+0,       1758,       1758,        1,  1382400, 0x00000000
+0,       1759,       1759,        1,  1382400, 0x00000000
+0,       1760,       1760,        1,  1382400, 0x00000000
+0,       1761,       1761,        1,  1382400, 0x00000000
+0,       1762,       1762,        1,  1382400, 0x00000000
+0,       1763,       1763,        1,  1382400, 0x00000000
+0,       1764,       1764,        1,  1382400, 0x00000000
+0,       1765,       1765,        1,  1382400, 0x00000000
+0,       1766,       1766,        1,  1382400, 0x00000000
+0,       1767,       1767,        1,  1382400, 0x00000000
+0,       1768,       1768,        1,  1382400, 0x00000000
+0,       1769,       1769,        1,  1382400, 0x00000000
+0,       1770,       1770,        1,  1382400, 0x00000000
+0,       1771,       1771,        1,  1382400, 0x00000000
+0,       1772,       1772,        1,  1382400, 0x00000000
+0,       1773,       1773,        1,  1382400, 0x00000000
+0,       1774,       1774,        1,  1382400, 0x00000000
+0,       1775,       1775,        1,  1382400, 0x00000000
+0,       1776,       1776,        1,  1382400, 0x00000000
+0,       1777,       1777,        1,  1382400, 0x00000000
+0,       1778,       1778,        1,  1382400, 0x00000000
+0,       1779,       1779,        1,  1382400, 0x00000000
+0,       1780,       1780,        1,  1382400, 0x00000000
+0,       1781,       1781,        1,  1382400, 0x00000000
+0,       1782,       1782,        1,  1382400, 0x00000000
+0,       1783,       1783,        1,  1382400, 0x00000000
+0,       1784,       1784,        1,  1382400, 0x00000000
+0,       1785,       1785,        1,  1382400, 0x00000000
+0,       1786,       1786,        1,  1382400, 0x00000000
+0,       1788,       1788,        1,  1382400, 0x43ef5128
+0,       1788,       1788,        1,  1382400, 0x43ef5128
+0,       1789,       1789,        1,  1382400, 0x43ef5128
+0,       1790,       1790,        1,  1382400, 0x43ef5128
+0,       1791,       1791,        1,  1382400, 0x43ef5128
+0,       1792,       1792,        1,  1382400, 0x43ef5128
+0,       1793,       1793,        1,  1382400, 0x43ef5128
+0,       1794,       1794,        1,  1382400, 0x43ef5128
+0,       1795,       1795,        1,  1382400, 0x43ef5128
+0,       1796,       1796,        1,  1382400, 0x43ef5128
+0,       1797,       1797,        1,  1382400, 0x43ef5128
+0,       1798,       1798,        1,  1382400, 0x43ef5128
+0,       1799,       1799,        1,  1382400, 0x3c3e3819
+0,       1800,       1800,        1,  1382400, 0x3c3e3819
+0,       1801,       1801,        1,  1382400, 0x3c3e3819
+0,       1802,       1802,        1,  1382400, 0x3c3e3819
+0,       1803,       1803,        1,  1382400, 0x3c3e3819
+0,       1804,       1804,        1,  1382400, 0x3c3e3819
+0,       1805,       1805,        1,  1382400, 0x00000000
diff --git a/tests/ref/fate/sub2video_time_limited b/tests/ref/fate/sub2video_time_limited
index 9fb6fb06f9..3a7cca384a 100644
--- a/tests/ref/fate/sub2video_time_limited
+++ b/tests/ref/fate/sub2video_time_limited
@@ -1,8 +1,80 @@
-#tb 0: 1/25
+#tb 0: 1/5
 #media_type 0: video
 #codec_id 0: rawvideo
 #dimensions 0: 1920x1080
-#sar 0: 0/1
-0,          2,          2,        1,  8294400, 0x00000000
+#sar 0: 1/1
+0,          0,          0,        1,  8294400, 0x00000000
+0,          1,          1,        1,  8294400, 0x00000000
 0,          2,          2,        1,  8294400, 0xa87c518f
+0,          3,          3,        1,  8294400, 0xa87c518f
+0,          4,          4,        1,  8294400, 0xa87c518f
+0,          5,          5,        1,  8294400, 0xa87c518f
+0,          6,          6,        1,  8294400, 0xa87c518f
+0,          7,          7,        1,  8294400, 0xa87c518f
+0,          8,          8,        1,  8294400, 0xa87c518f
+0,          9,          9,        1,  8294400, 0xa87c518f
 0,         10,         10,        1,  8294400, 0xa87c518f
+0,         11,         11,        1,  8294400, 0xa87c518f
+0,         12,         12,        1,  8294400, 0xa87c518f
+0,         13,         13,        1,  8294400, 0xa87c518f
+0,         14,         14,        1,  8294400, 0xa87c518f
+0,         15,         15,        1,  8294400, 0xa87c518f
+0,         16,         16,        1,  8294400, 0xa87c518f
+0,         17,         17,        1,  8294400, 0xa87c518f
+0,         18,         18,        1,  8294400, 0xa87c518f
+0,         19,         19,        1,  8294400, 0xa87c518f
+0,         20,         20,        1,  8294400, 0xa87c518f
+0,         21,         21,        1,  8294400, 0xa87c518f
+0,         22,         22,        1,  8294400, 0xa87c518f
+0,         23,         23,        1,  8294400, 0xa87c518f
+0,         24,         24,        1,  8294400, 0xa87c518f
+0,         25,         25,        1,  8294400, 0xa87c518f
+0,         26,         26,        1,  8294400, 0xa87c518f
+0,         27,         27,        1,  8294400, 0xa87c518f
+0,         28,         28,        1,  8294400, 0xa87c518f
+0,         29,         29,        1,  8294400, 0xa87c518f
+0,         30,         30,        1,  8294400, 0xa87c518f
+0,         31,         31,        1,  8294400, 0xa87c518f
+0,         32,         32,        1,  8294400, 0xa87c518f
+0,         33,         33,        1,  8294400, 0xa87c518f
+0,         34,         34,        1,  8294400, 0xa87c518f
+0,         35,         35,        1,  8294400, 0xa87c518f
+0,         36,         36,        1,  8294400, 0xa87c518f
+0,         37,         37,        1,  8294400, 0xa87c518f
+0,         38,         38,        1,  8294400, 0xa87c518f
+0,         39,         39,        1,  8294400, 0xa87c518f
+0,         40,         40,        1,  8294400, 0xa87c518f
+0,         41,         41,        1,  8294400, 0xa87c518f
+0,         42,         42,        1,  8294400, 0xa87c518f
+0,         43,         43,        1,  8294400, 0xa87c518f
+0,         44,         44,        1,  8294400, 0xa87c518f
+0,         45,         45,        1,  8294400, 0xa87c518f
+0,         46,         46,        1,  8294400, 0xa87c518f
+0,         47,         47,        1,  8294400, 0xa87c518f
+0,         48,         48,        1,  8294400, 0xa87c518f
+0,         49,         49,        1,  8294400, 0xa87c518f
+0,         50,         50,        1,  8294400, 0xa87c518f
+0,         51,         51,        1,  8294400, 0xa87c518f
+0,         52,         52,        1,  8294400, 0xa87c518f
+0,         53,         53,        1,  8294400, 0xa87c518f
+0,         54,         54,        1,  8294400, 0xa87c518f
+0,         55,         55,        1,  8294400, 0xa87c518f
+0,         56,         56,        1,  8294400, 0xa87c518f
+0,         57,         57,        1,  8294400, 0xa87c518f
+0,         58,         58,        1,  8294400, 0xa87c518f
+0,         59,         59,        1,  8294400, 0xa87c518f
+0,         60,         60,        1,  8294400, 0xa87c518f
+0,         61,         61,        1,  8294400, 0xa87c518f
+0,         62,         62,        1,  8294400, 0xa87c518f
+0,         63,         63,        1,  8294400, 0xa87c518f
+0,         64,         64,        1,  8294400, 0xa87c518f
+0,         65,         65,        1,  8294400, 0xa87c518f
+0,         66,         66,        1,  8294400, 0xa87c518f
+0,         67,         67,        1,  8294400, 0xa87c518f
+0,         68,         68,        1,  8294400, 0xa87c518f
+0,         69,         69,        1,  8294400, 0xa87c518f
+0,         70,         70,        1,  8294400, 0xa87c518f
+0,         71,         71,        1,  8294400, 0xa87c518f
+0,         72,         72,        1,  8294400, 0xa87c518f
+0,         73,         73,        1,  8294400, 0xa87c518f
+0,         74,         74,        1,  8294400, 0xa87c518f
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v3 22/26] avutil/ass_split: Add parsing of hard-space tags (\h)
  2022-01-20  3:25   ` [FFmpeg-devel] [PATCH v3 00/26] Subtitle Filtering 2022 ffmpegagent
                       ` (20 preceding siblings ...)
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 21/26] fftools/ffmpeg: Introduce subtitle filtering and new frame-based subtitle encoding ffmpegagent
@ 2022-01-20  3:25     ` ffmpegagent
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 23/26] avcodec/webvttenc: convert hard-space tags to &nbsp; ffmpegagent
                       ` (4 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  3:25 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

The \h tag in ASS/SSA is indicating a non-breaking space. See
https://github.com/Aegisub/aegisite/blob/master/source/docs/3.2/
ASS_Tags.html.md

The ass_split implementation is used by almost all text subtitle
encoders and it didn't handle this tag. Interestingly, several
tests are testing for \h parsing and had incorrect reference data
for those tests.

The \h tag is specific to ASS and doesn't have any meaning outside
of ASS.
Still, the reference data for ttmlenc, textenc and webvttenc were
full of \h tags even though this tag doesn't have a meaning there.

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavutil/ass_split.c            |  7 +++++++
 tests/ref/fate/.gitattributes    |  3 +++
 tests/ref/fate/mov-mp4-ttml-dfxp |  8 ++++----
 tests/ref/fate/mov-mp4-ttml-stpp |  8 ++++----
 tests/ref/fate/sub-textenc       | 10 +++++-----
 tests/ref/fate/sub-ttmlenc       |  8 ++++----
 tests/ref/fate/sub-webvttenc     | 10 +++++-----
 7 files changed, 32 insertions(+), 22 deletions(-)
 create mode 100644 tests/ref/fate/.gitattributes

diff --git a/libavutil/ass_split.c b/libavutil/ass_split.c
index c5963351fc..30512dfc74 100644
--- a/libavutil/ass_split.c
+++ b/libavutil/ass_split.c
@@ -484,6 +484,7 @@ int avpriv_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *pr
     while (buf && *buf) {
         if (text && callbacks->text &&
             (sscanf(buf, "\\%1[nN]", new_line) == 1 ||
+             sscanf(buf, "\\%1[hH]", new_line) == 1 ||
              !strncmp(buf, "{\\", 2))) {
             callbacks->text(priv, text, text_len);
             text = NULL;
@@ -492,6 +493,12 @@ int avpriv_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *pr
             if (callbacks->new_line)
                 callbacks->new_line(priv, new_line[0] == 'N');
             buf += 2;
+        } else if (sscanf(buf, "\\%1[hH]", new_line) == 1) {
+            if (callbacks->hard_space)
+                callbacks->hard_space(priv);
+            else if (callbacks->text)
+                callbacks->text(priv, " ", 1);
+            buf += 2;
         } else if (!strncmp(buf, "{\\", 2)) {
             buf++;
             while (*buf == '\\') {
diff --git a/tests/ref/fate/.gitattributes b/tests/ref/fate/.gitattributes
new file mode 100644
index 0000000000..19be64d085
--- /dev/null
+++ b/tests/ref/fate/.gitattributes
@@ -0,0 +1,3 @@
+sub-textenc -diff
+sub-ttmlenc -diff
+sub-webvttenc -diff
diff --git a/tests/ref/fate/mov-mp4-ttml-dfxp b/tests/ref/fate/mov-mp4-ttml-dfxp
index e24b5d618b..e565ffa1f6 100644
--- a/tests/ref/fate/mov-mp4-ttml-dfxp
+++ b/tests/ref/fate/mov-mp4-ttml-dfxp
@@ -1,9 +1,9 @@
-2e7e01c821c111466e7a2844826b7f6d *tests/data/fate/mov-mp4-ttml-dfxp.mp4
-8519 tests/data/fate/mov-mp4-ttml-dfxp.mp4
+658884e1b789e75c454b25bdf71283c9 *tests/data/fate/mov-mp4-ttml-dfxp.mp4
+8486 tests/data/fate/mov-mp4-ttml-dfxp.mp4
 #tb 0: 1/1000
 #media_type 0: data
 #codec_id 0: none
-0,          0,          0,    68500,     7866, 0x456c36b7
+0,          0,          0,    68500,     7833, 0x31b22193
 {
     "packets": [
         {
@@ -15,7 +15,7 @@
             "dts_time": "0.000000",
             "duration": 68500,
             "duration_time": "68.500000",
-            "size": "7866",
+            "size": "7833",
             "pos": "44",
             "flags": "K_"
         }
diff --git a/tests/ref/fate/mov-mp4-ttml-stpp b/tests/ref/fate/mov-mp4-ttml-stpp
index 77bd23b7bf..f25b5b2d28 100644
--- a/tests/ref/fate/mov-mp4-ttml-stpp
+++ b/tests/ref/fate/mov-mp4-ttml-stpp
@@ -1,9 +1,9 @@
-cbd2c7ff864a663b0d893deac5a0caec *tests/data/fate/mov-mp4-ttml-stpp.mp4
-8547 tests/data/fate/mov-mp4-ttml-stpp.mp4
+c9570de0ccebc858b0c662a7e449582c *tests/data/fate/mov-mp4-ttml-stpp.mp4
+8514 tests/data/fate/mov-mp4-ttml-stpp.mp4
 #tb 0: 1/1000
 #media_type 0: data
 #codec_id 0: none
-0,          0,          0,    68500,     7866, 0x456c36b7
+0,          0,          0,    68500,     7833, 0x31b22193
 {
     "packets": [
         {
@@ -15,7 +15,7 @@ cbd2c7ff864a663b0d893deac5a0caec *tests/data/fate/mov-mp4-ttml-stpp.mp4
             "dts_time": "0.000000",
             "duration": 68500,
             "duration_time": "68.500000",
-            "size": "7866",
+            "size": "7833",
             "pos": "44",
             "flags": "K_"
         }
diff --git a/tests/ref/fate/sub-textenc b/tests/ref/fate/sub-textenc
index 3ea56b38f0..910ca3d6e3 100644
--- a/tests/ref/fate/sub-textenc
+++ b/tests/ref/fate/sub-textenc
@@ -160,18 +160,18 @@ but show this: {normal text}
 \ N is a forced line break
 \ h is a hard space
 Normal spaces at the start and at the end of the line are trimmed while hard spaces are not trimmed.
-The\hline\hwill\hnever\hbreak\hautomatically\hright\hbefore\hor\hafter\ha\hhard\hspace.\h:-D
+The line will never break automatically right before or after a hard space. :-D
 
 31
 00:00:54,501 --> 00:00:56,500
 
-\h\h\h\h\hA (05 hard spaces followed by a letter)
+     A (05 hard spaces followed by a letter)
 A (Normal  spaces followed by a letter)
 A (No hard spaces followed by a letter)
 
 32
 00:00:56,501 --> 00:00:58,500
-\h\h\h\h\hA (05 hard spaces followed by a letter)
+     A (05 hard spaces followed by a letter)
 A (Normal  spaces followed by a letter)
 A (No hard spaces followed by a letter)
 Show this: \TEST and this: \-)
@@ -179,10 +179,10 @@ Show this: \TEST and this: \-)
 33
 00:00:58,501 --> 00:01:00,500
 
-A letter followed by 05 hard spaces: A\h\h\h\h\h
+A letter followed by 05 hard spaces: A     
 A letter followed by normal  spaces: A
 A letter followed by no hard spaces: A
-05 hard  spaces between letters: A\h\h\h\h\hA
+05 hard  spaces between letters: A     A
 5 normal spaces between letters: A     A
 
 ^--Forced line break
diff --git a/tests/ref/fate/sub-ttmlenc b/tests/ref/fate/sub-ttmlenc
index 4df8f8796f..aea09bb31e 100644
--- a/tests/ref/fate/sub-ttmlenc
+++ b/tests/ref/fate/sub-ttmlenc
@@ -109,16 +109,16 @@
         end="00:00:54.500"><span region="Default">Hide these tags:<br/>also hide these tags:<br/>but show this: {normal text}</span></p>
       <p
         begin="00:00:54.501"
-        end="00:01:00.500"><span region="Default"><br/>\ N is a forced line break<br/>\ h is a hard space<br/>Normal spaces at the start and at the end of the line are trimmed while hard spaces are not trimmed.<br/>The\hline\hwill\hnever\hbreak\hautomatically\hright\hbefore\hor\hafter\ha\hhard\hspace.\h:-D</span></p>
+        end="00:01:00.500"><span region="Default"><br/>\ N is a forced line break<br/>\ h is a hard space<br/>Normal spaces at the start and at the end of the line are trimmed while hard spaces are not trimmed.<br/>The line will never break automatically right before or after a hard space. :-D</span></p>
       <p
         begin="00:00:54.501"
-        end="00:00:56.500"><span region="Default"><br/>\h\h\h\h\hA (05 hard spaces followed by a letter)<br/>A (Normal  spaces followed by a letter)<br/>A (No hard spaces followed by a letter)</span></p>
+        end="00:00:56.500"><span region="Default"><br/>     A (05 hard spaces followed by a letter)<br/>A (Normal  spaces followed by a letter)<br/>A (No hard spaces followed by a letter)</span></p>
       <p
         begin="00:00:56.501"
-        end="00:00:58.500"><span region="Default">\h\h\h\h\hA (05 hard spaces followed by a letter)<br/>A (Normal  spaces followed by a letter)<br/>A (No hard spaces followed by a letter)<br/>Show this: \TEST and this: \-)</span></p>
+        end="00:00:58.500"><span region="Default">     A (05 hard spaces followed by a letter)<br/>A (Normal  spaces followed by a letter)<br/>A (No hard spaces followed by a letter)<br/>Show this: \TEST and this: \-)</span></p>
       <p
         begin="00:00:58.501"
-        end="00:01:00.500"><span region="Default"><br/>A letter followed by 05 hard spaces: A\h\h\h\h\h<br/>A letter followed by normal  spaces: A<br/>A letter followed by no hard spaces: A<br/>05 hard  spaces between letters: A\h\h\h\h\hA<br/>5 normal spaces between letters: A     A<br/><br/>^--Forced line break</span></p>
+        end="00:01:00.500"><span region="Default"><br/>A letter followed by 05 hard spaces: A     <br/>A letter followed by normal  spaces: A<br/>A letter followed by no hard spaces: A<br/>05 hard  spaces between letters: A     A<br/>5 normal spaces between letters: A     A<br/><br/>^--Forced line break</span></p>
       <p
         begin="00:01:00.501"
         end="00:01:02.500"><span region="Default">Both line should be strikethrough,<br/>yes.<br/>Correctly closed tags<br/>should be hidden.</span></p>
diff --git a/tests/ref/fate/sub-webvttenc b/tests/ref/fate/sub-webvttenc
index 45ae0b6131..f4172dcc84 100644
--- a/tests/ref/fate/sub-webvttenc
+++ b/tests/ref/fate/sub-webvttenc
@@ -132,26 +132,26 @@ but show this: {normal text}
 \ N is a forced line break
 \ h is a hard space
 Normal spaces at the start and at the end of the line are trimmed while hard spaces are not trimmed.
-The\hline\hwill\hnever\hbreak\hautomatically\hright\hbefore\hor\hafter\ha\hhard\hspace.\h:-D
+The line will never break automatically right before or after a hard space. :-D
 
 00:54.501 --> 00:56.500
 
-\h\h\h\h\hA (05 hard spaces followed by a letter)
+     A (05 hard spaces followed by a letter)
 A (Normal  spaces followed by a letter)
 A (No hard spaces followed by a letter)
 
 00:56.501 --> 00:58.500
-\h\h\h\h\hA (05 hard spaces followed by a letter)
+     A (05 hard spaces followed by a letter)
 A (Normal  spaces followed by a letter)
 A (No hard spaces followed by a letter)
 Show this: \TEST and this: \-)
 
 00:58.501 --> 01:00.500
 
-A letter followed by 05 hard spaces: A\h\h\h\h\h
+A letter followed by 05 hard spaces: A     
 A letter followed by normal  spaces: A
 A letter followed by no hard spaces: A
-05 hard  spaces between letters: A\h\h\h\h\hA
+05 hard  spaces between letters: A     A
 5 normal spaces between letters: A     A
 
 ^--Forced line break
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v3 23/26] avcodec/webvttenc: convert hard-space tags to &nbsp;
  2022-01-20  3:25   ` [FFmpeg-devel] [PATCH v3 00/26] Subtitle Filtering 2022 ffmpegagent
                       ` (21 preceding siblings ...)
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 22/26] avutil/ass_split: Add parsing of hard-space tags (\h) ffmpegagent
@ 2022-01-20  3:25     ` ffmpegagent
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 24/26] doc/APIchanges: update for subtitle filtering changes ffmpegagent
                       ` (3 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  3:25 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/webvttenc.c       |  6 ++++++
 tests/ref/fate/sub-webvttenc | 10 +++++-----
 2 files changed, 11 insertions(+), 5 deletions(-)

diff --git a/libavcodec/webvttenc.c b/libavcodec/webvttenc.c
index c0436f5739..48945dcb8e 100644
--- a/libavcodec/webvttenc.c
+++ b/libavcodec/webvttenc.c
@@ -123,6 +123,11 @@ static void webvtt_new_line_cb(void *priv, int forced)
     webvtt_print(priv, "\n");
 }
 
+static void webvtt_hard_space_cb(void *priv)
+{
+    webvtt_print(priv, "&nbsp;");
+}
+
 static void webvtt_style_cb(void *priv, char style, int close)
 {
     if (style == 's') // strikethrough unsupported
@@ -147,6 +152,7 @@ static void webvtt_end_cb(void *priv)
 static const ASSCodesCallbacks webvtt_callbacks = {
     .text             = webvtt_text_cb,
     .new_line         = webvtt_new_line_cb,
+    .hard_space       = webvtt_hard_space_cb,
     .style            = webvtt_style_cb,
     .color            = NULL,
     .font_name        = NULL,
diff --git a/tests/ref/fate/sub-webvttenc b/tests/ref/fate/sub-webvttenc
index f4172dcc84..ee9de2859e 100644
--- a/tests/ref/fate/sub-webvttenc
+++ b/tests/ref/fate/sub-webvttenc
@@ -132,26 +132,26 @@ but show this: {normal text}
 \ N is a forced line break
 \ h is a hard space
 Normal spaces at the start and at the end of the line are trimmed while hard spaces are not trimmed.
-The line will never break automatically right before or after a hard space. :-D
+The&nbsp;line&nbsp;will&nbsp;never&nbsp;break&nbsp;automatically&nbsp;right&nbsp;before&nbsp;or&nbsp;after&nbsp;a&nbsp;hard&nbsp;space.&nbsp;:-D
 
 00:54.501 --> 00:56.500
 
-     A (05 hard spaces followed by a letter)
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;A (05 hard spaces followed by a letter)
 A (Normal  spaces followed by a letter)
 A (No hard spaces followed by a letter)
 
 00:56.501 --> 00:58.500
-     A (05 hard spaces followed by a letter)
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;A (05 hard spaces followed by a letter)
 A (Normal  spaces followed by a letter)
 A (No hard spaces followed by a letter)
 Show this: \TEST and this: \-)
 
 00:58.501 --> 01:00.500
 
-A letter followed by 05 hard spaces: A     
+A letter followed by 05 hard spaces: A&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 A letter followed by normal  spaces: A
 A letter followed by no hard spaces: A
-05 hard  spaces between letters: A     A
+05 hard  spaces between letters: A&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;A
 5 normal spaces between letters: A     A
 
 ^--Forced line break
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v3 24/26] doc/APIchanges: update for subtitle filtering changes
  2022-01-20  3:25   ` [FFmpeg-devel] [PATCH v3 00/26] Subtitle Filtering 2022 ffmpegagent
                       ` (22 preceding siblings ...)
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 23/26] avcodec/webvttenc: convert hard-space tags to &nbsp; ffmpegagent
@ 2022-01-20  3:25     ` ffmpegagent
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 25/26] avcodec/webvttenc: Don't encode drawing codes and empty lines ffmpegagent
                       ` (2 subsequent siblings)
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  3:25 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 doc/APIchanges       | 24 ++++++++++++++++++++++++
 libavcodec/version.h |  2 +-
 libavutil/version.h  |  2 +-
 3 files changed, 26 insertions(+), 2 deletions(-)

diff --git a/doc/APIchanges b/doc/APIchanges
index 8df0364e4c..c8238fb008 100644
--- a/doc/APIchanges
+++ b/doc/APIchanges
@@ -14,6 +14,30 @@ libavutil:     2021-04-27
 
 API changes, most recent first:
 
+2021-12-05 - xxxxxxxxxx - lavc 59.15.100 - avcodec.h
+  Deprecate avcodec_encode_subtitle(), use regular encode api now
+
+2021-12-05 - xxxxxxxxxx - lavc 59.15.100 - codec_desc.h
+  Add avcodec_descriptor_get_subtitle_format()
+
+2021-12-05 - xxxxxxxxxx - lavc 59.15.100 - avcodec.h
+  Deprecate avsubtitle_free()
+  Deprecate avcodec_decode_subtitle2(), use regular decode api now
+
+2021-12-05 - xxxxxxxxxx - lavu 57.11.100 - frame.h
+  Add AVMediaType field to AVFrame
+  Add Fields for carrying subtitle data to AVFrame
+  (subtitle_areas, subtitle_header, subtitle_pts, start/end time, etc.)
+  Add av_frame_get_buffer2() and deprecate av_frame_get_buffer()
+
+2021-12-05 - xxxxxxxxxx - lavu 57.11.100 - subfmt.h
+  Add struct AVSubtitleArea (replaces AVSubtitle)
+  Add av_get_subtitle_fmt_name() and av_get_subtitle_fmt()
+
+2021-12-05 - xxxxxxxxxx - lavu 57.11.100 - subfmt.h
+  Add enum AVSubtitleType (moved from lavc), add new values, deprecate existing
+
+2021-11-xx - xxxxxxxxxx - lavfi 8.19.100 - avfilter.h
 2022-01-04 - 78dc21b123e - lavu 57.16.100 - frame.h
   Add AV_FRAME_DATA_DOVI_METADATA.
 
diff --git a/libavcodec/version.h b/libavcodec/version.h
index a46fb05f1a..b5867ad041 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -28,7 +28,7 @@
 #include "libavutil/version.h"
 
 #define LIBAVCODEC_VERSION_MAJOR  59
-#define LIBAVCODEC_VERSION_MINOR  20
+#define LIBAVCODEC_VERSION_MINOR  21
 #define LIBAVCODEC_VERSION_MICRO 100
 
 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
diff --git a/libavutil/version.h b/libavutil/version.h
index 5bf48f6304..168e24f410 100644
--- a/libavutil/version.h
+++ b/libavutil/version.h
@@ -79,7 +79,7 @@
  */
 
 #define LIBAVUTIL_VERSION_MAJOR  57
-#define LIBAVUTIL_VERSION_MINOR  18
+#define LIBAVUTIL_VERSION_MINOR  19
 #define LIBAVUTIL_VERSION_MICRO 100
 
 #define LIBAVUTIL_VERSION_INT   AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v3 25/26] avcodec/webvttenc: Don't encode drawing codes and empty lines
  2022-01-20  3:25   ` [FFmpeg-devel] [PATCH v3 00/26] Subtitle Filtering 2022 ffmpegagent
                       ` (23 preceding siblings ...)
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 24/26] doc/APIchanges: update for subtitle filtering changes ffmpegagent
@ 2022-01-20  3:25     ` ffmpegagent
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 26/26] avcodec/dvbsubdec: Fix conditions for fallback to default resolution ffmpegagent
  2022-05-28 13:25     ` [FFmpeg-devel] [PATCH v4 00/23] Subtitle Filtering 2022 ffmpegagent
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  3:25 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/webvttenc.c | 31 +++++++++++++++++++++++++++----
 1 file changed, 27 insertions(+), 4 deletions(-)

diff --git a/libavcodec/webvttenc.c b/libavcodec/webvttenc.c
index 48945dcb8e..62c4aa7ffd 100644
--- a/libavcodec/webvttenc.c
+++ b/libavcodec/webvttenc.c
@@ -39,6 +39,8 @@ typedef struct {
     int count;
     char stack[WEBVTT_STACK_SIZE];
     int stack_ptr;
+    int has_text;
+    int drawing_scale;
 } WebVTTContext;
 
 #ifdef __GNUC__
@@ -115,17 +117,24 @@ static void webvtt_style_apply(WebVTTContext *s, const char *style)
 static void webvtt_text_cb(void *priv, const char *text, int len)
 {
     WebVTTContext *s = priv;
-    av_bprint_append_data(&s->buffer, text, len);
+    if (!s->drawing_scale) {
+        av_bprint_append_data(&s->buffer, text, len);
+        s->has_text = 1;
+    }
 }
 
 static void webvtt_new_line_cb(void *priv, int forced)
 {
-    webvtt_print(priv, "\n");
+    WebVTTContext *s = priv;
+    if (!s->drawing_scale)
+        webvtt_print(priv, "\n");
 }
 
 static void webvtt_hard_space_cb(void *priv)
 {
-    webvtt_print(priv, "&nbsp;");
+    WebVTTContext *s = priv;
+    if (!s->drawing_scale)
+        webvtt_print(priv, "&nbsp;");
 }
 
 static void webvtt_style_cb(void *priv, char style, int close)
@@ -149,6 +158,12 @@ static void webvtt_end_cb(void *priv)
     webvtt_stack_push_pop(priv, 0, 1);
 }
 
+static void dialog_drawing_mode_cb(void *priv, int scale)
+{
+    WebVTTContext *s = priv;
+    s->drawing_scale = scale;
+}
+
 static const ASSCodesCallbacks webvtt_callbacks = {
     .text             = webvtt_text_cb,
     .new_line         = webvtt_new_line_cb,
@@ -161,6 +176,7 @@ static const ASSCodesCallbacks webvtt_callbacks = {
     .cancel_overrides = webvtt_cancel_overrides_cb,
     .move             = NULL,
     .end              = webvtt_end_cb,
+    .drawing_mode     = dialog_drawing_mode_cb,
 };
 
 static void ensure_ass_context(WebVTTContext* s, const AVFrame* frame)
@@ -211,16 +227,23 @@ static int webvtt_encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
         }
 
         if (ass) {
+            const unsigned saved_len = s->buffer.len;
 
-            if (i > 0)
+            if (i > 0 && s->buffer.len > 0)
                 webvtt_new_line_cb(s, 0);
 
+            s->drawing_scale = 0;
+            s->has_text = 0;
+
             dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
             if (!dialog)
                 return AVERROR(ENOMEM);
             webvtt_style_apply(s, dialog->style);
             avpriv_ass_split_override_codes(&webvtt_callbacks, s, dialog->text);
             avpriv_ass_free_dialog(&dialog);
+
+            if (!s->has_text)
+                s->buffer.len = saved_len;
         }
     }
 
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v3 26/26] avcodec/dvbsubdec: Fix conditions for fallback to default resolution
  2022-01-20  3:25   ` [FFmpeg-devel] [PATCH v3 00/26] Subtitle Filtering 2022 ffmpegagent
                       ` (24 preceding siblings ...)
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 25/26] avcodec/webvttenc: Don't encode drawing codes and empty lines ffmpegagent
@ 2022-01-20  3:25     ` ffmpegagent
  2022-05-28 13:25     ` [FFmpeg-devel] [PATCH v4 00/23] Subtitle Filtering 2022 ffmpegagent
  26 siblings, 0 replies; 217+ messages in thread
From: ffmpegagent @ 2022-01-20  3:25 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, softworkz, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

The previous code expected a segment of type CLUT definition to exist
in order to accept a set of segments to be complete.
This was an incorrect assumption as the presence of a CLUT segment
is not mandatory.
(version 1.6.1 of the spec is probably a bit more clear about this
than earlier versions: https://www.etsi.org/deliver/etsi_en/
300700_300799/300743/01.06.01_20/en_300743v010601a.pdf)

The flawed condition prevented proper fallback to using the default
resolution for the decoding context.

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/dvbsubdec.c | 51 +++++++++++++++++++++++++-----------------
 1 file changed, 30 insertions(+), 21 deletions(-)

diff --git a/libavcodec/dvbsubdec.c b/libavcodec/dvbsubdec.c
index 0d64c6e71c..3a6259101c 100644
--- a/libavcodec/dvbsubdec.c
+++ b/libavcodec/dvbsubdec.c
@@ -33,7 +33,7 @@
 #define DVBSUB_CLUT_SEGMENT     0x12
 #define DVBSUB_OBJECT_SEGMENT   0x13
 #define DVBSUB_DISPLAYDEFINITION_SEGMENT 0x14
-#define DVBSUB_DISPLAY_SEGMENT  0x80
+#define DVBSUB_END_DISPLAY_SEGMENT  0x80
 
 #define cm (ff_crop_tab + MAX_NEG_CROP)
 
@@ -1620,8 +1620,12 @@ static int dvbsub_decode(AVCodecContext *avctx,
     int segment_length;
     int i;
     int ret = 0;
-    int got_segment = 0;
-    int got_dds = 0;
+    //int got_segment = 0;
+    int got_page = 0;
+    int got_region = 0;
+    int got_object = 0;
+    int got_end_display = 0;
+    int got_displaydef = 0;
 
     ff_dlog(avctx, "DVB sub packet:\n");
 
@@ -1666,34 +1670,28 @@ static int dvbsub_decode(AVCodecContext *avctx,
             switch (segment_type) {
             case DVBSUB_PAGE_SEGMENT:
                 ret = dvbsub_parse_page_segment(avctx, p, segment_length, sub, got_sub_ptr);
-                got_segment |= 1;
+                got_page = 1;
                 break;
             case DVBSUB_REGION_SEGMENT:
                 ret = dvbsub_parse_region_segment(avctx, p, segment_length);
-                got_segment |= 2;
+                got_region = 1;
                 break;
             case DVBSUB_CLUT_SEGMENT:
                 ret = dvbsub_parse_clut_segment(avctx, p, segment_length);
                 if (ret < 0) goto end;
-                got_segment |= 4;
                 break;
             case DVBSUB_OBJECT_SEGMENT:
                 ret = dvbsub_parse_object_segment(avctx, p, segment_length);
-                got_segment |= 8;
+                got_object = 1;
                 break;
             case DVBSUB_DISPLAYDEFINITION_SEGMENT:
                 ret = dvbsub_parse_display_definition_segment(avctx, p,
                                                               segment_length);
-                got_dds = 1;
+                got_displaydef = 1;
                 break;
-            case DVBSUB_DISPLAY_SEGMENT:
+            case DVBSUB_END_DISPLAY_SEGMENT:
                 ret = dvbsub_display_end_segment(avctx, p, segment_length, sub, got_sub_ptr);
-                if (got_segment == 15 && !got_dds && !avctx->width && !avctx->height) {
-                    // Default from ETSI EN 300 743 V1.3.1 (7.2.1)
-                    avctx->width  = 720;
-                    avctx->height = 576;
-                }
-                got_segment |= 16;
+                got_end_display = 1;
                 break;
             default:
                 ff_dlog(avctx, "Subtitling segment type 0x%x, page id %d, length %d\n",
@@ -1706,13 +1704,24 @@ static int dvbsub_decode(AVCodecContext *avctx,
 
         p += segment_length;
     }
-    // Some streams do not send a display segment but if we have all the other
-    // segments then we need no further data.
-    if (got_segment == 15) {
-        av_log(avctx, AV_LOG_DEBUG, "Missing display_end_segment, emulating\n");
-        dvbsub_display_end_segment(avctx, p, 0, sub, got_sub_ptr);
-    }
 
+    // Even though not mandated by the spec, we're imposing a minimum requirement
+    // for a useful packet to have at least one page, region and object segment.
+    if (got_page && got_region && got_object && got_end_display) {
+
+        if (!got_displaydef && !avctx->width && !avctx->height) {
+            // Default from ETSI EN 300 743 V1.3.1 (7.2.1)
+            avctx->width  = 720;
+            avctx->height = 576;
+        }
+
+        // Some streams do not send an end-of-display segment but if we have all the other
+        // segments then we need no further data.
+        if (!got_end_display) {
+            av_log(avctx, AV_LOG_DEBUG, "Missing display_end_segment, emulating\n");
+            dvbsub_display_end_segment(avctx, p, 0, sub, got_sub_ptr);
+        }
+    }
 end:
     if (ret < 0) {
         return ret;
-- 
ffmpeg-codebot
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v3 21/26] fftools/ffmpeg: Introduce subtitle filtering and new frame-based subtitle encoding
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 21/26] fftools/ffmpeg: Introduce subtitle filtering and new frame-based subtitle encoding ffmpegagent
@ 2022-01-20 10:06       ` Michael Niedermayer
  2022-01-20 15:17         ` Soft Works
  0 siblings, 1 reply; 217+ messages in thread
From: Michael Niedermayer @ 2022-01-20 10:06 UTC (permalink / raw)
  To: FFmpeg development discussions and patches


[-- Attachment #1.1: Type: text/plain, Size: 3420 bytes --]

On Thu, Jan 20, 2022 at 03:25:29AM +0000, ffmpegagent wrote:
> From: softworkz <softworkz@hotmail.com>
> 
> This commit actually enables subtitle filtering in ffmpeg by
> sending and receiving subtitle frames to and from a filtergraph.
> 
> The heartbeat functionality from the previous sub2video implementation
> is removed and now provided by the 'subfeed' filter.
> The other part of sub2video functionality is retained by
> auto-insertion of the new graphicsub2video filter.
> 
> Justification for changed test refs:
> 
> - sub2video
>   The new results are identical excepting the last frame which
>   is due to the implementation changes
> 
> - sub2video_basic
>   The previous results had some incorrect output because multiple
>   frames had the same dts
>   The non-empty content frames are visually identical, the different
>   CRC is due to the different blending algorithm that is being used.
> 
> - sub2video_time_limited
>   The third frame in the previous ref was a repetition, which doesn't
>   happen anymore with the new subtitle filtering.
> 
> - sub-dvb
>   Running ffprobe -show_frames on the source file shows that there
>   are 7 subtitle frames with 0 rects in the source at the start
>   and 2 at the end. This translates to the 14 and 4 additional
>   entries in the new test results.
> 
> - filter-overlay-dvdsub-2397
>   Overlay results have slightly different CRCs due to different
>   blending implementation
> 
> Signed-off-by: softworkz <softworkz@hotmail.com>
> ---
>  fftools/ffmpeg.c                          |  501 ++++-----
>  fftools/ffmpeg.h                          |   13 +-
>  fftools/ffmpeg_filter.c                   |  235 ++--
>  fftools/ffmpeg_hw.c                       |    2 +-
>  fftools/ffmpeg_opt.c                      |    3 +-
>  tests/ref/fate/filter-overlay-dvdsub-2397 |  182 +--
>  tests/ref/fate/sub-dvb                    |  162 +--
>  tests/ref/fate/sub2video                  | 1091 +++++++++++++++++-
>  tests/ref/fate/sub2video_basic            | 1239 +++++++++++++++++++--
>  tests/ref/fate/sub2video_time_limited     |   78 +-
>  10 files changed, 2837 insertions(+), 669 deletions(-)

segfaults

Stream mapping:
  Stream #0:2 -> #0:0 (ass (ssa) -> subrip (srt))
Press [q] to stop, [?] for help
==4485== Invalid read of size 8
==4485==    at 0x3052F7: transcode (in /home/michael/ffmpeg-git/ffmpeg/ffmpeg_g)
==4485==    by 0x2DDCCB: main (in /home/michael/ffmpeg-git/ffmpeg/ffmpeg_g)
==4485==  Address 0x10 is not stack'd, malloc'd or (recently) free'd
==4485== 
==4485== 
==4485== Process terminating with default action of signal 11 (SIGSEGV)
==4485==  Access not within mapped region at address 0x10
==4485==    at 0x3052F7: transcode (in /home/michael/ffmpeg-git/ffmpeg/ffmpeg_g)
==4485==    by 0x2DDCCB: main (in /home/michael/ffmpeg-git/ffmpeg/ffmpeg_g)
==4485==  If you believe this happened as a result of a stack
==4485==  overflow in your program's main thread (unlikely but
==4485==  possible), you can try to increase the size of the
==4485==  main thread stack using the --main-stacksize= flag.
==4485==  The main thread stack size used in this run was 8388608.


will send you the sample privatly

[...]
-- 
Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB

No great genius has ever existed without some touch of madness. -- Aristotle

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 195 bytes --]

[-- Attachment #2: Type: text/plain, Size: 251 bytes --]

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v3 21/26] fftools/ffmpeg: Introduce subtitle filtering and new frame-based subtitle encoding
  2022-01-20 10:06       ` Michael Niedermayer
@ 2022-01-20 15:17         ` Soft Works
  2022-01-20 18:00           ` Andriy Gelman
  0 siblings, 1 reply; 217+ messages in thread
From: Soft Works @ 2022-01-20 15:17 UTC (permalink / raw)
  To: FFmpeg development discussions and patches



> -----Original Message-----
> From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of Michael
> Niedermayer
> Sent: Thursday, January 20, 2022 11:07 AM
> To: FFmpeg development discussions and patches <ffmpeg-devel@ffmpeg.org>
> Subject: Re: [FFmpeg-devel] [PATCH v3 21/26] fftools/ffmpeg: Introduce
> subtitle filtering and new frame-based subtitle encoding
> 
> On Thu, Jan 20, 2022 at 03:25:29AM +0000, ffmpegagent wrote:
> > From: softworkz <softworkz@hotmail.com>


[..]

> > ---
> >  fftools/ffmpeg.c                          |  501 ++++-----
> >  fftools/ffmpeg.h                          |   13 +-
> >  fftools/ffmpeg_filter.c                   |  235 ++--
> >  fftools/ffmpeg_hw.c                       |    2 +-
> >  fftools/ffmpeg_opt.c                      |    3 +-
> >  tests/ref/fate/filter-overlay-dvdsub-2397 |  182 +--
> >  tests/ref/fate/sub-dvb                    |  162 +--
> >  tests/ref/fate/sub2video                  | 1091 +++++++++++++++++-
> >  tests/ref/fate/sub2video_basic            | 1239 +++++++++++++++++++--
> >  tests/ref/fate/sub2video_time_limited     |   78 +-
> >  10 files changed, 2837 insertions(+), 669 deletions(-)
> 
> segfaults
> 
> Stream mapping:
>   Stream #0:2 -> #0:0 (ass (ssa) -> subrip (srt))
> Press [q] to stop, [?] for help
> ==4485== Invalid read of size 8
> ==4485==    at 0x3052F7: transcode (in /home/michael/ffmpeg-
> git/ffmpeg/ffmpeg_g)
> ==4485==    by 0x2DDCCB: main (in /home/michael/ffmpeg-git/ffmpeg/ffmpeg_g)
> ==4485==  Address 0x10 is not stack'd, malloc'd or (recently) free'd
> ==4485==
> ==4485==
> ==4485== Process terminating with default action of signal 11 (SIGSEGV)
> ==4485==  Access not within mapped region at address 0x10
> ==4485==    at 0x3052F7: transcode (in /home/michael/ffmpeg-
> git/ffmpeg/ffmpeg_g)
> ==4485==    by 0x2DDCCB: main (in /home/michael/ffmpeg-git/ffmpeg/ffmpeg_g)
> ==4485==  If you believe this happened as a result of a stack
> ==4485==  overflow in your program's main thread (unlikely but
> ==4485==  possible), you can try to increase the size of the
> ==4485==  main thread stack using the --main-stacksize= flag.
> ==4485==  The main thread stack size used in this run was 8388608.
> 
> 
> will send you the sample privatly


Great, thanks!

Patches 19,20,21,22 and 23 do not appear on Patchwork.

Do you have an idea why?

sw
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v3 21/26] fftools/ffmpeg: Introduce subtitle filtering and new frame-based subtitle encoding
  2022-01-20 15:17         ` Soft Works
@ 2022-01-20 18:00           ` Andriy Gelman
  2022-01-20 20:01             ` Soft Works
  0 siblings, 1 reply; 217+ messages in thread
From: Andriy Gelman @ 2022-01-20 18:00 UTC (permalink / raw)
  To: FFmpeg development discussions and patches

Hi,

On Thu, 20. Jan 15:17, Soft Works wrote:
> 

> 
> Patches 19,20,21,22 and 23 do not appear on Patchwork.
> 
> Do you have an idea why?

The full set is on patchwork now.

-- 
Andriy
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v3 21/26] fftools/ffmpeg: Introduce subtitle filtering and new frame-based subtitle encoding
  2022-01-20 18:00           ` Andriy Gelman
@ 2022-01-20 20:01             ` Soft Works
  0 siblings, 0 replies; 217+ messages in thread
From: Soft Works @ 2022-01-20 20:01 UTC (permalink / raw)
  To: FFmpeg development discussions and patches



> -----Original Message-----
> From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of Andriy
> Gelman
> Sent: Thursday, January 20, 2022 7:01 PM
> To: FFmpeg development discussions and patches <ffmpeg-devel@ffmpeg.org>
> Subject: Re: [FFmpeg-devel] [PATCH v3 21/26] fftools/ffmpeg: Introduce
> subtitle filtering and new frame-based subtitle encoding
> 
> Hi,
> 
> On Thu, 20. Jan 15:17, Soft Works wrote:
> >
> 
> >
> > Patches 19,20,21,22 and 23 do not appear on Patchwork.
> >
> > Do you have an idea why?
> 
> The full set is on patchwork now.

Thanks Andriy!
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v4 00/23] Subtitle Filtering 2022
  2022-01-20  3:25   ` [FFmpeg-devel] [PATCH v3 00/26] Subtitle Filtering 2022 ffmpegagent
                       ` (25 preceding siblings ...)
  2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 26/26] avcodec/dvbsubdec: Fix conditions for fallback to default resolution ffmpegagent
@ 2022-05-28 13:25     ` ffmpegagent
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 01/23] avcodec, avutil: Move enum AVSubtitleType to avutil, add new and deprecate old values softworkz
                         ` (23 more replies)
  26 siblings, 24 replies; 217+ messages in thread
From: ffmpegagent @ 2022-05-28 13:25 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt


Subtitle Filtering 2022
=======================

This is a substantial update to the earlier subtitle filtering patch series.
A primary goal has been to address others' concerns as much as possible on
one side and to provide more clarity and control over the way things are
working. Clarity is is specifically important to allow for a better
understanding of the need for a subtitle start pts value that can be
different from the frame's pts value. This is done by refactoring the
subtitle timing fields in AVFrame, adding a frame field to indicate repeated
subtitle frames, and finally the full removal of the heartbeat
functionality, replaced by a new 'subfeed' filter that provides different
modes for arbitrating subtitle frames in a filter graph. Finally, each
subtitle filter's documentation has been amended by a section describing the
filter's timeline behavior (in v3 update).

The update also includes major improvements to graphicsub2text and lots of
other details.

Versioning is restarting at v1 due to the new submission procedure.


v4 - Quality Improvements
=========================

 * finally an updated version
 * includes many improvements from internal testing
 * all FATE tests passed
 * all example commands from the docs verified to work
 * can't list all the detail changes..
 * I have left out the extra commits which can be handled separately, just
   in case somebody wonders why these are missing:
   * avcodec/webvttenc: Don't encode drawing codes and empty lines
   * avcodec/webvttenc: convert hard-space tags to  
   * avutil/ass_split: Add parsing of hard-space tags (\h)
   * avutil/ass_split: Treat all content in curly braces as hidden
   * avutil/ass_split: Fix ass parsing of style codes with comments


v3 - Rebase
===========

due to merge conflicts - apologies.


Changes in v2
=============

 * added .gitattributes file to enforce binary diffs for the test refs that
   cannot be applied when being sent via e-mail
 * perform filter graph re-init due to subtitle "frame size" change only
   when the size was unknown before and not set via -canvas_size
 * overlaytextsubs: Make sure to request frames on the subtitle input
 * avfilter/splitcc: Start parsing cc data on key frames only
 * avcodec/webvttenc: Don't encode ass drawing codes and empty lines
 * stripstyles: fix mem leak
 * gs2t: improve color detection
 * gs2t: empty frames must not be skipped
 * subfeed: fix name
 * textmod: preserve margins
 * added .gitattributes file to enforce binary diffs for the test refs that
   cannot be applied when being sent via e-mail
 * perform filter graph re-init due to subtitle "frame size" change only
   when the size was unknown before and not set via -canvas_size
 * avcodec/dvbsubdec: Fix conditions for fallback to default resolution
 * Made changes suggested by Andreas
 * Fixed failing command line reported by Michael

Changes from previous version v24:


AVFrame
=======

 * Removed sub_start_time The start time is now added to the subtitle
   start_pts during decoding The sub_end_time field is adjusted accordingly
 * Renamed sub_end_time to duration which it is effectively after removing
   the start_time
 * Added a sub-struct 'subtitle_timing' to av frame Contains subtitle_pts
   renamed to 'subtitle_timing.start_pts' and 'subtitle_timing.duration'
 * Change both fields to (fixed) time_base AV_TIMEBASE
 * add repeat_sub field provides a clear indication whether a subtitle frame
   is an actual subtitle event or a repeated subtitle frame in a filter
   graph


Heartbeat Removal
=================

 * completely removed the earlier heartbeat implementation
 * filtering arbitration is now implemented in a new filter: 'subfeed'
 * subfeed will be auto-inserted for compatiblity with sub2video command
   lines
 * the new behavior is not exactly identical to the earlier behavior, but it
   basically allows to achieve the same results
 * there's a small remainder, now named subtitle kickoff which serves to get
   things (in the filter graph) going right from the start


New 'subfeed' Filter
====================

 * a versatile filter for solving all kinds of problems with subtile frame
   flow in filter graphs
 * Can be inserted at any position in a graph
 * Auto-inserted for sub2video command lines (in repeat-mode)
 * Allows duration fixup delay input frames with unknown duration and infer
   duration from start of subsequent frame
 * Provides multiple modes of operation:
   * repeat mode (default) Queues input frames Outputs frames at a fixed
     (configurable) rate Either sends a matching input frame (repeatedly) or
     empty frames otherwise
   * scatter mode similar to repeat mode, but splits input frames by
     duration into small segments with same content
   * forward mode No fixed output rate Useful in combination with duration
     fixup or overlap fixup


ffmpeg Tool Changes
===================

 * delay subtitle output stream initialization (like for audio and video)
   This is needed for example when a format header depends on having
   received an initial frame to derive certain header values from
 * decoding: set subtitle frame size from decoding context
 * re-init graph when subtitle size changes
 * always insert subscale filter for sub2video command lines (to ensure
   correct scaling)


Subtitle Encoding
=================

 * ignore repeated frames for encoding based on repeat_sub field in AVFrame
 * support multi-area encoding for text subtitles Subtitle OCR can create
   multiple areas at different positions. Previously, the texts were always
   squashed into a single area ('subtitle rect'), which was not ideal.
   Multiple text areas are now generally supported:
   * ASS Encoder Changed to use the 'receive_packet' encoding API A single
     frame with multiple text areas will create multiple packets now
   * All other text subtitle encoders A newline is inserted between the text
     from multiple areas


graphicsub2text (OCR)
=====================

 * enhanced preprocessing
   * using elbg algorithm for color quantization
   * detection and removal of text outlines
   * map-based identification of colors per word (text, outline, background)
 * add option for duration fixup
 * add option to dump preprocessing bitmaps
 * Recognize formatting and apply as ASS inline styles
   * per word(!)
   * paragraph alignment
   * positioning
   * font names
   * font size
   * font style (italic, underline, bold)
   * text color, outline color


Other Filter Changes
====================

 * all: Make sure to forward all link properties (time base, frame rate, w,
   h) where appropriate
 * overlaytextsubs: request frames on the subtitle input
 * overlaytextsubs: disable read-order checking
 * overlaytextsubs: improve implementation of render_latest_only
 * overlaytextsubs: ensure equal in/out video formats
 * splitcc: derive framerate from realtime_latency
 * graphicsub2video: implement caching of converted frames
 * graphicsub2video: use 1x1 output frame size as long as subtitle size is
   unknown (0x0)

Plus a dozen of things I forgot..

softworkz (23):
  avcodec,avutil: Move enum AVSubtitleType to avutil, add new and
    deprecate old values
  avutil/frame: Prepare AVFrame for subtitle handling
  avcodec/subtitles: Introduce new frame-based subtitle decoding API
  avcodec/libzvbi: set subtitle type
  avfilter/subtitles: Update vf_subtitles to use new decoding api
  avcodec,avutil: Move ass helper functions to avutil as avpriv_ and
    extend ass dialog parsing
  avcodec/subtitles: Replace deprecated enum values
  fftools/play,probe: Adjust for subtitle changes
  avfilter/subtitles: Add subtitles.c for subtitle frame allocation
  avfilter/avfilter: Handle subtitle frames
  avfilter/avfilter: Fix hardcoded input index
  avfilter/sbuffer: Add sbuffersrc and sbuffersink filters
  avfilter/overlaygraphicsubs: Add overlaygraphicsubs and
    graphicsub2video filters
  avfilter/overlaytextsubs: Add overlaytextsubs and textsubs2video
    filters
  avfilter/textmod: Add textmod, censor and show_speaker filters
  avfilter/stripstyles: Add stripstyles filter
  avfilter/splitcc: Add splitcc filter for closed caption handling
  avfilter/graphicsub2text: Add new graphicsub2text filter (OCR)
  avfilter/subscale: Add filter for scaling and/or re-arranging
    graphical subtitles
  avfilter/subfeed: add subtitle feed filter
  avcodec/subtitles: Migrate subtitle encoders to frame-based API
  fftools/ffmpeg: Introduce subtitle filtering and new frame-based
    subtitle encoding
  avcodec/dvbsubdec: Fix conditions for fallback to default resolution

 configure                                     |    7 +-
 doc/filters.texi                              |  756 ++++++++++
 fftools/ffmpeg.c                              |  610 ++++----
 fftools/ffmpeg.h                              |   17 +-
 fftools/ffmpeg_filter.c                       |  243 +++-
 fftools/ffmpeg_hw.c                           |    2 +-
 fftools/ffmpeg_opt.c                          |    4 +-
 fftools/ffplay.c                              |  102 +-
 fftools/ffprobe.c                             |   47 +-
 libavcodec/Makefile                           |   56 +-
 libavcodec/ass.h                              |  151 +-
 libavcodec/assdec.c                           |    4 +-
 libavcodec/assenc.c                           |  191 ++-
 libavcodec/avcodec.c                          |    8 +
 libavcodec/avcodec.h                          |   34 +-
 libavcodec/ccaption_dec.c                     |   20 +-
 libavcodec/codec_internal.h                   |   12 -
 libavcodec/decode.c                           |   60 +-
 libavcodec/dvbsubdec.c                        |   53 +-
 libavcodec/dvbsubenc.c                        |   96 +-
 libavcodec/dvdsubdec.c                        |    2 +-
 libavcodec/dvdsubenc.c                        |  102 +-
 libavcodec/encode.c                           |   61 +-
 libavcodec/internal.h                         |   16 +
 libavcodec/jacosubdec.c                       |    2 +-
 libavcodec/libaribb24.c                       |    2 +-
 libavcodec/libzvbi-teletextdec.c              |   17 +-
 libavcodec/microdvddec.c                      |    7 +-
 libavcodec/movtextdec.c                       |    3 +-
 libavcodec/movtextenc.c                       |  126 +-
 libavcodec/mpl2dec.c                          |    2 +-
 libavcodec/pgssubdec.c                        |    2 +-
 libavcodec/realtextdec.c                      |    2 +-
 libavcodec/samidec.c                          |    2 +-
 libavcodec/srtdec.c                           |    2 +-
 libavcodec/srtenc.c                           |  116 +-
 libavcodec/subviewerdec.c                     |    2 +-
 libavcodec/tests/avcodec.c                    |    5 +-
 libavcodec/textdec.c                          |    4 +-
 libavcodec/ttmlenc.c                          |  114 +-
 libavcodec/utils.c                            |  185 ++-
 libavcodec/webvttdec.c                        |    2 +-
 libavcodec/webvttenc.c                        |   94 +-
 libavcodec/xsubdec.c                          |    2 +-
 libavcodec/xsubenc.c                          |   88 +-
 libavfilter/Makefile                          |   15 +
 libavfilter/allfilters.c                      |   14 +
 libavfilter/avfilter.c                        |   34 +-
 libavfilter/avfilter.h                        |   11 +
 libavfilter/avfiltergraph.c                   |    5 +
 libavfilter/buffersink.c                      |   54 +
 libavfilter/buffersink.h                      |    7 +
 libavfilter/buffersrc.c                       |   72 +
 libavfilter/buffersrc.h                       |    1 +
 libavfilter/formats.c                         |   16 +
 libavfilter/formats.h                         |    3 +
 libavfilter/internal.h                        |   19 +-
 libavfilter/sf_graphicsub2text.c              | 1132 +++++++++++++++
 libavfilter/sf_splitcc.c                      |  395 ++++++
 libavfilter/sf_stripstyles.c                  |  211 +++
 libavfilter/sf_subfeed.c                      |  402 ++++++
 libavfilter/sf_subscale.c                     |  884 ++++++++++++
 libavfilter/sf_textmod.c                      |  710 ++++++++++
 libavfilter/subtitles.c                       |   63 +
 libavfilter/subtitles.h                       |   44 +
 libavfilter/vf_overlaygraphicsubs.c           |  765 ++++++++++
 libavfilter/vf_overlaytextsubs.c              |  680 +++++++++
 libavfilter/vf_subtitles.c                    |   67 +-
 libavutil/Makefile                            |    4 +
 {libavcodec => libavutil}/ass.c               |  115 +-
 libavutil/ass_internal.h                      |  135 ++
 {libavcodec => libavutil}/ass_split.c         |   30 +-
 .../ass_split_internal.h                      |   32 +-
 libavutil/frame.c                             |  211 ++-
 libavutil/frame.h                             |   85 +-
 libavutil/subfmt.c                            |   45 +
 libavutil/subfmt.h                            |  115 ++
 libavutil/version.h                           |    1 +
 tests/ref/fate/filter-overlay-dvdsub-2397     |  182 +--
 tests/ref/fate/sub-dvb                        |  162 ++-
 tests/ref/fate/sub2video                      | 1091 ++++++++++++++-
 tests/ref/fate/sub2video_basic                | 1238 +++++++++++++++--
 tests/ref/fate/sub2video_time_limited         |   78 +-
 83 files changed, 11149 insertions(+), 1412 deletions(-)
 create mode 100644 libavfilter/sf_graphicsub2text.c
 create mode 100644 libavfilter/sf_splitcc.c
 create mode 100644 libavfilter/sf_stripstyles.c
 create mode 100644 libavfilter/sf_subfeed.c
 create mode 100644 libavfilter/sf_subscale.c
 create mode 100644 libavfilter/sf_textmod.c
 create mode 100644 libavfilter/subtitles.c
 create mode 100644 libavfilter/subtitles.h
 create mode 100644 libavfilter/vf_overlaygraphicsubs.c
 create mode 100644 libavfilter/vf_overlaytextsubs.c
 rename {libavcodec => libavutil}/ass.c (59%)
 create mode 100644 libavutil/ass_internal.h
 rename {libavcodec => libavutil}/ass_split.c (94%)
 rename libavcodec/ass_split.h => libavutil/ass_split_internal.h (86%)
 create mode 100644 libavutil/subfmt.c
 create mode 100644 libavutil/subfmt.h


base-commit: 9fba0b8a8c754a012fc74c90ffb7c26a56be8ca0
Published-As: https://github.com/ffstaging/FFmpeg/releases/tag/pr-ffstaging-18%2Fsoftworkz%2Fsubmit_subfiltering-v4
Fetch-It-Via: git fetch https://github.com/ffstaging/FFmpeg pr-ffstaging-18/softworkz/submit_subfiltering-v4
Pull-Request: https://github.com/ffstaging/FFmpeg/pull/18

Range-diff vs v3:

  1:  7767933235 !  1:  2f3ba171f5 avcodec,avutil: Move enum AVSubtitleType to avutil, add new and deprecate old values
     @@ libavutil/subfmt.h (new)
      
       ## libavutil/version.h ##
      @@
     - #define FF_API_COLORSPACE_NAME          (LIBAVUTIL_VERSION_MAJOR < 58)
     - #define FF_API_AV_MALLOCZ_ARRAY         (LIBAVUTIL_VERSION_MAJOR < 58)
     - #define FF_API_FIFO_PEEK2               (LIBAVUTIL_VERSION_MAJOR < 58)
     + #define FF_API_XVMC                     (LIBAVUTIL_VERSION_MAJOR < 58)
     + #define FF_API_OLD_CHANNEL_LAYOUT       (LIBAVUTIL_VERSION_MAJOR < 58)
     + #define FF_API_AV_FOPEN_UTF8            (LIBAVUTIL_VERSION_MAJOR < 58)
      +#define FF_API_OLD_SUBTITLES            (LIBAVUTIL_VERSION_MAJOR < 58)
       
       /**
  2:  e922f141ba !  2:  ff101f8a76 avutil/frame: Prepare AVFrame for subtitle handling
     @@ libavutil/frame.c
      +#include "subfmt.h"
       #include "hwcontext.h"
       
     - #define CHECK_CHANNELS_CONSISTENCY(frame) \
     + #if FF_API_OLD_CHANNEL_LAYOUT
      @@ libavutil/frame.c: const char *av_get_colorspace_name(enum AVColorSpace val)
           return name[val];
       }
     @@ libavutil/frame.c: static void get_frame_defaults(AVFrame *frame)
       }
       
       static void free_side_data(AVFrameSideData **ptr_sd)
     -@@ libavutil/frame.c: static int get_audio_buffer(AVFrame *frame, int align)
     +@@ libavutil/frame.c: FF_ENABLE_DEPRECATION_WARNINGS
       
       }
       
     @@ libavutil/frame.c: static int get_audio_buffer(AVFrame *frame, int align)
      +}
      +
       int av_frame_get_buffer(AVFrame *frame, int align)
     -+{
     -+    if (frame->width > 0 && frame->height > 0)
     + {
     +     if (frame->format < 0)
     +@@ libavutil/frame.c: int av_frame_get_buffer(AVFrame *frame, int align)
     + 
     + FF_DISABLE_DEPRECATION_WARNINGS
     +     if (frame->width > 0 && frame->height > 0)
     +-        return get_video_buffer(frame, align);
      +        frame->type = AVMEDIA_TYPE_VIDEO;
     -+    else if (frame->nb_samples > 0 && (frame->channel_layout || frame->channels > 0))
     +     else if (frame->nb_samples > 0 &&
     +              (av_channel_layout_check(&frame->ch_layout)
     + #if FF_API_OLD_CHANNEL_LAYOUT
     +               || frame->channel_layout || frame->channels > 0
     + #endif
     +              ))
     +-        return get_audio_buffer(frame, align);
      +        frame->type = AVMEDIA_TYPE_AUDIO;
     -+
     + FF_ENABLE_DEPRECATION_WARNINGS
     + 
     +-    return AVERROR(EINVAL);
      +    return av_frame_get_buffer2(frame, align);
      +}
      +
      +int av_frame_get_buffer2(AVFrame *frame, int align)
     - {
     -     if (frame->format < 0)
     -         return AVERROR(EINVAL);
     - 
     --    if (frame->width > 0 && frame->height > 0)
     -+    switch(frame->type) {
     ++{
     ++    if (frame->format < 0)
     ++        return AVERROR(EINVAL);
     ++
     ++    switch (frame->type) {
      +    case AVMEDIA_TYPE_VIDEO:
     -         return get_video_buffer(frame, align);
     --    else if (frame->nb_samples > 0 && (frame->channel_layout || frame->channels > 0))
     ++        return get_video_buffer(frame, align);
      +    case AVMEDIA_TYPE_AUDIO:
     -         return get_audio_buffer(frame, align);
     --
     --    return AVERROR(EINVAL);
     ++        return get_audio_buffer(frame, align);
      +    case AVMEDIA_TYPE_SUBTITLE:
      +        return get_subtitle_buffer(frame);
      +    default:
     @@ libavutil/frame.c: static int frame_copy_props(AVFrame *dst, const AVFrame *src,
       
           av_dict_copy(&dst->metadata, src->metadata, 0);
       
     -@@ libavutil/frame.c: int av_frame_ref(AVFrame *dst, const AVFrame *src)
     -     av_assert1(dst->width == 0 && dst->height == 0);
     -     av_assert1(dst->channels == 0);
     +@@ libavutil/frame.c: FF_ENABLE_DEPRECATION_WARNINGS
     +     av_assert1(dst->ch_layout.nb_channels == 0 &&
     +                dst->ch_layout.order == AV_CHANNEL_ORDER_UNSPEC);
       
      +    dst->type           = src->type;
           dst->format         = src->format;
           dst->width          = src->width;
           dst->height         = src->height;
     -@@ libavutil/frame.c: int av_frame_ref(AVFrame *dst, const AVFrame *src)
     +@@ libavutil/frame.c: FF_ENABLE_DEPRECATION_WARNINGS
       
           /* duplicate the frame data if it's not refcounted */
           if (!src->buf[0]) {
     @@ libavutil/frame.c: int av_frame_ref(AVFrame *dst, const AVFrame *src)
               if (ret < 0)
                   goto fail;
       
     -@@ libavutil/frame.c: int av_frame_ref(AVFrame *dst, const AVFrame *src)
     +@@ libavutil/frame.c: FF_ENABLE_DEPRECATION_WARNINGS
               }
           }
       
     @@ libavutil/frame.c: void av_frame_unref(AVFrame *frame)
           if (frame->extended_data != frame->data)
               av_freep(&frame->extended_data);
       
     -@@ libavutil/frame.c: void av_frame_move_ref(AVFrame *dst, AVFrame *src)
     +@@ libavutil/frame.c: FF_ENABLE_DEPRECATION_WARNINGS
       
       int av_frame_is_writable(AVFrame *frame)
       {
     @@ libavutil/frame.c: int av_frame_make_writable(AVFrame *frame)
           tmp.format         = frame->format;
           tmp.width          = frame->width;
           tmp.height         = frame->height;
     -@@ libavutil/frame.c: int av_frame_make_writable(AVFrame *frame)
     +@@ libavutil/frame.c: FF_ENABLE_DEPRECATION_WARNINGS
           if (frame->hw_frames_ctx)
               ret = av_hwframe_get_buffer(frame->hw_frames_ctx, &tmp, 0);
           else
     @@ libavutil/frame.c: AVBufferRef *av_frame_get_plane_buffer(AVFrame *frame, int pl
           int planes, i;
       
      -    if (frame->nb_samples) {
     --        int channels = frame->channels;
     --        if (!channels)
     --            return NULL;
     --        CHECK_CHANNELS_CONSISTENCY(frame);
     --        planes = av_sample_fmt_is_planar(frame->format) ? channels : 1;
     --    } else
      +    switch(frame->type) {
      +    case AVMEDIA_TYPE_VIDEO:
     -         planes = 4;
     ++        planes = 4;
      +        break;
      +    case AVMEDIA_TYPE_AUDIO:
      +        {
     -+            int channels = frame->channels;
     -+            if (!channels)
     -+                return NULL;
     -+            CHECK_CHANNELS_CONSISTENCY(frame);
     -+            planes = av_sample_fmt_is_planar(frame->format) ? channels : 1;
     -+            break;
     +         int channels = frame->ch_layout.nb_channels;
     + 
     + #if FF_API_OLD_CHANNEL_LAYOUT
     +@@ libavutil/frame.c: FF_ENABLE_DEPRECATION_WARNINGS
     +         if (!channels)
     +             return NULL;
     +         planes = av_sample_fmt_is_planar(frame->format) ? channels : 1;
     +-    } else
     +-        planes = 4;
     ++        break;
      +        }
      +    default:
      +        return NULL;
     @@ libavutil/frame.c: AVBufferRef *av_frame_get_plane_buffer(AVFrame *frame, int pl
       
           if (plane < 0 || plane >= planes || !frame->extended_data[plane])
               return NULL;
     -@@ libavutil/frame.c: static int frame_copy_audio(AVFrame *dst, const AVFrame *src)
     +@@ libavutil/frame.c: FF_ENABLE_DEPRECATION_WARNINGS
           return 0;
       }
       
     @@ libavutil/frame.c: static int frame_copy_audio(AVFrame *dst, const AVFrame *src)
           if (dst->format != src->format || dst->format < 0)
               return AVERROR(EINVAL);
       
     +-FF_DISABLE_DEPRECATION_WARNINGS
      -    if (dst->width > 0 && dst->height > 0)
      +    switch(dst->type) {
      +    case AVMEDIA_TYPE_VIDEO:
               return frame_copy_video(dst, src);
     --    else if (dst->nb_samples > 0 && dst->channels > 0)
     +-    else if (dst->nb_samples > 0 &&
     +-             (av_channel_layout_check(&dst->ch_layout)
     +-#if FF_API_OLD_CHANNEL_LAYOUT
     +-              || dst->channel_layout || dst->channels
     +-#endif
     +-            ))
      +    case AVMEDIA_TYPE_AUDIO:
               return frame_copy_audio(dst, src);
     +-FF_ENABLE_DEPRECATION_WARNINGS
      -
      -    return AVERROR(EINVAL);
      +    case AVMEDIA_TYPE_SUBTITLE:
     @@ libavutil/frame.h: typedef struct AVFrame {
           int format;
       
      @@ libavutil/frame.h: typedef struct AVFrame {
     -      * for the target frame's private_ref field.
     +      * Channel layout of the audio data.
            */
     -     AVBufferRef *private_ref;
     +     AVChannelLayout ch_layout;
      +
      +    /**
      +     * Media type of the frame (audio, video, subtitles..)
  3:  ec262914b0 !  3:  b8935d5e68 avcodec/subtitles: Introduce new frame-based subtitle decoding API
     @@ Commit message
      
          Signed-off-by: softworkz <softworkz@hotmail.com>
      
     + ## libavcodec/avcodec.c ##
     +@@ libavcodec/avcodec.c: FF_DISABLE_DEPRECATION_WARNINGS
     + FF_ENABLE_DEPRECATION_WARNINGS
     + #endif
     + 
     ++        // Set the subtitle type from the codec descriptor in case the decoder hasn't done itself
     ++        if (avctx->codec_type == AVMEDIA_TYPE_SUBTITLE && avctx->subtitle_type == AV_SUBTITLE_FMT_UNKNOWN) {
     ++            if(avctx->codec_descriptor->props & AV_CODEC_PROP_BITMAP_SUB)
     ++                avctx->subtitle_type = AV_SUBTITLE_FMT_BITMAP;
     ++            if(avctx->codec_descriptor->props & AV_CODEC_PROP_TEXT_SUB)
     ++                 avctx->subtitle_type = AV_SUBTITLE_FMT_ASS;
     ++        }
     ++
     + #if FF_API_AVCTX_TIMEBASE
     +         if (avctx->framerate.num > 0 && avctx->framerate.den > 0)
     +             avctx->time_base = av_inv_q(av_mul_q(avctx->framerate, (AVRational){avctx->ticks_per_frame, 1}));
     +
       ## libavcodec/avcodec.h ##
      @@ libavcodec/avcodec.h: typedef struct AVCodecContext {
       
     @@ libavcodec/avcodec.h: typedef struct AVCodecContext {
            * [Script Info] and [V4+ Styles] section, plus the [Events] line and
            * the Format line following. It shouldn't include any Dialogue line.
            * - encoding: Set/allocated/freed by user (before avcodec_open2())
     +@@ libavcodec/avcodec.h: typedef struct AVCodecContext {
     +      *             The decoder can then override during decoding as needed.
     +      */
     +     AVChannelLayout ch_layout;
     ++
     ++    enum AVSubtitleType subtitle_type;
     + } AVCodecContext;
     + 
     + /**
      @@ libavcodec/avcodec.h: int avcodec_close(AVCodecContext *avctx);
        * Free all allocated data in the given subtitle struct.
        *
     @@ libavcodec/avcodec.h: enum AVChromaLocation avcodec_chroma_pos_to_enum(int xpos,
                                   int *got_sub_ptr,
                                   AVPacket *avpkt);
      
     - ## libavcodec/codec_desc.c ##
     -@@ libavcodec/codec_desc.c: enum AVMediaType avcodec_get_type(enum AVCodecID codec_id)
     -     const AVCodecDescriptor *desc = avcodec_descriptor_get(codec_id);
     -     return desc ? desc->type : AVMEDIA_TYPE_UNKNOWN;
     - }
     -+
     -+enum AVSubtitleType avcodec_descriptor_get_subtitle_format(const AVCodecDescriptor *codec_descriptor)
     -+{
     -+    if(codec_descriptor->props & AV_CODEC_PROP_BITMAP_SUB)
     -+        return AV_SUBTITLE_FMT_BITMAP;
     -+
     -+    if(codec_descriptor->props & AV_CODEC_PROP_TEXT_SUB)
     -+        return AV_SUBTITLE_FMT_ASS;
     -+
     -+    return AV_SUBTITLE_FMT_UNKNOWN;
     -+}
     -
     - ## libavcodec/codec_desc.h ##
     -@@ libavcodec/codec_desc.h: const AVCodecDescriptor *avcodec_descriptor_next(const AVCodecDescriptor *prev);
     -  */
     - const AVCodecDescriptor *avcodec_descriptor_get_by_name(const char *name);
     - 
     -+/**
     -+ * Return subtitle format from a codec descriptor
     -+ *
     -+ * @param codec_descriptor codec descriptor
     -+ * @return                 the subtitle type (e.g. bitmap, text)
     -+ */
     -+enum AVSubtitleType avcodec_descriptor_get_subtitle_format(const AVCodecDescriptor *codec_descriptor);
     -+
     - /**
     -  * @}
     -  */
     -
       ## libavcodec/decode.c ##
      @@ libavcodec/decode.c: static int decode_receive_frame_internal(AVCodecContext *avctx, AVFrame *frame)
           return ret;
     @@ libavcodec/decode.c: int attribute_align_arg avcodec_send_packet(AVCodecContext
               return AVERROR(EINVAL);
       
      +    if (avctx->codec_type == AVMEDIA_TYPE_SUBTITLE)
     -+		// this does not exactly implement the avcodec_send_packet/avcodec_receive_frame API 
     ++		// this does not exactly implement the avcodec_send_packet/avcodec_receive_frame API
      +	    // but we know that no subtitle decoder produces multiple AVSubtitles per packet through
      +		// the legacy API, and this will be changed when migrating the subtitle decoders
      +		// to the frame based decoding api
     @@ libavcodec/decode.c: int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubti
      -            sub->format = 0;
      -        else if (avctx->codec_descriptor->props & AV_CODEC_PROP_TEXT_SUB)
      -            sub->format = 1;
     -+        sub->format = avcodec_descriptor_get_subtitle_format(avctx->codec_descriptor);
     ++        sub->format = (uint16_t)avctx->subtitle_type;
       
               for (unsigned i = 0; i < sub->num_rects; i++) {
                   if (avctx->sub_charenc_mode != FF_SUB_CHARENC_MODE_IGNORE &&
     @@ libavcodec/internal.h: int ff_int_from_list_or_default(void *ctx, const char * v
       #endif /* AVCODEC_INTERNAL_H */
      
       ## libavcodec/utils.c ##
     -@@ libavcodec/utils.c: int av_get_audio_frame_duration(AVCodecContext *avctx, int frame_bytes)
     +@@ libavcodec/utils.c: FF_ENABLE_DEPRECATION_WARNINGS
           return FFMAX(0, duration);
       }
       
     @@ libavcodec/utils.c: int av_get_audio_frame_duration(AVCodecContext *avctx, int f
      +
       int av_get_audio_frame_duration2(AVCodecParameters *par, int frame_bytes)
       {
     -     int duration = get_audio_frame_duration(par->codec_id, par->sample_rate,
     +    int channels = par->ch_layout.nb_channels;
  -:  ---------- >  4:  4b44732e07 avcodec/libzvbi: set subtitle type
  4:  77bd67ee37 !  5:  8faa7a4043 avfilter/subtitles: Update vf_subtitles to use new decoding api
     @@ libavfilter/vf_subtitles.c: static int attachment_is_font(AVStream * st)
      +
       AVFILTER_DEFINE_CLASS(subtitles);
       
     ++static enum AVSubtitleType get_subtitle_format(const AVCodecDescriptor *codec_descriptor)
     ++{
     ++    if(codec_descriptor->props & AV_CODEC_PROP_BITMAP_SUB)
     ++        return AV_SUBTITLE_FMT_BITMAP;
     ++
     ++    if(codec_descriptor->props & AV_CODEC_PROP_TEXT_SUB)
     ++        return AV_SUBTITLE_FMT_ASS;
     ++
     ++    return AV_SUBTITLE_FMT_UNKNOWN;
     ++}
     ++
       static av_cold int init_subtitles(AVFilterContext *ctx)
     + {
     +     int j, ret, sid;
      @@ libavfilter/vf_subtitles.c: static av_cold int init_subtitles(AVFilterContext *ctx)
           AVStream *st;
           AVPacket pkt;
     @@ libavfilter/vf_subtitles.c: static av_cold int init_subtitles(AVFilterContext *c
      +
           dec_desc = avcodec_descriptor_get(st->codecpar->codec_id);
      -    if (dec_desc && !(dec_desc->props & AV_CODEC_PROP_TEXT_SUB)) {
     -+    subtitle_format = avcodec_descriptor_get_subtitle_format(dec_desc);
     ++    subtitle_format = get_subtitle_format(dec_desc);
      +
      +    if (subtitle_format != AV_SUBTITLE_FMT_ASS) {
               av_log(ctx, AV_LOG_ERROR,
  5:  26bad0c088 !  6:  1664026d7c avcodec,avutil: Move ass helper functions to avutil as avpriv_ and extend ass dialog parsing
     @@ libavcodec/ass.h
      +static inline int ff_ass_subtitle_header_default(AVCodecContext *avctx)
      +{
      +    avctx->subtitle_header = (uint8_t *)avpriv_ass_get_subtitle_header_default(!(avctx->flags & AV_CODEC_FLAG_BITEXACT));
     -+
     + 
     +-/**
     +- * Add an ASS dialog to a subtitle.
     +- */
     +-int ff_ass_add_rect(AVSubtitle *sub, const char *dialog,
     +-                    int readorder, int layer, const char *style,
     +-                    const char *speaker);
      +    if (!avctx->subtitle_header)
      +        return AVERROR(ENOMEM);
      +    avctx->subtitle_header_size = strlen((char *)avctx->subtitle_header);
     @@ libavcodec/ass.h
       /**
        * Add an ASS dialog to a subtitle.
        */
     --int ff_ass_add_rect(AVSubtitle *sub, const char *dialog,
     +-int ff_ass_add_rect2(AVSubtitle *sub, const char *dialog,
     +-                     int readorder, int layer, const char *style,
     +-                     const char *speaker, unsigned *nb_rect_allocated);
      +static inline int avpriv_ass_add_rect(AVSubtitle *sub, const char *dialog,
     -                     int readorder, int layer, const char *style,
     --                    const char *speaker);
     ++                    int readorder, int layer, const char *style,
      +                    const char *speaker)
      +{
      +    char *ass_str;
     @@ libavcodec/assdec.c
       
       #include "avcodec.h"
      -#include "ass.h"
     + #include "codec_internal.h"
     + #include "config_components.h"
      +#include "libavutil/ass_internal.h"
     - #include "internal.h"
       #include "libavutil/internal.h"
       #include "libavutil/mem.h"
     + 
      
       ## libavcodec/assenc.c ##
      @@
     @@ libavcodec/assenc.c
       
       #include "avcodec.h"
      -#include "ass.h"
     + #include "codec_internal.h"
      +#include "libavutil/ass_internal.h"
     - #include "internal.h"
       #include "libavutil/avstring.h"
       #include "libavutil/internal.h"
     + #include "libavutil/mem.h"
      
       ## libavcodec/ccaption_dec.c ##
      @@ libavcodec/ccaption_dec.c: static av_cold int init_decoder(AVCodecContext *avctx)
     @@ libavcodec/ccaption_dec.c: static av_cold int init_decoder(AVCodecContext *avctx
           if (ret < 0) {
               return ret;
           }
     -@@ libavcodec/ccaption_dec.c: static int decode(AVCodecContext *avctx, void *data, int *got_sub, AVPacket *avp
     +@@ libavcodec/ccaption_dec.c: static int decode(AVCodecContext *avctx, AVSubtitle *sub,
     +     int len = avpkt->size;
     +     int ret = 0;
     +     int i;
     +-    unsigned nb_rect_allocated = 0;
     + 
     +     for (i = 0; i < len; i += 3) {
     +         uint8_t hi, cc_type = bptr[i] & 1;
     +@@ libavcodec/ccaption_dec.c: static int decode(AVCodecContext *avctx, AVSubtitle *sub,
                                                            AV_TIME_BASE_Q, ms_tb);
                   else
                       sub->end_display_time = -1;
     --            ret = ff_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL);
     +-            ret = ff_ass_add_rect2(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL, &nb_rect_allocated);
      +            ret = avpriv_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL);
                   if (ret < 0)
                       return ret;
                   ctx->last_real_time = sub->pts;
     -@@ libavcodec/ccaption_dec.c: static int decode(AVCodecContext *avctx, void *data, int *got_sub, AVPacket *avp
     +@@ libavcodec/ccaption_dec.c: static int decode(AVCodecContext *avctx, AVSubtitle *sub,
       
           if (!bptr && !ctx->real_time && ctx->buffer[!ctx->buffer_index].str[0]) {
               bidx = !ctx->buffer_index;
     --        ret = ff_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL);
     +-        ret = ff_ass_add_rect2(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL, &nb_rect_allocated);
      +        ret = avpriv_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL);
               if (ret < 0)
                   return ret;
               sub->pts = ctx->buffer_time[1];
     -@@ libavcodec/ccaption_dec.c: static int decode(AVCodecContext *avctx, void *data, int *got_sub, AVPacket *avp
     +@@ libavcodec/ccaption_dec.c: static int decode(AVCodecContext *avctx, AVSubtitle *sub,
               capture_screen(ctx);
               ctx->buffer_changed = 0;
       
     --        ret = ff_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL);
     +-        ret = ff_ass_add_rect2(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL, &nb_rect_allocated);
      +        ret = avpriv_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL);
               if (ret < 0)
                   return ret;
               sub->end_display_time = -1;
      
       ## libavcodec/jacosubdec.c ##
     -@@ libavcodec/jacosubdec.c: static int jacosub_decode_frame(AVCodecContext *avctx,
     +@@ libavcodec/jacosubdec.c: static int jacosub_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
       
               av_bprint_init(&buffer, JSS_MAX_LINESIZE, JSS_MAX_LINESIZE);
               jacosub_to_ass(avctx, &buffer, ptr);
     @@ libavcodec/libzvbi-teletextdec.c: static int gen_sub_bitmap(TeletextContext *ctx
               return 0;
           }
       
     -@@ libavcodec/libzvbi-teletextdec.c: static int teletext_decode_frame(AVCodecContext *avctx, void *data, int *got_sub
     +@@ libavcodec/libzvbi-teletextdec.c: static int teletext_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
               sub->num_rects = 0;
               sub->pts = ctx->pages->pts;
       
     @@ libavcodec/libzvbi-teletextdec.c: static int teletext_decode_frame(AVCodecContex
                       sub->num_rects = 1;
      
       ## libavcodec/microdvddec.c ##
     -@@ libavcodec/microdvddec.c: static int microdvd_decode_frame(AVCodecContext *avctx,
     +@@ libavcodec/microdvddec.c: static int microdvd_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
               }
           }
           if (new_line.len) {
     @@ libavcodec/movtextdec.c
       #include "libavutil/common.h"
       #include "libavutil/bprint.h"
       #include "libavutil/intreadwrite.h"
     -@@ libavcodec/movtextdec.c: static int mov_text_decode_frame(AVCodecContext *avctx,
     +@@ libavcodec/movtextdec.c: static int mov_text_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
           } else
               text_to_ass(&buf, ptr, end, avctx);
       
     @@ libavcodec/movtextenc.c
      +#include "libavutil/ass_split_internal.h"
      +#include "libavutil/ass_internal.h"
       #include "bytestream.h"
     - #include "internal.h"
     + #include "codec_internal.h"
       
      @@ libavcodec/movtextenc.c: static int mov_text_encode_close(AVCodecContext *avctx)
       {
     @@ libavcodec/movtextenc.c: static int mov_text_encode_frame(AVCodecContext *avctx,
           if (s->buffer.len > UINT16_MAX)
      
       ## libavcodec/mpl2dec.c ##
     -@@ libavcodec/mpl2dec.c: static int mpl2_decode_frame(AVCodecContext *avctx, void *data,
     +@@ libavcodec/mpl2dec.c: static int mpl2_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
       
           av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
           if (ptr && avpkt->size > 0 && *ptr && !mpl2_event_to_ass(&buf, ptr))
     @@ libavcodec/mpl2dec.c: static int mpl2_decode_frame(AVCodecContext *avctx, void *
               return ret;
      
       ## libavcodec/realtextdec.c ##
     -@@ libavcodec/realtextdec.c: static int realtext_decode_frame(AVCodecContext *avctx,
     +@@ libavcodec/realtextdec.c: static int realtext_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
       
           av_bprint_init(&buf, 0, 4096);
           if (ptr && avpkt->size > 0 && !rt_event_to_ass(&buf, ptr))
     @@ libavcodec/realtextdec.c: static int realtext_decode_frame(AVCodecContext *avctx
               return ret;
      
       ## libavcodec/samidec.c ##
     -@@ libavcodec/samidec.c: static int sami_decode_frame(AVCodecContext *avctx,
     +@@ libavcodec/samidec.c: static int sami_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
               if (ret < 0)
                   return ret;
               // TODO: pass escaped sami->encoded_source.str as source
     @@ libavcodec/samidec.c: static int sami_decode_frame(AVCodecContext *avctx,
           }
      
       ## libavcodec/srtdec.c ##
     -@@ libavcodec/srtdec.c: static int srt_decode_frame(AVCodecContext *avctx,
     +@@ libavcodec/srtdec.c: static int srt_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
       
           ret = srt_to_ass(avctx, &buffer, avpkt->data, x1, y1, x2, y2);
           if (ret >= 0)
     @@ libavcodec/srtenc.c
       #include "libavutil/bprint.h"
      -#include "ass_split.h"
      -#include "ass.h"
     + #include "codec_internal.h"
      +#include "libavutil/ass_split_internal.h"
      +#include "libavutil/ass_internal.h"
     - #include "internal.h"
       
       
     + #define SRT_STACK_SIZE 64
      @@ libavcodec/srtenc.c: static void srt_stack_push_pop(SRTContext *s, const char c, int close)
       
       static void srt_style_apply(SRTContext *s, const char *style)
     @@ libavcodec/srtenc.c: static int text_encode_frame(AVCodecContext *avctx,
       }
      
       ## libavcodec/subviewerdec.c ##
     -@@ libavcodec/subviewerdec.c: static int subviewer_decode_frame(AVCodecContext *avctx,
     +@@ libavcodec/subviewerdec.c: static int subviewer_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
       
           av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
           if (ptr && avpkt->size > 0 && !subviewer_event_to_ass(&buf, ptr))
     @@ libavcodec/subviewerdec.c: static int subviewer_decode_frame(AVCodecContext *avc
               return ret;
      
       ## libavcodec/textdec.c ##
     -@@ libavcodec/textdec.c: static int text_decode_frame(AVCodecContext *avctx, void *data,
     +@@ libavcodec/textdec.c: static int text_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
       
           av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
           if (ptr && avpkt->size > 0 && *ptr) {
     @@ libavcodec/ttmlenc.c: static av_cold int ttml_encode_init(AVCodecContext *avctx)
       
      
       ## libavcodec/webvttdec.c ##
     -@@ libavcodec/webvttdec.c: static int webvtt_decode_frame(AVCodecContext *avctx,
     +@@ libavcodec/webvttdec.c: static int webvtt_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
       
           av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
           if (ptr && avpkt->size > 0 && !webvtt_event_to_ass(&buf, ptr))
     @@ libavcodec/webvttenc.c
       #include "libavutil/bprint.h"
      -#include "ass_split.h"
      -#include "ass.h"
     + #include "codec_internal.h"
      +#include "libavutil/ass_split_internal.h"
      +#include "libavutil/ass_internal.h"
     - #include "internal.h"
       
       #define WEBVTT_STACK_SIZE 64
     + typedef struct {
      @@ libavcodec/webvttenc.c: static void webvtt_stack_push_pop(WebVTTContext *s, const char c, int close)
       
       static void webvtt_style_apply(WebVTTContext *s, const char *style)
     @@ libavutil/ass.c: char *ff_ass_get_dialog(int readorder, int layer, const char *s
                              speaker ? speaker : "", text);
       }
       
     --int ff_ass_add_rect(AVSubtitle *sub, const char *dialog,
     +-int ff_ass_add_rect2(AVSubtitle *sub, const char *dialog,
      -                    int readorder, int layer, const char *style,
     --                    const char *speaker)
     -+char *avpriv_ass_get_dialog_ex(int readorder, int layer, const char *style,
     -+                        const char *speaker, int margin_l, int margin_r,
     -+                        int margin_v, const char *text)
     - {
     --    AVSubtitleRect **rects, *rect;
     +-                    const char *speaker, unsigned *nb_rect_allocated)
     +-{
     +-    AVSubtitleRect **rects = sub->rects, *rect;
      -    char *ass_str;
     +-    uint64_t new_nb = 0;
      -
     --    rects = av_realloc_array(sub->rects, sub->num_rects+1, sizeof(*sub->rects));
     --    if (!rects)
     +-    if (sub->num_rects >= UINT_MAX)
      -        return AVERROR(ENOMEM);
     --    sub->rects = rects;
     +-
     +-    if (nb_rect_allocated && *nb_rect_allocated <= sub->num_rects) {
     +-        if (sub->num_rects < UINT_MAX / 17 * 16) {
     +-            new_nb = sub->num_rects + sub->num_rects/16 + 1;
     +-        } else
     +-            new_nb = UINT_MAX;
     +-    } else if (!nb_rect_allocated)
     +-        new_nb = sub->num_rects + 1;
     +-
     +-    if (new_nb) {
     +-        rects = av_realloc_array(rects, new_nb, sizeof(*sub->rects));
     +-        if (!rects)
     +-            return AVERROR(ENOMEM);
     +-        if (nb_rect_allocated)
     +-            *nb_rect_allocated = new_nb;
     +-        sub->rects = rects;
     +-    }
     +-
      -    rect       = av_mallocz(sizeof(*rect));
      -    if (!rect)
      -        return AVERROR(ENOMEM);
     @@ libavutil/ass.c: char *ff_ass_get_dialog(int readorder, int layer, const char *s
      -    return 0;
      -}
      -
     +-int ff_ass_add_rect(AVSubtitle *sub, const char *dialog,
     +-                    int readorder, int layer, const char *style,
     +-                    const char *speaker)
     ++char *avpriv_ass_get_dialog_ex(int readorder, int layer, const char *style,
     ++                        const char *speaker, int margin_l, int margin_r,
     ++                        int margin_v, const char *text)
     + {
     +-    return ff_ass_add_rect2(sub, dialog, readorder, layer, style, speaker, NULL);
     +-}
     +-
      -void ff_ass_decoder_flush(AVCodecContext *avctx)
      -{
      -    FFASSDecoderContext *s = avctx->priv_data;
     @@ libavutil/ass_internal.h (new)
      
       ## libavcodec/ass_split.c => libavutil/ass_split.c ##
      @@
     - #include "libavutil/common.h"
       #include "libavutil/error.h"
     + #include "libavutil/macros.h"
       #include "libavutil/mem.h"
      -#include "ass_split.h"
      +#include "ass_split_internal.h"
  6:  c6fb78e038 !  7:  09d8cf7880 avcodec/subtitles: Replace deprecated enum values
     @@ libavcodec/ass.h: static inline int avpriv_ass_add_rect(AVSubtitle *sub, const c
               return AVERROR(ENOMEM);
      
       ## libavcodec/assdec.c ##
     -@@ libavcodec/assdec.c: static int ass_decode_frame(AVCodecContext *avctx, void *data, int *got_sub_ptr,
     +@@ libavcodec/assdec.c: static int ass_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
           if (!sub->rects[0])
               return AVERROR(ENOMEM);
           sub->num_rects = 1;
     @@ libavcodec/dvdsubenc.c: static int encode_dvd_subtitles(AVCodecContext *avctx,
               }
      
       ## libavcodec/pgssubdec.c ##
     -@@ libavcodec/pgssubdec.c: static int display_end_segment(AVCodecContext *avctx, void *data,
     +@@ libavcodec/pgssubdec.c: static int display_end_segment(AVCodecContext *avctx, AVSubtitle *sub,
               if (!rect)
                   return AVERROR(ENOMEM);
               sub->rects[sub->num_rects++] = rect;
     @@ libavcodec/pgssubdec.c: static int display_end_segment(AVCodecContext *avctx, vo
               object = find_object(ctx->presentation.objects[i].id, &ctx->objects);
      
       ## libavcodec/xsubdec.c ##
     -@@ libavcodec/xsubdec.c: static int decode_frame(AVCodecContext *avctx, void *data, int *got_sub_ptr,
     +@@ libavcodec/xsubdec.c: static int decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
           sub->num_rects = 1;
           rect->x = x; rect->y = y;
           rect->w = w; rect->h = h;
  7:  3d8673919f =  8:  897299bf7f fftools/play,probe: Adjust for subtitle changes
  8:  ba8b675326 !  9:  ca580c6d21 avfilter/subtitles: Add subtitles.c for subtitle frame allocation
     @@ libavfilter/Makefile: OBJS = allfilters.o
              graphdump.o                                                      \
              graphparser.o                                                    \
      +       subtitles.o                                                      \
     +        version.o                                                        \
              video.o                                                          \
       
     - OBJS-$(HAVE_THREADS)                         += pthread.o
      
       ## libavfilter/avfilter.c ##
      @@
     @@ libavfilter/avfilter.c
       #include "internal.h"
      +#include "subtitles.h"
       
     - #include "libavutil/ffversion.h"
     - const char av_filter_ffversion[] = "FFmpeg version " FFMPEG_VERSION;
     + static void tlog_ref(void *ctx, AVFrame *ref, int end)
     + {
      @@ libavfilter/avfilter.c: int ff_inlink_make_frame_writable(AVFilterLink *link, AVFrame **rframe)
           case AVMEDIA_TYPE_AUDIO:
               out = ff_get_audio_buffer(link, frame->nb_samples);
  9:  eb09db0e00 ! 10:  0781e974a2 avfilter/avfilter: Handle subtitle frames
     @@ libavfilter/avfilter.c: static void tlog_ref(void *ctx, AVFrame *ref, int end)
           }
       
           ff_tlog(ctx, "]%s", end ? "\n" : "");
     +@@ libavfilter/avfilter.c: int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
     +             av_assert1(frame->width               == link->w);
     +             av_assert1(frame->height               == link->h);
     +         }
     ++    } else if (link->type == AVMEDIA_TYPE_SUBTITLE) {
     ++        if (frame->format != link->format) {
     ++            av_log(link->dst, AV_LOG_WARNING, "Subtitle format change from %d to %d\n", link->format, frame->format);
     ++        }
     +     } else {
     +         if (frame->format != link->format) {
     +             av_log(link->dst, AV_LOG_ERROR, "Format change is not supported\n");
      
       ## libavfilter/avfilter.h ##
      @@
     @@ libavfilter/avfilter.h
      +#include "libavutil/subfmt.h"
       #include "libavutil/rational.h"
       
     - #include "libavfilter/version.h"
     + #include "libavfilter/version_major.h"
      @@ libavfilter/avfilter.h: typedef struct AVFilter {
                * and outputs use the same sample rate and channel count/layout.
                */
     @@ libavfilter/formats.c
       #include "libavutil/avassert.h"
       #include "libavutil/channel_layout.h"
       #include "libavutil/common.h"
     -@@ libavfilter/formats.c: int ff_add_channel_layout(AVFilterChannelLayouts **l, uint64_t channel_layout)
     -     return 0;
     - }
     - 
     -+int ff_add_subtitle_type(AVFilterFormats **avff, int64_t fmt)
     -+{
     -+    ADD_FORMAT(avff, fmt, ff_formats_unref, int, formats, nb_formats);
     -+    return 0;
     -+}
     -+
     - AVFilterFormats *ff_make_formats_list_singleton(int fmt)
     - {
     -     int fmts[2] = { fmt, -1 };
      @@ libavfilter/formats.c: AVFilterFormats *ff_all_formats(enum AVMediaType type)
                       return NULL;
                   fmt++;
               }
      +    } else if (type == AVMEDIA_TYPE_SUBTITLE) {
     -+        if (ff_add_subtitle_type(&ret, AV_SUBTITLE_FMT_BITMAP) < 0)
     ++        if (ff_add_format(&ret, AV_SUBTITLE_FMT_BITMAP) < 0)
      +            return NULL;
     -+        if (ff_add_subtitle_type(&ret, AV_SUBTITLE_FMT_ASS) < 0)
     ++        if (ff_add_format(&ret, AV_SUBTITLE_FMT_ASS) < 0)
      +            return NULL;
     -+        if (ff_add_subtitle_type(&ret, AV_SUBTITLE_FMT_TEXT) < 0)
     ++        if (ff_add_format(&ret, AV_SUBTITLE_FMT_TEXT) < 0)
      +            return NULL;
           }
       
     @@ libavfilter/formats.c: int ff_default_query_formats(AVFilterContext *ctx)
           /* Intended fallthrough */
      
       ## libavfilter/formats.h ##
     -@@ libavfilter/formats.h: int ff_set_common_formats_from_list(AVFilterContext *ctx, const int *fmts);
     - av_warn_unused_result
     - int ff_add_channel_layout(AVFilterChannelLayouts **l, uint64_t channel_layout);
     +@@ libavfilter/formats.h: av_warn_unused_result
     + int ff_add_channel_layout(AVFilterChannelLayouts **l,
     +                           const AVChannelLayout *channel_layout);
       
      +av_warn_unused_result
      +int ff_add_subtitle_type(AVFilterFormats **avff, int64_t fmt);
 10:  b31a991320 = 11:  d9d9f42558 avfilter/avfilter: Fix hardcoded input index
 11:  59abea7693 ! 12:  af69a4b321 avfilter/sbuffer: Add sbuffersrc and sbuffersink filters
     @@ libavfilter/buffersink.c: static int asink_query_formats(AVFilterContext *ctx)
      +    CHECK_LIST_SIZE(subtitle_types)
      +    if (buf->subtitle_types_size) {
      +        for (i = 0; i < NB_ITEMS(buf->subtitle_types); i++)
     -+            if ((ret = ff_add_subtitle_type(&formats, buf->subtitle_types[i])) < 0)
     ++            if ((ret = ff_add_format(&formats, buf->subtitle_types[i])) < 0)
      +                return ret;
      +        if ((ret = ff_set_common_formats(ctx, formats)) < 0)
      +            return ret;
     @@ libavfilter/buffersrc.c
       typedef struct BufferSourceContext {
           const AVClass    *class;
      @@ libavfilter/buffersrc.c: typedef struct BufferSourceContext {
     -     uint64_t channel_layout;
           char    *channel_layout_str;
     +     AVChannelLayout ch_layout;
       
      +    /* subtitle only */
      +    enum AVSubtitleType subtitle_type;
     @@ libavfilter/buffersrc.c: typedef struct BufferSourceContext {
           int eof;
       } BufferSourceContext;
       
     -@@ libavfilter/buffersrc.c: int av_buffersrc_parameters_set(AVFilterContext *ctx, AVBufferSrcParameters *par
     -         if (param->channel_layout)
     -             s->channel_layout = param->channel_layout;
     +@@ libavfilter/buffersrc.c: FF_ENABLE_DEPRECATION_WARNINGS
     +                 return ret;
     +         }
               break;
      +    case AVMEDIA_TYPE_SUBTITLE:
      +        s->subtitle_type = param->format;
     @@ libavfilter/buffersrc.c: int av_buffersrc_parameters_set(AVFilterContext *ctx, A
           default:
               return AVERROR_BUG;
           }
     -@@ libavfilter/buffersrc.c: int attribute_align_arg av_buffersrc_add_frame_flags(AVFilterContext *ctx, AVFra
     -             CHECK_AUDIO_PARAM_CHANGE(ctx, s, frame->sample_rate, frame->channel_layout,
     -                                      frame->channels, frame->format, frame->pts);
     +@@ libavfilter/buffersrc.c: FF_ENABLE_DEPRECATION_WARNINGS
     +             CHECK_AUDIO_PARAM_CHANGE(ctx, s, frame->sample_rate, frame->ch_layout,
     +                                      frame->format, frame->pts);
                   break;
      +        case AVMEDIA_TYPE_SUBTITLE:
      +            break;
     @@ libavfilter/buffersrc.c: static const AVOption abuffer_options[] = {
       static av_cold int init_audio(AVFilterContext *ctx)
       {
           BufferSourceContext *s = ctx->priv;
     -@@ libavfilter/buffersrc.c: static av_cold int init_audio(AVFilterContext *ctx)
     +@@ libavfilter/buffersrc.c: FF_ENABLE_DEPRECATION_WARNINGS
           return ret;
       }
       
     @@ libavfilter/buffersrc.c: static int query_formats(AVFilterContext *ctx)
               return AVERROR(EINVAL);
           }
      @@ libavfilter/buffersrc.c: static int config_props(AVFilterLink *link)
     -         if (!c->channel_layout)
     -             c->channel_layout = link->channel_layout;
     +                 return ret;
     +         }
               break;
      +    case AVMEDIA_TYPE_SUBTITLE:
      +        link->format = c->subtitle_type;
     @@ libavfilter/buffersrc.c: const AVFilter ff_asrc_abuffer = {
           .priv_class = &abuffer_class,
       };
      +
     -+static const AVFilterPad ssrc_sbuffer_outputs[] = {
     ++static const AVFilterPad avfilter_ssrc_sbuffer_outputs[] = {
      +    {
      +        .name          = "default",
      +        .type          = AVMEDIA_TYPE_SUBTITLE,
     @@ libavfilter/buffersrc.c: const AVFilter ff_asrc_abuffer = {
      +    .uninit    = uninit,
      +
      +    .inputs    = NULL,
     -+    FILTER_OUTPUTS(ssrc_sbuffer_outputs),
     ++    FILTER_OUTPUTS(avfilter_ssrc_sbuffer_outputs),
      +    FILTER_QUERY_FUNC(query_formats),
      +    .priv_class = &sbuffer_class,
      +};
 12:  867b60c60d ! 13:  f7e5b590a2 avfilter/overlaygraphicsubs: Add overlaygraphicsubs and graphicsub2video filters
     @@ doc/filters.texi: tools.
      +This filter replaces the previous "sub2video" hack which did the conversion implicitly and up-front as subtitle filtering wasn't possible at that time.
      +To retain compatibility with earlier sub2video command lines, this filter is being auto-inserted in those cases.
      +
     -+For overlaying graphicsal subtitles it is recommended to use the 'overlay_graphicsubs' filter which is more efficient and takes less processing resources.
     ++For overlaying graphicsal subtitles it is recommended to use the 'overlaygraphicsubs' filter which is more efficient and takes less processing resources.
      +
      +This filter is still useful in cases where the overlay is done with hardware acceleration (e.g. overlay_qsv, overlay_vaapi, overlay_cuda) for preparing the overlay frames.
      +
     @@ doc/filters.texi: tools.
      +@itemize
      +@item
      +Overlay PGS subtitles
     -+(not recommended - better use overlay_graphicsubs)
     ++(not recommended - better use overlaygraphicsubs)
      +@example
      +ffmpeg -i "https://streams.videolan.org/samples/sub/PGS/Girl_With_The_Dragon_Tattoo_2%3A23%3A56.mkv" -filter_complex "[0:1]graphicsub2video[subs];[0:0][subs]overlay" output.mp4
      +@end example
     @@ libavfilter/Makefile: OBJS-$(CONFIG_OVERLAY_OPENCL_FILTER)         += vf_overlay
       OBJS-$(CONFIG_PAD_OPENCL_FILTER)             += vf_pad_opencl.o opencl.o opencl/pad.o
      
       ## libavfilter/allfilters.c ##
     -@@ libavfilter/allfilters.c: extern const AVFilter ff_vf_oscilloscope;
     - extern const AVFilter ff_vf_overlay;
     - extern const AVFilter ff_vf_overlay_opencl;
     - extern const AVFilter ff_vf_overlay_qsv;
     -+extern const AVFilter ff_vf_overlaygraphicsubs;
     +@@ libavfilter/allfilters.c: extern const AVFilter ff_vf_overlay_qsv;
       extern const AVFilter ff_vf_overlay_vaapi;
       extern const AVFilter ff_vf_overlay_vulkan;
       extern const AVFilter ff_vf_overlay_cuda;
     ++extern const AVFilter ff_vf_overlaygraphicsubs;
     + extern const AVFilter ff_vf_owdenoise;
     + extern const AVFilter ff_vf_pad;
     + extern const AVFilter ff_vf_pad_opencl;
      @@ libavfilter/allfilters.c: extern const AVFilter ff_avf_showvolume;
       extern const AVFilter ff_avf_showwaves;
       extern const AVFilter ff_avf_showwavespic;
     @@ libavfilter/allfilters.c: extern const AVFilter ff_avf_showvolume;
      +extern const AVFilter ff_svf_graphicsub2video;
       
       /* multimedia sources */
     - extern const AVFilter ff_avsrc_amovie;
     + extern const AVFilter ff_avsrc_avsynctest;
      
       ## libavfilter/vf_overlaygraphicsubs.c (new) ##
      @@
 13:  f88a5667e1 ! 14:  4c8092357f avfilter/overlaytextsubs: Add overlaytextsubs and textsubs2video filters
     @@ configure: overlay_qsv_filter_deps="libmfx"
       owdenoise_filter_deps="gpl"
       pad_opencl_filter_deps="opencl"
       pan_filter_deps="swresample"
     -@@ configure: superequalizer_filter_deps="avcodec"
     - superequalizer_filter_select="rdft"
     - surround_filter_deps="avcodec"
     - surround_filter_select="rdft"
     +@@ configure: stereo3d_filter_deps="gpl"
     + subtitles_filter_deps="avformat avcodec libass"
     + super2xsai_filter_deps="gpl"
     + pixfmts_super2xsai_test_deps="super2xsai_filter"
      +textsub2video_filter_deps="avcodec libass"
       tinterlace_filter_deps="gpl"
       tinterlace_merge_test_deps="tinterlace_filter"
     @@ doc/filters.texi: Overlay PGS subtitles
      +
      +Converts text subtitles to video frames.
      +
     -+For overlaying text subtitles onto video frames it is recommended to use the overlay_textsubs filter.
     ++For overlaying text subtitles onto video frames it is recommended to use the overlaytextsubs filter.
      +The textsub2video is useful for for creating transparent text-frames when overlay is done via hw acceleration
      +
      +Inputs:
     @@ libavfilter/Makefile: OBJS-$(CONFIG_SWAPRECT_FILTER)               += vf_swaprec
       OBJS-$(CONFIG_THUMBNAIL_FILTER)              += vf_thumbnail.o
      
       ## libavfilter/allfilters.c ##
     -@@ libavfilter/allfilters.c: extern const AVFilter ff_vf_oscilloscope;
     - extern const AVFilter ff_vf_overlay;
     - extern const AVFilter ff_vf_overlay_opencl;
     - extern const AVFilter ff_vf_overlay_qsv;
     --extern const AVFilter ff_vf_overlaygraphicsubs;
     --extern const AVFilter ff_vf_overlay_vaapi;
     +@@ libavfilter/allfilters.c: extern const AVFilter ff_vf_overlay_vaapi;
       extern const AVFilter ff_vf_overlay_vulkan;
       extern const AVFilter ff_vf_overlay_cuda;
     -+extern const AVFilter ff_vf_overlaygraphicsubs;
     + extern const AVFilter ff_vf_overlaygraphicsubs;
      +extern const AVFilter ff_vf_overlaytextsubs;
     -+extern const AVFilter ff_vf_overlay_vaapi;
       extern const AVFilter ff_vf_owdenoise;
       extern const AVFilter ff_vf_pad;
       extern const AVFilter ff_vf_pad_opencl;
     @@ libavfilter/allfilters.c: extern const AVFilter ff_avf_showwaves;
      +extern const AVFilter ff_svf_textsub2video;
       
       /* multimedia sources */
     - extern const AVFilter ff_avsrc_amovie;
     + extern const AVFilter ff_avsrc_avsynctest;
      
       ## libavfilter/vf_overlaytextsubs.c (new) ##
      @@
     @@ libavfilter/vf_overlaytextsubs.c (new)
      + * overlay text subtitles on top of a video frame
      + */
      +
     ++#include "config_components.h"
     ++
      +#include <ass/ass.h>
      +#include "libavutil/ass_internal.h"
      +#include "libavutil/thread.h"
 14:  25cda21970 ! 15:  8fdbdf7c5f avfilter/textmod: Add textmod, censor and show_speaker filters
     @@ doc/filters.texi: ffmpeg -i "http://streams.videolan.org/samples/sub/SSA/subtitl
      +@item
      +Prepend speaker names with blue text, smooth edges and blend/overlay that onto the video.
      +@example
     -+ffmpeg -i INPUT -filter_complex "showspeaker=format=colon:style='@{\\c&HDD0000&\\be1@}',[0:v]overlay_textsubs"
     ++ffmpeg -i INPUT -filter_complex "showspeaker=format=colon:style='@{\\c&HDD0000&\\be1@}',[0:v]overlaytextsubs"
      +@end example
      +@end itemize
      +
 15:  296f1f697b = 16:  d44b22f15b avfilter/stripstyles: Add stripstyles filter
 16:  509d6c67ba = 17:  28d75dc982 avfilter/splitcc: Add splitcc filter for closed caption handling
 17:  1d7acba39f ! 18:  42d1d1c819 avfilter/graphicsub2text: Add new graphicsub2text filter (OCR)
     @@ doc/filters.texi: ffmpeg -i "https://streams.videolan.org/ffmpeg/mkv_subtitles.m
       Renders graphic subtitles as video frames.
      
       ## libavfilter/Makefile ##
     -@@ libavfilter/Makefile: OBJS-$(CONFIG_GBLUR_VULKAN_FILTER)           += vf_gblur_vulkan.o vulkan.o vulka
     +@@ libavfilter/Makefile: OBJS-$(CONFIG_GBLUR_FILTER)                  += vf_gblur.o
     + OBJS-$(CONFIG_GBLUR_VULKAN_FILTER)           += vf_gblur_vulkan.o vulkan.o vulkan_filter.o
       OBJS-$(CONFIG_GEQ_FILTER)                    += vf_geq.o
       OBJS-$(CONFIG_GRADFUN_FILTER)                += vf_gradfun.o
     - OBJS-$(CONFIG_GRAPHICSUB2VIDEO_FILTER)       += vf_overlaygraphicsubs.o framesync.o
      +OBJS-$(CONFIG_GRAPHICSUB2TEXT_FILTER)        += sf_graphicsub2text.o
     -+OBJS-$(CONFIG_GRAPHICSUB2VIDEO_FILTER)       += vf_overlaygraphicsubs.o framesync.o
     + OBJS-$(CONFIG_GRAPHICSUB2VIDEO_FILTER)       += vf_overlaygraphicsubs.o framesync.o
       OBJS-$(CONFIG_GRAPHMONITOR_FILTER)           += f_graphmonitor.o
       OBJS-$(CONFIG_GRAYWORLD_FILTER)              += vf_grayworld.o
     - OBJS-$(CONFIG_GREYEDGE_FILTER)               += vf_colorconstancy.o
      
       ## libavfilter/allfilters.c ##
      @@ libavfilter/allfilters.c: extern const AVFilter ff_avf_showwaves;
 18:  244dd6de33 ! 19:  7095e8aa26 avfilter/subscale: Add filter for scaling and/or re-arranging graphical subtitles
     @@ doc/filters.texi: Set the rendering margin in pixels.
      +inside the video area without scaling, then overlay
      +subtitles onto the video.
      +@example
     -+ffmpeg -y -loglevel verbose -ss 32 -i "https://streams.videolan.org/samples/sub/largeres_vobsub.mkv" -filter_complex "[0:0]scale=1920x400,setsar=1[vid1];[0:10]subscale=w=1920:h=400:scale_mode=none:margin_h=50:arrange_h=margin_no_scale:arrange_v=margin_no_scale:margin_v=50:num_colors=256[sub1];[vid1][sub1]overlay_graphicsubs" -c:v libx265 output.ts
     ++ffmpeg -y -loglevel verbose -ss 32 -i "https://streams.videolan.org/samples/sub/largeres_vobsub.mkv" -filter_complex "[0:0]scale=1920x400,setsar=1[vid1];[0:10]subscale=w=1920:h=400:scale_mode=none:margin_h=50:arrange_h=margin_no_scale:arrange_v=margin_no_scale:margin_v=50:num_colors=256[sub1];[vid1][sub1]overlaygraphicsubs" -c:v libx265 output.ts
      +@end example
      +@item
      +Scale both, a video and its embedded VOB subs, then encode them as separate streams into an MKV.
 19:  a87812bd3c ! 20:  697939451e avfilter/subfeed: add subtitle feed filter
     @@ libavfilter/sf_subfeed.c (new)
      +        next_pts = 0;
      +
      +    if (s->next_pts_offset) {
     ++        av_log(outlink->src, AV_LOG_VERBOSE, "Subtracting next_pts_offset: %"PRId64" \n", s->next_pts_offset);
      +        next_pts -= s->next_pts_offset;
      +        s->next_pts_offset = 0;
      +    }
     @@ libavfilter/sf_subfeed.c (new)
      +        const AVFrame *current_frame = ff_framequeue_peek(&s->fifo, 0);
      +        const int64_t sub_end_time   = current_frame->subtitle_timing.start_pts + current_frame->subtitle_timing.duration;
      +
     -+        if (next_pts > sub_end_time) {
     -+            AVFrame *remove_frame = ff_framequeue_take(&s->fifo);
     -+            av_frame_free(&remove_frame);
     -+
     -+            if (ff_framequeue_queued_frames(&s->fifo)) {
     ++        if (ff_framequeue_queued_frames(&s->fifo) > 1) {
     ++            const AVFrame *next_frame = ff_framequeue_peek(&s->fifo, 1);
     ++            if (next_pts + interval > next_frame->subtitle_timing.start_pts) {
     ++                AVFrame *remove_frame = ff_framequeue_take(&s->fifo);
     ++                av_frame_free(&remove_frame);
      +                s->current_frame_isnew = 1;
      +                goto retry;
      +            }
      +        }
     ++
     ++        if (next_pts > sub_end_time) {
     ++            AVFrame *remove_frame = ff_framequeue_take(&s->fifo);
     ++            av_frame_free(&remove_frame);
     ++            s->current_frame_isnew = 1;
     ++            goto retry;
     ++        }
      +    }
      +
      +    if (ff_framequeue_queued_frames(&s->fifo)) {
     @@ libavfilter/sf_subfeed.c (new)
      +            s->current_frame_isnew = 0;
      +            s->recent_subtitle_pts = out->subtitle_timing.start_pts;
      +
     ++            av_log(outlink->src, AV_LOG_DEBUG, "Output1 frame pts: %"PRId64"  subtitle_pts: %"PRId64"  repeat_frame: %d\n",
     ++                out->pts, out->subtitle_timing.start_pts, out->repeat_sub);
     ++
      +            return ff_filter_frame(outlink, out);
      +        }
      +    }
     @@ libavfilter/sf_subfeed.c (new)
      +    out->repeat_sub = 1;
      +    out->subtitle_timing.start_pts = s->recent_subtitle_pts;
      +
     ++    av_log(outlink->src, AV_LOG_DEBUG, "Output2 frame pts: %"PRId64"  subtitle_pts: %"PRId64"  repeat_frame: %d\n",
     ++        out->pts, out->subtitle_timing.start_pts, out->repeat_sub);
     ++
      +    return ff_filter_frame(outlink, out);
      +}
      +
     @@ libavfilter/sf_subfeed.c (new)
      +    SubFeedContext *s           = inlink->dst->priv;
      +    AVFilterLink *outlink       = inlink->dst->outputs[0];
      +    const int64_t index         = (int64_t)ff_framequeue_queued_frames(&s->fifo) - 1;
     ++    size_t nb_queued_frames;
      +    int ret = 0;
      +
     ++    av_log(ctx, AV_LOG_VERBOSE, "frame.pts: %"PRId64" (AVTB: %"PRId64") -  subtitle_timing.start_pts: %"PRId64" subtitle_timing.duration: %"PRId64" - format: %d\n",
     ++        frame->pts, av_rescale_q(frame->pts, inlink->time_base, AV_TIME_BASE_Q), frame->subtitle_timing.start_pts, frame->subtitle_timing.duration, frame->format);
     ++
      +    frame->pts = av_rescale_q(frame->pts, inlink->time_base, AV_TIME_BASE_Q);
      +
      +    if (index < 0) {
     @@ libavfilter/sf_subfeed.c (new)
      +    } else if (s->fix_durations || s->fix_overlap) {
      +        AVFrame *previous_frame = ff_framequeue_peek(&s->fifo, index);
      +        const int64_t pts_diff = frame->subtitle_timing.start_pts - previous_frame->subtitle_timing.start_pts;
     ++        nb_queued_frames = ff_framequeue_queued_frames(&s->fifo);
      +
     -+        if (s->fix_durations && pts_diff > 0 && previous_frame->subtitle_timing.duration > ms_to_avtb(29000))
     -+                    previous_frame->subtitle_timing.duration = frame->subtitle_timing.start_pts - previous_frame->subtitle_timing.start_pts;
     ++        if (s->fix_durations && pts_diff > 0 && previous_frame->subtitle_timing.duration > ms_to_avtb(29000)) {
     ++            av_log(ctx, AV_LOG_VERBOSE, "Previous frame (index #%"PRId64") has a duration of %"PRId64" ms, setting to  %"PRId64" ms\n",
     ++                index, avtb_to_ms(previous_frame->subtitle_timing.duration), avtb_to_ms(frame->subtitle_timing.start_pts - previous_frame->subtitle_timing.start_pts));
     ++            previous_frame->subtitle_timing.duration = frame->subtitle_timing.start_pts - previous_frame->subtitle_timing.start_pts;
     ++        }
     ++
     ++        if (s->fix_overlap && pts_diff > 0 && previous_frame->subtitle_timing.duration > pts_diff) {
     ++            av_log(ctx, AV_LOG_VERBOSE, "Detected overlap from previous frame (index #%"PRId64") which had a duration of %"PRId64" ms, setting to the pts_diff which is %"PRId64" ms\n",
     ++                index, avtb_to_ms(previous_frame->subtitle_timing.duration), avtb_to_ms(pts_diff));
     ++            previous_frame->subtitle_timing.duration = pts_diff;
     ++        }
      +
     -+        if (s->fix_overlap && pts_diff > 0 && previous_frame->subtitle_timing.duration > pts_diff)
     -+                    previous_frame->subtitle_timing.duration = pts_diff;
     ++        if (pts_diff <= 0) {
     ++            av_log(ctx, AV_LOG_WARNING, "The pts_diff to the previous frame (index #%"PRId64")  is <= 0: %"PRId64" ms. The previous frame duration is %"PRId64" ms.\n",
     ++                index, avtb_to_ms(pts_diff),  avtb_to_ms(previous_frame->subtitle_timing.duration));
     ++        }
      +    }
      +
      +    ff_framequeue_add(&s->fifo, frame);
      +
     -+    if (index > 3)
     -+        av_log(ctx, AV_LOG_WARNING, "frame queue count: %d\n", (int)index);
     ++    nb_queued_frames = ff_framequeue_queued_frames(&s->fifo);
     ++
     ++    if (nb_queued_frames > 3)
     ++        av_log(ctx, AV_LOG_WARNING, "frame queue count: %zu\n", nb_queued_frames);
      +
     -+    if (s->mode == FM_FORWARD && ff_framequeue_queued_frames(&s->fifo)) {
     ++    if (s->mode == FM_FORWARD && nb_queued_frames) {
      +
      +        AVFrame *first_frame = ff_framequeue_peek(&s->fifo, 0);
      +
     -+        if (s->fix_overlap && ff_framequeue_queued_frames(&s->fifo) < 2)
     -+            return 0;
     ++        if (s->fix_overlap && nb_queued_frames < 2) {
     ++          av_log(ctx, AV_LOG_VERBOSE, "Return no frame since we have less than 2\n");
     ++          return 0;
     ++        }
      +
     -+        if (s->fix_durations && first_frame->subtitle_timing.duration > ms_to_avtb(29000))
     ++        if (s->fix_durations && first_frame->subtitle_timing.duration > ms_to_avtb(29000)) {
     ++            av_log(ctx, AV_LOG_VERBOSE, "Return no frame because first frame duration is %"PRId64" ms\n", avtb_to_ms(first_frame->subtitle_timing.duration));
      +            return 0;
     ++        }
      +
      +        first_frame = ff_framequeue_take(&s->fifo);
      +        return ff_filter_frame(outlink, first_frame);
 20:  aea8acb057 ! 21:  32e9af0806 avcodec/subtitles: Migrate subtitle encoders to frame-based API
     @@ Commit message
      
       ## libavcodec/assenc.c ##
      @@
     - #include <string.h>
       
       #include "avcodec.h"
     + #include "codec_internal.h"
      +#include "encode.h"
       #include "libavutil/ass_internal.h"
     - #include "internal.h"
       #include "libavutil/avstring.h"
       #include "libavutil/internal.h"
       #include "libavutil/mem.h"
     @@ libavcodec/assenc.c
       }
       
       #if CONFIG_SSA_ENCODER
     - const AVCodec ff_ssa_encoder = {
     --    .name         = "ssa",
     --    .long_name    = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"),
     --    .type         = AVMEDIA_TYPE_SUBTITLE,
     --    .id           = AV_CODEC_ID_ASS,
     + const FFCodec ff_ssa_encoder = {
     +-    .p.name       = "ssa",
     +-    .p.long_name  = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"),
     +-    .p.type       = AVMEDIA_TYPE_SUBTITLE,
     +-    .p.id         = AV_CODEC_ID_ASS,
      -    .init         = ass_encode_init,
     --    .encode_sub   = ass_encode_frame,
     -+    .name           = "ssa",
     -+    .long_name      = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"),
     -+    .type           = AVMEDIA_TYPE_SUBTITLE,
     -+    .id             = AV_CODEC_ID_ASS,
     +-    FF_CODEC_ENCODE_SUB_CB(ass_encode_frame),
     ++    .p.name           = "ssa",
     ++    .p.long_name      = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"),
     ++    .p.type           = AVMEDIA_TYPE_SUBTITLE,
     ++    .p.id             = AV_CODEC_ID_ASS,
      +    .priv_data_size = sizeof(AssEncContext),
      +    .init           = ass_encode_init,
      +    .close          = ass_encode_close,
     -+    .receive_packet = ass_receive_packet,
     ++    FF_CODEC_RECEIVE_PACKET_CB(ass_receive_packet),
           .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
       };
       #endif
       
       #if CONFIG_ASS_ENCODER
     - const AVCodec ff_ass_encoder = {
     --    .name         = "ass",
     --    .long_name    = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"),
     --    .type         = AVMEDIA_TYPE_SUBTITLE,
     --    .id           = AV_CODEC_ID_ASS,
     + const FFCodec ff_ass_encoder = {
     +-    .p.name       = "ass",
     +-    .p.long_name  = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"),
     +-    .p.type       = AVMEDIA_TYPE_SUBTITLE,
     +-    .p.id         = AV_CODEC_ID_ASS,
      -    .init         = ass_encode_init,
     --    .encode_sub   = ass_encode_frame,
     -+    .name           = "ass",
     -+    .long_name      = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"),
     -+    .type           = AVMEDIA_TYPE_SUBTITLE,
     +-    FF_CODEC_ENCODE_SUB_CB(ass_encode_frame),
     ++    .p.name           = "ass",
     ++    .p.long_name      = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"),
     ++    .p.type           = AVMEDIA_TYPE_SUBTITLE,
      +    .priv_data_size = sizeof(AssEncContext),
     -+    .id             = AV_CODEC_ID_ASS,
     ++    .p.id             = AV_CODEC_ID_ASS,
      +    .init           = ass_encode_init,
      +    .close          = ass_encode_close,
     -+    .receive_packet = ass_receive_packet,
     ++    FF_CODEC_RECEIVE_PACKET_CB(ass_receive_packet),
           .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
       };
       #endif
     @@ libavcodec/avcodec.h: void av_parser_close(AVCodecParserContext *s);
        * @}
        */
      
     + ## libavcodec/codec_internal.h ##
     +@@ libavcodec/codec_internal.h: enum FFCodecType {
     +     /* The codec is an encoder using the encode callback;
     +      * audio and video codecs only. */
     +     FF_CODEC_CB_TYPE_ENCODE,
     +-    /* The codec is an encoder using the encode_sub callback;
     +-     * subtitle codecs only. */
     +-    FF_CODEC_CB_TYPE_ENCODE_SUB,
     +     /* The codec is an encoder using the receive_packet callback;
     +      * audio and video codecs only. */
     +     FF_CODEC_CB_TYPE_RECEIVE_PACKET,
     +@@ libavcodec/codec_internal.h: typedef struct FFCodec {
     +          */
     +         int (*encode)(struct AVCodecContext *avctx, struct AVPacket *avpkt,
     +                       const struct AVFrame *frame, int *got_packet_ptr);
     +-        /**
     +-         * Encode subtitles to a raw buffer.
     +-         * cb is in this state if cb_type is FF_CODEC_CB_TYPE_ENCODE_SUB.
     +-         */
     +-        int (*encode_sub)(struct AVCodecContext *avctx, uint8_t *buf,
     +-                          int buf_size, const struct AVSubtitle *sub);
     +         /**
     +          * Encode API with decoupled frame/packet dataflow.
     +          * cb is in this state if cb_type is FF_CODEC_CB_TYPE_RECEIVE_PACKET.
     +@@ libavcodec/codec_internal.h: typedef struct FFCodec {
     + #define FF_CODEC_ENCODE_CB(func)                          \
     +     .cb_type           = FF_CODEC_CB_TYPE_ENCODE,         \
     +     .cb.encode         = (func)
     +-#define FF_CODEC_ENCODE_SUB_CB(func)                      \
     +-    .cb_type           = FF_CODEC_CB_TYPE_ENCODE_SUB,     \
     +-    .cb.encode_sub     = (func)
     + #define FF_CODEC_RECEIVE_PACKET_CB(func)                  \
     +     .cb_type           = FF_CODEC_CB_TYPE_RECEIVE_PACKET, \
     +     .cb.receive_packet = (func)
     +
       ## libavcodec/dvbsubenc.c ##
      @@
     -  */
       #include "avcodec.h"
       #include "bytestream.h"
     + #include "codec_internal.h"
      +#include "encode.h"
       #include "libavutil/colorspace.h"
       
     @@ libavcodec/dvbsubenc.c: static int dvbsub_encode(AVCodecContext *avctx, uint8_t
      +    return 0;
       }
       
     - const AVCodec ff_dvbsub_encoder = {
     -@@ libavcodec/dvbsubenc.c: const AVCodec ff_dvbsub_encoder = {
     -     .type           = AVMEDIA_TYPE_SUBTITLE,
     -     .id             = AV_CODEC_ID_DVB_SUBTITLE,
     + const FFCodec ff_dvbsub_encoder = {
     +@@ libavcodec/dvbsubenc.c: const FFCodec ff_dvbsub_encoder = {
     +     .p.type         = AVMEDIA_TYPE_SUBTITLE,
     +     .p.id           = AV_CODEC_ID_DVB_SUBTITLE,
           .priv_data_size = sizeof(DVBSubtitleContext),
     --    .encode_sub     = dvbsub_encode,
     -+    .encode2        = dvbsub_encode,
     +-    FF_CODEC_ENCODE_SUB_CB(dvbsub_encode),
     ++    FF_CODEC_ENCODE_CB(dvbsub_encode),
       };
      
       ## libavcodec/dvdsubenc.c ##
      @@
     -  */
       #include "avcodec.h"
       #include "bytestream.h"
     + #include "codec_internal.h"
      +#include "encode.h"
       #include "internal.h"
       #include "libavutil/avassert.h"
     @@ libavcodec/dvdsubenc.c: static int dvdsub_init(AVCodecContext *avctx)
           return ret;
       }
       
     -@@ libavcodec/dvdsubenc.c: const AVCodec ff_dvdsub_encoder = {
     -     .type           = AVMEDIA_TYPE_SUBTITLE,
     -     .id             = AV_CODEC_ID_DVD_SUBTITLE,
     +@@ libavcodec/dvdsubenc.c: const FFCodec ff_dvdsub_encoder = {
     +     .p.type         = AVMEDIA_TYPE_SUBTITLE,
     +     .p.id           = AV_CODEC_ID_DVD_SUBTITLE,
           .init           = dvdsub_init,
     --    .encode_sub     = dvdsub_encode,
     -+    .encode2        = dvdsub_encode,
     -     .priv_class     = &dvdsubenc_class,
     +-    FF_CODEC_ENCODE_SUB_CB(dvdsub_encode),
     ++    FF_CODEC_ENCODE_CB(dvdsub_encode),
     +     .p.priv_class   = &dvdsubenc_class,
           .priv_data_size = sizeof(DVDSubtitleContext),
           .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
      
     @@ libavcodec/encode.c: fail:
      +int avcodec_encode_subtitle(AVCodecContext *avctx, uint8_t *buf, int buf_size, const AVSubtitle *sub)
       {
      -    int ret;
     -+    int ret = 0, got_packet = 0;
     ++    int ret = 0;
      +    AVFrame *frame = NULL;
      +    AVPacket* avpkt = NULL;
      +
     @@ libavcodec/encode.c: fail:
               return -1;
           }
       
     --    ret = avctx->codec->encode_sub(avctx, buf, buf_size, sub);
     +-    ret = ffcodec(avctx->codec)->cb.encode_sub(avctx, buf, buf_size, sub);
      +    memset(buf, 0, buf_size);
      +    // Create a temporary frame for calling the regular api:
      +    frame = av_frame_alloc();
     @@ libavcodec/encode.c: fail:
      
       ## libavcodec/movtextenc.c ##
      @@
     - #include "libavutil/ass_split_internal.h"
       #include "libavutil/ass_internal.h"
       #include "bytestream.h"
     + #include "codec_internal.h"
      +#include "encode.h"
     - #include "internal.h"
       
       #define STYLE_FLAG_BOLD         (1<<0)
     + #define STYLE_FLAG_ITALIC       (1<<1)
      @@ libavcodec/movtextenc.c: typedef struct {
           AVCodecContext *avctx;
       
     @@ libavcodec/movtextenc.c: static const ASSCodesCallbacks mov_text_callbacks = {
       }
       
       #define OFFSET(x) offsetof(MovTextContext, x)
     -@@ libavcodec/movtextenc.c: const AVCodec ff_movtext_encoder = {
     +@@ libavcodec/movtextenc.c: const FFCodec ff_movtext_encoder = {
           .priv_data_size = sizeof(MovTextContext),
     -     .priv_class     = &mov_text_encoder_class,
     +     .p.priv_class   = &mov_text_encoder_class,
           .init           = mov_text_encode_init,
     --    .encode_sub     = mov_text_encode_frame,
     -+    .encode2        = mov_text_encode_frame,
     +-    FF_CODEC_ENCODE_SUB_CB(mov_text_encode_frame),
     ++    FF_CODEC_ENCODE_CB(mov_text_encode_frame),
           .close          = mov_text_encode_close,
           .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP,
       };
     @@ libavcodec/srtenc.c
      +#include "encode.h"
       #include "libavutil/avstring.h"
       #include "libavutil/bprint.h"
     - #include "libavutil/ass_split_internal.h"
     + #include "codec_internal.h"
      @@
       typedef struct {
           AVCodecContext *avctx;
     @@ libavcodec/srtenc.c: static const ASSCodesCallbacks text_callbacks = {
       }
       
       static int srt_encode_close(AVCodecContext *avctx)
     -@@ libavcodec/srtenc.c: const AVCodec ff_srt_encoder = {
     -     .id             = AV_CODEC_ID_SUBRIP,
     +@@ libavcodec/srtenc.c: const FFCodec ff_srt_encoder = {
     +     .p.id           = AV_CODEC_ID_SUBRIP,
           .priv_data_size = sizeof(SRTContext),
           .init           = srt_encode_init,
     --    .encode_sub     = srt_encode_frame,
     -+    .encode2        = srt_encode_frame,
     +-    FF_CODEC_ENCODE_SUB_CB(srt_encode_frame),
     ++    FF_CODEC_ENCODE_CB(srt_encode_frame),
           .close          = srt_encode_close,
           .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
       };
     -@@ libavcodec/srtenc.c: const AVCodec ff_subrip_encoder = {
     -     .id             = AV_CODEC_ID_SUBRIP,
     +@@ libavcodec/srtenc.c: const FFCodec ff_subrip_encoder = {
     +     .p.id           = AV_CODEC_ID_SUBRIP,
           .priv_data_size = sizeof(SRTContext),
           .init           = srt_encode_init,
     --    .encode_sub     = srt_encode_frame,
     -+    .encode2        = srt_encode_frame,
     +-    FF_CODEC_ENCODE_SUB_CB(srt_encode_frame),
     ++    FF_CODEC_ENCODE_CB(srt_encode_frame),
           .close          = srt_encode_close,
           .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
       };
     -@@ libavcodec/srtenc.c: const AVCodec ff_text_encoder = {
     -     .id             = AV_CODEC_ID_TEXT,
     +@@ libavcodec/srtenc.c: const FFCodec ff_text_encoder = {
     +     .p.id           = AV_CODEC_ID_TEXT,
           .priv_data_size = sizeof(SRTContext),
           .init           = srt_encode_init,
     --    .encode_sub     = text_encode_frame,
     -+    .encode2        = text_encode_frame,
     +-    FF_CODEC_ENCODE_SUB_CB(text_encode_frame),
     ++    FF_CODEC_ENCODE_CB(text_encode_frame),
           .close          = srt_encode_close,
           .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
       };
      
       ## libavcodec/tests/avcodec.c ##
      @@ libavcodec/tests/avcodec.c: int main(void){
     -             continue;
     +             is_decoder = 1;
     +             break;
     +         case FF_CODEC_CB_TYPE_ENCODE:
     +-        case FF_CODEC_CB_TYPE_ENCODE_SUB:
     +         case FF_CODEC_CB_TYPE_RECEIVE_PACKET:
     +             is_encoder = 1;
     +             break;
     +@@ libavcodec/tests/avcodec.c: int main(void){
     + #define CHECK(TYPE, type) (codec2->cb_type == FF_CODEC_CB_TYPE_ ## TYPE && !codec2->cb.type)
     +         if (CHECK(DECODE, decode) || CHECK(DECODE_SUB, decode_sub) ||
     +             CHECK(RECEIVE_PACKET, receive_packet) ||
     +-            CHECK(ENCODE, encode) || CHECK(ENCODE_SUB, encode_sub) ||
     ++            CHECK(ENCODE, encode) ||
     +             CHECK(RECEIVE_FRAME, receive_frame)) {
     +             ERR_EXT("Codec %s does not implement its %s callback.\n",
     +                     is_decoder ? "decoding" : "encoding");
               }
     + #undef CHECK
               if (is_encoder) {
     --            if (codec->type == AVMEDIA_TYPE_SUBTITLE ^ !!codec->encode_sub)
     +-            if ((codec->type == AVMEDIA_TYPE_SUBTITLE) != (codec2->cb_type == FF_CODEC_CB_TYPE_ENCODE_SUB))
      -                ERR("Encoder %s is both subtitle encoder and not subtitle encoder.");
     -             if (!!codec->encode_sub + !!codec->encode2 + !!codec->receive_packet != 1)
     -                 ERR("Encoder %s does not implement exactly one encode API.\n");
     -             if (codec->update_thread_context || codec->update_thread_context_for_user || codec->bsfs)
     +             if (codec2->update_thread_context || codec2->update_thread_context_for_user || codec2->bsfs)
     +                 ERR("Encoder %s has decoder-only thread functions or bsf.\n");
     +             if (codec->type == AVMEDIA_TYPE_AUDIO) {
      
       ## libavcodec/ttmlenc.c ##
      @@
     @@ libavcodec/ttmlenc.c: static av_cold int ttml_encode_init(AVCodecContext *avctx)
           }
       
           return 0;
     -@@ libavcodec/ttmlenc.c: const AVCodec ff_ttml_encoder = {
     -     .id             = AV_CODEC_ID_TTML,
     +@@ libavcodec/ttmlenc.c: const FFCodec ff_ttml_encoder = {
     +     .p.id           = AV_CODEC_ID_TTML,
           .priv_data_size = sizeof(TTMLContext),
           .init           = ttml_encode_init,
     --    .encode_sub     = ttml_encode_frame,
     -+    .encode2        = ttml_encode_frame,
     +-    FF_CODEC_ENCODE_SUB_CB(ttml_encode_frame),
     ++    FF_CODEC_ENCODE_CB(ttml_encode_frame),
           .close          = ttml_encode_close,
           .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP,
       };
      
     + ## libavcodec/utils.c ##
     +@@ libavcodec/utils.c: int av_codec_is_encoder(const AVCodec *avcodec)
     + {
     +     const FFCodec *const codec = ffcodec(avcodec);
     +     return codec && (codec->cb_type == FF_CODEC_CB_TYPE_ENCODE     ||
     +-                     codec->cb_type == FF_CODEC_CB_TYPE_ENCODE_SUB ||
     +                      codec->cb_type == FF_CODEC_CB_TYPE_RECEIVE_PACKET);
     + }
     + 
     +
       ## libavcodec/webvttenc.c ##
      @@
       
     @@ libavcodec/webvttenc.c
      +#include "encode.h"
       #include "libavutil/avstring.h"
       #include "libavutil/bprint.h"
     - #include "libavutil/ass_split_internal.h"
     + #include "codec_internal.h"
      @@
       typedef struct {
           AVCodecContext *avctx;
     @@ libavcodec/webvttenc.c: static const ASSCodesCallbacks webvtt_callbacks = {
      +
      +    // The frame has content, so we need to set up a context
      +    if (frame->subtitle_header && frame->subtitle_header->size > 0) {
     -+        avpriv_ass_split_free(s->ass_ctx);
      +        const char* subtitle_header = (char*)frame->subtitle_header->data;
     ++        avpriv_ass_split_free(s->ass_ctx);
      +        s->ass_ctx = avpriv_ass_split(subtitle_header);
      +        s->is_default_ass_context = 0;
      +    }
     @@ libavcodec/webvttenc.c: static av_cold int webvtt_encode_init(AVCodecContext *av
      +    return 0;
       }
       
     - const AVCodec ff_webvtt_encoder = {
     -@@ libavcodec/webvttenc.c: const AVCodec ff_webvtt_encoder = {
     -     .id             = AV_CODEC_ID_WEBVTT,
     + const FFCodec ff_webvtt_encoder = {
     +@@ libavcodec/webvttenc.c: const FFCodec ff_webvtt_encoder = {
     +     .p.id           = AV_CODEC_ID_WEBVTT,
           .priv_data_size = sizeof(WebVTTContext),
           .init           = webvtt_encode_init,
     --    .encode_sub     = webvtt_encode_frame,
     -+    .encode2        = webvtt_encode_frame,
     +-    FF_CODEC_ENCODE_SUB_CB(webvtt_encode_frame),
     ++    FF_CODEC_ENCODE_CB(webvtt_encode_frame),
           .close          = webvtt_encode_close,
           .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
       };
      
       ## libavcodec/xsubenc.c ##
      @@
     - 
       #include "avcodec.h"
       #include "bytestream.h"
     + #include "codec_internal.h"
      +#include "encode.h"
     - #include "internal.h"
       #include "put_bits.h"
       
     + /**
      @@ libavcodec/xsubenc.c: static int make_tc(uint64_t ms, int *tc)
           return ms > 99;
       }
     @@ libavcodec/xsubenc.c: static int xsub_encode(AVCodecContext *avctx, unsigned cha
       }
       
       static av_cold int xsub_encoder_init(AVCodecContext *avctx)
     -@@ libavcodec/xsubenc.c: const AVCodec ff_xsub_encoder = {
     -     .type       = AVMEDIA_TYPE_SUBTITLE,
     -     .id         = AV_CODEC_ID_XSUB,
     +@@ libavcodec/xsubenc.c: const FFCodec ff_xsub_encoder = {
     +     .p.type     = AVMEDIA_TYPE_SUBTITLE,
     +     .p.id       = AV_CODEC_ID_XSUB,
           .init       = xsub_encoder_init,
     --    .encode_sub = xsub_encode,
     -+    .encode2    = xsub_encode,
     +-    FF_CODEC_ENCODE_SUB_CB(xsub_encode),
     ++    FF_CODEC_ENCODE_CB(xsub_encode),
           .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE,
       };
 21:  314d1da505 ! 22:  fa0b5c2077 fftools/ffmpeg: Introduce subtitle filtering and new frame-based subtitle encoding
     @@ Commit message
          Justification for changed test refs:
      
          - sub2video
     -      The new results are identical excepting the last frame which
     -      is due to the implementation changes
     +      The previous results were incorrect. The command line for this test
     +      specifies -r 5 (5 frames per second), which is now fulfilled by the
     +      additional frames in the reference output.
     +      Example: The first subtitle time is 499000, the second is 15355000,
     +      which means 0.5s and 15.35s with a difference of 14.85s.
     +      15s * 5fps = 75 frames and that's now the exact number of video
     +      frames between these two subtitle events.
      
          - sub2video_basic
            The previous results had some incorrect output because multiple
     @@ Commit message
            CRC is due to the different blending algorithm that is being used.
      
          - sub2video_time_limited
     -      The third frame in the previous ref was a repetition, which doesn't
     -      happen anymore with the new subtitle filtering.
     +      Subtitle frames are emitted to the filter graphs at a 5 fps rate
     +      by default. The time limit for this test is 15s * 5fps = 75 frames
     +      which matches the count in the new ref.
      
          - sub-dvb
            Running ffprobe -show_frames on the source file shows that there
     @@ Commit message
          Signed-off-by: softworkz <softworkz@hotmail.com>
      
       ## fftools/ffmpeg.c ##
     -@@ fftools/ffmpeg.c: static int want_sdp = 1;
     +@@ fftools/ffmpeg.c: int want_sdp = 1;
       static BenchmarkTimeStamps current_time;
       AVIOContext *progress_avio = NULL;
       
     @@ fftools/ffmpeg.c: static void ffmpeg_cleanup(int ret)
                   InputFilter *ifilter = fg->inputs[j];
      -            struct InputStream *ist = ifilter->ist;
       
     -             while (av_fifo_size(ifilter->frame_queue)) {
     +             if (ifilter->frame_queue) {
                       AVFrame *frame;
      @@ fftools/ffmpeg.c: static void ffmpeg_cleanup(int ret)
     +                 av_fifo_freep2(&ifilter->frame_queue);
                   }
     -             av_fifo_freep(&ifilter->frame_queue);
                   av_freep(&ifilter->displaymatrix);
      -            if (ist->sub2video.sub_queue) {
     --                while (av_fifo_size(ist->sub2video.sub_queue)) {
     --                    AVSubtitle sub;
     --                    av_fifo_generic_read(ist->sub2video.sub_queue,
     --                                         &sub, sizeof(sub), NULL);
     +-                AVSubtitle sub;
     +-                while (av_fifo_read(ist->sub2video.sub_queue, &sub, 1) >= 0)
      -                    avsubtitle_free(&sub);
     --                }
     --                av_fifo_freep(&ist->sub2video.sub_queue);
     +-                av_fifo_freep2(&ist->sub2video.sub_queue);
      -            }
                   av_buffer_unref(&ifilter->hw_frames_ctx);
                   av_freep(&ifilter->name);
     @@ fftools/ffmpeg.c: static void ffmpeg_cleanup(int ret)
      +    ////av_freep(&subtitle_out);
       
           /* close files */
     -     for (i = 0; i < nb_output_files; i++) {
     +     for (i = 0; i < nb_output_files; i++)
      @@ fftools/ffmpeg.c: static void ffmpeg_cleanup(int ret)
     +     for (i = 0; i < nb_input_streams; i++) {
     +         InputStream *ist = input_streams[i];
     + 
     ++        if (ist->prev_sub.subtitle == ist->decoded_frame)
     ++            // Prevent double-free
     ++            ist->prev_sub.subtitle = NULL;
     ++
               av_frame_free(&ist->decoded_frame);
               av_packet_free(&ist->pkt);
               av_dict_free(&ist->decoder_opts);
     @@ fftools/ffmpeg.c: static void ffmpeg_cleanup(int ret)
               avcodec_free_context(&ist->dec_ctx);
       
               av_freep(&input_streams[i]);
     -@@ fftools/ffmpeg.c: error:
     -     exit_program(1);
     +@@ fftools/ffmpeg.c: static void do_audio_out(OutputFile *of, OutputStream *ost,
     +         exit_program(1);
       }
       
      -static void do_subtitle_out(OutputFile *of,
      -                            OutputStream *ost,
      -                            AVSubtitle *sub)
      +static void encode_subtitle_frame(OutputFile *of, OutputStream *ost, AVFrame *frame, AVPacket *pkt, int64_t pts_offset)
     - {
     --    int subtitle_out_max_size = 1024 * 1024;
     --    int subtitle_out_size, nb, i;
     ++{
      +    AVCodecContext *enc = ost->enc_ctx;
      +    int ret;
      +
     @@ fftools/ffmpeg.c: error:
      +}
      +
      +static void do_subtitle_out(OutputFile *of, OutputStream *ost, AVFrame *frame)
     -+{
     + {
     +-    int subtitle_out_max_size = 1024 * 1024;
     +-    int subtitle_out_size, nb, i;
      +    int nb, i;
           AVCodecContext *enc;
           AVPacket *pkt = ost->pkt;
     @@ fftools/ffmpeg.c: error:
      +        return;
           }
       
     ++    ////if (ost->last_subtitle_pts && ost->last_subtitle_pts == frame->subtitle_timing.start_pts) {
     ++    ////    av_log(NULL, AV_LOG_DEBUG, "Ignoring subtitle frame with duplicate subtitle_pts\n");
     ++    ////    return;
     ++    ////}
     ++
     ++    ost->last_subtitle_pts = frame->subtitle_timing.start_pts;
     ++
      +    init_output_stream_wrapper(ost, frame, 1);
      +
      +    enc = ost->enc_ctx;
     @@ fftools/ffmpeg.c: static int ifilter_send_frame(InputFilter *ifilter, AVFrame *f
           }
       
      @@ fftools/ffmpeg.c: static int ifilter_send_eof(InputFilter *ifilter, int64_t pts)
     +             return ret;
     +     } else {
               // the filtergraph was never configured
     -         if (ifilter->format < 0)
     -             ifilter_parameters_from_codecpar(ifilter, ifilter->ist->st->codecpar);
     +-        if (ifilter->format < 0) {
     +-            ret = ifilter_parameters_from_codecpar(ifilter, ifilter->ist->st->codecpar);
     +-            if (ret < 0)
     +-                return ret;
     +-        }
      -        if (ifilter->format < 0 && (ifilter->type == AVMEDIA_TYPE_AUDIO || ifilter->type == AVMEDIA_TYPE_VIDEO)) {
     ++        if (ifilter->format < 0)
     ++            ifilter_parameters_from_codecpar(ifilter, ifilter->ist->st->codecpar);
      +        if (ifilter->format < 0 && (ifilter->type == AVMEDIA_TYPE_AUDIO || ifilter->type == AVMEDIA_TYPE_VIDEO || ifilter->type == AVMEDIA_TYPE_SUBTITLE)) {
                   av_log(NULL, AV_LOG_ERROR, "Cannot determine format of input stream %d:%d after EOF\n", ifilter->ist->file_index, ifilter->ist->st->index);
                   return AVERROR_INVALIDDATA;
     @@ fftools/ffmpeg.c: fail:
       }
       
      -static int transcode_subtitles(InputStream *ist, AVPacket *pkt, int *got_output,
     ++static void subtitle_send_kickoff(InputStream *ist, int64_t heartbeat_pts)
     ++{
     ++    AVFrame *frame;
     ++    int64_t pts;
     ++
     ++    frame = av_frame_alloc();
     ++    if (!frame) {
     ++        av_log(ist->dec_ctx, AV_LOG_ERROR, "Unable to alloc frame (out of memory).\n");
     ++        return;
     ++    }
     ++
     ++    frame->type = AVMEDIA_TYPE_SUBTITLE;
     ++    av_frame_get_buffer2(frame, 0);
     ++
     ++    frame->format = (uint16_t)ist->dec_ctx->subtitle_type;
     ++
     ++    pts = FFMAX(heartbeat_pts, ist->subtitle_kickoff.last_pts) + av_rescale_q(10, (AVRational){ 1, 1000 }, ist->st->time_base);
     ++
     ++    frame->width = ist->subtitle_kickoff.w;
     ++    frame->height = ist->subtitle_kickoff.h;
     ++    frame->subtitle_timing.start_pts = av_rescale_q(pts, ist->st->time_base, AV_TIME_BASE_Q);
     ++    frame->subtitle_timing.duration =  av_rescale_q(10, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q);
     ++    frame->pts = pts;
     ++
     ++    av_log(NULL, AV_LOG_WARNING, "subtitle_kickoff: call subtitle_resend_current %"PRId64" frame->format: %d\n", pts, frame->format);
     ++
     ++    ist->subtitle_kickoff.last_pts = pts;
     ++
     ++    send_frame_to_filters(ist, frame);
     ++
     ++    av_frame_free(&frame);
     ++}
     ++
     ++// Opposed to the earlier "subtitle hearbeat", this is primarily aimed at
     ++// sending an initial subtitle frame to the filters for propagating the initial
     ++// timing values and to avoid that too much time is spent on a single "HW" decoder
     ++// which could overflow its buffer pool otherwise
     ++static void subtitle_kickoff(InputStream *ist, int64_t pts)
     ++{
     ++    int i;
     ++    int64_t pts2;
     ++    int64_t diff;
     ++
     ++    if (ist->st->codecpar->codec_type != AVMEDIA_TYPE_VIDEO)
     ++        return;
     ++
     ++    for (i = 0; i < nb_input_streams; i++) {
     ++        InputStream *ist2 = input_streams[i];
     ++        unsigned j, nb_reqs;
     ++
     ++        if (!ist2->subtitle_kickoff.is_active)
     ++            continue;
     ++        /* It's primarily the initial send that matters and which propagates
     ++         * the right start times to subtitle filters depending on input from decoding */
     ++        diff = av_rescale_q(5000, (AVRational){ 1, 1000 }, ist2->st->time_base);
     ++        pts2 = av_rescale_q(pts, ist->st->time_base, ist2->st->time_base);
     ++
     ++        /* do not send a kickoff frame if the subtitle is already ahead */
     ++        if (pts2 - diff <= ist2->subtitle_kickoff.last_pts)
     ++            continue;
     ++
     ++        for (j = 0, nb_reqs = 0; j < ist2->nb_filters; j++) {
     ++            if (ist2->filters[j]->filter == NULL) {
     ++                subtitle_send_kickoff(ist2, pts2);
     ++                return;
     ++            }
     ++            nb_reqs += av_buffersrc_get_nb_failed_requests(ist2->filters[j]->filter);
     ++        }
     ++        if (nb_reqs) {
     ++            av_log(NULL, AV_LOG_WARNING, "subtitle_kickoff: resend - pts: %"PRIi64"\n", pts2);
     ++            subtitle_send_kickoff(ist2, pts2);
     ++        }
     ++    }
     ++}
     ++
      +static InputStream *get_input_stream(OutputStream *ost)
      +{
      +    if (ost->source_index >= 0)
     @@ fftools/ffmpeg.c: fail:
      +    AVFrame *decoded_frame;
      +    AVCodecContext *avctx = ist->dec_ctx;
      +    int i = 0, ret = 0, err = 0;
     ++    int64_t pts;
      +
     -+    if (!ist->decoded_frame && !(ist->decoded_frame = av_frame_alloc()))
     ++    if (!ist->decoded_frame && !((ist->decoded_frame = av_frame_alloc())))
      +        return AVERROR(ENOMEM);
      +    decoded_frame = ist->decoded_frame;
      +    decoded_frame->type = AVMEDIA_TYPE_SUBTITLE;
     -+    decoded_frame->format = avcodec_descriptor_get_subtitle_format(avctx->codec_descriptor);
     - 
     --    check_decode_result(NULL, got_output, ret);
     ++    decoded_frame->format = (uint16_t)avctx->subtitle_type;
     ++
      +    if (!ist->subtitle_header && avctx->subtitle_header && avctx->subtitle_header_size > 0) {
      +        ist->subtitle_header = av_buffer_allocz(avctx->subtitle_header_size + 1);
      +        if (!ist->subtitle_header)
     @@ fftools/ffmpeg.c: fail:
      +    }
      +
      +    ret = decode(avctx, decoded_frame, got_output, pkt);
     -+
     + 
     +-    check_decode_result(NULL, got_output, ret);
      +    if (ret != AVERROR_EOF)
      +        check_decode_result(NULL, got_output, ret);
       
     @@ fftools/ffmpeg.c: fail:
      +        if (!pkt->size) {
      +            // Flush
      +            for (i = 0; i < ist->nb_filters; i++) {
     -+                ret = av_buffersrc_add_frame(ist->filters[i]->filter, NULL);
     -+                if (ret != AVERROR_EOF && ret < 0)
     -+                    av_log(NULL, AV_LOG_WARNING, "Flush the frame error.\n");
     ++                if (ist->filters[i]->filter != NULL) {
     ++                    ret = av_buffersrc_add_frame(ist->filters[i]->filter, NULL);
     ++                    if (ret != AVERROR_EOF && ret < 0)
     ++                        av_log(NULL, AV_LOG_WARNING, "Flush the frame error.\n");
     ++                }
      +            }
      +        }
               return ret;
     @@ fftools/ffmpeg.c: fail:
      -        FFSWAP(int,        *got_output, ist->prev_sub.got_output);
      -        FFSWAP(int,        ret,         ist->prev_sub.ret);
      -        FFSWAP(AVSubtitle, subtitle,    ist->prev_sub.subtitle);
     -+        FFSWAP(int,        *got_output,   ist->prev_sub.got_output);
     -+        FFSWAP(int,        ret,           ist->prev_sub.ret);
     -+        FFSWAP(AVFrame*,   decoded_frame, ist->prev_sub.subtitle);
     ++        FFSWAP(int,        *got_output,        ist->prev_sub.got_output);
     ++        FFSWAP(int,        ret,                ist->prev_sub.ret);
     ++        FFSWAP(AVFrame*,   ist->decoded_frame, ist->prev_sub.subtitle);
     ++        decoded_frame = ist->decoded_frame;
               if (end <= 0)
      -            goto out;
      +            return (int)end;
     @@ fftools/ffmpeg.c: fail:
      -        sub2video_update(ist, INT64_MIN, &subtitle);
      -    } else if (ist->nb_filters) {
      -        if (!ist->sub2video.sub_queue)
     --            ist->sub2video.sub_queue = av_fifo_alloc(8 * sizeof(AVSubtitle));
     +-            ist->sub2video.sub_queue = av_fifo_alloc2(8, sizeof(AVSubtitle), AV_FIFO_FLAG_AUTO_GROW);
      -        if (!ist->sub2video.sub_queue)
      -            exit_program(1);
     --        if (!av_fifo_space(ist->sub2video.sub_queue)) {
     --            ret = av_fifo_realloc2(ist->sub2video.sub_queue, 2 * av_fifo_size(ist->sub2video.sub_queue));
     --            if (ret < 0)
     --                exit_program(1);
      +    decoded_frame->type = AVMEDIA_TYPE_SUBTITLE;
     -+    decoded_frame->format = avcodec_descriptor_get_subtitle_format(avctx->codec_descriptor);
     + 
     +-        ret = av_fifo_write(ist->sub2video.sub_queue, &subtitle, 1);
     +-        if (ret < 0)
     +-            exit_program(1);
     +-        free_sub = 0;
     +-    }
     ++    if (decoded_frame->format == AV_SUBTITLE_FMT_UNKNOWN)
     ++        decoded_frame->format = (uint16_t)avctx->subtitle_type;
      +
      +    if ((ret = av_buffer_replace(&decoded_frame->subtitle_header, ist->subtitle_header)) < 0)
      +        return ret;
     -+
     + 
     +-    if (!subtitle.num_rects)
     +-        goto out;
      +    decoded_frame->pts = av_rescale_q(decoded_frame->subtitle_timing.start_pts, AV_TIME_BASE_Q, ist->st->time_base);
     -+
     + 
     +-    ist->frames_decoded++;
     ++    pts     = av_rescale_q(decoded_frame->subtitle_timing.start_pts,
     ++                           AV_TIME_BASE_Q, ist->st->time_base);
     + 
     +-    for (i = 0; i < nb_output_streams; i++) {
     +-        OutputStream *ost = output_streams[i];
     ++    ist->subtitle_kickoff.last_pts = decoded_frame->pts = pts;
     + 
     +-        if (!check_output_constraints(ist, ost) || !ost->encoding_needed
     +-            || ost->enc->type != AVMEDIA_TYPE_SUBTITLE)
     +-            continue;
      +    if (ist->nb_filters > 0) {
      +        AVFrame *filter_frame = av_frame_clone(decoded_frame);
      +        if (!filter_frame) {
      +            err = AVERROR(ENOMEM);
      +            goto end;
     -         }
     --        av_fifo_generic_write(ist->sub2video.sub_queue, &subtitle, sizeof(subtitle), NULL);
     --        free_sub = 0;
     --    }
     ++        }
       
     --    if (!subtitle.num_rects)
     --        goto out;
     +-        do_subtitle_out(output_files[ost->file_index], ost, &subtitle);
      +        err = send_frame_to_filters(ist, filter_frame);
      +        av_frame_free(&filter_frame);
     -+    }
     +     }
       
     --    ist->frames_decoded++;
     +-out:
     +-    if (free_sub)
     +-        avsubtitle_free(&subtitle);
     +-    return ret;
      +    if (err >= 0) {
      +        for (i = 0; i < nb_output_streams; i++) {
      +            OutputStream *ost = output_streams[i];
      +            InputStream *ist_src = get_input_stream(ost);
     - 
     --    for (i = 0; i < nb_output_streams; i++) {
     --        OutputStream *ost = output_streams[i];
     ++
      +            if (!ist_src || !check_output_constraints(ist, ost)
      +                || ist_src != ist
      +                || !ost->encoding_needed
      +                || ost->enc->type != AVMEDIA_TYPE_SUBTITLE)
      +                continue;
     - 
     --        if (!check_output_constraints(ist, ost) || !ost->encoding_needed
     --            || ost->enc->type != AVMEDIA_TYPE_SUBTITLE)
     --            continue;
     ++
      +            if (ost->filter && ost->filter->filter->nb_inputs > 0)
      +                continue;
     - 
     --        do_subtitle_out(output_files[ost->file_index], ost, &subtitle);
     ++
      +            do_subtitle_out(output_files[ost->file_index], ost, decoded_frame);
      +        }
     -     }
     - 
     --out:
     --    if (free_sub)
     --        avsubtitle_free(&subtitle);
     --    return ret;
     ++    }
     ++
      +end:
      +    av_frame_unref(decoded_frame);
      +    return err < 0 ? err : ret;
     @@ fftools/ffmpeg.c: static int process_input_packet(InputStream *ist, const AVPack
                   if (!pkt && ret >= 0)
                       ret = AVERROR_EOF;
                   av_packet_unref(avpkt);
     -@@ fftools/ffmpeg.c: FF_ENABLE_DEPRECATION_WARNINGS
     +@@ fftools/ffmpeg.c: static int init_input_stream(int ist_index, char *error, int error_len)
           return 0;
       }
       
     @@ fftools/ffmpeg.c: static int init_output_stream_encode(OutputStream *ost, AVFram
                   enc_ctx->width     = input_streams[ost->source_index]->st->codecpar->width;
                   enc_ctx->height    = input_streams[ost->source_index]->st->codecpar->height;
               }
     +@@ fftools/ffmpeg.c: static int init_output_stream_encode(OutputStream *ost, AVFrame *frame)
     +     return 0;
     + }
     + 
     ++static enum AVSubtitleType get_subtitle_format(const AVCodecDescriptor *codec_descriptor)
     ++{
     ++    if(codec_descriptor->props & AV_CODEC_PROP_BITMAP_SUB)
     ++        return AV_SUBTITLE_FMT_BITMAP;
     ++
     ++    if(codec_descriptor->props & AV_CODEC_PROP_TEXT_SUB)
     ++        return AV_SUBTITLE_FMT_ASS;
     ++
     ++    return AV_SUBTITLE_FMT_UNKNOWN;
     ++}
     ++
     + static int init_output_stream(OutputStream *ost, AVFrame *frame,
     +                               char *error, int error_len)
     + {
      @@ fftools/ffmpeg.c: static int init_output_stream(OutputStream *ost, AVFrame *frame,
               }
       
     @@ fftools/ffmpeg.c: static int init_output_stream(OutputStream *ost, AVFrame *fram
      -                snprintf(error, error_len,
      -                         "Subtitle encoding currently only possible from text to text "
      -                         "or bitmap to bitmap");
     -+            AVCodecDescriptor const *input_descriptor     = avcodec_descriptor_get(dec->codec_id);
      +            AVCodecDescriptor const *output_descriptor    = avcodec_descriptor_get(ost->enc_ctx->codec_id);
     -+            const enum AVSubtitleType in_subtitle_format  = output_descriptor ? avcodec_descriptor_get_subtitle_format(input_descriptor) : AV_SUBTITLE_FMT_UNKNOWN;
     -+            const enum AVSubtitleType out_subtitle_format = output_descriptor ? avcodec_descriptor_get_subtitle_format(output_descriptor) : AV_SUBTITLE_FMT_UNKNOWN;
     ++            const enum AVSubtitleType in_subtitle_format  = (uint16_t)dec->subtitle_type;
     ++            const enum AVSubtitleType out_subtitle_format = output_descriptor ? get_subtitle_format(output_descriptor) : AV_SUBTITLE_FMT_UNKNOWN;
      +
      +            if (ist->nb_filters == 0 && in_subtitle_format != AV_SUBTITLE_FMT_UNKNOWN && out_subtitle_format != AV_SUBTITLE_FMT_UNKNOWN
      +                && in_subtitle_format != out_subtitle_format) {
     @@ fftools/ffmpeg.c: static int init_output_stream(OutputStream *ost, AVFrame *fram
                       return AVERROR_INVALIDDATA;
                   }
               }
     -@@ fftools/ffmpeg.c: static int transcode_init(void)
     -     for (i = 0; i < nb_output_streams; i++) {
     -         if (!output_streams[i]->stream_copy &&
     -             (output_streams[i]->enc_ctx->codec_type == AVMEDIA_TYPE_VIDEO ||
     --             output_streams[i]->enc_ctx->codec_type == AVMEDIA_TYPE_AUDIO))
     -+             output_streams[i]->enc_ctx->codec_type == AVMEDIA_TYPE_AUDIO ||
     -+             output_streams[i]->enc_ctx->codec_type == AVMEDIA_TYPE_SUBTITLE))
     -             continue;
     +@@ fftools/ffmpeg.c: static OutputStream *choose_output(void)
     +                        av_rescale_q(ost->last_mux_dts, ost->st->time_base,
     +                                     AV_TIME_BASE_Q);
     +         if (ost->last_mux_dts == AV_NOPTS_VALUE)
     +-            av_log(NULL, AV_LOG_DEBUG,
     ++            av_log(NULL, AV_LOG_INFO,
     +                 "cur_dts is invalid st:%d (%d) [init:%d i_done:%d finish:%d] (this is harmless if it occurs once at the start per stream)\n",
     +                 ost->st->index, ost->st->id, ost->initialized, ost->inputs_done, ost->finished);
       
     -         ret = init_output_stream_wrapper(output_streams[i], NULL, 0);
      @@ fftools/ffmpeg.c: static int process_input(int file_index)
                      av_ts2timestr(input_files[ist->file_index]->ts_offset, &AV_TIME_BASE_Q));
           }
       
      -    sub2video_heartbeat(ist, pkt->pts);
     --
     ++    subtitle_kickoff(ist, pkt->pts);
     + 
           process_input_packet(ist, pkt, 0);
       
     - discard_packet:
     +@@ fftools/ffmpeg.c: static int transcode(void)
     +     /* at the end of stream, we must flush the decoder buffers */
     +     for (i = 0; i < nb_input_streams; i++) {
     +         ist = input_streams[i];
     ++        ist->subtitle_kickoff.is_active = 0;
     +         if (!input_files[ist->file_index]->eof_reached) {
     +             process_input_packet(ist, NULL, 0);
     +         }
      
       ## fftools/ffmpeg.h ##
      @@ fftools/ffmpeg.h: typedef struct InputStream {
     @@ fftools/ffmpeg.h: typedef struct InputStream {
           } prev_sub;
       
      -    struct sub2video {
     --        int64_t last_pts;
     ++    struct subtitle_kickoff {
     ++        int is_active;
     +         int64_t last_pts;
      -        int64_t end_pts;
     --        AVFifoBuffer *sub_queue;    ///< queue of AVSubtitle* before filter init
     +-        AVFifo *sub_queue;    ///< queue of AVSubtitle* before filter init
      -        AVFrame *frame;
     --        int w, h;
     +         int w, h;
      -        unsigned int initialize; ///< marks if sub2video_update should force an initialization
      -    } sub2video;
     ++    } subtitle_kickoff;
     ++
      +    AVBufferRef *subtitle_header;
       
           /* decoded data from this stream goes into all those filters
            * currently video and audio only */
     +@@ fftools/ffmpeg.h: typedef struct OutputStream {
     +     int64_t first_pts;
     +     /* dts of the last packet sent to the muxer */
     +     int64_t last_mux_dts;
     ++    /* subtitle_pts values of the last subtitle frame having arrived for encoding */
     ++    int64_t last_subtitle_pts;
     +     // the timebase of the packets sent to the muxer
     +     AVRational mux_timebase;
     +     AVRational enc_timebase;
      @@ fftools/ffmpeg.h: int filtergraph_is_simple(FilterGraph *fg);
       int init_simple_filtergraph(InputStream *ist, OutputStream *ost);
       int init_complex_filtergraph(FilterGraph *fg);
     @@ fftools/ffmpeg_filter.c: static void init_input_filter(FilterGraph *fg, AVFilter
                       continue;
                   if (check_stream_specifier(s, s->streams[i], *p == ':' ? p + 1 : p) == 1) {
                       st = s->streams[i];
     -@@ fftools/ffmpeg_filter.c: static void init_input_filter(FilterGraph *fg, AVFilterInOut *in)
     -     ifilter->type   = ist->st->codecpar->codec_type;
     -     ifilter->name   = describe_filter_link(fg, in, 1);
     - 
     -+    if (ist->st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE && ist->dec_ctx) {
     -+        const AVCodecDescriptor *codec_descriptor = ist->dec_ctx->codec_descriptor;
     -+        if (!codec_descriptor)
     -+            codec_descriptor = avcodec_descriptor_get(ist->dec_ctx->codec_id);
     -+
     -+        // For subtitles, we need to set the format here. Would we leave the format
     -+        // at -1, it would delay processing (ifilter_has_all_input_formats()) until
     -+        // the first subtile frame arrives, which could never happen in the worst case
     -+        fg->inputs[fg->nb_inputs - 1]->format = avcodec_descriptor_get_subtitle_format(codec_descriptor);
     -+    }
     -+
     -     ifilter->frame_queue = av_fifo_alloc(8 * sizeof(AVFrame*));
     -     if (!ifilter->frame_queue)
     -         exit_program(1);
      @@ fftools/ffmpeg_filter.c: static int insert_filter(AVFilterContext **last_filter, int *pad_idx,
           return 0;
       }
     @@ fftools/ffmpeg_filter.c: void check_filter_outputs(void)
      +        ret = AVERROR(EINVAL);
      +        goto fail;
      +    }
     ++
     ++    if (ist->st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE && ist->dec_ctx) {
     ++        // For subtitles, we need to set the format here. Would we leave the format
     ++        // at -1, it would delay processing (ifilter_has_all_input_formats()) until
     ++        // the first subtile frame arrives, which could never happen in the worst case
     ++        ifilter->format = (uint16_t)ist->dec_ctx->subtitle_type;
     ++    }
     ++
     ++    ist->subtitle_kickoff.is_active = 1;
       
      -    /* Compute the size of the canvas for the subtitles stream.
      -       If the subtitles codecpar has set a size, use it. Otherwise use the
     @@ fftools/ffmpeg_filter.c: void check_filter_outputs(void)
      -        av_log(avf, AV_LOG_INFO, "sub2video: using %dx%d canvas\n", w, h);
      +        w = ist->dec_ctx->width;
      +        h = ist->dec_ctx->height;
     -     }
     --    ist->sub2video.w = ifilter->width  = w;
     --    ist->sub2video.h = ifilter->height = h;
     - 
     --    ifilter->width  = ist->dec_ctx->width  ? ist->dec_ctx->width  : ist->sub2video.w;
     --    ifilter->height = ist->dec_ctx->height ? ist->dec_ctx->height : ist->sub2video.h;
     ++    }
     ++
      +    if (!(w && h) && ist->dec_ctx->subtitle_header) {
      +        ASSSplitContext *ass_ctx = avpriv_ass_split((char *)ist->dec_ctx->subtitle_header);
      +        ASS *ass = (ASS *)ass_ctx;
      +        w = ass->script_info.play_res_x;
      +        h = ass->script_info.play_res_y;
      +        avpriv_ass_split_free(ass_ctx);
     -+    }
     +     }
     +-    ist->sub2video.w = ifilter->width  = w;
     +-    ist->sub2video.h = ifilter->height = h;
     + 
     +-    ifilter->width  = ist->dec_ctx->width  ? ist->dec_ctx->width  : ist->sub2video.w;
     +-    ifilter->height = ist->dec_ctx->height ? ist->dec_ctx->height : ist->sub2video.h;
     ++    ist->subtitle_kickoff.w = w;
     ++    ist->subtitle_kickoff.h = h;
     ++    av_log(ifilter, AV_LOG_INFO, "subtitle input filter: decoding size %dx%d\n", ist->subtitle_kickoff.w, ist->subtitle_kickoff.h);
       
      -    /* rectangles are AV_PIX_FMT_PAL8, but we have no guarantee that the
      -       palettes for all rectangles are identical or compatible */
     @@ fftools/ffmpeg_filter.c: void check_filter_outputs(void)
      -        return AVERROR(ENOMEM);
      -    ist->sub2video.last_pts = INT64_MIN;
      -    ist->sub2video.end_pts  = INT64_MIN;
     ++    ist->subtitle_kickoff.last_pts = INT64_MIN;
     ++
      +    snprintf(name, sizeof(name), "graph %d subtitle input from stream %d:%d", fg->index,
      +             ist->file_index, ist->st->index);
      +
     @@ fftools/ffmpeg_filter.c: void check_filter_outputs(void)
      +    if ((ret = avfilter_graph_create_filter(&ifilter->filter, buffer_filt, name,
      +                                            args.str, NULL, fg->graph)) < 0)
      +        goto fail;
     -+
     + 
     +-    /* sub2video structure has been (re-)initialized.
     +-       Mark it as such so that the system will be
     +-       initialized with the first received heartbeat. */
     +-    ist->sub2video.initialize = 1;
      +    par->hw_frames_ctx = ifilter->hw_frames_ctx;
      +    par->format = ifilter->format;
      +    par->width = ifilter->width;
     @@ fftools/ffmpeg_filter.c: void check_filter_outputs(void)
      +            if (ret < 0)
      +                return ret;
      +        }
     - 
     --    /* sub2video structure has been (re-)initialized.
     --       Mark it as such so that the system will be
     --       initialized with the first received heartbeat. */
     --    ist->sub2video.initialize = 1;
     ++
      +        av_log(NULL, AV_LOG_INFO, "Auto-inserting graphicsub2video filter\n");
      +        ret = insert_filter(&last_filter, &pad_idx, "graphicsub2video", NULL);
      +        if (ret < 0)
     @@ fftools/ffmpeg_filter.c: static int configure_input_filter(FilterGraph *fg, Inpu
           default: av_assert0(0); return 0;
           }
       }
     +@@ fftools/ffmpeg_filter.c: static void cleanup_filtergraph(FilterGraph *fg)
     + static int filter_is_buffersrc(const AVFilterContext *f)
     + {
     +     return f->nb_inputs == 0 &&
     +-           (!strcmp(f->filter->name, "buffer") ||
     +-            !strcmp(f->filter->name, "abuffer"));
     ++           (!strcmp(f->filter->name, "buffersrc") ||
     ++            !strcmp(f->filter->name, "abuffersrc") ||
     ++            !strcmp(f->filter->name, "sbuffersrc"));
     + }
     + 
     + static int graph_is_meta(AVFilterGraph *graph)
      @@ fftools/ffmpeg_filter.c: int configure_filtergraph(FilterGraph *fg)
               }
           }
     @@ fftools/ffmpeg_filter.c: int configure_filtergraph(FilterGraph *fg)
      -    for (i = 0; i < fg->nb_inputs; i++) {
      -        InputStream *ist = fg->inputs[i]->ist;
      -        if (ist->sub2video.sub_queue && ist->sub2video.frame) {
     --            while (av_fifo_size(ist->sub2video.sub_queue)) {
     --                AVSubtitle tmp;
     --                av_fifo_generic_read(ist->sub2video.sub_queue, &tmp, sizeof(tmp), NULL);
     +-            AVSubtitle tmp;
     +-            while (av_fifo_read(ist->sub2video.sub_queue, &tmp, 1) >= 0) {
      -                sub2video_update(ist, INT64_MIN, &tmp);
      -                avsubtitle_free(&tmp);
      -            }
     @@ fftools/ffmpeg_filter.c: int configure_filtergraph(FilterGraph *fg)
       
       fail:
      @@ fftools/ffmpeg_filter.c: int ifilter_parameters_from_frame(InputFilter *ifilter, const AVFrame *frame)
     -     ifilter->sample_rate         = frame->sample_rate;
     -     ifilter->channels            = frame->channels;
     -     ifilter->channel_layout      = frame->channel_layout;
     +     ifilter->width               = frame->width;
     +     ifilter->height              = frame->height;
     +     ifilter->sample_aspect_ratio = frame->sample_aspect_ratio;
      +    ifilter->type                = frame->type;
       
     -     av_freep(&ifilter->displaymatrix);
     -     sd = av_frame_get_side_data(frame, AV_FRAME_DATA_DISPLAYMATRIX);
     +     ifilter->sample_rate         = frame->sample_rate;
     +     ret = av_channel_layout_copy(&ifilter->ch_layout, &frame->ch_layout);
      
       ## fftools/ffmpeg_hw.c ##
      @@ fftools/ffmpeg_hw.c: int hw_device_setup_for_encode(OutputStream *ost)
     @@ fftools/ffmpeg_hw.c: int hw_device_setup_for_encode(OutputStream *ost)
                   ((AVHWFramesContext*)frames_ref->data)->format ==
      
       ## fftools/ffmpeg_opt.c ##
     +@@ fftools/ffmpeg_opt.c: static void add_input_streams(OptionsContext *o, AVFormatContext *ic)
     +                 av_log(NULL, AV_LOG_FATAL, "Invalid canvas size: %s.\n", canvas_size);
     +                 exit_program(1);
     +             }
     ++            ist->subtitle_kickoff.is_active = 1;
     +             break;
     +         }
     +         case AVMEDIA_TYPE_ATTACHMENT:
      @@ fftools/ffmpeg_opt.c: static void init_output_filter(OutputFilter *ofilter, OptionsContext *o,
           switch (ofilter->type) {
           case AVMEDIA_TYPE_VIDEO: ost = new_video_stream(o, oc, -1); break;
     @@ tests/ref/fate/sub2video
      +0,        254,        254,        1,   518400, 0x7a11c812
      +0,        255,        255,        1,   518400, 0x7a11c812
      +0,        256,        256,        1,   518400, 0x7a11c812
     -+0,        257,        257,        1,   518400, 0x7a11c812
     ++0,        257,        257,        1,   518400, 0x34cdddee
       1,   51433000,   51433000,  2366000,     3059, 0xc95b8a05
       0,        258,        258,        1,   518400, 0x34cdddee
      -0,        269,        269,        1,   518400, 0xbab197ea
     @@ tests/ref/fate/sub2video
      +0,        280,        280,        1,   518400, 0x4db4ce51
      +0,        281,        281,        1,   518400, 0x4db4ce51
      +0,        282,        282,        1,   518400, 0x4db4ce51
     -+0,        283,        283,        1,   518400, 0x4db4ce51
     ++0,        283,        283,        1,   518400, 0xe6bc0ea9
       1,   56663000,   56663000,  1262000,     1013, 0xc9ae89b7
       0,        284,        284,        1,   518400, 0xe6bc0ea9
      -0,        290,        290,        1,   518400, 0xbab197ea
     @@ tests/ref/fate/sub2video
      +0,        287,        287,        1,   518400, 0xe6bc0ea9
      +0,        288,        288,        1,   518400, 0xe6bc0ea9
      +0,        289,        289,        1,   518400, 0xe6bc0ea9
     -+0,        290,        290,        1,   518400, 0xe6bc0ea9
     ++0,        290,        290,        1,   518400, 0xa8643af7
       1,   58014000,   58014000,  1661000,      969, 0xe01878f0
       0,        291,        291,        1,   518400, 0xa8643af7
      -0,        298,        298,        1,   518400, 0xbab197ea
     @@ tests/ref/fate/sub2video
      +0,        351,        351,        1,   518400, 0x378e3fd0
      +0,        352,        352,        1,   518400, 0x378e3fd0
      +0,        353,        353,        1,   518400, 0x378e3fd0
     -+0,        354,        354,        1,   518400, 0x378e3fd0
     ++0,        354,        354,        1,   518400, 0xa3782469
       1,   70819000,   70819000,  1865000,     1709, 0xb4d5a1bd
       0,        355,        355,        1,   518400, 0xa3782469
      -0,        363,        363,        1,   518400, 0xbab197ea
     @@ tests/ref/fate/sub2video
      +0,        371,        371,        1,   518400, 0xba23a0d5
      +0,        372,        372,        1,   518400, 0xba23a0d5
      +0,        373,        373,        1,   518400, 0xba23a0d5
     -+0,        374,        374,        1,   518400, 0xba23a0d5
     ++0,        374,        374,        1,   518400, 0x129de2f8
       1,   74806000,   74806000,  1831000,     2116, 0x96514097
       0,        375,        375,        1,   518400, 0x129de2f8
      -0,        383,        383,        1,   518400, 0xbab197ea
     @@ tests/ref/fate/sub2video
      +0,        387,        387,        1,   518400, 0x19772f0f
      +0,        388,        388,        1,   518400, 0x19772f0f
      +0,        389,        389,        1,   518400, 0x19772f0f
     -+0,        390,        390,        1,   518400, 0x19772f0f
     ++0,        390,        390,        1,   518400, 0x56f54e73
       1,   78051000,   78051000,  1524000,      987, 0x7b927a27
       0,        391,        391,        1,   518400, 0x56f54e73
      -0,        398,        398,        1,   518400, 0xbab197ea
     @@ tests/ref/fate/sub2video
      +0,        395,        395,        1,   518400, 0x56f54e73
      +0,        396,        396,        1,   518400, 0x56f54e73
      +0,        397,        397,        1,   518400, 0x56f54e73
     -+0,        398,        398,        1,   518400, 0x56f54e73
     ++0,        398,        398,        1,   518400, 0x300b5247
       1,   79644000,   79644000,  2662000,     2956, 0x190778f7
       0,        399,        399,        1,   518400, 0x300b5247
      +0,        400,        400,        1,   518400, 0x300b5247
     @@ tests/ref/fate/sub2video
      +0,        411,        411,        1,   518400, 0x300b5247
       1,   82380000,   82380000,  2764000,     3094, 0xc021b7d3
      -0,        412,        412,        1,   518400, 0xbab197ea
     -+0,        412,        412,        1,   518400, 0x300b5247
     ++0,        412,        412,        1,   518400, 0x6fd028fa
       0,        413,        413,        1,   518400, 0x6fd028fa
      -0,        426,        426,        1,   518400, 0xbab197ea
      +0,        414,        414,        1,   518400, 0x6fd028fa
     @@ tests/ref/fate/sub2video
      +0,        423,        423,        1,   518400, 0x6fd028fa
      +0,        424,        424,        1,   518400, 0x6fd028fa
      +0,        425,        425,        1,   518400, 0x6fd028fa
     -+0,        426,        426,        1,   518400, 0x6fd028fa
     ++0,        426,        426,        1,   518400, 0x01f80e9d
       1,   85225000,   85225000,  2366000,     2585, 0x74d0048f
       0,        427,        427,        1,   518400, 0x01f80e9d
      -0,        438,        438,        1,   518400, 0xbab197ea
     @@ tests/ref/fate/sub2video
      +0,        435,        435,        1,   518400, 0x01f80e9d
      +0,        436,        436,        1,   518400, 0x01f80e9d
      +0,        437,        437,        1,   518400, 0x01f80e9d
     -+0,        438,        438,        1,   518400, 0x01f80e9d
     ++0,        438,        438,        1,   518400, 0xb48d90c0
       1,   87652000,   87652000,  1831000,      634, 0x8832fda1
       0,        439,        439,        1,   518400, 0xb48d90c0
      -0,        447,        447,        1,   518400, 0xbab197ea
     @@ tests/ref/fate/sub2video
      +0,        491,        491,        1,   518400, 0xb8a323e4
      +0,        492,        492,        1,   518400, 0xb8a323e4
      +0,        493,        493,        1,   518400, 0xb8a323e4
     -+0,        494,        494,        1,   518400, 0xb8a323e4
     ++0,        494,        494,        1,   518400, 0xc43518ba
       1,   98872000,   98872000,  2161000,     1875, 0x9002ef71
       0,        495,        495,        1,   518400, 0xc43518ba
      -0,        505,        505,        1,   518400, 0xbab197ea
     @@ tests/ref/fate/sub2video
      +0,        847,        847,        1,   518400, 0x03ea0e90
      +0,        848,        848,        1,   518400, 0x03ea0e90
      +0,        849,        849,        1,   518400, 0x03ea0e90
     -+0,        850,        850,        1,   518400, 0x03ea0e90
     ++0,        850,        850,        1,   518400, 0x8780239e
       1,  170035000,  170035000,  1524000,     1279, 0xb6c2dafe
       0,        851,        851,        1,   518400, 0x8780239e
      -0,        858,        858,        1,   518400, 0xbab197ea
     @@ tests/ref/fate/sub2video
      +0,        960,        960,        1,   518400, 0x59f4e72f
      +0,        961,        961,        1,   518400, 0x59f4e72f
      +0,        962,        962,        1,   518400, 0x59f4e72f
     -+0,        963,        963,        1,   518400, 0x59f4e72f
     ++0,        963,        963,        1,   518400, 0x9d5b9d69
       1,  192640000,  192640000,  1763000,     2506, 0xa458d6d4
       0,        964,        964,        1,   518400, 0x9d5b9d69
      -0,        972,        972,        1,   518400, 0xbab197ea
     @@ tests/ref/fate/sub2video
      +0,        993,        993,        1,   518400, 0x25113966
      +0,        994,        994,        1,   518400, 0x25113966
      +0,        995,        995,        1,   518400, 0x25113966
     -+0,        996,        996,        1,   518400, 0x25113966
     ++0,        996,        996,        1,   518400, 0x2dc83609
       1,  199230000,  199230000,  1627000,     1846, 0xeea8c599
       0,        997,        997,        1,   518400, 0x2dc83609
      -0,       1004,       1004,        1,   518400, 0xbab197ea
     @@ tests/ref/fate/sub2video
      +0,       1136,       1136,        1,   518400, 0x81e977b2
      +0,       1137,       1137,        1,   518400, 0x81e977b2
      +0,       1138,       1138,        1,   518400, 0x81e977b2
     -+0,       1139,       1139,        1,   518400, 0x81e977b2
     ++0,       1139,       1139,        1,   518400, 0xb046dd30
       1,  227834000,  227834000,  1262000,     1264, 0xc1d9fc57
       0,       1140,       1140,        1,   518400, 0xb046dd30
      -0,       1145,       1145,        1,   518400, 0xbab197ea
     @@ tests/ref/fate/sub2video_basic
      -0,       8996,       8996,        1,  1382400, 0xda2ceb55
      -0,       9027,       9027,        1,  1382400, 0x00000000
      +#sar 0: 1/1
     -+0,          0,          0,        1,  1382400, 0x00000000
      +0,        662,        662,        1,  1382400, 0xc637b893
      +0,        663,        663,        1,  1382400, 0xc637b893
      +0,        664,        664,        1,  1382400, 0xc637b893
     @@ tests/ref/fate/sub2video_time_limited
      -#sar 0: 0/1
      -0,          2,          2,        1,  8294400, 0x00000000
      +#sar 0: 1/1
     -+0,          0,          0,        1,  8294400, 0x00000000
     -+0,          1,          1,        1,  8294400, 0x00000000
     ++0,          0,          0,        1,  8294400, 0xa87c518f
     ++0,          1,          1,        1,  8294400, 0xa87c518f
       0,          2,          2,        1,  8294400, 0xa87c518f
      +0,          3,          3,        1,  8294400, 0xa87c518f
      +0,          4,          4,        1,  8294400, 0xa87c518f
 22:  d3cdd2a0e2 <  -:  ---------- avutil/ass_split: Add parsing of hard-space tags (\h)
 23:  5fca566749 <  -:  ---------- avcodec/webvttenc: convert hard-space tags to &nbsp;
 24:  93b469b8d0 <  -:  ---------- doc/APIchanges: update for subtitle filtering changes
 25:  0c279550d6 <  -:  ---------- avcodec/webvttenc: Don't encode drawing codes and empty lines
 26:  03e1a98e08 ! 23:  a66debd96e avcodec/dvbsubdec: Fix conditions for fallback to default resolution
     @@ libavcodec/dvbsubdec.c
       
       #define cm (ff_crop_tab + MAX_NEG_CROP)
       
     -@@ libavcodec/dvbsubdec.c: static int dvbsub_decode(AVCodecContext *avctx,
     +@@ libavcodec/dvbsubdec.c: static int dvbsub_decode(AVCodecContext *avctx, AVSubtitle *sub,
           int segment_length;
           int i;
           int ret = 0;
     @@ libavcodec/dvbsubdec.c: static int dvbsub_decode(AVCodecContext *avctx,
       
           ff_dlog(avctx, "DVB sub packet:\n");
       
     -@@ libavcodec/dvbsubdec.c: static int dvbsub_decode(AVCodecContext *avctx,
     +@@ libavcodec/dvbsubdec.c: static int dvbsub_decode(AVCodecContext *avctx, AVSubtitle *sub,
                   switch (segment_type) {
                   case DVBSUB_PAGE_SEGMENT:
                       ret = dvbsub_parse_page_segment(avctx, p, segment_length, sub, got_sub_ptr);
     @@ libavcodec/dvbsubdec.c: static int dvbsub_decode(AVCodecContext *avctx,
                       break;
                   default:
                       ff_dlog(avctx, "Subtitling segment type 0x%x, page id %d, length %d\n",
     -@@ libavcodec/dvbsubdec.c: static int dvbsub_decode(AVCodecContext *avctx,
     +@@ libavcodec/dvbsubdec.c: static int dvbsub_decode(AVCodecContext *avctx, AVSubtitle *sub,
       
               p += segment_length;
           }

-- 
ffmpeg-codebot
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v4 01/23] avcodec, avutil: Move enum AVSubtitleType to avutil, add new and deprecate old values
  2022-05-28 13:25     ` [FFmpeg-devel] [PATCH v4 00/23] Subtitle Filtering 2022 ffmpegagent
@ 2022-05-28 13:25       ` softworkz
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 02/23] avutil/frame: Prepare AVFrame for subtitle handling softworkz
                         ` (22 subsequent siblings)
  23 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-05-28 13:25 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/avcodec.h | 19 +------------
 libavutil/Makefile   |  1 +
 libavutil/subfmt.h   | 68 ++++++++++++++++++++++++++++++++++++++++++++
 libavutil/version.h  |  1 +
 4 files changed, 71 insertions(+), 18 deletions(-)
 create mode 100644 libavutil/subfmt.h

diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index 4dae23d06e..56d551f92d 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -35,6 +35,7 @@
 #include "libavutil/frame.h"
 #include "libavutil/log.h"
 #include "libavutil/pixfmt.h"
+#include "libavutil/subfmt.h"
 #include "libavutil/rational.h"
 
 #include "codec.h"
@@ -2255,24 +2256,6 @@ typedef struct AVHWAccel {
  * @}
  */
 
-enum AVSubtitleType {
-    SUBTITLE_NONE,
-
-    SUBTITLE_BITMAP,                ///< A bitmap, pict will be set
-
-    /**
-     * Plain text, the text field must be set by the decoder and is
-     * authoritative. ass and pict fields may contain approximations.
-     */
-    SUBTITLE_TEXT,
-
-    /**
-     * Formatted text, the ass field must be set by the decoder and is
-     * authoritative. pict and text fields may contain approximations.
-     */
-    SUBTITLE_ASS,
-};
-
 #define AV_SUBTITLE_FLAG_FORCED 0x00000001
 
 typedef struct AVSubtitleRect {
diff --git a/libavutil/Makefile b/libavutil/Makefile
index 234de62a4b..5fcdd68ca8 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -76,6 +76,7 @@ HEADERS = adler32.h                                                     \
           sha512.h                                                      \
           spherical.h                                                   \
           stereo3d.h                                                    \
+          subfmt.h                                                      \
           threadmessage.h                                               \
           time.h                                                        \
           timecode.h                                                    \
diff --git a/libavutil/subfmt.h b/libavutil/subfmt.h
new file mode 100644
index 0000000000..791b45519f
--- /dev/null
+++ b/libavutil/subfmt.h
@@ -0,0 +1,68 @@
+/*
+ * 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
+ */
+
+#ifndef AVUTIL_SUBFMT_H
+#define AVUTIL_SUBFMT_H
+
+#include "version.h"
+
+enum AVSubtitleType {
+
+    /**
+     * Subtitle format unknown.
+     */
+    AV_SUBTITLE_FMT_NONE = -1,
+
+    /**
+     * Subtitle format unknown.
+     */
+    AV_SUBTITLE_FMT_UNKNOWN = 0,
+#if FF_API_OLD_SUBTITLES
+    SUBTITLE_NONE = 0,          ///< Deprecated, use AV_SUBTITLE_FMT_NONE instead.
+#endif
+
+    /**
+     * Bitmap area in AVSubtitleRect.data, pixfmt AV_PIX_FMT_PAL8.
+     */
+    AV_SUBTITLE_FMT_BITMAP = 1,
+#if FF_API_OLD_SUBTITLES
+    SUBTITLE_BITMAP = 1,        ///< Deprecated, use AV_SUBTITLE_FMT_BITMAP instead.
+#endif
+
+    /**
+     * Plain text in AVSubtitleRect.text.
+     */
+    AV_SUBTITLE_FMT_TEXT = 2,
+#if FF_API_OLD_SUBTITLES
+    SUBTITLE_TEXT = 2,          ///< Deprecated, use AV_SUBTITLE_FMT_TEXT instead.
+#endif
+
+    /**
+     * Text Formatted as per ASS specification, contained AVSubtitleRect.ass.
+     */
+    AV_SUBTITLE_FMT_ASS = 3,
+#if FF_API_OLD_SUBTITLES
+    SUBTITLE_ASS = 3,           ///< Deprecated, use AV_SUBTITLE_FMT_ASS instead.
+#endif
+
+    AV_SUBTITLE_FMT_NB,         ///< number of subtitle formats, DO NOT USE THIS if you want to link with shared libav* because the number of formats might differ between versions.
+};
+
+#endif /* AVUTIL_SUBFMT_H */
diff --git a/libavutil/version.h b/libavutil/version.h
index 1b4b41d81f..11baa362d3 100644
--- a/libavutil/version.h
+++ b/libavutil/version.h
@@ -114,6 +114,7 @@
 #define FF_API_XVMC                     (LIBAVUTIL_VERSION_MAJOR < 58)
 #define FF_API_OLD_CHANNEL_LAYOUT       (LIBAVUTIL_VERSION_MAJOR < 58)
 #define FF_API_AV_FOPEN_UTF8            (LIBAVUTIL_VERSION_MAJOR < 58)
+#define FF_API_OLD_SUBTITLES            (LIBAVUTIL_VERSION_MAJOR < 58)
 
 /**
  * @}
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v4 02/23] avutil/frame: Prepare AVFrame for subtitle handling
  2022-05-28 13:25     ` [FFmpeg-devel] [PATCH v4 00/23] Subtitle Filtering 2022 ffmpegagent
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 01/23] avcodec, avutil: Move enum AVSubtitleType to avutil, add new and deprecate old values softworkz
@ 2022-05-28 13:25       ` softworkz
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 03/23] avcodec/subtitles: Introduce new frame-based subtitle decoding API softworkz
                         ` (21 subsequent siblings)
  23 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-05-28 13:25 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Root commit for adding subtitle filtering capabilities.
In detail:

- Add type (AVMediaType) field to AVFrame
  Replaces previous way of distinction which was based on checking
  width and height to determine whether a frame is audio or video
- Add subtitle fields to AVFrame
- Add new struct AVSubtitleArea, similar to AVSubtitleRect, but
  different allocation logic. Cannot and must not be used
  interchangeably, hence the new struct

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavutil/Makefile |   1 +
 libavutil/frame.c  | 211 +++++++++++++++++++++++++++++++++++++++------
 libavutil/frame.h  |  85 +++++++++++++++++-
 libavutil/subfmt.c |  45 ++++++++++
 libavutil/subfmt.h |  47 ++++++++++
 5 files changed, 362 insertions(+), 27 deletions(-)
 create mode 100644 libavutil/subfmt.c

diff --git a/libavutil/Makefile b/libavutil/Makefile
index 5fcdd68ca8..78f65c144a 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -162,6 +162,7 @@ OBJS = adler32.o                                                        \
        slicethread.o                                                    \
        spherical.o                                                      \
        stereo3d.o                                                       \
+       subfmt.o                                                         \
        threadmessage.o                                                  \
        time.o                                                           \
        timecode.o                                                       \
diff --git a/libavutil/frame.c b/libavutil/frame.c
index fbb869fffa..b24fdc5868 100644
--- a/libavutil/frame.c
+++ b/libavutil/frame.c
@@ -26,6 +26,7 @@
 #include "imgutils.h"
 #include "mem.h"
 #include "samplefmt.h"
+#include "subfmt.h"
 #include "hwcontext.h"
 
 #if FF_API_OLD_CHANNEL_LAYOUT
@@ -52,6 +53,9 @@ const char *av_get_colorspace_name(enum AVColorSpace val)
     return name[val];
 }
 #endif
+
+static int frame_copy_subtitles(AVFrame *dst, const AVFrame *src, int copy_data);
+
 static void get_frame_defaults(AVFrame *frame)
 {
     memset(frame, 0, sizeof(*frame));
@@ -72,7 +76,12 @@ static void get_frame_defaults(AVFrame *frame)
     frame->colorspace          = AVCOL_SPC_UNSPECIFIED;
     frame->color_range         = AVCOL_RANGE_UNSPECIFIED;
     frame->chroma_location     = AVCHROMA_LOC_UNSPECIFIED;
-    frame->flags               = 0;
+    frame->num_subtitle_areas  = 0;
+    frame->subtitle_areas      = NULL;
+    frame->subtitle_header     = NULL;
+    frame->repeat_sub          = 0;
+    frame->subtitle_timing.start_pts = 0;
+    frame->subtitle_timing.duration  = 0;
 }
 
 static void free_side_data(AVFrameSideData **ptr_sd)
@@ -251,6 +260,23 @@ FF_ENABLE_DEPRECATION_WARNINGS
 
 }
 
+static int get_subtitle_buffer(AVFrame *frame)
+{
+    // Buffers in AVFrame->buf[] are not used in case of subtitle frames.
+    // To accomodate with existing code, checking ->buf[0] to determine
+    // whether a frame is ref-counted or has data, we're adding a 1-byte
+    // buffer here, which marks the subtitle frame to contain data.
+    frame->buf[0] = av_buffer_alloc(1);
+    if (!frame->buf[0]) {
+        av_frame_unref(frame);
+        return AVERROR(ENOMEM);
+    }
+
+    frame->extended_data = frame->data;
+
+    return 0;
+}
+
 int av_frame_get_buffer(AVFrame *frame, int align)
 {
     if (frame->format < 0)
@@ -258,23 +284,41 @@ int av_frame_get_buffer(AVFrame *frame, int align)
 
 FF_DISABLE_DEPRECATION_WARNINGS
     if (frame->width > 0 && frame->height > 0)
-        return get_video_buffer(frame, align);
+        frame->type = AVMEDIA_TYPE_VIDEO;
     else if (frame->nb_samples > 0 &&
              (av_channel_layout_check(&frame->ch_layout)
 #if FF_API_OLD_CHANNEL_LAYOUT
               || frame->channel_layout || frame->channels > 0
 #endif
              ))
-        return get_audio_buffer(frame, align);
+        frame->type = AVMEDIA_TYPE_AUDIO;
 FF_ENABLE_DEPRECATION_WARNINGS
 
-    return AVERROR(EINVAL);
+    return av_frame_get_buffer2(frame, align);
+}
+
+int av_frame_get_buffer2(AVFrame *frame, int align)
+{
+    if (frame->format < 0)
+        return AVERROR(EINVAL);
+
+    switch (frame->type) {
+    case AVMEDIA_TYPE_VIDEO:
+        return get_video_buffer(frame, align);
+    case AVMEDIA_TYPE_AUDIO:
+        return get_audio_buffer(frame, align);
+    case AVMEDIA_TYPE_SUBTITLE:
+        return get_subtitle_buffer(frame);
+    default:
+        return AVERROR(EINVAL);
+    }
 }
 
 static int frame_copy_props(AVFrame *dst, const AVFrame *src, int force_copy)
 {
     int ret, i;
 
+    dst->type                   = src->type;
     dst->key_frame              = src->key_frame;
     dst->pict_type              = src->pict_type;
     dst->sample_aspect_ratio    = src->sample_aspect_ratio;
@@ -306,6 +350,12 @@ static int frame_copy_props(AVFrame *dst, const AVFrame *src, int force_copy)
     dst->colorspace             = src->colorspace;
     dst->color_range            = src->color_range;
     dst->chroma_location        = src->chroma_location;
+    dst->repeat_sub             = src->repeat_sub;
+    dst->subtitle_timing.start_pts = src->subtitle_timing.start_pts;
+    dst->subtitle_timing.duration  = src->subtitle_timing.duration;
+
+    if ((ret = av_buffer_replace(&dst->subtitle_header, src->subtitle_header)) < 0)
+        return ret;
 
     av_dict_copy(&dst->metadata, src->metadata, 0);
 
@@ -353,6 +403,7 @@ FF_ENABLE_DEPRECATION_WARNINGS
     av_assert1(dst->ch_layout.nb_channels == 0 &&
                dst->ch_layout.order == AV_CHANNEL_ORDER_UNSPEC);
 
+    dst->type           = src->type;
     dst->format         = src->format;
     dst->width          = src->width;
     dst->height         = src->height;
@@ -385,7 +436,7 @@ FF_ENABLE_DEPRECATION_WARNINGS
 
     /* duplicate the frame data if it's not refcounted */
     if (!src->buf[0]) {
-        ret = av_frame_get_buffer(dst, 0);
+        ret = av_frame_get_buffer2(dst, 0);
         if (ret < 0)
             goto fail;
 
@@ -407,6 +458,10 @@ FF_ENABLE_DEPRECATION_WARNINGS
         }
     }
 
+    /* copy subtitle areas */
+    if (src->type == AVMEDIA_TYPE_SUBTITLE)
+        frame_copy_subtitles(dst, src, 0);
+
     if (src->extended_buf) {
         dst->extended_buf = av_calloc(src->nb_extended_buf,
                                       sizeof(*dst->extended_buf));
@@ -476,7 +531,7 @@ AVFrame *av_frame_clone(const AVFrame *src)
 
 void av_frame_unref(AVFrame *frame)
 {
-    int i;
+    unsigned i, n;
 
     if (!frame)
         return;
@@ -495,6 +550,21 @@ void av_frame_unref(AVFrame *frame)
     av_buffer_unref(&frame->opaque_ref);
     av_buffer_unref(&frame->private_ref);
 
+    av_buffer_unref(&frame->subtitle_header);
+
+    for (i = 0; i < frame->num_subtitle_areas; i++) {
+        AVSubtitleArea *area = frame->subtitle_areas[i];
+
+        for (n = 0; n < FF_ARRAY_ELEMS(area->buf); n++)
+            av_buffer_unref(&area->buf[n]);
+
+        av_freep(&area->text);
+        av_freep(&area->ass);
+        av_free(area);
+    }
+
+    av_freep(&frame->subtitle_areas);
+
     if (frame->extended_data != frame->data)
         av_freep(&frame->extended_data);
 
@@ -522,18 +592,28 @@ FF_ENABLE_DEPRECATION_WARNINGS
 
 int av_frame_is_writable(AVFrame *frame)
 {
-    int i, ret = 1;
+    AVSubtitleArea *area;
+    int ret = 1;
 
     /* assume non-refcounted frames are not writable */
     if (!frame->buf[0])
         return 0;
 
-    for (i = 0; i < FF_ARRAY_ELEMS(frame->buf); i++)
+    for (unsigned i = 0; i < FF_ARRAY_ELEMS(frame->buf); i++)
         if (frame->buf[i])
             ret &= !!av_buffer_is_writable(frame->buf[i]);
-    for (i = 0; i < frame->nb_extended_buf; i++)
+    for (unsigned i = 0; i < frame->nb_extended_buf; i++)
         ret &= !!av_buffer_is_writable(frame->extended_buf[i]);
 
+    for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+        area = frame->subtitle_areas[i];
+        if (area) {
+            for (unsigned n = 0; n < FF_ARRAY_ELEMS(area->buf); n++)
+                if (area->buf[n])
+                    ret &= !!av_buffer_is_writable(area->buf[n]);
+            }
+    }
+
     return ret;
 }
 
@@ -549,6 +629,7 @@ int av_frame_make_writable(AVFrame *frame)
         return 0;
 
     memset(&tmp, 0, sizeof(tmp));
+    tmp.type           = frame->type;
     tmp.format         = frame->format;
     tmp.width          = frame->width;
     tmp.height         = frame->height;
@@ -568,7 +649,7 @@ FF_ENABLE_DEPRECATION_WARNINGS
     if (frame->hw_frames_ctx)
         ret = av_hwframe_get_buffer(frame->hw_frames_ctx, &tmp, 0);
     else
-        ret = av_frame_get_buffer(&tmp, 0);
+        ret = av_frame_get_buffer2(&tmp, 0);
     if (ret < 0)
         return ret;
 
@@ -603,7 +684,12 @@ AVBufferRef *av_frame_get_plane_buffer(AVFrame *frame, int plane)
     uint8_t *data;
     int planes, i;
 
-    if (frame->nb_samples) {
+    switch(frame->type) {
+    case AVMEDIA_TYPE_VIDEO:
+        planes = 4;
+        break;
+    case AVMEDIA_TYPE_AUDIO:
+        {
         int channels = frame->ch_layout.nb_channels;
 
 #if FF_API_OLD_CHANNEL_LAYOUT
@@ -617,8 +703,11 @@ FF_ENABLE_DEPRECATION_WARNINGS
         if (!channels)
             return NULL;
         planes = av_sample_fmt_is_planar(frame->format) ? channels : 1;
-    } else
-        planes = 4;
+        break;
+        }
+    default:
+        return NULL;
+    }
 
     if (plane < 0 || plane >= planes || !frame->extended_data[plane])
         return NULL;
@@ -752,24 +841,98 @@ FF_ENABLE_DEPRECATION_WARNINGS
     return 0;
 }
 
+static int av_subtitle_area2area(AVSubtitleArea *dst, const AVSubtitleArea *src, int copy_data)
+{
+    dst->x         =  src->x;
+    dst->y         =  src->y;
+    dst->w         =  src->w;
+    dst->h         =  src->h;
+    dst->nb_colors =  src->nb_colors;
+    dst->type      =  src->type;
+    dst->flags     =  src->flags;
+
+    switch (dst->type) {
+    case AV_SUBTITLE_FMT_BITMAP:
+
+        for (unsigned i = 0; i < AV_NUM_BUFFER_POINTERS; i++) {
+            if (src->h > 0 && src->w > 0 && src->buf[i]) {
+                dst->buf[0] = av_buffer_ref(src->buf[i]);
+                if (!dst->buf[i])
+                    return AVERROR(ENOMEM);
+
+                if (copy_data) {
+                    const int ret = av_buffer_make_writable(&dst->buf[i]);
+                    if (ret < 0)
+                        return ret;
+                }
+
+                dst->linesize[i] = src->linesize[i];
+            }
+        }
+
+        memcpy(&dst->pal[0], &src->pal[0], sizeof(src->pal[0]) * 256);
+
+        break;
+    case AV_SUBTITLE_FMT_TEXT:
+
+        if (src->text) {
+            dst->text = av_strdup(src->text);
+            if (!dst->text)
+                return AVERROR(ENOMEM);
+        }
+
+        break;
+    case AV_SUBTITLE_FMT_ASS:
+
+        if (src->ass) {
+            dst->ass = av_strdup(src->ass);
+            if (!dst->ass)
+                return AVERROR(ENOMEM);
+        }
+
+        break;
+    default:
+
+        av_log(NULL, AV_LOG_ERROR, "Subtitle area has invalid format: %d", dst->type);
+        return AVERROR(ENOMEM);
+    }
+
+    return 0;
+}
+
+static int frame_copy_subtitles(AVFrame *dst, const AVFrame *src, int copy_data)
+{
+    dst->format = src->format;
+
+    dst->num_subtitle_areas = src->num_subtitle_areas;
+
+    if (src->num_subtitle_areas) {
+        dst->subtitle_areas = av_malloc_array(src->num_subtitle_areas, sizeof(AVSubtitleArea *));
+
+        for (unsigned i = 0; i < src->num_subtitle_areas; i++) {
+            dst->subtitle_areas[i] = av_mallocz(sizeof(AVSubtitleArea));
+            av_subtitle_area2area(dst->subtitle_areas[i], src->subtitle_areas[i], copy_data);
+        }
+    }
+
+    return 0;
+}
+
 int av_frame_copy(AVFrame *dst, const AVFrame *src)
 {
     if (dst->format != src->format || dst->format < 0)
         return AVERROR(EINVAL);
 
-FF_DISABLE_DEPRECATION_WARNINGS
-    if (dst->width > 0 && dst->height > 0)
+    switch(dst->type) {
+    case AVMEDIA_TYPE_VIDEO:
         return frame_copy_video(dst, src);
-    else if (dst->nb_samples > 0 &&
-             (av_channel_layout_check(&dst->ch_layout)
-#if FF_API_OLD_CHANNEL_LAYOUT
-              || dst->channel_layout || dst->channels
-#endif
-            ))
+    case AVMEDIA_TYPE_AUDIO:
         return frame_copy_audio(dst, src);
-FF_ENABLE_DEPRECATION_WARNINGS
-
-    return AVERROR(EINVAL);
+    case AVMEDIA_TYPE_SUBTITLE:
+        return frame_copy_subtitles(dst, src, 1);
+    default:
+        return AVERROR(EINVAL);
+    }
 }
 
 void av_frame_remove_side_data(AVFrame *frame, enum AVFrameSideDataType type)
diff --git a/libavutil/frame.h b/libavutil/frame.h
index 33fac2054c..dd36f5b27c 100644
--- a/libavutil/frame.h
+++ b/libavutil/frame.h
@@ -25,7 +25,6 @@
 #ifndef AVUTIL_FRAME_H
 #define AVUTIL_FRAME_H
 
-#include <stddef.h>
 #include <stdint.h>
 
 #include "avutil.h"
@@ -35,6 +34,7 @@
 #include "rational.h"
 #include "samplefmt.h"
 #include "pixfmt.h"
+#include "subfmt.h"
 #include "version.h"
 
 
@@ -293,7 +293,7 @@ typedef struct AVRegionOfInterest {
 } AVRegionOfInterest;
 
 /**
- * This structure describes decoded (raw) audio or video data.
+ * This structure describes decoded (raw) audio, video or subtitle data.
  *
  * AVFrame must be allocated using av_frame_alloc(). Note that this only
  * allocates the AVFrame itself, the buffers for the data must be managed
@@ -407,7 +407,7 @@ typedef struct AVFrame {
     /**
      * format of the frame, -1 if unknown or unset
      * Values correspond to enum AVPixelFormat for video frames,
-     * enum AVSampleFormat for audio)
+     * enum AVSampleFormat for audio, AVSubtitleType for subtitles)
      */
     int format;
 
@@ -702,6 +702,53 @@ typedef struct AVFrame {
      * Channel layout of the audio data.
      */
     AVChannelLayout ch_layout;
+
+    /**
+     * Media type of the frame (audio, video, subtitles..)
+     *
+     * See AVMEDIA_TYPE_xxx
+     */
+    enum AVMediaType type;
+
+    /**
+     * Number of items in the @ref subtitle_areas array.
+     */
+    unsigned num_subtitle_areas;
+
+    /**
+     * Array of subtitle areas, may be empty.
+     */
+    AVSubtitleArea **subtitle_areas;
+
+    /**
+     * Header containing style information for text subtitles.
+     */
+    AVBufferRef *subtitle_header;
+
+    /**
+     * Indicates that a subtitle frame is a repeated frame for arbitrating flow
+     * in a filter graph.
+     * The field subtitle_timing.start_pts always indicates the original presentation
+     * time, while the frame's pts field may be different.
+     */
+    int repeat_sub;
+
+    struct SubtitleTiming
+    {
+        /**
+         * The display start time, in AV_TIME_BASE.
+         *
+         * For subtitle frames, AVFrame.pts is populated from the packet pts value,
+         * which is not always the same as this value.
+         */
+        int64_t start_pts;
+
+        /**
+         * Display duration, in AV_TIME_BASE.
+         */
+        int64_t duration;
+
+    } subtitle_timing;
 } AVFrame;
 
 
@@ -778,6 +825,8 @@ void av_frame_move_ref(AVFrame *dst, AVFrame *src);
 /**
  * Allocate new buffer(s) for audio or video data.
  *
+ * Note: For subtitle data, use av_frame_get_buffer2
+ *
  * The following fields must be set on frame before calling this function:
  * - format (pixel format for video, sample format for audio)
  * - width and height for video
@@ -797,9 +846,39 @@ void av_frame_move_ref(AVFrame *dst, AVFrame *src);
  *              recommended to pass 0 here unless you know what you are doing.
  *
  * @return 0 on success, a negative AVERROR on error.
+ *
+ * @deprecated Use @ref av_frame_get_buffer2 instead and set @ref AVFrame.type
+ * before calling.
  */
+attribute_deprecated
 int av_frame_get_buffer(AVFrame *frame, int align);
 
+/**
+ * Allocate new buffer(s) for audio, video or subtitle data.
+ *
+ * The following fields must be set on frame before calling this function:
+ * - format (pixel format for video, sample format for audio)
+ * - width and height for video
+ * - nb_samples and channel_layout for audio
+ * - type (AVMediaType)
+ *
+ * This function will fill AVFrame.data and AVFrame.buf arrays and, if
+ * necessary, allocate and fill AVFrame.extended_data and AVFrame.extended_buf.
+ * For planar formats, one buffer will be allocated for each plane.
+ *
+ * @warning: if frame already has been allocated, calling this function will
+ *           leak memory. In addition, undefined behavior can occur in certain
+ *           cases.
+ *
+ * @param frame frame in which to store the new buffers.
+ * @param align Required buffer size alignment. If equal to 0, alignment will be
+ *              chosen automatically for the current CPU. It is highly
+ *              recommended to pass 0 here unless you know what you are doing.
+ *
+ * @return 0 on success, a negative AVERROR on error.
+ */
+int av_frame_get_buffer2(AVFrame *frame, int align);
+
 /**
  * Check if the frame data is writable.
  *
diff --git a/libavutil/subfmt.c b/libavutil/subfmt.c
new file mode 100644
index 0000000000..c72ebe2a43
--- /dev/null
+++ b/libavutil/subfmt.c
@@ -0,0 +1,45 @@
+/*
+ * 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
+ */
+
+#include <string.h>
+#include "common.h"
+#include "subfmt.h"
+
+static const char sub_fmt_info[AV_SUBTITLE_FMT_NB][24] = {
+    [AV_SUBTITLE_FMT_UNKNOWN] = "Unknown subtitle format",
+    [AV_SUBTITLE_FMT_BITMAP]  = "Graphical subtitles",
+    [AV_SUBTITLE_FMT_TEXT]    = "Text subtitles (plain)",
+    [AV_SUBTITLE_FMT_ASS]     = "Text subtitles (ass)",
+};
+
+const char *av_get_subtitle_fmt_name(enum AVSubtitleType sub_fmt)
+{
+    if (sub_fmt < 0 || sub_fmt >= AV_SUBTITLE_FMT_NB)
+        return NULL;
+    return sub_fmt_info[sub_fmt];
+}
+
+enum AVSubtitleType av_get_subtitle_fmt(const char *name)
+{
+    for (int i = 0; i < AV_SUBTITLE_FMT_NB; i++)
+        if (!strcmp(sub_fmt_info[i], name))
+            return i;
+    return AV_SUBTITLE_FMT_NONE;
+}
diff --git a/libavutil/subfmt.h b/libavutil/subfmt.h
index 791b45519f..3b8a0613b2 100644
--- a/libavutil/subfmt.h
+++ b/libavutil/subfmt.h
@@ -21,6 +21,9 @@
 #ifndef AVUTIL_SUBFMT_H
 #define AVUTIL_SUBFMT_H
 
+#include <stdint.h>
+
+#include "buffer.h"
 #include "version.h"
 
 enum AVSubtitleType {
@@ -65,4 +68,48 @@ enum AVSubtitleType {
     AV_SUBTITLE_FMT_NB,         ///< number of subtitle formats, DO NOT USE THIS if you want to link with shared libav* because the number of formats might differ between versions.
 };
 
+typedef struct AVSubtitleArea {
+#define AV_NUM_BUFFER_POINTERS 1
+
+    enum AVSubtitleType type;
+    int flags;
+
+    int x;         ///< top left corner  of area.
+    int y;         ///< top left corner  of area.
+    int w;         ///< width            of area.
+    int h;         ///< height           of area.
+    int nb_colors; ///< number of colors in bitmap palette (@ref pal).
+
+    /**
+     * Buffers and line sizes for the bitmap of this subtitle.
+     *
+     * @{
+     */
+    AVBufferRef *buf[AV_NUM_BUFFER_POINTERS];
+    int linesize[AV_NUM_BUFFER_POINTERS];
+    /**
+     * @}
+     */
+
+    uint32_t pal[256]; ///< RGBA palette for the bitmap.
+
+    char *text;        ///< 0-terminated plain UTF-8 text
+    char *ass;         ///< 0-terminated ASS/SSA compatible event line.
+
+} AVSubtitleArea;
+
+/**
+ * Return the name of sub_fmt, or NULL if sub_fmt is not
+ * recognized.
+ */
+const char *av_get_subtitle_fmt_name(enum AVSubtitleType sub_fmt);
+
+/**
+ * Return a subtitle format corresponding to name, or AV_SUBTITLE_FMT_NONE
+ * on error.
+ *
+ * @param name Subtitle format name.
+ */
+enum AVSubtitleType av_get_subtitle_fmt(const char *name);
+
 #endif /* AVUTIL_SUBFMT_H */
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v4 03/23] avcodec/subtitles: Introduce new frame-based subtitle decoding API
  2022-05-28 13:25     ` [FFmpeg-devel] [PATCH v4 00/23] Subtitle Filtering 2022 ffmpegagent
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 01/23] avcodec, avutil: Move enum AVSubtitleType to avutil, add new and deprecate old values softworkz
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 02/23] avutil/frame: Prepare AVFrame for subtitle handling softworkz
@ 2022-05-28 13:25       ` softworkz
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 04/23] avcodec/libzvbi: set subtitle type softworkz
                         ` (20 subsequent siblings)
  23 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-05-28 13:25 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

- Modify avcodec_send_packet() to support subtitles via the regular
  frame based decoding API
- Add decode_subtitle_shim() which takes subtitle frames,
  and serves as a compatibility shim to the legacy subtitle decoding
  API until all subtitle decoders are migrated to the frame-based API
- Add additional methods for conversion between old and new API

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/avcodec.c  |   8 ++
 libavcodec/avcodec.h  |  10 ++-
 libavcodec/decode.c   |  60 ++++++++++++--
 libavcodec/internal.h |  16 ++++
 libavcodec/utils.c    | 184 ++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 269 insertions(+), 9 deletions(-)

diff --git a/libavcodec/avcodec.c b/libavcodec/avcodec.c
index 5f6e71a39e..0a1d961fc6 100644
--- a/libavcodec/avcodec.c
+++ b/libavcodec/avcodec.c
@@ -358,6 +358,14 @@ FF_DISABLE_DEPRECATION_WARNINGS
 FF_ENABLE_DEPRECATION_WARNINGS
 #endif
 
+        // Set the subtitle type from the codec descriptor in case the decoder hasn't done itself
+        if (avctx->codec_type == AVMEDIA_TYPE_SUBTITLE && avctx->subtitle_type == AV_SUBTITLE_FMT_UNKNOWN) {
+            if(avctx->codec_descriptor->props & AV_CODEC_PROP_BITMAP_SUB)
+                avctx->subtitle_type = AV_SUBTITLE_FMT_BITMAP;
+            if(avctx->codec_descriptor->props & AV_CODEC_PROP_TEXT_SUB)
+                 avctx->subtitle_type = AV_SUBTITLE_FMT_ASS;
+        }
+
 #if FF_API_AVCTX_TIMEBASE
         if (avctx->framerate.num > 0 && avctx->framerate.den > 0)
             avctx->time_base = av_inv_q(av_mul_q(avctx->framerate, (AVRational){avctx->ticks_per_frame, 1}));
diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index 56d551f92d..de87b0406b 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -1698,7 +1698,7 @@ typedef struct AVCodecContext {
 
     /**
      * Header containing style information for text subtitles.
-     * For SUBTITLE_ASS subtitle type, it should contain the whole ASS
+     * For AV_SUBTITLE_FMT_ASS subtitle type, it should contain the whole ASS
      * [Script Info] and [V4+ Styles] section, plus the [Events] line and
      * the Format line following. It shouldn't include any Dialogue line.
      * - encoding: Set/allocated/freed by user (before avcodec_open2())
@@ -2056,6 +2056,8 @@ typedef struct AVCodecContext {
      *             The decoder can then override during decoding as needed.
      */
     AVChannelLayout ch_layout;
+
+    enum AVSubtitleType subtitle_type;
 } AVCodecContext;
 
 /**
@@ -2432,7 +2434,10 @@ int avcodec_close(AVCodecContext *avctx);
  * Free all allocated data in the given subtitle struct.
  *
  * @param sub AVSubtitle to free.
+ *
+ * @deprecated Use the regular frame based encode and decode APIs instead.
  */
+attribute_deprecated
 void avsubtitle_free(AVSubtitle *sub);
 
 /**
@@ -2525,7 +2530,10 @@ enum AVChromaLocation avcodec_chroma_pos_to_enum(int xpos, int ypos);
  *                 must be freed with avsubtitle_free if *got_sub_ptr is set.
  * @param[in,out] got_sub_ptr Zero if no subtitle could be decompressed, otherwise, it is nonzero.
  * @param[in] avpkt The input AVPacket containing the input buffer.
+ *
+ * @deprecated Use the new decode API (avcodec_send_packet, avcodec_receive_frame) instead.
  */
+attribute_deprecated
 int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub,
                             int *got_sub_ptr,
                             AVPacket *avpkt);
diff --git a/libavcodec/decode.c b/libavcodec/decode.c
index 1893caa6a6..e8ca7b6da4 100644
--- a/libavcodec/decode.c
+++ b/libavcodec/decode.c
@@ -573,6 +573,39 @@ static int decode_receive_frame_internal(AVCodecContext *avctx, AVFrame *frame)
     return ret;
 }
 
+static int decode_subtitle2_priv(AVCodecContext *avctx, AVSubtitle *sub,
+                                 int *got_sub_ptr, AVPacket *avpkt);
+
+static int decode_subtitle_shim(AVCodecContext *avctx, AVFrame *frame, AVPacket *avpkt)
+{
+    int ret, got_sub_ptr = 0;
+    AVSubtitle subtitle = { 0 };
+
+    if (frame->buf[0])
+        return AVERROR(EAGAIN);
+
+    av_frame_unref(frame);
+
+    ret = decode_subtitle2_priv(avctx, &subtitle, &got_sub_ptr, avpkt);
+
+    if (ret >= 0 && got_sub_ptr) {
+        frame->type = AVMEDIA_TYPE_SUBTITLE;
+        frame->format = subtitle.format;
+        ret = av_frame_get_buffer2(frame, 0);
+
+        if (ret >= 0)
+            ret = ff_frame_put_subtitle(frame, &subtitle);
+
+        frame->width = avctx->width;
+        frame->height = avctx->height;
+        frame->pkt_dts = avpkt->dts;
+    }
+
+    avsubtitle_free(&subtitle);
+
+    return ret;
+}
+
 int attribute_align_arg avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt)
 {
     AVCodecInternal *avci = avctx->internal;
@@ -587,6 +620,13 @@ int attribute_align_arg avcodec_send_packet(AVCodecContext *avctx, const AVPacke
     if (avpkt && !avpkt->size && avpkt->data)
         return AVERROR(EINVAL);
 
+    if (avctx->codec_type == AVMEDIA_TYPE_SUBTITLE)
+		// this does not exactly implement the avcodec_send_packet/avcodec_receive_frame API
+	    // but we know that no subtitle decoder produces multiple AVSubtitles per packet through
+		// the legacy API, and this will be changed when migrating the subtitle decoders
+		// to the frame based decoding api
+        return decode_subtitle_shim(avctx, avci->buffer_frame, avpkt);
+
     av_packet_unref(avci->buffer_pkt);
     if (avpkt && (avpkt->data || avpkt->side_data_elems)) {
         ret = av_packet_ref(avci->buffer_pkt, avpkt);
@@ -648,7 +688,9 @@ int attribute_align_arg avcodec_receive_frame(AVCodecContext *avctx, AVFrame *fr
 
     if (avci->buffer_frame->buf[0]) {
         av_frame_move_ref(frame, avci->buffer_frame);
-    } else {
+    } else if (avctx->codec_type == AVMEDIA_TYPE_SUBTITLE)
+        return AVERROR(EAGAIN);
+    else {
         ret = decode_receive_frame_internal(avctx, frame);
         if (ret < 0)
             return ret;
@@ -813,9 +855,8 @@ static int utf8_check(const uint8_t *str)
     return 1;
 }
 
-int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub,
-                             int *got_sub_ptr,
-                             AVPacket *avpkt)
+static int decode_subtitle2_priv(AVCodecContext *avctx, AVSubtitle *sub,
+                             int *got_sub_ptr, AVPacket *avpkt)
 {
     int ret = 0;
 
@@ -861,10 +902,7 @@ int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub,
                                                  avctx->pkt_timebase, ms);
         }
 
-        if (avctx->codec_descriptor->props & AV_CODEC_PROP_BITMAP_SUB)
-            sub->format = 0;
-        else if (avctx->codec_descriptor->props & AV_CODEC_PROP_TEXT_SUB)
-            sub->format = 1;
+        sub->format = (uint16_t)avctx->subtitle_type;
 
         for (unsigned i = 0; i < sub->num_rects; i++) {
             if (avctx->sub_charenc_mode != FF_SUB_CHARENC_MODE_IGNORE &&
@@ -885,6 +923,12 @@ int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub,
     return ret;
 }
 
+int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub,
+                             int *got_sub_ptr, AVPacket *avpkt)
+{
+    return decode_subtitle2_priv(avctx, sub, got_sub_ptr, avpkt);
+}
+
 enum AVPixelFormat avcodec_default_get_format(struct AVCodecContext *avctx,
                                               const enum AVPixelFormat *fmt)
 {
diff --git a/libavcodec/internal.h b/libavcodec/internal.h
index 17e1de8127..69656729d8 100644
--- a/libavcodec/internal.h
+++ b/libavcodec/internal.h
@@ -290,4 +290,20 @@ int ff_int_from_list_or_default(void *ctx, const char * val_name, int val,
 
 void ff_dvdsub_parse_palette(uint32_t *palette, const char *p);
 
+/**
+ * Copies subtitle data from AVSubtitle to AVFrame.
+ *
+ * @deprecated This is a compatibility method for interoperability with
+ * the legacy subtitle API.
+ */
+int ff_frame_put_subtitle(AVFrame* frame, const AVSubtitle* sub);
+
+/**
+ * Copies subtitle data from AVFrame to AVSubtitle.
+ *
+ * @deprecated This is a compatibility method for interoperability with
+ * the legacy subtitle API.
+ */
+int ff_frame_get_subtitle(AVSubtitle* sub, AVFrame* frame);
+
 #endif /* AVCODEC_INTERNAL_H */
diff --git a/libavcodec/utils.c b/libavcodec/utils.c
index eb7e505a62..b67b6b6122 100644
--- a/libavcodec/utils.c
+++ b/libavcodec/utils.c
@@ -827,6 +827,190 @@ FF_ENABLE_DEPRECATION_WARNINGS
     return FFMAX(0, duration);
 }
 
+static int subtitle_area2rect(AVSubtitleRect *dst, const AVSubtitleArea *src)
+{
+    dst->x         =  src->x;
+    dst->y         =  src->y;
+    dst->w         =  src->w;
+    dst->h         =  src->h;
+    dst->nb_colors =  src->nb_colors;
+    dst->type      =  src->type;
+    dst->flags     =  src->flags;
+
+    switch (dst->type) {
+    case AV_SUBTITLE_FMT_BITMAP:
+
+        if (src->h > 0 && src->w > 0 && src->buf[0]) {
+            uint32_t *pal;
+            AVBufferRef *buf = src->buf[0];
+            dst->data[0] = av_mallocz(buf->size);
+            memcpy(dst->data[0], buf->data, buf->size);
+            dst->linesize[0] = src->linesize[0];
+
+            dst->data[1] = av_mallocz(256 * 4);
+            pal = (uint32_t *)dst->data[1];
+
+            for (unsigned i = 0; i < 256; i++) {
+                pal[i] = src->pal[i];
+            }
+        }
+
+        break;
+    case AV_SUBTITLE_FMT_TEXT:
+
+        if (src->text)
+            dst->text = av_strdup(src->text);
+        else
+            dst->text = av_strdup("");
+
+        if (!dst->text)
+            return AVERROR(ENOMEM);
+
+        break;
+    case AV_SUBTITLE_FMT_ASS:
+
+        if (src->ass)
+            dst->ass = av_strdup(src->ass);
+        else
+            dst->ass = av_strdup("");
+
+        if (!dst->ass)
+            return AVERROR(ENOMEM);
+
+        break;
+    default:
+
+        av_log(NULL, AV_LOG_ERROR, "Subtitle rect has invalid format: %d", dst->type);
+        return AVERROR(EINVAL);
+    }
+
+    return 0;
+}
+
+static int subtitle_rect2area(AVSubtitleArea *dst, const AVSubtitleRect *src)
+{
+    dst->x         =  src->x;
+    dst->y         =  src->y;
+    dst->w         =  src->w;
+    dst->h         =  src->h;
+    dst->nb_colors =  src->nb_colors;
+    dst->type      =  src->type;
+    dst->flags     =  src->flags;
+
+    switch (dst->type) {
+    case AV_SUBTITLE_FMT_BITMAP:
+
+        if (src->h > 0 && src->w > 0 && src->data[0]) {
+            AVBufferRef *buf = av_buffer_allocz(src->h * src->linesize[0]);
+            memcpy(buf->data, src->data[0], buf->size);
+
+            dst->buf[0] = buf;
+            dst->linesize[0] = src->linesize[0];
+        }
+
+        if (src->data[1]) {
+            uint32_t *pal = (uint32_t *)src->data[1];
+
+            for (unsigned i = 0; i < 256; i++) {
+                dst->pal[i] = pal[i];
+            }
+        }
+
+        break;
+    case AV_SUBTITLE_FMT_TEXT:
+
+        if (src->text) {
+            dst->text = av_strdup(src->text);
+            if (!dst->text)
+                return AVERROR(ENOMEM);
+        }
+
+        break;
+    case AV_SUBTITLE_FMT_ASS:
+
+        if (src->ass) {
+            dst->ass = av_strdup(src->ass);
+            if (!dst->ass)
+                return AVERROR(ENOMEM);
+        }
+
+        break;
+    default:
+
+        av_log(NULL, AV_LOG_ERROR, "Subtitle area has invalid format: %d", dst->type);
+        return AVERROR(EINVAL);
+    }
+
+    return 0;
+}
+
+/**
+ * Copies subtitle data from AVSubtitle (deprecated) to AVFrame
+ *
+ * @note This is a compatibility method for conversion to the legacy API
+ */
+int ff_frame_put_subtitle(AVFrame *frame, const AVSubtitle *sub)
+{
+    frame->format = sub->format;
+    frame->subtitle_timing.start_pts = sub->pts;
+    frame->subtitle_timing.start_pts += av_rescale_q(sub->start_display_time, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q);
+    frame->subtitle_timing.duration = av_rescale_q(sub->end_display_time - sub->start_display_time, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q);
+
+    if (sub->num_rects) {
+        frame->subtitle_areas = av_malloc_array(sub->num_rects, sizeof(AVSubtitleArea*));
+        if (!frame->subtitle_areas)
+            return AVERROR(ENOMEM);
+
+        for (unsigned i = 0; i < sub->num_rects; i++) {
+            int ret;
+            frame->subtitle_areas[i] = av_mallocz(sizeof(AVSubtitleArea));
+            if (!frame->subtitle_areas[i])
+                return AVERROR(ENOMEM);
+            ret = subtitle_rect2area(frame->subtitle_areas[i], sub->rects[i]);
+            if (ret < 0) {
+                frame->num_subtitle_areas = i;
+                return ret;
+            }
+        }
+    }
+
+    frame->num_subtitle_areas = sub->num_rects;
+    return 0;
+}
+
+/**
+ * Copies subtitle data from AVFrame to AVSubtitle (deprecated)
+ *
+ * @note This is a compatibility method for conversion to the legacy API
+ */
+int ff_frame_get_subtitle(AVSubtitle *sub, AVFrame *frame)
+{
+    const int64_t duration_ms = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, (AVRational){ 1, 1000 });
+
+    sub->start_display_time = 0;
+    sub->end_display_time = (int32_t)duration_ms;
+    sub->pts = frame->subtitle_timing.start_pts;
+
+    if (frame->num_subtitle_areas) {
+        sub->rects = av_malloc_array(frame->num_subtitle_areas, sizeof(AVSubtitleRect*));
+        if (!sub->rects)
+            return AVERROR(ENOMEM);
+
+        for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+            int ret;
+            sub->rects[i] = av_mallocz(sizeof(AVSubtitleRect));
+            ret = subtitle_area2rect(sub->rects[i], frame->subtitle_areas[i]);
+            if (ret < 0) {
+                sub->num_rects = i;
+                return ret;
+            }
+        }
+    }
+
+    sub->num_rects = frame->num_subtitle_areas;
+    return 0;
+}
+
 int av_get_audio_frame_duration2(AVCodecParameters *par, int frame_bytes)
 {
    int channels = par->ch_layout.nb_channels;
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v4 04/23] avcodec/libzvbi: set subtitle type
  2022-05-28 13:25     ` [FFmpeg-devel] [PATCH v4 00/23] Subtitle Filtering 2022 ffmpegagent
                         ` (2 preceding siblings ...)
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 03/23] avcodec/subtitles: Introduce new frame-based subtitle decoding API softworkz
@ 2022-05-28 13:25       ` softworkz
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 05/23] avfilter/subtitles: Update vf_subtitles to use new decoding api softworkz
                         ` (19 subsequent siblings)
  23 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-05-28 13:25 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/libzvbi-teletextdec.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libavcodec/libzvbi-teletextdec.c b/libavcodec/libzvbi-teletextdec.c
index 92466cc11e..2aab10a548 100644
--- a/libavcodec/libzvbi-teletextdec.c
+++ b/libavcodec/libzvbi-teletextdec.c
@@ -751,10 +751,13 @@ static int teletext_init_decoder(AVCodecContext *avctx)
 
     switch (ctx->format_id) {
         case 0:
+            avctx->subtitle_type = AV_SUBTITLE_FMT_BITMAP;
             return 0;
         case 1:
+            avctx->subtitle_type = AV_SUBTITLE_FMT_ASS;
             return ff_ass_subtitle_header_default(avctx);
         case 2:
+            avctx->subtitle_type = AV_SUBTITLE_FMT_ASS;
             return my_ass_subtitle_header(avctx);
     }
     return AVERROR_BUG;
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v4 05/23] avfilter/subtitles: Update vf_subtitles to use new decoding api
  2022-05-28 13:25     ` [FFmpeg-devel] [PATCH v4 00/23] Subtitle Filtering 2022 ffmpegagent
                         ` (3 preceding siblings ...)
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 04/23] avcodec/libzvbi: set subtitle type softworkz
@ 2022-05-28 13:25       ` softworkz
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 06/23] avcodec, avutil: Move ass helper functions to avutil as avpriv_ and extend ass dialog parsing softworkz
                         ` (18 subsequent siblings)
  23 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-05-28 13:25 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavfilter/vf_subtitles.c | 67 ++++++++++++++++++++++++++++++--------
 1 file changed, 54 insertions(+), 13 deletions(-)

diff --git a/libavfilter/vf_subtitles.c b/libavfilter/vf_subtitles.c
index 82e140e986..0ae156ad07 100644
--- a/libavfilter/vf_subtitles.c
+++ b/libavfilter/vf_subtitles.c
@@ -36,14 +36,12 @@
 # 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"
 
 typedef struct AssContext {
     const AVClass *class;
@@ -304,8 +302,42 @@ static int attachment_is_font(AVStream * st)
     return 0;
 }
 
+static int decode(AVCodecContext *avctx, AVFrame *frame, int *got_frame, AVPacket *pkt)
+{
+    int ret;
+
+    *got_frame = 0;
+
+    if (pkt) {
+        ret = avcodec_send_packet(avctx, pkt);
+        // In particular, we don't expect AVERROR(EAGAIN), because we read all
+        // decoded frames with avcodec_receive_frame() until done.
+        if (ret < 0 && ret != AVERROR_EOF)
+            return ret;
+    }
+
+    ret = avcodec_receive_frame(avctx, frame);
+    if (ret < 0 && ret != AVERROR(EAGAIN))
+        return ret;
+    if (ret >= 0)
+        *got_frame = 1;
+
+    return 0;
+}
+
 AVFILTER_DEFINE_CLASS(subtitles);
 
+static enum AVSubtitleType get_subtitle_format(const AVCodecDescriptor *codec_descriptor)
+{
+    if(codec_descriptor->props & AV_CODEC_PROP_BITMAP_SUB)
+        return AV_SUBTITLE_FMT_BITMAP;
+
+    if(codec_descriptor->props & AV_CODEC_PROP_TEXT_SUB)
+        return AV_SUBTITLE_FMT_ASS;
+
+    return AV_SUBTITLE_FMT_UNKNOWN;
+}
+
 static av_cold int init_subtitles(AVFilterContext *ctx)
 {
     int j, ret, sid;
@@ -318,6 +350,7 @@ static av_cold int init_subtitles(AVFilterContext *ctx)
     AVStream *st;
     AVPacket pkt;
     AssContext *ass = ctx->priv;
+    enum AVSubtitleType subtitle_format;
 
     /* Init libass */
     ret = init(ctx);
@@ -398,13 +431,17 @@ static av_cold int init_subtitles(AVFilterContext *ctx)
         ret = AVERROR_DECODER_NOT_FOUND;
         goto end;
     }
+
     dec_desc = avcodec_descriptor_get(st->codecpar->codec_id);
-    if (dec_desc && !(dec_desc->props & AV_CODEC_PROP_TEXT_SUB)) {
+    subtitle_format = get_subtitle_format(dec_desc);
+
+    if (subtitle_format != AV_SUBTITLE_FMT_ASS) {
         av_log(ctx, AV_LOG_ERROR,
-               "Only text based subtitles are currently supported\n");
-        ret = AVERROR_PATCHWELCOME;
+               "Only text based subtitles are supported by this filter\n");
+        ret = AVERROR_INVALIDDATA;
         goto end;
     }
+
     if (ass->charenc)
         av_dict_set(&codec_opts, "sub_charenc", ass->charenc, 0);
 
@@ -460,27 +497,31 @@ static av_cold int init_subtitles(AVFilterContext *ctx)
                                   dec_ctx->subtitle_header_size);
     while (av_read_frame(fmt, &pkt) >= 0) {
         int i, got_subtitle;
-        AVSubtitle sub = {0};
+        AVFrame *sub = av_frame_alloc();
+        if (!sub) {
+            ret = AVERROR(ENOMEM);
+            goto end;
+        }
 
         if (pkt.stream_index == sid) {
-            ret = avcodec_decode_subtitle2(dec_ctx, &sub, &got_subtitle, &pkt);
+            ret = decode(dec_ctx, sub, &got_subtitle, &pkt);
             if (ret < 0) {
                 av_log(ctx, AV_LOG_WARNING, "Error decoding: %s (ignored)\n",
                        av_err2str(ret));
             } else if (got_subtitle) {
-                const int64_t start_time = av_rescale_q(sub.pts, AV_TIME_BASE_Q, av_make_q(1, 1000));
-                const int64_t duration   = sub.end_display_time;
-                for (i = 0; i < sub.num_rects; i++) {
-                    char *ass_line = sub.rects[i]->ass;
+                const int64_t start_time = av_rescale_q(sub->subtitle_timing.start_pts, AV_TIME_BASE_Q, av_make_q(1, 1000));
+                const int64_t duration   = av_rescale_q(sub->subtitle_timing.duration, AV_TIME_BASE_Q, av_make_q(1, 1000));
+                for (i = 0; i < sub->num_subtitle_areas; i++) {
+                    char *ass_line = sub->subtitle_areas[i]->ass;
                     if (!ass_line)
-                        break;
+                        continue;
                     ass_process_chunk(ass->track, ass_line, strlen(ass_line),
                                       start_time, duration);
                 }
             }
         }
         av_packet_unref(&pkt);
-        avsubtitle_free(&sub);
+        av_frame_free(&sub);
     }
 
 end:
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v4 06/23] avcodec, avutil: Move ass helper functions to avutil as avpriv_ and extend ass dialog parsing
  2022-05-28 13:25     ` [FFmpeg-devel] [PATCH v4 00/23] Subtitle Filtering 2022 ffmpegagent
                         ` (4 preceding siblings ...)
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 05/23] avfilter/subtitles: Update vf_subtitles to use new decoding api softworkz
@ 2022-05-28 13:25       ` softworkz
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 07/23] avcodec/subtitles: Replace deprecated enum values softworkz
                         ` (17 subsequent siblings)
  23 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-05-28 13:25 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Also add

- hard_space callback (for upcoming fix)
- extensible callback (for future extension)

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/Makefile                           |  56 +++----
 libavcodec/ass.h                              | 151 ++++++------------
 libavcodec/assdec.c                           |   2 +-
 libavcodec/assenc.c                           |   2 +-
 libavcodec/ccaption_dec.c                     |  20 +--
 libavcodec/jacosubdec.c                       |   2 +-
 libavcodec/libaribb24.c                       |   2 +-
 libavcodec/libzvbi-teletextdec.c              |  14 +-
 libavcodec/microdvddec.c                      |   7 +-
 libavcodec/movtextdec.c                       |   3 +-
 libavcodec/movtextenc.c                       |  20 +--
 libavcodec/mpl2dec.c                          |   2 +-
 libavcodec/realtextdec.c                      |   2 +-
 libavcodec/samidec.c                          |   2 +-
 libavcodec/srtdec.c                           |   2 +-
 libavcodec/srtenc.c                           |  16 +-
 libavcodec/subviewerdec.c                     |   2 +-
 libavcodec/textdec.c                          |   4 +-
 libavcodec/ttmlenc.c                          |  15 +-
 libavcodec/webvttdec.c                        |   2 +-
 libavcodec/webvttenc.c                        |  16 +-
 libavutil/Makefile                            |   2 +
 {libavcodec => libavutil}/ass.c               | 115 ++++---------
 libavutil/ass_internal.h                      | 135 ++++++++++++++++
 {libavcodec => libavutil}/ass_split.c         |  30 ++--
 .../ass_split_internal.h                      |  32 ++--
 26 files changed, 355 insertions(+), 301 deletions(-)
 rename {libavcodec => libavutil}/ass.c (59%)
 create mode 100644 libavutil/ass_internal.h
 rename {libavcodec => libavutil}/ass_split.c (94%)
 rename libavcodec/ass_split.h => libavutil/ass_split_internal.h (86%)

diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 38425d2f22..1864f06cbb 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -221,10 +221,10 @@ OBJS-$(CONFIG_APNG_DECODER)            += png.o pngdec.o pngdsp.o
 OBJS-$(CONFIG_APNG_ENCODER)            += png.o pngenc.o
 OBJS-$(CONFIG_ARBC_DECODER)            += arbc.o
 OBJS-$(CONFIG_ARGO_DECODER)            += argo.o
-OBJS-$(CONFIG_SSA_DECODER)             += assdec.o ass.o
-OBJS-$(CONFIG_SSA_ENCODER)             += assenc.o ass.o
-OBJS-$(CONFIG_ASS_DECODER)             += assdec.o ass.o
-OBJS-$(CONFIG_ASS_ENCODER)             += assenc.o ass.o
+OBJS-$(CONFIG_SSA_DECODER)             += assdec.o
+OBJS-$(CONFIG_SSA_ENCODER)             += assenc.o
+OBJS-$(CONFIG_ASS_DECODER)             += assdec.o
+OBJS-$(CONFIG_ASS_ENCODER)             += assenc.o
 OBJS-$(CONFIG_ASV1_DECODER)            += asvdec.o asv.o mpeg12data.o
 OBJS-$(CONFIG_ASV1_ENCODER)            += asvenc.o asv.o mpeg12data.o
 OBJS-$(CONFIG_ASV2_DECODER)            += asvdec.o asv.o mpeg12data.o
@@ -265,7 +265,7 @@ OBJS-$(CONFIG_BRENDER_PIX_DECODER)     += brenderpix.o
 OBJS-$(CONFIG_C93_DECODER)             += c93.o
 OBJS-$(CONFIG_CAVS_DECODER)            += cavs.o cavsdec.o cavsdsp.o \
                                           cavsdata.o
-OBJS-$(CONFIG_CCAPTION_DECODER)        += ccaption_dec.o ass.o
+OBJS-$(CONFIG_CCAPTION_DECODER)        += ccaption_dec.o
 OBJS-$(CONFIG_CDGRAPHICS_DECODER)      += cdgraphics.o
 OBJS-$(CONFIG_CDTOONS_DECODER)         += cdtoons.o
 OBJS-$(CONFIG_CDXL_DECODER)            += cdxl.o
@@ -442,7 +442,7 @@ OBJS-$(CONFIG_INTERPLAY_ACM_DECODER)   += interplayacm.o
 OBJS-$(CONFIG_INTERPLAY_DPCM_DECODER)  += dpcm.o
 OBJS-$(CONFIG_INTERPLAY_VIDEO_DECODER) += interplayvideo.o
 OBJS-$(CONFIG_IPU_DECODER)             += mpeg12dec.o mpeg12.o mpeg12data.o
-OBJS-$(CONFIG_JACOSUB_DECODER)         += jacosubdec.o ass.o
+OBJS-$(CONFIG_JACOSUB_DECODER)         += jacosubdec.o
 OBJS-$(CONFIG_JPEG2000_ENCODER)        += j2kenc.o mqcenc.o mqc.o jpeg2000.o \
                                           jpeg2000dwt.o
 OBJS-$(CONFIG_JPEG2000_DECODER)        += jpeg2000dec.o jpeg2000.o jpeg2000dsp.o \
@@ -464,7 +464,7 @@ OBJS-$(CONFIG_MAGICYUV_ENCODER)        += magicyuvenc.o
 OBJS-$(CONFIG_MDEC_DECODER)            += mdec.o mpeg12.o mpeg12data.o
 OBJS-$(CONFIG_METASOUND_DECODER)       += metasound.o metasound_data.o \
                                           twinvq.o
-OBJS-$(CONFIG_MICRODVD_DECODER)        += microdvddec.o ass.o
+OBJS-$(CONFIG_MICRODVD_DECODER)        += microdvddec.o
 OBJS-$(CONFIG_MIMIC_DECODER)           += mimic.o
 OBJS-$(CONFIG_MJPEG_DECODER)           += mjpegdec.o mjpegdec_common.o
 OBJS-$(CONFIG_MJPEG_QSV_DECODER)       += qsvdec.o
@@ -479,8 +479,8 @@ OBJS-$(CONFIG_MLP_ENCODER)             += mlpenc.o mlp.o
 OBJS-$(CONFIG_MMVIDEO_DECODER)         += mmvideo.o
 OBJS-$(CONFIG_MOBICLIP_DECODER)        += mobiclip.o
 OBJS-$(CONFIG_MOTIONPIXELS_DECODER)    += motionpixels.o
-OBJS-$(CONFIG_MOVTEXT_DECODER)         += movtextdec.o ass.o
-OBJS-$(CONFIG_MOVTEXT_ENCODER)         += movtextenc.o ass_split.o
+OBJS-$(CONFIG_MOVTEXT_DECODER)         += movtextdec.o
+OBJS-$(CONFIG_MOVTEXT_ENCODER)         += movtextenc.o
 OBJS-$(CONFIG_MP1_DECODER)             += mpegaudiodec_fixed.o
 OBJS-$(CONFIG_MP1FLOAT_DECODER)        += mpegaudiodec_float.o
 OBJS-$(CONFIG_MP2_DECODER)             += mpegaudiodec_fixed.o
@@ -521,7 +521,7 @@ OBJS-$(CONFIG_MPEG4_MEDIACODEC_DECODER) += mediacodecdec.o
 OBJS-$(CONFIG_MPEG4_OMX_ENCODER)       += omx.o
 OBJS-$(CONFIG_MPEG4_V4L2M2M_DECODER)   += v4l2_m2m_dec.o
 OBJS-$(CONFIG_MPEG4_V4L2M2M_ENCODER)   += v4l2_m2m_enc.o
-OBJS-$(CONFIG_MPL2_DECODER)            += mpl2dec.o ass.o
+OBJS-$(CONFIG_MPL2_DECODER)            += mpl2dec.o
 OBJS-$(CONFIG_MSA1_DECODER)            += mss3.o
 OBJS-$(CONFIG_MSCC_DECODER)            += mscc.o
 OBJS-$(CONFIG_MSMPEG4V1_DECODER)       += msmpeg4dec.o msmpeg4.o msmpeg4data.o
@@ -574,7 +574,7 @@ OBJS-$(CONFIG_PGX_DECODER)             += pgxdec.o
 OBJS-$(CONFIG_PHOTOCD_DECODER)         += photocd.o
 OBJS-$(CONFIG_PICTOR_DECODER)          += pictordec.o cga_data.o
 OBJS-$(CONFIG_PIXLET_DECODER)          += pixlet.o
-OBJS-$(CONFIG_PJS_DECODER)             += textdec.o ass.o
+OBJS-$(CONFIG_PJS_DECODER)             += textdec.o
 OBJS-$(CONFIG_PNG_DECODER)             += png.o pngdec.o pngdsp.o
 OBJS-$(CONFIG_PNG_ENCODER)             += png.o pngenc.o
 OBJS-$(CONFIG_PPM_DECODER)             += pnmdec.o pnm.o
@@ -607,7 +607,7 @@ OBJS-$(CONFIG_RALF_DECODER)            += ralf.o
 OBJS-$(CONFIG_RASC_DECODER)            += rasc.o
 OBJS-$(CONFIG_RAWVIDEO_DECODER)        += rawdec.o
 OBJS-$(CONFIG_RAWVIDEO_ENCODER)        += rawenc.o
-OBJS-$(CONFIG_REALTEXT_DECODER)        += realtextdec.o ass.o
+OBJS-$(CONFIG_REALTEXT_DECODER)        += realtextdec.o
 OBJS-$(CONFIG_RL2_DECODER)             += rl2.o
 OBJS-$(CONFIG_ROQ_DECODER)             += roqvideodec.o roqvideo.o
 OBJS-$(CONFIG_ROQ_ENCODER)             += roqvideoenc.o roqvideo.o elbg.o
@@ -622,7 +622,7 @@ OBJS-$(CONFIG_RV20_DECODER)            += rv10.o
 OBJS-$(CONFIG_RV20_ENCODER)            += rv20enc.o
 OBJS-$(CONFIG_RV30_DECODER)            += rv30.o rv34.o rv30dsp.o
 OBJS-$(CONFIG_RV40_DECODER)            += rv40.o rv34.o rv40dsp.o
-OBJS-$(CONFIG_SAMI_DECODER)            += samidec.o ass.o htmlsubtitles.o
+OBJS-$(CONFIG_SAMI_DECODER)            += samidec.o htmlsubtitles.o
 OBJS-$(CONFIG_S302M_DECODER)           += s302m.o
 OBJS-$(CONFIG_S302M_ENCODER)           += s302menc.o
 OBJS-$(CONFIG_SANM_DECODER)            += sanm.o
@@ -657,13 +657,13 @@ OBJS-$(CONFIG_SPEEDHQ_ENCODER)         += speedhq.o mpeg12data.o mpeg12enc.o spe
 OBJS-$(CONFIG_SPEEX_DECODER)           += speexdec.o
 OBJS-$(CONFIG_SP5X_DECODER)            += sp5xdec.o
 OBJS-$(CONFIG_SRGC_DECODER)            += mscc.o
-OBJS-$(CONFIG_SRT_DECODER)             += srtdec.o ass.o htmlsubtitles.o
-OBJS-$(CONFIG_SRT_ENCODER)             += srtenc.o ass_split.o
-OBJS-$(CONFIG_STL_DECODER)             += textdec.o ass.o
-OBJS-$(CONFIG_SUBRIP_DECODER)          += srtdec.o ass.o htmlsubtitles.o
-OBJS-$(CONFIG_SUBRIP_ENCODER)          += srtenc.o ass_split.o
-OBJS-$(CONFIG_SUBVIEWER1_DECODER)      += textdec.o ass.o
-OBJS-$(CONFIG_SUBVIEWER_DECODER)       += subviewerdec.o ass.o
+OBJS-$(CONFIG_SRT_DECODER)             += srtdec.o htmlsubtitles.o
+OBJS-$(CONFIG_SRT_ENCODER)             += srtenc.o
+OBJS-$(CONFIG_STL_DECODER)             += textdec.o
+OBJS-$(CONFIG_SUBRIP_DECODER)          += srtdec.o htmlsubtitles.o
+OBJS-$(CONFIG_SUBRIP_ENCODER)          += srtenc.o
+OBJS-$(CONFIG_SUBVIEWER1_DECODER)      += textdec.o
+OBJS-$(CONFIG_SUBVIEWER_DECODER)       += subviewerdec.o
 OBJS-$(CONFIG_SUNRAST_DECODER)         += sunrast.o
 OBJS-$(CONFIG_SUNRAST_ENCODER)         += sunrastenc.o
 OBJS-$(CONFIG_LIBRSVG_DECODER)         += librsvgdec.o
@@ -673,8 +673,8 @@ OBJS-$(CONFIG_SVQ1_DECODER)            += svq1dec.o svq1.o h263data.o
 OBJS-$(CONFIG_SVQ1_ENCODER)            += svq1enc.o svq1.o  h263data.o  \
                                           h263.o ituh263enc.o
 OBJS-$(CONFIG_SVQ3_DECODER)            += svq3.o mpegutils.o h264data.o
-OBJS-$(CONFIG_TEXT_DECODER)            += textdec.o ass.o
-OBJS-$(CONFIG_TEXT_ENCODER)            += srtenc.o ass_split.o
+OBJS-$(CONFIG_TEXT_DECODER)            += textdec.o
+OBJS-$(CONFIG_TEXT_ENCODER)            += srtenc.o
 OBJS-$(CONFIG_TAK_DECODER)             += takdec.o tak.o takdsp.o
 OBJS-$(CONFIG_TARGA_DECODER)           += targa.o
 OBJS-$(CONFIG_TARGA_ENCODER)           += targaenc.o rle.o
@@ -694,7 +694,7 @@ OBJS-$(CONFIG_TSCC_DECODER)            += tscc.o msrledec.o
 OBJS-$(CONFIG_TSCC2_DECODER)           += tscc2.o
 OBJS-$(CONFIG_TTA_DECODER)             += tta.o ttadata.o ttadsp.o
 OBJS-$(CONFIG_TTA_ENCODER)             += ttaenc.o ttaencdsp.o ttadata.o
-OBJS-$(CONFIG_TTML_ENCODER)            += ttmlenc.o ass_split.o
+OBJS-$(CONFIG_TTML_ENCODER)            += ttmlenc.o
 OBJS-$(CONFIG_TWINVQ_DECODER)          += twinvqdec.o twinvq.o metasound_data.o
 OBJS-$(CONFIG_TXD_DECODER)             += txd.o
 OBJS-$(CONFIG_ULTI_DECODER)            += ulti.o
@@ -751,15 +751,15 @@ OBJS-$(CONFIG_VP9_MEDIACODEC_DECODER)  += mediacodecdec.o
 OBJS-$(CONFIG_VP9_RKMPP_DECODER)       += rkmppdec.o
 OBJS-$(CONFIG_VP9_VAAPI_ENCODER)       += vaapi_encode_vp9.o
 OBJS-$(CONFIG_VP9_QSV_ENCODER)         += qsvenc_vp9.o
-OBJS-$(CONFIG_VPLAYER_DECODER)         += textdec.o ass.o
+OBJS-$(CONFIG_VPLAYER_DECODER)         += textdec.o
 OBJS-$(CONFIG_VP9_V4L2M2M_DECODER)     += v4l2_m2m_dec.o
 OBJS-$(CONFIG_VQA_DECODER)             += vqavideo.o
 OBJS-$(CONFIG_WAVPACK_DECODER)         += wavpack.o wavpackdata.o dsd.o
 OBJS-$(CONFIG_WAVPACK_ENCODER)         += wavpackdata.o wavpackenc.o
 OBJS-$(CONFIG_WCMV_DECODER)            += wcmv.o
 OBJS-$(CONFIG_WEBP_DECODER)            += webp.o
-OBJS-$(CONFIG_WEBVTT_DECODER)          += webvttdec.o ass.o
-OBJS-$(CONFIG_WEBVTT_ENCODER)          += webvttenc.o ass_split.o
+OBJS-$(CONFIG_WEBVTT_DECODER)          += webvttdec.o
+OBJS-$(CONFIG_WEBVTT_ENCODER)          += webvttenc.o
 OBJS-$(CONFIG_WMALOSSLESS_DECODER)     += wmalosslessdec.o wma_common.o
 OBJS-$(CONFIG_WMAPRO_DECODER)          += wmaprodec.o wma.o wma_common.o
 OBJS-$(CONFIG_WMAV1_DECODER)           += wmadec.o wma.o wma_common.o aactab.o
@@ -1049,7 +1049,7 @@ OBJS-$(CONFIG_PCM_ALAW_AT_ENCODER)        += audiotoolboxenc.o
 OBJS-$(CONFIG_PCM_MULAW_AT_ENCODER)       += audiotoolboxenc.o
 OBJS-$(CONFIG_LIBAOM_AV1_DECODER)         += libaomdec.o
 OBJS-$(CONFIG_LIBAOM_AV1_ENCODER)         += libaomenc.o
-OBJS-$(CONFIG_LIBARIBB24_DECODER)         += libaribb24.o ass.o
+OBJS-$(CONFIG_LIBARIBB24_DECODER)         += libaribb24.o
 OBJS-$(CONFIG_LIBCELT_DECODER)            += libcelt_dec.o
 OBJS-$(CONFIG_LIBCODEC2_DECODER)          += libcodec2.o
 OBJS-$(CONFIG_LIBCODEC2_ENCODER)          += libcodec2.o
@@ -1102,7 +1102,7 @@ OBJS-$(CONFIG_LIBX265_ENCODER)            += libx265.o
 OBJS-$(CONFIG_LIBXAVS_ENCODER)            += libxavs.o
 OBJS-$(CONFIG_LIBXAVS2_ENCODER)           += libxavs2.o
 OBJS-$(CONFIG_LIBXVID_ENCODER)            += libxvid.o
-OBJS-$(CONFIG_LIBZVBI_TELETEXT_DECODER)   += libzvbi-teletextdec.o ass.o
+OBJS-$(CONFIG_LIBZVBI_TELETEXT_DECODER)   += libzvbi-teletextdec.o
 
 # parsers
 OBJS-$(CONFIG_AAC_LATM_PARSER)         += latm_parser.o
diff --git a/libavcodec/ass.h b/libavcodec/ass.h
index 4dffe923d9..8bc13d7ab8 100644
--- a/libavcodec/ass.h
+++ b/libavcodec/ass.h
@@ -23,124 +23,73 @@
 #define AVCODEC_ASS_H
 
 #include "avcodec.h"
-#include "libavutil/bprint.h"
-
-#define ASS_DEFAULT_PLAYRESX 384
-#define ASS_DEFAULT_PLAYRESY 288
-
-/**
- * @name Default values for ASS style
- * @{
- */
-#define ASS_DEFAULT_FONT        "Arial"
-#define ASS_DEFAULT_FONT_SIZE   16
-#define ASS_DEFAULT_COLOR       0xffffff
-#define ASS_DEFAULT_BACK_COLOR  0
-#define ASS_DEFAULT_BOLD        0
-#define ASS_DEFAULT_ITALIC      0
-#define ASS_DEFAULT_UNDERLINE   0
-#define ASS_DEFAULT_ALIGNMENT   2
-#define ASS_DEFAULT_BORDERSTYLE 1
-/** @} */
+#include "libavutil/ass_internal.h"
 
 typedef struct FFASSDecoderContext {
     int readorder;
 } FFASSDecoderContext;
 
-/**
- * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS.
- * Can specify all fields explicitly
- *
- * @param avctx pointer to the AVCodecContext
- * @param play_res_x subtitle frame width
- * @param play_res_y subtitle frame height
- * @param font name of the default font face to use
- * @param font_size default font size to use
- * @param primary_color default text color to use (ABGR)
- * @param secondary_color default secondary text color to use (ABGR)
- * @param outline_color default outline color to use (ABGR)
- * @param back_color default background color to use (ABGR)
- * @param bold 1 for bold text, 0 for normal text
- * @param italic 1 for italic text, 0 for normal text
- * @param underline 1 for underline text, 0 for normal text
- * @param border_style 1 for outline, 3 for opaque box
- * @param alignment position of the text (left, center, top...), defined after
- *                  the layout of the numpad (1-3 sub, 4-6 mid, 7-9 top)
- * @return >= 0 on success otherwise an error code <0
- */
-int ff_ass_subtitle_header_full(AVCodecContext *avctx,
+static inline int ff_ass_subtitle_header_full(AVCodecContext *avctx,
                                 int play_res_x, int play_res_y,
                                 const char *font, int font_size,
                                 int primary_color, int secondary_color,
                                 int outline_color, int back_color,
                                 int bold, int italic, int underline,
-                                int border_style, int alignment);
-/**
- * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS.
- *
- * @param avctx pointer to the AVCodecContext
- * @param font name of the default font face to use
- * @param font_size default font size to use
- * @param color default text color to use (ABGR)
- * @param back_color default background color to use (ABGR)
- * @param bold 1 for bold text, 0 for normal text
- * @param italic 1 for italic text, 0 for normal text
- * @param underline 1 for underline text, 0 for normal text
- * @param alignment position of the text (left, center, top...), defined after
- *                  the layout of the numpad (1-3 sub, 4-6 mid, 7-9 top)
- * @return >= 0 on success otherwise an error code <0
- */
-int ff_ass_subtitle_header(AVCodecContext *avctx,
-                           const char *font, int font_size,
-                           int color, int back_color,
-                           int bold, int italic, int underline,
-                           int border_style, int alignment);
+                                int border_style, int alignment)
+{
+    avctx->subtitle_header = (uint8_t *)avpriv_ass_get_subtitle_header_full(
+                                play_res_x, play_res_y, font, font_size,
+                                primary_color, secondary_color, outline_color,
+                                back_color, bold,italic,underline,border_style,alignment,
+                                !(avctx->flags & AV_CODEC_FLAG_BITEXACT));
 
-/**
- * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS
- * with default style.
- *
- * @param avctx pointer to the AVCodecContext
- * @return >= 0 on success otherwise an error code <0
- */
-int ff_ass_subtitle_header_default(AVCodecContext *avctx);
+    if (!avctx->subtitle_header)
+        return AVERROR(ENOMEM);
+    avctx->subtitle_header_size = strlen((char *)avctx->subtitle_header);
+    return 0;
+}
 
-/**
- * Craft an ASS dialog string.
- */
-char *ff_ass_get_dialog(int readorder, int layer, const char *style,
-                        const char *speaker, const char *text);
+static inline int ff_ass_subtitle_header_default(AVCodecContext *avctx)
+{
+    avctx->subtitle_header = (uint8_t *)avpriv_ass_get_subtitle_header_default(!(avctx->flags & AV_CODEC_FLAG_BITEXACT));
 
-/**
- * Add an ASS dialog to a subtitle.
- */
-int ff_ass_add_rect(AVSubtitle *sub, const char *dialog,
-                    int readorder, int layer, const char *style,
-                    const char *speaker);
+    if (!avctx->subtitle_header)
+        return AVERROR(ENOMEM);
+    avctx->subtitle_header_size = strlen((char *)avctx->subtitle_header);
+    return 0;
+}
+
+static inline void ff_ass_decoder_flush(AVCodecContext *avctx)
+{
+    FFASSDecoderContext *s = avctx->priv_data;
+    if (!(avctx->flags2 & AV_CODEC_FLAG2_RO_FLUSH_NOOP))
+        s->readorder = 0;
+}
 
 /**
  * Add an ASS dialog to a subtitle.
  */
-int ff_ass_add_rect2(AVSubtitle *sub, const char *dialog,
-                     int readorder, int layer, const char *style,
-                     const char *speaker, unsigned *nb_rect_allocated);
+static inline int avpriv_ass_add_rect(AVSubtitle *sub, const char *dialog,
+                    int readorder, int layer, const char *style,
+                    const char *speaker)
+{
+    char *ass_str;
+    AVSubtitleRect **rects;
 
-/**
- * Helper to flush a text subtitles decoder making use of the
- * FFASSDecoderContext.
- */
-void ff_ass_decoder_flush(AVCodecContext *avctx);
+    rects = av_realloc_array(sub->rects, sub->num_rects+1, sizeof(*sub->rects));
+    if (!rects)
+        return AVERROR(ENOMEM);
+    sub->rects = rects;
+    rects[sub->num_rects]       = av_mallocz(sizeof(*rects[0]));
+    if (!rects[sub->num_rects])
+        return AVERROR(ENOMEM);
+    rects[sub->num_rects]->type = SUBTITLE_ASS;
+    ass_str = avpriv_ass_get_dialog(readorder, layer, style, speaker, dialog);
+    if (!ass_str)
+        return AVERROR(ENOMEM);
+    rects[sub->num_rects]->ass = ass_str;
+    sub->num_rects++;
+    return 0;
+}
 
-/**
- * Escape a text subtitle using ASS syntax into an AVBPrint buffer.
- * Newline characters will be escaped to \N.
- *
- * @param buf pointer to an initialized AVBPrint buffer
- * @param p source text
- * @param size size of the source text
- * @param linebreaks additional newline chars, which will be escaped to \N
- * @param keep_ass_markup braces and backslash will not be escaped if set
- */
-void ff_ass_bprint_text_event(AVBPrint *buf, const char *p, int size,
-                             const char *linebreaks, int keep_ass_markup);
 #endif /* AVCODEC_ASS_H */
diff --git a/libavcodec/assdec.c b/libavcodec/assdec.c
index f43b500aa7..1a1363471d 100644
--- a/libavcodec/assdec.c
+++ b/libavcodec/assdec.c
@@ -22,9 +22,9 @@
 #include <string.h>
 
 #include "avcodec.h"
-#include "ass.h"
 #include "codec_internal.h"
 #include "config_components.h"
+#include "libavutil/ass_internal.h"
 #include "libavutil/internal.h"
 #include "libavutil/mem.h"
 
diff --git a/libavcodec/assenc.c b/libavcodec/assenc.c
index 2ac40d5afe..391d690133 100644
--- a/libavcodec/assenc.c
+++ b/libavcodec/assenc.c
@@ -24,8 +24,8 @@
 #include <string.h>
 
 #include "avcodec.h"
-#include "ass.h"
 #include "codec_internal.h"
+#include "libavutil/ass_internal.h"
 #include "libavutil/avstring.h"
 #include "libavutil/internal.h"
 #include "libavutil/mem.h"
diff --git a/libavcodec/ccaption_dec.c b/libavcodec/ccaption_dec.c
index 34f0513b1a..5f706d985f 100644
--- a/libavcodec/ccaption_dec.c
+++ b/libavcodec/ccaption_dec.c
@@ -272,15 +272,12 @@ static av_cold int init_decoder(AVCodecContext *avctx)
     ctx->bg_color = CCCOL_BLACK;
     ctx->rollup = 2;
     ctx->cursor_row = 10;
-    ret = ff_ass_subtitle_header(avctx, "Monospace",
+    ret = ff_ass_subtitle_header_full(avctx, ASS_DEFAULT_PLAYRESX, ASS_DEFAULT_PLAYRESY, "Monospace",
                                  ASS_DEFAULT_FONT_SIZE,
-                                 ASS_DEFAULT_COLOR,
-                                 ASS_DEFAULT_BACK_COLOR,
-                                 ASS_DEFAULT_BOLD,
-                                 ASS_DEFAULT_ITALIC,
-                                 ASS_DEFAULT_UNDERLINE,
-                                 3,
-                                 ASS_DEFAULT_ALIGNMENT);
+                                 ASS_DEFAULT_COLOR, ASS_DEFAULT_COLOR,
+                                 ASS_DEFAULT_BACK_COLOR, ASS_DEFAULT_BACK_COLOR,
+                                 ASS_DEFAULT_BOLD, ASS_DEFAULT_ITALIC, ASS_DEFAULT_UNDERLINE,
+                                 3, ASS_DEFAULT_ALIGNMENT);
     if (ret < 0) {
         return ret;
     }
@@ -850,7 +847,6 @@ static int decode(AVCodecContext *avctx, AVSubtitle *sub,
     int len = avpkt->size;
     int ret = 0;
     int i;
-    unsigned nb_rect_allocated = 0;
 
     for (i = 0; i < len; i += 3) {
         uint8_t hi, cc_type = bptr[i] & 1;
@@ -887,7 +883,7 @@ static int decode(AVCodecContext *avctx, AVSubtitle *sub,
                                                      AV_TIME_BASE_Q, ms_tb);
             else
                 sub->end_display_time = -1;
-            ret = ff_ass_add_rect2(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL, &nb_rect_allocated);
+            ret = avpriv_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL);
             if (ret < 0)
                 return ret;
             ctx->last_real_time = sub->pts;
@@ -897,7 +893,7 @@ static int decode(AVCodecContext *avctx, AVSubtitle *sub,
 
     if (!bptr && !ctx->real_time && ctx->buffer[!ctx->buffer_index].str[0]) {
         bidx = !ctx->buffer_index;
-        ret = ff_ass_add_rect2(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL, &nb_rect_allocated);
+        ret = avpriv_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL);
         if (ret < 0)
             return ret;
         sub->pts = ctx->buffer_time[1];
@@ -915,7 +911,7 @@ static int decode(AVCodecContext *avctx, AVSubtitle *sub,
         capture_screen(ctx);
         ctx->buffer_changed = 0;
 
-        ret = ff_ass_add_rect2(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL, &nb_rect_allocated);
+        ret = avpriv_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL);
         if (ret < 0)
             return ret;
         sub->end_display_time = -1;
diff --git a/libavcodec/jacosubdec.c b/libavcodec/jacosubdec.c
index e3bf9f4226..40abdebcc6 100644
--- a/libavcodec/jacosubdec.c
+++ b/libavcodec/jacosubdec.c
@@ -182,7 +182,7 @@ static int jacosub_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
 
         av_bprint_init(&buffer, JSS_MAX_LINESIZE, JSS_MAX_LINESIZE);
         jacosub_to_ass(avctx, &buffer, ptr);
-        ret = ff_ass_add_rect(sub, buffer.str, s->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, buffer.str, s->readorder++, 0, NULL, NULL);
         av_bprint_finalize(&buffer, NULL);
         if (ret < 0)
             return ret;
diff --git a/libavcodec/libaribb24.c b/libavcodec/libaribb24.c
index 9658e1d5ac..360e20834b 100644
--- a/libavcodec/libaribb24.c
+++ b/libavcodec/libaribb24.c
@@ -274,7 +274,7 @@ next_region:
         av_log(avctx, AV_LOG_DEBUG, "Styled ASS line: %s\n",
                buf.str);
 
-        ret = ff_ass_add_rect(sub, buf.str, b24->read_order++,
+        ret = avpriv_ass_add_rect(sub, buf.str, b24->read_order++,
                               0, NULL, NULL);
     }
 
diff --git a/libavcodec/libzvbi-teletextdec.c b/libavcodec/libzvbi-teletextdec.c
index 2aab10a548..54a78342f2 100644
--- a/libavcodec/libzvbi-teletextdec.c
+++ b/libavcodec/libzvbi-teletextdec.c
@@ -153,12 +153,12 @@ static char *create_ass_text(TeletextContext *ctx, const char *text)
     AVBPrint buf;
 
     av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
-    ff_ass_bprint_text_event(&buf, text, strlen(text), "", 0);
+    avpriv_ass_bprint_text_event(&buf, text, strlen(text), "", 0);
     if (!av_bprint_is_complete(&buf)) {
         av_bprint_finalize(&buf, NULL);
         return NULL;
     }
-    dialog = ff_ass_get_dialog(ctx->readorder++, 0, NULL, NULL, buf.str);
+    dialog = avpriv_ass_get_dialog(ctx->readorder++, 0, NULL, NULL, buf.str);
     av_bprint_finalize(&buf, NULL);
     return dialog;
 }
@@ -225,7 +225,7 @@ static int gen_sub_text(TeletextContext *ctx, AVSubtitleRect *sub_rect, vbi_page
         }
         av_log(ctx, AV_LOG_DEBUG, "subtext:%s:txetbus\n", sub_rect->ass);
     } else {
-        sub_rect->type = SUBTITLE_NONE;
+        sub_rect->type = AV_SUBTITLE_FMT_NONE;
     }
     av_bprint_finalize(&buf, NULL);
     return 0;
@@ -395,7 +395,7 @@ static int gen_sub_ass(TeletextContext *ctx, AVSubtitleRect *sub_rect, vbi_page
 
     if (buf.len) {
         sub_rect->type = SUBTITLE_ASS;
-        sub_rect->ass = ff_ass_get_dialog(ctx->readorder++, 0, is_subtitle_page ? "Subtitle" : "Teletext", NULL, buf.str);
+        sub_rect->ass = avpriv_ass_get_dialog(ctx->readorder++, 0, is_subtitle_page ? "Subtitle" : "Teletext", NULL, buf.str);
 
         if (!sub_rect->ass) {
             av_bprint_finalize(&buf, NULL);
@@ -403,7 +403,7 @@ static int gen_sub_ass(TeletextContext *ctx, AVSubtitleRect *sub_rect, vbi_page
         }
         av_log(ctx, AV_LOG_DEBUG, "subtext:%s:txetbus\n", sub_rect->ass);
     } else {
-        sub_rect->type = SUBTITLE_NONE;
+        sub_rect->type = AV_SUBTITLE_FMT_NONE;
     }
     av_bprint_finalize(&buf, NULL);
     return 0;
@@ -463,7 +463,7 @@ static int gen_sub_bitmap(TeletextContext *ctx, AVSubtitleRect *sub_rect, vbi_pa
 
     if (vc >= vcend) {
         av_log(ctx, AV_LOG_DEBUG, "dropping empty page %3x\n", page->pgno);
-        sub_rect->type = SUBTITLE_NONE;
+        sub_rect->type = AV_SUBTITLE_FMT_NONE;
         return 0;
     }
 
@@ -696,7 +696,7 @@ static int teletext_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
         sub->num_rects = 0;
         sub->pts = ctx->pages->pts;
 
-        if (ctx->pages->sub_rect->type != SUBTITLE_NONE) {
+        if (ctx->pages->sub_rect->type != AV_SUBTITLE_FMT_NONE) {
             sub->rects = av_malloc(sizeof(*sub->rects));
             if (sub->rects) {
                 sub->num_rects = 1;
diff --git a/libavcodec/microdvddec.c b/libavcodec/microdvddec.c
index f36ad51468..de17400edd 100644
--- a/libavcodec/microdvddec.c
+++ b/libavcodec/microdvddec.c
@@ -309,7 +309,7 @@ static int microdvd_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
         }
     }
     if (new_line.len) {
-        int ret = ff_ass_add_rect(sub, new_line.str, s->readorder++, 0, NULL, NULL);
+        int ret = avpriv_ass_add_rect(sub, new_line.str, s->readorder++, 0, NULL, NULL);
         av_bprint_finalize(&new_line, NULL);
         if (ret < 0)
             return ret;
@@ -362,8 +362,9 @@ static int microdvd_init(AVCodecContext *avctx)
             }
         }
     }
-    return ff_ass_subtitle_header(avctx, font_buf.str, font_size, color,
-                                  ASS_DEFAULT_BACK_COLOR, bold, italic,
+    return ff_ass_subtitle_header_full(avctx, ASS_DEFAULT_PLAYRESX, ASS_DEFAULT_PLAYRESY,
+                                  font_buf.str, font_size, color, color,
+                                  ASS_DEFAULT_BACK_COLOR, ASS_DEFAULT_BACK_COLOR, bold, italic,
                                   underline, ASS_DEFAULT_BORDERSTYLE,
                                   alignment);
 }
diff --git a/libavcodec/movtextdec.c b/libavcodec/movtextdec.c
index 70162b4888..8f2459d45b 100644
--- a/libavcodec/movtextdec.c
+++ b/libavcodec/movtextdec.c
@@ -22,7 +22,6 @@
 #include "avcodec.h"
 #include "ass.h"
 #include "libavutil/opt.h"
-#include "libavutil/avstring.h"
 #include "libavutil/common.h"
 #include "libavutil/bprint.h"
 #include "libavutil/intreadwrite.h"
@@ -553,7 +552,7 @@ static int mov_text_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
     } else
         text_to_ass(&buf, ptr, end, avctx);
 
-    ret = ff_ass_add_rect(sub, buf.str, m->readorder++, 0, NULL, NULL);
+    ret = avpriv_ass_add_rect(sub, buf.str, m->readorder++, 0, NULL, NULL);
     av_bprint_finalize(&buf, NULL);
     if (ret < 0)
         return ret;
diff --git a/libavcodec/movtextenc.c b/libavcodec/movtextenc.c
index 728338f2cc..6f0b7a8a5c 100644
--- a/libavcodec/movtextenc.c
+++ b/libavcodec/movtextenc.c
@@ -26,8 +26,8 @@
 #include "libavutil/intreadwrite.h"
 #include "libavutil/mem.h"
 #include "libavutil/common.h"
-#include "ass_split.h"
-#include "ass.h"
+#include "libavutil/ass_split_internal.h"
+#include "libavutil/ass_internal.h"
 #include "bytestream.h"
 #include "codec_internal.h"
 
@@ -167,7 +167,7 @@ static int mov_text_encode_close(AVCodecContext *avctx)
 {
     MovTextContext *s = avctx->priv_data;
 
-    ff_ass_split_free(s->ass_ctx);
+    avpriv_ass_split_free(s->ass_ctx);
     av_freep(&s->style_attributes);
     av_freep(&s->fonts);
     av_bprint_finalize(&s->buffer, NULL);
@@ -222,7 +222,7 @@ static int encode_sample_description(AVCodecContext *avctx)
     else
         s->font_scale_factor = 1;
 
-    style = ff_ass_style_get(s->ass_ctx, "Default");
+    style = avpriv_ass_style_get(s->ass_ctx, "Default");
     if (!style && ass->styles_count) {
         style = &ass->styles[0];
     }
@@ -329,7 +329,7 @@ static av_cold int mov_text_encode_init(AVCodecContext *avctx)
 
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
 
-    s->ass_ctx = ff_ass_split(avctx->subtitle_header);
+    s->ass_ctx = avpriv_ass_split(avctx->subtitle_header);
     if (!s->ass_ctx)
         return AVERROR_INVALIDDATA;
     ret = encode_sample_description(avctx);
@@ -566,7 +566,7 @@ static void mov_text_ass_style_set(MovTextContext *s, ASSStyle *style)
 
 static void mov_text_dialog(MovTextContext *s, ASSDialog *dialog)
 {
-    ASSStyle *style = ff_ass_style_get(s->ass_ctx, dialog->style);
+    ASSStyle *style = avpriv_ass_style_get(s->ass_ctx, dialog->style);
 
     s->ass_dialog_style = style;
     mov_text_ass_style_set(s, style);
@@ -580,7 +580,7 @@ static void mov_text_cancel_overrides_cb(void *priv, const char *style_name)
     if (!style_name || !*style_name)
         style = s->ass_dialog_style;
     else
-        style= ff_ass_style_get(s->ass_ctx, style_name);
+        style= avpriv_ass_style_get(s->ass_ctx, style_name);
 
     mov_text_ass_style_set(s, style);
 }
@@ -652,12 +652,12 @@ static int mov_text_encode_frame(AVCodecContext *avctx, unsigned char *buf,
             return AVERROR(EINVAL);
         }
 
-        dialog = ff_ass_split_dialog(s->ass_ctx, ass);
+        dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
         if (!dialog)
             return AVERROR(ENOMEM);
         mov_text_dialog(s, dialog);
-        ff_ass_split_override_codes(&mov_text_callbacks, s, dialog->text);
-        ff_ass_free_dialog(&dialog);
+        avpriv_ass_split_override_codes(&mov_text_callbacks, s, dialog->text);
+        avpriv_ass_free_dialog(&dialog);
     }
 
     if (s->buffer.len > UINT16_MAX)
diff --git a/libavcodec/mpl2dec.c b/libavcodec/mpl2dec.c
index 56f008b65c..175bd319e1 100644
--- a/libavcodec/mpl2dec.c
+++ b/libavcodec/mpl2dec.c
@@ -73,7 +73,7 @@ static int mpl2_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
 
     av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
     if (ptr && avpkt->size > 0 && *ptr && !mpl2_event_to_ass(&buf, ptr))
-        ret = ff_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
     av_bprint_finalize(&buf, NULL);
     if (ret < 0)
         return ret;
diff --git a/libavcodec/realtextdec.c b/libavcodec/realtextdec.c
index c3e138a7ba..49d42a1d4d 100644
--- a/libavcodec/realtextdec.c
+++ b/libavcodec/realtextdec.c
@@ -66,7 +66,7 @@ static int realtext_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
 
     av_bprint_init(&buf, 0, 4096);
     if (ptr && avpkt->size > 0 && !rt_event_to_ass(&buf, ptr))
-        ret = ff_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
     av_bprint_finalize(&buf, NULL);
     if (ret < 0)
         return ret;
diff --git a/libavcodec/samidec.c b/libavcodec/samidec.c
index cf5dec955b..f35d312c2b 100644
--- a/libavcodec/samidec.c
+++ b/libavcodec/samidec.c
@@ -143,7 +143,7 @@ static int sami_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
         if (ret < 0)
             return ret;
         // TODO: pass escaped sami->encoded_source.str as source
-        ret = ff_ass_add_rect(sub, sami->full.str, sami->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, sami->full.str, sami->readorder++, 0, NULL, NULL);
         if (ret < 0)
             return ret;
     }
diff --git a/libavcodec/srtdec.c b/libavcodec/srtdec.c
index b2df34474e..ccf5981263 100644
--- a/libavcodec/srtdec.c
+++ b/libavcodec/srtdec.c
@@ -79,7 +79,7 @@ static int srt_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
 
     ret = srt_to_ass(avctx, &buffer, avpkt->data, x1, y1, x2, y2);
     if (ret >= 0)
-        ret = ff_ass_add_rect(sub, buffer.str, s->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, buffer.str, s->readorder++, 0, NULL, NULL);
     av_bprint_finalize(&buffer, NULL);
     if (ret < 0)
         return ret;
diff --git a/libavcodec/srtenc.c b/libavcodec/srtenc.c
index 51456c8b9d..2baa6e70ad 100644
--- a/libavcodec/srtenc.c
+++ b/libavcodec/srtenc.c
@@ -25,9 +25,9 @@
 #include "avcodec.h"
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
-#include "ass_split.h"
-#include "ass.h"
 #include "codec_internal.h"
+#include "libavutil/ass_split_internal.h"
+#include "libavutil/ass_internal.h"
 
 
 #define SRT_STACK_SIZE 64
@@ -96,7 +96,7 @@ static void srt_stack_push_pop(SRTContext *s, const char c, int close)
 
 static void srt_style_apply(SRTContext *s, const char *style)
 {
-    ASSStyle *st = ff_ass_style_get(s->ass_ctx, style);
+    ASSStyle *st = avpriv_ass_style_get(s->ass_ctx, style);
     if (st) {
         int c = st->primary_color & 0xFFFFFF;
         if (st->font_name && strcmp(st->font_name, ASS_DEFAULT_FONT) ||
@@ -137,7 +137,7 @@ static av_cold int srt_encode_init(AVCodecContext *avctx)
 {
     SRTContext *s = avctx->priv_data;
     s->avctx = avctx;
-    s->ass_ctx = ff_ass_split(avctx->subtitle_header);
+    s->ass_ctx = avpriv_ass_split(avctx->subtitle_header);
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
     return s->ass_ctx ? 0 : AVERROR_INVALIDDATA;
 }
@@ -247,14 +247,14 @@ static int encode_frame(AVCodecContext *avctx,
             return AVERROR(EINVAL);
         }
 
-        dialog = ff_ass_split_dialog(s->ass_ctx, ass);
+        dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
         if (!dialog)
             return AVERROR(ENOMEM);
         s->alignment_applied = 0;
         if (avctx->codec_id == AV_CODEC_ID_SUBRIP)
             srt_style_apply(s, dialog->style);
-        ff_ass_split_override_codes(cb, s, dialog->text);
-        ff_ass_free_dialog(&dialog);
+        avpriv_ass_split_override_codes(cb, s, dialog->text);
+        avpriv_ass_free_dialog(&dialog);
     }
 
     if (!av_bprint_is_complete(&s->buffer))
@@ -286,7 +286,7 @@ static int text_encode_frame(AVCodecContext *avctx,
 static int srt_encode_close(AVCodecContext *avctx)
 {
     SRTContext *s = avctx->priv_data;
-    ff_ass_split_free(s->ass_ctx);
+    avpriv_ass_split_free(s->ass_ctx);
     av_bprint_finalize(&s->buffer, NULL);
     return 0;
 }
diff --git a/libavcodec/subviewerdec.c b/libavcodec/subviewerdec.c
index 2bda5fa5c1..8651453e3f 100644
--- a/libavcodec/subviewerdec.c
+++ b/libavcodec/subviewerdec.c
@@ -57,7 +57,7 @@ static int subviewer_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
 
     av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
     if (ptr && avpkt->size > 0 && !subviewer_event_to_ass(&buf, ptr))
-        ret = ff_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
     av_bprint_finalize(&buf, NULL);
     if (ret < 0)
         return ret;
diff --git a/libavcodec/textdec.c b/libavcodec/textdec.c
index d509452391..06a25e7128 100644
--- a/libavcodec/textdec.c
+++ b/libavcodec/textdec.c
@@ -55,8 +55,8 @@ static int text_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
 
     av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
     if (ptr && avpkt->size > 0 && *ptr) {
-        ff_ass_bprint_text_event(&buf, ptr, avpkt->size, text->linebreaks, text->keep_ass_markup);
-        ret = ff_ass_add_rect(sub, buf.str, text->readorder++, 0, NULL, NULL);
+        avpriv_ass_bprint_text_event(&buf, ptr, avpkt->size, text->linebreaks, text->keep_ass_markup);
+        ret = avpriv_ass_add_rect(sub, buf.str, text->readorder++, 0, NULL, NULL);
     }
     av_bprint_finalize(&buf, NULL);
     if (ret < 0)
diff --git a/libavcodec/ttmlenc.c b/libavcodec/ttmlenc.c
index be1d8fb2e8..d4f11a87d2 100644
--- a/libavcodec/ttmlenc.c
+++ b/libavcodec/ttmlenc.c
@@ -32,8 +32,7 @@
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
 #include "libavutil/internal.h"
-#include "ass_split.h"
-#include "ass.h"
+#include "libavutil/ass_split_internal.h"
 #include "ttmlenc.h"
 
 typedef struct {
@@ -95,7 +94,7 @@ static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf,
             return AVERROR(EINVAL);
         }
 
-        dialog = ff_ass_split_dialog(s->ass_ctx, ass);
+        dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
         if (!dialog)
             return AVERROR(ENOMEM);
 
@@ -107,7 +106,7 @@ static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf,
             av_bprintf(&s->buffer, "\">");
         }
 
-        ret = ff_ass_split_override_codes(&ttml_callbacks, s, dialog->text);
+        ret = avpriv_ass_split_override_codes(&ttml_callbacks, s, dialog->text);
         if (ret < 0) {
             int log_level = (ret != AVERROR_INVALIDDATA ||
                              avctx->err_recognition & AV_EF_EXPLODE) ?
@@ -118,7 +117,7 @@ static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf,
                    av_err2str(ret));
 
             if (log_level == AV_LOG_ERROR) {
-                ff_ass_free_dialog(&dialog);
+                avpriv_ass_free_dialog(&dialog);
                 return ret;
             }
         }
@@ -126,7 +125,7 @@ static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf,
         if (dialog->style)
             av_bprintf(&s->buffer, "</span>");
 
-        ff_ass_free_dialog(&dialog);
+        avpriv_ass_free_dialog(&dialog);
     }
 
     if (!av_bprint_is_complete(&s->buffer))
@@ -148,7 +147,7 @@ static av_cold int ttml_encode_close(AVCodecContext *avctx)
 {
     TTMLContext *s = avctx->priv_data;
 
-    ff_ass_split_free(s->ass_ctx);
+    avpriv_ass_split_free(s->ass_ctx);
 
     av_bprint_finalize(&s->buffer, NULL);
 
@@ -372,7 +371,7 @@ static av_cold int ttml_encode_init(AVCodecContext *avctx)
 
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
 
-    if (!(s->ass_ctx = ff_ass_split(avctx->subtitle_header))) {
+    if (!(s->ass_ctx = avpriv_ass_split(avctx->subtitle_header))) {
         return AVERROR_INVALIDDATA;
     }
 
diff --git a/libavcodec/webvttdec.c b/libavcodec/webvttdec.c
index fcf1062d86..549e60fe62 100644
--- a/libavcodec/webvttdec.c
+++ b/libavcodec/webvttdec.c
@@ -90,7 +90,7 @@ static int webvtt_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
 
     av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
     if (ptr && avpkt->size > 0 && !webvtt_event_to_ass(&buf, ptr))
-        ret = ff_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
     av_bprint_finalize(&buf, NULL);
     if (ret < 0)
         return ret;
diff --git a/libavcodec/webvttenc.c b/libavcodec/webvttenc.c
index e433bb4579..24d60c5dc1 100644
--- a/libavcodec/webvttenc.c
+++ b/libavcodec/webvttenc.c
@@ -24,9 +24,9 @@
 #include "avcodec.h"
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
-#include "ass_split.h"
-#include "ass.h"
 #include "codec_internal.h"
+#include "libavutil/ass_split_internal.h"
+#include "libavutil/ass_internal.h"
 
 #define WEBVTT_STACK_SIZE 64
 typedef struct {
@@ -93,7 +93,7 @@ static void webvtt_stack_push_pop(WebVTTContext *s, const char c, int close)
 
 static void webvtt_style_apply(WebVTTContext *s, const char *style)
 {
-    ASSStyle *st = ff_ass_style_get(s->ass_ctx, style);
+    ASSStyle *st = avpriv_ass_style_get(s->ass_ctx, style);
     if (st) {
         if (st->bold != ASS_DEFAULT_BOLD) {
             webvtt_print(s, "<b>");
@@ -172,12 +172,12 @@ static int webvtt_encode_frame(AVCodecContext *avctx,
             return AVERROR(EINVAL);
         }
 
-        dialog = ff_ass_split_dialog(s->ass_ctx, ass);
+        dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
         if (!dialog)
             return AVERROR(ENOMEM);
         webvtt_style_apply(s, dialog->style);
-        ff_ass_split_override_codes(&webvtt_callbacks, s, dialog->text);
-        ff_ass_free_dialog(&dialog);
+        avpriv_ass_split_override_codes(&webvtt_callbacks, s, dialog->text);
+        avpriv_ass_free_dialog(&dialog);
     }
 
     if (!av_bprint_is_complete(&s->buffer))
@@ -197,7 +197,7 @@ static int webvtt_encode_frame(AVCodecContext *avctx,
 static int webvtt_encode_close(AVCodecContext *avctx)
 {
     WebVTTContext *s = avctx->priv_data;
-    ff_ass_split_free(s->ass_ctx);
+    avpriv_ass_split_free(s->ass_ctx);
     av_bprint_finalize(&s->buffer, NULL);
     return 0;
 }
@@ -206,7 +206,7 @@ static av_cold int webvtt_encode_init(AVCodecContext *avctx)
 {
     WebVTTContext *s = avctx->priv_data;
     s->avctx = avctx;
-    s->ass_ctx = ff_ass_split(avctx->subtitle_header);
+    s->ass_ctx = avpriv_ass_split(avctx->subtitle_header);
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
     return s->ass_ctx ? 0 : AVERROR_INVALIDDATA;
 }
diff --git a/libavutil/Makefile b/libavutil/Makefile
index 78f65c144a..a803add1e0 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -101,6 +101,8 @@ BUILT_HEADERS = avconfig.h                                              \
 OBJS = adler32.o                                                        \
        aes.o                                                            \
        aes_ctr.o                                                        \
+       ass.o                                                            \
+       ass_split.o                                                      \
        audio_fifo.o                                                     \
        avstring.o                                                       \
        avsscanf.o                                                       \
diff --git a/libavcodec/ass.c b/libavutil/ass.c
similarity index 59%
rename from libavcodec/ass.c
rename to libavutil/ass.c
index a1e560d7ff..9eeaa38ba9 100644
--- a/libavcodec/ass.c
+++ b/libavutil/ass.c
@@ -19,21 +19,22 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-#include "avcodec.h"
-#include "ass.h"
+#include "ass_internal.h"
+
+#include "subfmt.h"
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
 #include "libavutil/common.h"
 
-int ff_ass_subtitle_header_full(AVCodecContext *avctx,
-                                int play_res_x, int play_res_y,
-                                const char *font, int font_size,
-                                int primary_color, int secondary_color,
-                                int outline_color, int back_color,
-                                int bold, int italic, int underline,
-                                int border_style, int alignment)
+char* avpriv_ass_get_subtitle_header_full(int play_res_x, int play_res_y,
+                                    const char *font, int font_size,
+                                    int primary_color, int secondary_color,
+                                    int outline_color, int back_color,
+                                    int bold, int italic, int underline,
+                                    int border_style, int alignment,
+                                    int print_av_version)
 {
-    avctx->subtitle_header = av_asprintf(
+    char* header = av_asprintf(
              "[Script Info]\r\n"
              "; Script generated by FFmpeg/Lavc%s\r\n"
              "ScriptType: v4.00+\r\n"
@@ -68,34 +69,31 @@ int ff_ass_subtitle_header_full(AVCodecContext *avctx,
              "\r\n"
              "[Events]\r\n"
              "Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\r\n",
-             !(avctx->flags & AV_CODEC_FLAG_BITEXACT) ? AV_STRINGIFY(LIBAVCODEC_VERSION) : "",
+             print_av_version ? AV_STRINGIFY(LIBAVCODEC_VERSION) : "",
              play_res_x, play_res_y, font, font_size,
              primary_color, secondary_color, outline_color, back_color,
              -bold, -italic, -underline, border_style, alignment);
 
-    if (!avctx->subtitle_header)
-        return AVERROR(ENOMEM);
-    avctx->subtitle_header_size = strlen(avctx->subtitle_header);
-    return 0;
+    return header;
 }
 
-int ff_ass_subtitle_header(AVCodecContext *avctx,
-                           const char *font, int font_size,
+char* avpriv_ass_get_subtitle_header(const char *font, int font_size,
                            int color, int back_color,
                            int bold, int italic, int underline,
-                           int border_style, int alignment)
+                           int border_style, int alignment,
+                           int print_av_version)
 {
-    return ff_ass_subtitle_header_full(avctx,
-                               ASS_DEFAULT_PLAYRESX, ASS_DEFAULT_PLAYRESY,
-                               font, font_size, color, color,
-                               back_color, back_color,
-                               bold, italic, underline,
-                               border_style, alignment);
+    return avpriv_ass_get_subtitle_header_full(ASS_DEFAULT_PLAYRESX, ASS_DEFAULT_PLAYRESY,
+                                       font, font_size, color, color,
+                                       back_color, back_color,
+                                       bold, italic, underline,
+                                       border_style, alignment,
+                                       print_av_version);
 }
 
-int ff_ass_subtitle_header_default(AVCodecContext *avctx)
+char* avpriv_ass_get_subtitle_header_default(int print_av_version)
 {
-    return ff_ass_subtitle_header(avctx, ASS_DEFAULT_FONT,
+    return avpriv_ass_get_subtitle_header(ASS_DEFAULT_FONT,
                                ASS_DEFAULT_FONT_SIZE,
                                ASS_DEFAULT_COLOR,
                                ASS_DEFAULT_BACK_COLOR,
@@ -103,10 +101,11 @@ int ff_ass_subtitle_header_default(AVCodecContext *avctx)
                                ASS_DEFAULT_ITALIC,
                                ASS_DEFAULT_UNDERLINE,
                                ASS_DEFAULT_BORDERSTYLE,
-                               ASS_DEFAULT_ALIGNMENT);
+                               ASS_DEFAULT_ALIGNMENT,
+                               print_av_version);
 }
 
-char *ff_ass_get_dialog(int readorder, int layer, const char *style,
+char *avpriv_ass_get_dialog(int readorder, int layer, const char *style,
                         const char *speaker, const char *text)
 {
     return av_asprintf("%d,%d,%s,%s,0,0,0,,%s",
@@ -114,61 +113,17 @@ char *ff_ass_get_dialog(int readorder, int layer, const char *style,
                        speaker ? speaker : "", text);
 }
 
-int ff_ass_add_rect2(AVSubtitle *sub, const char *dialog,
-                    int readorder, int layer, const char *style,
-                    const char *speaker, unsigned *nb_rect_allocated)
-{
-    AVSubtitleRect **rects = sub->rects, *rect;
-    char *ass_str;
-    uint64_t new_nb = 0;
-
-    if (sub->num_rects >= UINT_MAX)
-        return AVERROR(ENOMEM);
-
-    if (nb_rect_allocated && *nb_rect_allocated <= sub->num_rects) {
-        if (sub->num_rects < UINT_MAX / 17 * 16) {
-            new_nb = sub->num_rects + sub->num_rects/16 + 1;
-        } else
-            new_nb = UINT_MAX;
-    } else if (!nb_rect_allocated)
-        new_nb = sub->num_rects + 1;
-
-    if (new_nb) {
-        rects = av_realloc_array(rects, new_nb, sizeof(*sub->rects));
-        if (!rects)
-            return AVERROR(ENOMEM);
-        if (nb_rect_allocated)
-            *nb_rect_allocated = new_nb;
-        sub->rects = rects;
-    }
-
-    rect       = av_mallocz(sizeof(*rect));
-    if (!rect)
-        return AVERROR(ENOMEM);
-    rects[sub->num_rects++] = rect;
-    rect->type = SUBTITLE_ASS;
-    ass_str = ff_ass_get_dialog(readorder, layer, style, speaker, dialog);
-    if (!ass_str)
-        return AVERROR(ENOMEM);
-    rect->ass = ass_str;
-    return 0;
-}
-
-int ff_ass_add_rect(AVSubtitle *sub, const char *dialog,
-                    int readorder, int layer, const char *style,
-                    const char *speaker)
+char *avpriv_ass_get_dialog_ex(int readorder, int layer, const char *style,
+                        const char *speaker, int margin_l, int margin_r,
+                        int margin_v, const char *text)
 {
-    return ff_ass_add_rect2(sub, dialog, readorder, layer, style, speaker, NULL);
-}
-
-void ff_ass_decoder_flush(AVCodecContext *avctx)
-{
-    FFASSDecoderContext *s = avctx->priv_data;
-    if (!(avctx->flags2 & AV_CODEC_FLAG2_RO_FLUSH_NOOP))
-        s->readorder = 0;
+    return av_asprintf("%d,%d,%s,%s,%d,%d,%d,,%s",
+                       readorder, layer, style ? style : "Default",
+                       speaker ? speaker : "", margin_l, margin_r,
+                       margin_v, text);
 }
 
-void ff_ass_bprint_text_event(AVBPrint *buf, const char *p, int size,
+void avpriv_ass_bprint_text_event(AVBPrint *buf, const char *p, int size,
                              const char *linebreaks, int keep_ass_markup)
 {
     const char *p_end = p + size;
diff --git a/libavutil/ass_internal.h b/libavutil/ass_internal.h
new file mode 100644
index 0000000000..cde5561cd3
--- /dev/null
+++ b/libavutil/ass_internal.h
@@ -0,0 +1,135 @@
+/*
+ * SSA/ASS common functions
+ * Copyright (c) 2010  Aurelien Jacobs <aurel@gnuage.org>
+ *
+ * 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
+ */
+
+#ifndef AVUTIL_ASS_INTERNAL_H
+#define AVUTIL_ASS_INTERNAL_H
+
+#include "subfmt.h"
+#include "libavutil/bprint.h"
+
+#define ASS_DEFAULT_PLAYRESX 384
+#define ASS_DEFAULT_PLAYRESY 288
+
+/**
+ * @name Default values for ASS style
+ * @{
+ */
+#define ASS_DEFAULT_FONT        "Arial"
+#define ASS_DEFAULT_FONT_SIZE   16
+#define ASS_DEFAULT_COLOR       0xffffff
+#define ASS_DEFAULT_BACK_COLOR  0
+#define ASS_DEFAULT_BOLD        0
+#define ASS_DEFAULT_ITALIC      0
+#define ASS_DEFAULT_UNDERLINE   0
+#define ASS_DEFAULT_ALIGNMENT   2
+#define ASS_DEFAULT_BORDERSTYLE 1
+/** @} */
+
+/**
+ * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS.
+ * Can specify all fields explicitly
+ *
+ * @param play_res_x subtitle frame width
+ * @param play_res_y subtitle frame height
+ * @param font name of the default font face to use
+ * @param font_size default font size to use
+ * @param primary_color default text color to use (ABGR)
+ * @param secondary_color default secondary text color to use (ABGR)
+ * @param outline_color default outline color to use (ABGR)
+ * @param back_color default background color to use (ABGR)
+ * @param bold 1 for bold text, 0 for normal text
+ * @param italic 1 for italic text, 0 for normal text
+ * @param underline 1 for underline text, 0 for normal text
+ * @param border_style 1 for outline, 3 for opaque box
+ * @param alignment position of the text (left, center, top...), defined after
+ *                  the layout of the numpad (1-3 sub, 4-6 mid, 7-9 top)
+ * @param print_av_version include library version in header
+ * @return a string containing the subtitle header that needs
+ *         to be released via av_free()
+ */
+char* avpriv_ass_get_subtitle_header_full(int play_res_x, int play_res_y,
+                                  const char *font, int font_size,
+                                  int primary_color, int secondary_color,
+                                  int outline_color, int back_color,
+                                  int bold, int italic, int underline,
+                                  int border_style, int alignment,
+                                  int print_av_version);
+
+/**
+ * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS.
+ *
+ * @param font name of the default font face to use
+ * @param font_size default font size to use
+ * @param color default text color to use (ABGR)
+ * @param back_color default background color to use (ABGR)
+ * @param bold 1 for bold text, 0 for normal text
+ * @param italic 1 for italic text, 0 for normal text
+ * @param underline 1 for underline text, 0 for normal text
+ * @param border_style 1 for outline, 3 for opaque box
+ * @param alignment position of the text (left, center, top...), defined after
+ *                  the layout of the numpad (1-3 sub, 4-6 mid, 7-9 top)
+ * @param print_av_version include library version in header
+ * @return a string containing the subtitle header that needs
+ *         to be released via av_free()
+ */
+char* avpriv_ass_get_subtitle_header(const char *font, int font_size,
+                                int color, int back_color,
+                                int bold, int italic, int underline,
+                                int border_style, int alignment,
+                                int print_av_version);
+
+/**
+ * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS
+ * with default style.
+ *
+ * @param print_av_version include library version in header
+ * @return a string containing the subtitle header that needs
+ *         to be released via av_free()
+ */
+char* avpriv_ass_get_subtitle_header_default(int print_av_version);
+
+/**
+ * Craft an ASS dialog string.
+ */
+char *avpriv_ass_get_dialog(int readorder, int layer, const char *style,
+                        const char *speaker, const char *text);
+
+/**
+ * Craft an ASS dialog string.
+ */
+char *avpriv_ass_get_dialog_ex(int readorder, int layer, const char *style,
+                        const char *speaker, int margin_l, int margin_r,
+                        int margin_v, const char *text);
+
+/**
+ * Escape a text subtitle using ASS syntax into an AVBPrint buffer.
+ * Newline characters will be escaped to \N.
+ *
+ * @param buf pointer to an initialized AVBPrint buffer
+ * @param p source text
+ * @param size size of the source text
+ * @param linebreaks additional newline chars, which will be escaped to \N
+ * @param keep_ass_markup braces and backslash will not be escaped if set
+ */
+void avpriv_ass_bprint_text_event(AVBPrint *buf, const char *p, int size,
+                             const char *linebreaks, int keep_ass_markup);
+
+#endif /* AVUTIL_ASS_INTERNAL_H */
diff --git a/libavcodec/ass_split.c b/libavutil/ass_split.c
similarity index 94%
rename from libavcodec/ass_split.c
rename to libavutil/ass_split.c
index 73ef6196c5..deb8b2e07f 100644
--- a/libavcodec/ass_split.c
+++ b/libavutil/ass_split.c
@@ -28,7 +28,7 @@
 #include "libavutil/error.h"
 #include "libavutil/macros.h"
 #include "libavutil/mem.h"
-#include "ass_split.h"
+#include "ass_split_internal.h"
 
 typedef enum {
     ASS_STR,
@@ -379,7 +379,7 @@ static int ass_split(ASSSplitContext *ctx, const char *buf)
     return buf ? 0 : AVERROR_INVALIDDATA;
 }
 
-ASSSplitContext *ff_ass_split(const char *buf)
+ASSSplitContext *avpriv_ass_split(const char *buf)
 {
     ASSSplitContext *ctx = av_mallocz(sizeof(*ctx));
     if (!ctx)
@@ -388,7 +388,7 @@ ASSSplitContext *ff_ass_split(const char *buf)
         buf += 3;
     ctx->current_section = -1;
     if (ass_split(ctx, buf) < 0) {
-        ff_ass_split_free(ctx);
+        avpriv_ass_split_free(ctx);
         return NULL;
     }
     return ctx;
@@ -418,7 +418,7 @@ static void free_section(ASSSplitContext *ctx, const ASSSection *section)
         av_freep((uint8_t *)&ctx->ass + section->offset);
 }
 
-void ff_ass_free_dialog(ASSDialog **dialogp)
+void avpriv_ass_free_dialog(ASSDialog **dialogp)
 {
     ASSDialog *dialog = *dialogp;
     if (!dialog)
@@ -430,7 +430,7 @@ void ff_ass_free_dialog(ASSDialog **dialogp)
     av_freep(dialogp);
 }
 
-ASSDialog *ff_ass_split_dialog(ASSSplitContext *ctx, const char *buf)
+ASSDialog *avpriv_ass_split_dialog(ASSSplitContext *ctx, const char *buf)
 {
     int i;
     static const ASSFields fields[] = {
@@ -457,7 +457,7 @@ ASSDialog *ff_ass_split_dialog(ASSSplitContext *ctx, const char *buf)
         buf = skip_space(buf);
         len = last ? strlen(buf) : strcspn(buf, ",");
         if (len >= INT_MAX) {
-            ff_ass_free_dialog(&dialog);
+            avpriv_ass_free_dialog(&dialog);
             return NULL;
         }
         convert_func[type](ptr, buf, len);
@@ -467,7 +467,7 @@ ASSDialog *ff_ass_split_dialog(ASSSplitContext *ctx, const char *buf)
     return dialog;
 }
 
-void ff_ass_split_free(ASSSplitContext *ctx)
+void avpriv_ass_split_free(ASSSplitContext *ctx)
 {
     if (ctx) {
         int i;
@@ -480,7 +480,7 @@ void ff_ass_split_free(ASSSplitContext *ctx)
 }
 
 
-int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
+int avpriv_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
                                 const char *buf)
 {
     const char *text = NULL;
@@ -503,8 +503,8 @@ int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
             while (*buf == '\\') {
                 char style[2], c[2], sep[2], c_num[2] = "0", tmp[128] = {0};
                 unsigned int color = 0xFFFFFFFF;
-                int len, size = -1, an = -1, alpha = -1;
-                int x1, y1, x2, y2, t1 = -1, t2 = -1;
+                int len, size = -1, an = -1, alpha = -1, scale = 0;
+                int x1, y1, x2, y2, t1 = -1, t2 = -1, accel = 1;
                 if (sscanf(buf, "\\%1[bisu]%1[01\\}]%n", style, c, &len) > 1) {
                     int close = c[0] == '0' ? 1 : c[0] == '1' ? 0 : -1;
                     len += close != -1;
@@ -552,6 +552,14 @@ int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
                 } else if (sscanf(buf, "\\org(%d,%d)%1[\\}]%n", &x1, &y1, sep, &len) > 2) {
                     if (callbacks->origin)
                         callbacks->origin(priv, x1, y1);
+                } else if (sscanf(buf, "\\t(%d,%d,%1[\\}]%n", &t1, &t2, sep, &len) > 2 ||
+                           sscanf(buf, "\\t(%d,%d,%d,%1[\\}]%n", &t1, &t2, &accel, sep, &len) > 3) {
+                    if (callbacks->animate)
+                        callbacks->animate(priv, t1, t2, accel, tmp);
+                } else if (sscanf(buf, "\\p%1[\\}]%n", sep, &len) > 0 ||
+                           sscanf(buf, "\\p%u%1[\\}]%n", &scale, sep, &len) > 1) {
+                    if (callbacks->drawing_mode)
+                        callbacks->drawing_mode(priv, scale);
                 } else {
                     len = strcspn(buf+1, "\\}") + 2;  /* skip unknown code */
                 }
@@ -575,7 +583,7 @@ int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
     return 0;
 }
 
-ASSStyle *ff_ass_style_get(ASSSplitContext *ctx, const char *style)
+ASSStyle *avpriv_ass_style_get(ASSSplitContext *ctx, const char *style)
 {
     ASS *ass = &ctx->ass;
     int i;
diff --git a/libavcodec/ass_split.h b/libavutil/ass_split_internal.h
similarity index 86%
rename from libavcodec/ass_split.h
rename to libavutil/ass_split_internal.h
index a45fb9b8a1..eee49ef0f5 100644
--- a/libavcodec/ass_split.h
+++ b/libavutil/ass_split_internal.h
@@ -19,8 +19,8 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-#ifndef AVCODEC_ASS_SPLIT_H
-#define AVCODEC_ASS_SPLIT_H
+#ifndef AVUTIL_ASS_SPLIT_INTERNAL_H
+#define AVUTIL_ASS_SPLIT_INTERNAL_H
 
 /**
  * fields extracted from the [Script Info] section
@@ -81,7 +81,7 @@ typedef struct {
     char *effect;
     char *text;     /**< actual text which will be displayed as a subtitle,
                          can include style override control codes (see
-                         ff_ass_split_override_codes()) */
+                         avpriv_ass_split_override_codes()) */
 } ASSDialog;
 
 /**
@@ -107,12 +107,12 @@ typedef struct ASSSplitContext ASSSplitContext;
  * @param buf String containing the ASS formatted data.
  * @return Newly allocated struct containing split data.
  */
-ASSSplitContext *ff_ass_split(const char *buf);
+ASSSplitContext *avpriv_ass_split(const char *buf);
 
 /**
- * Free a dialogue obtained from ff_ass_split_dialog().
+ * Free a dialogue obtained from avpriv_ass_split_dialog().
  */
-void ff_ass_free_dialog(ASSDialog **dialogp);
+void avpriv_ass_free_dialog(ASSDialog **dialogp);
 
 /**
  * Split one ASS Dialogue line from a string buffer.
@@ -121,14 +121,14 @@ void ff_ass_free_dialog(ASSDialog **dialogp);
  * @param buf String containing the ASS "Dialogue" line.
  * @return Pointer to the split ASSDialog. Must be freed with ff_ass_free_dialog()
  */
-ASSDialog *ff_ass_split_dialog(ASSSplitContext *ctx, const char *buf);
+ASSDialog *avpriv_ass_split_dialog(ASSSplitContext *ctx, const char *buf);
 
 /**
  * Free all the memory allocated for an ASSSplitContext.
  *
  * @param ctx Context previously initialized by ff_ass_split().
  */
-void ff_ass_split_free(ASSSplitContext *ctx);
+void avpriv_ass_split_free(ASSSplitContext *ctx);
 
 
 /**
@@ -141,6 +141,7 @@ typedef struct {
      * @{
      */
     void (*text)(void *priv, const char *text, int len);
+    void (*hard_space)(void *priv);
     void (*new_line)(void *priv, int forced);
     void (*style)(void *priv, char style, int close);
     void (*color)(void *priv, unsigned int /* color */, unsigned int color_id);
@@ -156,7 +157,16 @@ typedef struct {
      * @{
      */
     void (*move)(void *priv, int x1, int y1, int x2, int y2, int t1, int t2);
+    void (*animate)(void *priv, int t1, int t2, int accel, char *style);
     void (*origin)(void *priv, int x, int y);
+    void (*drawing_mode)(void *priv, int scale);
+    /** @} */
+
+    /**
+     * @defgroup ass_ext    ASS extensible parsing callback
+     * @{
+     */
+    void (*ext)(void *priv, int ext_id, const char *text, int p1, int p2);
     /** @} */
 
     /**
@@ -176,7 +186,7 @@ typedef struct {
  * @param buf The ASS "Dialogue" Text field to split.
  * @return >= 0 on success otherwise an error code <0
  */
-int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
+int avpriv_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
                                 const char *buf);
 
 /**
@@ -186,6 +196,6 @@ int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
  * @param style name of the style to search for.
  * @return the ASSStyle corresponding to style, or NULL if style can't be found
  */
-ASSStyle *ff_ass_style_get(ASSSplitContext *ctx, const char *style);
+ASSStyle *avpriv_ass_style_get(ASSSplitContext *ctx, const char *style);
 
-#endif /* AVCODEC_ASS_SPLIT_H */
+#endif /* AVUTIL_ASS_SPLIT_INTERNAL_H */
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v4 07/23] avcodec/subtitles: Replace deprecated enum values
  2022-05-28 13:25     ` [FFmpeg-devel] [PATCH v4 00/23] Subtitle Filtering 2022 ffmpegagent
                         ` (5 preceding siblings ...)
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 06/23] avcodec, avutil: Move ass helper functions to avutil as avpriv_ and extend ass dialog parsing softworkz
@ 2022-05-28 13:25       ` softworkz
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 08/23] fftools/play, probe: Adjust for subtitle changes softworkz
                         ` (16 subsequent siblings)
  23 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-05-28 13:25 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/ass.h       | 2 +-
 libavcodec/assdec.c    | 2 +-
 libavcodec/dvbsubdec.c | 2 +-
 libavcodec/dvdsubdec.c | 2 +-
 libavcodec/dvdsubenc.c | 2 +-
 libavcodec/pgssubdec.c | 2 +-
 libavcodec/xsubdec.c   | 2 +-
 7 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/libavcodec/ass.h b/libavcodec/ass.h
index 8bc13d7ab8..43c5ad651a 100644
--- a/libavcodec/ass.h
+++ b/libavcodec/ass.h
@@ -83,7 +83,7 @@ static inline int avpriv_ass_add_rect(AVSubtitle *sub, const char *dialog,
     rects[sub->num_rects]       = av_mallocz(sizeof(*rects[0]));
     if (!rects[sub->num_rects])
         return AVERROR(ENOMEM);
-    rects[sub->num_rects]->type = SUBTITLE_ASS;
+    rects[sub->num_rects]->type = AV_SUBTITLE_FMT_ASS;
     ass_str = avpriv_ass_get_dialog(readorder, layer, style, speaker, dialog);
     if (!ass_str)
         return AVERROR(ENOMEM);
diff --git a/libavcodec/assdec.c b/libavcodec/assdec.c
index 1a1363471d..7bb60c9b26 100644
--- a/libavcodec/assdec.c
+++ b/libavcodec/assdec.c
@@ -53,7 +53,7 @@ static int ass_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
     if (!sub->rects[0])
         return AVERROR(ENOMEM);
     sub->num_rects = 1;
-    sub->rects[0]->type = SUBTITLE_ASS;
+    sub->rects[0]->type = AV_SUBTITLE_FMT_ASS;
     sub->rects[0]->ass  = av_strdup(avpkt->data);
     if (!sub->rects[0]->ass)
         return AVERROR(ENOMEM);
diff --git a/libavcodec/dvbsubdec.c b/libavcodec/dvbsubdec.c
index 6e510d12c7..fb54cf3f33 100644
--- a/libavcodec/dvbsubdec.c
+++ b/libavcodec/dvbsubdec.c
@@ -796,7 +796,7 @@ static int save_subtitle_set(AVCodecContext *avctx, AVSubtitle *sub, int *got_ou
             rect->w = region->width;
             rect->h = region->height;
             rect->nb_colors = (1 << region->depth);
-            rect->type      = SUBTITLE_BITMAP;
+            rect->type      = AV_SUBTITLE_FMT_BITMAP;
             rect->linesize[0] = region->width;
 
             clut = get_clut(ctx, region->clut);
diff --git a/libavcodec/dvdsubdec.c b/libavcodec/dvdsubdec.c
index b54073393e..058f4e22c5 100644
--- a/libavcodec/dvdsubdec.c
+++ b/libavcodec/dvdsubdec.c
@@ -407,7 +407,7 @@ static int decode_dvd_subtitles(DVDSubContext *ctx, AVSubtitle *sub_header,
                 sub_header->rects[0]->y = y1;
                 sub_header->rects[0]->w = w;
                 sub_header->rects[0]->h = h;
-                sub_header->rects[0]->type = SUBTITLE_BITMAP;
+                sub_header->rects[0]->type = AV_SUBTITLE_FMT_BITMAP;
                 sub_header->rects[0]->linesize[0] = w;
                 sub_header->rects[0]->flags = is_menu ? AV_SUBTITLE_FLAG_FORCED : 0;
             }
diff --git a/libavcodec/dvdsubenc.c b/libavcodec/dvdsubenc.c
index d29db7d49c..24da94faee 100644
--- a/libavcodec/dvdsubenc.c
+++ b/libavcodec/dvdsubenc.c
@@ -269,7 +269,7 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
     if (rects == 0 || !h->rects)
         return AVERROR(EINVAL);
     for (i = 0; i < rects; i++)
-        if (h->rects[i]->type != SUBTITLE_BITMAP) {
+        if (h->rects[i]->type != AV_SUBTITLE_FMT_BITMAP) {
             av_log(avctx, AV_LOG_ERROR, "Bitmap subtitle required\n");
             return AVERROR(EINVAL);
         }
diff --git a/libavcodec/pgssubdec.c b/libavcodec/pgssubdec.c
index e50c6766c5..05399863b6 100644
--- a/libavcodec/pgssubdec.c
+++ b/libavcodec/pgssubdec.c
@@ -535,7 +535,7 @@ static int display_end_segment(AVCodecContext *avctx, AVSubtitle *sub,
         if (!rect)
             return AVERROR(ENOMEM);
         sub->rects[sub->num_rects++] = rect;
-        rect->type = SUBTITLE_BITMAP;
+        rect->type = AV_SUBTITLE_FMT_BITMAP;
 
         /* Process bitmap */
         object = find_object(ctx->presentation.objects[i].id, &ctx->objects);
diff --git a/libavcodec/xsubdec.c b/libavcodec/xsubdec.c
index d62fa164a5..30c3595c97 100644
--- a/libavcodec/xsubdec.c
+++ b/libavcodec/xsubdec.c
@@ -107,7 +107,7 @@ static int decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
     sub->num_rects = 1;
     rect->x = x; rect->y = y;
     rect->w = w; rect->h = h;
-    rect->type = SUBTITLE_BITMAP;
+    rect->type = AV_SUBTITLE_FMT_BITMAP;
     rect->linesize[0] = w;
     rect->data[0] = av_malloc(w * h);
     rect->nb_colors = 4;
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v4 08/23] fftools/play, probe: Adjust for subtitle changes
  2022-05-28 13:25     ` [FFmpeg-devel] [PATCH v4 00/23] Subtitle Filtering 2022 ffmpegagent
                         ` (6 preceding siblings ...)
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 07/23] avcodec/subtitles: Replace deprecated enum values softworkz
@ 2022-05-28 13:25       ` softworkz
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 09/23] avfilter/subtitles: Add subtitles.c for subtitle frame allocation softworkz
                         ` (15 subsequent siblings)
  23 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-05-28 13:25 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 fftools/ffplay.c  | 102 +++++++++++++++++++++-------------------------
 fftools/ffprobe.c |  47 +++++++++++++--------
 2 files changed, 77 insertions(+), 72 deletions(-)

diff --git a/fftools/ffplay.c b/fftools/ffplay.c
index 040afa0189..111e157979 100644
--- a/fftools/ffplay.c
+++ b/fftools/ffplay.c
@@ -153,7 +153,6 @@ typedef struct Clock {
 /* Common struct for handling all types of decoded data and allocated render buffers. */
 typedef struct Frame {
     AVFrame *frame;
-    AVSubtitle sub;
     int serial;
     double pts;           /* presentation timestamp for the frame */
     double duration;      /* estimated duration of the frame */
@@ -574,7 +573,7 @@ static int decoder_init(Decoder *d, AVCodecContext *avctx, PacketQueue *queue, S
     return 0;
 }
 
-static int decoder_decode_frame(Decoder *d, AVFrame *frame, AVSubtitle *sub) {
+static int decoder_decode_frame(Decoder *d, AVFrame *frame) {
     int ret = AVERROR(EAGAIN);
 
     for (;;) {
@@ -608,6 +607,9 @@ static int decoder_decode_frame(Decoder *d, AVFrame *frame, AVSubtitle *sub) {
                             }
                         }
                         break;
+                    case AVMEDIA_TYPE_SUBTITLE:
+                        ret = avcodec_receive_frame(d->avctx, frame);
+                        break;
                 }
                 if (ret == AVERROR_EOF) {
                     d->finished = d->pkt_serial;
@@ -640,25 +642,11 @@ static int decoder_decode_frame(Decoder *d, AVFrame *frame, AVSubtitle *sub) {
             av_packet_unref(d->pkt);
         } while (1);
 
-        if (d->avctx->codec_type == AVMEDIA_TYPE_SUBTITLE) {
-            int got_frame = 0;
-            ret = avcodec_decode_subtitle2(d->avctx, sub, &got_frame, d->pkt);
-            if (ret < 0) {
-                ret = AVERROR(EAGAIN);
-            } else {
-                if (got_frame && !d->pkt->data) {
-                    d->packet_pending = 1;
-                }
-                ret = got_frame ? 0 : (d->pkt->data ? AVERROR(EAGAIN) : AVERROR_EOF);
-            }
-            av_packet_unref(d->pkt);
+        if (avcodec_send_packet(d->avctx, d->pkt) == AVERROR(EAGAIN)) {
+            av_log(d->avctx, AV_LOG_ERROR, "Receive_frame and send_packet both returned EAGAIN, which is an API violation.\n");
+            d->packet_pending = 1;
         } else {
-            if (avcodec_send_packet(d->avctx, d->pkt) == AVERROR(EAGAIN)) {
-                av_log(d->avctx, AV_LOG_ERROR, "Receive_frame and send_packet both returned EAGAIN, which is an API violation.\n");
-                d->packet_pending = 1;
-            } else {
-                av_packet_unref(d->pkt);
-            }
+            av_packet_unref(d->pkt);
         }
     }
 }
@@ -671,7 +659,6 @@ static void decoder_destroy(Decoder *d) {
 static void frame_queue_unref_item(Frame *vp)
 {
     av_frame_unref(vp->frame);
-    avsubtitle_free(&vp->sub);
 }
 
 static int frame_queue_init(FrameQueue *f, PacketQueue *pktq, int max_size, int keep_last)
@@ -969,7 +956,7 @@ static void video_image_display(VideoState *is)
         if (frame_queue_nb_remaining(&is->subpq) > 0) {
             sp = frame_queue_peek(&is->subpq);
 
-            if (vp->pts >= sp->pts + ((float) sp->sub.start_display_time / 1000)) {
+            if (vp->pts >= sp->pts) {
                 if (!sp->uploaded) {
                     uint8_t* pixels[4];
                     int pitch[4];
@@ -981,25 +968,27 @@ static void video_image_display(VideoState *is)
                     if (realloc_texture(&is->sub_texture, SDL_PIXELFORMAT_ARGB8888, sp->width, sp->height, SDL_BLENDMODE_BLEND, 1) < 0)
                         return;
 
-                    for (i = 0; i < sp->sub.num_rects; i++) {
-                        AVSubtitleRect *sub_rect = sp->sub.rects[i];
+                    for (i = 0; i < sp->frame->num_subtitle_areas; i++) {
+                        AVSubtitleArea *area = sp->frame->subtitle_areas[i];
+                        SDL_Rect sdl_rect = { .x = area->x, .y = area->y, .w = area->w, .h = area->h };
 
-                        sub_rect->x = av_clip(sub_rect->x, 0, sp->width );
-                        sub_rect->y = av_clip(sub_rect->y, 0, sp->height);
-                        sub_rect->w = av_clip(sub_rect->w, 0, sp->width  - sub_rect->x);
-                        sub_rect->h = av_clip(sub_rect->h, 0, sp->height - sub_rect->y);
+                        area->x = av_clip(area->x, 0, sp->width );
+                        area->y = av_clip(area->y, 0, sp->height);
+                        area->w = av_clip(area->w, 0, sp->width  - area->x);
+                        area->h = av_clip(area->h, 0, sp->height - area->y);
 
                         is->sub_convert_ctx = sws_getCachedContext(is->sub_convert_ctx,
-                            sub_rect->w, sub_rect->h, AV_PIX_FMT_PAL8,
-                            sub_rect->w, sub_rect->h, AV_PIX_FMT_BGRA,
+                            area->w, area->h, AV_PIX_FMT_PAL8,
+                            area->w, area->h, AV_PIX_FMT_BGRA,
                             0, NULL, NULL, NULL);
                         if (!is->sub_convert_ctx) {
                             av_log(NULL, AV_LOG_FATAL, "Cannot initialize the conversion context\n");
                             return;
                         }
-                        if (!SDL_LockTexture(is->sub_texture, (SDL_Rect *)sub_rect, (void **)pixels, pitch)) {
-                            sws_scale(is->sub_convert_ctx, (const uint8_t * const *)sub_rect->data, sub_rect->linesize,
-                                      0, sub_rect->h, pixels, pitch);
+                        if (!SDL_LockTexture(is->sub_texture, &sdl_rect, (void **)pixels, pitch)) {
+                            const uint8_t* data[2] = { area->buf[0]->data, (uint8_t *)&area->pal };
+                            sws_scale(is->sub_convert_ctx, data, area->linesize,
+                                      0, area->h, pixels, pitch);
                             SDL_UnlockTexture(is->sub_texture);
                         }
                     }
@@ -1026,16 +1015,18 @@ static void video_image_display(VideoState *is)
 #if USE_ONEPASS_SUBTITLE_RENDER
         SDL_RenderCopy(renderer, is->sub_texture, NULL, &rect);
 #else
-        int i;
+        unsigned i;
         double xratio = (double)rect.w / (double)sp->width;
         double yratio = (double)rect.h / (double)sp->height;
-        for (i = 0; i < sp->sub.num_rects; i++) {
-            SDL_Rect *sub_rect = (SDL_Rect*)sp->sub.rects[i];
-            SDL_Rect target = {.x = rect.x + sub_rect->x * xratio,
-                               .y = rect.y + sub_rect->y * yratio,
-                               .w = sub_rect->w * xratio,
-                               .h = sub_rect->h * yratio};
-            SDL_RenderCopy(renderer, is->sub_texture, sub_rect, &target);
+        for (i = 0; i < sp->frame->num_subtitle_areas; i++) {
+            AVSubtitleArea *area = sp->frame->subtitle_areas[i];
+            SDL_Rect sub_rect = { .x = area->x, .y = area->y,
+                                  .w = area->w, .h = area->h};
+            SDL_Rect target = {.x = rect.x + sub_rect.x * xratio,
+                               .y = rect.y + sub_rect.y * yratio,
+                               .w = sub_rect.w * xratio,
+                               .h = sub_rect.h * yratio};
+            SDL_RenderCopy(renderer, is->sub_texture, &sub_rect, &target);
         }
 #endif
     }
@@ -1639,19 +1630,20 @@ retry:
                         sp2 = NULL;
 
                     if (sp->serial != is->subtitleq.serial
-                            || (is->vidclk.pts > (sp->pts + ((float) sp->sub.end_display_time / 1000)))
-                            || (sp2 && is->vidclk.pts > (sp2->pts + ((float) sp2->sub.start_display_time / 1000))))
+                            || (is->vidclk.pts > (sp->pts + ((float) sp->frame->subtitle_timing.duration / AV_TIME_BASE)))
+                            || (sp2 && is->vidclk.pts > (sp2->pts)))
                     {
                         if (sp->uploaded) {
                             int i;
-                            for (i = 0; i < sp->sub.num_rects; i++) {
-                                AVSubtitleRect *sub_rect = sp->sub.rects[i];
+                            for (i = 0; i < sp->frame->num_subtitle_areas; i++) {
+                                AVSubtitleArea *area = sp->frame->subtitle_areas[i];
+                                SDL_Rect sdl_rect = { .x = area->x, .y = area->y, .w = area->w, .h = area->h };
                                 uint8_t *pixels;
                                 int pitch, j;
 
-                                if (!SDL_LockTexture(is->sub_texture, (SDL_Rect *)sub_rect, (void **)&pixels, &pitch)) {
-                                    for (j = 0; j < sub_rect->h; j++, pixels += pitch)
-                                        memset(pixels, 0, sub_rect->w << 2);
+                                if (!SDL_LockTexture(is->sub_texture, &sdl_rect, (void **)&pixels, &pitch)) {
+                                    for (j = 0; j < area->h; j++, pixels += pitch)
+                                        memset(pixels, 0, area->w << 2);
                                     SDL_UnlockTexture(is->sub_texture);
                                 }
                             }
@@ -1762,7 +1754,7 @@ static int get_video_frame(VideoState *is, AVFrame *frame)
 {
     int got_picture;
 
-    if ((got_picture = decoder_decode_frame(&is->viddec, frame, NULL)) < 0)
+    if ((got_picture = decoder_decode_frame(&is->viddec, frame)) < 0)
         return -1;
 
     if (got_picture) {
@@ -2032,7 +2024,7 @@ static int audio_thread(void *arg)
         return AVERROR(ENOMEM);
 
     do {
-        if ((got_frame = decoder_decode_frame(&is->auddec, frame, NULL)) < 0)
+        if ((got_frame = decoder_decode_frame(&is->auddec, frame)) < 0)
             goto the_end;
 
         if (got_frame) {
@@ -2229,14 +2221,14 @@ static int subtitle_thread(void *arg)
         if (!(sp = frame_queue_peek_writable(&is->subpq)))
             return 0;
 
-        if ((got_subtitle = decoder_decode_frame(&is->subdec, NULL, &sp->sub)) < 0)
+        if ((got_subtitle = decoder_decode_frame(&is->subdec, sp->frame)) < 0)
             break;
 
         pts = 0;
 
-        if (got_subtitle && sp->sub.format == 0) {
-            if (sp->sub.pts != AV_NOPTS_VALUE)
-                pts = sp->sub.pts / (double)AV_TIME_BASE;
+        if (got_subtitle && sp->frame->format == AV_SUBTITLE_FMT_BITMAP) {
+            if (sp->frame->subtitle_timing.start_pts != AV_NOPTS_VALUE)
+                pts = (double)sp->frame->subtitle_timing.start_pts / (double)AV_TIME_BASE;
             sp->pts = pts;
             sp->serial = is->subdec.pkt_serial;
             sp->width = is->subdec.avctx->width;
@@ -2246,7 +2238,7 @@ static int subtitle_thread(void *arg)
             /* now we can update the picture count */
             frame_queue_push(&is->subpq);
         } else if (got_subtitle) {
-            avsubtitle_free(&sp->sub);
+            av_frame_free(&sp->frame);
         }
     }
     return 0;
diff --git a/fftools/ffprobe.c b/fftools/ffprobe.c
index c51c82ff65..d8e63c6df4 100644
--- a/fftools/ffprobe.c
+++ b/fftools/ffprobe.c
@@ -2452,22 +2452,42 @@ static void show_packet(WriterContext *w, InputFile *ifile, AVPacket *pkt, int p
     fflush(stdout);
 }
 
-static void show_subtitle(WriterContext *w, AVSubtitle *sub, AVStream *stream,
+static void show_subtitle(WriterContext *w, AVFrame *sub, AVStream *stream,
                           AVFormatContext *fmt_ctx)
 {
     AVBPrint pbuf;
+    const char *s;
 
     av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
 
     writer_print_section_header(w, SECTION_ID_SUBTITLE);
 
     print_str ("media_type",         "subtitle");
-    print_ts  ("pts",                 sub->pts);
-    print_time("pts_time",            sub->pts, &AV_TIME_BASE_Q);
-    print_int ("format",              sub->format);
-    print_int ("start_display_time",  sub->start_display_time);
-    print_int ("end_display_time",    sub->end_display_time);
-    print_int ("num_rects",           sub->num_rects);
+    print_ts  ("pts",                 sub->subtitle_timing.start_pts);
+    print_time("pts_time",            sub->subtitle_timing.start_pts, &AV_TIME_BASE_Q);
+    print_time("duration",            sub->subtitle_timing.duration, &AV_TIME_BASE_Q);
+
+    // Remain compatible with previous outputs
+    switch (sub->format) {
+    case AV_SUBTITLE_FMT_BITMAP:
+        print_int ("format",         0);
+        break;
+    case AV_SUBTITLE_FMT_TEXT:
+        print_int ("format",         1);
+        break;
+    case AV_SUBTITLE_FMT_ASS:
+        print_int ("format",         1);
+        break;
+    default:
+        print_int ("format",         -1);
+        break;
+    }
+
+    s = av_get_subtitle_fmt_name(sub->format);
+    if (s) print_str    ("format_str", s);
+    else   print_str_opt("format_str", "unknown");
+
+    print_int ("num_subtitle_rects",           sub->num_subtitle_areas);
 
     writer_print_section_footer(w);
 
@@ -2635,7 +2655,6 @@ static av_always_inline int process_frame(WriterContext *w,
     AVFormatContext *fmt_ctx = ifile->fmt_ctx;
     AVCodecContext *dec_ctx = ifile->streams[pkt->stream_index].dec_ctx;
     AVCodecParameters *par = ifile->streams[pkt->stream_index].st->codecpar;
-    AVSubtitle sub;
     int ret = 0, got_frame = 0;
 
     clear_log(1);
@@ -2643,6 +2662,7 @@ static av_always_inline int process_frame(WriterContext *w,
         switch (par->codec_type) {
         case AVMEDIA_TYPE_VIDEO:
         case AVMEDIA_TYPE_AUDIO:
+        case AVMEDIA_TYPE_SUBTITLE:
             if (*packet_new) {
                 ret = avcodec_send_packet(dec_ctx, pkt);
                 if (ret == AVERROR(EAGAIN)) {
@@ -2661,12 +2681,6 @@ static av_always_inline int process_frame(WriterContext *w,
                 }
             }
             break;
-
-        case AVMEDIA_TYPE_SUBTITLE:
-            if (*packet_new)
-                ret = avcodec_decode_subtitle2(dec_ctx, &sub, &got_frame, pkt);
-            *packet_new = 0;
-            break;
         default:
             *packet_new = 0;
         }
@@ -2681,12 +2695,11 @@ static av_always_inline int process_frame(WriterContext *w,
         nb_streams_frames[pkt->stream_index]++;
         if (do_show_frames)
             if (is_sub)
-                show_subtitle(w, &sub, ifile->streams[pkt->stream_index].st, fmt_ctx);
+                show_subtitle(w, frame, ifile->streams[pkt->stream_index].st, fmt_ctx);
             else
                 show_frame(w, frame, ifile->streams[pkt->stream_index].st, fmt_ctx);
-        if (is_sub)
-            avsubtitle_free(&sub);
     }
+
     return got_frame || *packet_new;
 }
 
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v4 09/23] avfilter/subtitles: Add subtitles.c for subtitle frame allocation
  2022-05-28 13:25     ` [FFmpeg-devel] [PATCH v4 00/23] Subtitle Filtering 2022 ffmpegagent
                         ` (7 preceding siblings ...)
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 08/23] fftools/play, probe: Adjust for subtitle changes softworkz
@ 2022-05-28 13:25       ` softworkz
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 10/23] avfilter/avfilter: Handle subtitle frames softworkz
                         ` (14 subsequent siblings)
  23 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-05-28 13:25 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Analog to avfilter/video.c and avfilter/audio.c

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavfilter/Makefile    |  1 +
 libavfilter/avfilter.c  |  4 +++
 libavfilter/internal.h  |  1 +
 libavfilter/subtitles.c | 63 +++++++++++++++++++++++++++++++++++++++++
 libavfilter/subtitles.h | 44 ++++++++++++++++++++++++++++
 5 files changed, 113 insertions(+)
 create mode 100644 libavfilter/subtitles.c
 create mode 100644 libavfilter/subtitles.h

diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index e0e4d0de2c..842068e902 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -20,6 +20,7 @@ OBJS = allfilters.o                                                     \
        framequeue.o                                                     \
        graphdump.o                                                      \
        graphparser.o                                                    \
+       subtitles.o                                                      \
        version.o                                                        \
        video.o                                                          \
 
diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c
index 965f5d0f63..28c5430c3e 100644
--- a/libavfilter/avfilter.c
+++ b/libavfilter/avfilter.c
@@ -42,6 +42,7 @@
 #include "formats.h"
 #include "framepool.h"
 #include "internal.h"
+#include "subtitles.h"
 
 static void tlog_ref(void *ctx, AVFrame *ref, int end)
 {
@@ -1452,6 +1453,9 @@ int ff_inlink_make_frame_writable(AVFilterLink *link, AVFrame **rframe)
     case AVMEDIA_TYPE_AUDIO:
         out = ff_get_audio_buffer(link, frame->nb_samples);
         break;
+    case AVMEDIA_TYPE_SUBTITLE:
+        out = ff_get_subtitles_buffer(link, link->format);
+        break;
     default:
         return AVERROR(EINVAL);
     }
diff --git a/libavfilter/internal.h b/libavfilter/internal.h
index 0f8da367d0..6c8496879a 100644
--- a/libavfilter/internal.h
+++ b/libavfilter/internal.h
@@ -89,6 +89,7 @@ struct AVFilterPad {
     union {
         AVFrame *(*video)(AVFilterLink *link, int w, int h);
         AVFrame *(*audio)(AVFilterLink *link, int nb_samples);
+        AVFrame *(*subtitle)(AVFilterLink *link, int format);
     } get_buffer;
 
     /**
diff --git a/libavfilter/subtitles.c b/libavfilter/subtitles.c
new file mode 100644
index 0000000000..951bfd612c
--- /dev/null
+++ b/libavfilter/subtitles.c
@@ -0,0 +1,63 @@
+/*
+ * 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
+ */
+
+#include "libavutil/common.h"
+
+#include "subtitles.h"
+#include "avfilter.h"
+#include "internal.h"
+
+
+AVFrame *ff_null_get_subtitles_buffer(AVFilterLink *link, int format)
+{
+    return ff_get_subtitles_buffer(link->dst->outputs[0], format);
+}
+
+AVFrame *ff_default_get_subtitles_buffer(AVFilterLink *link, int format)
+{
+    AVFrame *frame;
+
+    frame = av_frame_alloc();
+    if (!frame)
+        return NULL;
+
+    frame->format = format;
+    frame->type = AVMEDIA_TYPE_SUBTITLE;
+
+    if (av_frame_get_buffer2(frame, 0) < 0) {
+        av_frame_free(&frame);
+        return NULL;
+    }
+
+    return frame;
+}
+
+AVFrame *ff_get_subtitles_buffer(AVFilterLink *link, int format)
+{
+    AVFrame *ret = NULL;
+
+    if (link->dstpad->get_buffer.subtitle)
+        ret = link->dstpad->get_buffer.subtitle(link, format);
+
+    if (!ret)
+        ret = ff_default_get_subtitles_buffer(link, format);
+
+    return ret;
+}
diff --git a/libavfilter/subtitles.h b/libavfilter/subtitles.h
new file mode 100644
index 0000000000..4a9115126e
--- /dev/null
+++ b/libavfilter/subtitles.h
@@ -0,0 +1,44 @@
+/*
+ * 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
+ */
+
+#ifndef AVFILTER_SUBTITLES_H
+#define AVFILTER_SUBTITLES_H
+
+#include "avfilter.h"
+#include "internal.h"
+
+/** default handler for get_subtitles_buffer() for subtitle inputs */
+AVFrame *ff_default_get_subtitles_buffer(AVFilterLink *link, int format);
+
+/** get_subtitles_buffer() handler for filters which simply pass subtitles along */
+AVFrame *ff_null_get_subtitles_buffer(AVFilterLink *link, int format);
+
+/**
+ * Request a subtitles frame with a specific set of permissions.
+ *
+ * @param link           the output link to the filter from which the buffer will
+ *                       be requested
+ * @param format         The subtitles format.
+ * @return               A reference to the frame. This must be unreferenced with
+ *                       av_frame_free when you are finished with it.
+*/
+AVFrame *ff_get_subtitles_buffer(AVFilterLink *link, int format);
+
+#endif /* AVFILTER_SUBTITLES_H */
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v4 10/23] avfilter/avfilter: Handle subtitle frames
  2022-05-28 13:25     ` [FFmpeg-devel] [PATCH v4 00/23] Subtitle Filtering 2022 ffmpegagent
                         ` (8 preceding siblings ...)
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 09/23] avfilter/subtitles: Add subtitles.c for subtitle frame allocation softworkz
@ 2022-05-28 13:25       ` softworkz
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 11/23] avfilter/avfilter: Fix hardcoded input index softworkz
                         ` (13 subsequent siblings)
  23 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-05-28 13:25 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavfilter/avfilter.c      | 12 +++++++++---
 libavfilter/avfilter.h      | 11 +++++++++++
 libavfilter/avfiltergraph.c |  5 +++++
 libavfilter/formats.c       | 16 ++++++++++++++++
 libavfilter/formats.h       |  3 +++
 libavfilter/internal.h      | 18 +++++++++++++++---
 6 files changed, 59 insertions(+), 6 deletions(-)

diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c
index 28c5430c3e..30745f41cb 100644
--- a/libavfilter/avfilter.c
+++ b/libavfilter/avfilter.c
@@ -52,7 +52,8 @@ static void tlog_ref(void *ctx, AVFrame *ref, int end)
             ref->linesize[0], ref->linesize[1], ref->linesize[2], ref->linesize[3],
             ref->pts, ref->pkt_pos);
 
-    if (ref->width) {
+    switch(ref->type) {
+    case AVMEDIA_TYPE_VIDEO:
         ff_tlog(ctx, " a:%d/%d s:%dx%d i:%c iskey:%d type:%c",
                 ref->sample_aspect_ratio.num, ref->sample_aspect_ratio.den,
                 ref->width, ref->height,
@@ -60,12 +61,13 @@ static void tlog_ref(void *ctx, AVFrame *ref, int end)
                 ref->top_field_first ? 'T' : 'B',    /* Top / Bottom */
                 ref->key_frame,
                 av_get_picture_type_char(ref->pict_type));
-    }
-    if (ref->nb_samples) {
+        break;
+    case AVMEDIA_TYPE_AUDIO:
         ff_tlog(ctx, " cl:%"PRId64"d n:%d r:%d",
                 ref->channel_layout,
                 ref->nb_samples,
                 ref->sample_rate);
+        break;
     }
 
     ff_tlog(ctx, "]%s", end ? "\n" : "");
@@ -1013,6 +1015,10 @@ int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
             av_assert1(frame->width               == link->w);
             av_assert1(frame->height               == link->h);
         }
+    } else if (link->type == AVMEDIA_TYPE_SUBTITLE) {
+        if (frame->format != link->format) {
+            av_log(link->dst, AV_LOG_WARNING, "Subtitle format change from %d to %d\n", link->format, frame->format);
+        }
     } else {
         if (frame->format != link->format) {
             av_log(link->dst, AV_LOG_ERROR, "Format change is not supported\n");
diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h
index 2e8197c9a6..c436f304bc 100644
--- a/libavfilter/avfilter.h
+++ b/libavfilter/avfilter.h
@@ -45,6 +45,7 @@
 #include "libavutil/log.h"
 #include "libavutil/samplefmt.h"
 #include "libavutil/pixfmt.h"
+#include "libavutil/subfmt.h"
 #include "libavutil/rational.h"
 
 #include "libavfilter/version_major.h"
@@ -349,6 +350,12 @@ typedef struct AVFilter {
          * and outputs use the same sample rate and channel count/layout.
          */
         const enum AVSampleFormat *samples_list;
+        /**
+         * Analogous to pixels, but delimited by AV_SUBTITLE_FMT_NONE
+         * and restricted to filters that only have AVMEDIA_TYPE_SUBTITLE
+         * inputs and outputs.
+         */
+        const enum AVSubtitleType *subs_list;
         /**
          * Equivalent to { pix_fmt, AV_PIX_FMT_NONE } as pixels_list.
          */
@@ -357,6 +364,10 @@ typedef struct AVFilter {
          * Equivalent to { sample_fmt, AV_SAMPLE_FMT_NONE } as samples_list.
          */
         enum AVSampleFormat sample_fmt;
+        /**
+         * Equivalent to { sub_fmt, AV_SUBTITLE_FMT_NONE } as subs_list.
+         */
+        enum AVSubtitleType sub_fmt;
     } formats;
 
     int priv_size;      ///< size of private data to allocate for the filter
diff --git a/libavfilter/avfiltergraph.c b/libavfilter/avfiltergraph.c
index b7dbfc063b..f37be0203b 100644
--- a/libavfilter/avfiltergraph.c
+++ b/libavfilter/avfiltergraph.c
@@ -309,6 +309,8 @@ static int filter_link_check_formats(void *log, AVFilterLink *link, AVFilterForm
             return ret;
         break;
 
+    case AVMEDIA_TYPE_SUBTITLE:
+        return 0;
     default:
         av_assert0(!"reached");
     }
@@ -439,6 +441,9 @@ static int query_formats(AVFilterGraph *graph, void *log_ctx)
             if (!link)
                 continue;
 
+            if (link->type == AVMEDIA_TYPE_SUBTITLE)
+                continue;
+
             neg = ff_filter_get_negotiation(link);
             av_assert0(neg);
             for (neg_step = 1; neg_step < neg->nb_mergers; neg_step++) {
diff --git a/libavfilter/formats.c b/libavfilter/formats.c
index e8c2888c0c..12585ed428 100644
--- a/libavfilter/formats.c
+++ b/libavfilter/formats.c
@@ -19,6 +19,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
+#include "libavutil/subfmt.h"
 #include "libavutil/avassert.h"
 #include "libavutil/channel_layout.h"
 #include "libavutil/common.h"
@@ -491,6 +492,13 @@ AVFilterFormats *ff_all_formats(enum AVMediaType type)
                 return NULL;
             fmt++;
         }
+    } else if (type == AVMEDIA_TYPE_SUBTITLE) {
+        if (ff_add_format(&ret, AV_SUBTITLE_FMT_BITMAP) < 0)
+            return NULL;
+        if (ff_add_format(&ret, AV_SUBTITLE_FMT_ASS) < 0)
+            return NULL;
+        if (ff_add_format(&ret, AV_SUBTITLE_FMT_TEXT) < 0)
+            return NULL;
     }
 
     return ret;
@@ -774,6 +782,10 @@ int ff_default_query_formats(AVFilterContext *ctx)
         type    = AVMEDIA_TYPE_AUDIO;
         formats = ff_make_format_list(f->formats.samples_list);
         break;
+    case FF_FILTER_FORMATS_SUBFMTS_LIST:
+        type    = AVMEDIA_TYPE_SUBTITLE;
+        formats = ff_make_format_list(f->formats.subs_list);
+        break;
     case FF_FILTER_FORMATS_SINGLE_PIXFMT:
         type    = AVMEDIA_TYPE_VIDEO;
         formats = ff_make_formats_list_singleton(f->formats.pix_fmt);
@@ -782,6 +794,10 @@ int ff_default_query_formats(AVFilterContext *ctx)
         type    = AVMEDIA_TYPE_AUDIO;
         formats = ff_make_formats_list_singleton(f->formats.sample_fmt);
         break;
+    case FF_FILTER_FORMATS_SINGLE_SUBFMT:
+        type    = AVMEDIA_TYPE_SUBTITLE;
+        formats = ff_make_formats_list_singleton(f->formats.sub_fmt);
+        break;
     default:
         av_assert2(!"Unreachable");
     /* Intended fallthrough */
diff --git a/libavfilter/formats.h b/libavfilter/formats.h
index 22224dce2d..6cf952a059 100644
--- a/libavfilter/formats.h
+++ b/libavfilter/formats.h
@@ -183,6 +183,9 @@ av_warn_unused_result
 int ff_add_channel_layout(AVFilterChannelLayouts **l,
                           const AVChannelLayout *channel_layout);
 
+av_warn_unused_result
+int ff_add_subtitle_type(AVFilterFormats **avff, int64_t fmt);
+
 /**
  * Add *ref as a new reference to f.
  */
diff --git a/libavfilter/internal.h b/libavfilter/internal.h
index 6c8496879a..7972c5c6de 100644
--- a/libavfilter/internal.h
+++ b/libavfilter/internal.h
@@ -148,9 +148,11 @@ static av_always_inline int ff_filter_execute(AVFilterContext *ctx, avfilter_act
 
 enum FilterFormatsState {
     /**
-     * The default value meaning that this filter supports all formats
-     * and (for audio) sample rates and channel layouts/counts as long
-     * as these properties agree for all inputs and outputs.
+     * The default value meaning that this filter supports
+     * - For video:     all formats
+     * - For audio:     all sample rates and channel layouts/counts
+     * - For subtitles: all subtitle formats
+     * as long as these properties agree for all inputs and outputs.
      * This state is only allowed in case all inputs and outputs actually
      * have the same type.
      * The union is unused in this state.
@@ -161,8 +163,10 @@ enum FilterFormatsState {
     FF_FILTER_FORMATS_QUERY_FUNC,       ///< formats.query active.
     FF_FILTER_FORMATS_PIXFMT_LIST,      ///< formats.pixels_list active.
     FF_FILTER_FORMATS_SAMPLEFMTS_LIST,  ///< formats.samples_list active.
+    FF_FILTER_FORMATS_SUBFMTS_LIST,     ///< formats.subs_list active.
     FF_FILTER_FORMATS_SINGLE_PIXFMT,    ///< formats.pix_fmt active
     FF_FILTER_FORMATS_SINGLE_SAMPLEFMT, ///< formats.sample_fmt active.
+    FF_FILTER_FORMATS_SINGLE_SUBFMT,    ///< formats.sub_fmt active.
 };
 
 #define FILTER_QUERY_FUNC(func)        \
@@ -174,16 +178,24 @@ enum FilterFormatsState {
 #define FILTER_SAMPLEFMTS_ARRAY(array) \
         .formats.samples_list = array, \
         .formats_state        = FF_FILTER_FORMATS_SAMPLEFMTS_LIST
+#define FILTER_SUBFMTS_ARRAY(array) \
+        .formats.subs_list = array, \
+        .formats_state        = FF_FILTER_FORMATS_SUBFMTS_LIST
 #define FILTER_PIXFMTS(...)            \
     FILTER_PIXFMTS_ARRAY(((const enum AVPixelFormat []) { __VA_ARGS__, AV_PIX_FMT_NONE }))
 #define FILTER_SAMPLEFMTS(...)         \
     FILTER_SAMPLEFMTS_ARRAY(((const enum AVSampleFormat[]) { __VA_ARGS__, AV_SAMPLE_FMT_NONE }))
+#define FILTER_SUBFMTS(...)         \
+    FILTER_SUBFMTS_ARRAY(((const enum AVSubtitleType[]) { __VA_ARGS__, AV_SUBTITLE_FMT_NONE }))
 #define FILTER_SINGLE_PIXFMT(pix_fmt_)  \
         .formats.pix_fmt = pix_fmt_,    \
         .formats_state   = FF_FILTER_FORMATS_SINGLE_PIXFMT
 #define FILTER_SINGLE_SAMPLEFMT(sample_fmt_) \
         .formats.sample_fmt = sample_fmt_,   \
         .formats_state      = FF_FILTER_FORMATS_SINGLE_SAMPLEFMT
+#define FILTER_SINGLE_SUBFMT(sub_fmt_) \
+        .formats.sub_fmt = sub_fmt_,   \
+        .formats_state      = FF_FILTER_FORMATS_SINGLE_SUBFMT
 
 #define FILTER_INOUTPADS(inout, array) \
        .inout        = array, \
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v4 11/23] avfilter/avfilter: Fix hardcoded input index
  2022-05-28 13:25     ` [FFmpeg-devel] [PATCH v4 00/23] Subtitle Filtering 2022 ffmpegagent
                         ` (9 preceding siblings ...)
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 10/23] avfilter/avfilter: Handle subtitle frames softworkz
@ 2022-05-28 13:25       ` softworkz
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 12/23] avfilter/sbuffer: Add sbuffersrc and sbuffersink filters softworkz
                         ` (12 subsequent siblings)
  23 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-05-28 13:25 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

This fix targets (rare) cases where multiple input pads have a
.filter_frame function. ff_request_frame_to_filter needs
to call ff_request_frame with the correct input pad
instead of the hardcoded first one.

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavfilter/avfilter.c | 18 +++++++++++++-----
 1 file changed, 13 insertions(+), 5 deletions(-)

diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c
index 30745f41cb..6cbfc54a85 100644
--- a/libavfilter/avfilter.c
+++ b/libavfilter/avfilter.c
@@ -443,7 +443,7 @@ static int64_t guess_status_pts(AVFilterContext *ctx, int status, AVRational lin
     return AV_NOPTS_VALUE;
 }
 
-static int ff_request_frame_to_filter(AVFilterLink *link)
+static int ff_request_frame_to_filter(AVFilterLink *link, int input_index)
 {
     int ret = -1;
 
@@ -452,8 +452,8 @@ static int ff_request_frame_to_filter(AVFilterLink *link)
     link->frame_blocked_in = 1;
     if (link->srcpad->request_frame)
         ret = link->srcpad->request_frame(link);
-    else if (link->src->inputs[0])
-        ret = ff_request_frame(link->src->inputs[0]);
+    else if (link->src->inputs[input_index])
+        ret = ff_request_frame(link->src->inputs[input_index]);
     if (ret < 0) {
         if (ret != AVERROR(EAGAIN) && ret != link->status_in)
             ff_avfilter_link_set_in_status(link, ret, guess_status_pts(link->src, ret, link->time_base));
@@ -1153,6 +1153,14 @@ static int forward_status_change(AVFilterContext *filter, AVFilterLink *in)
 {
     unsigned out = 0, progress = 0;
     int ret;
+    int input_index = 0;
+
+    for (int i = 0; i < in->dst->nb_inputs; i++) {
+        if (&in->dst->input_pads[i] == in->dstpad) {
+            input_index = i;
+            break;
+        }
+    }
 
     av_assert0(!in->status_out);
     if (!filter->nb_outputs) {
@@ -1162,7 +1170,7 @@ static int forward_status_change(AVFilterContext *filter, AVFilterLink *in)
     while (!in->status_out) {
         if (!filter->outputs[out]->status_in) {
             progress++;
-            ret = ff_request_frame_to_filter(filter->outputs[out]);
+            ret = ff_request_frame_to_filter(filter->outputs[out], input_index);
             if (ret < 0)
                 return ret;
         }
@@ -1199,7 +1207,7 @@ static int ff_filter_activate_default(AVFilterContext *filter)
     for (i = 0; i < filter->nb_outputs; i++) {
         if (filter->outputs[i]->frame_wanted_out &&
             !filter->outputs[i]->frame_blocked_in) {
-            return ff_request_frame_to_filter(filter->outputs[i]);
+            return ff_request_frame_to_filter(filter->outputs[i], 0);
         }
     }
     return FFERROR_NOT_READY;
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v4 12/23] avfilter/sbuffer: Add sbuffersrc and sbuffersink filters
  2022-05-28 13:25     ` [FFmpeg-devel] [PATCH v4 00/23] Subtitle Filtering 2022 ffmpegagent
                         ` (10 preceding siblings ...)
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 11/23] avfilter/avfilter: Fix hardcoded input index softworkz
@ 2022-05-28 13:25       ` softworkz
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 13/23] avfilter/overlaygraphicsubs: Add overlaygraphicsubs and graphicsub2video filters softworkz
                         ` (11 subsequent siblings)
  23 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-05-28 13:25 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 configure                |  2 +-
 libavfilter/allfilters.c |  2 ++
 libavfilter/buffersink.c | 54 ++++++++++++++++++++++++++++++
 libavfilter/buffersink.h |  7 ++++
 libavfilter/buffersrc.c  | 72 ++++++++++++++++++++++++++++++++++++++++
 libavfilter/buffersrc.h  |  1 +
 6 files changed, 137 insertions(+), 1 deletion(-)

diff --git a/configure b/configure
index 5a167613a4..c26bcdebc6 100755
--- a/configure
+++ b/configure
@@ -7879,7 +7879,7 @@ print_enabled_components(){
         fi
     done
     if [ "$name" = "filter_list" ]; then
-        for c in asrc_abuffer vsrc_buffer asink_abuffer vsink_buffer; do
+        for c in asrc_abuffer vsrc_buffer ssrc_sbuffer asink_abuffer vsink_buffer ssink_sbuffer; do
             printf "    &ff_%s,\n" $c >> $TMPH
         done
     fi
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 2409964e53..1fe6132e0c 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -569,8 +569,10 @@ extern const AVFilter ff_avsrc_movie;
  * being the same while having different 'types'). */
 extern  const AVFilter ff_asrc_abuffer;
 extern  const AVFilter ff_vsrc_buffer;
+extern  const AVFilter ff_ssrc_sbuffer;
 extern  const AVFilter ff_asink_abuffer;
 extern  const AVFilter ff_vsink_buffer;
+extern  const AVFilter ff_ssink_sbuffer;
 extern const AVFilter ff_af_afifo;
 extern const AVFilter ff_vf_fifo;
 
diff --git a/libavfilter/buffersink.c b/libavfilter/buffersink.c
index e269cf72d1..204e9bad6c 100644
--- a/libavfilter/buffersink.c
+++ b/libavfilter/buffersink.c
@@ -30,6 +30,8 @@
 #include "libavutil/internal.h"
 #include "libavutil/opt.h"
 
+#include "libavcodec/avcodec.h"
+
 #define FF_INTERNAL_FIELDS 1
 #include "framequeue.h"
 
@@ -61,6 +63,10 @@ typedef struct BufferSinkContext {
     int *sample_rates;                  ///< list of accepted sample rates
     int sample_rates_size;
 
+    /* only used for subtitles */
+    enum AVSubtitleType *subtitle_types;     ///< list of accepted subtitle types, must be terminated with -1
+    int subtitle_types_size;
+
     AVFrame *peeked_frame;
 } BufferSinkContext;
 
@@ -372,6 +378,28 @@ static int asink_query_formats(AVFilterContext *ctx)
     return 0;
 }
 
+static int ssink_query_formats(AVFilterContext *ctx)
+{
+    BufferSinkContext *buf = ctx->priv;
+    AVFilterFormats *formats = NULL;
+    unsigned i;
+    int ret;
+
+    CHECK_LIST_SIZE(subtitle_types)
+    if (buf->subtitle_types_size) {
+        for (i = 0; i < NB_ITEMS(buf->subtitle_types); i++)
+            if ((ret = ff_add_format(&formats, buf->subtitle_types[i])) < 0)
+                return ret;
+        if ((ret = ff_set_common_formats(ctx, formats)) < 0)
+            return ret;
+    } else {
+        if ((ret = ff_default_query_formats(ctx)) < 0)
+            return ret;
+    }
+
+    return 0;
+}
+
 #define OFFSET(x) offsetof(BufferSinkContext, x)
 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
 static const AVOption buffersink_options[] = {
@@ -395,9 +423,16 @@ static const AVOption abuffersink_options[] = {
     { NULL },
 };
 #undef FLAGS
+#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_SUBTITLE_PARAM
+static const AVOption sbuffersink_options[] = {
+    { "subtitle_types", "set the supported subtitle formats", OFFSET(subtitle_types), AV_OPT_TYPE_BINARY, .flags = FLAGS },
+    { NULL },
+};
+#undef FLAGS
 
 AVFILTER_DEFINE_CLASS(buffersink);
 AVFILTER_DEFINE_CLASS(abuffersink);
+AVFILTER_DEFINE_CLASS(sbuffersink);
 
 static const AVFilterPad avfilter_vsink_buffer_inputs[] = {
     {
@@ -436,3 +471,22 @@ const AVFilter ff_asink_abuffer = {
     .outputs       = NULL,
     FILTER_QUERY_FUNC(asink_query_formats),
 };
+
+static const AVFilterPad avfilter_ssink_sbuffer_inputs[] = {
+    {
+        .name = "default",
+        .type = AVMEDIA_TYPE_SUBTITLE,
+    },
+};
+
+const AVFilter ff_ssink_sbuffer = {
+    .name          = "sbuffersink",
+    .description   = NULL_IF_CONFIG_SMALL("Buffer subtitle frames, and make them available to the end of the filter graph."),
+    .priv_class    = &sbuffersink_class,
+    .priv_size     = sizeof(BufferSinkContext),
+    .init          = common_init,
+    .activate      = activate,
+    FILTER_INPUTS(avfilter_ssink_sbuffer_inputs),
+    .outputs       = NULL,
+    FILTER_QUERY_FUNC(ssink_query_formats),
+};
diff --git a/libavfilter/buffersink.h b/libavfilter/buffersink.h
index 01e7c747d8..42c164e670 100644
--- a/libavfilter/buffersink.h
+++ b/libavfilter/buffersink.h
@@ -128,6 +128,13 @@ typedef struct AVABufferSinkParams {
  */
 attribute_deprecated
 AVABufferSinkParams *av_abuffersink_params_alloc(void);
+
+/**
+ * Deprecated and unused struct to use for initializing an sbuffersink context.
+ */
+typedef struct AVSBufferSinkParams {
+    const int *subtitle_type;
+} AVSBufferSinkParams;
 #endif
 
 /**
diff --git a/libavfilter/buffersrc.c b/libavfilter/buffersrc.c
index a3190468bb..1679c54f17 100644
--- a/libavfilter/buffersrc.c
+++ b/libavfilter/buffersrc.c
@@ -39,6 +39,7 @@
 #include "formats.h"
 #include "internal.h"
 #include "video.h"
+#include "libavcodec/avcodec.h"
 
 typedef struct BufferSourceContext {
     const AVClass    *class;
@@ -63,6 +64,9 @@ typedef struct BufferSourceContext {
     char    *channel_layout_str;
     AVChannelLayout ch_layout;
 
+    /* subtitle only */
+    enum AVSubtitleType subtitle_type;
+
     int eof;
 } BufferSourceContext;
 
@@ -143,6 +147,13 @@ FF_ENABLE_DEPRECATION_WARNINGS
                 return ret;
         }
         break;
+    case AVMEDIA_TYPE_SUBTITLE:
+        s->subtitle_type = param->format;
+        if (param->width > 0)
+            s->w = param->width;
+        if (param->height > 0)
+            s->h = param->height;
+        break;
     default:
         return AVERROR_BUG;
     }
@@ -224,6 +235,8 @@ FF_ENABLE_DEPRECATION_WARNINGS
             CHECK_AUDIO_PARAM_CHANGE(ctx, s, frame->sample_rate, frame->ch_layout,
                                      frame->format, frame->pts);
             break;
+        case AVMEDIA_TYPE_SUBTITLE:
+            break;
         default:
             return AVERROR(EINVAL);
         }
@@ -296,6 +309,7 @@ unsigned av_buffersrc_get_nb_failed_requests(AVFilterContext *buffer_src)
 #define OFFSET(x) offsetof(BufferSourceContext, x)
 #define A AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_AUDIO_PARAM
 #define V AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
+#define S AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_SUBTITLE_PARAM
 
 static const AVOption buffer_options[] = {
     { "width",         NULL,                     OFFSET(w),                AV_OPT_TYPE_INT,      { .i64 = 0 }, 0, INT_MAX, V },
@@ -325,6 +339,16 @@ static const AVOption abuffer_options[] = {
 
 AVFILTER_DEFINE_CLASS(abuffer);
 
+static const AVOption sbuffer_options[] = {
+    { "time_base",     NULL, OFFSET(time_base),            AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, 0, INT_MAX, S },
+    { "subtitle_type", NULL, OFFSET(subtitle_type),        AV_OPT_TYPE_INT,      { .i64 = 0 }, 0, INT_MAX, S },
+    { "width",         NULL, OFFSET(w),                    AV_OPT_TYPE_INT,      { .i64 = 0 }, 0, INT_MAX, V },
+    { "height",        NULL, OFFSET(h),                    AV_OPT_TYPE_INT,      { .i64 = 0 }, 0, INT_MAX, V },
+    { NULL },
+};
+
+AVFILTER_DEFINE_CLASS(sbuffer);
+
 static av_cold int init_audio(AVFilterContext *ctx)
 {
     BufferSourceContext *s = ctx->priv;
@@ -393,6 +417,21 @@ FF_ENABLE_DEPRECATION_WARNINGS
     return ret;
 }
 
+static av_cold int init_subtitle(AVFilterContext *ctx)
+{
+    BufferSourceContext *c = ctx->priv;
+
+    if (c->subtitle_type == AV_SUBTITLE_FMT_BITMAP)
+        av_log(ctx, AV_LOG_VERBOSE, "graphical subtitles - w:%d h:%d tb:%d/%d\n",
+               c->w, c->h, c->time_base.num, c->time_base.den);
+    else
+        av_log(ctx, AV_LOG_VERBOSE, "text subtitles -  w:%d h:%d tb:%d/%d\n",
+               c->w, c->h, c->time_base.num, c->time_base.den);
+
+    return 0;
+}
+
+
 static av_cold void uninit(AVFilterContext *ctx)
 {
     BufferSourceContext *s = ctx->priv;
@@ -426,6 +465,11 @@ static int query_formats(AVFilterContext *ctx)
         if ((ret = ff_set_common_channel_layouts(ctx, channel_layouts)) < 0)
             return ret;
         break;
+    case AVMEDIA_TYPE_SUBTITLE:
+        if ((ret = ff_add_format         (&formats, c->subtitle_type)) < 0 ||
+            (ret = ff_set_common_formats (ctx     , formats   )) < 0)
+            return ret;
+        break;
     default:
         return AVERROR(EINVAL);
     }
@@ -456,6 +500,11 @@ static int config_props(AVFilterLink *link)
                 return ret;
         }
         break;
+    case AVMEDIA_TYPE_SUBTITLE:
+        link->format = c->subtitle_type;
+        link->w = c->w;
+        link->h = c->h;
+        break;
     default:
         return AVERROR(EINVAL);
     }
@@ -520,3 +569,26 @@ const AVFilter ff_asrc_abuffer = {
     FILTER_QUERY_FUNC(query_formats),
     .priv_class = &abuffer_class,
 };
+
+static const AVFilterPad avfilter_ssrc_sbuffer_outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_SUBTITLE,
+        .request_frame = request_frame,
+        .config_props  = config_props,
+    },
+};
+
+const AVFilter ff_ssrc_sbuffer = {
+    .name          = "sbuffer",
+    .description   = NULL_IF_CONFIG_SMALL("Buffer subtitle frames, and make them accessible to the filterchain."),
+    .priv_size     = sizeof(BufferSourceContext),
+
+    .init      = init_subtitle,
+    .uninit    = uninit,
+
+    .inputs    = NULL,
+    FILTER_OUTPUTS(avfilter_ssrc_sbuffer_outputs),
+    FILTER_QUERY_FUNC(query_formats),
+    .priv_class = &sbuffer_class,
+};
diff --git a/libavfilter/buffersrc.h b/libavfilter/buffersrc.h
index 3b248b37cd..45a657bbb6 100644
--- a/libavfilter/buffersrc.h
+++ b/libavfilter/buffersrc.h
@@ -74,6 +74,7 @@ typedef struct AVBufferSrcParameters {
     /**
      * video: the pixel format, value corresponds to enum AVPixelFormat
      * audio: the sample format, value corresponds to enum AVSampleFormat
+     * subtitles: the subtitle format, value corresponds to enum AVSubtitleType
      */
     int format;
     /**
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v4 13/23] avfilter/overlaygraphicsubs: Add overlaygraphicsubs and graphicsub2video filters
  2022-05-28 13:25     ` [FFmpeg-devel] [PATCH v4 00/23] Subtitle Filtering 2022 ffmpegagent
                         ` (11 preceding siblings ...)
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 12/23] avfilter/sbuffer: Add sbuffersrc and sbuffersink filters softworkz
@ 2022-05-28 13:25       ` softworkz
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 14/23] avfilter/overlaytextsubs: Add overlaytextsubs and textsubs2video filters softworkz
                         ` (10 subsequent siblings)
  23 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-05-28 13:25 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

- overlaygraphicsubs (VS -> V)
  Overlay graphic subtitles onto a video stream

- graphicsub2video {S -> V)
  Converts graphic subtitles to video frames (with alpha)
  Gets auto-inserted for retaining compatibility with
  sub2video command lines

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 doc/filters.texi                    | 118 +++++
 libavfilter/Makefile                |   2 +
 libavfilter/allfilters.c            |   2 +
 libavfilter/vf_overlaygraphicsubs.c | 765 ++++++++++++++++++++++++++++
 4 files changed, 887 insertions(+)
 create mode 100644 libavfilter/vf_overlaygraphicsubs.c

diff --git a/doc/filters.texi b/doc/filters.texi
index 0e10946cca..875c4a9f94 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -26879,6 +26879,124 @@ tools.
 
 @c man end VIDEO SINKS
 
+@chapter Subtitle Filters
+@c man begin SUBTITLE FILTERS
+
+When you configure your FFmpeg build, you can disable any of the
+existing filters using @code{--disable-filters}.
+
+Below is a description of the currently available subtitle filters.
+
+@section graphicsub2video
+
+Renders graphic subtitles as video frames.
+
+This filter replaces the previous "sub2video" hack which did the conversion implicitly and up-front as subtitle filtering wasn't possible at that time.
+To retain compatibility with earlier sub2video command lines, this filter is being auto-inserted in those cases.
+
+For overlaying graphicsal subtitles it is recommended to use the 'overlaygraphicsubs' filter which is more efficient and takes less processing resources.
+
+This filter is still useful in cases where the overlay is done with hardware acceleration (e.g. overlay_qsv, overlay_vaapi, overlay_cuda) for preparing the overlay frames.
+
+Inputs:
+@itemize
+@item 0: Subtitles [BITMAP]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Video [RGB32]
+@end itemize
+
+
+It accepts the following parameters:
+
+@table @option
+@item size, s
+Set the size of the output video frame.
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Overlay PGS subtitles
+(not recommended - better use overlaygraphicsubs)
+@example
+ffmpeg -i "https://streams.videolan.org/samples/sub/PGS/Girl_With_The_Dragon_Tattoo_2%3A23%3A56.mkv" -filter_complex "[0:1]graphicsub2video[subs];[0:0][subs]overlay" output.mp4
+@end example
+
+@item
+Overlay PGS subtitles implicitly
+The graphicsub2video is inserted automatically for compatibility with legacy command lines.
+@example
+ffmpeg -i "https://streams.videolan.org/samples/sub/PGS/Girl_With_The_Dragon_Tattoo_2%3A23%3A56.mkv" -filter_complex "[0:0][0:1]overlay" output.mp4
+@end example
+@end itemize
+
+@section overlaygraphicsubs
+
+Overlay graphic subtitles onto a video stream.
+
+This filter can blend graphical subtitles on a video stream directly, i.e. without creating full-size alpha images first.
+The blending operation is limited to the area of the subtitle rectangles, which also means that no processing is done at times where no subtitles are to be displayed.
+
+Inputs:
+@itemize
+@item 0: Video [YUV420P, YUV422P, YUV444P, ARGB, RGBA, ABGR, BGRA, RGB24, BGR24]
+@item 1: Subtitles [BITMAP]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Video (same as input)
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+@item x
+@item y
+Set the expression for the x and y coordinates of the overlaid video
+on the main video. Default value is "0" for both expressions. In case
+the expression is invalid, it is set to a huge value (meaning that the
+overlay will not be displayed within the output visible area).
+
+@item eof_action
+See @ref{framesync}.
+
+@item eval
+Set when the expressions for @option{x}, and @option{y} are evaluated.
+
+It accepts the following values:
+@table @samp
+@item init
+only evaluate expressions once during the filter initialization or
+when a command is processed
+
+@item frame
+evaluate expressions for each incoming frame
+@end table
+
+Default value is @samp{frame}.
+
+@item shortest
+See @ref{framesync}.
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Overlay PGS subtitles
+@example
+ffmpeg -i "https://streams.videolan.org/samples/sub/PGS/Girl_With_The_Dragon_Tattoo_2%3A23%3A56.mkv" -filter_complex "[0:0][0:1]overlaygraphicsubs" output.mp4
+@end example
+@end itemize
+@c man end SUBTITLE FILTERS
+
 @chapter Multimedia Filters
 @c man begin MULTIMEDIA FILTERS
 
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 842068e902..9114316f79 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -306,6 +306,7 @@ OBJS-$(CONFIG_GBLUR_FILTER)                  += vf_gblur.o
 OBJS-$(CONFIG_GBLUR_VULKAN_FILTER)           += vf_gblur_vulkan.o vulkan.o vulkan_filter.o
 OBJS-$(CONFIG_GEQ_FILTER)                    += vf_geq.o
 OBJS-$(CONFIG_GRADFUN_FILTER)                += vf_gradfun.o
+OBJS-$(CONFIG_GRAPHICSUB2VIDEO_FILTER)       += vf_overlaygraphicsubs.o framesync.o
 OBJS-$(CONFIG_GRAPHMONITOR_FILTER)           += f_graphmonitor.o
 OBJS-$(CONFIG_GRAYWORLD_FILTER)              += vf_grayworld.o
 OBJS-$(CONFIG_GREYEDGE_FILTER)               += vf_colorconstancy.o
@@ -390,6 +391,7 @@ OBJS-$(CONFIG_OVERLAY_OPENCL_FILTER)         += vf_overlay_opencl.o opencl.o \
 OBJS-$(CONFIG_OVERLAY_QSV_FILTER)            += vf_overlay_qsv.o framesync.o
 OBJS-$(CONFIG_OVERLAY_VAAPI_FILTER)          += vf_overlay_vaapi.o framesync.o vaapi_vpp.o
 OBJS-$(CONFIG_OVERLAY_VULKAN_FILTER)         += vf_overlay_vulkan.o vulkan.o vulkan_filter.o
+OBJS-$(CONFIG_OVERLAYGRAPHICSUBS_FILTER)     += vf_overlaygraphicsubs.o framesync.o
 OBJS-$(CONFIG_OWDENOISE_FILTER)              += vf_owdenoise.o
 OBJS-$(CONFIG_PAD_FILTER)                    += vf_pad.o
 OBJS-$(CONFIG_PAD_OPENCL_FILTER)             += vf_pad_opencl.o opencl.o opencl/pad.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 1fe6132e0c..4fb84ff881 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -369,6 +369,7 @@ extern const AVFilter ff_vf_overlay_qsv;
 extern const AVFilter ff_vf_overlay_vaapi;
 extern const AVFilter ff_vf_overlay_vulkan;
 extern const AVFilter ff_vf_overlay_cuda;
+extern const AVFilter ff_vf_overlaygraphicsubs;
 extern const AVFilter ff_vf_owdenoise;
 extern const AVFilter ff_vf_pad;
 extern const AVFilter ff_vf_pad_opencl;
@@ -557,6 +558,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_svf_graphicsub2video;
 
 /* multimedia sources */
 extern const AVFilter ff_avsrc_avsynctest;
diff --git a/libavfilter/vf_overlaygraphicsubs.c b/libavfilter/vf_overlaygraphicsubs.c
new file mode 100644
index 0000000000..7b26d8ef37
--- /dev/null
+++ b/libavfilter/vf_overlaygraphicsubs.c
@@ -0,0 +1,765 @@
+/*
+ * Copyright (c) 2021 softworkz (derived from vf_overlay)
+ * Copyright (c) 2010 Stefano Sabatini
+ * Copyright (c) 2010 Baptiste Coudurier
+ * Copyright (c) 2007 Bobby Bingham
+ *
+ * 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
+ * overlay graphical subtitles on top of a video frame
+ */
+
+#include "libavutil/eval.h"
+#include "libavutil/imgutils.h"
+#include "libavutil/opt.h"
+#include "internal.h"
+#include "drawutils.h"
+#include "framesync.h"
+
+enum var_name {
+    VAR_MAIN_W,    VAR_MW,
+    VAR_MAIN_H,    VAR_MH,
+    VAR_OVERLAY_W, VAR_OW,
+    VAR_OVERLAY_H, VAR_OH,
+    VAR_HSUB,
+    VAR_VSUB,
+    VAR_X,
+    VAR_Y,
+    VAR_N,
+    VAR_POS,
+    VAR_T,
+    VAR_VARS_NB
+};
+
+typedef struct OverlaySubsContext {
+    const AVClass *class;
+    int x, y;                   ///< position of overlaid picture
+    int w, h;
+    AVFrame *outpicref;
+
+    int main_is_packed_rgb;
+    uint8_t main_rgba_map[4];
+    int main_has_alpha;
+    uint8_t overlay_rgba_map[4];
+    int eval_mode;              ///< EvalMode
+    int use_caching;
+    AVFrame *cache_frame;
+
+    FFFrameSync fs;
+
+    int main_pix_step[4];       ///< steps per pixel for each plane of the main output
+    int hsub, vsub;             ///< chroma subsampling values
+    const AVPixFmtDescriptor *main_desc; ///< format descriptor for main input
+
+    double var_values[VAR_VARS_NB];
+    char *x_expr, *y_expr;
+
+    AVExpr *x_pexpr, *y_pexpr;
+
+    int pic_counter;
+} OverlaySubsContext;
+
+static const char *const var_names[] = {
+    "main_w",    "W", ///< width  of the main    video
+    "main_h",    "H", ///< height of the main    video
+    "overlay_w", "w", ///< width  of the overlay video
+    "overlay_h", "h", ///< height of the overlay video
+    "hsub",
+    "vsub",
+    "x",
+    "y",
+    "n",            ///< number of frame
+    "pos",          ///< position in the file
+    "t",            ///< timestamp expressed in seconds
+    NULL
+};
+
+#define MAIN    0
+#define OVERLAY 1
+
+#define R 0
+#define G 1
+#define B 2
+#define A 3
+
+#define Y 0
+#define U 1
+#define V 2
+
+enum EvalMode {
+    EVAL_MODE_INIT,
+    EVAL_MODE_FRAME,
+    EVAL_MODE_NB
+};
+
+static av_cold void overlay_graphicsubs_uninit(AVFilterContext *ctx)
+{
+    OverlaySubsContext *s = ctx->priv;
+
+    av_frame_free(&s->cache_frame);
+    ff_framesync_uninit(&s->fs);
+    av_expr_free(s->x_pexpr); s->x_pexpr = NULL;
+    av_expr_free(s->y_pexpr); s->y_pexpr = NULL;
+}
+
+static inline int normalize_xy(double d, int chroma_sub)
+{
+    if (isnan(d))
+        return INT_MAX;
+    return (int)d & ~((1 << chroma_sub) - 1);
+}
+
+static void eval_expr(AVFilterContext *ctx)
+{
+    OverlaySubsContext *s = ctx->priv;
+
+    s->var_values[VAR_X] = av_expr_eval(s->x_pexpr, s->var_values, NULL);
+    s->var_values[VAR_Y] = av_expr_eval(s->y_pexpr, s->var_values, NULL);
+    /* It is necessary if x is expressed from y  */
+    s->var_values[VAR_X] = av_expr_eval(s->x_pexpr, s->var_values, NULL);
+    s->x = normalize_xy(s->var_values[VAR_X], s->hsub);
+    s->y = normalize_xy(s->var_values[VAR_Y], s->vsub);
+}
+
+static int set_expr(AVExpr **pexpr, const char *expr, const char *option, void *log_ctx)
+{
+    int ret;
+    AVExpr *old = NULL;
+
+    if (*pexpr)
+        old = *pexpr;
+    ret = av_expr_parse(pexpr, expr, var_names,
+                        NULL, NULL, NULL, NULL, 0, log_ctx);
+    if (ret < 0) {
+        av_log(log_ctx, AV_LOG_ERROR,
+               "Error when evaluating the expression '%s' for %s\n",
+               expr, option);
+        *pexpr = old;
+        return ret;
+    }
+
+    av_expr_free(old);
+    return 0;
+}
+
+static int overlay_graphicsubs_query_formats(AVFilterContext *ctx)
+{
+    AVFilterFormats *formats;
+    AVFilterLink *inlink0 = ctx->inputs[0];
+    AVFilterLink *inlink1 = ctx->inputs[1];
+    AVFilterLink *outlink = ctx->outputs[0];
+    int ret;
+    static const enum AVSubtitleType subtitle_fmts[] = { AV_SUBTITLE_FMT_BITMAP, AV_SUBTITLE_FMT_NONE };
+    static const enum AVPixelFormat supported_pix_fmts[] = {
+        AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV444P,
+        AV_PIX_FMT_ARGB,  AV_PIX_FMT_RGBA,
+        AV_PIX_FMT_ABGR,  AV_PIX_FMT_BGRA,
+        AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24,
+        AV_PIX_FMT_NONE
+    };
+
+    /* set input0 video formats */
+    formats = ff_make_format_list(supported_pix_fmts);
+    if ((ret = ff_formats_ref(formats, &inlink0->outcfg.formats)) < 0)
+        return ret;
+
+    /* set output0 video formats */
+    if ((ret = ff_formats_ref(formats, &outlink->incfg.formats)) < 0)
+        return ret;
+
+    /* set input1 subtitle formats */
+    formats = ff_make_format_list(subtitle_fmts);
+    if ((ret = ff_formats_ref(formats, &inlink1->outcfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    OverlaySubsContext *s = ctx->priv;
+    int ret;
+
+    if ((ret = ff_framesync_init_dualinput(&s->fs, ctx)) < 0)
+        return ret;
+
+    outlink->w = ctx->inputs[MAIN]->w;
+    outlink->h = ctx->inputs[MAIN]->h;
+    outlink->time_base = ctx->inputs[MAIN]->time_base;
+    outlink->frame_rate = ctx->inputs[MAIN]->frame_rate;
+
+    return ff_framesync_configure(&s->fs);
+}
+
+// divide by 255 and round to nearest
+// apply a fast variant: (X+127)/255 = ((X+127)*257+257)>>16 = ((X+128)*257)>>16
+#define FAST_DIV255(x) ((((x) + 128) * 257) >> 16)
+
+// calculate the non-pre-multiplied alpha, applying the general equation:
+// alpha = alpha_overlay / ( (alpha_main + alpha_overlay) - (alpha_main * alpha_overlay) )
+// (((x) << 16) - ((x) << 9) + (x)) is a faster version of: 255 * 255 * x
+// ((((x) + (y)) << 8) - ((x) + (y)) - (y) * (x)) is a faster version of: 255 * (x + y)
+#define UNPREMULTIPLY_ALPHA(x, y) ((((x) << 16) - ((x) << 9) + (x)) / ((((x) + (y)) << 8) - ((x) + (y)) - (y) * (x)))
+
+/**
+ * Blend image in src to destination buffer dst at position (x, y).
+ */
+static av_always_inline void blend_packed_rgb(const AVFilterContext *ctx,
+    const AVFrame *dst, const AVSubtitleArea *src,
+    int x, int y,
+    int is_straight)
+{
+    OverlaySubsContext *s = ctx->priv;
+    int i, imax, j, jmax;
+    const int src_w = src->w;
+    const int src_h = src->h;
+    const int dst_w = dst->width;
+    const int dst_h = dst->height;
+    uint8_t alpha;          ///< the amount of overlay to blend on to main
+    const int dr = s->main_rgba_map[R];
+    const int dg = s->main_rgba_map[G];
+    const int db = s->main_rgba_map[B];
+    const int da = s->main_rgba_map[A];
+    const int dstep = s->main_pix_step[0];
+    const int sr = s->overlay_rgba_map[R];
+    const int sg = s->overlay_rgba_map[G];
+    const int sb = s->overlay_rgba_map[B];
+    const int sa = s->overlay_rgba_map[A];
+    int slice_start, slice_end;
+    uint8_t *S, *sp, *d, *dp;
+
+    i = FFMAX(-y, 0);
+    imax = FFMIN3(-y + dst_h, FFMIN(src_h, dst_h), y + src_h);
+
+    slice_start = i;
+    slice_end = i + imax;
+
+    sp = src->buf[0]->data + slice_start       * src->linesize[0];
+    dp = dst->data[0] + (slice_start + y) * dst->linesize[0];
+
+    for (i = slice_start; i < slice_end; i++) {
+        j = FFMAX(-x, 0);
+        S = sp + j;
+        d = dp + ((x + j) * dstep);
+
+        for (jmax = FFMIN(-x + dst_w, src_w); j < jmax; j++) {
+            uint32_t val = src->pal[*S];
+            const uint8_t *sval = (uint8_t *)&val;
+            alpha = sval[sa];
+
+            // if the main channel has an alpha channel, alpha has to be calculated
+            // to create an un-premultiplied (straight) alpha value
+            if (s->main_has_alpha && alpha != 0 && alpha != 255) {
+                const uint8_t alpha_d = d[da];
+                alpha = UNPREMULTIPLY_ALPHA(alpha, alpha_d);
+            }
+
+            switch (alpha) {
+            case 0:
+                break;
+            case 255:
+                d[dr] = sval[sr];
+                d[dg] = sval[sg];
+                d[db] = sval[sb];
+                break;
+            default:
+                // main_value = main_value * (1 - alpha) + overlay_value * alpha
+                // since alpha is in the range 0-255, the result must divided by 255
+                d[dr] = is_straight ? FAST_DIV255(d[dr] * (255 - alpha) + sval[sr] * alpha) :
+                        FFMIN(FAST_DIV255(d[dr] * (255 - alpha)) + sval[sr], 255);
+                d[dg] = is_straight ? FAST_DIV255(d[dg] * (255 - alpha) + sval[sg] * alpha) :
+                        FFMIN(FAST_DIV255(d[dg] * (255 - alpha)) + sval[sg], 255);
+                d[db] = is_straight ? FAST_DIV255(d[db] * (255 - alpha) + sval[sb] * alpha) :
+                        FFMIN(FAST_DIV255(d[db] * (255 - alpha)) + sval[sb], 255);
+            }
+
+            if (s->main_has_alpha) {
+                switch (alpha) {
+                case 0:
+                    break;
+                case 255:
+                    d[da] = sval[sa];
+                    break;
+                default:
+                    // apply alpha compositing: main_alpha += (1-main_alpha) * overlay_alpha
+                    d[da] += FAST_DIV255((255 - d[da]) * S[sa]);
+                }
+            }
+            d += dstep;
+            S += 1;
+        }
+        dp += dst->linesize[0];
+        sp += src->linesize[0];
+    }
+}
+
+static av_always_inline void blend_plane_8_8bits(const AVFilterContext *ctx, const AVFrame *dst, const AVSubtitleArea *area,
+    const uint32_t *yuv_pal, int src_w, int src_h, int dst_w, int dst_h, int plane, int hsub, int vsub,
+    int x, int y, int dst_plane, int dst_offset, int dst_step)
+{
+    const int src_wp = AV_CEIL_RSHIFT(src_w, hsub);
+    const int src_hp = AV_CEIL_RSHIFT(src_h, vsub);
+    const int dst_wp = AV_CEIL_RSHIFT(dst_w, hsub);
+    const int dst_hp = AV_CEIL_RSHIFT(dst_h, vsub);
+    const int yp = y >> vsub;
+    const int xp = x >> hsub;
+    uint8_t *s, *sp, *d, *dp, *dap;
+    int imax, i, j, jmax;
+    int slice_start, slice_end;
+
+    i = FFMAX(-yp, 0);                                                                                     \
+    imax = FFMIN3(-yp + dst_hp, FFMIN(src_hp, dst_hp), yp + src_hp);                                       \
+
+    slice_start = i;
+    slice_end = i + imax;
+
+    sp = area->buf[0]->data + (slice_start << vsub) * area->linesize[0];
+    dp = dst->data[dst_plane] + (yp + slice_start) * dst->linesize[dst_plane] + dst_offset;
+
+    dap = dst->data[3] + ((yp + slice_start) << vsub) * dst->linesize[3];
+
+    for (i = slice_start; i < slice_end; i++) {
+        j = FFMAX(-xp, 0);
+        d = dp + (xp + j) * dst_step;
+        s = sp + (j << hsub);
+        jmax = FFMIN(-xp + dst_wp, src_wp);
+
+        for (; j < jmax; j++) {
+            uint32_t val = yuv_pal[*s];
+            const uint8_t *sval = (uint8_t *)&val;
+            const int alpha = sval[3];
+            const int max = 255, mid = 128;
+            const int d_int = *d;
+            const int sval_int = sval[plane];
+
+            switch (alpha) {
+            case 0:
+                break;
+            case 255:
+                *d = sval[plane];
+                break;
+            default:
+                if (plane > 0)
+                    *d = av_clip(FAST_DIV255((d_int - mid) * (max - alpha) + (sval_int - mid) * alpha) , -mid, mid) + mid;
+                else
+                    *d = FAST_DIV255(d_int * (max - alpha) + sval_int * alpha);
+                break;
+            }
+
+            d += dst_step;
+            s += 1 << hsub;
+        }
+        dp += dst->linesize[dst_plane];
+        sp +=  (1 << vsub) * area->linesize[0];
+        dap += (1 << vsub) * dst->linesize[3];
+    }
+}
+
+#define RGB2Y(r, g, b) (uint8_t)(((66 * (r) + 129 * (g) +  25 * (b) + 128) >> 8) +  16)
+#define RGB2U(r, g, b) (uint8_t)(((-38 * (r) - 74 * (g) + 112 * (b) + 128) >> 8) + 128)
+#define RGB2V(r, g, b) (uint8_t)(((112 * (r) - 94 * (g) -  18 * (b) + 128) >> 8) + 128)
+/* Converts R8 G8 B8 color to YUV. */
+static av_always_inline void rgb_2_yuv(uint8_t r, uint8_t g, uint8_t b, uint8_t* y, uint8_t* u, uint8_t* v)
+{
+    *y = RGB2Y((int)r, (int)g, (int)b);
+    *u = RGB2U((int)r, (int)g, (int)b);
+    *v = RGB2V((int)r, (int)g, (int)b);
+}
+
+
+static av_always_inline void blend_yuv_8_8bits(AVFilterContext *ctx, AVFrame *dst, const AVSubtitleArea *area, int hsub, int vsub, int x, int y)
+{
+    OverlaySubsContext *s = ctx->priv;
+    const int src_w = area->w;
+    const int src_h = area->h;
+    const int dst_w = dst->width;
+    const int dst_h = dst->height;
+    const int sr = s->overlay_rgba_map[R];
+    const int sg = s->overlay_rgba_map[G];
+    const int sb = s->overlay_rgba_map[B];
+    const int sa = s->overlay_rgba_map[A];
+    uint32_t yuvpal[256];
+
+    for (int i = 0; i < 256; ++i) {
+        const uint8_t *rgba = (const uint8_t *)&area->pal[i];
+        uint8_t *yuva = (uint8_t *)&yuvpal[i];
+        rgb_2_yuv(rgba[sr], rgba[sg], rgba[sb], &yuva[Y], &yuva[U], &yuva[V]);
+        yuva[3] = rgba[sa];
+    }
+
+    blend_plane_8_8bits(ctx, dst, area, yuvpal, src_w, src_h, dst_w, dst_h, Y, 0,    0,    x, y, s->main_desc->comp[Y].plane, s->main_desc->comp[Y].offset, s->main_desc->comp[Y].step);
+    blend_plane_8_8bits(ctx, dst, area, yuvpal, src_w, src_h, dst_w, dst_h, U, hsub, vsub, x, y, s->main_desc->comp[U].plane, s->main_desc->comp[U].offset, s->main_desc->comp[U].step);
+    blend_plane_8_8bits(ctx, dst, area, yuvpal, src_w, src_h, dst_w, dst_h, V, hsub, vsub, x, y, s->main_desc->comp[V].plane, s->main_desc->comp[V].offset, s->main_desc->comp[V].step);
+}
+
+static int config_input_main(AVFilterLink *inlink)
+{
+    int ret;
+    AVFilterContext *ctx  = inlink->dst;
+    OverlaySubsContext *s = inlink->dst->priv;
+    const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(inlink->format);
+
+    av_image_fill_max_pixsteps(s->main_pix_step,    NULL, pix_desc);
+    ff_fill_rgba_map(s->overlay_rgba_map, AV_PIX_FMT_RGB32); // it's actually AV_PIX_FMT_PAL8);
+
+    s->hsub = pix_desc->log2_chroma_w;
+    s->vsub = pix_desc->log2_chroma_h;
+
+    s->main_desc = pix_desc;
+
+    s->main_is_packed_rgb = ff_fill_rgba_map(s->main_rgba_map, inlink->format) >= 0;
+    s->main_has_alpha = !!(pix_desc->flags & AV_PIX_FMT_FLAG_ALPHA);
+
+    /* Finish the configuration by evaluating the expressions
+       now when both inputs are configured. */
+    s->var_values[VAR_MAIN_W   ] = s->var_values[VAR_MW] = ctx->inputs[MAIN   ]->w;
+    s->var_values[VAR_MAIN_H   ] = s->var_values[VAR_MH] = ctx->inputs[MAIN   ]->h;
+    s->var_values[VAR_OVERLAY_W] = s->var_values[VAR_OW] = ctx->inputs[OVERLAY]->w;
+    s->var_values[VAR_OVERLAY_H] = s->var_values[VAR_OH] = ctx->inputs[OVERLAY]->h;
+    s->var_values[VAR_HSUB]  = 1<<pix_desc->log2_chroma_w;
+    s->var_values[VAR_VSUB]  = 1<<pix_desc->log2_chroma_h;
+    s->var_values[VAR_X]     = NAN;
+    s->var_values[VAR_Y]     = NAN;
+    s->var_values[VAR_N]     = 0;
+    s->var_values[VAR_T]     = NAN;
+    s->var_values[VAR_POS]   = NAN;
+
+    if ((ret = set_expr(&s->x_pexpr,      s->x_expr,      "x",      ctx)) < 0 ||
+        (ret = set_expr(&s->y_pexpr,      s->y_expr,      "y",      ctx)) < 0)
+        return ret;
+
+    if (s->eval_mode == EVAL_MODE_INIT) {
+        eval_expr(ctx);
+        av_log(ctx, AV_LOG_VERBOSE, "x:%f xi:%d y:%f yi:%d\n",
+               s->var_values[VAR_X], s->x,
+               s->var_values[VAR_Y], s->y);
+    }
+
+    av_log(ctx, AV_LOG_VERBOSE,
+           "main w:%d h:%d fmt:%s overlay w:%d h:%d fmt:%s\n",
+           ctx->inputs[MAIN]->w, ctx->inputs[MAIN]->h,
+           av_get_pix_fmt_name(ctx->inputs[MAIN]->format),
+           ctx->inputs[OVERLAY]->w, ctx->inputs[OVERLAY]->h,
+           av_get_pix_fmt_name(ctx->inputs[OVERLAY]->format));
+    return 0;
+}
+
+static int do_blend(FFFrameSync *fs)
+{
+    AVFilterContext *ctx = fs->parent;
+    AVFrame *mainpic, *second;
+    OverlaySubsContext *s = ctx->priv;
+    AVFilterLink *inlink = ctx->inputs[0];
+    unsigned i;
+    int ret;
+
+    ret = ff_framesync_dualinput_get_writable(fs, &mainpic, &second);
+    if (ret < 0)
+        return ret;
+    if (!second)
+        return ff_filter_frame(ctx->outputs[0], mainpic);
+
+    if (s->eval_mode == EVAL_MODE_FRAME) {
+        int64_t pos = mainpic->pkt_pos;
+
+        s->var_values[VAR_N] = (double)inlink->frame_count_out;
+        s->var_values[VAR_T] = mainpic->pts == AV_NOPTS_VALUE ?
+            NAN :(double)mainpic->pts * av_q2d(inlink->time_base);
+        s->var_values[VAR_POS] = pos == -1 ? NAN : (double)pos;
+
+        s->var_values[VAR_OVERLAY_W] = s->var_values[VAR_OW] = second->width;
+        s->var_values[VAR_OVERLAY_H] = s->var_values[VAR_OH] = second->height;
+        s->var_values[VAR_MAIN_W   ] = s->var_values[VAR_MW] = mainpic->width;
+        s->var_values[VAR_MAIN_H   ] = s->var_values[VAR_MH] = mainpic->height;
+
+        eval_expr(ctx);
+        av_log(ctx, AV_LOG_DEBUG, "n:%f t:%f pos:%f x:%f xi:%d y:%f yi:%d\n",
+               s->var_values[VAR_N], s->var_values[VAR_T], s->var_values[VAR_POS],
+               s->var_values[VAR_X], s->x,
+               s->var_values[VAR_Y], s->y);
+    }
+
+    for (i = 0; i < second->num_subtitle_areas; i++) {
+        const AVSubtitleArea *sub_area = second->subtitle_areas[i];
+
+        if (sub_area->type != AV_SUBTITLE_FMT_BITMAP) {
+            av_log(NULL, AV_LOG_WARNING, "overlay_graphicsubs: non-bitmap subtitle\n");
+            return AVERROR_INVALIDDATA;
+        }
+
+        switch (inlink->format) {
+        case AV_PIX_FMT_YUV420P:
+            blend_yuv_8_8bits(ctx, mainpic, sub_area, 1, 1, sub_area->x + s->x, sub_area->y + s->y);
+            break;
+        case AV_PIX_FMT_YUV422P:
+            blend_yuv_8_8bits(ctx, mainpic, sub_area, 1, 0, sub_area->x + s->x, sub_area->y + s->y);
+            break;
+        case AV_PIX_FMT_YUV444P:
+            blend_yuv_8_8bits(ctx, mainpic, sub_area, 0, 0, sub_area->x + s->x, sub_area->y + s->y);
+            break;
+        case AV_PIX_FMT_RGB24:
+        case AV_PIX_FMT_BGR24:
+        case AV_PIX_FMT_ARGB:
+        case AV_PIX_FMT_RGBA:
+        case AV_PIX_FMT_BGRA:
+        case AV_PIX_FMT_ABGR:
+            blend_packed_rgb(ctx, mainpic, sub_area, sub_area->x + s->x, sub_area->y + s->y, 1);
+            break;
+        default:
+            av_log(NULL, AV_LOG_ERROR, "Unsupported input pix fmt: %d\n", inlink->format);
+            return AVERROR(EINVAL);
+        }
+    }
+
+    return ff_filter_frame(ctx->outputs[0], mainpic);
+}
+
+static av_cold int overlay_graphicsubs_init(AVFilterContext *ctx)
+{
+    OverlaySubsContext *s = ctx->priv;
+
+    s->fs.on_event = do_blend;
+    return 0;
+}
+
+static int overlay_graphicsubs_activate(AVFilterContext *ctx)
+{
+    OverlaySubsContext *s = ctx->priv;
+    return ff_framesync_activate(&s->fs);
+}
+
+static int graphicsub2video_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_BITMAP, AV_SUBTITLE_FMT_NONE };
+    static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_RGB32, AV_PIX_FMT_NONE };
+    int ret;
+
+    /* set input subtitle formats */
+    formats = ff_make_format_list(subtitle_fmts);
+    if ((ret = ff_formats_ref(formats, &inlink->outcfg.formats)) < 0)
+        return ret;
+
+    /* set output video formats */
+    formats = ff_make_format_list(pix_fmts);
+    if ((ret = ff_formats_ref(formats, &outlink->incfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int graphicsub2video_config_input(AVFilterLink *inlink)
+{
+    AVFilterContext *ctx = inlink->dst;
+    OverlaySubsContext *s = ctx->priv;
+
+    if (s->w <= 0 || s->h <= 0) {
+        s->w = inlink->w;
+        s->h = inlink->h;
+    }
+    return 0;
+}
+
+static int graphicsub2video_config_output(AVFilterLink *outlink)
+{
+    const AVFilterContext *ctx  = outlink->src;
+    OverlaySubsContext *s = ctx->priv;
+    const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(outlink->format);
+
+    outlink->w = s->w;
+    outlink->h = s->h;
+
+    if (outlink->w == 0 && outlink->h == 0) {
+        outlink->w = 1;
+        outlink->h = 1;
+    }
+    outlink->sample_aspect_ratio = (AVRational){1,1};
+    outlink->time_base = ctx->inputs[0]->time_base;
+    outlink->frame_rate = ctx->inputs[0]->frame_rate;
+
+    av_image_fill_max_pixsteps(s->main_pix_step, NULL, pix_desc);
+    ff_fill_rgba_map(s->overlay_rgba_map, AV_PIX_FMT_RGB32);
+
+    s->hsub = pix_desc->log2_chroma_w;
+    s->vsub = pix_desc->log2_chroma_h;
+
+    s->main_desc = pix_desc;
+
+    s->main_is_packed_rgb = ff_fill_rgba_map(s->main_rgba_map, outlink->format) >= 0;
+    s->main_has_alpha = !!(pix_desc->flags & AV_PIX_FMT_FLAG_ALPHA);
+
+    return 0;
+}
+
+static int graphicsub2video_filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFilterLink *outlink = inlink->dst->outputs[0];
+    const AVFilterContext *ctx  = outlink->src;
+    OverlaySubsContext *s = ctx->priv;
+    AVFrame *out;
+    const unsigned num_rects = frame->num_subtitle_areas;
+    unsigned int i;
+    int ret;
+
+    if (s->use_caching && s->cache_frame && frame->repeat_sub
+        && s->cache_frame->subtitle_timing.start_pts == frame->subtitle_timing.start_pts) {
+        out = av_frame_clone(s->cache_frame);
+        if (!out)
+            return AVERROR(ENOMEM);
+
+        ret = av_frame_copy_props(out, frame);
+        if (ret < 0)
+            return ret;
+
+        av_log(inlink->dst, AV_LOG_DEBUG, "graphicsub2video CACHED - size %dx%d  pts: %"PRId64"  areas: %d\n", frame->width, frame->height, frame->subtitle_timing.start_pts, frame->num_subtitle_areas);
+        av_frame_free(&frame);
+        return ff_filter_frame(outlink, out);
+    }
+
+
+    out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
+    if (!out) {
+        av_frame_free(&frame);
+        return AVERROR(ENOMEM);
+    }
+
+    memset(out->data[0], 0, (size_t)out->linesize[0] * out->height);
+
+    out->pts = out->pkt_dts = out->best_effort_timestamp = frame->pts;
+    out->coded_picture_number = out->display_picture_number = s->pic_counter++;
+
+    for (i = 0; i < num_rects; i++) {
+        const AVSubtitleArea  *sub_rect = frame->subtitle_areas[i];
+
+        if (sub_rect->type != AV_SUBTITLE_FMT_BITMAP) {
+            av_log(NULL, AV_LOG_WARNING, "graphicsub2video: non-bitmap subtitle\n");
+            av_frame_free(&frame);
+            return AVERROR_INVALIDDATA;
+        }
+
+        blend_packed_rgb(inlink->dst, out, sub_rect, sub_rect->x, sub_rect->y, 1);
+    }
+
+    av_log(inlink->dst, AV_LOG_DEBUG, "graphicsub2video output - size %dx%d  pts: %"PRId64"  areas: %d\n", out->width, out->height, out->pts, frame->num_subtitle_areas);
+
+    if (s->use_caching) {
+        av_frame_free(&s->cache_frame);
+        s->cache_frame = av_frame_clone(out);
+    }
+
+    av_frame_free(&frame);
+    return ff_filter_frame(outlink, out);
+}
+
+#define OFFSET(x) offsetof(OverlaySubsContext, x)
+#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption overlaygraphicsubs_options[] = {
+    { "x", "set the x expression", OFFSET(x_expr), AV_OPT_TYPE_STRING, {.str = "0"}, 0, 0, .flags = FLAGS },
+    { "y", "set the y expression", OFFSET(y_expr), AV_OPT_TYPE_STRING, {.str = "0"}, 0, 0, .flags = FLAGS },
+    { "eof_action", "Action to take when encountering EOF from secondary input ",
+        OFFSET(fs.opt_eof_action), AV_OPT_TYPE_INT, { .i64 = EOF_ACTION_REPEAT },
+        EOF_ACTION_REPEAT, EOF_ACTION_PASS, .flags = FLAGS, "eof_action" },
+        { "repeat", "Repeat the previous frame.",   0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_REPEAT }, .flags = FLAGS, "eof_action" },
+        { "endall", "End both streams.",            0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_ENDALL }, .flags = FLAGS, "eof_action" },
+        { "pass",   "Pass through the main input.", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_PASS },   .flags = FLAGS, "eof_action" },
+    { "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, {.i64 = EVAL_MODE_FRAME}, 0, EVAL_MODE_NB-1, FLAGS, "eval" },
+         { "init",  "eval expressions once during initialization", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_INIT},  .flags = FLAGS, .unit = "eval" },
+         { "frame", "eval expressions per-frame",                  0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_FRAME}, .flags = FLAGS, .unit = "eval" },
+    { "shortest", "force termination when the shortest input terminates", OFFSET(fs.opt_shortest), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, .flags = FLAGS },
+    { "repeatlast", "repeat overlay of the last overlay frame", OFFSET(fs.opt_repeatlast), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, .flags = FLAGS },
+    { NULL }
+};
+
+static const AVOption graphicsub2video_options[] = {
+    { "size",        "set output frame size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, .flags = FLAGS },
+    { "s",           "set output frame size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, .flags = FLAGS },
+    { "use_caching", "Cache output frames",   OFFSET(use_caching), AV_OPT_TYPE_BOOL, { .i64 = 1}, 0, 1, .flags = FLAGS },
+    { NULL }
+};
+
+FRAMESYNC_DEFINE_CLASS(overlaygraphicsubs, OverlaySubsContext, fs);
+
+static const AVFilterPad overlaygraphicsubs_inputs[] = {
+    {
+        .name         = "main",
+        .type         = AVMEDIA_TYPE_VIDEO,
+        .config_props = config_input_main,
+        .flags        = AVFILTERPAD_FLAG_NEEDS_WRITABLE,
+    },
+    {
+        .name         = "overlay",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+    },
+};
+
+static const AVFilterPad overlaygraphicsubs_outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_VIDEO,
+        .config_props  = config_output,
+    },
+};
+
+const AVFilter ff_vf_overlaygraphicsubs = {
+    .name          = "overlaygraphicsubs",
+    .description   = NULL_IF_CONFIG_SMALL("Overlay graphical subtitles on top of the input."),
+    .preinit       = overlaygraphicsubs_framesync_preinit,
+    .init          = overlay_graphicsubs_init,
+    .uninit        = overlay_graphicsubs_uninit,
+    .priv_size     = sizeof(OverlaySubsContext),
+    .priv_class    = &overlaygraphicsubs_class,
+    .activate      = overlay_graphicsubs_activate,
+    FILTER_INPUTS(overlaygraphicsubs_inputs),
+    FILTER_OUTPUTS(overlaygraphicsubs_outputs),
+    FILTER_QUERY_FUNC(overlay_graphicsubs_query_formats),
+};
+
+AVFILTER_DEFINE_CLASS(graphicsub2video);
+
+static const AVFilterPad graphicsub2video_inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .filter_frame = graphicsub2video_filter_frame,
+        .config_props = graphicsub2video_config_input,
+    },
+};
+
+static const AVFilterPad graphicsub2video_outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_VIDEO,
+        .config_props  = graphicsub2video_config_output,
+    },
+};
+
+const AVFilter ff_svf_graphicsub2video = {
+    .name          = "graphicsub2video",
+    .description   = NULL_IF_CONFIG_SMALL("Convert graphical subtitles to video"),
+    .priv_size     = sizeof(OverlaySubsContext),
+    .priv_class    = &graphicsub2video_class,
+    FILTER_INPUTS(graphicsub2video_inputs),
+    FILTER_OUTPUTS(graphicsub2video_outputs),
+    FILTER_QUERY_FUNC(graphicsub2video_query_formats),
+};
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v4 14/23] avfilter/overlaytextsubs: Add overlaytextsubs and textsubs2video filters
  2022-05-28 13:25     ` [FFmpeg-devel] [PATCH v4 00/23] Subtitle Filtering 2022 ffmpegagent
                         ` (12 preceding siblings ...)
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 13/23] avfilter/overlaygraphicsubs: Add overlaygraphicsubs and graphicsub2video filters softworkz
@ 2022-05-28 13:25       ` softworkz
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 15/23] avfilter/textmod: Add textmod, censor and show_speaker filters softworkz
                         ` (9 subsequent siblings)
  23 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-05-28 13:25 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

- overlaytextsubs {VS -> V)
  Overlay text subtitles onto a video stream.

- textsubs2video {S -> V)
  Converts text subtitles to video frames

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 configure                        |   2 +
 doc/filters.texi                 | 113 +++++
 libavfilter/Makefile             |   2 +
 libavfilter/allfilters.c         |   2 +
 libavfilter/vf_overlaytextsubs.c | 680 +++++++++++++++++++++++++++++++
 5 files changed, 799 insertions(+)
 create mode 100644 libavfilter/vf_overlaytextsubs.c

diff --git a/configure b/configure
index c26bcdebc6..39c4e94a1f 100755
--- a/configure
+++ b/configure
@@ -3691,6 +3691,7 @@ overlay_qsv_filter_deps="libmfx"
 overlay_qsv_filter_select="qsvvpp"
 overlay_vaapi_filter_deps="vaapi VAProcPipelineCaps_blend_flags"
 overlay_vulkan_filter_deps="vulkan spirv_compiler"
+overlaytextsubs_filter_deps="avcodec libass"
 owdenoise_filter_deps="gpl"
 pad_opencl_filter_deps="opencl"
 pan_filter_deps="swresample"
@@ -3729,6 +3730,7 @@ stereo3d_filter_deps="gpl"
 subtitles_filter_deps="avformat avcodec libass"
 super2xsai_filter_deps="gpl"
 pixfmts_super2xsai_test_deps="super2xsai_filter"
+textsub2video_filter_deps="avcodec libass"
 tinterlace_filter_deps="gpl"
 tinterlace_merge_test_deps="tinterlace_filter"
 tinterlace_pad_test_deps="tinterlace_filter"
diff --git a/doc/filters.texi b/doc/filters.texi
index 875c4a9f94..70d4c90663 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -26995,6 +26995,119 @@ Overlay PGS subtitles
 ffmpeg -i "https://streams.videolan.org/samples/sub/PGS/Girl_With_The_Dragon_Tattoo_2%3A23%3A56.mkv" -filter_complex "[0:0][0:1]overlaygraphicsubs" output.mp4
 @end example
 @end itemize
+
+@section overlaytextsubs
+
+Overlay text subtitles onto a video stream.
+
+This filter supersedes the classic @ref{subtitles} filter opposed to which it does no longer require to open and access the source stream separately, which is often causing problems or doesn't even work for non-local or slow sources.
+
+Inputs:
+@itemize
+@item 0: Video [YUV420P, YUV422P, YUV444P, ARGB, RGBA, ABGR, BGRA, RGB24, BGR24]
+@item 1: Subtitles [TEXT]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Video (same as input)
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+
+@item alpha
+Process alpha channel, by default alpha channel is untouched.
+
+@item fonts_dir
+Set a directory path containing fonts that can be used by the filter.
+These fonts will be used in addition to whatever the font provider uses.
+
+@item default_font_path
+Path to a font file to be used as the default font.
+
+@item font_size
+Set the default font size.
+
+@item fontconfig_file
+Path to ASS fontconfig configuration file.
+
+@item force_style
+Override default style or script info parameters of the subtitles. It accepts a
+string containing ASS style format @code{KEY=VALUE} couples separated by ",".
+
+@item margin
+Set the rendering margin in pixels.
+
+@item render_latest_only
+For rendering, alway use the latest event only, which is covering the given point in time
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Overlay ASS subtitles with animations:
+@example
+ffmpeg -i "http://streams.videolan.org/samples/sub/SSA/subtitle_testing_complex.mkv" -filter_complex "[0:v]overlaytextsubs" -map 0 -y out.mkv
+@end example
+@end itemize
+
+@section textsub2video
+
+Converts text subtitles to video frames.
+
+For overlaying text subtitles onto video frames it is recommended to use the overlaytextsubs filter.
+The textsub2video is useful for for creating transparent text-frames when overlay is done via hw acceleration
+
+Inputs:
+@itemize
+@item 0: Subtitles [TEXT]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Video [RGB32]
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+
+@item rate, r
+Set the framerate for updating overlay frames.
+Normally, overlay frames will only be updated each time when the subtitles to display are changing.
+In cases where subtitles include advanced features (like animation), this parameter determines the frequency by which the overlay frames should be updated.
+
+@item size, s
+Set the output frame size.
+Allows to override the size of output video frames.
+
+@item fonts_dir
+Set a directory path containing fonts that can be used by the filter.
+These fonts will be used in addition to whatever the font provider uses.
+
+@item default_font_path
+Path to a font file to be used as the default font.
+
+@item font_size
+Set the default font size.
+
+@item fontconfig_file
+Path to ASS fontconfig configuration file.
+
+@item force_style
+Override default style or script info parameters of the subtitles. It accepts a
+string containing ASS style format @code{KEY=VALUE} couples separated by ",".
+
+@item margin
+Set the rendering margin in pixels.
+
+@item render_latest_only
+For rendering, alway use the latest event only, which is covering the given point in time.
+@end table
+
 @c man end SUBTITLE FILTERS
 
 @chapter Multimedia Filters
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 9114316f79..84ec2fa542 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -392,6 +392,7 @@ OBJS-$(CONFIG_OVERLAY_QSV_FILTER)            += vf_overlay_qsv.o framesync.o
 OBJS-$(CONFIG_OVERLAY_VAAPI_FILTER)          += vf_overlay_vaapi.o framesync.o vaapi_vpp.o
 OBJS-$(CONFIG_OVERLAY_VULKAN_FILTER)         += vf_overlay_vulkan.o vulkan.o vulkan_filter.o
 OBJS-$(CONFIG_OVERLAYGRAPHICSUBS_FILTER)     += vf_overlaygraphicsubs.o framesync.o
+OBJS-$(CONFIG_OVERLAYTEXTSUBS_FILTER)        += vf_overlaytextsubs.o
 OBJS-$(CONFIG_OWDENOISE_FILTER)              += vf_owdenoise.o
 OBJS-$(CONFIG_PAD_FILTER)                    += vf_pad.o
 OBJS-$(CONFIG_PAD_OPENCL_FILTER)             += vf_pad_opencl.o opencl.o opencl/pad.o
@@ -483,6 +484,7 @@ OBJS-$(CONFIG_SWAPRECT_FILTER)               += vf_swaprect.o
 OBJS-$(CONFIG_SWAPUV_FILTER)                 += vf_swapuv.o
 OBJS-$(CONFIG_TBLEND_FILTER)                 += vf_blend.o framesync.o
 OBJS-$(CONFIG_TELECINE_FILTER)               += vf_telecine.o
+OBJS-$(CONFIG_TEXTSUB2VIDEO_FILTER)          += vf_overlaytextsubs.o
 OBJS-$(CONFIG_THISTOGRAM_FILTER)             += vf_histogram.o
 OBJS-$(CONFIG_THRESHOLD_FILTER)              += vf_threshold.o framesync.o
 OBJS-$(CONFIG_THUMBNAIL_FILTER)              += vf_thumbnail.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 4fb84ff881..a6b95785be 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -370,6 +370,7 @@ extern const AVFilter ff_vf_overlay_vaapi;
 extern const AVFilter ff_vf_overlay_vulkan;
 extern const AVFilter ff_vf_overlay_cuda;
 extern const AVFilter ff_vf_overlaygraphicsubs;
+extern const AVFilter ff_vf_overlaytextsubs;
 extern const AVFilter ff_vf_owdenoise;
 extern const AVFilter ff_vf_pad;
 extern const AVFilter ff_vf_pad_opencl;
@@ -559,6 +560,7 @@ extern const AVFilter ff_avf_showwaves;
 extern const AVFilter ff_avf_showwavespic;
 extern const AVFilter ff_vaf_spectrumsynth;
 extern const AVFilter ff_svf_graphicsub2video;
+extern const AVFilter ff_svf_textsub2video;
 
 /* multimedia sources */
 extern const AVFilter ff_avsrc_avsynctest;
diff --git a/libavfilter/vf_overlaytextsubs.c b/libavfilter/vf_overlaytextsubs.c
new file mode 100644
index 0000000000..1e43289bec
--- /dev/null
+++ b/libavfilter/vf_overlaytextsubs.c
@@ -0,0 +1,680 @@
+/*
+ * 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
+ * overlay text subtitles on top of a video frame
+ */
+
+#include "config_components.h"
+
+#include <ass/ass.h>
+#include "libavutil/ass_internal.h"
+#include "libavutil/thread.h"
+
+#include "drawutils.h"
+#include "filters.h"
+
+#include "libavutil/opt.h"
+#include "libavutil/pixdesc.h"
+
+typedef struct TextSubsContext {
+    const AVClass *class;
+    AVMutex mutex;
+    int is_mutex_initialized;
+
+    ASS_Library   *library;
+    ASS_Renderer  *renderer;
+    ASS_Track     *track;
+
+    char *default_font_path;
+    char *fonts_dir;
+    char *fc_file;
+    double font_size;
+    char *force_style;
+    char *language;
+    int margin;
+    int render_latest_only;
+
+    int alpha;
+    FFDrawContext draw;
+
+    int got_header;
+    int out_w, out_h;
+    AVRational frame_rate;
+    AVFrame *last_frame;
+    int need_frame;
+    int eof;
+} TextSubsContext;
+
+/* libass supports a log level ranging from 0 to 7 */
+static const int ass_libavfilter_log_level_map[] = {
+    AV_LOG_QUIET,               /* 0 */
+    AV_LOG_PANIC,               /* 1 */
+    AV_LOG_FATAL,               /* 2 */
+    AV_LOG_ERROR,               /* 3 */
+    AV_LOG_WARNING,             /* 4 */
+    AV_LOG_INFO,                /* 5 */
+    AV_LOG_VERBOSE,             /* 6 */
+    AV_LOG_DEBUG,               /* 7 */
+};
+
+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 void uninit(AVFilterContext *ctx)
+{
+    TextSubsContext *s = ctx->priv;
+
+    if (s->track)
+        ass_free_track(s->track);
+    if (s->renderer)
+        ass_renderer_done(s->renderer);
+    if (s->library)
+        ass_library_done(s->library);
+
+    s->track = NULL;
+    s->renderer = NULL;
+    s->library = NULL;
+
+    if (s->is_mutex_initialized) {
+        ff_mutex_destroy(&s->mutex);
+        s->is_mutex_initialized = 0;
+    }
+
+    av_frame_free(&s->last_frame);
+}
+
+static int overlay_textsubs_query_formats(AVFilterContext *ctx)
+{
+    AVFilterFormats *formats;
+    AVFilterLink *inlink0 = ctx->inputs[0];
+    AVFilterLink *inlink1 = ctx->inputs[1];
+    AVFilterLink *outlink = ctx->outputs[0];
+    static const enum AVSubtitleType subtitle_fmts[] = { AV_SUBTITLE_FMT_ASS, AV_SUBTITLE_FMT_NONE };
+    int ret;
+
+    /* set input0 and output0 video formats */
+    formats = ff_draw_supported_pixel_formats(0);
+    if ((ret = ff_formats_ref(formats, &inlink0->outcfg.formats)) < 0)
+        return ret;
+    if ((ret = ff_formats_ref(formats, &outlink->incfg.formats)) < 0)
+        return ret;
+
+    /* set input1 subtitle formats */
+    formats = ff_make_format_list(subtitle_fmts);
+    if ((ret = ff_formats_ref(formats, &inlink1->outcfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+
+    outlink->w = ctx->inputs[0]->w;
+    outlink->h = ctx->inputs[0]->h;
+    outlink->time_base = ctx->inputs[0]->time_base;
+    outlink->frame_rate = ctx->inputs[0]->frame_rate;
+
+    return 0;
+}
+
+static int config_input_main(AVFilterLink *inlink)
+{
+    AVFilterContext *ctx  = inlink->dst;
+    TextSubsContext *s = inlink->dst->priv;
+    int ret;
+
+    ret = ff_draw_init(&s->draw, inlink->format, s->alpha ? FF_DRAW_PROCESS_ALPHA : 0);
+    if (ret < 0) {
+        av_log(ctx, AV_LOG_ERROR, "Could not initialize ff_draw.\n");
+        return ret;
+    }
+
+    ass_set_frame_size  (s->renderer, inlink->w, inlink->h);
+    ass_set_pixel_aspect(s->renderer, av_q2d(inlink->sample_aspect_ratio));
+
+    av_log(ctx, AV_LOG_VERBOSE, "Subtitle screen: %dx%d\n\n\n\n", inlink->w, inlink->h);
+
+    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(TextSubsContext *s, 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(&s->draw, &color, rgba_color);
+        ff_blend_mask(&s->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 void process_header(AVFilterContext *link, AVFrame *frame)
+{
+    TextSubsContext *s = link->priv;
+    ASS_Track *track = s->track;
+    ASS_Style *style;
+    int sid = 0;
+
+    if (!track)
+        return;
+
+    if (frame && frame->subtitle_header) {
+        char *subtitle_header = (char *)frame->subtitle_header->data;
+        ass_process_codec_private(s->track, subtitle_header, strlen(subtitle_header));
+    }
+    else {
+        char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+        if (!subtitle_header)
+            return;
+
+        ass_process_codec_private(s->track, subtitle_header, strlen(subtitle_header));
+        av_free(subtitle_header);
+    }
+
+    if (s->language)
+        s->track->Language = av_strdup(s->language);
+
+    if (!s->track->event_format) {
+        s->track->event_format = av_strdup("ReadOrder, Layer, Style, Name, MarginL, MarginR, MarginV, Effect, Text");
+    }
+
+    if (s->track->n_styles == 0) {
+        sid = ass_alloc_style(track);
+        style = &s->track->styles[sid];
+        style->Name             = av_strdup("Default");
+        style->PrimaryColour    = 0xffffff00;
+        style->SecondaryColour  = 0x00ffff00;
+        style->OutlineColour    = 0x00000000;
+        style->BackColour       = 0x00000080;
+        style->Bold             = 200;
+        style->ScaleX           = 1.0;
+        style->ScaleY           = 1.0;
+        style->Spacing          = 0;
+        style->BorderStyle      = 1;
+        style->Outline          = 2;
+        style->Shadow           = 3;
+        style->Alignment        = 2;
+    }
+    else
+        style = &s->track->styles[sid];
+
+    style->FontSize         = s->font_size;
+    style->MarginL = style->MarginR = style->MarginV = s->margin;
+
+    track->default_style = sid;
+
+    s->got_header = 1;
+}
+
+static int filter_video_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFilterContext *ctx = inlink->dst;
+    TextSubsContext *s = ctx->priv;
+    int detect_change = 0;
+    ASS_Image *image;
+
+
+    int64_t time_ms  = (int64_t)((double)frame->pts * av_q2d(inlink->time_base) * 1000);
+    int64_t time_ms1 = (int64_t)((double)ctx->inputs[1]->current_pts * av_q2d(ctx->inputs[1]->time_base) * 1000);
+
+    if (time_ms1 < time_ms + 1000)
+        ff_request_frame(ctx->inputs[1]);
+
+    av_log(ctx, AV_LOG_DEBUG, "filter_video_frame - video: %"PRId64"ms  sub: %"PRId64"ms  rel %d\n", time_ms, time_ms1, (time_ms1 < time_ms));
+
+    ff_mutex_lock(&s->mutex);
+    image = ass_render_frame(s->renderer, s->track, time_ms, &detect_change);
+    ff_mutex_unlock(&s->mutex);
+
+    if (detect_change)
+        av_log(ctx, AV_LOG_DEBUG, "Change happened at time ms:%"PRId64"\n", time_ms);
+
+    overlay_ass_image(s, frame, image);
+
+    return ff_filter_frame(ctx->outputs[0], frame);
+}
+
+static int filter_subtitle_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFilterContext *ctx = inlink->dst;
+    TextSubsContext *s = ctx->priv;
+    const int64_t start_time = av_rescale_q(frame->subtitle_timing.start_pts, AV_TIME_BASE_Q, av_make_q(1, 1000));
+    const int64_t duration   = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, av_make_q(1, 1000));
+    const int64_t frame_time = (int64_t)((double)frame->pts * av_q2d(inlink->time_base) * 1000);
+
+    // Postpone header processing until we receive a frame with content
+    if (!s->got_header && frame->num_subtitle_areas > 0)
+        process_header(ctx, frame);
+
+    av_log(ctx, AV_LOG_DEBUG, "filter_subtitle_frame dur: %"PRId64"ms frame: %"PRId64"ms  sub: %"PRId64"ms  repeat_sub %d\n", duration, frame_time, start_time, frame->repeat_sub);
+
+    if (frame->repeat_sub)
+        goto exit;
+
+    ff_mutex_lock(&s->mutex);
+
+    if (s->render_latest_only && s->track->n_events > 0) {
+        const int64_t previous_start_time = s->track->events[s->track->n_events - 1].Start;
+        const int64_t diff = start_time - previous_start_time;
+        for (int i = s->track->n_events - 1; i >= 0; i--) {
+            if (previous_start_time != s->track->events[i].Start)
+                break;
+
+            if (s->track->events[i].Duration > diff)
+                s->track->events[i].Duration = diff;
+        }
+    }
+
+    for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+        char *ass_line = frame->subtitle_areas[i]->ass;
+        if (!ass_line)
+            continue;
+
+        ass_process_chunk(s->track, ass_line, strlen(ass_line), start_time, duration);
+    }
+
+    ff_mutex_unlock(&s->mutex);
+
+exit:
+    av_frame_free(&frame);
+    return 0;
+}
+
+static av_cold int init(AVFilterContext *ctx)
+{
+    int ret;
+    TextSubsContext *s = ctx->priv;
+
+    s->library = ass_library_init();
+
+    if (!s->library) {
+        av_log(ctx, AV_LOG_ERROR, "Could not initialize libass.\n");
+        return AVERROR(EINVAL);
+    }
+
+    ass_set_message_cb(s->library, ass_log, ctx);
+
+    /* Initialize fonts */
+    if (s->fonts_dir)
+        ass_set_fonts_dir(s->library, s->fonts_dir);
+
+    ass_set_extract_fonts(s->library, 1);
+
+    s->renderer = ass_renderer_init(s->library);
+    if (!s->renderer) {
+        av_log(ctx, AV_LOG_ERROR, "Could not initialize libass renderer.\n");
+        return AVERROR(EINVAL);
+    }
+
+    s->track = ass_new_track(s->library);
+    if (!s->track) {
+        av_log(ctx, AV_LOG_ERROR, "ass_new_track() failed!\n");
+        return AVERROR(EINVAL);
+    }
+
+    ass_set_check_readorder(s->track, 0);
+
+    ass_set_fonts(s->renderer, s->default_font_path, NULL, 1, s->fc_file, 1);
+
+    if (s->force_style) {
+        char **list = NULL;
+        char *temp = NULL;
+        char *ptr = av_strtok(s->force_style, ",", &temp);
+        int i = 0;
+        while (ptr) {
+            av_dynarray_add(&list, &i, ptr);
+            if (!list) {
+                return AVERROR(ENOMEM);
+            }
+            ptr = av_strtok(NULL, ",", &temp);
+        }
+        av_dynarray_add(&list, &i, NULL);
+        if (!list) {
+            return AVERROR(ENOMEM);
+        }
+        ass_set_style_overrides(s->library, list);
+        av_free(list);
+    }
+
+    ret = ff_mutex_init(&s->mutex, NULL);
+    if (ret) {
+        av_log(ctx, AV_LOG_ERROR, "mutex initialiuzation failed! Error code: %d\n", ret);
+        return AVERROR(EINVAL);
+    }
+
+    s->is_mutex_initialized = 1;
+
+    return ret;
+}
+
+static int textsub2video_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 input0 subtitle format */
+    formats = ff_make_format_list(subtitle_fmts);
+    if ((ret = ff_formats_ref(formats, &inlink->outcfg.formats)) < 0)
+        return ret;
+
+    /* set output0 video format */
+    formats = ff_draw_supported_pixel_formats(AV_PIX_FMT_FLAG_ALPHA);
+    if ((ret = ff_formats_ref(formats, &outlink->incfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int textsub2video_config_input(AVFilterLink *inlink)
+{
+    AVFilterContext *ctx = inlink->dst;
+    TextSubsContext *s = ctx->priv;
+
+    if (s->out_w <= 0 || s->out_h <= 0) {
+        s->out_w = inlink->w;
+        s->out_h = inlink->h;
+    }
+
+    return 0;
+}
+
+static int textsub2video_config_output(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    TextSubsContext *s = ctx->priv;
+    int ret;
+
+    ret = ff_draw_init(&s->draw, outlink->format, FF_DRAW_PROCESS_ALPHA);
+    if (ret < 0) {
+        av_log(ctx, AV_LOG_ERROR, "Could not initialize ff_draw.\n");
+        return ret;
+    }
+
+    if (s->out_w <= 0 || s->out_h <= 0) {
+        av_log(ctx, AV_LOG_ERROR, "No output image size set.\n");
+        return AVERROR(EINVAL);
+    }
+
+    ass_set_frame_size  (s->renderer, s->out_w, s->out_h);
+
+    outlink->w = s->out_w;
+    outlink->h = s->out_h;
+    outlink->sample_aspect_ratio = (AVRational){1,1};
+    outlink->frame_rate = s->frame_rate;
+
+    return 0;
+}
+
+static int textsub2video_request_frame(AVFilterLink *outlink)
+{
+    TextSubsContext *s = outlink->src->priv;
+    AVFilterLink *inlink = outlink->src->inputs[0];
+    int64_t last_pts = outlink->current_pts;
+    int64_t next_pts, time_ms;
+    int i, detect_change = 0, status;
+    AVFrame *out;
+    ASS_Image *image;
+
+    status = ff_outlink_get_status(inlink);
+    if (status == AVERROR_EOF)
+        return AVERROR_EOF;
+
+    if (s->eof)
+        return AVERROR_EOF;
+
+    if (inlink->current_pts == AV_NOPTS_VALUE) { // || outlink->current_pts > inlink->current_pts) {
+        int ret = ff_request_frame(inlink);
+        if (ret == AVERROR_EOF) {
+            s->eof = 1;
+        }
+
+        if (ret != 0)
+            av_log(outlink->src, AV_LOG_DEBUG, "ff_request_frame returned: %d\n", ret);
+
+        s->need_frame = 1;
+        return 0;
+    }
+
+    if (last_pts == AV_NOPTS_VALUE)
+        next_pts = last_pts = inlink->current_pts * av_q2d(inlink->time_base) / av_q2d(outlink->time_base);
+    else
+        next_pts = last_pts + (int64_t)(1.0 / av_q2d(outlink->frame_rate) / av_q2d(outlink->time_base));
+
+    time_ms = (int64_t)((double)next_pts * av_q2d(outlink->time_base) * 1000);
+
+    ff_mutex_lock(&s->mutex);
+    image = ass_render_frame(s->renderer, s->track, time_ms, &detect_change);
+    ff_mutex_unlock(&s->mutex);
+
+    if (detect_change)
+        av_log(outlink->src, AV_LOG_VERBOSE, "Change happened at time ms:%"PRId64" pts:%"PRId64"\n", time_ms, next_pts);
+    else if (s->last_frame) {
+        out = av_frame_clone(s->last_frame);
+        if (!out)
+            return AVERROR(ENOMEM);
+
+        out->pts = out->pkt_dts = out->best_effort_timestamp = next_pts;
+        return ff_filter_frame(outlink, out);
+    }
+
+    out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
+    if (!out)
+        return AVERROR(ENOMEM);
+
+    for (i = 0; i < AV_NUM_DATA_POINTERS; i++) {
+        if (out->buf[i] && i != 1)
+            memset(out->buf[i]->data, 0, out->buf[i]->size);
+    }
+
+    out->pts = out->pkt_dts = out->best_effort_timestamp = next_pts;
+
+    if (image)
+        overlay_ass_image(s, out, image);
+
+    av_frame_free(&s->last_frame);
+
+    s->last_frame = av_frame_clone(out);
+
+    return ff_filter_frame(outlink, out);
+}
+
+static int textsub2video_filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFilterContext *ctx = inlink->dst;
+    TextSubsContext *s = ctx->priv;
+    const int64_t start_time = av_rescale_q(frame->subtitle_timing.start_pts, AV_TIME_BASE_Q, av_make_q(1, 1000));
+    const int64_t duration   = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, av_make_q(1, 1000));
+
+    av_log(ctx, AV_LOG_VERBOSE, "textsub2video_filter_frame num_subtitle_rects: %d, start_time_ms: %"PRId64"\n", frame->num_subtitle_areas, start_time);
+
+    if (!s->got_header && frame->num_subtitle_areas > 0)
+        process_header(ctx, frame);
+
+    if (frame->repeat_sub)
+        goto exit;
+
+    ff_mutex_lock(&s->mutex);
+
+    if (s->render_latest_only && s->track->n_events > 0) {
+        const int64_t previous_start_time = s->track->events[s->track->n_events - 1].Start;
+        const int64_t diff = start_time - previous_start_time;
+        for (int i = s->track->n_events - 1; i >= 0; i--) {
+            if (previous_start_time != s->track->events[i].Start)
+                break;
+
+            if (s->track->events[i].Duration > diff)
+                s->track->events[i].Duration = diff;
+
+        }
+    }
+
+    for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+        char *ass_line = frame->subtitle_areas[i]->ass;
+        if (!ass_line)
+            continue;
+
+        ass_process_chunk(s->track, ass_line, strlen(ass_line), start_time, duration);
+    }
+
+
+    ff_mutex_unlock(&s->mutex);
+
+exit:
+    av_frame_free(&frame);
+
+    if (s->need_frame) {
+        s->need_frame = 0;
+        return textsub2video_request_frame(ctx->outputs[0]);
+    }
+
+    return 0;
+}
+
+#define OFFSET(x) offsetof(TextSubsContext, x)
+#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption overlaytextsubs_options[] = {
+    {"alpha",              "enable processing of alpha channel", OFFSET(alpha),              AV_OPT_TYPE_BOOL,   {.i64 = 0   }, 0,         1,        .flags =  FLAGS},
+    {"font_size",          "default font size",                  OFFSET(font_size),          AV_OPT_TYPE_DOUBLE, {.dbl = 18.0}, 0.0,       100.0,    .flags =  FLAGS},
+    {"force_style",        "force subtitle style",               OFFSET(force_style),        AV_OPT_TYPE_STRING, {.str = NULL}, 0,         0,        .flags =  FLAGS},
+    {"margin",             "default margin",                     OFFSET(margin),             AV_OPT_TYPE_INT,    {.i64 = 20  }, 0,         INT_MAX,  .flags =  FLAGS},
+    {"default_font_path",  "path to default font",               OFFSET(default_font_path),  AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"fonts_dir",          "directory to scan for fonts",        OFFSET(fonts_dir),          AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"fontsdir",           "directory to scan for fonts",        OFFSET(fonts_dir),          AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"fontconfig_file",    "fontconfig file to load",            OFFSET(fc_file),            AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"language",           "default language",                   OFFSET(language),           AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"render_latest_only", "newest sub event for each time",     OFFSET(render_latest_only), AV_OPT_TYPE_BOOL,   {.i64 = 0   }, 0,         1,        .flags =  FLAGS},
+    { .name = NULL }
+};
+
+static const AVOption textsub2video_options[] = {
+    {"rate",               "set frame rate",                   OFFSET(frame_rate),         AV_OPT_TYPE_VIDEO_RATE, {.str="8"},   0,         INT_MAX,   .flags =  FLAGS},
+    {"r",                  "set frame rate",                   OFFSET(frame_rate),         AV_OPT_TYPE_VIDEO_RATE, {.str="8"},   0,         INT_MAX,   .flags =  FLAGS},
+    {"size",               "set video size",                   OFFSET(out_w),              AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0,         0,        .flags =  FLAGS},
+    {"s",                  "set video size",                   OFFSET(out_w),              AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0,         0,        .flags =  FLAGS},
+    {"font_size",          "default font size",                OFFSET(font_size),          AV_OPT_TYPE_DOUBLE,     {.dbl = 18.0}, 0.0,       100.0,    .flags =  FLAGS},
+    {"force_style",        "force subtitle style",             OFFSET(force_style),        AV_OPT_TYPE_STRING,     {.str = NULL}, 0,         0,        .flags =  FLAGS},
+    {"margin",             "default margin",                   OFFSET(margin),             AV_OPT_TYPE_INT,        {.i64 = 20  }, 0,         INT_MAX,  .flags =  FLAGS},
+    {"default_font_path",  "path to default font",             OFFSET(default_font_path),  AV_OPT_TYPE_STRING,     {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"fonts_dir",          "directory to scan for fonts",      OFFSET(fonts_dir),          AV_OPT_TYPE_STRING,     {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"fontsdir",           "directory to scan for fonts",      OFFSET(fonts_dir),          AV_OPT_TYPE_STRING,     {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"fontconfig_file",    "fontconfig file to load",          OFFSET(fc_file),            AV_OPT_TYPE_STRING,     {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"language",           "default language",                 OFFSET(language),           AV_OPT_TYPE_STRING,     {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"render_latest_only", "newest sub event for each time",   OFFSET(render_latest_only), AV_OPT_TYPE_BOOL,       {.i64 = 0   }, 0,         1,        .flags =  FLAGS},
+    { .name = NULL }
+};
+
+#if CONFIG_OVERLAYTEXTSUBS_FILTER
+
+AVFILTER_DEFINE_CLASS(overlaytextsubs);
+
+static const AVFilterPad overlaytextsubs_inputs[] = {
+    {
+        .name         = "main",
+        .type         = AVMEDIA_TYPE_VIDEO,
+        .config_props = config_input_main,
+        .flags        = AVFILTERPAD_FLAG_NEEDS_WRITABLE,
+        .filter_frame = filter_video_frame,
+    },
+    {
+        .name         = "overlay",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .filter_frame = filter_subtitle_frame,
+    },
+};
+
+static const AVFilterPad overlaytextsubs_outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_VIDEO,
+        .config_props  = config_output,
+    },
+};
+
+const AVFilter ff_vf_overlaytextsubs = {
+    .name          = "overlaytextsubs",
+    .description   = NULL_IF_CONFIG_SMALL("Overlay textual subtitles on top of the input."),
+    .init          = init,
+    .uninit        = uninit,
+    .priv_size     = sizeof(TextSubsContext),
+    .priv_class    = &overlaytextsubs_class,
+    FILTER_INPUTS(overlaytextsubs_inputs),
+    FILTER_OUTPUTS(overlaytextsubs_outputs),
+    FILTER_QUERY_FUNC(overlay_textsubs_query_formats),
+};
+#endif
+
+#if CONFIG_TEXTSUB2VIDEO_FILTER
+
+AVFILTER_DEFINE_CLASS(textsub2video);
+
+static const AVFilterPad textsub2video_inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .config_props = textsub2video_config_input,
+        .filter_frame = textsub2video_filter_frame,
+    },
+};
+
+static const AVFilterPad textsub2video_outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_VIDEO,
+        .config_props  = textsub2video_config_output,
+        .request_frame = textsub2video_request_frame,
+    },
+};
+
+const AVFilter ff_svf_textsub2video = {
+    .name          = "textsub2video",
+    .description   = NULL_IF_CONFIG_SMALL("Convert textual subtitles to video frames"),
+    .init          = init,
+    .uninit        = uninit,
+    .priv_size     = sizeof(TextSubsContext),
+    .priv_class    = &textsub2video_class,
+    FILTER_INPUTS(textsub2video_inputs),
+    FILTER_OUTPUTS(textsub2video_outputs),
+    FILTER_QUERY_FUNC(textsub2video_query_formats),
+};
+#endif
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v4 15/23] avfilter/textmod: Add textmod, censor and show_speaker filters
  2022-05-28 13:25     ` [FFmpeg-devel] [PATCH v4 00/23] Subtitle Filtering 2022 ffmpegagent
                         ` (13 preceding siblings ...)
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 14/23] avfilter/overlaytextsubs: Add overlaytextsubs and textsubs2video filters softworkz
@ 2022-05-28 13:25       ` softworkz
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 16/23] avfilter/stripstyles: Add stripstyles filter softworkz
                         ` (8 subsequent siblings)
  23 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-05-28 13:25 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

- textmod {S -> S)
  Modify subtitle text in a number of ways

- censor {S -> S)
  Censor subtitles using a word list

- show_speaker {S -> S)
  Prepend speaker names from ASS subtitles to the visible text lines

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 doc/filters.texi         | 206 ++++++++++++
 libavfilter/Makefile     |   5 +
 libavfilter/allfilters.c |   3 +
 libavfilter/sf_textmod.c | 710 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 924 insertions(+)
 create mode 100644 libavfilter/sf_textmod.c

diff --git a/doc/filters.texi b/doc/filters.texi
index 70d4c90663..e0ceba80e2 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -26887,6 +26887,145 @@ existing filters using @code{--disable-filters}.
 
 Below is a description of the currently available subtitle filters.
 
+
+@section censor
+
+Censor selected words in text subtitles.
+
+Inputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+@item mode
+The censoring mode to apply.
+
+Supported censoring modes are:
+
+@table @var
+@item 0, keep_first_last
+Replace all characters with the 'censor_char' except the first and the last character of a word.
+For words with less than 4 characters, the last character will be replaced as well.
+For words with less than 3 characters, the first character will be replaced as well.
+@item 1, keep_first
+Replace all characters with the 'censor_char' except the first character of a word.
+For words with less than 3 characters, the first character will be replaced as well.
+@item 2, all
+Replace all characters with the 'censor_char'.
+@end table
+
+@item words
+A list of words to censor, separated by 'separator'.
+
+@item words_file
+Specify a file from which to load the contents for the 'words' parameter.
+
+@item censor_char
+Single character used as replacement for censoring.
+
+@item separator
+Delimiter character for words. Used with replace_words and remove_words- Must be a single character.
+The default is '.'.
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Censor a few given words with a pound character.
+@example
+ffmpeg -i "http://streams.videolan.org/samples/sub/SSA/subtitle_testing_complex.mkv" -filter_complex "[0:1]censor=words='diss,louder,hope,beam,word':censor_char='#'" -map 0 -y output.mkv
+@end example
+@end itemize
+
+
+@section textmod
+
+Modify subtitle text in a number of ways.
+
+Inputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+@item mode
+The kind of text modification to apply
+
+Supported operation modes are:
+
+@table @var
+@item 0, leet
+Convert subtitle text to 'leet speak'. It's primarily useful for testing as the modification will be visible with almost all text lines.
+@item 1, to_upper
+Change all text to upper case. Might improve readability.
+@item 2, to_lower
+Change all text to lower case.
+@item 3, replace_chars
+Replace one or more characters. Requires the find and replace parameters to be specified.
+Both need to be equal in length.
+The first char in find is replaced by the first char in replace, same for all subsequent chars.
+@item 4, remove_chars
+Remove certain characters. Requires the find parameter to be specified.
+All chars in the find parameter string will be removed from all subtitle text.
+@item 5, replace_words
+Replace one or more words. Requires the find and replace parameters to be specified. Multiple words must be separated by the delimiter char specified vie the separator parameter (default: ',').
+The number of words in the find and replace parameters needs to be equal.
+The first word in find is replaced by the first word in replace, same for all subsequent words
+@item 6, remove_words
+Remove certain words. Requires the find parameter to be specified. Multiple words must be separated by the delimiter char specified vie the separator parameter (default: ',').
+All words in the find parameter string will be removed from all subtitle text.
+@end table
+
+@item find
+Required for replace_chars, remove_chars, replace_words and remove_words.
+
+@item find_file
+Specify a file from which to load the contents for the 'find' parameter.
+
+@item replace
+Required for replace_chars and replace_words.
+
+@item replace_file
+Specify a file from which to load the contents for the 'replace' parameter.
+
+@item separator
+Delimiter character for words. Used with replace_words and remove_words- Must be a single character.
+The default is '.'.
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Change all characters to upper case while keeping all styles and animations:
+@example
+ffmpeg -i "https://streams.videolan.org/ffmpeg/mkv_subtitles.mkv" -filter_complex "[0:s]textmod=mode=to_upper" -map 0 -y out.mkv
+@end example
+@item
+Remove a set of symbol characters for am improved and smoother visual apperance:
+@example
+ffmpeg -i "https://streams.videolan.org/ffmpeg/mkv_subtitles.mkv" -filter_complex "[0:s]textmod=mode=remove_chars:find='$&#@*§'" -map 0 -y out.mkv
+@end example
+@end itemize
+
 @section graphicsub2video
 
 Renders graphic subtitles as video frames.
@@ -27054,6 +27193,73 @@ ffmpeg -i "http://streams.videolan.org/samples/sub/SSA/subtitle_testing_complex.
 @end example
 @end itemize
 
+@section showspeaker
+
+Prepend speaker names to subtitle lines (when available).
+
+Subtitles in ASS/SSA format are often including the names of the persons
+or character for each subtitle line. The showspeaker filter adds those names
+to the actual subtitle text to make it visible on playback.
+
+Inputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+@item format
+The format for prepending speaker names. Default is 'square_brackets'.
+
+Supported operation modes are:
+
+@table @var
+@item 0, square_brackets
+Enclose the speaker name in square brackets, followed by space ('[speaker] text').
+@item 1, round_brackets
+Enclose the speaker name in round brackets, followed by space ('(speaker) text').
+@item 2, colon
+Separate the speaker name with a colon and space ('speaker: text').
+@item 3, plain
+Separate the speaker name with a space only ('speaker text').
+@end table
+
+@item line_break
+Set thís parameter to insert a line break between speaker name and text instead of the space character.
+
+@item style
+Allows to set a specific style for the speaker name text.
+
+This can be either a named style that exists in the ass subtitle header script (e.g. 'Default') or an ass style override code.
+Example:  @{\\c&HDD0000&\\be1\\i1\\bord10@}
+This sets the color to blue, enables edge blurring, italic font and a border of size 10.
+
+The behavior is as follows:
+
+- When the style parameter is not provided, the filter will find the first position in the event string that is actual text.
+  The speaker name will be inserted at this position. This allows to have the speaker name shown in the same style like the
+  regular text, in case the string would start with a sequence of style codes.
+- When the style parameter is provided, everything will be prepended to the original text:
+  Style Code or Style name >> Speaker Name >> Style Reset Code >> Original Text
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Prepend speaker names with blue text, smooth edges and blend/overlay that onto the video.
+@example
+ffmpeg -i INPUT -filter_complex "showspeaker=format=colon:style='@{\\c&HDD0000&\\be1@}',[0:v]overlaytextsubs"
+@end example
+@end itemize
+
 @section textsub2video
 
 Converts text subtitles to video frames.
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 84ec2fa542..6d2a75128c 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -576,6 +576,11 @@ OBJS-$(CONFIG_YUVTESTSRC_FILTER)             += vsrc_testsrc.o
 
 OBJS-$(CONFIG_NULLSINK_FILTER)               += vsink_nullsink.o
 
+# subtitle filters
+OBJS-$(CONFIG_CENSOR_FILTER)                 += sf_textmod.o
+OBJS-$(CONFIG_SHOW_SPEAKER_FILTER)           += sf_textmod.o
+OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
+
 # multimedia filters
 OBJS-$(CONFIG_ABITSCOPE_FILTER)              += avf_abitscope.o
 OBJS-$(CONFIG_ADRAWGRAPH_FILTER)             += f_drawgraph.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index a6b95785be..960726ba08 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -559,6 +559,9 @@ 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_censor;
+extern const AVFilter ff_sf_showspeaker;
+extern const AVFilter ff_sf_textmod;
 extern const AVFilter ff_svf_graphicsub2video;
 extern const AVFilter ff_svf_textsub2video;
 
diff --git a/libavfilter/sf_textmod.c b/libavfilter/sf_textmod.c
new file mode 100644
index 0000000000..c75244cfb2
--- /dev/null
+++ b/libavfilter/sf_textmod.c
@@ -0,0 +1,710 @@
+/*
+ * 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 allows to modify subtitle text in several ways
+ */
+
+#include <libavutil/ass_internal.h>
+
+#include "libavutil/opt.h"
+#include "internal.h"
+#include "libavutil/ass_split_internal.h"
+#include "libavutil/bprint.h"
+#include "libavutil/file.h"
+
+static const char* leet_src = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+static const char* leet_dst = "abcd3f6#1jklmn0pq257uvwxyzAB(D3F6#1JKLMN0PQ257UVWXYZ";
+
+enum TextModFilterType {
+    TM_TEXTMOD,
+    TM_CENSOR,
+    TM_SHOW_SPEAKER,
+};
+
+enum TextModOperation {
+    OP_LEET,
+    OP_TO_UPPER,
+    OP_TO_LOWER,
+    OP_REPLACE_CHARS,
+    OP_REMOVE_CHARS,
+    OP_REPLACE_WORDS,
+    OP_REMOVE_WORDS,
+    NB_OPS,
+};
+
+enum CensorMode {
+    CM_KEEP_FIRST_LAST,
+    CM_KEEP_FIRST,
+    CM_ALL,
+};
+
+enum ShowSpeakerMode {
+    SM_SQUARE_BRACKETS,
+    SM_ROUND_BRACKETS,
+    SM_COLON,
+    SM_PLAIN,
+};
+
+typedef struct TextModContext {
+    const AVClass *class;
+    enum AVSubtitleType format;
+    enum TextModFilterType filter_type;
+    enum TextModOperation operation;
+    enum CensorMode censor_mode;
+    enum ShowSpeakerMode speaker_mode;
+    char *find;
+    char *find_file;
+    char *style;
+    char *replace;
+    char *replace_file;
+    char *separator;
+    char *censor_char;
+    char **find_list;
+    int  line_break;
+    int  nb_find_list;
+    char **replace_list;
+    int  nb_replace_list;
+} TextModContext;
+
+static char **split_string(char *source, int *nb_elems, const char *delim)
+{
+    char **list = NULL;
+    char *temp = NULL;
+    char *ptr = av_strtok(source, delim, &temp);
+
+    while (ptr) {
+        if (strlen(ptr)) {
+            av_dynarray_add(&list, nb_elems, ptr);
+            if (!list)
+                return NULL;
+        }
+
+        ptr = av_strtok(NULL, delim, &temp);
+    }
+
+    if (!list)
+        return NULL;
+
+    for (int i = 0; i < *nb_elems; i++) {
+        list[i] = av_strdup(list[i]);
+        if (!list[i]) {
+            for (int n = 0; n < i; n++)
+                av_free(list[n]);
+            av_free(list);
+            return NULL;
+        }
+    }
+
+    return list;
+}
+
+static int load_text_from_file(AVFilterContext *ctx, const char *file_name, char **text, char separator)
+{
+    int err;
+    uint8_t *textbuf;
+    char *tmp;
+    size_t textbuf_size;
+    int offset = 0;
+
+    if ((err = av_file_map(file_name, &textbuf, &textbuf_size, 0, ctx)) < 0) {
+        av_log(ctx, AV_LOG_ERROR, "The text file '%s' could not be read or is empty\n", file_name);
+        return err;
+    }
+
+    if (textbuf_size > 1 &&
+        (textbuf[0] == 0xFF && textbuf[1] == 0xFE
+        || textbuf[0] == 0xFE && textbuf[1] == 0xFF)) {
+        av_log(ctx, AV_LOG_ERROR, "UTF text files are not supported. File: %s\n", file_name);
+        return AVERROR(EINVAL);
+    }
+
+    if (textbuf_size > 2 && textbuf[0] == 0xEF && textbuf[1] == 0xBB && textbuf[2] == 0xBF)
+        offset = 3; // UTF-8
+
+    if (textbuf_size > SIZE_MAX - 1 || !((tmp = av_strndup((char *)textbuf + offset, textbuf_size - offset)))) {
+        av_file_unmap(textbuf, textbuf_size);
+        return AVERROR(ENOMEM);
+    }
+
+    av_file_unmap(textbuf, textbuf_size);
+
+    for (size_t i = 0; i < strlen(tmp); i++) {
+        switch (tmp[i]) {
+        case '\n':
+        case '\r':
+        case '\f':
+        case '\v':
+            tmp[i] = separator;
+        }
+    }
+
+    *text = tmp;
+
+    return 0;
+}
+
+static int load_files(AVFilterContext *ctx)
+{
+    TextModContext *s = ctx->priv;
+    int ret;
+
+    if (!s->separator || strlen(s->separator) != 1) {
+        av_log(ctx, AV_LOG_ERROR, "A single character needs to be specified for the separator parameter.\n");
+        return AVERROR(EINVAL);
+    }
+
+    if (s->find_file && strlen(s->find_file)) {
+        ret = load_text_from_file(ctx, s->find_file, &s->find, s->separator[0]);
+        if (ret < 0 )
+            return ret;
+    }
+
+    if (s->replace_file && strlen(s->replace_file)) {
+        ret = load_text_from_file(ctx, s->replace_file, &s->replace, s->separator[0]);
+        if (ret < 0 )
+            return ret;
+    }
+
+    return 0;
+}
+
+static int init_censor(AVFilterContext *ctx)
+{
+    TextModContext *s = ctx->priv;
+    int ret;
+
+    s->filter_type = TM_CENSOR;
+    s->operation = OP_REPLACE_WORDS;
+
+    ret = load_files(ctx);
+    if (ret < 0 )
+        return ret;
+
+    if (!s->find || !strlen(s->find)) {
+        av_log(ctx, AV_LOG_ERROR, "Either the 'words' or the 'words_file' parameter needs to be specified\n");
+        return AVERROR(EINVAL);
+    }
+
+    if (!s->censor_char || strlen(s->censor_char) != 1) {
+        av_log(ctx, AV_LOG_ERROR, "A single character needs to be specified for the censor_char parameter\n");
+        return AVERROR(EINVAL);
+    }
+
+    s->find_list = split_string(s->find, &s->nb_find_list, s->separator);
+    if (!s->find_list)
+        return AVERROR(ENOMEM);
+
+    s->replace_list = av_calloc(s->nb_find_list, sizeof(char *));
+    if (!s->replace_list)
+        return AVERROR(ENOMEM);
+
+    for (int i = 0; i < s->nb_find_list; i++) {
+        size_t len, start = 0, end;
+        char *item = av_strdup(s->find_list[i]);
+        if (!item)
+            return AVERROR(ENOMEM);
+
+        len = end = strlen(item);
+
+        switch (s->censor_mode) {
+        case CM_KEEP_FIRST_LAST:
+
+            if (len > 2)
+                start = 1;
+            if (len > 3)
+                end--;
+
+            break;
+        case CM_KEEP_FIRST:
+
+            if (len > 2)
+                start = 1;
+
+            break;
+        }
+
+        for (size_t n = start; n < end; n++)
+            item[n] = s->censor_char[0];
+
+        s->replace_list[i] = item;
+    }
+
+    return 0;
+}
+
+static int init_showspeaker(AVFilterContext *ctx)
+{
+    TextModContext *s = ctx->priv;
+    s->filter_type = TM_SHOW_SPEAKER;
+
+    return 0;
+}
+
+static int init(AVFilterContext *ctx)
+{
+    TextModContext *s = ctx->priv;
+    int ret;
+
+    ret = load_files(ctx);
+    if (ret < 0 )
+        return ret;
+
+    switch (s->operation) {
+    case OP_REPLACE_CHARS:
+    case OP_REMOVE_CHARS:
+    case OP_REPLACE_WORDS:
+    case OP_REMOVE_WORDS:
+        if (!s->find || !strlen(s->find)) {
+            av_log(ctx, AV_LOG_ERROR, "Selected mode requires the 'find' parameter to be specified\n");
+            return AVERROR(EINVAL);
+        }
+        break;
+    }
+
+    switch (s->operation) {
+    case OP_REPLACE_CHARS:
+    case OP_REPLACE_WORDS:
+        if (!s->replace || !strlen(s->replace)) {
+            av_log(ctx, AV_LOG_ERROR, "Selected mode requires the 'replace' parameter to be specified\n");
+            return AVERROR(EINVAL);
+        }
+        break;
+    }
+
+    if (s->operation == OP_REPLACE_CHARS && strlen(s->find) != strlen(s->replace)) {
+        av_log(ctx, AV_LOG_ERROR, "Selected mode requires the 'find' and 'replace' parameters to have the same length\n");
+        return AVERROR(EINVAL);
+    }
+
+    if (s->operation == OP_REPLACE_WORDS || s->operation == OP_REMOVE_WORDS) {
+        if (!s->separator || strlen(s->separator) != 1) {
+            av_log(ctx, AV_LOG_ERROR, "Selected mode requires a single separator char to be specified\n");
+            return AVERROR(EINVAL);
+        }
+
+        s->find_list = split_string(s->find, &s->nb_find_list, s->separator);
+        if (!s->find_list)
+            return AVERROR(ENOMEM);
+
+        if (s->operation == OP_REPLACE_WORDS) {
+
+            s->replace_list = split_string(s->replace, &s->nb_replace_list, s->separator);
+            if (!s->replace_list)
+                return AVERROR(ENOMEM);
+
+            if (s->nb_find_list != s->nb_replace_list) {
+                av_log(ctx, AV_LOG_ERROR, "The number of words in 'find' and 'replace' needs to be equal\n");
+                return AVERROR(EINVAL);
+            }
+        }
+    }
+
+    return 0;
+}
+
+static void uninit(AVFilterContext *ctx)
+{
+    TextModContext *s = ctx->priv;
+
+    for (int i = 0; i < s->nb_find_list; i++)
+        av_freep(&s->find_list[i]);
+
+    s->nb_find_list = 0;
+    av_freep(&s->find_list);
+
+    for (int i = 0; i < s->nb_replace_list; i++)
+        av_freep(&s->replace_list[i]);
+
+    s->nb_replace_list = 0;
+    av_freep(&s->replace_list);
+}
+
+static char *process_text(TextModContext *s, char *text)
+{
+    const char *char_src = s->find;
+    const char *char_dst = s->replace;
+    char *result         = NULL;
+    int escape_level     = 0, k = 0;
+
+    switch (s->operation) {
+    case OP_LEET:
+    case OP_REPLACE_CHARS:
+
+        if (s->operation == OP_LEET) {
+            char_src = leet_src;
+            char_dst = leet_dst;
+        }
+
+        result = av_strdup(text);
+        if (!result)
+            return NULL;
+
+        for (size_t n = 0; n < strlen(result); n++) {
+            if (result[n] == '{')
+                escape_level++;
+
+            if (!escape_level) {
+                size_t len = strlen(char_src);
+                for (size_t t = 0; t < len; t++) {
+                    if (result[n] == char_src[t]) {
+                        result[n] = char_dst[t];
+                        break;
+                    }
+                }
+            }
+
+            if (result[n] == '}')
+                escape_level--;
+        }
+
+        break;
+    case OP_TO_UPPER:
+    case OP_TO_LOWER:
+
+        result = av_strdup(text);
+        if (!result)
+            return NULL;
+
+        for (size_t n = 0; n < strlen(result); n++) {
+            if (result[n] == '{')
+                escape_level++;
+            if (!escape_level)
+                result[n] = s->operation == OP_TO_LOWER ? av_tolower(result[n]) : av_toupper(result[n]);
+            if (result[n] == '}')
+                escape_level--;
+        }
+
+        break;
+    case OP_REMOVE_CHARS:
+
+        result = av_strdup(text);
+        if (!result)
+            return NULL;
+
+        for (size_t n = 0; n < strlen(result); n++) {
+            int skip_char = 0;
+
+            if (result[n] == '{')
+                escape_level++;
+
+            if (!escape_level) {
+                size_t len = strlen(char_src);
+                for (size_t t = 0; t < len; t++) {
+                    if (result[n] == char_src[t]) {
+                        skip_char = 1;
+                        break;
+                    }
+                }
+            }
+
+            if (!skip_char)
+                result[k++] = result[n];
+
+            if (result[n] == '}')
+                escape_level--;
+        }
+
+        result[k] = 0;
+
+        break;
+    case OP_REPLACE_WORDS:
+    case OP_REMOVE_WORDS:
+
+        result = av_strdup(text);
+        if (!result)
+            return NULL;
+
+        for (int n = 0; n < s->nb_find_list; n++) {
+            char *tmp           = result;
+            const char *replace = (s->operation == OP_REPLACE_WORDS) ? s->replace_list[n] : "";
+
+            result = av_strireplace(result, s->find_list[n], replace);
+            if (!result)
+                return NULL;
+
+            av_free(tmp);
+        }
+
+        break;
+    }
+
+    return result;
+}
+
+static char *process_dialog_show_speaker(TextModContext *s, char *ass_line)
+{
+    ASSDialog *dialog = avpriv_ass_split_dialog(NULL, ass_line);
+    int escape_level = 0;
+    unsigned pos = 0, len;
+    char *result, *text;
+    AVBPrint pbuf;
+
+    if (!dialog)
+        return NULL;
+
+    text = process_text(s, dialog->text);
+    if (!text)
+        return NULL;
+
+    if (!dialog->name || !strlen(dialog->name) || !dialog->text || !strlen(dialog->text))
+        return av_strdup(ass_line);
+
+    // Find insertion point in case the line starts with style codes
+    len = (unsigned)strlen(dialog->text);
+    for (unsigned i = 0; i < len; i++) {
+
+        if (dialog->text[i] == '{')
+            escape_level++;
+
+        if (dialog->text[i] == '}')
+            escape_level--;
+
+        if (escape_level == 0) {
+            pos = i;
+            break;
+        }
+    }
+
+    if (s->style && strlen(s->style))
+        // When a style is specified reset the insertion point
+        // (always add speaker plus style at the start in that case)
+        pos = 0;
+
+    if (pos >= len - 1)
+        return av_strdup(ass_line);
+
+    av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
+
+    if (pos > 0) {
+        av_bprint_append_data(&pbuf, dialog->text, pos);
+    }
+
+    if (s->style && strlen(s->style)) {
+        if (s->style[0] == '{')
+            // Assume complete and valid style code, e.g. {\c&HFF0000&}
+            av_bprintf(&pbuf, "%s", s->style);
+        else
+            // Otherwise it must be a style name
+            av_bprintf(&pbuf, "{\\r%s}", s->style);
+    }
+
+    switch (s->speaker_mode) {
+    case SM_SQUARE_BRACKETS:
+        av_bprintf(&pbuf, "[%s]", dialog->name);
+        break;
+    case SM_ROUND_BRACKETS:
+        av_bprintf(&pbuf, "(%s)", dialog->name);
+        break;
+    case SM_COLON:
+        av_bprintf(&pbuf, "%s:", dialog->name);
+        break;
+    case SM_PLAIN:
+        av_bprintf(&pbuf, "%s", dialog->name);
+        break;
+    }
+
+    if (s->style && strlen(s->style)) {
+        // Reset line style
+        if (dialog->style && strlen(dialog->style) && !av_strcasecmp(dialog->style, "default"))
+            av_bprintf(&pbuf, "{\\r%s}", dialog->style);
+        else
+            av_bprintf(&pbuf, "{\\r}");
+    }
+
+    if (s->line_break)
+        av_bprintf(&pbuf, "\\N");
+    else
+        av_bprintf(&pbuf, " ");
+
+    av_bprint_append_data(&pbuf, dialog->text + pos, len - pos);
+
+    av_bprint_finalize(&pbuf, &text);
+
+    result = avpriv_ass_get_dialog_ex(dialog->readorder, dialog->layer, dialog->style, dialog->name, dialog->margin_l, dialog->margin_r, dialog->margin_v, text);
+
+    av_free(text);
+    avpriv_ass_free_dialog(&dialog);
+    return result;
+}
+
+static char *process_dialog(TextModContext *s, char *ass_line)
+{
+    ASSDialog *dialog;
+    char *result, *text;
+
+    if (s->filter_type == TM_SHOW_SPEAKER)
+        return process_dialog_show_speaker(s, ass_line);
+
+    dialog = avpriv_ass_split_dialog(NULL, ass_line);
+    if (!dialog)
+        return NULL;
+
+    text = process_text(s, dialog->text);
+    if (!text)
+        return NULL;
+
+    result = avpriv_ass_get_dialog_ex(dialog->readorder, dialog->layer, dialog->style, dialog->name, dialog->margin_l, dialog->margin_r, dialog->margin_v, text);
+
+    av_free(text);
+    avpriv_ass_free_dialog(&dialog);
+    return result;
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    AVFilterLink *inlink = outlink->src->inputs[0];
+
+    outlink->w = inlink->w;
+    outlink->h = inlink->h;
+    outlink->time_base = inlink->time_base;
+    outlink->frame_rate = inlink->frame_rate;
+
+    return 0;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    TextModContext *s = inlink->dst->priv;
+    AVFilterLink *outlink = inlink->dst->outputs[0];
+    int ret;
+
+    outlink->format = inlink->format;
+
+    ret = av_frame_make_writable(frame);
+    if (ret < 0)
+        return ret;
+
+    for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+
+        AVSubtitleArea *area = frame->subtitle_areas[i];
+
+        if (area->ass) {
+            char *tmp = area->ass;
+            area->ass = process_dialog(s, area->ass);
+            av_free(tmp);
+            if (!area->ass)
+                return AVERROR(ENOMEM);
+        }
+    }
+
+    return ff_filter_frame(outlink, frame);
+}
+
+#define OFFSET(x) offsetof(TextModContext, x)
+#define FLAGS (AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption textmod_options[] = {
+    { "mode",             "set operation mode",              OFFSET(operation),    AV_OPT_TYPE_INT,    {.i64=OP_LEET},          OP_LEET, NB_OPS-1, FLAGS, "mode" },
+    {   "leet",           "convert text to 'leet speak'",    0,                    AV_OPT_TYPE_CONST,  {.i64=OP_LEET},          0,       0,        FLAGS, "mode" },
+    {   "to_upper",       "change to upper case",            0,                    AV_OPT_TYPE_CONST,  {.i64=OP_TO_UPPER},      0,       0,        FLAGS, "mode" },
+    {   "to_lower",       "change to lower case",            0,                    AV_OPT_TYPE_CONST,  {.i64=OP_TO_LOWER},      0,       0,        FLAGS, "mode" },
+    {   "replace_chars",  "replace characters",              0,                    AV_OPT_TYPE_CONST,  {.i64=OP_REPLACE_CHARS}, 0,       0,        FLAGS, "mode" },
+    {   "remove_chars",   "remove characters",               0,                    AV_OPT_TYPE_CONST,  {.i64=OP_REMOVE_CHARS},  0,       0,        FLAGS, "mode" },
+    {   "replace_words",  "replace words",                   0,                    AV_OPT_TYPE_CONST,  {.i64=OP_REPLACE_WORDS}, 0,       0,        FLAGS, "mode" },
+    {   "remove_words",   "remove words",                    0,                    AV_OPT_TYPE_CONST,  {.i64=OP_REMOVE_WORDS},  0,       0,        FLAGS, "mode" },
+    { "find",             "chars/words to find or remove",   OFFSET(find),         AV_OPT_TYPE_STRING, {.str = NULL},           0,       0,        FLAGS, NULL   },
+    { "find_file",        "load find param from file",       OFFSET(find_file),    AV_OPT_TYPE_STRING, {.str = NULL},           0,       0,        FLAGS, NULL   },
+    { "replace",          "chars/words to replace",          OFFSET(replace),      AV_OPT_TYPE_STRING, {.str = NULL},           0,       0,        FLAGS, NULL   },
+    { "replace_file",     "load replace param from file",    OFFSET(replace_file), AV_OPT_TYPE_STRING, {.str = NULL},           0,       0,        FLAGS, NULL   },
+    { "separator",        "word separator",                  OFFSET(separator),    AV_OPT_TYPE_STRING, {.str = ","},            0,       0,        FLAGS, NULL   },
+    { NULL },
+};
+
+
+static const AVOption censor_options[] = {
+    { "mode",               "set censoring mode",        OFFSET(censor_mode), AV_OPT_TYPE_INT,    {.i64=CM_KEEP_FIRST_LAST}, 0, 2, FLAGS, "mode" },
+    {   "keep_first_last",  "censor inner chars",        0,                   AV_OPT_TYPE_CONST,  {.i64=CM_KEEP_FIRST_LAST}, 0, 0, FLAGS, "mode" },
+    {   "keep_first",       "censor all but first char", 0,                   AV_OPT_TYPE_CONST,  {.i64=CM_KEEP_FIRST},      0, 0, FLAGS, "mode" },
+    {   "all",              "censor all chars",          0,                   AV_OPT_TYPE_CONST,  {.i64=CM_ALL},             0, 0, FLAGS, "mode" },
+    { "words",              "list of words to censor",   OFFSET(find),        AV_OPT_TYPE_STRING, {.str = NULL},             0, 0, FLAGS, NULL   },
+    { "words_file",         "path to word list file",    OFFSET(find_file),   AV_OPT_TYPE_STRING, {.str = NULL},             0, 0, FLAGS, NULL   },
+    { "separator",          "word separator",            OFFSET(separator),   AV_OPT_TYPE_STRING, {.str = ","},              0, 0, FLAGS, NULL   },
+    { "censor_char",        "replacement character",     OFFSET(censor_char), AV_OPT_TYPE_STRING, {.str = "*"},              0, 0, FLAGS, NULL   },
+    { NULL },
+};
+
+static const AVOption showspeaker_options[] = {
+    { "format",             "speaker name formatting",        OFFSET(speaker_mode), AV_OPT_TYPE_INT,    {.i64=SM_SQUARE_BRACKETS}, 0, 2, FLAGS, "format" },
+    {   "square_brackets",  "[speaker] text",                 0,                    AV_OPT_TYPE_CONST,  {.i64=SM_SQUARE_BRACKETS}, 0, 0, FLAGS, "format" },
+    {   "round_brackets",   "(speaker) text",                 0,                    AV_OPT_TYPE_CONST,  {.i64=SM_ROUND_BRACKETS},  0, 0, FLAGS, "format" },
+    {   "colon",            "speaker: text",                  0,                    AV_OPT_TYPE_CONST,  {.i64=SM_COLON},           0, 0, FLAGS, "format" },
+    {   "plain",            "speaker text",                   0,                    AV_OPT_TYPE_CONST,  {.i64=SM_PLAIN},           0, 0, FLAGS, "format" },
+    { "line_break",         "insert line break",              OFFSET(line_break),   AV_OPT_TYPE_BOOL,   {.i64=0},                  0, 1, FLAGS, NULL     },
+    { "style",              "ass type name or style code",    OFFSET(style),        AV_OPT_TYPE_STRING, {.str = NULL},             0, 0, FLAGS, NULL     },
+    { NULL },
+};
+
+AVFILTER_DEFINE_CLASS(textmod);
+AVFILTER_DEFINE_CLASS(censor);
+AVFILTER_DEFINE_CLASS(showspeaker);
+
+static const AVFilterPad inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .filter_frame = filter_frame,
+    },
+};
+
+static const AVFilterPad outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_SUBTITLE,
+        .config_props  = config_output,
+    },
+};
+
+const AVFilter ff_sf_textmod = {
+    .name          = "textmod",
+    .description   = NULL_IF_CONFIG_SMALL("Modify subtitle text in several ways"),
+    .init          = init,
+    .uninit        = uninit,
+    .priv_size     = sizeof(TextModContext),
+    .priv_class    = &textmod_class,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_SINGLE_SUBFMT(AV_SUBTITLE_FMT_ASS),
+};
+
+const AVFilter ff_sf_censor = {
+    .name          = "censor",
+    .description   = NULL_IF_CONFIG_SMALL("Censor words in subtitle text"),
+    .init          = init_censor,
+    .uninit        = uninit,
+    .priv_size     = sizeof(TextModContext),
+    .priv_class    = &censor_class,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_SINGLE_SUBFMT(AV_SUBTITLE_FMT_ASS),
+};
+
+const AVFilter ff_sf_showspeaker = {
+    .name          = "showspeaker",
+    .description   = NULL_IF_CONFIG_SMALL("Prepend speaker names to text subtitles (when available)"),
+    .init          = init_showspeaker,
+    .uninit        = uninit,
+    .priv_size     = sizeof(TextModContext),
+    .priv_class    = &showspeaker_class,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_SINGLE_SUBFMT(AV_SUBTITLE_FMT_ASS),
+};
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v4 16/23] avfilter/stripstyles: Add stripstyles filter
  2022-05-28 13:25     ` [FFmpeg-devel] [PATCH v4 00/23] Subtitle Filtering 2022 ffmpegagent
                         ` (14 preceding siblings ...)
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 15/23] avfilter/textmod: Add textmod, censor and show_speaker filters softworkz
@ 2022-05-28 13:25       ` softworkz
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 17/23] avfilter/splitcc: Add splitcc filter for closed caption handling softworkz
                         ` (7 subsequent siblings)
  23 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-05-28 13:25 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

- stripstyles {S -> S)
  Remove all inline styles from subtitle events

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 doc/filters.texi             |  37 ++++++
 libavfilter/Makefile         |   1 +
 libavfilter/allfilters.c     |   1 +
 libavfilter/sf_stripstyles.c | 211 +++++++++++++++++++++++++++++++++++
 4 files changed, 250 insertions(+)
 create mode 100644 libavfilter/sf_stripstyles.c

diff --git a/doc/filters.texi b/doc/filters.texi
index e0ceba80e2..504001a4e7 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -26947,6 +26947,43 @@ ffmpeg -i "http://streams.videolan.org/samples/sub/SSA/subtitle_testing_complex.
 @end example
 @end itemize
 
+@section stripstyles
+
+Remove all inline styles from subtitle events.
+
+Inputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+@item remove_animated
+Also remove text which is subject to animation (default: true)
+Usually, animated text elements are used used in addition to static subtitle lines for creating effects, so in most cases it is safe to remove the animation content.
+If subtitle text is missing, try setting this to false.
+
+@item select_layer
+Process only ASS subtitle events from a specific layer. This allows to filter out certain effects where an ASS author duplicates the text onto multiple layers.
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Remove styles and animations from ASS subtitles and output events from ass layer 0 only. Then convert asn save as SRT stream:
+@example
+ffmpeg -i "https://streams.videolan.org/samples/sub/SSA/subtitle_testing_complex.mkv" -filter_complex "[0:1]stripstyles=select_layer=0" -map 0 -c:s srt output.mkv
+@end example
+@end itemize
+
 
 @section textmod
 
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 6d2a75128c..4290903897 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -580,6 +580,7 @@ OBJS-$(CONFIG_NULLSINK_FILTER)               += vsink_nullsink.o
 OBJS-$(CONFIG_CENSOR_FILTER)                 += sf_textmod.o
 OBJS-$(CONFIG_SHOW_SPEAKER_FILTER)           += sf_textmod.o
 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 960726ba08..94ae8c0bee 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -561,6 +561,7 @@ extern const AVFilter ff_avf_showwavespic;
 extern const AVFilter ff_vaf_spectrumsynth;
 extern const AVFilter ff_sf_censor;
 extern const AVFilter ff_sf_showspeaker;
+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..bc3c5d1441
--- /dev/null
+++ b/libavfilter/sf_stripstyles.c
@@ -0,0 +1,211 @@
+/*
+ * 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 "libavutil/opt.h"
+#include "internal.h"
+#include "libavutil/ass_split_internal.h"
+#include "libavutil/bprint.h"
+
+typedef struct StripStylesContext {
+    const AVClass *class;
+    enum AVSubtitleType format;
+    int remove_animated;
+    int select_layer;
+} StripStylesContext;
+
+typedef struct DialogContext {
+    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;
+
+    av_log(s->ss_ctx, AV_LOG_DEBUG, "dialog_text_cb: %s\n", text);
+
+    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 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)
+{
+    DialogContext dlg_ctx = { .ss_ctx = s };
+    ASSDialog *dialog = avpriv_ass_split_dialog(NULL, ass_line);
+    char *result = NULL;
+
+    if (!dialog)
+        return NULL;
+
+    if (s->select_layer >= 0 && dialog->layer != s->select_layer) {
+        avpriv_ass_free_dialog(&dialog);
+        return NULL;
+    }
+
+    dlg_ctx.ss_ctx = s;
+
+    av_bprint_init(&dlg_ctx.buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
+
+    avpriv_ass_split_override_codes(&dialog_callbacks, &dlg_ctx, dialog->text);
+
+    if (av_bprint_is_complete(&dlg_ctx.buffer)
+        && dlg_ctx.buffer.len > 0)
+        result = ass_get_line(dialog->readorder, dialog->layer, dialog->style, dialog->name, dialog->effect, dlg_ctx.buffer.str);
+
+    av_bprint_finalize(&dlg_ctx.buffer, NULL);
+    avpriv_ass_free_dialog(&dialog);
+
+    return result;
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    AVFilterLink *inlink = outlink->src->inputs[0];
+
+    outlink->w = inlink->w;
+    outlink->h = inlink->h;
+    outlink->time_base = inlink->time_base;
+    outlink->frame_rate = inlink->frame_rate;
+
+    return 0;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    StripStylesContext *s = inlink->dst->priv;
+    AVFilterLink *outlink = inlink->dst->outputs[0];
+    int ret;
+
+    outlink->format = inlink->format;
+
+    ret = av_frame_make_writable(frame);
+    if (ret <0 ) {
+        av_frame_free(&frame);
+        return AVERROR(ENOMEM);
+    }
+
+    for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+
+        AVSubtitleArea *area = frame->subtitle_areas[i];
+
+        if (area->ass) {
+            char *tmp = area->ass;
+            area->ass = process_dialog(s, area->ass);
+
+            if (area->ass) {
+                av_log(inlink->dst, AV_LOG_INFO, "original: %d %s\n", i, tmp);
+                av_log(inlink->dst, AV_LOG_INFO, "stripped: %d %s\n", i, area->ass);
+            }
+            else
+                area->ass = NULL;
+
+            av_free(tmp);
+        }
+    }
+
+    return ff_filter_frame(outlink, frame);
+}
+
+#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 },
+    { "select_layer", "process a specific ass layer only",   OFFSET(remove_animated),  AV_OPT_TYPE_INT, {.i64 = -1 }, -1, INT_MAX, 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,
+        .config_props  = config_output,
+    },
+};
+
+const AVFilter ff_sf_stripstyles = {
+    .name          = "stripstyles",
+    .description   = NULL_IF_CONFIG_SMALL("Strip subtitle inline styles"),
+    .priv_size     = sizeof(StripStylesContext),
+    .priv_class    = &stripstyles_class,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_SINGLE_SUBFMT(AV_SUBTITLE_FMT_ASS),
+};
+
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v4 17/23] avfilter/splitcc: Add splitcc filter for closed caption handling
  2022-05-28 13:25     ` [FFmpeg-devel] [PATCH v4 00/23] Subtitle Filtering 2022 ffmpegagent
                         ` (15 preceding siblings ...)
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 16/23] avfilter/stripstyles: Add stripstyles filter softworkz
@ 2022-05-28 13:25       ` softworkz
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 18/23] avfilter/graphicsub2text: Add new graphicsub2text filter (OCR) softworkz
                         ` (6 subsequent siblings)
  23 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-05-28 13:25 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

- splitcc {V -> VS)
  Extract closed-caption (A53) data from video
  frames as subtitle Frames

ffmpeg -y -loglevel verbose -i "https://streams.videolan.org/streams
/ts/CC/NewsStream-608-ac3.ts" -filter_complex "[0:v]splitcc[vid1],
textmod=mode=remove_chars:find='@',[vid1]overlay_textsubs" output.mkv

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 configure                |   1 +
 doc/filters.texi         |  63 +++++++
 libavfilter/Makefile     |   1 +
 libavfilter/allfilters.c |   1 +
 libavfilter/sf_splitcc.c | 395 +++++++++++++++++++++++++++++++++++++++
 5 files changed, 461 insertions(+)
 create mode 100644 libavfilter/sf_splitcc.c

diff --git a/configure b/configure
index 39c4e94a1f..a0c64500aa 100755
--- a/configure
+++ b/configure
@@ -3727,6 +3727,7 @@ spp_filter_select="fft idctdsp fdctdsp me_cmp pixblockdsp"
 sr_filter_deps="avformat swscale"
 sr_filter_select="dnn"
 stereo3d_filter_deps="gpl"
+splitcc_filter_deps="avcodec"
 subtitles_filter_deps="avformat avcodec libass"
 super2xsai_filter_deps="gpl"
 pixfmts_super2xsai_test_deps="super2xsai_filter"
diff --git a/doc/filters.texi b/doc/filters.texi
index 504001a4e7..6662d09a82 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -27297,6 +27297,69 @@ ffmpeg -i INPUT -filter_complex "showspeaker=format=colon:style='@{\\c&HDD0000&\
 @end example
 @end itemize
 
+
+@section splitcc
+
+Split-out closed-caption/A53 subtitles from video frame side data.
+
+This filter provides an input and an output for video frames, which are just passed through without modification.
+The second out provides subtitle frames which are extracted from video frame side data.
+
+Inputs:
+@itemize
+@item 0: Video [ALL]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Video (same as input)
+@item 1: Subtitles [TEXT]
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+
+@item use_cc_styles
+Emit closed caption style header.
+This will make closed captions appear in white font with a black rectangle background.
+
+@item real_time
+Emit subtitle events as they are decoded for real-time display.
+
+@item real_time_latency_msec
+Minimum elapsed time between emitting real-time subtitle events.
+Only applies to real_time mode.
+
+@item data_field
+Select data field. Possible values:
+
+@table @samp
+@item auto
+Pick first one that appears.
+@item first
+@item second
+@end table
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Extract closed captions as text subtitle stream and overlay it onto the video in cc style (black bar background):
+@example
+ffmpeg -i "https://streams.videolan.org/streams/ts/CC/NewsStream-608-ac3.ts" -filter_complex  "[0:v:0]splitcc=use_cc_styles=1[vid1][sub1];[vid1][sub1]overlaytextsubs" output.mkv
+@end example
+
+@item
+A nicer variant, using realtime output from cc_dec and rendering it with the render_latest_only parameter from overlaytextsubs to avoid ghosting by timely overlap.
+@example
+ffmpeg -i "https://streams.videolan.org/streams/ts/CC/NewsStream-608-ac3.ts" -filter_complex  "[0:v:0]splitcc=real_time=1:real_time_latency_msec=200[vid1][sub1];[vid1][sub1]overlaytextsubs=render_latest_only=1" output.mkv
+@end example
+@end itemize
+
+
 @section textsub2video
 
 Converts text subtitles to video frames.
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 4290903897..988c2fa692 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -580,6 +580,7 @@ OBJS-$(CONFIG_NULLSINK_FILTER)               += vsink_nullsink.o
 OBJS-$(CONFIG_CENSOR_FILTER)                 += sf_textmod.o
 OBJS-$(CONFIG_SHOW_SPEAKER_FILTER)           += sf_textmod.o
 OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
+OBJS-$(CONFIG_SPLITCC_FILTER)                += sf_splitcc.o
 OBJS-$(CONFIG_STRIPSTYLES_FILTER)            += sf_stripstyles.o
 
 # multimedia filters
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 94ae8c0bee..ee9d758d86 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -561,6 +561,7 @@ extern const AVFilter ff_avf_showwavespic;
 extern const AVFilter ff_vaf_spectrumsynth;
 extern const AVFilter ff_sf_censor;
 extern const AVFilter ff_sf_showspeaker;
+extern const AVFilter ff_sf_splitcc;
 extern const AVFilter ff_sf_stripstyles;
 extern const AVFilter ff_sf_textmod;
 extern const AVFilter ff_svf_graphicsub2video;
diff --git a/libavfilter/sf_splitcc.c b/libavfilter/sf_splitcc.c
new file mode 100644
index 0000000000..14235e822c
--- /dev/null
+++ b/libavfilter/sf_splitcc.c
@@ -0,0 +1,395 @@
+/*
+ * 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
+ * subtitle filter for splitting out closed-caption/A53 subtitles from video frame side data
+ */
+
+#include "filters.h"
+#include "libavutil/opt.h"
+#include "subtitles.h"
+#include "libavcodec/avcodec.h"
+
+static const AVRational ms_tb = {1, 1000};
+
+typedef struct SplitCaptionsContext {
+    const AVClass *class;
+    enum AVSubtitleType format;
+    AVCodecContext *cc_dec;
+    int eof;
+    AVFrame *next_sub_frame;
+    AVFrame *empty_sub_frame;
+    int new_frame;
+    int64_t next_repetition_pts;
+    int had_keyframe;
+    AVBufferRef *subtitle_header;
+    int use_cc_styles;
+    int real_time;
+    int real_time_latency_msec;
+    int data_field;
+    int scatter_realtime_output;
+} SplitCaptionsContext;
+
+static int64_t ms_to_avtb(int64_t ms)
+{
+    return av_rescale_q(ms, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q);
+}
+
+static int init(AVFilterContext *ctx)
+{
+    SplitCaptionsContext *s = ctx->priv;
+    AVDictionary *options = NULL;
+
+    int ret;
+    const AVCodec *codec = avcodec_find_decoder(AV_CODEC_ID_EIA_608);
+    if (!codec) {
+        av_log(ctx, AV_LOG_ERROR, "failed to find EIA-608/708 decoder\n");
+        return AVERROR_DECODER_NOT_FOUND;
+    }
+
+    if (!((s->cc_dec = avcodec_alloc_context3(codec)))) {
+        av_log(ctx, AV_LOG_ERROR, "failed to allocate EIA-608/708 decoder\n");
+        return AVERROR(ENOMEM);
+    }
+
+    av_dict_set_int(&options, "real_time", s->real_time, 0);
+    av_dict_set_int(&options, "real_time_latency_msec", s->real_time_latency_msec, 0);
+    av_dict_set_int(&options, "data_field", s->data_field, 0);
+
+    if ((ret = avcodec_open2(s->cc_dec, codec, &options)) < 0) {
+        av_log(ctx, AV_LOG_ERROR, "failed to open EIA-608/708 decoder: %i\n", ret);
+        return ret;
+    }
+
+    if (s->use_cc_styles && s->cc_dec->subtitle_header && s->cc_dec->subtitle_header[0] != 0) {
+        char* subtitle_header =  av_strdup((char *)s->cc_dec->subtitle_header);
+        if (!subtitle_header)
+            return AVERROR(ENOMEM);
+        s->subtitle_header = av_buffer_create((uint8_t *)subtitle_header, strlen(subtitle_header) + 1, NULL, NULL, 0);
+        if (!s->subtitle_header) {
+            av_free(subtitle_header);
+            return AVERROR(ENOMEM);
+        }
+    }
+
+    return 0;
+}
+
+static void uninit(AVFilterContext *ctx)
+{
+    SplitCaptionsContext *s = ctx->priv;
+    av_frame_free(&s->next_sub_frame);
+    av_frame_free(&s->empty_sub_frame);
+    av_buffer_unref(&s->subtitle_header);
+}
+
+static int config_input(AVFilterLink *link)
+{
+    const SplitCaptionsContext *context = link->dst->priv;
+
+    if (context->cc_dec)
+        context->cc_dec->pkt_timebase = link->time_base;
+
+    return 0;
+}
+
+static int query_formats(AVFilterContext *ctx)
+{
+    AVFilterFormats *formats;
+    AVFilterLink *inlink0 = ctx->inputs[0];
+    AVFilterLink *outlink0 = ctx->outputs[0];
+    AVFilterLink *outlink1 = ctx->outputs[1];
+    static const enum AVSubtitleType subtitle_fmts[] = { AV_SUBTITLE_FMT_ASS, AV_SUBTITLE_FMT_NONE };
+    int ret;
+
+    /* set input0 video formats */
+    formats = ff_all_formats(AVMEDIA_TYPE_VIDEO);
+    if ((ret = ff_formats_ref(formats, &inlink0->outcfg.formats)) < 0)
+        return ret;
+
+    /* set output0 video formats */
+    if ((ret = ff_formats_ref(formats, &outlink0->incfg.formats)) < 0)
+        return ret;
+
+    /* set output1 subtitle formats */
+    formats = ff_make_format_list(subtitle_fmts);
+    if ((ret = ff_formats_ref(formats, &outlink1->incfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int config_video_output(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    const AVFilterLink *inlink = outlink->src->inputs[0];
+
+    outlink->w = ctx->inputs[0]->w;
+    outlink->h = ctx->inputs[0]->h;
+    outlink->frame_rate = ctx->inputs[0]->frame_rate;
+    outlink->time_base = ctx->inputs[0]->time_base;
+    outlink->sample_aspect_ratio = ctx->inputs[0]->sample_aspect_ratio;
+
+    if (inlink->hw_frames_ctx)
+        outlink->hw_frames_ctx = av_buffer_ref(inlink->hw_frames_ctx);
+
+    return 0;
+}
+
+static int config_sub_output(AVFilterLink *outlink)
+{
+    SplitCaptionsContext *s = outlink->src->priv;
+    const AVFilterLink *inlink = outlink->src->inputs[0];
+
+    outlink->time_base = inlink->time_base;
+    outlink->format = AV_SUBTITLE_FMT_ASS;
+    outlink->frame_rate = (AVRational){1000, s->real_time_latency_msec};
+
+    return 0;
+}
+
+static int request_sub_frame(AVFilterLink *outlink)
+{
+    SplitCaptionsContext *s = outlink->src->priv;
+    int status;
+    int64_t pts;
+
+    if (!s->empty_sub_frame) {
+        s->empty_sub_frame = ff_get_subtitles_buffer(outlink, outlink->format);
+        if (!s->empty_sub_frame)
+            return AVERROR(ENOMEM);
+    }
+
+    if (!s->eof && ff_inlink_acknowledge_status(outlink->src->inputs[0], &status, &pts)) {
+        if (status == AVERROR_EOF)
+            s->eof = 1;
+    }
+
+    if (s->eof)
+        return AVERROR_EOF;
+
+    if (s->next_sub_frame) {
+
+        AVFrame *out = NULL;
+        s->next_sub_frame->pts++;
+
+        if (s->new_frame)
+            out = av_frame_clone(s->next_sub_frame);
+        else if (s->empty_sub_frame) {
+            s->empty_sub_frame->pts = s->next_sub_frame->pts;
+            out = av_frame_clone(s->empty_sub_frame);
+            av_frame_copy_props(out, s->next_sub_frame);
+            out->repeat_sub = 1;
+        }
+
+        if (!out)
+            return AVERROR(ENOMEM);
+
+        out->subtitle_timing.start_pts = av_rescale_q(s->next_sub_frame->pts, outlink->time_base, AV_TIME_BASE_Q);
+        s->new_frame = 0;
+
+        return ff_filter_frame(outlink, out);
+    }
+
+    return 0;
+}
+
+static int decode(AVCodecContext *avctx, AVFrame *frame, int *got_frame, AVPacket *pkt)
+{
+    int ret;
+
+    *got_frame = 0;
+
+    if (pkt) {
+        ret = avcodec_send_packet(avctx, pkt);
+        // In particular, we don't expect AVERROR(EAGAIN), because we read all
+        // decoded frames with avcodec_receive_frame() until done.
+        if (ret < 0 && ret != AVERROR_EOF)
+            return ret;
+    }
+
+    ret = avcodec_receive_frame(avctx, frame);
+    if (ret < 0 && ret != AVERROR(EAGAIN))
+        return ret;
+    if (ret >= 0)
+        *got_frame = 1;
+
+    return 0;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFrameSideData *sd;
+    SplitCaptionsContext *s = inlink->dst->priv;
+    AVFilterLink *outlink0 = inlink->dst->outputs[0];
+    AVFilterLink *outlink1 = inlink->dst->outputs[1];
+    AVPacket *pkt = NULL;
+    AVFrame *sub_out = NULL;
+
+    int ret;
+
+    outlink0->format = inlink->format;
+
+    sd = av_frame_get_side_data(frame, AV_FRAME_DATA_A53_CC);
+
+    if (sd && (s->had_keyframe || frame->key_frame)) {
+        int got_output = 0;
+
+        s->had_keyframe = 1;
+        pkt = av_packet_alloc();
+        pkt->buf = av_buffer_ref(sd->buf);
+        if (!pkt->buf) {
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
+
+        pkt->data = pkt->buf->data;
+        pkt->size = pkt->buf->size;
+        pkt->pts  = av_rescale_q(frame->pts, inlink->time_base, AV_TIME_BASE_Q);
+
+        sub_out = ff_get_subtitles_buffer(outlink1, AV_SUBTITLE_FMT_ASS);
+        if (!sub_out) {
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
+
+        if ((ret = av_buffer_replace(&sub_out->subtitle_header, s->subtitle_header)) < 0)
+            goto fail;
+
+        ret = decode(s->cc_dec, sub_out, &got_output, pkt);
+
+        ////if (got_output) {
+        ////    av_log(inlink->dst, AV_LOG_INFO, "CC Packet PTS: %"PRId64" got_output: %d  out_frame_pts: %"PRId64"  out_sub_pts: %"PRId64"\n", pkt->pts, got_output, sub_out->pts, sub_out->subtitle_pts);
+        ////}
+
+        av_packet_free(&pkt);
+
+        if (ret < 0) {
+            av_log(inlink->dst, AV_LOG_ERROR, "Decode error: %d \n", ret);
+            goto fail;
+        }
+
+        if (got_output) {
+            sub_out->pts = frame->pts;
+            av_frame_free(&s->next_sub_frame);
+            s->next_sub_frame = sub_out;
+            sub_out = NULL;
+            s->new_frame = 1;
+            s->next_sub_frame->pts = frame->pts;
+
+            if ((ret = av_buffer_replace(&s->next_sub_frame->subtitle_header, s->subtitle_header)) < 0)
+                goto fail;
+
+            if (s->real_time && s->scatter_realtime_output) {
+                if (s->next_repetition_pts)
+                    s->next_sub_frame->pts = s->next_repetition_pts;
+
+                s->next_sub_frame->subtitle_timing.duration = ms_to_avtb(s->real_time_latency_msec);
+                s->next_repetition_pts = s->next_sub_frame->pts + av_rescale_q(s->real_time_latency_msec, ms_tb, inlink->time_base);
+            }
+
+            ret = request_sub_frame(outlink1);
+            if (ret < 0)
+                goto fail;
+        }
+    }
+
+    if (s->real_time && s->scatter_realtime_output && !s->new_frame && s->next_repetition_pts > 0 && frame->pts > s->next_repetition_pts) {
+        s->new_frame = 1;
+        s->next_sub_frame->pts = s->next_repetition_pts;
+        s->next_repetition_pts = s->next_sub_frame->pts + av_rescale_q(s->real_time_latency_msec, ms_tb, inlink->time_base);
+    }
+
+    if (!s->next_sub_frame) {
+        s->next_sub_frame = ff_get_subtitles_buffer(outlink1, outlink1->format);
+        if (!s->next_sub_frame) {
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
+
+        s->next_sub_frame->subtitle_timing.duration = ms_to_avtb(s->real_time_latency_msec);
+        s->next_sub_frame->pts = frame->pts;
+        s->new_frame = 1;
+
+        if ((ret = av_buffer_replace(&s->next_sub_frame->subtitle_header, s->subtitle_header)) < 0)
+            goto fail;
+    }
+
+    ret = ff_filter_frame(outlink0, frame);
+
+fail:
+    av_packet_free(&pkt);
+    av_frame_free(&sub_out);
+    return ret;
+}
+
+#define OFFSET(x) offsetof(SplitCaptionsContext, x)
+#define FLAGS (AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption split_cc_options[] = {
+    { "use_cc_styles",    "Emit closed caption style header", OFFSET(use_cc_styles),  AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, NULL },
+    { "real_time", "emit subtitle events as they are decoded for real-time display", OFFSET(real_time), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS },
+    { "real_time_latency_msec", "minimum elapsed time between emitting real-time subtitle events", OFFSET(real_time_latency_msec), AV_OPT_TYPE_INT, { .i64 = 200 }, 0, 500, FLAGS },
+    { "scatter_realtime_output", "scatter output events to a duration of real_time_latency_msec", OFFSET(scatter_realtime_output), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS },
+    { "data_field", "select data field", OFFSET(data_field), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1, FLAGS, "data_field" },
+    { "auto",   "pick first one that appears", 0, AV_OPT_TYPE_CONST, { .i64 =-1 }, 0, 0, FLAGS, "data_field" },
+    { "first",  NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, FLAGS, "data_field" },
+    { "second", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, FLAGS, "data_field" },
+    { NULL },
+};
+
+AVFILTER_DEFINE_CLASS(split_cc);
+
+static const AVFilterPad inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_VIDEO,
+        .filter_frame = filter_frame,
+        .config_props = config_input,
+    },
+};
+
+static const AVFilterPad outputs[] = {
+    {
+        .name          = "video_passthrough",
+        .type          = AVMEDIA_TYPE_VIDEO,
+        .config_props  = config_video_output,
+    },
+    {
+        .name          = "subtitles",
+        .type          = AVMEDIA_TYPE_SUBTITLE,
+        .request_frame = request_sub_frame,
+        .config_props  = config_sub_output,
+    },
+};
+
+const AVFilter ff_sf_splitcc = {
+    .name           = "splitcc",
+    .description    = NULL_IF_CONFIG_SMALL("Extract closed-caption (A53) data from video as subtitle stream."),
+    .init           = init,
+    .uninit         = uninit,
+    .priv_size      = sizeof(SplitCaptionsContext),
+    .priv_class     = &split_cc_class,
+    .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_QUERY_FUNC(query_formats),
+};
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v4 18/23] avfilter/graphicsub2text: Add new graphicsub2text filter (OCR)
  2022-05-28 13:25     ` [FFmpeg-devel] [PATCH v4 00/23] Subtitle Filtering 2022 ffmpegagent
                         ` (16 preceding siblings ...)
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 17/23] avfilter/splitcc: Add splitcc filter for closed caption handling softworkz
@ 2022-05-28 13:25       ` softworkz
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 19/23] avfilter/subscale: Add filter for scaling and/or re-arranging graphical subtitles softworkz
                         ` (5 subsequent siblings)
  23 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-05-28 13:25 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 configure                        |    1 +
 doc/filters.texi                 |   55 ++
 libavfilter/Makefile             |    1 +
 libavfilter/allfilters.c         |    1 +
 libavfilter/sf_graphicsub2text.c | 1132 ++++++++++++++++++++++++++++++
 5 files changed, 1190 insertions(+)
 create mode 100644 libavfilter/sf_graphicsub2text.c

diff --git a/configure b/configure
index a0c64500aa..e9e1fc0966 100755
--- a/configure
+++ b/configure
@@ -3662,6 +3662,7 @@ frei0r_filter_deps="frei0r"
 frei0r_src_filter_deps="frei0r"
 fspp_filter_deps="gpl"
 gblur_vulkan_filter_deps="vulkan spirv_compiler"
+graphicsub2text_filter_deps="libtesseract"
 hflip_vulkan_filter_deps="vulkan spirv_compiler"
 histeq_filter_deps="gpl"
 hqdn3d_filter_deps="gpl"
diff --git a/doc/filters.texi b/doc/filters.texi
index 6662d09a82..8b823d4e12 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -27063,6 +27063,61 @@ ffmpeg -i "https://streams.videolan.org/ffmpeg/mkv_subtitles.mkv" -filter_comple
 @end example
 @end itemize
 
+@section graphicsub2text
+
+Converts graphic subtitles to text subtitles by performing OCR.
+
+For this filter to be available, ffmpeg needs to be compiled with libtesseract (see https://github.com/tesseract-ocr/tesseract).
+Language models need to be downloaded from https://github.com/tesseract-ocr/tessdata and put into as subfolder named 'tessdata' or into a folder specified via the environment variable 'TESSDATA_PREFIX'.
+The path can also be specified via filter option (see below).
+
+Note: These models are including the data for both OCR modes.
+
+Inputs:
+- 0: Subtitles [bitmap]
+
+Outputs:
+- 0: Subtitles [text]
+
+It accepts the following parameters:
+
+@table @option
+@item ocr_mode
+The character recognition mode to use.
+
+Supported OCR modes are:
+
+@table @var
+@item 0, tesseract
+This is the classic libtesseract operation mode. It is fast but less accurate than LSTM.
+@item 1, lstm
+Newer OCR implementation based on ML models. Provides usually better results, requires more processing resources.
+@item 2, both
+Use a combination of both modes.
+@end table
+
+@item tessdata_path
+The path to a folder containing the language models to be used.
+
+@item language
+The recognition language. It needs to match the first three characters of a  language model file in the tessdata path.
+
+@end table
+
+
+@subsection Examples
+
+@itemize
+@item
+Convert DVB graphic subtitles to ASS (text) subtitles
+
+Note: For this to work, you need to have the data file 'eng.traineddata' in a 'tessdata' subfolder (see above).
+@example
+ffmpeg -loglevel verbose -i "https://streams.videolan.org/streams/ts/video_subs_ttxt%2Bdvbsub.ts" -filter_complex "[0:13]graphicsub2text=delay_when_no_duration=1" -c:s ass -y output.mkv
+@end example
+@end itemize
+
+
 @section graphicsub2video
 
 Renders graphic subtitles as video frames.
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 988c2fa692..66a3b73e33 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -306,6 +306,7 @@ OBJS-$(CONFIG_GBLUR_FILTER)                  += vf_gblur.o
 OBJS-$(CONFIG_GBLUR_VULKAN_FILTER)           += vf_gblur_vulkan.o vulkan.o vulkan_filter.o
 OBJS-$(CONFIG_GEQ_FILTER)                    += vf_geq.o
 OBJS-$(CONFIG_GRADFUN_FILTER)                += vf_gradfun.o
+OBJS-$(CONFIG_GRAPHICSUB2TEXT_FILTER)        += sf_graphicsub2text.o
 OBJS-$(CONFIG_GRAPHICSUB2VIDEO_FILTER)       += vf_overlaygraphicsubs.o framesync.o
 OBJS-$(CONFIG_GRAPHMONITOR_FILTER)           += f_graphmonitor.o
 OBJS-$(CONFIG_GRAYWORLD_FILTER)              += vf_grayworld.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index ee9d758d86..46d38ef2cd 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -560,6 +560,7 @@ extern const AVFilter ff_avf_showwaves;
 extern const AVFilter ff_avf_showwavespic;
 extern const AVFilter ff_vaf_spectrumsynth;
 extern const AVFilter ff_sf_censor;
+extern const AVFilter ff_sf_graphicsub2text;
 extern const AVFilter ff_sf_showspeaker;
 extern const AVFilter ff_sf_splitcc;
 extern const AVFilter ff_sf_stripstyles;
diff --git a/libavfilter/sf_graphicsub2text.c b/libavfilter/sf_graphicsub2text.c
new file mode 100644
index 0000000000..9b413d314e
--- /dev/null
+++ b/libavfilter/sf_graphicsub2text.c
@@ -0,0 +1,1132 @@
+/*
+ * 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
+ * subtitle filter to convert graphical subs to text subs via OCR
+ */
+
+#include <tesseract/capi.h>
+#include <libavutil/ass_internal.h>
+
+#include "drawutils.h"
+#include "libavutil/opt.h"
+#include "subtitles.h"
+
+#include "libavcodec/elbg.h"
+
+enum {
+    RFLAGS_NONE         = 0,
+    RFLAGS_HALIGN       = 1 << 0,
+    RFLAGS_VALIGN       = 1 << 1,
+    RFLAGS_FBOLD        = 1 << 2,
+    RFLAGS_FITALIC      = 1 << 3,
+    RFLAGS_FUNDERLINE   = 1 << 4,
+    RFLAGS_FONT         = 1 << 5,
+    RFLAGS_FONTSIZE     = 1 << 6,
+    RFLAGS_COLOR        = 1 << 7,
+    RFLAGS_OUTLINECOLOR = 1 << 8,
+    RFLAGS_ALL = RFLAGS_HALIGN | RFLAGS_VALIGN | RFLAGS_FBOLD | RFLAGS_FITALIC | RFLAGS_FUNDERLINE |
+                RFLAGS_FONT | RFLAGS_FONTSIZE | RFLAGS_COLOR | RFLAGS_OUTLINECOLOR,
+};
+
+typedef struct SubOcrContext {
+    const AVClass *class;
+    int w, h;
+
+    TessBaseAPI *tapi;
+    TessOcrEngineMode ocr_mode;
+    char *tessdata_path;
+    char *language;
+    int preprocess_images;
+    int dump_bitmaps;
+    int delay_when_no_duration;
+    int recognize;
+    double font_size_factor;
+
+    int readorder_counter;
+
+    AVFrame *pending_frame;
+    AVBufferRef *subtitle_header;
+    AVBPrint buffer;
+
+    // Color Quantization Fields
+    struct ELBGContext *ctx;
+    AVLFG lfg;
+    int *codeword;
+    int *codeword_closest_codebook_idxs;
+    int *codebook;
+    int r_idx, g_idx, b_idx, a_idx;
+    int64_t last_subtitle_pts;
+} SubOcrContext;
+
+typedef struct OcrImageProps {
+    int background_color_index;
+    int fill_color_index;
+
+} OcrImageProps;
+
+static int64_t ms_to_avtb(int64_t ms)
+{
+    return av_rescale_q(ms, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q);
+}
+
+static int create_ass_header(AVFilterContext* ctx)
+{
+    SubOcrContext* s = ctx->priv;
+
+    if (!(s->w && s->h)) {
+        av_log(ctx, AV_LOG_WARNING, "create_ass_header: no width and height specified!\n");
+        s->w = ASS_DEFAULT_PLAYRESX;
+        s->h = ASS_DEFAULT_PLAYRESY;
+    }
+
+    char* subtitle_header_text = avpriv_ass_get_subtitle_header_full(s->w, s->h, ASS_DEFAULT_FONT, ASS_DEFAULT_FONT_SIZE,
+        ASS_DEFAULT_COLOR, ASS_DEFAULT_COLOR, ASS_DEFAULT_BACK_COLOR, ASS_DEFAULT_BACK_COLOR, ASS_DEFAULT_BOLD,
+        ASS_DEFAULT_ITALIC, ASS_DEFAULT_UNDERLINE, ASS_DEFAULT_BORDERSTYLE, ASS_DEFAULT_ALIGNMENT, 0);
+
+    if (!subtitle_header_text)
+        return AVERROR(ENOMEM);
+
+    s->subtitle_header = av_buffer_create((uint8_t*)subtitle_header_text, strlen(subtitle_header_text) + 1, NULL, NULL, 0);
+
+    if (!s->subtitle_header)
+        return AVERROR(ENOMEM);
+
+    return 0;
+}
+
+static int init(AVFilterContext *ctx)
+{
+    SubOcrContext *s = ctx->priv;
+    const char* tver = TessVersion();
+    uint8_t rgba_map[4];
+    int ret;
+
+    s->tapi = TessBaseAPICreate();
+
+    if (!s->tapi || !tver || !strlen(tver)) {
+        av_log(ctx, AV_LOG_ERROR, "Failed to access libtesseract\n");
+        return AVERROR(ENOSYS);
+    }
+
+    av_log(ctx, AV_LOG_VERBOSE, "Initializing libtesseract, version: %s\n", tver);
+
+    ret = TessBaseAPIInit4(s->tapi, s->tessdata_path, s->language, s->ocr_mode, NULL, 0, NULL, NULL, 0, 1);
+    if (ret < 0 ) {
+        av_log(ctx, AV_LOG_ERROR, "Failed to initialize libtesseract. Error: %d\n", ret);
+        return AVERROR(ENOSYS);
+    }
+
+    ret = TessBaseAPISetVariable(s->tapi, "tessedit_char_blacklist", "|");
+    if (ret < 0 ) {
+        av_log(ctx, AV_LOG_ERROR, "Failed to set 'tessedit_char_blacklist'. Error: %d\n", ret);
+        return AVERROR(EINVAL);
+    }
+
+    av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
+
+    ff_fill_rgba_map(&rgba_map[0], AV_PIX_FMT_RGB32);
+
+    s->r_idx = rgba_map[0]; // R
+    s->g_idx = rgba_map[1]; // G
+    s->b_idx = rgba_map[2]; // B
+    s->a_idx = rgba_map[3]; // A
+
+    av_lfg_init(&s->lfg, 123456789);
+
+    return 0;
+}
+
+static void uninit(AVFilterContext *ctx)
+{
+    SubOcrContext *s = ctx->priv;
+
+    av_buffer_unref(&s->subtitle_header);
+    av_bprint_finalize(&s->buffer, NULL);
+
+    if (s->tapi) {
+        TessBaseAPIEnd(s->tapi);
+        TessBaseAPIDelete(s->tapi);
+    }
+
+    avpriv_elbg_free(&s->ctx);
+}
+
+static int query_formats(AVFilterContext *ctx)
+{
+    AVFilterFormats *formats, *formats2;
+    AVFilterLink *inlink = ctx->inputs[0];
+    AVFilterLink *outlink = ctx->outputs[0];
+    static const enum AVSubtitleType in_fmts[] = { AV_SUBTITLE_FMT_BITMAP, AV_SUBTITLE_FMT_NONE };
+    static const enum AVSubtitleType out_fmts[] = { AV_SUBTITLE_FMT_ASS, AV_SUBTITLE_FMT_NONE };
+    int ret;
+
+    /* set input format */
+    formats = ff_make_format_list(in_fmts);
+    if ((ret = ff_formats_ref(formats, &inlink->outcfg.formats)) < 0)
+        return ret;
+
+    /* set output format */
+    formats2 = ff_make_format_list(out_fmts);
+    if ((ret = ff_formats_ref(formats2, &outlink->incfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int config_input(AVFilterLink *inlink)
+{
+    AVFilterContext *ctx = inlink->dst;
+    SubOcrContext *s = ctx->priv;
+
+    if (s->w <= 0 || s->h <= 0) {
+        s->w = inlink->w;
+        s->h = inlink->h;
+    }
+
+    return create_ass_header(ctx);
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    AVFilterLink *inlink = outlink->src->inputs[0];
+    const AVFilterContext *ctx  = outlink->src;
+    SubOcrContext *s = ctx->priv;
+
+    outlink->format = AV_SUBTITLE_FMT_ASS;
+    outlink->w = s->w;
+    outlink->h = s->h;
+    outlink->time_base = inlink->time_base;
+    outlink->frame_rate = inlink->frame_rate;
+
+    return 0;
+}
+
+static void free_subtitle_area(AVSubtitleArea *area)
+{
+    for (unsigned n = 0; n < FF_ARRAY_ELEMS(area->buf); n++)
+        av_buffer_unref(&area->buf[n]);
+
+    av_freep(&area->text);
+    av_freep(&area->ass);
+    av_free(area);
+
+}
+
+static AVSubtitleArea *copy_subtitle_area(const AVSubtitleArea *src)
+{
+    AVSubtitleArea *dst = av_mallocz(sizeof(AVSubtitleArea));
+
+    if (!dst)
+        return NULL;
+
+    dst->x         =  src->x;
+    dst->y         =  src->y;
+    dst->w         =  src->w;
+    dst->h         =  src->h;
+    dst->nb_colors =  src->nb_colors;
+    dst->type      =  src->type;
+    dst->flags     =  src->flags;
+
+    for (unsigned i = 0; i < AV_NUM_BUFFER_POINTERS; i++) {
+        if (src->h > 0 && src->w > 0 && src->buf[i]) {
+            dst->buf[0] = av_buffer_ref(src->buf[i]);
+            if (!dst->buf[i])
+                return NULL;
+
+            const int ret = av_buffer_make_writable(&dst->buf[i]);
+            if (ret < 0)
+                return NULL;
+
+            dst->linesize[i] = src->linesize[i];
+        }
+    }
+
+    memcpy(&dst->pal[0], &src->pal[0], sizeof(src->pal[0]) * 256);
+
+
+    return dst;
+}
+
+static int quantize_image_colors(SubOcrContext *const s, AVSubtitleArea *subtitle_area)
+{
+    const int num_quantized_colors = 3;
+    int k, ret;
+    const int codeword_length = subtitle_area->w * subtitle_area->h;
+    uint8_t *src_data = subtitle_area->buf[0]->data;
+
+    if (subtitle_area->nb_colors <= num_quantized_colors) {
+        av_log(s, AV_LOG_DEBUG, "No need to quantize colors. Color count: %d\n", subtitle_area->nb_colors);
+        return 0;
+    }
+
+    // Convert palette to grayscale
+    for (int i = 0; i < subtitle_area->nb_colors; i++) {
+        uint8_t *color        = (uint8_t *)&subtitle_area->pal[i];
+        const uint8_t average = (uint8_t)(((int)color[s->r_idx] + color[s->g_idx] + color[s->b_idx]) / 3);
+        color[s->b_idx]       = average;
+        color[s->g_idx]       = average;
+        color[s->r_idx]       = average;
+    }
+
+    /* Re-Initialize */
+    s->codeword = av_realloc_f(s->codeword, codeword_length, 4 * sizeof(*s->codeword));
+    if (!s->codeword)
+        return AVERROR(ENOMEM);
+
+    s->codeword_closest_codebook_idxs = av_realloc_f(s->codeword_closest_codebook_idxs,
+        codeword_length, sizeof(*s->codeword_closest_codebook_idxs));
+    if (!s->codeword_closest_codebook_idxs)
+        return AVERROR(ENOMEM);
+
+    s->codebook = av_realloc_f(s->codebook, num_quantized_colors, 4 * sizeof(*s->codebook));
+    if (!s->codebook)
+        return AVERROR(ENOMEM);
+
+    /* build the codeword */
+    k = 0;
+    for (int i = 0; i < subtitle_area->h; i++) {
+        uint8_t *p = src_data;
+        for (int j = 0; j < subtitle_area->w; j++) {
+            const uint8_t *color = (uint8_t *)&subtitle_area->pal[*p];
+            s->codeword[k++] = color[s->b_idx];
+            s->codeword[k++] = color[s->g_idx];
+            s->codeword[k++] = color[s->r_idx];
+            s->codeword[k++] = color[s->a_idx];
+            p++;
+        }
+        src_data += subtitle_area->linesize[0];
+    }
+
+    /* compute the codebook */
+    ret = avpriv_elbg_do(&s->ctx, s->codeword, 4, codeword_length, s->codebook,
+        num_quantized_colors, 1, s->codeword_closest_codebook_idxs, &s->lfg, 0);
+    if (ret < 0)
+        return ret;
+
+    /* Write Palette */
+    for (int i = 0; i < num_quantized_colors; i++) {
+        subtitle_area->pal[i] = s->codebook[i*4+3] << 24  |
+                    (s->codebook[i*4+2] << 16) |
+                    (s->codebook[i*4+1] <<  8) |
+                    (s->codebook[i*4  ] <<  0);
+    }
+
+
+    av_log(s, AV_LOG_DEBUG, "Quantized colors from %d to %d\n", subtitle_area->nb_colors, num_quantized_colors);
+
+    subtitle_area->nb_colors = num_quantized_colors;
+    src_data = subtitle_area->buf[0]->data;
+
+    /* Write Image */
+    k = 0;
+    for (int i = 0; i < subtitle_area->h; i++) {
+        uint8_t *p = src_data;
+        for (int j = 0; j < subtitle_area->w; j++, p++) {
+            p[0] = (uint8_t)s->codeword_closest_codebook_idxs[k++];
+        }
+
+        src_data += subtitle_area->linesize[0];
+    }
+
+    return ret;
+}
+
+#define MEASURE_LINE_COUNT 6
+
+static uint8_t get_background_color_index(SubOcrContext *const s, const AVSubtitleArea *subtitle_area)
+{
+    const int linesize = subtitle_area->linesize[0];
+    int index_counts[256] = {0};
+    const unsigned int line_offsets[MEASURE_LINE_COUNT] = {
+        0,
+        linesize,
+        2 * linesize,
+        (subtitle_area->h - 3) * linesize,
+        (subtitle_area->h - 2) * linesize,
+        (subtitle_area->h - 1) * linesize
+    };
+
+    const uint8_t *src_data = subtitle_area->buf[0]->data;
+    const uint8_t tl = src_data[0];
+    const uint8_t tr = src_data[subtitle_area->w - 1];
+    const uint8_t bl = src_data[(subtitle_area->h - 1) * linesize + 0];
+    const uint8_t br = src_data[(subtitle_area->h - 1) * linesize + subtitle_area->w - 1];
+    uint8_t max_index = 0;
+    int max_count;
+
+    // When all corner pixels are equal, assume that as background color
+    if (tl == tr == bl == br || subtitle_area->h < 6)
+        return tl;
+
+    for (unsigned int i = 0; i < MEASURE_LINE_COUNT; i++) {
+        uint8_t *p = subtitle_area->buf[0]->data + line_offsets[i];
+        for (int k = 0; k < subtitle_area->w; k++)
+            index_counts[p[k]]++;
+    }
+
+    max_count = index_counts[0];
+
+    for (uint8_t i = 1; i < subtitle_area->nb_colors; i++) {
+        if (index_counts[i] > max_count) {
+            max_count = index_counts[i];
+            max_index = i;
+        }
+    }
+
+    return max_index;
+}
+
+static uint8_t get_text_color_index(SubOcrContext *const s, const AVSubtitleArea *subtitle_area, const uint8_t bg_color_index, uint8_t *outline_color_index)
+{
+    const int linesize = subtitle_area->linesize[0];
+    int index_counts[256] = {0};
+    uint8_t last_index = bg_color_index;
+    int max_count, min_req_count;
+    uint8_t max_index = 0;
+
+    for (int i = 3; i < subtitle_area->h - 3; i += 5) {
+        const uint8_t *p = subtitle_area->buf[0]->data + ((ptrdiff_t)linesize * i);
+        for (int k = 0; k < subtitle_area->w; k++) {
+            const uint8_t cur_index = p[k];
+
+            // When color hasn't changed, continue
+            if (cur_index == last_index)
+                continue;
+
+            if (cur_index != bg_color_index)
+                index_counts[cur_index]++;
+
+            last_index = cur_index;
+        }
+    }
+
+    max_count = index_counts[0];
+
+    for (uint8_t i = 1; i < subtitle_area->nb_colors; i++) {
+        if (index_counts[i] > max_count) {
+            max_count = index_counts[i];
+            max_index = i;
+        }
+    }
+
+    min_req_count = max_count / 3;
+
+    for (uint8_t i = 1; i < subtitle_area->nb_colors; i++) {
+        if (index_counts[i] < min_req_count)
+            index_counts[i] = 0;
+    }
+
+    *outline_color_index = max_index;
+
+    index_counts[max_index] = 0;
+    max_count = 0;
+
+    for (uint8_t i = 0; i < subtitle_area->nb_colors; i++) {
+        if (index_counts[i] > max_count) {
+            max_count = index_counts[i];
+            max_index = i;
+        }
+    }
+
+    if (*outline_color_index == max_index)
+        *outline_color_index = 255;
+
+    return max_index;
+}
+
+static void make_image_binary(SubOcrContext *const s, AVSubtitleArea *subtitle_area, const uint8_t text_color_index)
+{
+    for (int i = 0; i < subtitle_area->nb_colors; i++) {
+
+        if (i != text_color_index)
+            subtitle_area->pal[i] = 0xffffffff;
+        else
+            subtitle_area->pal[i] = 0xff000000;
+    }
+}
+
+static int get_crop_region(SubOcrContext *const s, const AVSubtitleArea *subtitle_area, uint8_t text_color_index, int *x, int *y, int *w, int *h)
+{
+    const int linesize = subtitle_area->linesize[0];
+    int max_y = 0, max_x = 0;
+    int min_y = subtitle_area->h - 1, min_x = subtitle_area->w - 1;
+
+    for (int i = 0; i < subtitle_area->h; i += 3) {
+        const uint8_t *p = subtitle_area->buf[0]->data + ((ptrdiff_t)linesize * i);
+        for (int k = 0; k < subtitle_area->w; k += 2) {
+            if (p[k] == text_color_index) {
+                min_y = FFMIN(min_y, i);
+                min_x = FFMIN(min_x, k);
+                max_y = FFMAX(max_y, i);
+                max_x = FFMAX(max_x, k);
+            }
+        }
+    }
+
+    if (max_y <= min_y || max_x <= min_x) {
+        av_log(s, AV_LOG_WARNING, "Unable to detect crop region\n");
+        *x = 0;
+        *y = 0;
+        *w = subtitle_area->w;
+        *h = subtitle_area->h;
+    }    else {
+        *x = FFMAX(min_x - 10, 0);
+        *y = FFMAX(min_y - 10, 0);
+        *w = FFMIN(max_x + 10 - *x, (subtitle_area->w - *x));
+        *h = FFMIN(max_y + 10 - *y, (subtitle_area->h - *y));
+    }
+
+    return 0;
+}
+
+static int crop_area_bitmap(SubOcrContext *const s, AVSubtitleArea *subtitle_area, int x, int y, int w, int h)
+{
+    const int linesize = subtitle_area->linesize[0];
+    AVBufferRef *dst = av_buffer_allocz(h * w);
+    uint8_t *d;
+
+    if (!dst)
+        return AVERROR(ENOMEM);
+
+    d = dst->data;
+
+    for (int i = y; i < y + h; i++) {
+        const uint8_t *p = subtitle_area->buf[0]->data + ((ptrdiff_t)linesize * i);
+        for (int k = x; k < x + w; k++) {
+            *d = p[k];
+            d++;
+        }
+    }
+
+    subtitle_area->w = w;
+    subtitle_area->h = h;
+    subtitle_area->x += x;
+    subtitle_area->y += y;
+    subtitle_area->linesize[0] = w;
+    av_buffer_replace(&subtitle_area->buf[0], dst);
+
+    av_buffer_unref(&dst);
+    return 0;
+}
+
+#define R 0
+#define G 1
+#define B 2
+#define A 3
+
+static int print_code(AVBPrint *buf, int in_code, const char *fmt, ...)
+{
+    va_list vl;
+
+    if (!in_code)
+        av_bprint_chars(buf, '{', 1);
+
+    va_start(vl, fmt);
+    av_vbprintf(buf, fmt, vl);
+    va_end(vl);
+
+    return 1;
+}
+
+static int end_code(AVBPrint *buf, int in_code)
+{
+    if (in_code)
+        av_bprint_chars(buf, '}', 1);
+    return 0;
+}
+
+static uint8_t* create_grayscale_image(AVFilterContext *ctx, AVSubtitleArea *area, int invert)
+{
+    uint8_t gray_pal[256];
+    const size_t img_size = area->buf[0]->size;
+    const uint8_t* img    = area->buf[0]->data;
+    uint8_t* gs_img       = av_malloc(img_size);
+
+    if (!gs_img)
+        return NULL;
+
+    for (unsigned i = 0; i < 256; i++) {
+        const uint8_t *col = (uint8_t*)&area->pal[i];
+        const int val      = (int)col[3] * FFMAX3(col[0], col[1], col[2]);
+        gray_pal[i]        = (uint8_t)(val >> 8);
+    }
+
+    if (invert)
+        for (unsigned i = 0; i < img_size; i++)
+            gs_img[i]   = 255 - gray_pal[img[i]];
+    else
+        for (unsigned i = 0; i < img_size; i++)
+            gs_img[i]   = gray_pal[img[i]];
+
+    return gs_img;
+}
+
+static uint8_t* create_bitmap_image(AVFilterContext *ctx, AVSubtitleArea *area, const uint8_t text_color_index)
+{
+    const size_t img_size = area->buf[0]->size;
+    const uint8_t* img    = area->buf[0]->data;
+    uint8_t* gs_img       = av_malloc(img_size);
+
+    if (!gs_img)
+        return NULL;
+
+    for (unsigned i = 0; i < img_size; i++) {
+        if (img[i] == text_color_index)
+            gs_img[i]   = 0;
+        else
+            gs_img[i]   = 255;
+    }
+
+    return gs_img;
+}
+
+static void png_save(AVFilterContext *ctx, const char *filename, AVSubtitleArea *area)
+{
+    int x, y;
+    int v;
+    FILE *f;
+    char fname[40];
+    const uint8_t *data = area->buf[0]->data;
+
+    snprintf(fname, sizeof(fname), "%s.ppm", filename);
+
+    f = fopen(fname, "wb");
+    if (!f) {
+        perror(fname);
+        return;
+    }
+    fprintf(f, "P6\n"
+            "%d %d\n"
+            "%d\n",
+            area->w, area->h, 255);
+    for(y = 0; y < area->h; y++) {
+        for(x = 0; x < area->w; x++) {
+            const uint8_t index = data[y * area->linesize[0] + x];
+            v = (int)area->pal[index];
+            putc(v >> 16 & 0xff, f);
+            putc(v >> 8 & 0xff, f);
+            putc(v >> 0 & 0xff, f);
+        }
+    }
+
+    fclose(f);
+}
+
+static int get_max_index(int score[256])
+{
+    int max_val = 0, max_index = 0;
+
+    for (int i = 0; i < 256; i++) {
+        if (score[i] > max_val) {
+            max_val = score[i];
+            max_index = i;
+        }
+    }
+
+    return max_index;
+}
+
+static int get_word_colors(AVFilterContext *ctx, TessResultIterator* ri, const AVSubtitleArea* area, const AVSubtitleArea* original_area,
+                           uint8_t bg_color_index, uint8_t text_color_index, uint8_t outline_color_index,
+                           uint32_t* bg_color, uint32_t* text_color, uint32_t* outline_color)
+{
+    int left = 0, top = 0, right = 0, bottom = 0, ret;
+    int bg_score[256] = {0}, text_score[256] = {0}, outline_score[256] = {0};
+    int max_index;
+
+    ret = TessPageIteratorBoundingBox((TessPageIterator*)ri, RIL_WORD, &left, &top, &right, &bottom);
+    if (ret < 0) {
+        av_log(ctx, AV_LOG_WARNING, "get_word_colors: IteratorBoundingBox failed: %d\n", ret);
+        return  ret;
+    }
+
+    if (left >= area->w || right >= area->w || top >= area->h || bottom >= area->h) {
+        av_log(ctx, AV_LOG_WARNING, "get_word_colors: word bounding box (l: %d, t: %d r: %d, b: %d) out of image bounds (%dx%d)\n", left,top, right, bottom, area->w, area->h);
+        return  AVERROR(EINVAL);
+    }
+
+    for (int y = top; y < bottom; y += 3) {
+        uint8_t *p = area->buf[0]->data + (y * area->linesize[0]) + left;
+        uint8_t *porig = original_area->buf[0]->data + (y * original_area->linesize[0]) + left;
+        uint8_t current_index = 255;
+
+        for (int x = left; x < right; x++, p++, porig++) {
+
+            if (*p == current_index) {
+                if (*p == bg_color_index)
+                    bg_score[*porig]++;
+                if (*p == text_color_index)
+                    text_score[*porig]++;
+                if (*p == outline_color_index)
+                    outline_score[*porig]++;
+            }
+
+            current_index = *p;
+        }
+    }
+
+    max_index = get_max_index(bg_score);
+    if (bg_score[max_index] > 0)
+        *bg_color = original_area->pal[max_index];
+
+    max_index = get_max_index(text_score);
+    if (text_score[max_index] > 0)
+        *text_color = original_area->pal[max_index];
+
+    max_index = get_max_index(outline_score);
+    if (outline_score[max_index] > 0)
+        *outline_color = original_area->pal[max_index];
+
+    return 0;
+}
+
+static int convert_area(AVFilterContext *ctx, AVSubtitleArea *area, const AVFrame *frame, const unsigned area_index, int *margin_v)
+{
+    SubOcrContext *s = ctx->priv;
+    char *ocr_text = NULL;
+    int ret = 0;
+    uint8_t *gs_img;
+    uint8_t bg_color_index;
+    uint8_t text_color_index = 255;
+    uint8_t outline_color_index = 255;
+    char filename[32];
+    AVSubtitleArea *original_area = copy_subtitle_area(area);
+
+    if (!original_area)
+        return AVERROR(ENOMEM);
+
+    if (area->w < 6 || area->h < 6) {
+        area->ass = NULL;
+        goto exit;
+    }
+
+    if (s->dump_bitmaps) {
+        snprintf(filename, sizeof(filename), "graphicsub2text_%"PRId64"_%d_original", frame->subtitle_timing.start_pts, area_index);
+        png_save(ctx, filename, area);
+    }
+
+    if (s->preprocess_images) {
+        ret = quantize_image_colors(s, area);
+        if (ret < 0)
+            goto exit;
+        if (s->dump_bitmaps && original_area->nb_colors != area->nb_colors) {
+            snprintf(filename, sizeof(filename), "graphicsub2text_%"PRId64"_%d_quantized", frame->subtitle_timing.start_pts, area_index);
+            png_save(ctx, filename, area);
+        }
+    }
+
+    bg_color_index = get_background_color_index(s, area);
+
+    if (s->preprocess_images) {
+        int x, y, w, h;
+
+        for (int i = 0; i < area->nb_colors; ++i) {
+            av_log(s, AV_LOG_DEBUG, "Color #%d: %0.8X\n", i, area->pal[i]);
+        }
+
+        text_color_index = get_text_color_index(s, area, bg_color_index, &outline_color_index);
+
+        get_crop_region(s, area, text_color_index, &x, &y, &w, &h);
+
+        if ((ret = crop_area_bitmap(s, area, x, y, w, h) < 0))
+            goto exit;
+
+        if ((ret = crop_area_bitmap(s, original_area, x, y, w, h) < 0))
+            goto exit;
+
+        make_image_binary(s, area, text_color_index);
+
+        if (s->dump_bitmaps) {
+            snprintf(filename, sizeof(filename), "graphicsub2text_%"PRId64"_%d_preprocessed", frame->subtitle_timing.start_pts, area_index);
+            png_save(ctx, filename, area);
+        }
+
+        gs_img = create_bitmap_image(ctx, area, text_color_index);
+    } else
+        gs_img = create_grayscale_image(ctx, area, 1);
+
+    if (!gs_img) {
+        ret = AVERROR(ENOMEM);
+        goto exit;
+    }
+
+    area->type = AV_SUBTITLE_FMT_ASS;
+    TessBaseAPISetImage(s->tapi, gs_img, area->w, area->h, 1, area->linesize[0]);
+
+    TessBaseAPISetSourceResolution(s->tapi, 72);
+
+    ret = TessBaseAPIRecognize(s->tapi, NULL);
+    if (ret == 0)
+        ocr_text = TessBaseAPIGetUTF8Text(s->tapi);
+
+    if (!ocr_text || !strlen(ocr_text)) {
+        av_log(ctx, AV_LOG_WARNING, "OCR didn't return a text. ret=%d\n", ret);
+        area->ass = NULL;
+
+        goto exit;
+    }
+
+    const size_t len = strlen(ocr_text);
+    if (len > 0 && ocr_text[len - 1] == '\n')
+        ocr_text[len - 1] = 0;
+
+    av_log(ctx, AV_LOG_VERBOSE, "OCR Result: %s\n", ocr_text);
+
+    area->ass = av_strdup(ocr_text);
+    TessDeleteText(ocr_text);
+
+    // End of simple recognition
+
+    if (s->recognize != RFLAGS_NONE) {
+        TessResultIterator* ri = 0;
+        const TessPageIteratorLevel level = RIL_WORD;
+        int cur_is_bold = 0, cur_is_italic = 0, cur_is_underlined = 0, cur_pointsize = 0;
+        uint32_t cur_text_color = 0, cur_bg_color = 0, cur_outline_color = 0;
+
+        char *cur_font_name = NULL;
+        int valign = 0; // 0: bottom, 4: top, 8 middle
+        int halign = 2; // 1: left, 2: center, 3: right
+        int in_code = 0;
+        double font_factor = (0.000666 * (s->h - 480) + 1) * s->font_size_factor;
+
+        av_freep(&area->ass);
+        av_bprint_clear(&s->buffer);
+
+        ri = TessBaseAPIGetIterator(s->tapi);
+
+        // Horizontal Alignment
+        if (s->w && s->recognize & RFLAGS_HALIGN) {
+            int left_margin = area->x;
+            int right_margin = s->w - area->x - area->w;
+            double relative_diff = ((double)left_margin - right_margin) / s->w;
+
+            if (FFABS(relative_diff) < 0.1)
+                halign = 2; // center
+            else if (relative_diff > 0)
+                halign = 3; // right
+            else
+                halign = 1; // left
+        }
+
+        // Vertical Alignment
+        if (s->h && frame->height && s->recognize & RFLAGS_VALIGN) {
+            int left = 0, top = 0, right = 0, bottom = 0;
+
+            TessPageIteratorBoundingBox((TessPageIterator*)ri, RIL_TEXTLINE, &left, &top, &right, &bottom);
+            av_log(s, AV_LOG_DEBUG, "RIL_TEXTLINE - TOP: %d  BOTTOM: %d HEIGHT: %d\n", top, bottom, bottom - top);
+
+            TessPageIteratorBoundingBox((TessPageIterator*)ri, RIL_BLOCK, &left, &top, &right, &bottom);
+
+            const int vertical_pos = area->y + area->h / 2;
+            if (vertical_pos < s->h / 3) {
+                *margin_v = area->y + top;
+                valign = 4;
+            }
+            else if (vertical_pos < s->h / 3 * 2) {
+                *margin_v = 0;
+                valign = 8;
+            } else {
+                *margin_v = frame->height - area->y - area->h;
+                valign = 0;
+            }
+        }
+
+        if (*margin_v < 0)
+            *margin_v = 0;
+
+        // Set alignment when not default (2)
+        if ((valign | halign) != 2)
+            in_code = print_code(&s->buffer, in_code, "\\a%d", valign | halign);
+
+        do {
+            int is_bold, is_italic, is_underlined, is_monospace, is_serif, is_smallcaps, pointsize, font_id;
+            char* word;
+            const char *font_name = TessResultIteratorWordFontAttributes(ri, &is_bold, &is_italic, &is_underlined, &is_monospace, &is_serif, &is_smallcaps, &pointsize, &font_id);
+            uint32_t text_color = 0, bg_color = 0, outline_color = 0;
+
+            if (cur_is_underlined && !is_underlined && s->recognize & RFLAGS_FUNDERLINE)
+                in_code = print_code(&s->buffer, in_code, "\\u0");
+
+            if (cur_is_bold && !is_bold && s->recognize & RFLAGS_FBOLD)
+                in_code = print_code(&s->buffer, in_code, "\\b0");
+
+            if (cur_is_italic && !is_italic && s->recognize & RFLAGS_FITALIC)
+                in_code = print_code(&s->buffer, in_code, "\\i0");
+
+
+            if (TessPageIteratorIsAtBeginningOf((TessPageIterator*)ri, RIL_TEXTLINE) && !TessPageIteratorIsAtBeginningOf((TessPageIterator*)ri, RIL_BLOCK)) {
+                in_code = end_code(&s->buffer, in_code);
+                av_bprintf(&s->buffer, "\\N");
+            }
+
+            if (get_word_colors(ctx, ri, area, original_area, bg_color_index, text_color_index, outline_color_index, &bg_color, &text_color, &outline_color) == 0) {
+
+                if (text_color > 0 && cur_text_color != text_color && s->recognize & RFLAGS_COLOR) {
+                    const uint8_t* tval = (uint8_t*)&text_color;
+                    const int color = (int)tval[R] << 16 | (int)tval[G] << 8 | tval[B];
+
+                    in_code = print_code(&s->buffer, in_code, "\\1c&H%0.6X&", color);
+                    if (tval[A] != 255)
+                        in_code = print_code(&s->buffer, in_code, "\\1a&H%0.2X&", 255 - tval[A]);
+                }
+
+                if (outline_color > 0 && cur_outline_color != outline_color && s->recognize & RFLAGS_OUTLINECOLOR) {
+                    const uint8_t* tval = (uint8_t*)&outline_color;
+                    const int color = (int)tval[R] << 16 | (int)tval[G] << 8 | tval[B];
+
+                    in_code = print_code(&s->buffer, in_code, "\\3c&H%0.6X&\\bord2", color);
+                    in_code = print_code(&s->buffer, in_code, "\\3a&H%0.2X&", FFMIN(255 - tval[A], 30));
+                }
+
+                cur_text_color = text_color;
+                cur_outline_color = outline_color;
+            }
+
+            if (font_name && strlen(font_name) && s->recognize & RFLAGS_FONT) {
+                if (!cur_font_name || !strlen(cur_font_name) || strcmp(cur_font_name, font_name) != 0) {
+                    char *sanitized_font_name = av_strireplace(font_name, "_", " ");
+                    if (!sanitized_font_name) {
+                        ret = AVERROR(ENOMEM);
+                        goto exit;
+                    }
+
+                    in_code = print_code(&s->buffer, in_code, "\\fn%s", sanitized_font_name);
+                    av_freep(&sanitized_font_name);
+
+                    if (cur_font_name)
+                        av_freep(&cur_font_name);
+                    cur_font_name = av_strdup(font_name);
+                    if (!cur_font_name) {
+                        ret = AVERROR(ENOMEM);
+                        goto exit;
+                    }
+                }
+            }
+
+            if (pointsize != cur_pointsize && s->recognize & RFLAGS_FONTSIZE) {
+                av_log(s, AV_LOG_DEBUG, "pointsize - pointsize: %d\n", pointsize);
+                in_code = print_code(&s->buffer, in_code, "\\fs%d", (int)(pointsize * font_factor));
+                cur_pointsize = pointsize;
+            }
+
+            if (is_italic && !cur_is_italic && s->recognize & RFLAGS_FITALIC)
+                in_code = print_code(&s->buffer, in_code, "\\i1");
+
+            if (is_bold && !cur_is_bold && s->recognize & RFLAGS_FBOLD)
+                in_code = print_code(&s->buffer, in_code, "\\b1");
+
+            if (is_underlined && !cur_is_underlined && s->recognize & RFLAGS_FUNDERLINE)
+                in_code = print_code(&s->buffer, in_code, "\\u1");
+
+            in_code = end_code(&s->buffer, in_code);
+
+            cur_is_underlined = is_underlined;
+            cur_is_bold = is_bold;
+            cur_is_italic = is_italic;
+
+            if (!TessPageIteratorIsAtBeginningOf((TessPageIterator*)ri, RIL_TEXTLINE))
+                av_bprint_chars(&s->buffer, ' ', 1);
+
+            word = TessResultIteratorGetUTF8Text(ri, level);
+            av_bprint_append_data(&s->buffer, word, strlen(word));
+            TessDeleteText(word);
+
+        } while (TessResultIteratorNext(ri, level));
+
+        if (!av_bprint_is_complete(&s->buffer))
+            ret = AVERROR(ENOMEM);
+        else {
+            av_log(ctx, AV_LOG_VERBOSE, "ASS Result: %s\n", s->buffer.str);
+            area->ass = av_strdup(s->buffer.str);
+        }
+
+        TessResultIteratorDelete(ri);
+        av_freep(&cur_font_name);
+    }
+
+exit:
+    free_subtitle_area(original_area);
+    av_freep(&gs_img);
+    av_buffer_unref(&area->buf[0]);
+    area->type = AV_SUBTITLE_FMT_ASS;
+
+    return ret;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFilterContext *ctx = inlink->dst;
+    SubOcrContext *s = ctx->priv;
+    AVFilterLink *outlink = inlink->dst->outputs[0];
+    int ret, frame_sent = 0;
+
+    if (s->pending_frame && !frame->repeat_sub) {
+        const int64_t pts_diff = frame->subtitle_timing.start_pts - s->pending_frame->subtitle_timing.start_pts;
+
+        if (pts_diff == 0) {
+            // This is just a repetition of the previous frame, ignore it
+            av_frame_free(&frame);
+            return 0;
+        }
+
+        s->pending_frame->subtitle_timing.duration = pts_diff;
+
+        if ((ret = av_buffer_replace(&s->pending_frame->subtitle_header, s->subtitle_header)) < 0)
+            return ret;
+
+        ret = ff_filter_frame(outlink, s->pending_frame);
+        s->pending_frame = NULL;
+        if (ret < 0)
+            return  ret;
+
+        frame_sent = 1;
+        s->last_subtitle_pts = frame->subtitle_timing.start_pts;
+    }
+
+    if (frame->repeat_sub) {
+        // Ignore repeated frame
+        av_frame_free(&frame);
+        return 0;
+    }
+
+    s->last_subtitle_pts = frame->subtitle_timing.start_pts;
+
+    ret = av_frame_make_writable(frame);
+
+    if (ret < 0) {
+        av_frame_free(&frame);
+        return ret;
+    }
+
+    frame->format = AV_SUBTITLE_FMT_ASS;
+
+    av_log(ctx, AV_LOG_VERBOSE, "filter_frame sub_pts: %"PRIu64", duration: %"PRIu64", num_areas: %d\n",
+        frame->subtitle_timing.start_pts, frame->subtitle_timing.duration, frame->num_subtitle_areas);
+
+    if (frame->num_subtitle_areas > 1 &&
+        frame->subtitle_areas[0]->y > frame->subtitle_areas[frame->num_subtitle_areas - 1]->y) {
+
+        for (unsigned i = 0; i < frame->num_subtitle_areas / 2; i++)
+            FFSWAP(AVSubtitleArea*, frame->subtitle_areas[i], frame->subtitle_areas[frame->num_subtitle_areas - i - 1]);
+    }
+
+    for (int i = 0; i < frame->num_subtitle_areas; i++) {
+        AVSubtitleArea *area = frame->subtitle_areas[i];
+        int margin_v = 0;
+
+        ret = convert_area(ctx, area, frame, i, &margin_v);
+        if (ret < 0)
+            return ret;
+
+        if (area->ass && area->ass[0] != '\0') {
+
+            const int layer = s->recognize ? i : 0;
+            char *tmp = area->ass;
+            area->ass = avpriv_ass_get_dialog_ex(s->readorder_counter++, layer, "Default", NULL, 0, 0, margin_v, tmp);
+            av_free(tmp);
+        }
+    }
+
+    // When decoders can't determine the end time, they are setting it either to UINT32_NAX
+    // or 30s (dvbsub).
+    if (s->delay_when_no_duration && frame->subtitle_timing.duration >= ms_to_avtb(29000)) {
+        // Can't send it without end time, wait for the next frame to determine the end_display time
+        s->pending_frame = frame;
+
+        if (frame_sent)
+            return 0;
+
+        // To keep all going, send an empty frame instead
+        frame = ff_get_subtitles_buffer(outlink, AV_SUBTITLE_FMT_ASS);
+        if (!frame)
+            return AVERROR(ENOMEM);
+
+        av_frame_copy_props(frame, s->pending_frame);
+        frame->subtitle_timing.start_pts = 0;
+        frame->subtitle_timing.duration = 1;
+        frame->repeat_sub = 1;
+    }
+
+    if ((ret = av_buffer_replace(&frame->subtitle_header, s->subtitle_header)) < 0)
+        return ret;
+
+    return ff_filter_frame(outlink, frame);
+}
+
+#define OFFSET(x) offsetof(SubOcrContext, x)
+#define FLAGS (AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption graphicsub2text_options[] = {
+    { "delay_when_no_duration", "delay output when duration is unknown", OFFSET(delay_when_no_duration), AV_OPT_TYPE_BOOL,   { .i64 = 0 },                         0,                  1,       FLAGS, NULL },
+    { "dump_bitmaps",           "save processed bitmaps as .ppm",        OFFSET(dump_bitmaps),           AV_OPT_TYPE_BOOL,   { .i64 = 0 },                         0,                  1,       FLAGS, NULL },
+    { "font_size_factor",       "font size adjustment factor",           OFFSET(font_size_factor),       AV_OPT_TYPE_DOUBLE, { .dbl = 1.0 },                       0.2,                5,       FLAGS, NULL },
+    { "language",               "ocr language",                          OFFSET(language),               AV_OPT_TYPE_STRING, { .str = "eng" },                     0,                  0,       FLAGS, NULL },
+    { "ocr_mode",               "set ocr mode",                          OFFSET(ocr_mode),               AV_OPT_TYPE_INT,    { .i64=OEM_TESSERACT_ONLY },          OEM_TESSERACT_ONLY, 2,       FLAGS, "ocr_mode" },
+    {   "tesseract",            "classic tesseract ocr",                 0,                              AV_OPT_TYPE_CONST,  { .i64=OEM_TESSERACT_ONLY },          0,                  0,       FLAGS, "ocr_mode" },
+    {   "lstm",                 "lstm (ML based)",                       0,                              AV_OPT_TYPE_CONST,  { .i64=OEM_LSTM_ONLY},                0,                  0,       FLAGS, "ocr_mode" },
+    {   "both",                 "use both models combined",              0,                              AV_OPT_TYPE_CONST,  { .i64=OEM_TESSERACT_LSTM_COMBINED }, 0,                  0,       FLAGS, "ocr_mode" },
+    { "preprocess_images",      "reduce colors, remove outlines",        OFFSET(preprocess_images),      AV_OPT_TYPE_BOOL,   { .i64 = 1 },                         0,                  1,       FLAGS, NULL },
+    { "recognize",              "detect fonts, styles and colors",       OFFSET(recognize),              AV_OPT_TYPE_FLAGS,  { .i64 = RFLAGS_ALL},                  0,                  INT_MAX, FLAGS, "reco_flags" },
+        { "none",         "no format detection",  0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_NONE         }, 0, 0, FLAGS, "reco_flags" },
+        { "halign",       "horizontal alignment", 0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_HALIGN       }, 0, 0, FLAGS, "reco_flags" },
+        { "valign",       "vertical alignment",   0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_VALIGN       }, 0, 0, FLAGS, "reco_flags" },
+        { "bold",         "font bold",            0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_FBOLD        }, 0, 0, FLAGS, "reco_flags" },
+        { "italic",       "font italic",          0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_FITALIC      }, 0, 0, FLAGS, "reco_flags" },
+        { "underline",    "font underline",       0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_FUNDERLINE   }, 0, 0, FLAGS, "reco_flags" },
+        { "font",         "font name",            0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_FONT         }, 0, 0, FLAGS, "reco_flags" },
+        { "fontsize",     "font size",            0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_FONTSIZE     }, 0, 0, FLAGS, "reco_flags" },
+        { "color",        "font color",           0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_COLOR        }, 0, 0, FLAGS, "reco_flags" },
+        { "outlinecolor", "outline color",        0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_OUTLINECOLOR }, 0, 0, FLAGS, "reco_flags" },
+    { "tessdata_path",          "path to tesseract data",                OFFSET(tessdata_path),          AV_OPT_TYPE_STRING, { .str = NULL },                      0,                  0,       FLAGS, NULL },
+    { NULL },
+};
+
+AVFILTER_DEFINE_CLASS(graphicsub2text);
+
+static const AVFilterPad inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .filter_frame = filter_frame,
+        .config_props = config_input,
+    },
+};
+
+static const AVFilterPad outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_SUBTITLE,
+        .config_props  = config_output,
+    },
+};
+
+const AVFilter ff_sf_graphicsub2text = {
+    .name          = "graphicsub2text",
+    .description   = NULL_IF_CONFIG_SMALL("Convert graphical subtitles to text subtitles via OCR"),
+    .init          = init,
+    .uninit        = uninit,
+    .priv_size     = sizeof(SubOcrContext),
+    .priv_class    = &graphicsub2text_class,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_QUERY_FUNC(query_formats),
+};
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v4 19/23] avfilter/subscale: Add filter for scaling and/or re-arranging graphical subtitles
  2022-05-28 13:25     ` [FFmpeg-devel] [PATCH v4 00/23] Subtitle Filtering 2022 ffmpegagent
                         ` (17 preceding siblings ...)
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 18/23] avfilter/graphicsub2text: Add new graphicsub2text filter (OCR) softworkz
@ 2022-05-28 13:25       ` softworkz
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 20/23] avfilter/subfeed: add subtitle feed filter softworkz
                         ` (4 subsequent siblings)
  23 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-05-28 13:25 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 configure                 |   1 +
 doc/filters.texi          | 164 +++++++
 libavfilter/Makefile      |   1 +
 libavfilter/allfilters.c  |   1 +
 libavfilter/sf_subscale.c | 884 ++++++++++++++++++++++++++++++++++++++
 5 files changed, 1051 insertions(+)
 create mode 100644 libavfilter/sf_subscale.c

diff --git a/configure b/configure
index e9e1fc0966..c321c0b1d5 100755
--- a/configure
+++ b/configure
@@ -3729,6 +3729,7 @@ sr_filter_deps="avformat swscale"
 sr_filter_select="dnn"
 stereo3d_filter_deps="gpl"
 splitcc_filter_deps="avcodec"
+subscale_filter_deps="swscale avcodec"
 subtitles_filter_deps="avformat avcodec libass"
 super2xsai_filter_deps="gpl"
 pixfmts_super2xsai_test_deps="super2xsai_filter"
diff --git a/doc/filters.texi b/doc/filters.texi
index 8b823d4e12..53a5656d7d 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -27469,6 +27469,170 @@ Set the rendering margin in pixels.
 For rendering, alway use the latest event only, which is covering the given point in time.
 @end table
 
+@section subscale
+
+Provides high-quality scaling and rearranging functionality for graphical subtitles.
+
+The subscale filter provides multiple approaches for manipulating
+the size and position of graphical subtitle rectangles wich can
+be combined or used separately.
+Scaling is performed by converting the palettized subtitle bitmaps
+to RGBA and re-quantization to palette colors afterwards via elbg algorithm.
+
+The two major operations are 'scale' and 're-arrange' with the
+latter being separated as 'arrange_h' and 'arrange_v'.
+
+
+Inputs:
+- 0: Subtitles [bitmap]
+
+Outputs:
+- 0: Subtitles [bitmap]
+
+It accepts the following parameters:
+
+@table @option
+
+@item w, width
+Set the width of the output.
+Width and height in case of graphical subtitles are just indicating
+a virtual size for which the output (consisting of 0-n bitmap rectangles)
+is intended to be displayed on.
+
+@item h, height
+Set the height of the output.
+
+@item margin_h
+Sets a horizontal margin to be preserverved when using any
+of the arrange modes.
+
+@item margin_v
+Sets a vertical margin to be preserverved when using any
+of the arrange modes.
+
+@item force_original_aspect_ratio
+Enable decreasing or increasing output video width or height if necessary to
+keep the original aspect ratio. Possible values:
+
+@table @samp
+@item disable
+Scale the video as specified and disable this feature.
+
+@item decrease
+The output video dimensions will automatically be decreased if needed.
+
+@item increase
+The output video dimensions will automatically be increased if needed.
+
+@end table
+
+
+@item scale_mode
+Specifies how subtitle bitmaps should be scaled.
+The scale factor is determined by the the factor between input
+and output size.
+
+@table @samp
+@item none
+Do not apply any common scaling.
+
+@item uniform
+Uniformly scale all subtitle bitmaps including their positions.
+
+@item uniform_no_reposition
+Uniformly scale all subtitle bitmaps without changing positions.
+
+@end table
+
+
+@item arrange_h
+Specifies how subtitle bitmaps should be arranged horizontally.
+
+@item arrange_v
+Specifies how subtitle bitmaps should be arranged vertically.
+
+
+@table @samp
+@item none
+Do not rearrange subtitle bitmaps.
+
+@item margin_no_scale
+Move subtitle bitmaps to be positioned inside the specified
+margin (margin_h or margin_v) when possible and without scaling.
+
+@item margin_and_scale
+Move subtitle bitmaps to be positioned inside the specified
+margin (margin_h or margin_v) and scale in case it doesn't fit.
+
+@item snapalign_no_scale
+Categorize subtitle bitmap positions as one of left/center/right
+or top/bottom/middle based on original positioning and apply
+these alignments for the target positioning.
+No scaling will be applied.
+
+@item snapalign_and_scale
+Categorize subtitle bitmap positions as one of left/center/right
+or top/bottom/middle based on original positioning and apply
+these alignments for the target positioning.
+Bitmaps that do not fit inside the margins borders are
+scaled to fit.
+@end table
+
+@item eval
+Set evaluation mode for the expressions (@option{width}, @option{height}).
+
+It accepts the following values:
+@table @samp
+@item init
+Evaluate expressions only once during the filter initialization.
+
+@item frame
+Evaluate expressions for each incoming frame. This is way slower than the
+@samp{init} mode since it requires all the scalers to be re-computed, but it
+allows advanced dynamic expressions.
+@end table
+
+Default value is @samp{init}.
+
+
+@item num_colors
+Set the number of palette colors for output images.
+Choose the maximum (256) when further processing is done (e.g.
+overlaying on a video).
+When subtitles will be encoded as bitmap subtitles (e.g. dvbsub),
+a smaller number of palette colors (e.g. 4-16) might need to be used, depending
+on the target format and codec.
+
+@item bitmap_width_align
+@item bitmap_height_align
+Make sure that subtitle bitmap sizes are a multiple of this
+value. Default is 2.
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Uniformly scale down video and bitmap subtitles and encode
+subtitles as dvbsub.
+@example
+ffmpeg -y -loglevel verbose -ss 32 -i "https://streams.videolan.org/samples/sub/largeres_vobsub.mkv" -filter_complex "[0:0]scale=480x270[vid1];[0:10]subscale=w=480:h=270:num_colors=16[sub1]" -map [vid1] -map [sub1] -c:s dvbsub -c:v libx265 output.ts
+@end example
+@item
+Squeeze video vertically and arrange subtitle bitmaps
+inside the video area without scaling, then overlay
+subtitles onto the video.
+@example
+ffmpeg -y -loglevel verbose -ss 32 -i "https://streams.videolan.org/samples/sub/largeres_vobsub.mkv" -filter_complex "[0:0]scale=1920x400,setsar=1[vid1];[0:10]subscale=w=1920:h=400:scale_mode=none:margin_h=50:arrange_h=margin_no_scale:arrange_v=margin_no_scale:margin_v=50:num_colors=256[sub1];[vid1][sub1]overlaygraphicsubs" -c:v libx265 output.ts
+@end example
+@item
+Scale both, a video and its embedded VOB subs, then encode them as separate streams into an MKV.
+@example
+ffmpeg -y -y -loglevel verbose -i "https://streams.videolan.org/samples/sub/largeres_vobsub.mkv" -filter_complex "[0:0]scale=720x576[vid1];[0:7]subscale=w=720:h=576:num_colors=16[sub1]" -map [vid1] -map [sub1] -c:s dvdsub out.mkv
+@end example
+@end itemize
+
 @c man end SUBTITLE FILTERS
 
 @chapter Multimedia Filters
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 66a3b73e33..d6a9524b9a 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -583,6 +583,7 @@ OBJS-$(CONFIG_SHOW_SPEAKER_FILTER)           += sf_textmod.o
 OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
 OBJS-$(CONFIG_SPLITCC_FILTER)                += sf_splitcc.o
 OBJS-$(CONFIG_STRIPSTYLES_FILTER)            += sf_stripstyles.o
+OBJS-$(CONFIG_SUBSCALE_FILTER)               += sf_subscale.o
 
 # multimedia filters
 OBJS-$(CONFIG_ABITSCOPE_FILTER)              += avf_abitscope.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 46d38ef2cd..67a0570561 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -564,6 +564,7 @@ extern const AVFilter ff_sf_graphicsub2text;
 extern const AVFilter ff_sf_showspeaker;
 extern const AVFilter ff_sf_splitcc;
 extern const AVFilter ff_sf_stripstyles;
+extern const AVFilter ff_sf_subscale;
 extern const AVFilter ff_sf_textmod;
 extern const AVFilter ff_svf_graphicsub2video;
 extern const AVFilter ff_svf_textsub2video;
diff --git a/libavfilter/sf_subscale.c b/libavfilter/sf_subscale.c
new file mode 100644
index 0000000000..04f0f16c27
--- /dev/null
+++ b/libavfilter/sf_subscale.c
@@ -0,0 +1,884 @@
+/*
+ * 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
+ * scale graphical subtitles filter
+ */
+
+#include <string.h>
+
+#include "drawutils.h"
+#include "internal.h"
+#include "scale_eval.h"
+#include "libavutil/eval.h"
+#include "libavutil/opt.h"
+#include "libswscale/swscale.h"
+
+#include "libavcodec/elbg.h"
+
+static const char *const var_names[] = {
+    "in_w",   "iw",
+    "in_h",   "ih",
+    "out_w",  "ow",
+    "out_h",  "oh",
+    "a",
+    "sar",
+    "dar",
+    "margin_h",
+    "margin_v",
+    NULL
+};
+
+enum var_name {
+    VAR_IN_W,   VAR_IW,
+    VAR_IN_H,   VAR_IH,
+    VAR_OUT_W,  VAR_OW,
+    VAR_OUT_H,  VAR_OH,
+    VAR_A,
+    VAR_SAR,
+    VAR_DAR,
+    VARS_B_H,
+    VARS_B_V,
+    VARS_NB
+};
+
+enum EvalMode {
+    EVAL_MODE_INIT,
+    EVAL_MODE_FRAME,
+    EVAL_MODE_NB
+};
+
+enum SubScaleMode {
+    SSM_NONE,
+    SSM_UNIFORM,
+    SSM_UNIFORM_NO_REPOSITION,
+};
+
+enum SubArrangeMode {
+    SAM_NONE,
+    SAM_ENSUREMARGIN_NO_SCALE,
+    SAM_ENSUREMARGIN_AND_SCALE,
+    SAM_SNAPALIGNMENT_NO_SCALE,
+    SAM_SNAPALIGNMENT_AND_SCALE,
+};
+
+typedef struct SubScaleContext {
+    const AVClass *class;
+    struct SwsContext *sws;
+    AVDictionary *opts;
+
+    int w, h;
+
+    char *w_expr;               ///< width  expression string
+    char *h_expr;               ///< height expression string
+    AVExpr *w_pexpr;
+    AVExpr *h_pexpr;
+    double var_values[VARS_NB];
+
+    int force_original_aspect_ratio;
+    int eval_mode;               ///< expression evaluation mode
+
+    int use_caching;
+
+    // Scale Options
+    enum SubScaleMode scale_mode;
+
+    // Arrange Options
+    enum SubArrangeMode arrange_mode_h;
+    enum SubArrangeMode arrange_mode_v;
+    int margin_h;
+    int margin_v;
+    char *margin_h_expr;
+    char *margin_v_expr;
+    AVExpr *margin_h_pexpr;
+    AVExpr *margin_v_pexpr;
+
+    // Bitmap Options
+    int num_output_colors;
+    int bitmap_width_align;
+    int bitmap_height_align;
+
+    // Color Quantization Fields
+    struct ELBGContext *ctx;
+    AVLFG lfg;
+    int *codeword;
+    int *codeword_closest_codebook_idxs;
+    int *codebook;
+    int r_idx, g_idx, b_idx, a_idx;
+    AVFrame *cache_frame;
+} SubScaleContext;
+
+
+static int config_output(AVFilterLink *outlink);
+
+static int check_exprs(AVFilterContext *ctx)
+{
+    const SubScaleContext *s = ctx->priv;
+    unsigned vars_w[VARS_NB] = { 0 }, vars_h[VARS_NB] = { 0 };
+
+    if (!s->w_pexpr && !s->h_pexpr)
+        return AVERROR(EINVAL);
+
+    if (s->w_pexpr)
+        av_expr_count_vars(s->w_pexpr, vars_w, VARS_NB);
+    if (s->h_pexpr)
+        av_expr_count_vars(s->h_pexpr, vars_h, VARS_NB);
+
+    if (vars_w[VAR_OUT_W] || vars_w[VAR_OW]) {
+        av_log(ctx, AV_LOG_ERROR, "Width expression cannot be self-referencing: '%s'.\n", s->w_expr);
+        return AVERROR(EINVAL);
+    }
+
+    if (vars_h[VAR_OUT_H] || vars_h[VAR_OH]) {
+        av_log(ctx, AV_LOG_ERROR, "Height expression cannot be self-referencing: '%s'.\n", s->h_expr);
+        return AVERROR(EINVAL);
+    }
+
+    if ((vars_w[VAR_OUT_H] || vars_w[VAR_OH]) &&
+        (vars_h[VAR_OUT_W] || vars_h[VAR_OW])) {
+        av_log(ctx, AV_LOG_WARNING, "Circular references detected for width '%s' and height '%s' - possibly invalid.\n", s->w_expr, s->h_expr);
+    }
+
+    if (s->margin_h_pexpr)
+        av_expr_count_vars(s->margin_h_pexpr, vars_w, VARS_NB);
+    if (s->margin_v_pexpr)
+        av_expr_count_vars(s->margin_v_pexpr, vars_h, VARS_NB);
+
+    return 0;
+}
+
+static int scale_parse_expr(AVFilterContext *ctx, char *str_expr, AVExpr **pexpr_ptr, const char *var, const char *args)
+{
+    SubScaleContext *s = ctx->priv;
+    int ret, is_inited = 0;
+    char *old_str_expr = NULL;
+    AVExpr *old_pexpr = NULL;
+
+    if (str_expr) {
+        old_str_expr = av_strdup(str_expr);
+        if (!old_str_expr)
+            return AVERROR(ENOMEM);
+        av_opt_set(s, var, args, 0);
+    }
+
+    if (*pexpr_ptr) {
+        old_pexpr = *pexpr_ptr;
+        *pexpr_ptr = NULL;
+        is_inited = 1;
+    }
+
+    ret = av_expr_parse(pexpr_ptr, args, var_names,
+                        NULL, NULL, NULL, NULL, 0, ctx);
+    if (ret < 0) {
+        av_log(ctx, AV_LOG_ERROR, "Cannot parse expression for %s: '%s'\n", var, args);
+        goto revert;
+    }
+
+    ret = check_exprs(ctx);
+    if (ret < 0)
+        goto revert;
+
+    if (is_inited && (ret = config_output(ctx->outputs[0])) < 0)
+        goto revert;
+
+    av_expr_free(old_pexpr);
+    av_freep(&old_str_expr);
+
+    return 0;
+
+revert:
+    av_expr_free(*pexpr_ptr);
+    *pexpr_ptr = NULL;
+    if (old_str_expr) {
+        av_opt_set(s, var, old_str_expr, 0);
+        av_free(old_str_expr);
+    }
+    if (old_pexpr)
+        *pexpr_ptr = old_pexpr;
+
+    return ret;
+}
+
+static av_cold int init_dict(AVFilterContext *ctx, AVDictionary **opts)
+{
+    SubScaleContext *s = ctx->priv;
+    uint8_t rgba_map[4];
+    int ret;
+
+    if (!s->w_expr)
+        av_opt_set(s, "w", "iw", 0);
+    if (!s->h_expr)
+        av_opt_set(s, "h", "ih", 0);
+
+    ret = scale_parse_expr(ctx, NULL, &s->w_pexpr, "width", s->w_expr);
+    if (ret < 0)
+        return ret;
+
+    ret = scale_parse_expr(ctx, NULL, &s->h_pexpr, "height", s->h_expr);
+    if (ret < 0)
+        return ret;
+
+    av_log(ctx, AV_LOG_VERBOSE, "w:%s h:%s\n",
+           s->w_expr, s->h_expr);
+
+    if (!s->margin_h_expr)
+        av_opt_set(s, "margin_h", "0", 0);
+    if (!s->margin_v_expr)
+        av_opt_set(s, "margin_v", "0", 0);
+
+    ret = scale_parse_expr(ctx, NULL, &s->margin_h_pexpr, "margin_h", s->margin_h_expr);
+    if (ret < 0)
+        return ret;
+
+    ret = scale_parse_expr(ctx, NULL, &s->margin_v_pexpr, "margin_v", s->margin_v_expr);
+    if (ret < 0)
+        return ret;
+
+    s->opts = *opts;
+    *opts = NULL;
+
+    ff_fill_rgba_map(&rgba_map[0], AV_PIX_FMT_RGB32);
+
+    s->r_idx = rgba_map[0]; // R
+    s->g_idx = rgba_map[1]; // G
+    s->b_idx = rgba_map[2]; // B
+    s->a_idx = rgba_map[3]; // A
+
+    av_lfg_init(&s->lfg, 123456789);
+
+
+    return 0;
+}
+
+static av_cold void uninit(AVFilterContext *ctx)
+{
+    SubScaleContext *s = ctx->priv;
+
+    av_frame_free(&s->cache_frame);
+
+    av_expr_free(s->w_pexpr);
+    av_expr_free(s->h_pexpr);
+    s->w_pexpr = s->h_pexpr = NULL;
+
+    av_expr_free(s->margin_h_pexpr);
+    av_expr_free(s->margin_v_pexpr);
+    s->margin_h_pexpr = s->margin_v_pexpr = NULL;
+
+    sws_freeContext(s->sws);
+    s->sws = NULL;
+    av_dict_free(&s->opts);
+
+    avpriv_elbg_free(&s->ctx);
+
+    av_freep(&s->codebook);
+    av_freep(&s->codeword);
+    av_freep(&s->codeword_closest_codebook_idxs);
+}
+
+static int config_input(AVFilterLink *inlink)
+{
+    ////const AVFilterContext *ctx = inlink->dst;
+    ////SubScaleContext *s = ctx->priv;
+
+    ////if (s->w <= 0 || s->h <= 0) {
+    ////    s->w = inlink->w;
+    ////    s->h = inlink->h;
+    ////}
+    return 0;
+}
+
+static int scale_eval_dimensions(AVFilterContext *ctx)
+{
+    SubScaleContext *s = ctx->priv;
+    const AVFilterLink *inlink = ctx->inputs[0];
+    char *expr;
+    int eval_w, eval_h, margin_h, margin_v;
+    int ret;
+    double res;
+
+    s->var_values[VAR_IN_W]  = s->var_values[VAR_IW] = inlink->w;
+    s->var_values[VAR_IN_H]  = s->var_values[VAR_IH] = inlink->h;
+    s->var_values[VAR_OUT_W] = s->var_values[VAR_OW] = NAN;
+    s->var_values[VAR_OUT_H] = s->var_values[VAR_OH] = NAN;
+    s->var_values[VARS_B_H]  = s->var_values[VARS_B_V] = 0;
+    s->var_values[VAR_A]     = (double) inlink->w / inlink->h;
+    s->var_values[VAR_SAR]   = inlink->sample_aspect_ratio.num ?
+        (double) inlink->sample_aspect_ratio.num / inlink->sample_aspect_ratio.den : 1;
+    s->var_values[VAR_DAR]   = s->var_values[VAR_A] * s->var_values[VAR_SAR];
+
+    res = av_expr_eval(s->w_pexpr, s->var_values, NULL);
+    s->var_values[VAR_OUT_W] = s->var_values[VAR_OW] = (int) res == 0 ? inlink->w : (int) res;
+
+    res = av_expr_eval(s->h_pexpr, s->var_values, NULL);
+    if (isnan(res)) {
+        expr = s->h_expr;
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+    eval_h = s->var_values[VAR_OUT_H] = s->var_values[VAR_OH] = (int) res == 0 ? inlink->h : (int) res;
+
+    res = av_expr_eval(s->w_pexpr, s->var_values, NULL);
+    if (isnan(res)) {
+        expr = s->w_expr;
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+    eval_w = s->var_values[VAR_OUT_W] = s->var_values[VAR_OW] = (int) res == 0 ? inlink->w : (int) res;
+
+    s->w = eval_w;
+    s->h = eval_h;
+
+    res = av_expr_eval(s->margin_h_pexpr, s->var_values, NULL);
+    s->var_values[VARS_B_H] = (int)res;
+
+    res = av_expr_eval(s->margin_v_pexpr, s->var_values, NULL);
+    if (isnan(res)) {
+        expr = s->margin_v_expr;
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+    margin_v = s->var_values[VARS_B_V] = (int)res;
+
+    res = av_expr_eval(s->margin_h_pexpr, s->var_values, NULL);
+    if (isnan(res)) {
+        expr = s->margin_h_expr;
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+    margin_h = s->var_values[VARS_B_H] = (int)res;
+
+    s->margin_h = margin_h;
+    s->margin_v = margin_v;
+
+    return 0;
+
+fail:
+    av_log(ctx, AV_LOG_ERROR,
+           "Error when evaluating the expression '%s'.\n", expr);
+    return ret;
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    AVFilterLink *inlink  = outlink->src->inputs[0];
+    SubScaleContext *s = ctx->priv;
+    int ret;
+
+    outlink->format = AV_SUBTITLE_FMT_BITMAP;
+    outlink->time_base = ctx->inputs[0]->time_base;
+    outlink->frame_rate = ctx->inputs[0]->frame_rate;
+
+    if ((ret = scale_eval_dimensions(ctx)) < 0)
+        goto fail;
+
+    ff_scale_adjust_dimensions(inlink, &s->w, &s->h,
+                               s->force_original_aspect_ratio, 2);
+
+    if (s->w > INT_MAX ||
+        s->h > INT_MAX ||
+        (s->h * inlink->w) > INT_MAX ||
+        (s->w * inlink->h) > INT_MAX)
+        av_log(ctx, AV_LOG_ERROR, "Rescaled value for width or height is too big.\n");
+
+    outlink->w = s->w;
+    outlink->h = s->h;
+
+    if (s->sws)
+        sws_freeContext(s->sws);
+
+    s->sws = sws_alloc_context();
+    if (!s->sws)
+        return AVERROR(ENOMEM);
+
+    av_opt_set_pixel_fmt(s->sws, "src_format", AV_PIX_FMT_PAL8, 0);
+    av_opt_set_int(s->sws, "dst_format", AV_PIX_FMT_RGB32, 0);
+    av_opt_set_int(s->sws, "threads", ff_filter_get_nb_threads(ctx), 0);
+
+    if (s->opts) {
+        const AVDictionaryEntry *e = NULL;
+        while ((e = av_dict_get(s->opts, "", e, AV_DICT_IGNORE_SUFFIX))) {
+            if ((ret = av_opt_set(s->sws, e->key, e->value, 0)) < 0)
+                return ret;
+        }
+    }
+
+    if ((ret = sws_init_context(s->sws, NULL, NULL)) < 0)
+        return ret;
+
+    if (inlink->sample_aspect_ratio.num){
+        outlink->sample_aspect_ratio = av_mul_q((AVRational){outlink->h * inlink->w, outlink->w * inlink->h}, inlink->sample_aspect_ratio);
+    } else
+        outlink->sample_aspect_ratio = inlink->sample_aspect_ratio;
+
+    av_log(ctx, AV_LOG_VERBOSE, "Output size set to %dx%d.\n", outlink->w, outlink->h);
+
+    return 0;
+fail:
+    return ret;
+}
+
+static int palettize_image(SubScaleContext *const s, const int w, const int h, const int src_linesize, uint8_t *src_data,
+                          int dst_linesize, uint8_t *dst_data, uint32_t *dst_pal)
+{
+    int k, ret;
+    const int codeword_length = w * h;
+
+    /* Re-Initialize */
+    s->codeword = av_realloc_f(s->codeword, codeword_length, 4 * sizeof(*s->codeword));
+    if (!s->codeword)
+        return AVERROR(ENOMEM);
+
+    s->codeword_closest_codebook_idxs =
+        av_realloc_f(s->codeword_closest_codebook_idxs, codeword_length,
+                     sizeof(*s->codeword_closest_codebook_idxs));
+    if (!s->codeword_closest_codebook_idxs)
+        return AVERROR(ENOMEM);
+
+    s->codebook = av_realloc_f(s->codebook, s->num_output_colors, 4 * sizeof(*s->codebook));
+    if (!s->codebook)
+        return AVERROR(ENOMEM);
+
+    /* build the codeword */
+    k = 0;
+    for (int i = 0; i < h; i++) {
+        uint8_t *p = src_data;
+        for (int j = 0; j < w; j++) {
+            s->codeword[k++] = p[s->b_idx];
+            s->codeword[k++] = p[s->g_idx];
+            s->codeword[k++] = p[s->r_idx];
+            s->codeword[k++] = p[s->a_idx];
+            p += 4;
+        }
+        src_data += src_linesize;
+    }
+
+    /* compute the codebook */
+    ret = avpriv_elbg_do(&s->ctx, s->codeword, 4,
+        codeword_length, s->codebook,
+        s->num_output_colors, 1,
+        s->codeword_closest_codebook_idxs, &s->lfg, 0);
+
+    if (ret < 0)
+        return ret;
+
+    /* Write Palette */
+    for (int i = 0; i < s->num_output_colors; i++) {
+        dst_pal[i] = s->codebook[i*4+3] << 24  |
+                    (s->codebook[i*4+2] << 16) |
+                    (s->codebook[i*4+1] <<  8) |
+                     s->codebook[i*4  ];
+    }
+
+    /* Write Image */
+    k = 0;
+    for (int i = 0; i < h; i++) {
+        uint8_t *p = dst_data;
+        for (int j = 0; j < w; j++, p++) {
+            p[0] = s->codeword_closest_codebook_idxs[k++];
+        }
+
+        dst_data += dst_linesize;
+    }
+
+    return ret;
+}
+
+static int rescale_size(int64_t a, AVRational factor)
+{
+    const int64_t res = av_rescale_rnd(a, factor.num, factor.den, AV_ROUND_NEAR_INF);
+    if (res > INT32_MAX || res < 0)
+        return 0;
+
+    return (int)res;
+}
+
+
+static int scale_area(AVFilterLink *link, AVSubtitleArea *area, const int target_width, const int target_height)
+{
+    const AVFilterContext *ctx = link->dst;
+    SubScaleContext *s = ctx->priv;
+    int ret;
+
+    AVBufferRef *dst_buffer;
+    const uint8_t* data[2]    = { area->buf[0]->data, (uint8_t *)&area->pal };
+    const int dstW            = FFALIGN(target_width, s->bitmap_width_align);
+    const int dstH            = FFALIGN(target_height, s->bitmap_height_align);
+    const int tmp_linesize[2] = { FFALIGN(dstW * 4, 32), 0 };
+    const int dst_linesize[2] = { dstW, 0 };
+    uint8_t* tmp[2] = { 0, 0 };
+
+    AVBufferRef *tmp_buffer = av_buffer_allocz(tmp_linesize[0] * dstH);
+    if (!tmp_buffer)
+        return AVERROR(ENOMEM);
+
+    if (!s->sws)
+        return 0;
+
+    tmp[0] = tmp_buffer->data;
+
+    s->sws = sws_getCachedContext(s->sws, area->w, area->h, AV_PIX_FMT_PAL8,
+        dstW, dstH, AV_PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL);
+    if (!s->sws) {
+        av_log(NULL, AV_LOG_FATAL, "Cannot initialize the conversion context. dstW=%d dstH=%d\n", dstW, dstH);
+        return AVERROR(EINVAL);
+    }
+
+    // Rescale to ARGB
+    ret = sws_scale(s->sws, data, area->linesize, 0, area->h, tmp, tmp_linesize);
+    if (ret < 0) {
+        av_buffer_unref(&tmp_buffer);
+        return ret;
+    }
+
+    // Alloc output buffer
+    dst_buffer = av_buffer_allocz(dst_linesize[0] * dstH);
+    if (!dst_buffer) {
+        av_buffer_unref(&tmp_buffer);
+        return AVERROR(ENOMEM);
+    }
+
+    // Quantize to palettized image
+    ret = palettize_image(s, dstW, dstH, tmp_linesize[0], tmp[0], dst_linesize[0], dst_buffer->data, area->pal);
+    av_buffer_unref(&tmp_buffer);
+
+    if (ret < 0) {
+        av_buffer_unref(&dst_buffer);
+        return ret;
+    }
+
+    av_buffer_unref(&area->buf[0]);
+    ret = av_buffer_replace(&area->buf[0], dst_buffer);
+    if (ret < 0) {
+        av_buffer_unref(&dst_buffer);
+        return ret;
+    }
+
+    area->w = dstW;
+    area->h = dstH;
+    area->linesize[0] = dst_linesize[0];
+    area->nb_colors = s->num_output_colors;
+
+    return ret;
+}
+
+static int process_area(AVFilterLink *inlink, AVSubtitleArea *area, AVRational x_factor, AVRational y_factor)
+{
+    AVFilterContext *ctx     = inlink->dst;
+    const SubScaleContext *s = ctx->priv;
+    int target_w, target_h, target_x, target_y;
+    const int border_l = s->margin_h;
+    const int border_r = s->w - s->margin_h;
+    const int border_t = s->margin_v;
+    const int border_b = s->h - s->margin_v;
+
+    av_log(ctx, AV_LOG_DEBUG, "process_area -  start: x/y: (%d:%d) size: %dx%d scale_mode: %d x-factor: %d:%d y-factor: %d:%d\n",
+        area->x, area->y, area->w, area->h, s->scale_mode, x_factor.num, x_factor.den, y_factor.num, y_factor.den);
+
+    switch (s->scale_mode) {
+    case SSM_NONE:
+        target_w = area->w;
+        target_h = area->h;
+        target_x = area->x;
+        target_y = area->y;
+        break;
+    case SSM_UNIFORM:
+        target_w = rescale_size(area->w, x_factor);
+        target_h = rescale_size(area->h, y_factor);
+        target_x = rescale_size(area->x, x_factor);
+        target_y = rescale_size(area->y, y_factor);
+        break;
+    case SSM_UNIFORM_NO_REPOSITION:
+        target_w = rescale_size(area->w, x_factor);
+        target_h = rescale_size(area->h, y_factor);
+        target_x = area->x;
+        target_y = area->y;
+        break;
+    default:
+        av_log(ctx, AV_LOG_ERROR, "Invalid scale_mode: %d\n", s->scale_mode);
+        return AVERROR(EINVAL);
+    }
+
+    av_log(ctx, AV_LOG_DEBUG, "process_area - scaled: x/y: (%d:%d) size: %dx%d.\n", target_x, target_y, target_w, target_h);
+
+
+    switch (s->arrange_mode_h) {
+    case SAM_ENSUREMARGIN_AND_SCALE:
+    case SAM_SNAPALIGNMENT_AND_SCALE:
+        {
+            // IF it doesn't fit - scale it
+            int max_width = s->w - 2 * s->margin_h;
+
+            if (max_width < 2)
+                max_width = 2;
+
+            if (target_w > max_width) {
+                target_h = (int)av_rescale(target_h, max_width, target_w);
+                target_w = max_width;
+                target_x = s->margin_h;
+            }
+        }
+        break;
+    }
+
+    switch (s->arrange_mode_h) {
+    case SAM_NONE:
+        break;
+    case SAM_ENSUREMARGIN_NO_SCALE:
+    case SAM_ENSUREMARGIN_AND_SCALE:
+        // Left border
+        if (target_x < border_l)
+            target_x = border_l;
+
+        // Right border
+        if (target_x + target_w > border_r)
+            target_x = border_r - target_w;
+
+        break;
+    case SAM_SNAPALIGNMENT_NO_SCALE:
+    case SAM_SNAPALIGNMENT_AND_SCALE:
+        {
+            // Use original values to detect alignment
+            const int left_margin          = area->x;
+            const int right_margin         = inlink->w - area->x - area->w;
+            const AVRational diff_factor_r = { left_margin - right_margin, area->w };
+            const float diff_factor        = (float)av_q2d(diff_factor_r);
+
+            if (diff_factor > 0.2f) {
+                // Right aligned
+                target_x = border_r - target_w;
+            } else if (diff_factor < -0.2f) {
+                // Left aligned
+                target_x = border_l;
+            } else {
+                // Centered
+                target_x = (inlink->w - area->w) / 2;
+            }
+        }
+
+        break;
+    default:
+        av_log(ctx, AV_LOG_ERROR, "Invalid arrange_mode_h: %d\n", s->arrange_mode_h);
+        return AVERROR(EINVAL);
+    }
+
+    av_log(ctx, AV_LOG_DEBUG, "process_area -  arr_h: x/y: (%d:%d) size: %dx%d.\n", target_x, target_y, target_w, target_h);
+
+
+    switch (s->arrange_mode_v) {
+    case SAM_ENSUREMARGIN_AND_SCALE:
+    case SAM_SNAPALIGNMENT_AND_SCALE:
+        {
+            // IF it doesn't fit - scale it
+            int max_height = s->h - 2 * s->margin_v;
+
+            if (max_height < 2)
+                max_height = 2;
+
+            if (target_h > max_height) {
+                target_w = (int)av_rescale(target_w, max_height, target_h);
+                target_h = max_height;
+                target_y = s->margin_v;
+            }
+        }
+        break;
+    }
+
+    switch (s->arrange_mode_v) {
+    case SAM_NONE:
+        break;
+    case SAM_ENSUREMARGIN_NO_SCALE:
+    case SAM_ENSUREMARGIN_AND_SCALE:
+        // Top border
+        if (target_y < border_t)
+            target_y = border_t;
+
+        // Bottom border
+        if (target_y + target_h > border_b)
+            target_y = border_b - target_h;
+
+        break;
+    case SAM_SNAPALIGNMENT_NO_SCALE:
+    case SAM_SNAPALIGNMENT_AND_SCALE:
+        {
+            // Use original values to detect alignment
+            const int top_margin           = area->y;
+            const int bottom_margin        = inlink->h - area->y - area->h;
+            const AVRational diff_factor_r = { top_margin - bottom_margin, area->h };
+            const float diff_factor        = (float)av_q2d(diff_factor_r);
+
+            if (diff_factor > 0.2f) {
+                // Bottom aligned
+                target_y = border_b - target_h;
+            } else if (diff_factor < -0.2f) {
+                // Top aligned
+                target_y = border_t;
+            } else {
+                // Centered
+                target_y = (inlink->h - area->h) / 2;
+            }
+        }
+
+        break;
+    default:
+        av_log(ctx, AV_LOG_ERROR, "Invalid arrange_mode_v: %d\n", s->arrange_mode_v);
+        return AVERROR(EINVAL);
+    }
+
+    av_log(ctx, AV_LOG_VERBOSE, "process_area -  arr_v: x/y: (%d:%d) size: %dx%d.\n", target_x, target_y, target_w, target_h);
+
+    area->x = target_x;
+    area->y = target_y;
+
+    if (area->w != target_w || area->h != target_h)
+        return scale_area(inlink, area, target_w, target_h);
+
+    return 0;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    const AVFilterContext *ctx = inlink->dst;
+    SubScaleContext *s = ctx->priv;
+    AVFilterLink *outlink = ctx->outputs[0];
+    int ret;
+
+    // just forward empty frames
+    if (frame->num_subtitle_areas == 0) {
+        av_frame_free(&s->cache_frame);
+        return ff_filter_frame(outlink, frame);
+    }
+
+    if (s->use_caching && s->cache_frame && frame->repeat_sub
+        && s->cache_frame->subtitle_timing.start_pts == frame->subtitle_timing.start_pts) {
+        AVFrame *out = av_frame_clone(s->cache_frame);
+        if (!out)
+            return AVERROR(ENOMEM);
+
+        ret = av_frame_copy_props(out, frame);
+        if (ret < 0)
+            return ret;
+
+        av_log(inlink->dst, AV_LOG_DEBUG, "subscale CACHED - size %dx%d  pts: %"PRId64"  areas: %d\n", frame->width, frame->height, frame->subtitle_timing.start_pts, frame->num_subtitle_areas);
+        av_frame_free(&frame);
+        return ff_filter_frame(outlink, out);
+    }
+
+    ret = av_frame_make_writable(frame);
+    if (ret >= 0) {
+        const AVRational x_factor = { .num = outlink->w, .den = inlink->w} ;
+        const AVRational y_factor = { .num = outlink->h, .den = inlink->h} ;
+
+        for (unsigned i = 0; i < frame->num_subtitle_areas; ++i) {
+            AVSubtitleArea *area = frame->subtitle_areas[i];
+
+            ret = process_area(inlink, area, x_factor, y_factor);
+            if (ret < 0)
+                return ret;
+        }
+
+        av_log(inlink->dst, AV_LOG_DEBUG, "subscale output - size %dx%d  pts: %"PRId64"  areas: %d\n", frame->width, frame->height, frame->subtitle_timing.start_pts, frame->num_subtitle_areas);
+
+        if (s->use_caching) {
+            av_frame_free(&s->cache_frame);
+            s->cache_frame = av_frame_clone(frame);
+        }
+
+        return ff_filter_frame(outlink, frame);
+    }
+
+    return ret;
+}
+
+#define OFFSET(x) offsetof(SubScaleContext, x)
+#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption subscale_options[] = {
+    { "margin_h", "horizontal border",        OFFSET(margin_h_expr),    AV_OPT_TYPE_STRING,        .flags = FLAGS },
+    { "margin_v", "vertical border",          OFFSET(margin_v_expr),    AV_OPT_TYPE_STRING,        .flags = FLAGS },
+    { "w",     "Output video width",          OFFSET(w_expr),    AV_OPT_TYPE_STRING,        .flags = FLAGS },
+    { "width", "Output video width",          OFFSET(w_expr),    AV_OPT_TYPE_STRING,        .flags = FLAGS },
+    { "h",     "Output video height",         OFFSET(h_expr),    AV_OPT_TYPE_STRING,        .flags = FLAGS },
+    { "height","Output video height",         OFFSET(h_expr),    AV_OPT_TYPE_STRING,        .flags = FLAGS },
+    { "force_original_aspect_ratio", "decrease or increase w/h if necessary to keep the original AR", OFFSET(force_original_aspect_ratio), AV_OPT_TYPE_INT, { .i64 = 0}, 0, 2, FLAGS, "force_oar" },
+    { "disable",  NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, 0, 0, FLAGS, "force_oar" },
+    { "decrease", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 1 }, 0, 0, FLAGS, "force_oar" },
+    { "increase", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 2 }, 0, 0, FLAGS, "force_oar" },
+    { "scale_mode", "specify how to scale subtitles", OFFSET(scale_mode), AV_OPT_TYPE_INT, {.i64 = SSM_UNIFORM}, 0, SSM_UNIFORM_NO_REPOSITION, FLAGS, "scale_mode" },
+         { "none",  "no common scaling", 0, AV_OPT_TYPE_CONST, {.i64=SSM_NONE},  .flags = FLAGS, .unit = "scale_mode" },
+         { "uniform",  "uniformly scale and reposition", 0, AV_OPT_TYPE_CONST, {.i64=SSM_UNIFORM},  .flags = FLAGS, .unit = "scale_mode" },
+         { "uniform_no_reposition",  "uniformly scale but keep positions", 0, AV_OPT_TYPE_CONST, {.i64=SSM_UNIFORM_NO_REPOSITION},  .flags = FLAGS, .unit = "scale_mode" },
+    { "use_caching", "Cache output frames",   OFFSET(use_caching), AV_OPT_TYPE_BOOL, { .i64 = 1}, 0, 1, .flags = FLAGS },
+
+    { "arrange_h", "specify how to arrange subtitles horizontally", OFFSET(arrange_mode_h), AV_OPT_TYPE_INT, {.i64 = SAM_NONE}, 0, SAM_SNAPALIGNMENT_AND_SCALE, FLAGS, "arrange" },
+    { "arrange_v", "specify how to arrange subtitles vertically", OFFSET(arrange_mode_v), AV_OPT_TYPE_INT, {.i64 = SAM_NONE}, 0, SAM_SNAPALIGNMENT_AND_SCALE, FLAGS, "arrange" },
+         { "none",  "no repositioning", 0, AV_OPT_TYPE_CONST, {.i64=SAM_NONE},  .flags = FLAGS, .unit = "arrange" },
+         { "margin_no_scale",  "move subs inside border when possible", 0, AV_OPT_TYPE_CONST, {.i64=SAM_ENSUREMARGIN_NO_SCALE,},  .flags = FLAGS, .unit = "arrange" },
+         { "margin_and_scale",  "move subs inside border and scale as needed", 0, AV_OPT_TYPE_CONST, {.i64=SAM_ENSUREMARGIN_AND_SCALE,},  .flags = FLAGS, .unit = "arrange" },
+         { "snapalign_no_scale",  "snap subs to near/far/center when possible", 0, AV_OPT_TYPE_CONST, {.i64=SAM_SNAPALIGNMENT_NO_SCALE,},  .flags = FLAGS, .unit = "arrange" },
+         { "snapalign_and_scale", "snap subs to near/far/center and scale as needed", 0, AV_OPT_TYPE_CONST, {.i64=SAM_SNAPALIGNMENT_AND_SCALE,}, .flags = FLAGS, .unit = "arrange" },
+
+    { "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, {.i64 = EVAL_MODE_INIT}, 0, EVAL_MODE_NB-1, FLAGS, "eval" },
+         { "init",  "eval expressions once during initialization", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_INIT},  .flags = FLAGS, .unit = "eval" },
+         { "frame", "eval expressions during initialization and per-frame", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_FRAME}, .flags = FLAGS, .unit = "eval" },
+    { "num_colors",     "number of palette colors in output", OFFSET(num_output_colors),  AV_OPT_TYPE_INT, {.i64 = 256 }, 2, 256, .flags = FLAGS },
+    { "bitmap_width_align",     "Bitmap width alignment", OFFSET(bitmap_width_align),  AV_OPT_TYPE_INT, {.i64 = 2 }, 1, 256, .flags = FLAGS },
+    { "bitmap_height_align",     "Bitmap height alignment", OFFSET(bitmap_height_align),  AV_OPT_TYPE_INT, {.i64 = 2 }, 1, 256, .flags = FLAGS },
+    { .name =  NULL }
+};
+
+static const AVClass subscale_class = {
+    .class_name       = "subscale",
+    .item_name        = av_default_item_name,
+    .option           = subscale_options,
+    .version          = LIBAVUTIL_VERSION_INT,
+    .category         = AV_CLASS_CATEGORY_FILTER,
+};
+
+static const AVFilterPad inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .filter_frame = filter_frame,
+        .config_props = config_input,
+    },
+};
+
+static const AVFilterPad outputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .config_props = config_output,
+    },
+};
+
+const AVFilter ff_sf_subscale = {
+    .name            = "subscale",
+    .description     = NULL_IF_CONFIG_SMALL("Scale graphical subtitles."),
+    .init_dict       = init_dict,
+    .uninit          = uninit,
+    .priv_size       = sizeof(SubScaleContext),
+    .priv_class      = &subscale_class,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_SINGLE_SUBFMT(AV_SUBTITLE_FMT_BITMAP),
+};
+
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v4 20/23] avfilter/subfeed: add subtitle feed filter
  2022-05-28 13:25     ` [FFmpeg-devel] [PATCH v4 00/23] Subtitle Filtering 2022 ffmpegagent
                         ` (18 preceding siblings ...)
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 19/23] avfilter/subscale: Add filter for scaling and/or re-arranging graphical subtitles softworkz
@ 2022-05-28 13:25       ` softworkz
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 21/23] avcodec/subtitles: Migrate subtitle encoders to frame-based API softworkz
                         ` (3 subsequent siblings)
  23 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-05-28 13:25 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavfilter/Makefile     |   1 +
 libavfilter/allfilters.c |   1 +
 libavfilter/sf_subfeed.c | 402 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 404 insertions(+)
 create mode 100644 libavfilter/sf_subfeed.c

diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index d6a9524b9a..319334790c 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -584,6 +584,7 @@ OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
 OBJS-$(CONFIG_SPLITCC_FILTER)                += sf_splitcc.o
 OBJS-$(CONFIG_STRIPSTYLES_FILTER)            += sf_stripstyles.o
 OBJS-$(CONFIG_SUBSCALE_FILTER)               += sf_subscale.o
+OBJS-$(CONFIG_SUBFEED_FILTER)                += sf_subfeed.o
 
 # multimedia filters
 OBJS-$(CONFIG_ABITSCOPE_FILTER)              += avf_abitscope.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 67a0570561..2d223d2398 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -565,6 +565,7 @@ extern const AVFilter ff_sf_showspeaker;
 extern const AVFilter ff_sf_splitcc;
 extern const AVFilter ff_sf_stripstyles;
 extern const AVFilter ff_sf_subscale;
+extern const AVFilter ff_sf_subfeed;
 extern const AVFilter ff_sf_textmod;
 extern const AVFilter ff_svf_graphicsub2video;
 extern const AVFilter ff_svf_textsub2video;
diff --git a/libavfilter/sf_subfeed.c b/libavfilter/sf_subfeed.c
new file mode 100644
index 0000000000..c7c4dddd38
--- /dev/null
+++ b/libavfilter/sf_subfeed.c
@@ -0,0 +1,402 @@
+/*
+ * 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
+ * subtitle filter for feeding subtitle frames into a filtergraph in a contiguous way
+ *
+ *
+ * also supports
+ *   - duration fixup
+ *     delaying a subtitle event with unknown duration and infer duration from the
+ *     start time of the subsequent subtitle
+ *   - scattering
+ *     splitting a subtitle event with unknown duration into multiple ones with
+ *     a short and fixed duration
+ *
+ */
+
+#include "filters.h"
+#include "libavutil/opt.h"
+#include "subtitles.h"
+#include "libavutil/avassert.h"
+
+enum SubFeedMode {
+    FM_REPEAT,
+    FM_SCATTER,
+    FM_FORWARD,
+};
+
+typedef struct SubFeedContext {
+    const AVClass *class;
+    enum AVSubtitleType format;
+    enum SubFeedMode mode;
+
+    AVRational frame_rate;
+    int fix_durations;
+    int fix_overlap;
+
+    int current_frame_isnew;
+    int eof;
+    int got_first_input;
+    int need_frame;
+    int64_t next_pts_offset;
+    int64_t recent_subtitle_pts;
+
+    int64_t counter;
+
+    /**
+     * Queue of frames waiting to be filtered.
+     */
+    FFFrameQueue fifo;
+
+} SubFeedContext;
+
+static int64_t ms_to_avtb(int64_t ms)
+{
+    return av_rescale_q(ms, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q);
+}
+
+static int64_t avtb_to_ms(int64_t avtb)
+{
+    return av_rescale_q(avtb, AV_TIME_BASE_Q, (AVRational){ 1, 1000 });
+}
+
+static int init(AVFilterContext *ctx)
+{
+    SubFeedContext *s = ctx->priv;
+
+    ff_framequeue_init(&s->fifo, NULL);
+
+    return 0;
+}
+
+static void uninit(AVFilterContext *ctx)
+{
+    SubFeedContext *s = ctx->priv;
+    ff_framequeue_free(&s->fifo);
+}
+
+static int config_input(AVFilterLink *link)
+{
+    ////const subfeedContext *context = link->dst->priv;
+
+    return 0;
+}
+
+static int query_formats(AVFilterContext *ctx)
+{
+    AVFilterFormats *formats;
+    AVFilterLink *inlink0 = ctx->inputs[0];
+    AVFilterLink *outlink0 = ctx->outputs[0];
+    static const enum AVSubtitleType subtitle_fmts[] = { AV_SUBTITLE_FMT_BITMAP, AV_SUBTITLE_FMT_ASS, AV_SUBTITLE_FMT_NB };
+    int ret;
+
+    formats = ff_make_format_list(subtitle_fmts);
+
+    if ((ret = ff_formats_ref(formats, &inlink0->outcfg.formats)) < 0)
+        return ret;
+
+    if ((ret = ff_formats_ref(formats, &outlink0->incfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    SubFeedContext *s = outlink->src->priv;
+    const AVFilterLink *inlink = outlink->src->inputs[0];
+
+    outlink->time_base = AV_TIME_BASE_Q;
+    outlink->format = inlink->format;
+    outlink->w = inlink->w;
+    outlink->h = inlink->h;
+
+    if (s->mode == FM_FORWARD)
+        outlink->frame_rate = (AVRational) { 1, 0 };
+    else
+        outlink->frame_rate = s->frame_rate;
+
+    return 0;
+}
+
+static int request_frame(AVFilterLink *outlink)
+{
+    SubFeedContext *s = outlink->src->priv;
+    AVFilterLink *inlink = outlink->src->inputs[0];
+    int64_t last_pts = outlink->current_pts;
+    int64_t next_pts;
+    int64_t interval = ms_to_avtb((int64_t)(av_q2d(av_inv_q(outlink->frame_rate)) * 1000));
+    AVFrame *out;
+    int status;
+
+    if (s->mode == FM_FORWARD)
+        return ff_request_frame(inlink);
+
+    s->counter++;
+    if (interval == 0)
+        interval =  ms_to_avtb(200);
+
+    status = ff_outlink_get_status(inlink);
+    if (status == AVERROR_EOF)
+        s->eof = 1;
+
+    if (s->eof)
+        return AVERROR_EOF;
+
+    if (!s->got_first_input && inlink->current_pts != AV_NOPTS_VALUE) {
+
+        s->got_first_input = 1;
+        next_pts = av_rescale_q(inlink->current_pts, inlink->time_base, AV_TIME_BASE_Q);
+        if (next_pts < last_pts)
+            next_pts = last_pts + interval;
+
+    } else if (last_pts == AV_NOPTS_VALUE)
+        next_pts = av_rescale_q(inlink->current_pts, inlink->time_base, AV_TIME_BASE_Q);
+    else
+        next_pts = last_pts + interval;
+
+    if (next_pts == AV_NOPTS_VALUE)
+        next_pts = 0;
+
+    if (s->next_pts_offset) {
+        av_log(outlink->src, AV_LOG_VERBOSE, "Subtracting next_pts_offset: %"PRId64" \n", s->next_pts_offset);
+        next_pts -= s->next_pts_offset;
+        s->next_pts_offset = 0;
+    }
+
+retry:
+    if (ff_framequeue_queued_frames(&s->fifo) && !s->current_frame_isnew) {
+
+        const AVFrame *current_frame = ff_framequeue_peek(&s->fifo, 0);
+        const int64_t sub_end_time   = current_frame->subtitle_timing.start_pts + current_frame->subtitle_timing.duration;
+
+        if (ff_framequeue_queued_frames(&s->fifo) > 1) {
+            const AVFrame *next_frame = ff_framequeue_peek(&s->fifo, 1);
+            if (next_pts + interval > next_frame->subtitle_timing.start_pts) {
+                AVFrame *remove_frame = ff_framequeue_take(&s->fifo);
+                av_frame_free(&remove_frame);
+                s->current_frame_isnew = 1;
+                goto retry;
+            }
+        }
+
+        if (next_pts > sub_end_time) {
+            AVFrame *remove_frame = ff_framequeue_take(&s->fifo);
+            av_frame_free(&remove_frame);
+            s->current_frame_isnew = 1;
+            goto retry;
+        }
+    }
+
+    if (ff_framequeue_queued_frames(&s->fifo)) {
+        AVFrame *current_frame = ff_framequeue_peek(&s->fifo, 0);
+
+        if (current_frame && current_frame->subtitle_timing.start_pts <= next_pts + interval) {
+            if (!s->current_frame_isnew)
+                current_frame->repeat_sub++;
+
+            out = av_frame_clone(current_frame);
+
+            if (!out)
+                return AVERROR(ENOMEM);
+
+            if (!s->current_frame_isnew) {
+                out->pts = next_pts;
+            } else {
+                out->pts = out->subtitle_timing.start_pts;
+
+                if (out->pts < next_pts)
+                    out->pts = next_pts;
+
+                s->next_pts_offset = (out->pts - next_pts) % interval;
+            }
+
+            if (s->mode == FM_SCATTER) {
+                const int64_t sub_end_time  = current_frame->subtitle_timing.start_pts + current_frame->subtitle_timing.duration;
+
+                if (s->current_frame_isnew == 1 && current_frame->subtitle_timing.start_pts < out->pts) {
+                    const int64_t diff = out->pts - current_frame->subtitle_timing.start_pts;
+                    current_frame->subtitle_timing.duration -= diff;
+                }
+
+                out->repeat_sub = 0;
+                out->subtitle_timing.start_pts = out->pts;
+                out->subtitle_timing.duration = interval;
+                av_assert1(out->pts >= next_pts);
+                av_assert1(out->pts < next_pts + interval);
+                av_assert1(out->pts < sub_end_time);
+
+                if (out->pts > next_pts)
+                    out->subtitle_timing.duration -= out->pts - next_pts;
+
+                if (sub_end_time < next_pts + interval) {
+                    const int64_t diff = next_pts + interval - sub_end_time;
+                    av_assert1(diff <= out->subtitle_timing.duration);
+                    out->subtitle_timing.duration -= diff;
+                }
+            }
+
+            s->current_frame_isnew = 0;
+            s->recent_subtitle_pts = out->subtitle_timing.start_pts;
+
+            av_log(outlink->src, AV_LOG_DEBUG, "Output1 frame pts: %"PRId64"  subtitle_pts: %"PRId64"  repeat_frame: %d\n",
+                out->pts, out->subtitle_timing.start_pts, out->repeat_sub);
+
+            return ff_filter_frame(outlink, out);
+        }
+    }
+
+    if (ff_framequeue_queued_frames(&s->fifo) == 0) {
+        status = ff_request_frame(inlink);
+        if (status == AVERROR_EOF) {
+            s->eof = 1;
+            return status;
+        }
+
+        if (s->counter > 1 && s->counter % 2)
+            return 0;
+    }
+
+    out = ff_get_subtitles_buffer(outlink, outlink->format);
+    out->pts = next_pts;
+    out->repeat_sub = 1;
+    out->subtitle_timing.start_pts = s->recent_subtitle_pts;
+
+    av_log(outlink->src, AV_LOG_DEBUG, "Output2 frame pts: %"PRId64"  subtitle_pts: %"PRId64"  repeat_frame: %d\n",
+        out->pts, out->subtitle_timing.start_pts, out->repeat_sub);
+
+    return ff_filter_frame(outlink, out);
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFilterContext *ctx        = inlink->dst;
+    SubFeedContext *s           = inlink->dst->priv;
+    AVFilterLink *outlink       = inlink->dst->outputs[0];
+    const int64_t index         = (int64_t)ff_framequeue_queued_frames(&s->fifo) - 1;
+    size_t nb_queued_frames;
+    int ret = 0;
+
+    av_log(ctx, AV_LOG_VERBOSE, "frame.pts: %"PRId64" (AVTB: %"PRId64") -  subtitle_timing.start_pts: %"PRId64" subtitle_timing.duration: %"PRId64" - format: %d\n",
+        frame->pts, av_rescale_q(frame->pts, inlink->time_base, AV_TIME_BASE_Q), frame->subtitle_timing.start_pts, frame->subtitle_timing.duration, frame->format);
+
+    frame->pts = av_rescale_q(frame->pts, inlink->time_base, AV_TIME_BASE_Q);
+
+    if (index < 0) {
+        s->current_frame_isnew = 1;
+    } else if (s->fix_durations || s->fix_overlap) {
+        AVFrame *previous_frame = ff_framequeue_peek(&s->fifo, index);
+        const int64_t pts_diff = frame->subtitle_timing.start_pts - previous_frame->subtitle_timing.start_pts;
+        nb_queued_frames = ff_framequeue_queued_frames(&s->fifo);
+
+        if (s->fix_durations && pts_diff > 0 && previous_frame->subtitle_timing.duration > ms_to_avtb(29000)) {
+            av_log(ctx, AV_LOG_VERBOSE, "Previous frame (index #%"PRId64") has a duration of %"PRId64" ms, setting to  %"PRId64" ms\n",
+                index, avtb_to_ms(previous_frame->subtitle_timing.duration), avtb_to_ms(frame->subtitle_timing.start_pts - previous_frame->subtitle_timing.start_pts));
+            previous_frame->subtitle_timing.duration = frame->subtitle_timing.start_pts - previous_frame->subtitle_timing.start_pts;
+        }
+
+        if (s->fix_overlap && pts_diff > 0 && previous_frame->subtitle_timing.duration > pts_diff) {
+            av_log(ctx, AV_LOG_VERBOSE, "Detected overlap from previous frame (index #%"PRId64") which had a duration of %"PRId64" ms, setting to the pts_diff which is %"PRId64" ms\n",
+                index, avtb_to_ms(previous_frame->subtitle_timing.duration), avtb_to_ms(pts_diff));
+            previous_frame->subtitle_timing.duration = pts_diff;
+        }
+
+        if (pts_diff <= 0) {
+            av_log(ctx, AV_LOG_WARNING, "The pts_diff to the previous frame (index #%"PRId64")  is <= 0: %"PRId64" ms. The previous frame duration is %"PRId64" ms.\n",
+                index, avtb_to_ms(pts_diff),  avtb_to_ms(previous_frame->subtitle_timing.duration));
+        }
+    }
+
+    ff_framequeue_add(&s->fifo, frame);
+
+    nb_queued_frames = ff_framequeue_queued_frames(&s->fifo);
+
+    if (nb_queued_frames > 3)
+        av_log(ctx, AV_LOG_WARNING, "frame queue count: %zu\n", nb_queued_frames);
+
+    if (s->mode == FM_FORWARD && nb_queued_frames) {
+
+        AVFrame *first_frame = ff_framequeue_peek(&s->fifo, 0);
+
+        if (s->fix_overlap && nb_queued_frames < 2) {
+          av_log(ctx, AV_LOG_VERBOSE, "Return no frame since we have less than 2\n");
+          return 0;
+        }
+
+        if (s->fix_durations && first_frame->subtitle_timing.duration > ms_to_avtb(29000)) {
+            av_log(ctx, AV_LOG_VERBOSE, "Return no frame because first frame duration is %"PRId64" ms\n", avtb_to_ms(first_frame->subtitle_timing.duration));
+            return 0;
+        }
+
+        first_frame = ff_framequeue_take(&s->fifo);
+        return ff_filter_frame(outlink, first_frame);
+    }
+
+    return ret;
+}
+
+#define OFFSET(x) offsetof(SubFeedContext, x)
+#define FLAGS (AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption subfeed_options[] = {
+    { "fix_durations", "delay output and determine duration from next frame", OFFSET(fix_durations),   AV_OPT_TYPE_BOOL,   { .i64=1 },                  0, 1, FLAGS, NULL     },
+    { "fix_overlap", "delay output and adjust durations to prevent overlap", OFFSET(fix_overlap),   AV_OPT_TYPE_BOOL,   { .i64=0 },                  0, 1, FLAGS, NULL     },
+    { "mode",       "set feed mode",         OFFSET(mode),      AV_OPT_TYPE_INT,                 { .i64=FM_REPEAT },  FM_REPEAT, FM_FORWARD,  FLAGS, "mode" },
+    {   "repeat",     "repeat recent while valid, send empty otherwise",   0, AV_OPT_TYPE_CONST, { .i64=FM_REPEAT },  0,                  0,  FLAGS, "mode" },
+    {   "scatter",    "subdivide subtitles into 1/framerate segments",     0, AV_OPT_TYPE_CONST, { .i64=FM_SCATTER }, 0,                  0,  FLAGS, "mode" },
+    {   "forward",    "forward only (clears output framerate)",            0, AV_OPT_TYPE_CONST, { .i64=FM_FORWARD }, 0,                  0,  FLAGS, "mode" },
+    { "rate",       "output frame rate",     OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE,         { .str = "5"},       0,         INT_MAX,     FLAGS, NULL },\
+    { "r",          "output frame rate",     OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE,         { .str = "5"},       0,         INT_MAX,     FLAGS, NULL },\
+    { NULL },
+};
+
+AVFILTER_DEFINE_CLASS(subfeed);
+
+static const AVFilterPad inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .filter_frame = filter_frame,
+        .config_props = config_input,
+    },
+};
+
+static const AVFilterPad outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_SUBTITLE,
+        .request_frame = request_frame,
+        .config_props  = config_output,
+    },
+};
+
+const AVFilter ff_sf_subfeed = {
+    .name           = "subfeed",
+    .description    = NULL_IF_CONFIG_SMALL("Control subtitle frame timing and flow in a filtergraph"),
+    .init           = init,
+    .uninit         = uninit,
+    .priv_size      = sizeof(SubFeedContext),
+    .priv_class     = &subfeed_class,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_QUERY_FUNC(query_formats),
+};
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v4 21/23] avcodec/subtitles: Migrate subtitle encoders to frame-based API
  2022-05-28 13:25     ` [FFmpeg-devel] [PATCH v4 00/23] Subtitle Filtering 2022 ffmpegagent
                         ` (19 preceding siblings ...)
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 20/23] avfilter/subfeed: add subtitle feed filter softworkz
@ 2022-05-28 13:25       ` softworkz
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 22/23] fftools/ffmpeg: Introduce subtitle filtering and new frame-based subtitle encoding softworkz
                         ` (2 subsequent siblings)
  23 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-05-28 13:25 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

and provide a compatibility shim for the legacy api

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/assenc.c         | 189 ++++++++++++++++++++++++++++++------
 libavcodec/avcodec.h        |   5 +-
 libavcodec/codec_internal.h |  12 ---
 libavcodec/dvbsubenc.c      |  96 ++++++++++--------
 libavcodec/dvdsubenc.c      | 102 +++++++++++--------
 libavcodec/encode.c         |  61 +++++++++++-
 libavcodec/movtextenc.c     | 114 ++++++++++++++++------
 libavcodec/srtenc.c         | 108 ++++++++++++++-------
 libavcodec/tests/avcodec.c  |   5 +-
 libavcodec/ttmlenc.c        | 101 ++++++++++++++-----
 libavcodec/utils.c          |   1 -
 libavcodec/webvttenc.c      |  86 +++++++++++-----
 libavcodec/xsubenc.c        |  88 ++++++++++-------
 13 files changed, 688 insertions(+), 280 deletions(-)

diff --git a/libavcodec/assenc.c b/libavcodec/assenc.c
index 391d690133..5067244e77 100644
--- a/libavcodec/assenc.c
+++ b/libavcodec/assenc.c
@@ -25,69 +25,194 @@
 
 #include "avcodec.h"
 #include "codec_internal.h"
+#include "encode.h"
 #include "libavutil/ass_internal.h"
 #include "libavutil/avstring.h"
 #include "libavutil/internal.h"
 #include "libavutil/mem.h"
 
+typedef struct {
+    AVCodecContext *avctx;
+    AVFrame* current_frame;
+    int have_frame;
+    int current_area;
+} AssEncContext;
+
+static void check_write_header(AVCodecContext* avctx, const AVFrame* frame)
+{
+    if (avctx->extradata_size)
+        return;
+
+    if (frame->subtitle_header && frame->subtitle_header->size > 0) {
+        const char* subtitle_header = (char*)frame->subtitle_header->data;
+        avctx->extradata_size = strlen(subtitle_header);
+        avctx->extradata = av_mallocz(frame->subtitle_header->size + 1);
+        memcpy(avctx->extradata, subtitle_header, avctx->extradata_size);
+        avctx->extradata[avctx->extradata_size] = 0;
+    }
+
+    if (!avctx->extradata_size) {
+        const char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+        if (!subtitle_header)
+            return;
+
+        avctx->extradata_size = strlen(subtitle_header);
+        avctx->extradata = av_mallocz(avctx->extradata_size + 1);
+        memcpy(avctx->extradata, subtitle_header, avctx->extradata_size);
+        avctx->extradata[avctx->extradata_size] = 0;
+        av_freep(&subtitle_header);
+    }
+}
+
 static av_cold int ass_encode_init(AVCodecContext *avctx)
 {
-    avctx->extradata = av_malloc(avctx->subtitle_header_size + 1);
-    if (!avctx->extradata)
-        return AVERROR(ENOMEM);
-    memcpy(avctx->extradata, avctx->subtitle_header, avctx->subtitle_header_size);
-    avctx->extradata_size = avctx->subtitle_header_size;
-    avctx->extradata[avctx->extradata_size] = 0;
+    AssEncContext *s = avctx->priv_data;
+
+    if (avctx->subtitle_header_size) {
+        avctx->extradata = av_malloc(avctx->subtitle_header_size + 1);
+        if (!avctx->extradata)
+            return AVERROR(ENOMEM);
+        memcpy(avctx->extradata, avctx->subtitle_header, avctx->subtitle_header_size);
+        avctx->extradata_size                   = avctx->subtitle_header_size;
+        avctx->extradata[avctx->extradata_size] = 0;
+    }
+
+    s->current_frame = av_frame_alloc();
+    return 0;
+}
+
+static av_cold int ass_encode_close(AVCodecContext *avctx)
+{
+    AssEncContext *s = avctx->priv_data;
+    av_frame_free(&s->current_frame);
     return 0;
 }
 
-static int ass_encode_frame(AVCodecContext *avctx,
-                            unsigned char *buf, int bufsize,
-                            const AVSubtitle *sub)
+////static int ass_encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+////                            const AVFrame* frame, int* got_packet)
+////{
+////    int ret;
+////    size_t req_len = 0, total_len = 0;
+////
+////    check_write_header(avctx, frame);
+////
+////    for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+////        const char *ass = frame->subtitle_areas[i]->ass;
+////
+////        if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) {
+////            av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
+////            return AVERROR(EINVAL);
+////        }
+////
+////        if (ass)
+////            req_len += strlen(ass);
+////    }
+////
+////    ret = ff_get_encode_buffer(avctx, avpkt, req_len + 1, 0);
+////    if (ret < 0) {
+////        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+////        return ret;
+////    }
+////
+////    for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+////        const char *ass = frame->subtitle_areas[i]->ass;
+////
+////        if (ass) {
+////            size_t len = av_strlcpy((char *)avpkt->data + total_len, ass, avpkt->size - total_len);
+////            total_len += len;
+////        }
+////    }
+////
+////    avpkt->size = total_len;
+////    *got_packet = total_len > 0;
+////
+////    return 0;
+////}
+
+static int ass_receive_packet(AVCodecContext *avctx, AVPacket *avpkt)
 {
-    int i, len, total_len = 0;
+    AssEncContext *s = avctx->priv_data;
+    int ret;
+
+    if (!s->have_frame) {
+        s->current_area = 0;
+        ret = ff_encode_get_frame(avctx, s->current_frame);
+
+        if (ret < 0) {
+            av_frame_unref(s->current_frame);
+            return ret;
+        }
+
+        s->have_frame = 1;
+    }
 
-    for (i=0; i<sub->num_rects; i++) {
-        const char *ass = sub->rects[i]->ass;
+    check_write_header(avctx, s->current_frame);
 
-        if (sub->rects[i]->type != SUBTITLE_ASS) {
-            av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
+    if (s->current_frame->repeat_sub) {
+        av_frame_unref(s->current_frame);
+        s->have_frame = 0;
+        return AVERROR(EAGAIN);
+    }
+
+    if (s->current_area < s->current_frame->num_subtitle_areas) {
+        const AVSubtitleArea *area = s->current_frame->subtitle_areas[s->current_area];
+        const char *ass = area->ass;
+
+        if (area->type != AV_SUBTITLE_FMT_ASS) {
+            av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
             return AVERROR(EINVAL);
         }
 
-        len = av_strlcpy(buf+total_len, ass, bufsize-total_len);
+        if (ass) {
+            size_t len = strlen(ass);
+
+            ret = ff_get_encode_buffer(avctx, avpkt, len + 1, 0);
+            if (ret < 0) {
+                av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+                return ret;
+            }
+
+            len = av_strlcpy((char *)avpkt->data, ass, avpkt->size);
 
-        if (len > bufsize-total_len-1) {
-            av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n");
-            return AVERROR_BUFFER_TOO_SMALL;
+            avpkt->size = len;
         }
 
-        total_len += len;
+        s->current_area++;
     }
 
-    return total_len;
+    if (s->current_area < s->current_frame->num_subtitle_areas)
+        return 0;
+
+    av_frame_unref(s->current_frame);
+    s->have_frame = 0;
+
+    return 0;
 }
 
 #if CONFIG_SSA_ENCODER
 const FFCodec ff_ssa_encoder = {
-    .p.name       = "ssa",
-    .p.long_name  = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"),
-    .p.type       = AVMEDIA_TYPE_SUBTITLE,
-    .p.id         = AV_CODEC_ID_ASS,
-    .init         = ass_encode_init,
-    FF_CODEC_ENCODE_SUB_CB(ass_encode_frame),
+    .p.name           = "ssa",
+    .p.long_name      = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"),
+    .p.type           = AVMEDIA_TYPE_SUBTITLE,
+    .p.id             = AV_CODEC_ID_ASS,
+    .priv_data_size = sizeof(AssEncContext),
+    .init           = ass_encode_init,
+    .close          = ass_encode_close,
+    FF_CODEC_RECEIVE_PACKET_CB(ass_receive_packet),
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
 };
 #endif
 
 #if CONFIG_ASS_ENCODER
 const FFCodec ff_ass_encoder = {
-    .p.name       = "ass",
-    .p.long_name  = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"),
-    .p.type       = AVMEDIA_TYPE_SUBTITLE,
-    .p.id         = AV_CODEC_ID_ASS,
-    .init         = ass_encode_init,
-    FF_CODEC_ENCODE_SUB_CB(ass_encode_frame),
+    .p.name           = "ass",
+    .p.long_name      = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"),
+    .p.type           = AVMEDIA_TYPE_SUBTITLE,
+    .priv_data_size = sizeof(AssEncContext),
+    .p.id             = AV_CODEC_ID_ASS,
+    .init           = ass_encode_init,
+    .close          = ass_encode_close,
+    FF_CODEC_RECEIVE_PACKET_CB(ass_receive_packet),
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
 };
 #endif
diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index de87b0406b..8a8efe6e4b 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -3014,10 +3014,13 @@ void av_parser_close(AVCodecParserContext *s);
  * @{
  */
 
+ /**
+  * @deprecated Use @ref avcodec_encode_subtitle2() instead.
+  */
+attribute_deprecated
 int avcodec_encode_subtitle(AVCodecContext *avctx, uint8_t *buf, int buf_size,
                             const AVSubtitle *sub);
 
-
 /**
  * @}
  */
diff --git a/libavcodec/codec_internal.h b/libavcodec/codec_internal.h
index 5df286ce52..7223f41757 100644
--- a/libavcodec/codec_internal.h
+++ b/libavcodec/codec_internal.h
@@ -101,9 +101,6 @@ enum FFCodecType {
     /* The codec is an encoder using the encode callback;
      * audio and video codecs only. */
     FF_CODEC_CB_TYPE_ENCODE,
-    /* The codec is an encoder using the encode_sub callback;
-     * subtitle codecs only. */
-    FF_CODEC_CB_TYPE_ENCODE_SUB,
     /* The codec is an encoder using the receive_packet callback;
      * audio and video codecs only. */
     FF_CODEC_CB_TYPE_RECEIVE_PACKET,
@@ -206,12 +203,6 @@ typedef struct FFCodec {
          */
         int (*encode)(struct AVCodecContext *avctx, struct AVPacket *avpkt,
                       const struct AVFrame *frame, int *got_packet_ptr);
-        /**
-         * Encode subtitles to a raw buffer.
-         * cb is in this state if cb_type is FF_CODEC_CB_TYPE_ENCODE_SUB.
-         */
-        int (*encode_sub)(struct AVCodecContext *avctx, uint8_t *buf,
-                          int buf_size, const struct AVSubtitle *sub);
         /**
          * Encode API with decoupled frame/packet dataflow.
          * cb is in this state if cb_type is FF_CODEC_CB_TYPE_RECEIVE_PACKET.
@@ -263,9 +254,6 @@ typedef struct FFCodec {
 #define FF_CODEC_ENCODE_CB(func)                          \
     .cb_type           = FF_CODEC_CB_TYPE_ENCODE,         \
     .cb.encode         = (func)
-#define FF_CODEC_ENCODE_SUB_CB(func)                      \
-    .cb_type           = FF_CODEC_CB_TYPE_ENCODE_SUB,     \
-    .cb.encode_sub     = (func)
 #define FF_CODEC_RECEIVE_PACKET_CB(func)                  \
     .cb_type           = FF_CODEC_CB_TYPE_RECEIVE_PACKET, \
     .cb.receive_packet = (func)
diff --git a/libavcodec/dvbsubenc.c b/libavcodec/dvbsubenc.c
index 06087b058d..3f4655bebe 100644
--- a/libavcodec/dvbsubenc.c
+++ b/libavcodec/dvbsubenc.c
@@ -21,6 +21,7 @@
 #include "avcodec.h"
 #include "bytestream.h"
 #include "codec_internal.h"
+#include "encode.h"
 #include "libavutil/colorspace.h"
 
 typedef struct DVBSubtitleContext {
@@ -269,21 +270,29 @@ static int dvb_encode_rle8(uint8_t **pq, int buf_size,
     return len;
 }
 
-static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
-                         const AVSubtitle *h)
+static int dvbsub_encode(AVCodecContext* avctx, AVPacket* avpkt,
+                         const AVFrame* frame, int* got_packet)
 {
     DVBSubtitleContext *s = avctx->priv_data;
     uint8_t *q, *pseg_len;
     int page_id, region_id, clut_id, object_id, i, bpp_index, page_state;
-
-
-    q = outbuf;
+    size_t buf_size;
+    int ret;
 
     page_id = 1;
 
-    if (h->num_rects && !h->rects)
+    if (frame->num_subtitle_areas && !frame->subtitle_areas)
         return AVERROR(EINVAL);
 
+    ret = ff_get_encode_buffer(avctx, avpkt, 1024 * 1024, 0);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+        return ret;
+    }
+
+    buf_size = avpkt->size;
+    q = avpkt->data;
+
     if (avctx->width > 0 && avctx->height > 0) {
         if (buf_size < 11)
             return AVERROR_BUFFER_TOO_SMALL;
@@ -302,7 +311,7 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
 
     /* page composition segment */
 
-    if (buf_size < 8 + h->num_rects * 6)
+    if (buf_size < 8 + frame->num_subtitle_areas * 6)
         return AVERROR_BUFFER_TOO_SMALL;
     *q++ = 0x0f; /* sync_byte */
     *q++ = 0x10; /* segment_type */
@@ -314,30 +323,30 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
     /* page_version = 0 + page_state */
     *q++ = (s->object_version << 4) | (page_state << 2) | 3;
 
-    for (region_id = 0; region_id < h->num_rects; region_id++) {
+    for (region_id = 0; region_id < frame->num_subtitle_areas; region_id++) {
         *q++ = region_id;
         *q++ = 0xff; /* reserved */
-        bytestream_put_be16(&q, h->rects[region_id]->x); /* left pos */
-        bytestream_put_be16(&q, h->rects[region_id]->y); /* top pos */
+        bytestream_put_be16(&q, frame->subtitle_areas[region_id]->x); /* left pos */
+        bytestream_put_be16(&q, frame->subtitle_areas[region_id]->y); /* top pos */
     }
 
     bytestream_put_be16(&pseg_len, q - pseg_len - 2);
-    buf_size -= 8 + h->num_rects * 6;
+    buf_size -= 8 + frame->num_subtitle_areas * 6;
 
-    if (h->num_rects) {
-        for (clut_id = 0; clut_id < h->num_rects; clut_id++) {
-            if (buf_size < 6 + h->rects[clut_id]->nb_colors * 6)
+    if (frame->num_subtitle_areas) {
+        for (clut_id = 0; clut_id < frame->num_subtitle_areas; clut_id++) {
+            if (buf_size < 6 + frame->subtitle_areas[clut_id]->nb_colors * 6)
                 return AVERROR_BUFFER_TOO_SMALL;
 
             /* CLUT segment */
 
-            if (h->rects[clut_id]->nb_colors <= 4) {
+            if (frame->subtitle_areas[clut_id]->nb_colors <= 4) {
                 /* 2 bpp, some decoders do not support it correctly */
                 bpp_index = 0;
-            } else if (h->rects[clut_id]->nb_colors <= 16) {
+            } else if (frame->subtitle_areas[clut_id]->nb_colors <= 16) {
                 /* 4 bpp, standard encoding */
                 bpp_index = 1;
-            } else if (h->rects[clut_id]->nb_colors <= 256) {
+            } else if (frame->subtitle_areas[clut_id]->nb_colors <= 256) {
                 /* 8 bpp, standard encoding */
                 bpp_index = 2;
             } else {
@@ -354,12 +363,12 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
             *q++ = clut_id;
             *q++ = (0 << 4) | 0xf; /* version = 0 */
 
-            for(i = 0; i < h->rects[clut_id]->nb_colors; i++) {
+            for(i = 0; i < frame->subtitle_areas[clut_id]->nb_colors; i++) {
                 *q++ = i; /* clut_entry_id */
                 *q++ = (1 << (7 - bpp_index)) | (0xf << 1) | 1; /* 2 bits/pixel full range */
                 {
                     int a, r, g, b;
-                    uint32_t x= ((uint32_t*)h->rects[clut_id]->data[1])[i];
+                    uint32_t x= ((uint32_t*)frame->subtitle_areas[clut_id]->pal)[i];
                     a = (x >> 24) & 0xff;
                     r = (x >> 16) & 0xff;
                     g = (x >>  8) & 0xff;
@@ -373,22 +382,22 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
             }
 
             bytestream_put_be16(&pseg_len, q - pseg_len - 2);
-            buf_size -= 6 + h->rects[clut_id]->nb_colors * 6;
+            buf_size -= 6 + frame->subtitle_areas[clut_id]->nb_colors * 6;
         }
 
-        if (buf_size < h->num_rects * 22)
+        if (buf_size < frame->num_subtitle_areas * 22)
             return AVERROR_BUFFER_TOO_SMALL;
-        for (region_id = 0; region_id < h->num_rects; region_id++) {
+        for (region_id = 0; region_id < frame->num_subtitle_areas; region_id++) {
 
             /* region composition segment */
 
-            if (h->rects[region_id]->nb_colors <= 4) {
+            if (frame->subtitle_areas[region_id]->nb_colors <= 4) {
                 /* 2 bpp, some decoders do not support it correctly */
                 bpp_index = 0;
-            } else if (h->rects[region_id]->nb_colors <= 16) {
+            } else if (frame->subtitle_areas[region_id]->nb_colors <= 16) {
                 /* 4 bpp, standard encoding */
                 bpp_index = 1;
-            } else if (h->rects[region_id]->nb_colors <= 256) {
+            } else if (frame->subtitle_areas[region_id]->nb_colors <= 256) {
                 /* 8 bpp, standard encoding */
                 bpp_index = 2;
             } else {
@@ -402,8 +411,8 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
             q += 2; /* segment length */
             *q++ = region_id;
             *q++ = (s->object_version << 4) | (0 << 3) | 0x07; /* version , no fill */
-            bytestream_put_be16(&q, h->rects[region_id]->w); /* region width */
-            bytestream_put_be16(&q, h->rects[region_id]->h); /* region height */
+            bytestream_put_be16(&q, frame->subtitle_areas[region_id]->w); /* region width */
+            bytestream_put_be16(&q, frame->subtitle_areas[region_id]->h); /* region height */
             *q++ = ((1 + bpp_index) << 5) | ((1 + bpp_index) << 2) | 0x03;
             *q++ = region_id; /* clut_id == region_id */
             *q++ = 0; /* 8 bit fill colors */
@@ -417,9 +426,9 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
 
             bytestream_put_be16(&pseg_len, q - pseg_len - 2);
         }
-        buf_size -= h->num_rects * 22;
+        buf_size -= frame->num_subtitle_areas * 22;
 
-        for (object_id = 0; object_id < h->num_rects; object_id++) {
+        for (object_id = 0; object_id < frame->num_subtitle_areas; object_id++) {
             int (*dvb_encode_rle)(uint8_t **pq, int buf_size,
                                   const uint8_t *bitmap, int linesize,
                                   int w, int h);
@@ -428,13 +437,13 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
                 return AVERROR_BUFFER_TOO_SMALL;
 
             /* bpp_index maths */
-            if (h->rects[object_id]->nb_colors <= 4) {
+            if (frame->subtitle_areas[object_id]->nb_colors <= 4) {
                 /* 2 bpp, some decoders do not support it correctly */
                 dvb_encode_rle = dvb_encode_rle2;
-            } else if (h->rects[object_id]->nb_colors <= 16) {
+            } else if (frame->subtitle_areas[object_id]->nb_colors <= 16) {
                 /* 4 bpp, standard encoding */
                 dvb_encode_rle = dvb_encode_rle4;
-            } else if (h->rects[object_id]->nb_colors <= 256) {
+            } else if (frame->subtitle_areas[object_id]->nb_colors <= 256) {
                 /* 8 bpp, standard encoding */
                 dvb_encode_rle = dvb_encode_rle8;
             } else {
@@ -464,19 +473,19 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
 
                 top_ptr = q;
                 ret = dvb_encode_rle(&q, buf_size,
-                                     h->rects[object_id]->data[0],
-                                     h->rects[object_id]->w * 2,
-                                     h->rects[object_id]->w,
-                                     h->rects[object_id]->h >> 1);
+                                     frame->subtitle_areas[object_id]->buf[0]->data,
+                                     frame->subtitle_areas[object_id]->w * 2,
+                                     frame->subtitle_areas[object_id]->w,
+                                     frame->subtitle_areas[object_id]->h >> 1);
                 if (ret < 0)
                     return ret;
                 buf_size -= ret;
                 bottom_ptr = q;
                 ret = dvb_encode_rle(&q, buf_size,
-                                     h->rects[object_id]->data[0] + h->rects[object_id]->w,
-                                     h->rects[object_id]->w * 2,
-                                     h->rects[object_id]->w,
-                                     h->rects[object_id]->h >> 1);
+                                     frame->subtitle_areas[object_id]->buf[0]->data + frame->subtitle_areas[object_id]->w,
+                                     frame->subtitle_areas[object_id]->w * 2,
+                                     frame->subtitle_areas[object_id]->w,
+                                     frame->subtitle_areas[object_id]->h >> 1);
                 if (ret < 0)
                     return ret;
                 buf_size -= ret;
@@ -503,7 +512,10 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
     buf_size -= 6;
 
     s->object_version = (s->object_version + 1) & 0xf;
-    return q - outbuf;
+    avpkt->size = q - avpkt->data;
+    *got_packet = 1;
+
+    return 0;
 }
 
 const FFCodec ff_dvbsub_encoder = {
@@ -512,5 +524,5 @@ const FFCodec ff_dvbsub_encoder = {
     .p.type         = AVMEDIA_TYPE_SUBTITLE,
     .p.id           = AV_CODEC_ID_DVB_SUBTITLE,
     .priv_data_size = sizeof(DVBSubtitleContext),
-    FF_CODEC_ENCODE_SUB_CB(dvbsub_encode),
+    FF_CODEC_ENCODE_CB(dvbsub_encode),
 };
diff --git a/libavcodec/dvdsubenc.c b/libavcodec/dvdsubenc.c
index 24da94faee..d8a86ee098 100644
--- a/libavcodec/dvdsubenc.c
+++ b/libavcodec/dvdsubenc.c
@@ -21,6 +21,7 @@
 #include "avcodec.h"
 #include "bytestream.h"
 #include "codec_internal.h"
+#include "encode.h"
 #include "internal.h"
 #include "libavutil/avassert.h"
 #include "libavutil/bprint.h"
@@ -115,15 +116,14 @@ static int color_distance(uint32_t a, uint32_t b)
  * Count colors used in a rectangle, quantizing alpha and grouping by
  * nearest global palette entry.
  */
-static void count_colors(AVCodecContext *avctx, unsigned hits[33],
-                         const AVSubtitleRect *r)
+static void count_colors(const AVCodecContext *avctx, unsigned hits[33],
+                         const AVSubtitleArea *r)
 {
     DVDSubtitleContext *dvdc = avctx->priv_data;
     unsigned count[256] = { 0 };
-    uint32_t *palette = (uint32_t *)r->data[1];
     uint32_t color;
     int x, y, i, j, match, d, best_d, av_uninit(best_j);
-    uint8_t *p = r->data[0];
+    uint8_t *p = r->buf[0]->data;
 
     for (y = 0; y < r->h; y++) {
         for (x = 0; x < r->w; x++)
@@ -133,7 +133,7 @@ static void count_colors(AVCodecContext *avctx, unsigned hits[33],
     for (i = 0; i < 256; i++) {
         if (!count[i]) /* avoid useless search */
             continue;
-        color = palette[i];
+        color = r->pal[i];
         /* 0: transparent, 1-16: semi-transparent, 17-33 opaque */
         match = color < 0x33000000 ? 0 : color < 0xCC000000 ? 1 : 17;
         if (match) {
@@ -233,13 +233,13 @@ static void build_color_map(AVCodecContext *avctx, int cmap[],
     }
 }
 
-static void copy_rectangle(AVSubtitleRect *dst, AVSubtitleRect *src, int cmap[])
+static void copy_rectangle(AVSubtitleArea*dst, AVSubtitleArea *src, int cmap[])
 {
     int x, y;
     uint8_t *p, *q;
 
-    p = src->data[0];
-    q = dst->data[0] + (src->x - dst->x) +
+    p = src->buf[0]->data;
+    q = dst->buf[0]->data + (src->x - dst->x) +
                             (src->y - dst->y) * dst->linesize[0];
     for (y = 0; y < src->h; y++) {
         for (x = 0; x < src->w; x++)
@@ -249,51 +249,57 @@ static void copy_rectangle(AVSubtitleRect *dst, AVSubtitleRect *src, int cmap[])
     }
 }
 
-static int encode_dvd_subtitles(AVCodecContext *avctx,
-                                uint8_t *outbuf, int outbuf_size,
-                                const AVSubtitle *h)
+static int encode_dvd_subtitles(AVCodecContext* avctx, AVPacket* avpkt,
+                                const AVFrame* frame, int* got_packet)
 {
     DVDSubtitleContext *dvdc = avctx->priv_data;
     uint8_t *q, *qq;
     int offset1, offset2;
-    int i, rects = h->num_rects, ret;
+    int ret = 0;
+    unsigned i, rects = frame->num_subtitle_areas;
     unsigned global_palette_hits[33] = { 0 };
     int cmap[256];
     int out_palette[4];
     int out_alpha[4];
-    AVSubtitleRect vrect;
-    uint8_t *vrect_data = NULL;
+    AVSubtitleArea vrect;
+    uint8_t* vrect_data = NULL, *outbuf;
     int x2, y2;
     int forced = 0;
+    int outbuf_size;
+    int64_t duration_ms;
 
-    if (rects == 0 || !h->rects)
+    if (rects == 0)
+        return 0;
+
+    if (!frame->subtitle_areas)
         return AVERROR(EINVAL);
+
     for (i = 0; i < rects; i++)
-        if (h->rects[i]->type != AV_SUBTITLE_FMT_BITMAP) {
+        if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_BITMAP) {
             av_log(avctx, AV_LOG_ERROR, "Bitmap subtitle required\n");
             return AVERROR(EINVAL);
         }
     /* Mark this subtitle forced if any of the rectangles is forced. */
     for (i = 0; i < rects; i++)
-        if ((h->rects[i]->flags & AV_SUBTITLE_FLAG_FORCED) != 0) {
+        if ((frame->subtitle_areas[i]->flags & AV_SUBTITLE_FLAG_FORCED) != 0) {
             forced = 1;
             break;
         }
 
-    vrect = *h->rects[0];
+    vrect = *frame->subtitle_areas[0];
 
     if (rects > 1) {
         /* DVD subtitles can have only one rectangle: build a virtual
            rectangle containing all actual rectangles.
            The data of the rectangles will be copied later, when the palette
            is decided, because the rectangles may have different palettes. */
-        int xmin = h->rects[0]->x, xmax = xmin + h->rects[0]->w;
-        int ymin = h->rects[0]->y, ymax = ymin + h->rects[0]->h;
+        int xmin = frame->subtitle_areas[0]->x, xmax = xmin + frame->subtitle_areas[0]->w;
+        int ymin = frame->subtitle_areas[0]->y, ymax = ymin + frame->subtitle_areas[0]->h;
         for (i = 1; i < rects; i++) {
-            xmin = FFMIN(xmin, h->rects[i]->x);
-            ymin = FFMIN(ymin, h->rects[i]->y);
-            xmax = FFMAX(xmax, h->rects[i]->x + h->rects[i]->w);
-            ymax = FFMAX(ymax, h->rects[i]->y + h->rects[i]->h);
+            xmin = FFMIN(xmin, frame->subtitle_areas[i]->x);
+            ymin = FFMIN(ymin, frame->subtitle_areas[i]->y);
+            xmax = FFMAX(xmax, frame->subtitle_areas[i]->x + frame->subtitle_areas[i]->w);
+            ymax = FFMAX(ymax, frame->subtitle_areas[i]->y + frame->subtitle_areas[i]->h);
         }
         vrect.x = xmin;
         vrect.y = ymin;
@@ -305,27 +311,29 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
         /* Count pixels outside the virtual rectangle as transparent */
         global_palette_hits[0] = vrect.w * vrect.h;
         for (i = 0; i < rects; i++)
-            global_palette_hits[0] -= h->rects[i]->w * h->rects[i]->h;
+            global_palette_hits[0] -= frame->subtitle_areas[i]->w * frame->subtitle_areas[i]->h;
     }
 
     for (i = 0; i < rects; i++)
-        count_colors(avctx, global_palette_hits, h->rects[i]);
+        count_colors(avctx, global_palette_hits, frame->subtitle_areas[i]);
     select_palette(avctx, out_palette, out_alpha, global_palette_hits);
 
     if (rects > 1) {
-        if (!(vrect_data = av_calloc(vrect.w, vrect.h)))
+
+        vrect.buf[0] = av_buffer_allocz((size_t)vrect.w * vrect.h);
+        if (!vrect.buf[0])
             return AVERROR(ENOMEM);
-        vrect.data    [0] = vrect_data;
+
         vrect.linesize[0] = vrect.w;
         for (i = 0; i < rects; i++) {
-            build_color_map(avctx, cmap, (uint32_t *)h->rects[i]->data[1],
+            build_color_map(avctx, cmap, frame->subtitle_areas[i]->pal,
                             out_palette, out_alpha);
-            copy_rectangle(&vrect, h->rects[i], cmap);
+            copy_rectangle(&vrect, frame->subtitle_areas[i], cmap);
         }
         for (i = 0; i < 4; i++)
             cmap[i] = i;
     } else {
-        build_color_map(avctx, cmap, (uint32_t *)h->rects[0]->data[1],
+        build_color_map(avctx, cmap, frame->subtitle_areas[0]->pal,
                         out_palette, out_alpha);
     }
 
@@ -336,6 +344,16 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
                out_palette[i], out_alpha[i] >> 4);
     av_log(avctx, AV_LOG_DEBUG, "\n");
 
+
+    ret = ff_get_encode_buffer(avctx, avpkt, 4 + vrect.w * vrect.h / 2 + 17 + 21, 0);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+        return ret;
+    }
+
+    outbuf_size = avpkt->size;
+    outbuf = avpkt->data;
+
     // encode data block
     q = outbuf + 4;
     offset1 = q - outbuf;
@@ -345,10 +363,10 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
         ret = AVERROR_BUFFER_TOO_SMALL;
         goto fail;
     }
-    dvd_encode_rle(&q, vrect.data[0], vrect.w * 2,
+    dvd_encode_rle(&q, vrect.buf[0]->data, vrect.w * 2,
                    vrect.w, (vrect.h + 1) >> 1, cmap);
     offset2 = q - outbuf;
-    dvd_encode_rle(&q, vrect.data[0] + vrect.w, vrect.w * 2,
+    dvd_encode_rle(&q, vrect.buf[0]->data + vrect.w, vrect.w * 2,
                    vrect.w, vrect.h >> 1, cmap);
 
     if (dvdc->even_rows_fix && (vrect.h & 1)) {
@@ -363,7 +381,7 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
     bytestream_put_be16(&qq, q - outbuf);
 
     // send start display command
-    bytestream_put_be16(&q, (h->start_display_time*90) >> 10);
+    bytestream_put_be16(&q, 0);
     bytestream_put_be16(&q, (q - outbuf) /*- 2 */ + 8 + 12 + 2);
     *q++ = 0x03; // palette - 4 nibbles
     *q++ = (out_palette[3] << 4) | out_palette[2];
@@ -401,7 +419,8 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
     *q++ = 0xff; // terminating command
 
     // send stop display command last
-    bytestream_put_be16(&q, (h->end_display_time*90) >> 10);
+    duration_ms = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, (AVRational){ 1, 1000 });
+    bytestream_put_be16(&q, (duration_ms*90) >> 10);
     bytestream_put_be16(&q, (q - outbuf) - 2 /*+ 4*/);
     *q++ = 0x02; // set end
     *q++ = 0xff; // terminating command
@@ -410,7 +429,9 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
     bytestream_put_be16(&qq, q - outbuf);
 
     av_log(NULL, AV_LOG_DEBUG, "subtitle_packet size=%"PTRDIFF_SPECIFIER"\n", q - outbuf);
-    ret = q - outbuf;
+    avpkt->size = q - outbuf;
+    ret = 0;
+    *got_packet = 1;
 
 fail:
     av_free(vrect_data);
@@ -474,14 +495,13 @@ static int dvdsub_init(AVCodecContext *avctx)
     return 0;
 }
 
-static int dvdsub_encode(AVCodecContext *avctx,
-                         unsigned char *buf, int buf_size,
-                         const AVSubtitle *sub)
+static int dvdsub_encode(struct AVCodecContext* avctx, struct AVPacket* avpkt,
+                         const struct AVFrame* frame, int* got_packet)
 {
     //DVDSubtitleContext *s = avctx->priv_data;
     int ret;
 
-    ret = encode_dvd_subtitles(avctx, buf, buf_size, sub);
+    ret = encode_dvd_subtitles(avctx, avpkt, frame, got_packet);
     return ret;
 }
 
@@ -506,7 +526,7 @@ const FFCodec ff_dvdsub_encoder = {
     .p.type         = AVMEDIA_TYPE_SUBTITLE,
     .p.id           = AV_CODEC_ID_DVD_SUBTITLE,
     .init           = dvdsub_init,
-    FF_CODEC_ENCODE_SUB_CB(dvdsub_encode),
+    FF_CODEC_ENCODE_CB(dvdsub_encode),
     .p.priv_class   = &dvdsubenc_class,
     .priv_data_size = sizeof(DVDSubtitleContext),
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
diff --git a/libavcodec/encode.c b/libavcodec/encode.c
index b68bf1e184..c96f5feb61 100644
--- a/libavcodec/encode.c
+++ b/libavcodec/encode.c
@@ -143,17 +143,70 @@ fail:
     return ret;
 }
 
-int avcodec_encode_subtitle(AVCodecContext *avctx, uint8_t *buf, int buf_size,
-                            const AVSubtitle *sub)
+int avcodec_encode_subtitle(AVCodecContext *avctx, uint8_t *buf, int buf_size, const AVSubtitle *sub)
 {
-    int ret;
+    int ret = 0;
+    AVFrame *frame = NULL;
+    AVPacket* avpkt = NULL;
+
     if (sub->start_display_time) {
         av_log(avctx, AV_LOG_ERROR, "start_display_time must be 0.\n");
         return -1;
     }
 
-    ret = ffcodec(avctx->codec)->cb.encode_sub(avctx, buf, buf_size, sub);
+    memset(buf, 0, buf_size);
+    // Create a temporary frame for calling the regular api:
+    frame = av_frame_alloc();
+    if (!frame) {
+        ret = AVERROR(ENOMEM);
+        goto exit;
+    }
+
+    frame->format = sub->format;
+    frame->type = AVMEDIA_TYPE_SUBTITLE;
+    ret = av_frame_get_buffer2(frame, 0);
+    if (ret < 0)
+        goto exit;
+
+    // Create a temporary packet
+    avpkt = av_packet_alloc();
+    if (!avpkt) {
+        ret = AVERROR(ENOMEM);
+        goto exit;
+    }
+
+    // Copy legacy subtitle data to temp frame
+    ret = ff_frame_put_subtitle(frame, sub);
+    if (ret < 0)
+        goto exit;
+
+    ret = avcodec_send_frame(avctx, frame);
+    if (ret < 0)
+        goto exit;
+
+    ret = avcodec_receive_packet(avctx, avpkt);
+
+    if (ret < 0 && ret != AVERROR(EAGAIN))
+        goto exit;
+
+    //ret = avctx->codec->encode2(avctx, avpkt, frame, &got_packet);
+
     avctx->frame_number++;
+
+    if (avpkt->size) {
+        if (avpkt->size > buf_size) {
+            ret = AVERROR_BUFFER_TOO_SMALL;
+            goto exit;
+        }
+
+        memcpy(buf, avpkt->data, avpkt->size);
+        ret = avpkt->size;
+    }
+
+exit:
+
+    av_packet_free(&avpkt);
+    av_frame_free(&frame);
     return ret;
 }
 
diff --git a/libavcodec/movtextenc.c b/libavcodec/movtextenc.c
index 6f0b7a8a5c..b9e0288403 100644
--- a/libavcodec/movtextenc.c
+++ b/libavcodec/movtextenc.c
@@ -30,6 +30,7 @@
 #include "libavutil/ass_internal.h"
 #include "bytestream.h"
 #include "codec_internal.h"
+#include "encode.h"
 
 #define STYLE_FLAG_BOLD         (1<<0)
 #define STYLE_FLAG_ITALIC       (1<<1)
@@ -73,6 +74,7 @@ typedef struct {
     AVCodecContext *avctx;
 
     ASSSplitContext *ass_ctx;
+    int is_default_ass_context;
     ASSStyle *ass_dialog_style;
     StyleBox *style_attributes;
     unsigned  count;
@@ -329,7 +331,7 @@ static av_cold int mov_text_encode_init(AVCodecContext *avctx)
 
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
 
-    s->ass_ctx = avpriv_ass_split(avctx->subtitle_header);
+    s->ass_ctx = avpriv_ass_split((char*)avctx->subtitle_header);
     if (!s->ass_ctx)
         return AVERROR_INVALIDDATA;
     ret = encode_sample_description(avctx);
@@ -633,56 +635,112 @@ static const ASSCodesCallbacks mov_text_callbacks = {
     .end              = mov_text_end_cb,
 };
 
-static int mov_text_encode_frame(AVCodecContext *avctx, unsigned char *buf,
-                                 int bufsize, const AVSubtitle *sub)
+static void ensure_ass_context(AVCodecContext* avctx, const AVFrame* frame)
+{
+    MovTextContext* s = avctx->priv_data;
+    int ret;
+
+    if (s->ass_ctx && !s->is_default_ass_context)
+        // We already have a (non-default context)
+        return;
+
+    if (!frame->num_subtitle_areas)
+        // Don't need ass context for processing empty subtitle frames
+        return;
+
+    // The frame has content, so we need to set up a context
+    if (frame->subtitle_header && frame->subtitle_header->size > 0) {
+        const char* subtitle_header = (char*)frame->subtitle_header->data;
+        avpriv_ass_split_free(s->ass_ctx);
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 0;
+    }
+    else if (!s->ass_ctx) {
+        char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+        if (!subtitle_header)
+            return;
+
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 1;
+        av_free(subtitle_header);
+    }
+
+    if (s->ass_ctx && !avctx->extradata_size) {
+        ret = encode_sample_description(avctx);
+        if (ret < 0)
+            av_log(avctx, AV_LOG_ERROR, "Error during encode_sample_description().\n");
+    }
+}
+
+static int mov_text_encode_frame(AVCodecContext *avctx, AVPacket *avpkt,
+                                 const AVFrame *frame, int *got_packet)
 {
     MovTextContext *s = avctx->priv_data;
     ASSDialog *dialog;
-    int i, length;
+    int i, ret = 0;
+    size_t j;
+    uint8_t* buf;
+
+    ensure_ass_context(avctx, frame);
 
     s->text_pos = 0;
     s->count = 0;
     s->box_flags = 0;
     av_bprint_clear(&s->buffer);
-    for (i = 0; i < sub->num_rects; i++) {
-        const char *ass = sub->rects[i]->ass;
+    for (i = 0; i < frame->num_subtitle_areas; i++) {
+        const char *ass = frame->subtitle_areas[i]->ass;
 
-        if (sub->rects[i]->type != SUBTITLE_ASS) {
-            av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
+        if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) {
+            av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
             return AVERROR(EINVAL);
         }
 
-        dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
-        if (!dialog)
-            return AVERROR(ENOMEM);
-        mov_text_dialog(s, dialog);
-        avpriv_ass_split_override_codes(&mov_text_callbacks, s, dialog->text);
-        avpriv_ass_free_dialog(&dialog);
+        if (ass) {
+
+            if (i > 0)
+                mov_text_new_line_cb(s, 0);
+
+            dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
+            if (!dialog)
+                return AVERROR(ENOMEM);
+            mov_text_dialog(s, dialog);
+            avpriv_ass_split_override_codes(&mov_text_callbacks, s, dialog->text);
+            avpriv_ass_free_dialog(&dialog);
+
+        }
     }
 
-    if (s->buffer.len > UINT16_MAX)
-        return AVERROR(ERANGE);
-    AV_WB16(buf, s->buffer.len);
-    buf += 2;
+    if (!av_bprint_is_complete(&s->buffer)) {
+        return AVERROR(ENOMEM);
+    }
 
-    for (size_t j = 0; j < box_count; j++)
+    for (j = 0; j < box_count; j++) {
         box_types[j].encode(s);
+    }
 
-    if (!av_bprint_is_complete(&s->buffer))
-        return AVERROR(ENOMEM);
+    ret = ff_get_encode_buffer(avctx, avpkt, s->buffer.len + 3, 0);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+        return ret;
+    }
 
-    if (!s->buffer.len)
-        return 0;
+    buf = avpkt->data;
 
-    if (s->buffer.len > bufsize - 3) {
+    AV_WB16(buf, s->buffer.len);
+    buf += 2;
+
+    if (s->buffer.len > avpkt->size - 3) {
         av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n");
-        return AVERROR_BUFFER_TOO_SMALL;
+        ret = AVERROR_BUFFER_TOO_SMALL;
+        goto exit;
     }
 
     memcpy(buf, s->buffer.str, s->buffer.len);
-    length = s->buffer.len + 2;
+    avpkt->size = s->buffer.len + 2;
+    *got_packet = 1;
 
-    return length;
+exit:
+    return ret;
 }
 
 #define OFFSET(x) offsetof(MovTextContext, x)
@@ -707,7 +765,7 @@ const FFCodec ff_movtext_encoder = {
     .priv_data_size = sizeof(MovTextContext),
     .p.priv_class   = &mov_text_encoder_class,
     .init           = mov_text_encode_init,
-    FF_CODEC_ENCODE_SUB_CB(mov_text_encode_frame),
+    FF_CODEC_ENCODE_CB(mov_text_encode_frame),
     .close          = mov_text_encode_close,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP,
 };
diff --git a/libavcodec/srtenc.c b/libavcodec/srtenc.c
index 2baa6e70ad..bc336ea91e 100644
--- a/libavcodec/srtenc.c
+++ b/libavcodec/srtenc.c
@@ -23,6 +23,7 @@
 
 #include <stdarg.h>
 #include "avcodec.h"
+#include "encode.h"
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
 #include "codec_internal.h"
@@ -35,6 +36,7 @@
 typedef struct {
     AVCodecContext *avctx;
     ASSSplitContext *ass_ctx;
+    int is_default_ass_context;
     AVBPrint buffer;
     char stack[SRT_STACK_SIZE];
     int stack_ptr;
@@ -132,14 +134,13 @@ static void srt_style_apply(SRTContext *s, const char *style)
     }
 }
 
-
 static av_cold int srt_encode_init(AVCodecContext *avctx)
 {
     SRTContext *s = avctx->priv_data;
     s->avctx = avctx;
-    s->ass_ctx = avpriv_ass_split(avctx->subtitle_header);
+    s->ass_ctx = avpriv_ass_split((char *)avctx->subtitle_header);
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
-    return s->ass_ctx ? 0 : AVERROR_INVALIDDATA;
+    return 0;
 }
 
 static void srt_text_cb(void *priv, const char *text, int len)
@@ -229,58 +230,95 @@ static const ASSCodesCallbacks text_callbacks = {
     .new_line         = srt_new_line_cb,
 };
 
-static int encode_frame(AVCodecContext *avctx,
-                        unsigned char *buf, int bufsize, const AVSubtitle *sub,
-                        const ASSCodesCallbacks *cb)
+static void ensure_ass_context(SRTContext* s, const AVFrame* frame)
+{
+    if (s->ass_ctx && !s->is_default_ass_context)
+        // We already have a (non-default context)
+        return;
+
+    if (!frame->num_subtitle_areas)
+        // Don't need ass context for processing empty subtitle frames
+        return;
+
+    // The frame has content, so we need to set up a context
+    if (frame->subtitle_header && frame->subtitle_header->size > 0) {
+        const char* subtitle_header = (char*)frame->subtitle_header->data;
+        avpriv_ass_split_free(s->ass_ctx);
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 0;
+    }
+    else if (!s->ass_ctx) {
+        char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+        if (!subtitle_header)
+            return;
+
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 1;
+        av_free(subtitle_header);
+    }
+}
+
+static int encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+                        const AVFrame* frame, int* got_packet, const ASSCodesCallbacks* cb)
 {
     SRTContext *s = avctx->priv_data;
     ASSDialog *dialog;
-    int i;
+    int i, ret;
+
+    ensure_ass_context(s, frame);
 
     av_bprint_clear(&s->buffer);
 
-    for (i=0; i<sub->num_rects; i++) {
-        const char *ass = sub->rects[i]->ass;
+    for (i=0; i< frame->num_subtitle_areas; i++) {
+        const char *ass = frame->subtitle_areas[i]->ass;
 
-        if (sub->rects[i]->type != SUBTITLE_ASS) {
-            av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
+        if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) {
+            av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
             return AVERROR(EINVAL);
         }
 
-        dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
-        if (!dialog)
-            return AVERROR(ENOMEM);
-        s->alignment_applied = 0;
-        if (avctx->codec_id == AV_CODEC_ID_SUBRIP)
-            srt_style_apply(s, dialog->style);
-        avpriv_ass_split_override_codes(cb, s, dialog->text);
-        avpriv_ass_free_dialog(&dialog);
+        if (ass) {
+
+            if (i > 0)
+                srt_new_line_cb(s, 0);
+
+            dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
+            if (!dialog)
+                return AVERROR(ENOMEM);
+            s->alignment_applied = 0;
+            if (avctx->codec_id == AV_CODEC_ID_SUBRIP)
+                srt_style_apply(s, dialog->style);
+            avpriv_ass_split_override_codes(cb, s, dialog->text);
+            avpriv_ass_free_dialog(&dialog);
+        }
     }
 
     if (!av_bprint_is_complete(&s->buffer))
         return AVERROR(ENOMEM);
-    if (!s->buffer.len)
-        return 0;
 
-    if (s->buffer.len > bufsize) {
-        av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n");
-        return AVERROR_BUFFER_TOO_SMALL;
+    ret = ff_get_encode_buffer(avctx, avpkt, s->buffer.len + 1, 0);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+        return ret;
     }
-    memcpy(buf, s->buffer.str, s->buffer.len);
 
-    return s->buffer.len;
+    memcpy(avpkt->data, s->buffer.str, s->buffer.len);
+    avpkt->size = s->buffer.len;
+    *got_packet = 1;
+
+    return 0;
 }
 
-static int srt_encode_frame(AVCodecContext *avctx,
-                               unsigned char *buf, int bufsize, const AVSubtitle *sub)
+static int srt_encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+                            const AVFrame* frame, int* got_packet)
 {
-    return encode_frame(avctx, buf, bufsize, sub, &srt_callbacks);
+    return encode_frame(avctx, avpkt, frame, got_packet, &srt_callbacks);
 }
 
-static int text_encode_frame(AVCodecContext *avctx,
-                             unsigned char *buf, int bufsize, const AVSubtitle *sub)
+static int text_encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+                             const AVFrame* frame, int* got_packet)
 {
-    return encode_frame(avctx, buf, bufsize, sub, &text_callbacks);
+    return encode_frame(avctx, avpkt, frame, got_packet, &text_callbacks);
 }
 
 static int srt_encode_close(AVCodecContext *avctx)
@@ -300,7 +338,7 @@ const FFCodec ff_srt_encoder = {
     .p.id           = AV_CODEC_ID_SUBRIP,
     .priv_data_size = sizeof(SRTContext),
     .init           = srt_encode_init,
-    FF_CODEC_ENCODE_SUB_CB(srt_encode_frame),
+    FF_CODEC_ENCODE_CB(srt_encode_frame),
     .close          = srt_encode_close,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
 };
@@ -314,7 +352,7 @@ const FFCodec ff_subrip_encoder = {
     .p.id           = AV_CODEC_ID_SUBRIP,
     .priv_data_size = sizeof(SRTContext),
     .init           = srt_encode_init,
-    FF_CODEC_ENCODE_SUB_CB(srt_encode_frame),
+    FF_CODEC_ENCODE_CB(srt_encode_frame),
     .close          = srt_encode_close,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
 };
@@ -328,7 +366,7 @@ const FFCodec ff_text_encoder = {
     .p.id           = AV_CODEC_ID_TEXT,
     .priv_data_size = sizeof(SRTContext),
     .init           = srt_encode_init,
-    FF_CODEC_ENCODE_SUB_CB(text_encode_frame),
+    FF_CODEC_ENCODE_CB(text_encode_frame),
     .close          = srt_encode_close,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
 };
diff --git a/libavcodec/tests/avcodec.c b/libavcodec/tests/avcodec.c
index 08b5fbede1..1469c23f99 100644
--- a/libavcodec/tests/avcodec.c
+++ b/libavcodec/tests/avcodec.c
@@ -109,7 +109,6 @@ int main(void){
             is_decoder = 1;
             break;
         case FF_CODEC_CB_TYPE_ENCODE:
-        case FF_CODEC_CB_TYPE_ENCODE_SUB:
         case FF_CODEC_CB_TYPE_RECEIVE_PACKET:
             is_encoder = 1;
             break;
@@ -125,15 +124,13 @@ int main(void){
 #define CHECK(TYPE, type) (codec2->cb_type == FF_CODEC_CB_TYPE_ ## TYPE && !codec2->cb.type)
         if (CHECK(DECODE, decode) || CHECK(DECODE_SUB, decode_sub) ||
             CHECK(RECEIVE_PACKET, receive_packet) ||
-            CHECK(ENCODE, encode) || CHECK(ENCODE_SUB, encode_sub) ||
+            CHECK(ENCODE, encode) ||
             CHECK(RECEIVE_FRAME, receive_frame)) {
             ERR_EXT("Codec %s does not implement its %s callback.\n",
                     is_decoder ? "decoding" : "encoding");
         }
 #undef CHECK
         if (is_encoder) {
-            if ((codec->type == AVMEDIA_TYPE_SUBTITLE) != (codec2->cb_type == FF_CODEC_CB_TYPE_ENCODE_SUB))
-                ERR("Encoder %s is both subtitle encoder and not subtitle encoder.");
             if (codec2->update_thread_context || codec2->update_thread_context_for_user || codec2->bsfs)
                 ERR("Encoder %s has decoder-only thread functions or bsf.\n");
             if (codec->type == AVMEDIA_TYPE_AUDIO) {
diff --git a/libavcodec/ttmlenc.c b/libavcodec/ttmlenc.c
index d4f11a87d2..035fe33f24 100644
--- a/libavcodec/ttmlenc.c
+++ b/libavcodec/ttmlenc.c
@@ -33,11 +33,17 @@
 #include "libavutil/bprint.h"
 #include "libavutil/internal.h"
 #include "libavutil/ass_split_internal.h"
+#include "libavutil/ass_internal.h"
 #include "ttmlenc.h"
 
+#include "encode.h"
+
+
 typedef struct {
     AVCodecContext *avctx;
     ASSSplitContext *ass_ctx;
+    int is_default_ass_context;
+    int extradata_written;
     AVBPrint buffer;
 } TTMLContext;
 
@@ -76,28 +82,75 @@ static const ASSCodesCallbacks ttml_callbacks = {
     .new_line         = ttml_new_line_cb,
 };
 
-static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf,
-                             int bufsize, const AVSubtitle *sub)
+static int ttml_write_header_content(AVCodecContext* avctx);
+
+static void ensure_ass_context(AVCodecContext* avctx, const AVFrame* frame)
+{
+    TTMLContext* s = avctx->priv_data;
+    int ret;
+
+    if (s->ass_ctx && !s->is_default_ass_context)
+        // We already have a (non-default context)
+        return;
+
+    if (!frame->num_subtitle_areas)
+        // Don't need ass context for processing empty subtitle frames
+        return;
+
+    // The frame has content, so we need to set up a context
+    if (frame->subtitle_header && frame->subtitle_header->size > 0) {
+        const char* subtitle_header = (char*)frame->subtitle_header->data;
+        avpriv_ass_split_free(s->ass_ctx);
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 0;
+    }
+    else if (!s->ass_ctx) {
+        char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+        if (!subtitle_header)
+            return;
+
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 1;
+        av_free(subtitle_header);
+    }
+
+    if (s->ass_ctx && !s->extradata_written) {
+        s->extradata_written = 1;
+        if ((ret = ttml_write_header_content(avctx)) < 0) {
+            av_log(avctx, AV_LOG_ERROR, "Error writing header content.\n");
+        }
+    }
+}
+
+static int ttml_encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+                             const AVFrame* frame, int* got_packet)
 {
     TTMLContext *s = avctx->priv_data;
     ASSDialog *dialog;
-    int i;
+    int i, ret;
+
+    ensure_ass_context(avctx, frame);
 
     av_bprint_clear(&s->buffer);
 
-    for (i=0; i<sub->num_rects; i++) {
-        const char *ass = sub->rects[i]->ass;
-        int ret;
+    for (i=0; i< frame->num_subtitle_areas; i++) {
+        const char *ass = frame->subtitle_areas[i]->ass;
 
-        if (sub->rects[i]->type != SUBTITLE_ASS) {
-            av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
+        if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) {
+            av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
             return AVERROR(EINVAL);
         }
 
+        if (!ass)
+            continue;
+
         dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
         if (!dialog)
             return AVERROR(ENOMEM);
 
+        if (i > 0)
+            ttml_new_line_cb(s, 0);
+
         if (dialog->style) {
             av_bprintf(&s->buffer, "<span region=\"");
             av_bprint_escape(&s->buffer, dialog->style, NULL,
@@ -130,17 +183,19 @@ static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf,
 
     if (!av_bprint_is_complete(&s->buffer))
         return AVERROR(ENOMEM);
-    if (!s->buffer.len)
-        return 0;
-
-    // force null-termination, so in case our destination buffer is
-    // too small, the return value is larger than bufsize minus null.
-    if (av_strlcpy(buf, s->buffer.str, bufsize) > bufsize - 1) {
-        av_log(avctx, AV_LOG_ERROR, "Buffer too small for TTML event.\n");
-        return AVERROR_BUFFER_TOO_SMALL;
+
+    ret = ff_get_encode_buffer(avctx, avpkt, s->buffer.len + 3, 0);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+        return ret;
     }
 
-    return s->buffer.len;
+    av_strlcpy(avpkt->data, s->buffer.str, avpkt->size);
+
+    avpkt->size = s->buffer.len;
+    *got_packet = 1;
+
+    return 0;
 }
 
 static av_cold int ttml_encode_close(AVCodecContext *avctx)
@@ -370,13 +425,13 @@ static av_cold int ttml_encode_init(AVCodecContext *avctx)
     s->avctx   = avctx;
 
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
+    s->ass_ctx = avpriv_ass_split((char*)avctx->subtitle_header);
 
-    if (!(s->ass_ctx = avpriv_ass_split(avctx->subtitle_header))) {
-        return AVERROR_INVALIDDATA;
-    }
+    if (s->ass_ctx) {
+        if (ret = ttml_write_header_content(avctx) < 0)
+            return ret;
 
-    if ((ret = ttml_write_header_content(avctx)) < 0) {
-        return ret;
+        s->extradata_written = 1;
     }
 
     return 0;
@@ -389,7 +444,7 @@ const FFCodec ff_ttml_encoder = {
     .p.id           = AV_CODEC_ID_TTML,
     .priv_data_size = sizeof(TTMLContext),
     .init           = ttml_encode_init,
-    FF_CODEC_ENCODE_SUB_CB(ttml_encode_frame),
+    FF_CODEC_ENCODE_CB(ttml_encode_frame),
     .close          = ttml_encode_close,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP,
 };
diff --git a/libavcodec/utils.c b/libavcodec/utils.c
index b67b6b6122..b9ebe18e32 100644
--- a/libavcodec/utils.c
+++ b/libavcodec/utils.c
@@ -75,7 +75,6 @@ int av_codec_is_encoder(const AVCodec *avcodec)
 {
     const FFCodec *const codec = ffcodec(avcodec);
     return codec && (codec->cb_type == FF_CODEC_CB_TYPE_ENCODE     ||
-                     codec->cb_type == FF_CODEC_CB_TYPE_ENCODE_SUB ||
                      codec->cb_type == FF_CODEC_CB_TYPE_RECEIVE_PACKET);
 }
 
diff --git a/libavcodec/webvttenc.c b/libavcodec/webvttenc.c
index 24d60c5dc1..128607a669 100644
--- a/libavcodec/webvttenc.c
+++ b/libavcodec/webvttenc.c
@@ -22,6 +22,7 @@
 
 #include <stdarg.h>
 #include "avcodec.h"
+#include "encode.h"
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
 #include "codec_internal.h"
@@ -32,6 +33,7 @@
 typedef struct {
     AVCodecContext *avctx;
     ASSSplitContext *ass_ctx;
+    int is_default_ass_context;
     AVBPrint buffer;
     unsigned timestamp_end;
     int count;
@@ -155,43 +157,81 @@ static const ASSCodesCallbacks webvtt_callbacks = {
     .end              = webvtt_end_cb,
 };
 
-static int webvtt_encode_frame(AVCodecContext *avctx,
-                               unsigned char *buf, int bufsize, const AVSubtitle *sub)
+static void ensure_ass_context(WebVTTContext* s, const AVFrame* frame)
+{
+    if (s->ass_ctx && !s->is_default_ass_context)
+        // We already have a (non-default context)
+        return;
+
+    if (!frame->num_subtitle_areas)
+        // Don't need ass context for processing empty subtitle frames
+        return;
+
+    // The frame has content, so we need to set up a context
+    if (frame->subtitle_header && frame->subtitle_header->size > 0) {
+        const char* subtitle_header = (char*)frame->subtitle_header->data;
+        avpriv_ass_split_free(s->ass_ctx);
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 0;
+    }
+    else if (!s->ass_ctx) {
+        char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+        if (!subtitle_header)
+            return;
+
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 1;
+        av_free(subtitle_header);
+    }
+}
+
+static int webvtt_encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+                               const AVFrame* frame, int* got_packet)
 {
     WebVTTContext *s = avctx->priv_data;
     ASSDialog *dialog;
-    int i;
+    int ret, i;
+
+    ensure_ass_context(s, frame);
 
     av_bprint_clear(&s->buffer);
 
-    for (i=0; i<sub->num_rects; i++) {
-        const char *ass = sub->rects[i]->ass;
+    for (i=0; i< frame->num_subtitle_areas; i++) {
+        const char *ass = frame->subtitle_areas[i]->ass;
 
-        if (sub->rects[i]->type != SUBTITLE_ASS) {
-            av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
+        if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) {
+            av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
             return AVERROR(EINVAL);
         }
 
-        dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
-        if (!dialog)
-            return AVERROR(ENOMEM);
-        webvtt_style_apply(s, dialog->style);
-        avpriv_ass_split_override_codes(&webvtt_callbacks, s, dialog->text);
-        avpriv_ass_free_dialog(&dialog);
+        if (ass) {
+
+            if (i > 0)
+                webvtt_new_line_cb(s, 0);
+
+            dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
+            if (!dialog)
+                return AVERROR(ENOMEM);
+            webvtt_style_apply(s, dialog->style);
+            avpriv_ass_split_override_codes(&webvtt_callbacks, s, dialog->text);
+            avpriv_ass_free_dialog(&dialog);
+        }
     }
 
     if (!av_bprint_is_complete(&s->buffer))
         return AVERROR(ENOMEM);
-    if (!s->buffer.len)
-        return 0;
 
-    if (s->buffer.len > bufsize) {
-        av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n");
-        return AVERROR_BUFFER_TOO_SMALL;
+    ret = ff_get_encode_buffer(avctx, avpkt, s->buffer.len + 1, 0);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+        return ret;
     }
-    memcpy(buf, s->buffer.str, s->buffer.len);
 
-    return s->buffer.len;
+    memcpy(avpkt->data, s->buffer.str, s->buffer.len);
+    avpkt->size = s->buffer.len;
+    *got_packet = s->buffer.len > 0;
+
+    return 0;
 }
 
 static int webvtt_encode_close(AVCodecContext *avctx)
@@ -206,9 +246,9 @@ static av_cold int webvtt_encode_init(AVCodecContext *avctx)
 {
     WebVTTContext *s = avctx->priv_data;
     s->avctx = avctx;
-    s->ass_ctx = avpriv_ass_split(avctx->subtitle_header);
+    s->ass_ctx = avpriv_ass_split((char*)avctx->subtitle_header);
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
-    return s->ass_ctx ? 0 : AVERROR_INVALIDDATA;
+    return 0;
 }
 
 const FFCodec ff_webvtt_encoder = {
@@ -218,7 +258,7 @@ const FFCodec ff_webvtt_encoder = {
     .p.id           = AV_CODEC_ID_WEBVTT,
     .priv_data_size = sizeof(WebVTTContext),
     .init           = webvtt_encode_init,
-    FF_CODEC_ENCODE_SUB_CB(webvtt_encode_frame),
+    FF_CODEC_ENCODE_CB(webvtt_encode_frame),
     .close          = webvtt_encode_close,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
 };
diff --git a/libavcodec/xsubenc.c b/libavcodec/xsubenc.c
index 8ca411e5af..19570aee2f 100644
--- a/libavcodec/xsubenc.c
+++ b/libavcodec/xsubenc.c
@@ -23,6 +23,7 @@
 #include "avcodec.h"
 #include "bytestream.h"
 #include "codec_internal.h"
+#include "encode.h"
 #include "put_bits.h"
 
 /**
@@ -111,39 +112,56 @@ static int make_tc(uint64_t ms, int *tc)
     return ms > 99;
 }
 
-static int xsub_encode(AVCodecContext *avctx, unsigned char *buf,
-                       int bufsize, const AVSubtitle *h)
+static int xsub_encode(AVCodecContext* avctx, AVPacket* avpkt,
+                       const AVFrame* frame, int* got_packet)
 {
-    uint64_t startTime = h->pts / 1000; // FIXME: need better solution...
-    uint64_t endTime = startTime + h->end_display_time - h->start_display_time;
+    const int64_t duration_ms = (int64_t)((double)frame->subtitle_timing.duration * av_q2d(AV_TIME_BASE_Q) * 1000);
+    const uint64_t startTime = (int64_t)((double)frame->subtitle_timing.start_pts * av_q2d(AV_TIME_BASE_Q) * 1000);
+    const uint64_t endTime   = startTime + duration_ms;
     int start_tc[4], end_tc[4];
-    uint8_t *hdr = buf + 27; // Point behind the timestamp
+    uint8_t *hdr;
     uint8_t *rlelenptr;
     uint16_t width, height;
-    int i;
+    int i, ret;
     PutBitContext pb;
+    uint8_t* buf;
+    int64_t req_size;
 
-    if (bufsize < 27 + 7*2 + 4*3) {
-        av_log(avctx, AV_LOG_ERROR, "Buffer too small for XSUB header.\n");
-        return AVERROR_BUFFER_TOO_SMALL;
+    if (!frame->num_subtitle_areas) {
+        // Don't encode empty sub events
+        return 0;
     }
 
+    // Estimate size (timestamp 27, header 7*2 + 4*3, padding 10)
+    req_size = 27 + 7*2 + 4*3 + 10;
+    req_size += frame->subtitle_areas[0]->linesize[0] * frame->subtitle_areas[0]->h;
+    req_size += 256; // Palette
+
+    ret = ff_get_encode_buffer(avctx, avpkt, req_size, 0);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+        return ret;
+    }
+
+    buf = avpkt->data;
+    hdr = avpkt->data + 27; // Point behind the timestamp
+
     // TODO: support multiple rects
-    if (h->num_rects != 1)
-        av_log(avctx, AV_LOG_WARNING, "Only single rects supported (%d in subtitle.)\n", h->num_rects);
+    if (frame->num_subtitle_areas != 1)
+        av_log(avctx, AV_LOG_WARNING, "Only single rects supported (%d in subtitle.)\n", frame->num_subtitle_areas);
 
     // TODO: render text-based subtitles into bitmaps
-    if (!h->rects[0]->data[0] || !h->rects[0]->data[1]) {
+    if (!frame->subtitle_areas[0]->buf[0]->data || !frame->subtitle_areas[0]->pal) {
         av_log(avctx, AV_LOG_WARNING, "No subtitle bitmap available.\n");
         return AVERROR(EINVAL);
     }
 
     // TODO: color reduction, similar to dvdsub encoder
-    if (h->rects[0]->nb_colors > 4)
-        av_log(avctx, AV_LOG_WARNING, "No more than 4 subtitle colors supported (%d found.)\n", h->rects[0]->nb_colors);
+    if (frame->subtitle_areas[0]->nb_colors > 4)
+        av_log(avctx, AV_LOG_WARNING, "No more than 4 subtitle colors supported (%d found.)\n", frame->subtitle_areas[0]->nb_colors);
 
     // TODO: Palette swapping if color zero is not transparent
-    if (((uint32_t *)h->rects[0]->data[1])[0] & 0xff000000)
+    if (((uint32_t *)frame->subtitle_areas[0]->pal)[0] & 0xff000000)
         av_log(avctx, AV_LOG_WARNING, "Color index 0 is not transparent. Transparency will be messed up.\n");
 
     if (make_tc(startTime, start_tc) || make_tc(endTime, end_tc)) {
@@ -151,7 +169,7 @@ static int xsub_encode(AVCodecContext *avctx, unsigned char *buf,
         return AVERROR(EINVAL);
     }
 
-    snprintf(buf, 28,
+    snprintf((char *)avpkt->data, 28,
         "[%02d:%02d:%02d.%03d-%02d:%02d:%02d.%03d]",
         start_tc[3], start_tc[2], start_tc[1], start_tc[0],
         end_tc[3],   end_tc[2],   end_tc[1],   end_tc[0]);
@@ -160,45 +178,47 @@ static int xsub_encode(AVCodecContext *avctx, unsigned char *buf,
     // 2 pixels required on either side of subtitle.
     // Possibly due to limitations of hardware renderers.
     // TODO: check if the bitmap is already padded
-    width  = FFALIGN(h->rects[0]->w, 2) + PADDING * 2;
-    height = FFALIGN(h->rects[0]->h, 2);
+    width  = FFALIGN(frame->subtitle_areas[0]->w, 2) + PADDING * 2;
+    height = FFALIGN(frame->subtitle_areas[0]->h, 2);
 
     bytestream_put_le16(&hdr, width);
     bytestream_put_le16(&hdr, height);
-    bytestream_put_le16(&hdr, h->rects[0]->x);
-    bytestream_put_le16(&hdr, h->rects[0]->y);
-    bytestream_put_le16(&hdr, h->rects[0]->x + width -1);
-    bytestream_put_le16(&hdr, h->rects[0]->y + height -1);
+    bytestream_put_le16(&hdr, frame->subtitle_areas[0]->x);
+    bytestream_put_le16(&hdr, frame->subtitle_areas[0]->y);
+    bytestream_put_le16(&hdr, frame->subtitle_areas[0]->x + width -1);
+    bytestream_put_le16(&hdr, frame->subtitle_areas[0]->y + height -1);
 
     rlelenptr = hdr; // Will store length of first field here later.
     hdr+=2;
 
     // Palette
     for (i=0; i<4; i++)
-        bytestream_put_be24(&hdr, ((uint32_t *)h->rects[0]->data[1])[i]);
+        bytestream_put_be24(&hdr, ((uint32_t *)frame->subtitle_areas[0]->pal)[i]);
 
     // Bitmap
     // RLE buffer. Reserve 2 bytes for possible padding after the last row.
-    init_put_bits(&pb, hdr, bufsize - (hdr - buf) - 2);
-    if (xsub_encode_rle(&pb, h->rects[0]->data[0],
-                        h->rects[0]->linesize[0] * 2,
-                        h->rects[0]->w, (h->rects[0]->h + 1) >> 1))
+    init_put_bits(&pb, hdr, avpkt->size - (hdr - buf) - 2);
+    if (xsub_encode_rle(&pb, frame->subtitle_areas[0]->buf[0]->data,
+                        frame->subtitle_areas[0]->linesize[0] * 2,
+                        frame->subtitle_areas[0]->w, (frame->subtitle_areas[0]->h + 1) >> 1))
         return AVERROR_BUFFER_TOO_SMALL;
     bytestream_put_le16(&rlelenptr, put_bytes_count(&pb, 0)); // Length of first field
 
-    if (xsub_encode_rle(&pb, h->rects[0]->data[0] + h->rects[0]->linesize[0],
-                        h->rects[0]->linesize[0] * 2,
-                        h->rects[0]->w, h->rects[0]->h >> 1))
+    if (xsub_encode_rle(&pb, frame->subtitle_areas[0]->buf[0]->data + frame->subtitle_areas[0]->linesize[0],
+                        frame->subtitle_areas[0]->linesize[0] * 2,
+                        frame->subtitle_areas[0]->w, frame->subtitle_areas[0]->h >> 1))
         return AVERROR_BUFFER_TOO_SMALL;
 
     // Enforce total height to be a multiple of 2
-    if (h->rects[0]->h & 1) {
-        put_xsub_rle(&pb, h->rects[0]->w, PADDING_COLOR);
+    if (frame->subtitle_areas[0]->h & 1) {
+        put_xsub_rle(&pb, frame->subtitle_areas[0]->w, PADDING_COLOR);
     }
 
     flush_put_bits(&pb);
 
-    return hdr - buf + put_bytes_output(&pb);
+    avpkt->size = hdr - buf + put_bytes_output(&pb);
+    *got_packet = 1;
+    return 0;
 }
 
 static av_cold int xsub_encoder_init(AVCodecContext *avctx)
@@ -217,6 +237,6 @@ const FFCodec ff_xsub_encoder = {
     .p.type     = AVMEDIA_TYPE_SUBTITLE,
     .p.id       = AV_CODEC_ID_XSUB,
     .init       = xsub_encoder_init,
-    FF_CODEC_ENCODE_SUB_CB(xsub_encode),
+    FF_CODEC_ENCODE_CB(xsub_encode),
     .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE,
 };
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v4 22/23] fftools/ffmpeg: Introduce subtitle filtering and new frame-based subtitle encoding
  2022-05-28 13:25     ` [FFmpeg-devel] [PATCH v4 00/23] Subtitle Filtering 2022 ffmpegagent
                         ` (20 preceding siblings ...)
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 21/23] avcodec/subtitles: Migrate subtitle encoders to frame-based API softworkz
@ 2022-05-28 13:25       ` softworkz
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 23/23] avcodec/dvbsubdec: Fix conditions for fallback to default resolution softworkz
  2022-06-25  9:57       ` [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022 ffmpegagent
  23 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-05-28 13:25 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

This commit actually enables subtitle filtering in ffmpeg by
sending and receiving subtitle frames to and from a filtergraph.

The heartbeat functionality from the previous sub2video implementation
is removed and now provided by the 'subfeed' filter.
The other part of sub2video functionality is retained by
auto-insertion of the new graphicsub2video filter.

Justification for changed test refs:

- sub2video
  The previous results were incorrect. The command line for this test
  specifies -r 5 (5 frames per second), which is now fulfilled by the
  additional frames in the reference output.
  Example: The first subtitle time is 499000, the second is 15355000,
  which means 0.5s and 15.35s with a difference of 14.85s.
  15s * 5fps = 75 frames and that's now the exact number of video
  frames between these two subtitle events.

- sub2video_basic
  The previous results had some incorrect output because multiple
  frames had the same dts
  The non-empty content frames are visually identical, the different
  CRC is due to the different blending algorithm that is being used.

- sub2video_time_limited
  Subtitle frames are emitted to the filter graphs at a 5 fps rate
  by default. The time limit for this test is 15s * 5fps = 75 frames
  which matches the count in the new ref.

- sub-dvb
  Running ffprobe -show_frames on the source file shows that there
  are 7 subtitle frames with 0 rects in the source at the start
  and 2 at the end. This translates to the 14 and 4 additional
  entries in the new test results.

- filter-overlay-dvdsub-2397
  Overlay results have slightly different CRCs due to different
  blending implementation

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 fftools/ffmpeg.c                          |  610 +++++-----
 fftools/ffmpeg.h                          |   17 +-
 fftools/ffmpeg_filter.c                   |  243 ++--
 fftools/ffmpeg_hw.c                       |    2 +-
 fftools/ffmpeg_opt.c                      |    4 +-
 tests/ref/fate/filter-overlay-dvdsub-2397 |  182 +--
 tests/ref/fate/sub-dvb                    |  162 +--
 tests/ref/fate/sub2video                  | 1091 +++++++++++++++++-
 tests/ref/fate/sub2video_basic            | 1238 +++++++++++++++++++--
 tests/ref/fate/sub2video_time_limited     |   78 +-
 10 files changed, 2960 insertions(+), 667 deletions(-)

diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c
index 5ed287c522..b9b7ae1f30 100644
--- a/fftools/ffmpeg.c
+++ b/fftools/ffmpeg.c
@@ -143,8 +143,6 @@ int want_sdp = 1;
 static BenchmarkTimeStamps current_time;
 AVIOContext *progress_avio = NULL;
 
-static uint8_t *subtitle_out;
-
 InputStream **input_streams = NULL;
 int        nb_input_streams = 0;
 InputFile   **input_files   = NULL;
@@ -169,163 +167,6 @@ static int restore_tty;
 static void free_input_threads(void);
 #endif
 
-/* sub2video hack:
-   Convert subtitles to video with alpha to insert them in filter graphs.
-   This is a temporary solution until libavfilter gets real subtitles support.
- */
-
-static int sub2video_get_blank_frame(InputStream *ist)
-{
-    int ret;
-    AVFrame *frame = ist->sub2video.frame;
-
-    av_frame_unref(frame);
-    ist->sub2video.frame->width  = ist->dec_ctx->width  ? ist->dec_ctx->width  : ist->sub2video.w;
-    ist->sub2video.frame->height = ist->dec_ctx->height ? ist->dec_ctx->height : ist->sub2video.h;
-    ist->sub2video.frame->format = AV_PIX_FMT_RGB32;
-    if ((ret = av_frame_get_buffer(frame, 0)) < 0)
-        return ret;
-    memset(frame->data[0], 0, frame->height * frame->linesize[0]);
-    return 0;
-}
-
-static void sub2video_copy_rect(uint8_t *dst, int dst_linesize, int w, int h,
-                                AVSubtitleRect *r)
-{
-    uint32_t *pal, *dst2;
-    uint8_t *src, *src2;
-    int x, y;
-
-    if (r->type != SUBTITLE_BITMAP) {
-        av_log(NULL, AV_LOG_WARNING, "sub2video: non-bitmap subtitle\n");
-        return;
-    }
-    if (r->x < 0 || r->x + r->w > w || r->y < 0 || r->y + r->h > h) {
-        av_log(NULL, AV_LOG_WARNING, "sub2video: rectangle (%d %d %d %d) overflowing %d %d\n",
-            r->x, r->y, r->w, r->h, w, h
-        );
-        return;
-    }
-
-    dst += r->y * dst_linesize + r->x * 4;
-    src = r->data[0];
-    pal = (uint32_t *)r->data[1];
-    for (y = 0; y < r->h; y++) {
-        dst2 = (uint32_t *)dst;
-        src2 = src;
-        for (x = 0; x < r->w; x++)
-            *(dst2++) = pal[*(src2++)];
-        dst += dst_linesize;
-        src += r->linesize[0];
-    }
-}
-
-static void sub2video_push_ref(InputStream *ist, int64_t pts)
-{
-    AVFrame *frame = ist->sub2video.frame;
-    int i;
-    int ret;
-
-    av_assert1(frame->data[0]);
-    ist->sub2video.last_pts = frame->pts = pts;
-    for (i = 0; i < ist->nb_filters; i++) {
-        ret = av_buffersrc_add_frame_flags(ist->filters[i]->filter, frame,
-                                           AV_BUFFERSRC_FLAG_KEEP_REF |
-                                           AV_BUFFERSRC_FLAG_PUSH);
-        if (ret != AVERROR_EOF && ret < 0)
-            av_log(NULL, AV_LOG_WARNING, "Error while add the frame to buffer source(%s).\n",
-                   av_err2str(ret));
-    }
-}
-
-void sub2video_update(InputStream *ist, int64_t heartbeat_pts, AVSubtitle *sub)
-{
-    AVFrame *frame = ist->sub2video.frame;
-    int8_t *dst;
-    int     dst_linesize;
-    int num_rects, i;
-    int64_t pts, end_pts;
-
-    if (!frame)
-        return;
-    if (sub) {
-        pts       = av_rescale_q(sub->pts + sub->start_display_time * 1000LL,
-                                 AV_TIME_BASE_Q, ist->st->time_base);
-        end_pts   = av_rescale_q(sub->pts + sub->end_display_time   * 1000LL,
-                                 AV_TIME_BASE_Q, ist->st->time_base);
-        num_rects = sub->num_rects;
-    } else {
-        /* If we are initializing the system, utilize current heartbeat
-           PTS as the start time, and show until the following subpicture
-           is received. Otherwise, utilize the previous subpicture's end time
-           as the fall-back value. */
-        pts       = ist->sub2video.initialize ?
-                    heartbeat_pts : ist->sub2video.end_pts;
-        end_pts   = INT64_MAX;
-        num_rects = 0;
-    }
-    if (sub2video_get_blank_frame(ist) < 0) {
-        av_log(ist->dec_ctx, AV_LOG_ERROR,
-               "Impossible to get a blank canvas.\n");
-        return;
-    }
-    dst          = frame->data    [0];
-    dst_linesize = frame->linesize[0];
-    for (i = 0; i < num_rects; i++)
-        sub2video_copy_rect(dst, dst_linesize, frame->width, frame->height, sub->rects[i]);
-    sub2video_push_ref(ist, pts);
-    ist->sub2video.end_pts = end_pts;
-    ist->sub2video.initialize = 0;
-}
-
-static void sub2video_heartbeat(InputStream *ist, int64_t pts)
-{
-    InputFile *infile = input_files[ist->file_index];
-    int i, j, nb_reqs;
-    int64_t pts2;
-
-    /* When a frame is read from a file, examine all sub2video streams in
-       the same file and send the sub2video frame again. Otherwise, decoded
-       video frames could be accumulating in the filter graph while a filter
-       (possibly overlay) is desperately waiting for a subtitle frame. */
-    for (i = 0; i < infile->nb_streams; i++) {
-        InputStream *ist2 = input_streams[infile->ist_index + i];
-        if (!ist2->sub2video.frame)
-            continue;
-        /* subtitles seem to be usually muxed ahead of other streams;
-           if not, subtracting a larger time here is necessary */
-        pts2 = av_rescale_q(pts, ist->st->time_base, ist2->st->time_base) - 1;
-        /* do not send the heartbeat frame if the subtitle is already ahead */
-        if (pts2 <= ist2->sub2video.last_pts)
-            continue;
-        if (pts2 >= ist2->sub2video.end_pts || ist2->sub2video.initialize)
-            /* if we have hit the end of the current displayed subpicture,
-               or if we need to initialize the system, update the
-               overlayed subpicture and its start/end times */
-            sub2video_update(ist2, pts2 + 1, NULL);
-        for (j = 0, nb_reqs = 0; j < ist2->nb_filters; j++)
-            nb_reqs += av_buffersrc_get_nb_failed_requests(ist2->filters[j]->filter);
-        if (nb_reqs)
-            sub2video_push_ref(ist2, pts2);
-    }
-}
-
-static void sub2video_flush(InputStream *ist)
-{
-    int i;
-    int ret;
-
-    if (ist->sub2video.end_pts < INT64_MAX)
-        sub2video_update(ist, INT64_MAX, NULL);
-    for (i = 0; i < ist->nb_filters; i++) {
-        ret = av_buffersrc_add_frame(ist->filters[i]->filter, NULL);
-        if (ret != AVERROR_EOF && ret < 0)
-            av_log(NULL, AV_LOG_WARNING, "Flush the frame error.\n");
-    }
-}
-
-/* end of sub2video hack */
-
 static void term_exit_sigsafe(void)
 {
 #if HAVE_TERMIOS_H
@@ -526,7 +367,6 @@ static void ffmpeg_cleanup(int ret)
         avfilter_graph_free(&fg->graph);
         for (j = 0; j < fg->nb_inputs; j++) {
             InputFilter *ifilter = fg->inputs[j];
-            struct InputStream *ist = ifilter->ist;
 
             if (ifilter->frame_queue) {
                 AVFrame *frame;
@@ -535,12 +375,6 @@ static void ffmpeg_cleanup(int ret)
                 av_fifo_freep2(&ifilter->frame_queue);
             }
             av_freep(&ifilter->displaymatrix);
-            if (ist->sub2video.sub_queue) {
-                AVSubtitle sub;
-                while (av_fifo_read(ist->sub2video.sub_queue, &sub, 1) >= 0)
-                    avsubtitle_free(&sub);
-                av_fifo_freep2(&ist->sub2video.sub_queue);
-            }
             av_buffer_unref(&ifilter->hw_frames_ctx);
             av_freep(&ifilter->name);
             av_freep(&fg->inputs[j]);
@@ -561,7 +395,7 @@ static void ffmpeg_cleanup(int ret)
     }
     av_freep(&filtergraphs);
 
-    av_freep(&subtitle_out);
+    ////av_freep(&subtitle_out);
 
     /* close files */
     for (i = 0; i < nb_output_files; i++)
@@ -614,15 +448,19 @@ static void ffmpeg_cleanup(int ret)
     for (i = 0; i < nb_input_streams; i++) {
         InputStream *ist = input_streams[i];
 
+        if (ist->prev_sub.subtitle == ist->decoded_frame)
+            // Prevent double-free
+            ist->prev_sub.subtitle = NULL;
+
         av_frame_free(&ist->decoded_frame);
         av_packet_free(&ist->pkt);
         av_dict_free(&ist->decoder_opts);
-        avsubtitle_free(&ist->prev_sub.subtitle);
-        av_frame_free(&ist->sub2video.frame);
+        av_frame_free(&ist->prev_sub.subtitle);
         av_freep(&ist->filters);
         av_freep(&ist->hwaccel_device);
         av_freep(&ist->dts_buffer);
 
+        av_buffer_unref(&ist->subtitle_header);
         avcodec_free_context(&ist->dec_ctx);
 
         av_freep(&input_streams[i]);
@@ -992,33 +830,83 @@ static void do_audio_out(OutputFile *of, OutputStream *ost,
         exit_program(1);
 }
 
-static void do_subtitle_out(OutputFile *of,
-                            OutputStream *ost,
-                            AVSubtitle *sub)
+static void encode_subtitle_frame(OutputFile *of, OutputStream *ost, AVFrame *frame, AVPacket *pkt, int64_t pts_offset)
+{
+    AVCodecContext *enc = ost->enc_ctx;
+    int ret;
+
+        ost->frames_encoded++;
+
+        ret = avcodec_send_frame(enc, frame);
+        if (ret < 0)
+            goto error;
+
+        while (1) {
+            ret = avcodec_receive_packet(enc, pkt);
+            update_benchmark("encode_subtitles %d.%d", ost->file_index, ost->index);
+            if (ret == AVERROR(EAGAIN))
+                break;
+            if (ret < 0)
+                goto error;
+
+            if (debug_ts) {
+                av_log(NULL, AV_LOG_INFO, "encoder -> type:subtitles "
+                       "pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s\n",
+                       av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &enc->time_base),
+                       av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &enc->time_base));
+            }
+
+            pkt->pts  = av_rescale_q(frame->subtitle_timing.start_pts, AV_TIME_BASE_Q, ost->mux_timebase);
+            pkt->duration = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, ost->mux_timebase);
+            pkt->pts += pts_offset;
+
+            pkt->dts = pkt->pts;
+            output_packet(of, pkt, ost, 0);
+        }
+        ost->sync_opts++;
+        ost->frame_number++;
+
+    return;
+error:
+    av_log(NULL, AV_LOG_FATAL, "Subtitle encoding failed - Error code: %d\n", ret);
+    exit_program(1);
+}
+
+static void do_subtitle_out(OutputFile *of, OutputStream *ost, AVFrame *frame)
 {
-    int subtitle_out_max_size = 1024 * 1024;
-    int subtitle_out_size, nb, i;
+    int nb, i;
     AVCodecContext *enc;
     AVPacket *pkt = ost->pkt;
     int64_t pts;
 
-    if (sub->pts == AV_NOPTS_VALUE) {
+    if (!frame)
+        return;
+
+    av_log(NULL, AV_LOG_DEBUG, "do_subtitle_out: sub->pts: %"PRId64"  frame->pts: %"PRId64"\n", frame->subtitle_timing.start_pts, frame->pts);
+
+    if (frame->subtitle_timing.start_pts == AV_NOPTS_VALUE) {
         av_log(NULL, AV_LOG_ERROR, "Subtitle packets must have a pts\n");
         if (exit_on_error)
             exit_program(1);
         return;
     }
 
-    enc = ost->enc_ctx;
-
-    if (!subtitle_out) {
-        subtitle_out = av_malloc(subtitle_out_max_size);
-        if (!subtitle_out) {
-            av_log(NULL, AV_LOG_FATAL, "Failed to allocate subtitle_out\n");
-            exit_program(1);
-        }
+    if (frame->repeat_sub) {
+        av_log(NULL, AV_LOG_WARNING, "Ignoring repeated subtitle frame\n");
+        return;
     }
 
+    ////if (ost->last_subtitle_pts && ost->last_subtitle_pts == frame->subtitle_timing.start_pts) {
+    ////    av_log(NULL, AV_LOG_DEBUG, "Ignoring subtitle frame with duplicate subtitle_pts\n");
+    ////    return;
+    ////}
+
+    ost->last_subtitle_pts = frame->subtitle_timing.start_pts;
+
+    init_output_stream_wrapper(ost, frame, 1);
+
+    enc = ost->enc_ctx;
+
     /* Note: DVB subtitle need one packet to draw them and one other
        packet to clear them */
     /* XXX: signal it in the codec context ? */
@@ -1028,50 +916,38 @@ static void do_subtitle_out(OutputFile *of,
         nb = 1;
 
     /* shift timestamp to honor -ss and make check_recording_time() work with -t */
-    pts = sub->pts;
+    pts = frame->subtitle_timing.start_pts;
     if (output_files[ost->file_index]->start_time != AV_NOPTS_VALUE)
         pts -= output_files[ost->file_index]->start_time;
-    for (i = 0; i < nb; i++) {
-        unsigned save_num_rects = sub->num_rects;
 
-        ost->sync_opts = av_rescale_q(pts, AV_TIME_BASE_Q, enc->time_base);
-        if (!check_recording_time(ost))
-            return;
+    ost->sync_opts = av_rescale_q(pts, AV_TIME_BASE_Q, enc->time_base);
+    if (!check_recording_time(ost))
+        return;
 
-        sub->pts = pts;
-        // start_display_time is required to be 0
-        sub->pts               += av_rescale_q(sub->start_display_time, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q);
-        sub->end_display_time  -= sub->start_display_time;
-        sub->start_display_time = 0;
-        if (i == 1)
-            sub->num_rects = 0;
+    frame->subtitle_timing.start_pts = pts;
+
+    for (i = 0; i < nb; i++) {
+        const unsigned save_num_rects = frame->num_subtitle_areas;
+        int64_t pts_offset = 0;
 
         ost->frames_encoded++;
 
-        subtitle_out_size = avcodec_encode_subtitle(enc, subtitle_out,
-                                                    subtitle_out_max_size, sub);
         if (i == 1)
-            sub->num_rects = save_num_rects;
-        if (subtitle_out_size < 0) {
-            av_log(NULL, AV_LOG_FATAL, "Subtitle encoding failed\n");
-            exit_program(1);
-        }
+            frame->num_subtitle_areas = 0;
 
-        av_packet_unref(pkt);
-        pkt->data = subtitle_out;
-        pkt->size = subtitle_out_size;
-        pkt->pts  = av_rescale_q(sub->pts, AV_TIME_BASE_Q, ost->mux_timebase);
-        pkt->duration = av_rescale_q(sub->end_display_time, (AVRational){ 1, 1000 }, ost->mux_timebase);
         if (enc->codec_id == AV_CODEC_ID_DVB_SUBTITLE) {
             /* XXX: the pts correction is handled here. Maybe handling
                it in the codec would be better */
             if (i == 0)
-                pkt->pts += av_rescale_q(sub->start_display_time, (AVRational){ 1, 1000 }, ost->mux_timebase);
+                pts_offset = 0;
             else
-                pkt->pts += av_rescale_q(sub->end_display_time, (AVRational){ 1, 1000 }, ost->mux_timebase);
+                pts_offset = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, ost->mux_timebase);
         }
-        pkt->dts = pkt->pts;
-        output_packet(of, pkt, ost, 0);
+
+        encode_subtitle_frame(of, ost, frame, pkt, pts_offset);
+
+        if (i == 1)
+            frame->num_subtitle_areas = save_num_rects;
     }
 }
 
@@ -1376,8 +1252,26 @@ static int reap_filters(int flush)
                 }
                 do_audio_out(of, ost, filtered_frame);
                 break;
+            case AVMEDIA_TYPE_SUBTITLE:
+
+                if (filtered_frame->format == AV_SUBTITLE_FMT_ASS && !enc->subtitle_header
+                    && filtered_frame->subtitle_header) {
+                    const char *subtitle_header = (char *)filtered_frame->subtitle_header->data;
+                    enc->subtitle_header = (uint8_t *)av_strdup(subtitle_header);
+                    if (!enc->subtitle_header)
+                        return AVERROR(ENOMEM);
+                    enc->subtitle_header_size = strlen(subtitle_header);
+                }
+
+                if ((ost->enc_ctx->width == 0 || ost->enc_ctx->height == 0)
+                    && filter->inputs[0]->w > 0 && filter->inputs[0]->h > 0 ) {
+                    ost->enc_ctx->width = filter->inputs[0]->w;
+                    ost->enc_ctx->height = filter->inputs[0]->h;
+                }
+
+                do_subtitle_out(of, ost, filtered_frame);
+                break;
             default:
-                // TODO support subtitle filters
                 av_assert0(0);
             }
 
@@ -1929,7 +1823,8 @@ static int ifilter_has_all_input_formats(FilterGraph *fg)
     int i;
     for (i = 0; i < fg->nb_inputs; i++) {
         if (fg->inputs[i]->format < 0 && (fg->inputs[i]->type == AVMEDIA_TYPE_AUDIO ||
-                                          fg->inputs[i]->type == AVMEDIA_TYPE_VIDEO))
+                                          fg->inputs[i]->type == AVMEDIA_TYPE_VIDEO ||
+                                          fg->inputs[i]->type == AVMEDIA_TYPE_SUBTITLE))
             return 0;
     }
     return 1;
@@ -1956,6 +1851,18 @@ static int ifilter_send_frame(InputFilter *ifilter, AVFrame *frame, int keep_ref
     case AVMEDIA_TYPE_VIDEO:
         need_reinit |= ifilter->width  != frame->width ||
                        ifilter->height != frame->height;
+
+        if (need_reinit)
+            need_reinit = 1;
+        break;
+    case AVMEDIA_TYPE_SUBTITLE:
+        need_reinit |= ifilter->width  != frame->width ||
+                       ifilter->height != frame->height;
+
+        need_reinit &= (ifilter->width == 0 || ifilter->height == 0);
+
+        if (need_reinit)
+            need_reinit = 1;
         break;
     }
 
@@ -2027,12 +1934,9 @@ static int ifilter_send_eof(InputFilter *ifilter, int64_t pts)
             return ret;
     } else {
         // the filtergraph was never configured
-        if (ifilter->format < 0) {
-            ret = ifilter_parameters_from_codecpar(ifilter, ifilter->ist->st->codecpar);
-            if (ret < 0)
-                return ret;
-        }
-        if (ifilter->format < 0 && (ifilter->type == AVMEDIA_TYPE_AUDIO || ifilter->type == AVMEDIA_TYPE_VIDEO)) {
+        if (ifilter->format < 0)
+            ifilter_parameters_from_codecpar(ifilter, ifilter->ist->st->codecpar);
+        if (ifilter->format < 0 && (ifilter->type == AVMEDIA_TYPE_AUDIO || ifilter->type == AVMEDIA_TYPE_VIDEO || ifilter->type == AVMEDIA_TYPE_SUBTITLE)) {
             av_log(NULL, AV_LOG_ERROR, "Cannot determine format of input stream %d:%d after EOF\n", ifilter->ist->file_index, ifilter->ist->st->index);
             return AVERROR_INVALIDDATA;
         }
@@ -2070,7 +1974,7 @@ static int decode(AVCodecContext *avctx, AVFrame *frame, int *got_frame, AVPacke
 
 static int send_frame_to_filters(InputStream *ist, AVFrame *decoded_frame)
 {
-    int i, ret;
+    int i, ret = 0;
 
     av_assert1(ist->nb_filters > 0); /* ensure ret is initialized */
     for (i = 0; i < ist->nb_filters; i++) {
@@ -2271,79 +2175,200 @@ fail:
     return err < 0 ? err : ret;
 }
 
-static int transcode_subtitles(InputStream *ist, AVPacket *pkt, int *got_output,
+static void subtitle_send_kickoff(InputStream *ist, int64_t heartbeat_pts)
+{
+    AVFrame *frame;
+    int64_t pts;
+
+    frame = av_frame_alloc();
+    if (!frame) {
+        av_log(ist->dec_ctx, AV_LOG_ERROR, "Unable to alloc frame (out of memory).\n");
+        return;
+    }
+
+    frame->type = AVMEDIA_TYPE_SUBTITLE;
+    av_frame_get_buffer2(frame, 0);
+
+    frame->format = (uint16_t)ist->dec_ctx->subtitle_type;
+
+    pts = FFMAX(heartbeat_pts, ist->subtitle_kickoff.last_pts) + av_rescale_q(10, (AVRational){ 1, 1000 }, ist->st->time_base);
+
+    frame->width = ist->subtitle_kickoff.w;
+    frame->height = ist->subtitle_kickoff.h;
+    frame->subtitle_timing.start_pts = av_rescale_q(pts, ist->st->time_base, AV_TIME_BASE_Q);
+    frame->subtitle_timing.duration =  av_rescale_q(10, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q);
+    frame->pts = pts;
+
+    av_log(NULL, AV_LOG_WARNING, "subtitle_kickoff: call subtitle_resend_current %"PRId64" frame->format: %d\n", pts, frame->format);
+
+    ist->subtitle_kickoff.last_pts = pts;
+
+    send_frame_to_filters(ist, frame);
+
+    av_frame_free(&frame);
+}
+
+// Opposed to the earlier "subtitle hearbeat", this is primarily aimed at
+// sending an initial subtitle frame to the filters for propagating the initial
+// timing values and to avoid that too much time is spent on a single "HW" decoder
+// which could overflow its buffer pool otherwise
+static void subtitle_kickoff(InputStream *ist, int64_t pts)
+{
+    int i;
+    int64_t pts2;
+    int64_t diff;
+
+    if (ist->st->codecpar->codec_type != AVMEDIA_TYPE_VIDEO)
+        return;
+
+    for (i = 0; i < nb_input_streams; i++) {
+        InputStream *ist2 = input_streams[i];
+        unsigned j, nb_reqs;
+
+        if (!ist2->subtitle_kickoff.is_active)
+            continue;
+        /* It's primarily the initial send that matters and which propagates
+         * the right start times to subtitle filters depending on input from decoding */
+        diff = av_rescale_q(5000, (AVRational){ 1, 1000 }, ist2->st->time_base);
+        pts2 = av_rescale_q(pts, ist->st->time_base, ist2->st->time_base);
+
+        /* do not send a kickoff frame if the subtitle is already ahead */
+        if (pts2 - diff <= ist2->subtitle_kickoff.last_pts)
+            continue;
+
+        for (j = 0, nb_reqs = 0; j < ist2->nb_filters; j++) {
+            if (ist2->filters[j]->filter == NULL) {
+                subtitle_send_kickoff(ist2, pts2);
+                return;
+            }
+            nb_reqs += av_buffersrc_get_nb_failed_requests(ist2->filters[j]->filter);
+        }
+        if (nb_reqs) {
+            av_log(NULL, AV_LOG_WARNING, "subtitle_kickoff: resend - pts: %"PRIi64"\n", pts2);
+            subtitle_send_kickoff(ist2, pts2);
+        }
+    }
+}
+
+static InputStream *get_input_stream(OutputStream *ost)
+{
+    if (ost->source_index >= 0)
+        return input_streams[ost->source_index];
+    return NULL;
+}
+
+static int decode_subtitles(InputStream *ist, AVPacket *pkt, int *got_output,
                                int *decode_failed)
 {
-    AVSubtitle subtitle;
-    int free_sub = 1;
-    int i, ret = avcodec_decode_subtitle2(ist->dec_ctx,
-                                          &subtitle, got_output, pkt);
+    AVFrame *decoded_frame;
+    AVCodecContext *avctx = ist->dec_ctx;
+    int i = 0, ret = 0, err = 0;
+    int64_t pts;
+
+    if (!ist->decoded_frame && !((ist->decoded_frame = av_frame_alloc())))
+        return AVERROR(ENOMEM);
+    decoded_frame = ist->decoded_frame;
+    decoded_frame->type = AVMEDIA_TYPE_SUBTITLE;
+    decoded_frame->format = (uint16_t)avctx->subtitle_type;
+
+    if (!ist->subtitle_header && avctx->subtitle_header && avctx->subtitle_header_size > 0) {
+        ist->subtitle_header = av_buffer_allocz(avctx->subtitle_header_size + 1);
+        if (!ist->subtitle_header)
+            return AVERROR(ENOMEM);
+        memcpy(ist->subtitle_header->data, avctx->subtitle_header, avctx->subtitle_header_size);
+    }
+
+    ret = decode(avctx, decoded_frame, got_output, pkt);
 
-    check_decode_result(NULL, got_output, ret);
+    if (ret != AVERROR_EOF)
+        check_decode_result(NULL, got_output, ret);
 
     if (ret < 0 || !*got_output) {
         *decode_failed = 1;
-        if (!pkt->size)
-            sub2video_flush(ist);
+        if (!pkt->size) {
+            // Flush
+            for (i = 0; i < ist->nb_filters; i++) {
+                if (ist->filters[i]->filter != NULL) {
+                    ret = av_buffersrc_add_frame(ist->filters[i]->filter, NULL);
+                    if (ret != AVERROR_EOF && ret < 0)
+                        av_log(NULL, AV_LOG_WARNING, "Flush the frame error.\n");
+                }
+            }
+        }
         return ret;
     }
 
     if (ist->fix_sub_duration) {
-        int end = 1;
-        if (ist->prev_sub.got_output) {
-            end = av_rescale(subtitle.pts - ist->prev_sub.subtitle.pts,
-                             1000, AV_TIME_BASE);
-            if (end < ist->prev_sub.subtitle.end_display_time) {
-                av_log(ist->dec_ctx, AV_LOG_DEBUG,
-                       "Subtitle duration reduced from %"PRId32" to %d%s\n",
-                       ist->prev_sub.subtitle.end_display_time, end,
-                       end <= 0 ? ", dropping it" : "");
-                ist->prev_sub.subtitle.end_display_time = end;
+        int64_t end = 1;
+        if (ist->prev_sub.got_output && ist->prev_sub.subtitle) {
+
+            const int64_t duration = ist->prev_sub.subtitle->subtitle_timing.duration;
+            end = decoded_frame->subtitle_timing.start_pts - ist->prev_sub.subtitle->subtitle_timing.start_pts;
+
+            if (end < duration) {
+                av_log(avctx, AV_LOG_DEBUG, "Subtitle duration reduced from %"PRId64" to %"PRId64"%s\n",
+                    duration, end, end <= 0 ? ", dropping it" : "");
+                ist->prev_sub.subtitle->subtitle_timing.duration = end;
             }
         }
-        FFSWAP(int,        *got_output, ist->prev_sub.got_output);
-        FFSWAP(int,        ret,         ist->prev_sub.ret);
-        FFSWAP(AVSubtitle, subtitle,    ist->prev_sub.subtitle);
+        FFSWAP(int,        *got_output,        ist->prev_sub.got_output);
+        FFSWAP(int,        ret,                ist->prev_sub.ret);
+        FFSWAP(AVFrame*,   ist->decoded_frame, ist->prev_sub.subtitle);
+        decoded_frame = ist->decoded_frame;
         if (end <= 0)
-            goto out;
+            return (int)end;
     }
 
-    if (!*got_output)
+    if (!*got_output || !decoded_frame)
         return ret;
 
-    if (ist->sub2video.frame) {
-        sub2video_update(ist, INT64_MIN, &subtitle);
-    } else if (ist->nb_filters) {
-        if (!ist->sub2video.sub_queue)
-            ist->sub2video.sub_queue = av_fifo_alloc2(8, sizeof(AVSubtitle), AV_FIFO_FLAG_AUTO_GROW);
-        if (!ist->sub2video.sub_queue)
-            exit_program(1);
+    decoded_frame->type = AVMEDIA_TYPE_SUBTITLE;
 
-        ret = av_fifo_write(ist->sub2video.sub_queue, &subtitle, 1);
-        if (ret < 0)
-            exit_program(1);
-        free_sub = 0;
-    }
+    if (decoded_frame->format == AV_SUBTITLE_FMT_UNKNOWN)
+        decoded_frame->format = (uint16_t)avctx->subtitle_type;
+
+    if ((ret = av_buffer_replace(&decoded_frame->subtitle_header, ist->subtitle_header)) < 0)
+        return ret;
 
-    if (!subtitle.num_rects)
-        goto out;
+    decoded_frame->pts = av_rescale_q(decoded_frame->subtitle_timing.start_pts, AV_TIME_BASE_Q, ist->st->time_base);
 
-    ist->frames_decoded++;
+    pts     = av_rescale_q(decoded_frame->subtitle_timing.start_pts,
+                           AV_TIME_BASE_Q, ist->st->time_base);
 
-    for (i = 0; i < nb_output_streams; i++) {
-        OutputStream *ost = output_streams[i];
+    ist->subtitle_kickoff.last_pts = decoded_frame->pts = pts;
 
-        if (!check_output_constraints(ist, ost) || !ost->encoding_needed
-            || ost->enc->type != AVMEDIA_TYPE_SUBTITLE)
-            continue;
+    if (ist->nb_filters > 0) {
+        AVFrame *filter_frame = av_frame_clone(decoded_frame);
+        if (!filter_frame) {
+            err = AVERROR(ENOMEM);
+            goto end;
+        }
 
-        do_subtitle_out(output_files[ost->file_index], ost, &subtitle);
+        err = send_frame_to_filters(ist, filter_frame);
+        av_frame_free(&filter_frame);
     }
 
-out:
-    if (free_sub)
-        avsubtitle_free(&subtitle);
-    return ret;
+    if (err >= 0) {
+        for (i = 0; i < nb_output_streams; i++) {
+            OutputStream *ost = output_streams[i];
+            InputStream *ist_src = get_input_stream(ost);
+
+            if (!ist_src || !check_output_constraints(ist, ost)
+                || ist_src != ist
+                || !ost->encoding_needed
+                || ost->enc->type != AVMEDIA_TYPE_SUBTITLE)
+                continue;
+
+            if (ost->filter && ost->filter->filter->nb_inputs > 0)
+                continue;
+
+            do_subtitle_out(output_files[ost->file_index], ost, decoded_frame);
+        }
+    }
+
+end:
+    av_frame_unref(decoded_frame);
+    return err < 0 ? err : ret;
 }
 
 static int send_filter_eof(InputStream *ist)
@@ -2447,7 +2472,7 @@ static int process_input_packet(InputStream *ist, const AVPacket *pkt, int no_eo
         case AVMEDIA_TYPE_SUBTITLE:
             if (repeating)
                 break;
-            ret = transcode_subtitles(ist, avpkt, &got_output, &decode_failed);
+            ret = decode_subtitles(ist, avpkt, &got_output, &decode_failed);
             if (!pkt && ret >= 0)
                 ret = AVERROR_EOF;
             av_packet_unref(avpkt);
@@ -2664,13 +2689,6 @@ static int init_input_stream(int ist_index, char *error, int error_len)
     return 0;
 }
 
-static InputStream *get_input_stream(OutputStream *ost)
-{
-    if (ost->source_index >= 0)
-        return input_streams[ost->source_index];
-    return NULL;
-}
-
 static int compare_int64(const void *a, const void *b)
 {
     return FFDIFFSIGN(*(const int64_t *)a, *(const int64_t *)b);
@@ -3097,7 +3115,7 @@ static int init_output_stream_encode(OutputStream *ost, AVFrame *frame)
         break;
     case AVMEDIA_TYPE_SUBTITLE:
         enc_ctx->time_base = AV_TIME_BASE_Q;
-        if (!enc_ctx->width) {
+        if (!enc_ctx->width && ost->source_index >= 0) {
             enc_ctx->width     = input_streams[ost->source_index]->st->codecpar->width;
             enc_ctx->height    = input_streams[ost->source_index]->st->codecpar->height;
         }
@@ -3114,6 +3132,17 @@ static int init_output_stream_encode(OutputStream *ost, AVFrame *frame)
     return 0;
 }
 
+static enum AVSubtitleType get_subtitle_format(const AVCodecDescriptor *codec_descriptor)
+{
+    if(codec_descriptor->props & AV_CODEC_PROP_BITMAP_SUB)
+        return AV_SUBTITLE_FMT_BITMAP;
+
+    if(codec_descriptor->props & AV_CODEC_PROP_TEXT_SUB)
+        return AV_SUBTITLE_FMT_ASS;
+
+    return AV_SUBTITLE_FMT_UNKNOWN;
+}
+
 static int init_output_stream(OutputStream *ost, AVFrame *frame,
                               char *error, int error_len)
 {
@@ -3150,19 +3179,13 @@ static int init_output_stream(OutputStream *ost, AVFrame *frame,
         }
 
         if (ist && ist->dec->type == AVMEDIA_TYPE_SUBTITLE && ost->enc->type == AVMEDIA_TYPE_SUBTITLE) {
-            int input_props = 0, output_props = 0;
-            AVCodecDescriptor const *input_descriptor =
-                avcodec_descriptor_get(dec->codec_id);
-            AVCodecDescriptor const *output_descriptor =
-                avcodec_descriptor_get(ost->enc_ctx->codec_id);
-            if (input_descriptor)
-                input_props = input_descriptor->props & (AV_CODEC_PROP_TEXT_SUB | AV_CODEC_PROP_BITMAP_SUB);
-            if (output_descriptor)
-                output_props = output_descriptor->props & (AV_CODEC_PROP_TEXT_SUB | AV_CODEC_PROP_BITMAP_SUB);
-            if (input_props && output_props && input_props != output_props) {
-                snprintf(error, error_len,
-                         "Subtitle encoding currently only possible from text to text "
-                         "or bitmap to bitmap");
+            AVCodecDescriptor const *output_descriptor    = avcodec_descriptor_get(ost->enc_ctx->codec_id);
+            const enum AVSubtitleType in_subtitle_format  = (uint16_t)dec->subtitle_type;
+            const enum AVSubtitleType out_subtitle_format = output_descriptor ? get_subtitle_format(output_descriptor) : AV_SUBTITLE_FMT_UNKNOWN;
+
+            if (ist->nb_filters == 0 && in_subtitle_format != AV_SUBTITLE_FMT_UNKNOWN && out_subtitle_format != AV_SUBTITLE_FMT_UNKNOWN
+                && in_subtitle_format != out_subtitle_format) {
+                snprintf(error, error_len, "Subtitle encoding is only possible from text to text or bitmap to bitmap");
                 return AVERROR_INVALIDDATA;
             }
         }
@@ -3497,7 +3520,7 @@ static OutputStream *choose_output(void)
                        av_rescale_q(ost->last_mux_dts, ost->st->time_base,
                                     AV_TIME_BASE_Q);
         if (ost->last_mux_dts == AV_NOPTS_VALUE)
-            av_log(NULL, AV_LOG_DEBUG,
+            av_log(NULL, AV_LOG_INFO,
                 "cur_dts is invalid st:%d (%d) [init:%d i_done:%d finish:%d] (this is harmless if it occurs once at the start per stream)\n",
                 ost->st->index, ost->st->id, ost->initialized, ost->inputs_done, ost->finished);
 
@@ -4166,7 +4189,7 @@ static int process_input(int file_index)
                av_ts2timestr(input_files[ist->file_index]->ts_offset, &AV_TIME_BASE_Q));
     }
 
-    sub2video_heartbeat(ist, pkt->pts);
+    subtitle_kickoff(ist, pkt->pts);
 
     process_input_packet(ist, pkt, 0);
 
@@ -4378,6 +4401,7 @@ static int transcode(void)
     /* at the end of stream, we must flush the decoder buffers */
     for (i = 0; i < nb_input_streams; i++) {
         ist = input_streams[i];
+        ist->subtitle_kickoff.is_active = 0;
         if (!input_files[ist->file_index]->eof_reached) {
             process_input_packet(ist, NULL, 0);
         }
diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h
index 7326193caf..d18647be58 100644
--- a/fftools/ffmpeg.h
+++ b/fftools/ffmpeg.h
@@ -351,17 +351,16 @@ typedef struct InputStream {
     struct { /* previous decoded subtitle and related variables */
         int got_output;
         int ret;
-        AVSubtitle subtitle;
+        AVFrame *subtitle;
     } prev_sub;
 
-    struct sub2video {
+    struct subtitle_kickoff {
+        int is_active;
         int64_t last_pts;
-        int64_t end_pts;
-        AVFifo *sub_queue;    ///< queue of AVSubtitle* before filter init
-        AVFrame *frame;
         int w, h;
-        unsigned int initialize; ///< marks if sub2video_update should force an initialization
-    } sub2video;
+    } subtitle_kickoff;
+
+    AVBufferRef *subtitle_header;
 
     /* decoded data from this stream goes into all those filters
      * currently video and audio only */
@@ -466,6 +465,8 @@ typedef struct OutputStream {
     int64_t first_pts;
     /* dts of the last packet sent to the muxer */
     int64_t last_mux_dts;
+    /* subtitle_pts values of the last subtitle frame having arrived for encoding */
+    int64_t last_subtitle_pts;
     // the timebase of the packets sent to the muxer
     AVRational mux_timebase;
     AVRational enc_timebase;
@@ -672,8 +673,6 @@ int filtergraph_is_simple(FilterGraph *fg);
 int init_simple_filtergraph(InputStream *ist, OutputStream *ost);
 int init_complex_filtergraph(FilterGraph *fg);
 
-void sub2video_update(InputStream *ist, int64_t heartbeat_pts, AVSubtitle *sub);
-
 int ifilter_parameters_from_frame(InputFilter *ifilter, const AVFrame *frame);
 
 int ffmpeg_parse_options(int argc, char **argv);
diff --git a/fftools/ffmpeg_filter.c b/fftools/ffmpeg_filter.c
index 0845c631a5..d0902af70b 100644
--- a/fftools/ffmpeg_filter.c
+++ b/fftools/ffmpeg_filter.c
@@ -22,6 +22,8 @@
 
 #include "ffmpeg.h"
 
+#include "libavutil/ass_split_internal.h"
+
 #include "libavfilter/avfilter.h"
 #include "libavfilter/buffersink.h"
 #include "libavfilter/buffersrc.h"
@@ -30,11 +32,9 @@
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
 #include "libavutil/channel_layout.h"
-#include "libavutil/display.h"
 #include "libavutil/opt.h"
 #include "libavutil/pixdesc.h"
 #include "libavutil/pixfmt.h"
-#include "libavutil/imgutils.h"
 #include "libavutil/samplefmt.h"
 
 // FIXME: YUV420P etc. are actually supported with full color range,
@@ -232,9 +232,8 @@ static void init_input_filter(FilterGraph *fg, AVFilterInOut *in)
     InputFilter *ifilter;
     int i;
 
-    // TODO: support other filter types
-    if (type != AVMEDIA_TYPE_VIDEO && type != AVMEDIA_TYPE_AUDIO) {
-        av_log(NULL, AV_LOG_FATAL, "Only video and audio filters supported "
+    if (type != AVMEDIA_TYPE_VIDEO && type != AVMEDIA_TYPE_AUDIO && type != AVMEDIA_TYPE_SUBTITLE) {
+        av_log(NULL, AV_LOG_FATAL, "Only video, audio and subtitle filters supported "
                "currently.\n");
         exit_program(1);
     }
@@ -255,8 +254,9 @@ static void init_input_filter(FilterGraph *fg, AVFilterInOut *in)
         for (i = 0; i < s->nb_streams; i++) {
             enum AVMediaType stream_type = s->streams[i]->codecpar->codec_type;
             if (stream_type != type &&
-                !(stream_type == AVMEDIA_TYPE_SUBTITLE &&
-                  type == AVMEDIA_TYPE_VIDEO /* sub2video hack */))
+                // in the followng case we auto-insert the graphicsub2video conversion filter
+                // for retaining compatibility with the previous sub2video hack
+                !(stream_type == AVMEDIA_TYPE_SUBTITLE && type == AVMEDIA_TYPE_VIDEO))
                 continue;
             if (check_stream_specifier(s, s->streams[i], *p == ':' ? p + 1 : p) == 1) {
                 st = s->streams[i];
@@ -423,6 +423,39 @@ static int insert_filter(AVFilterContext **last_filter, int *pad_idx,
     return 0;
 }
 
+static int configure_output_subtitle_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out)
+{
+    OutputStream *ost = ofilter->ost;
+    AVFilterContext *last_filter = out->filter_ctx;
+    int pad_idx = out->pad_idx;
+    int ret;
+    char name[255];
+
+    snprintf(name, sizeof(name), "out_%d_%d", ost->file_index, ost->index);
+    ret = avfilter_graph_create_filter(&ofilter->filter,
+                                       avfilter_get_by_name("sbuffersink"),
+                                       name, NULL, NULL, fg->graph);
+
+    if (ret < 0) {
+        av_log(NULL, AV_LOG_ERROR, "Unable to create filter sbuffersink\n");
+        return ret;
+    }
+
+    ////snprintf(name, sizeof(name), "trim_out_%d_%d",
+    ////         ost->file_index, ost->index);
+    ////ret = insert_trim(of->start_time, of->recording_time,
+    ////                  &last_filter, &pad_idx, name);
+    ////if (ret < 0)
+    ////    return ret;
+
+    ////ost->st->codecpar->codec_tag = MKTAG('a', 's', 's', 's');
+
+    if ((ret = avfilter_link(last_filter, pad_idx, ofilter->filter, 0)) < 0)
+        return ret;
+
+    return 0;
+}
+
 static int configure_output_video_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out)
 {
     OutputStream *ost = ofilter->ost;
@@ -604,7 +637,8 @@ static int configure_output_audio_filter(FilterGraph *fg, OutputFilter *ofilter,
         int i;
 
         for (i=0; i<of->ctx->nb_streams; i++)
-            if (of->ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
+            if (of->ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ||
+                of->ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)
                 break;
 
         if (i<of->ctx->nb_streams) {
@@ -638,6 +672,7 @@ static int configure_output_filter(FilterGraph *fg, OutputFilter *ofilter,
     switch (avfilter_pad_get_type(out->filter_ctx->output_pads, out->pad_idx)) {
     case AVMEDIA_TYPE_VIDEO: return configure_output_video_filter(fg, ofilter, out);
     case AVMEDIA_TYPE_AUDIO: return configure_output_audio_filter(fg, ofilter, out);
+    case AVMEDIA_TYPE_SUBTITLE: return configure_output_subtitle_filter(fg, ofilter, out);
     default: av_assert0(0); return 0;
     }
 }
@@ -657,51 +692,141 @@ void check_filter_outputs(void)
     }
 }
 
-static int sub2video_prepare(InputStream *ist, InputFilter *ifilter)
+static int configure_input_subtitle_filter(FilterGraph *fg, InputFilter *ifilter,
+                                        AVFilterInOut *in)
 {
-    AVFormatContext *avf = input_files[ist->file_index]->ctx;
-    int i, w, h;
+    AVFilterContext *last_filter;
+    const AVFilter *buffer_filt = avfilter_get_by_name("sbuffer");
+    InputStream *ist = ifilter->ist;
+    AVBPrint args;
+    char name[255];
+    int ret, pad_idx = 0;
+    int w, h;
+    AVBufferSrcParameters *par = av_buffersrc_parameters_alloc();
+    enum AVMediaType media_type;
+
+    if (!par)
+        return AVERROR(ENOMEM);
+
+    par->format = AV_PIX_FMT_NONE;
+
+    if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) {
+        av_log(NULL, AV_LOG_ERROR, "Cannot connect subtitle filter to audio input\n");
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+
+    if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO) {
+        av_log(NULL, AV_LOG_ERROR, "Cannot connect subtitle filter to video input\n");
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+
+    if (ist->st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE && ist->dec_ctx) {
+        // For subtitles, we need to set the format here. Would we leave the format
+        // at -1, it would delay processing (ifilter_has_all_input_formats()) until
+        // the first subtile frame arrives, which could never happen in the worst case
+        ifilter->format = (uint16_t)ist->dec_ctx->subtitle_type;
+    }
+
+    ist->subtitle_kickoff.is_active = 1;
 
-    /* Compute the size of the canvas for the subtitles stream.
-       If the subtitles codecpar has set a size, use it. Otherwise use the
-       maximum dimensions of the video streams in the same file. */
     w = ifilter->width;
     h = ifilter->height;
+
     if (!(w && h)) {
-        for (i = 0; i < avf->nb_streams; i++) {
-            if (avf->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
-                w = FFMAX(w, avf->streams[i]->codecpar->width);
-                h = FFMAX(h, avf->streams[i]->codecpar->height);
-            }
-        }
-        if (!(w && h)) {
-            w = FFMAX(w, 720);
-            h = FFMAX(h, 576);
-        }
-        av_log(avf, AV_LOG_INFO, "sub2video: using %dx%d canvas\n", w, h);
+        w = ist->dec_ctx->width;
+        h = ist->dec_ctx->height;
+    }
+
+    if (!(w && h) && ist->dec_ctx->subtitle_header) {
+        ASSSplitContext *ass_ctx = avpriv_ass_split((char *)ist->dec_ctx->subtitle_header);
+        ASS *ass = (ASS *)ass_ctx;
+        w = ass->script_info.play_res_x;
+        h = ass->script_info.play_res_y;
+        avpriv_ass_split_free(ass_ctx);
     }
-    ist->sub2video.w = ifilter->width  = w;
-    ist->sub2video.h = ifilter->height = h;
 
-    ifilter->width  = ist->dec_ctx->width  ? ist->dec_ctx->width  : ist->sub2video.w;
-    ifilter->height = ist->dec_ctx->height ? ist->dec_ctx->height : ist->sub2video.h;
+    ist->subtitle_kickoff.w = w;
+    ist->subtitle_kickoff.h = h;
+    av_log(ifilter, AV_LOG_INFO, "subtitle input filter: decoding size %dx%d\n", ist->subtitle_kickoff.w, ist->subtitle_kickoff.h);
 
-    /* rectangles are AV_PIX_FMT_PAL8, but we have no guarantee that the
-       palettes for all rectangles are identical or compatible */
-    ifilter->format = AV_PIX_FMT_RGB32;
+    ifilter->width = w;
+    ifilter->height = h;
+    ist->dec_ctx->width = w;
+    ist->dec_ctx->height = h;
 
-    ist->sub2video.frame = av_frame_alloc();
-    if (!ist->sub2video.frame)
-        return AVERROR(ENOMEM);
-    ist->sub2video.last_pts = INT64_MIN;
-    ist->sub2video.end_pts  = INT64_MIN;
+    ist->subtitle_kickoff.last_pts = INT64_MIN;
+
+    snprintf(name, sizeof(name), "graph %d subtitle input from stream %d:%d", fg->index,
+             ist->file_index, ist->st->index);
+
+
+    av_bprint_init(&args, 0, AV_BPRINT_SIZE_AUTOMATIC);
+    av_bprintf(&args,
+             "subtitle_type=%d:width=%d:height=%d:time_base=%d/%d:",
+             ifilter->format, ifilter->width, ifilter->height,
+             ist->st->time_base.num, ist->st->time_base.den);
+    if ((ret = avfilter_graph_create_filter(&ifilter->filter, buffer_filt, name,
+                                            args.str, NULL, fg->graph)) < 0)
+        goto fail;
 
-    /* sub2video structure has been (re-)initialized.
-       Mark it as such so that the system will be
-       initialized with the first received heartbeat. */
-    ist->sub2video.initialize = 1;
+    par->hw_frames_ctx = ifilter->hw_frames_ctx;
+    par->format = ifilter->format;
+    par->width = ifilter->width;
+    par->height = ifilter->height;
+
+    ret = av_buffersrc_parameters_set(ifilter->filter, par);
+    if (ret < 0)
+        goto fail;
+    av_freep(&par);
+    last_filter = ifilter->filter;
+
+    // This is for sub2video compatibility
+    media_type = avfilter_pad_get_type(in->filter_ctx->input_pads, in->pad_idx);
+    if (media_type == AVMEDIA_TYPE_VIDEO) {
+        int subscale_w = w, subscale_h = h;
+
+        av_log(NULL, AV_LOG_INFO, "Auto-inserting subfeed filter\n");
+        ret = insert_filter(&last_filter, &pad_idx, "subfeed", NULL);
+        if (ret < 0)
+            return ret;
+
+        if (!(subscale_w && subscale_h)) {
+            // If the subtitle frame size is unknown, try to find a video input
+            // and use its size for adding a subscale filter
+            for (int i = 0; i < fg->nb_inputs; i++) {
+                InputFilter *input = fg->inputs[i];
+                if (input->type == AVMEDIA_TYPE_VIDEO && input->width && input->height) {
+                    subscale_w = input->width;
+                    subscale_h = input->height;
+                    break;
+                }
+            }
+        }
+
+        if (subscale_w && subscale_h) {
+            char subscale_params[64];
+            snprintf(subscale_params, sizeof(subscale_params), "w=%d:h=%d", subscale_w, subscale_h);
+            ret = insert_filter(&last_filter, &pad_idx, "subscale", subscale_params);
+            if (ret < 0)
+                return ret;
+        }
+
+        av_log(NULL, AV_LOG_INFO, "Auto-inserting graphicsub2video filter\n");
+        ret = insert_filter(&last_filter, &pad_idx, "graphicsub2video", NULL);
+        if (ret < 0)
+            return ret;
+    }
+
+    if ((ret = avfilter_link(last_filter, 0, in->filter_ctx, in->pad_idx)) < 0)
+        return ret;
 
     return 0;
+fail:
+    av_freep(&par);
+
+    return ret;
 }
 
 static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter,
@@ -720,8 +845,15 @@ static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter,
     char name[255];
     int ret, pad_idx = 0;
     int64_t tsoffset = 0;
-    AVBufferSrcParameters *par = av_buffersrc_parameters_alloc();
+    AVBufferSrcParameters *par;
 
+    if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_SUBTITLE) {
+        // Automatically insert conversion filter to retain compatibility
+        // with sub2video command lines
+        return configure_input_subtitle_filter(fg, ifilter, in);
+    }
+
+    par = av_buffersrc_parameters_alloc();
     if (!par)
         return AVERROR(ENOMEM);
     memset(par, 0, sizeof(*par));
@@ -736,12 +868,6 @@ static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter,
     if (!fr.num)
         fr = av_guess_frame_rate(input_files[ist->file_index]->ctx, ist->st, NULL);
 
-    if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_SUBTITLE) {
-        ret = sub2video_prepare(ist, ifilter);
-        if (ret < 0)
-            goto fail;
-    }
-
     sar = ifilter->sample_aspect_ratio;
     if(!sar.den)
         sar = (AVRational){0,1};
@@ -753,7 +879,7 @@ static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter,
              tb.num, tb.den, sar.num, sar.den);
     if (fr.num && fr.den)
         av_bprintf(&args, ":frame_rate=%d/%d", fr.num, fr.den);
-    snprintf(name, sizeof(name), "graph %d input from stream %d:%d", fg->index,
+    snprintf(name, sizeof(name), "graph %d video input from stream %d:%d", fg->index,
              ist->file_index, ist->st->index);
 
 
@@ -952,6 +1078,7 @@ static int configure_input_filter(FilterGraph *fg, InputFilter *ifilter,
     switch (avfilter_pad_get_type(in->filter_ctx->input_pads, in->pad_idx)) {
     case AVMEDIA_TYPE_VIDEO: return configure_input_video_filter(fg, ifilter, in);
     case AVMEDIA_TYPE_AUDIO: return configure_input_audio_filter(fg, ifilter, in);
+    case AVMEDIA_TYPE_SUBTITLE: return configure_input_subtitle_filter(fg, ifilter, in);
     default: av_assert0(0); return 0;
     }
 }
@@ -969,8 +1096,9 @@ static void cleanup_filtergraph(FilterGraph *fg)
 static int filter_is_buffersrc(const AVFilterContext *f)
 {
     return f->nb_inputs == 0 &&
-           (!strcmp(f->filter->name, "buffer") ||
-            !strcmp(f->filter->name, "abuffer"));
+           (!strcmp(f->filter->name, "buffersrc") ||
+            !strcmp(f->filter->name, "abuffersrc") ||
+            !strcmp(f->filter->name, "sbuffersrc"));
 }
 
 static int graph_is_meta(AVFilterGraph *graph)
@@ -1147,18 +1275,6 @@ int configure_filtergraph(FilterGraph *fg)
         }
     }
 
-    /* process queued up subtitle packets */
-    for (i = 0; i < fg->nb_inputs; i++) {
-        InputStream *ist = fg->inputs[i]->ist;
-        if (ist->sub2video.sub_queue && ist->sub2video.frame) {
-            AVSubtitle tmp;
-            while (av_fifo_read(ist->sub2video.sub_queue, &tmp, 1) >= 0) {
-                sub2video_update(ist, INT64_MIN, &tmp);
-                avsubtitle_free(&tmp);
-            }
-        }
-    }
-
     return 0;
 
 fail:
@@ -1178,6 +1294,7 @@ int ifilter_parameters_from_frame(InputFilter *ifilter, const AVFrame *frame)
     ifilter->width               = frame->width;
     ifilter->height              = frame->height;
     ifilter->sample_aspect_ratio = frame->sample_aspect_ratio;
+    ifilter->type                = frame->type;
 
     ifilter->sample_rate         = frame->sample_rate;
     ret = av_channel_layout_copy(&ifilter->ch_layout, &frame->ch_layout);
diff --git a/fftools/ffmpeg_hw.c b/fftools/ffmpeg_hw.c
index 14e702bd92..be69d54aaf 100644
--- a/fftools/ffmpeg_hw.c
+++ b/fftools/ffmpeg_hw.c
@@ -449,7 +449,7 @@ int hw_device_setup_for_encode(OutputStream *ost)
     AVBufferRef *frames_ref = NULL;
     int i;
 
-    if (ost->filter) {
+    if (ost->filter && ost->filter->filter) {
         frames_ref = av_buffersink_get_hw_frames_ctx(ost->filter->filter);
         if (frames_ref &&
             ((AVHWFramesContext*)frames_ref->data)->format ==
diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c
index 2c1b3bd0dd..6389bb80ed 100644
--- a/fftools/ffmpeg_opt.c
+++ b/fftools/ffmpeg_opt.c
@@ -978,6 +978,7 @@ static void add_input_streams(OptionsContext *o, AVFormatContext *ic)
                 av_log(NULL, AV_LOG_FATAL, "Invalid canvas size: %s.\n", canvas_size);
                 exit_program(1);
             }
+            ist->subtitle_kickoff.is_active = 1;
             break;
         }
         case AVMEDIA_TYPE_ATTACHMENT:
@@ -2243,8 +2244,9 @@ static void init_output_filter(OutputFilter *ofilter, OptionsContext *o,
     switch (ofilter->type) {
     case AVMEDIA_TYPE_VIDEO: ost = new_video_stream(o, oc, -1); break;
     case AVMEDIA_TYPE_AUDIO: ost = new_audio_stream(o, oc, -1); break;
+    case AVMEDIA_TYPE_SUBTITLE: ost = new_subtitle_stream(o, oc, -1); break;
     default:
-        av_log(NULL, AV_LOG_FATAL, "Only video and audio filters are supported "
+        av_log(NULL, AV_LOG_FATAL, "Only video, audio and subtitle filters are supported "
                "currently.\n");
         exit_program(1);
     }
diff --git a/tests/ref/fate/filter-overlay-dvdsub-2397 b/tests/ref/fate/filter-overlay-dvdsub-2397
index 7df4f50776..7b827956f1 100644
--- a/tests/ref/fate/filter-overlay-dvdsub-2397
+++ b/tests/ref/fate/filter-overlay-dvdsub-2397
@@ -489,368 +489,368 @@
 1,       3877,       3877,       10,     2013, 0x95a39f9c
 1,       3887,       3887,       10,     2013, 0x4f7ea123
 1,       3897,       3897,       10,     2013, 0x9efb9ba1
-0,        117,        117,        1,   518400, 0xbf8523da
+0,        117,        117,        1,   518400, 0xc44e1d4c
 1,       3907,       3907,       10,     2013, 0xf395b2cd
 1,       3917,       3917,       10,     2013, 0x261a881e
 1,       3927,       3927,       10,     2013, 0x7f2d9f72
 1,       3937,       3937,       10,     2013, 0x0105b38d
-0,        118,        118,        1,   518400, 0x41890ed6
+0,        118,        118,        1,   518400, 0xad1a084c
 1,       3952,       3952,       10,     2013, 0x0e5db67e
 1,       3962,       3962,       10,     2013, 0xfc9baf97
-0,        119,        119,        1,   518400, 0x588534fc
+0,        119,        119,        1,   518400, 0x52d52e73
 1,       3972,       3972,       10,     2013, 0x8e02a1b1
 1,       3982,       3982,       10,     2013, 0x6eecaac8
 1,       3992,       3992,       10,     2013, 0xf5558f0c
 1,       4002,       4002,       10,     2013, 0x512ba99b
-0,        120,        120,        1,   518400, 0x2145ebc1
+0,        120,        120,        1,   518400, 0xc732e546
 1,       4012,       4012,       10,     2013, 0x932b9932
 1,       4022,       4022,       10,     2013, 0xc01ea987
-0,        121,        121,        1,   518400, 0x28bca595
+0,        121,        121,        1,   518400, 0xc36f9f14
 1,       4038,       4038,       10,     2013, 0x10879cf7
 1,       4048,       4048,       10,     2013, 0x90679338
 1,       4058,       4058,       10,     2013, 0x077d8a9e
 1,       4068,       4068,       10,     2013, 0x969fa57c
-0,        122,        122,        1,   518400, 0x77dc951e
+0,        122,        122,        1,   518400, 0x78428e8b
 1,       4078,       4078,       10,     2013, 0xe049ab07
 1,       4088,       4088,       10,     2013, 0xf535b3b3
 1,       4098,       4098,       10,     2013, 0xfe76bd37
-0,        123,        123,        1,   518400, 0xe8924c17
+0,        123,        123,        1,   518400, 0xf0f8458d
 1,       4108,       4108,       10,     2013, 0xde79ad8c
 1,       4123,       4123,       10,     2013, 0xe89b9c47
 1,       4133,       4133,       10,     2013, 0xc570b0f0
-0,        124,        124,        1,   518400, 0xadb4cccc
+0,        124,        124,        1,   518400, 0x7083c653
 1,       4143,       4143,       10,     2013, 0xee709cd9
 1,       4153,       4153,       10,     2013, 0xcfe5afab
 1,       4163,       4163,       10,     2013, 0x98ff8ce4
-0,        125,        125,        1,   518400, 0x1d7b56ac
+0,        125,        125,        1,   518400, 0xa105502c
 1,       4173,       4173,       10,     2013, 0x9d19b44c
 1,       4183,       4183,       10,     2013, 0x4349917a
 1,       4193,       4193,       10,     2013, 0xbf54a59a
-0,        126,        126,        1,   518400, 0xad5739a4
+0,        126,        126,        1,   518400, 0xd411331a
 1,       4208,       4208,       10,     2013, 0xc4a399e0
 1,       4218,       4218,       10,     2013, 0x1bf58ff0
 1,       4228,       4228,       10,     2013, 0x3518ac56
-0,        127,        127,        1,   518400, 0x2733d35a
+0,        127,        127,        1,   518400, 0x83b0ccdb
 1,       4238,       4238,       10,     2013, 0xcd38c1de
 1,       4248,       4248,       10,     2013, 0xbe7d9c4d
 1,       4258,       4258,       10,     2013, 0xe113a306
 1,       4268,       4268,       10,     2013, 0x083197ea
-0,        128,        128,        1,   518400, 0x78e76da2
+0,        128,        128,        1,   518400, 0xa9be671a
 1,       4278,       4278,       10,     2013, 0x1929b1eb
 1,       4294,       4294,       10,     2013, 0x5d6ea5af
 1,       4304,       4304,       10,     2013, 0x05519d53
-0,        129,        129,        1,   518400, 0x6c076013
+0,        129,        129,        1,   518400, 0xaeb75983
 1,       4314,       4314,       10,     2013, 0x5773b380
 1,       4324,       4324,       10,     2013, 0xaa70a8f5
 1,       4334,       4334,       10,     2013, 0x990db0ec
-0,        130,        130,        1,   518400, 0x7854f2b1
+0,        130,        130,        1,   518400, 0x81f8ec13
 1,       4344,       4344,       10,     2013, 0x91d3a623
 1,       4354,       4354,       10,     2013, 0xc91f9824
 1,       4364,       4364,       10,     2013, 0x1d058abf
-0,        131,        131,        1,   518400, 0xd2ae1ecd
+0,        131,        131,        1,   518400, 0x8aaa1839
 1,       4379,       4379,       10,     2013, 0x8de1b8d5
 1,       4389,       4389,       10,     2013, 0x7872b06b
 1,       4399,       4399,       10,     2013, 0xa084c203
-0,        132,        132,        1,   518400, 0xf5eab38d
+0,        132,        132,        1,   518400, 0xc98bacf5
 1,       4409,       4409,       10,     2013, 0xff90ae8d
 1,       4419,       4419,       10,     2013, 0x61dead8e
 1,       4429,       4429,       10,     2013, 0xee76b284
-0,        133,        133,        1,   518400, 0x994d3e9c
+0,        133,        133,        1,   518400, 0x31083804
 1,       4439,       4439,       10,     2013, 0xe888af7f
 1,       4449,       4449,       10,     2013, 0x5d57b115
 1,       4464,       4464,       10,     2013, 0xcdbfb1d0
-0,        134,        134,        1,   518400, 0x95ab705a
+0,        134,        134,        1,   518400, 0x540a69dc
 1,       4474,       4474,       10,     2013, 0x2e28a952
 1,       4484,       4484,       10,     2013, 0x4795a994
 1,       4494,       4494,       10,     2013, 0x7e7ea304
 1,       4504,       4504,       10,     2013, 0x9502c1e1
-0,        135,        135,        1,   518400, 0x3c83c5ce
+0,        135,        135,        1,   518400, 0x80d3bf46
 1,       4514,       4514,       10,     2013, 0xf7c78ab2
 1,       4524,       4524,       10,     2013, 0x24049816
 1,       4534,       4534,       10,     2013, 0x52089dcf
-0,        136,        136,        1,   518400, 0xfa22c508
+0,        136,        136,        1,   518400, 0x2967be7f
 1,       4550,       4550,       10,     2013, 0x2150a0b1
 1,       4560,       4560,       10,     2013, 0x3c2e9b93
 1,       4570,       4570,       10,     2013, 0x491f932b
-0,        137,        137,        1,   518400, 0xddda1712
+0,        137,        137,        1,   518400, 0x5a3b1092
 1,       4580,       4580,       10,     2013, 0x31359cf8
 1,       4590,       4590,       10,     2013, 0x1b00ac3f
 1,       4600,       4600,       10,     2013, 0x8d7ab3cb
-0,        138,        138,        1,   518400, 0x985a3b93
+0,        138,        138,        1,   518400, 0x8741350b
 1,       4610,       4610,       10,     2013, 0xb2c2a4de
 1,       4620,       4620,       10,     2013, 0x80a4abf2
 1,       4635,       4635,       10,     2013, 0x0701a4ee
-0,        139,        139,        1,   518400, 0xea63c5e7
+0,        139,        139,        1,   518400, 0xd5a9bf60
 1,       4645,       4645,       10,     2013, 0xdc1ba5bc
 1,       4655,       4655,       10,     2013, 0x6083a8a4
 1,       4665,       4665,       10,     2013, 0x6226ad45
-0,        140,        140,        1,   518400, 0xef64983d
+0,        140,        140,        1,   518400, 0xc05f91ba
 1,       4675,       4675,       10,     2013, 0x2732a205
 1,       4685,       4685,       10,     2013, 0x0f62a0d3
 1,       4695,       4695,       10,     2013, 0xc1799249
-0,        141,        141,        1,   518400, 0x747bb193
+0,        141,        141,        1,   518400, 0x3fdaab0b
 1,       4705,       4705,       10,     2013, 0xbccfa9c8
 1,       4720,       4720,       10,     2013, 0xded096e7
 1,       4730,       4730,       10,     2013, 0x7f0daf43
-0,        142,        142,        1,   518400, 0xb8748862
+0,        142,        142,        1,   518400, 0xab7281d9
 1,       4740,       4740,       10,     2013, 0xc47ea682
 1,       4750,       4750,       10,     2013, 0x5a72b07a
 1,       4760,       4760,       10,     2013, 0x386faa8c
 1,       4770,       4770,       10,     2013, 0xf9919a91
-0,        143,        143,        1,   518400, 0xaab55a5f
+0,        143,        143,        1,   518400, 0xc80053d6
 1,       4780,       4780,       10,     2013, 0x4908897e
 1,       4790,       4790,       10,     2013, 0x4882b594
-0,        144,        144,        1,   518400, 0x7b468add
+0,        144,        144,        1,   518400, 0x6526845c
 1,       4806,       4806,       10,     2013, 0x113e98d1
 1,       4816,       4816,       10,     2013, 0x5098b30d
 1,       4826,       4826,       10,     2013, 0x0ef7b857
 1,       4836,       4836,       10,     2013, 0x216ea176
-0,        145,        145,        1,   518400, 0xf2078707
+0,        145,        145,        1,   518400, 0x1b788089
 1,       4846,       4846,       10,     2013, 0xf906944a
 1,       4856,       4856,       10,     2013, 0xee9b92fb
 1,       4866,       4866,       10,     2013, 0xd6029209
-0,        146,        146,        1,   518400, 0x6a2d931e
+0,        146,        146,        1,   518400, 0xfa8e8ca9
 1,       4876,       4876,       10,     2013, 0x2256a12e
 1,       4891,       4891,       10,     2013, 0x89de8e4a
 1,       4901,       4901,       10,     2013, 0x0bf0a584
-0,        147,        147,        1,   518400, 0xbbe3c417
+0,        147,        147,        1,   518400, 0xb278bda1
 1,       4911,       4911,       10,     2013, 0x6a5ebd58
 1,       4921,       4921,       10,     2013, 0x3edd9aa4
 1,       4931,       4931,       10,     2013, 0xbd66ac26
-0,        148,        148,        1,   518400, 0x6294e449
+0,        148,        148,        1,   518400, 0xb0c3ddca
 1,       4941,       4941,       10,     2013, 0x313896ea
 1,       4951,       4951,       10,     2013, 0x6b83a6a0
 1,       4961,       4961,       10,     2013, 0x9aafb109
-0,        149,        149,        1,   518400, 0xa05721e7
+0,        149,        149,        1,   518400, 0x10351b53
 1,       4976,       4976,       10,     2013, 0x5192a85a
 1,       4986,       4986,       10,     2013, 0x1f919f79
 1,       4996,       4996,       10,     2013, 0xc0799c40
-0,        150,        150,        1,   518400, 0x37749183
+0,        150,        150,        1,   518400, 0xc1408aee
 1,       5006,       5006,       10,     2013, 0x2988bcd8
 1,       5016,       5016,       10,     2013, 0x1482913a
 1,       5026,       5026,       10,     2013, 0x74da9a94
 1,       5036,       5036,       10,     2013, 0x763eb709
-0,        151,        151,        1,   518400, 0xf9d9dca0
+0,        151,        151,        1,   518400, 0xf016d615
 1,       5046,       5046,       10,     2013, 0x1285b405
 1,       5062,       5062,       10,     2013, 0xb6ab9dfc
-0,        152,        152,        1,   518400, 0x5f8ccf08
+0,        152,        152,        1,   518400, 0xa768c892
 1,       5072,       5072,       10,     2013, 0xe4c8bf19
 1,       5082,       5082,       10,     2013, 0xabbbade8
 1,       5092,       5092,       10,     2013, 0xf8b69d89
 1,       5102,       5102,       10,     2013, 0xce04a866
-0,        153,        153,        1,   518400, 0x7303f77b
+0,        153,        153,        1,   518400, 0x11c3f11e
 1,       5112,       5112,       10,     2013, 0x07528abf
 1,       5122,       5122,       10,     2013, 0x74fb98bf
 1,       5132,       5132,       10,     2013, 0x579fb1c9
-0,        154,        154,        1,   518400, 0x22b0513f
+0,        154,        154,        1,   518400, 0xcd9a4ac4
 1,       5147,       5147,       10,     2013, 0x7ddea2ed
 1,       5157,       5157,       10,     2013, 0x296caa2c
 1,       5167,       5167,       10,     2013, 0x346d9c4f
-0,        155,        155,        1,   518400, 0x330485d2
+0,        155,        155,        1,   518400, 0x4ade7f5e
 1,       5177,       5177,       10,     2013, 0x3e1fba15
 1,       5187,       5187,       10,     2013, 0x48a2908f
 1,       5197,       5197,       10,     2013, 0xc1938d09
-0,        156,        156,        1,   518400, 0x7f83daea
+0,        156,        156,        1,   518400, 0x655dd46b
 1,       5207,       5207,       10,     2013, 0x0e96a060
 1,       5217,       5217,       10,     2013, 0x7b6a9e06
 1,       5232,       5232,       10,     2013, 0x5b779d28
-0,        157,        157,        1,   518400, 0xee19f2df
+0,        157,        157,        1,   518400, 0x5ab5ec61
 1,       5242,       5242,       10,     2013, 0xf600aca1
 1,       5252,       5252,       10,     2013, 0x3a6c9e68
 1,       5262,       5262,       10,     2013, 0x0c8dc1b0
-0,        158,        158,        1,   518400, 0xb71b1c77
+0,        158,        158,        1,   518400, 0x45dc15e6
 1,       5272,       5272,       10,     2013, 0x26beb245
 1,       5282,       5282,       10,     2013, 0x2bc09557
 1,       5292,       5292,       10,     2013, 0x27fc8845
 1,       5302,       5302,       10,     2013, 0x1025aa47
-0,        159,        159,        1,   518400, 0xbffc1856
+0,        159,        159,        1,   518400, 0x201911d3
 1,       5318,       5318,       10,     2013, 0xc2e69baa
 1,       5328,       5328,       10,     2013, 0xdb249b92
 1,       5338,       5338,       10,     2013, 0x6ccda29e
-0,        160,        160,        1,   518400, 0xabc125aa
+0,        160,        160,        1,   518400, 0x0fbc1f46
 1,       5348,       5348,       10,     2013, 0xeaf6a1cf
 1,       5358,       5358,       10,     2013, 0x509ba397
 1,       5368,       5368,       10,     2013, 0xfaf8a2df
-0,        161,        161,        1,   518400, 0x5ee467f8
+0,        161,        161,        1,   518400, 0x7e316179
 1,       5378,       5378,       10,     2013, 0x41388f28
 1,       5388,       5388,       10,     2013, 0xfe5eab39
 1,       5403,       5403,       10,     2013, 0xd5ffa066
-0,        162,        162,        1,   518400, 0x6c2cf168
+0,        162,        162,        1,   518400, 0x73bbeaed
 1,       5413,       5413,       10,     2013, 0x6813a30a
 1,       5423,       5423,       10,     2013, 0x9be89718
 1,       5433,       5433,       10,     2013, 0xaec3a27b
-0,        163,        163,        1,   518400, 0x63996b26
+0,        163,        163,        1,   518400, 0x3a7c648a
 1,       5446,       5446,       10,     2013, 0x579a983e
 1,       5456,       5456,       10,     2013, 0x98cea21f
 1,       5466,       5466,       10,     2013, 0xca77a58a
-0,        164,        164,        1,   518400, 0xb34d789a
+0,        164,        164,        1,   518400, 0x9f707209
 1,       5476,       5476,       10,     2013, 0xcbc3b1ee
 1,       5486,       5486,       10,     2013, 0xf3bb8f07
 1,       5496,       5496,       10,     2013, 0x6aeebd92
-0,        165,        165,        1,   518400, 0xf49c030f
+0,        165,        165,        1,   518400, 0x9f25fc5c
 1,       5506,       5506,       10,     2013, 0xe955a449
 1,       5516,       5516,       10,     2013, 0x9436aa5b
 1,       5531,       5531,       10,     2013, 0x4f0a8f9f
-0,        166,        166,        1,   518400, 0x092dc41a
+0,        166,        166,        1,   518400, 0x2ed8bd75
 1,       5541,       5541,       10,     2013, 0x3551b22d
 1,       5551,       5551,       10,     2013, 0x0959a3d4
 1,       5561,       5561,       10,     2013, 0x2ed5a11b
 1,       5571,       5571,       10,     2013, 0x8f52a5c3
-0,        167,        167,        1,   518400, 0x4134c577
+0,        167,        167,        1,   518400, 0xb493becb
 1,       5581,       5581,       10,     2013, 0x6552978d
 1,       5591,       5591,       10,     2013, 0x7dcca0c1
 1,       5601,       5601,       10,     2013, 0xbcd4a3c9
-0,        168,        168,        1,   518400, 0x261de1ed
+0,        168,        168,        1,   518400, 0x7df6db57
 1,       5616,       5616,       10,     2013, 0xfe41a8d8
 1,       5626,       5626,       10,     2013, 0xc85aae14
 1,       5636,       5636,       10,     2013, 0x1185b346
-0,        169,        169,        1,   518400, 0xcbc8566a
+0,        169,        169,        1,   518400, 0x1cb94fca
 1,       5646,       5646,       10,     2013, 0xf7429a0d
 1,       5656,       5656,       10,     2013, 0x48c2a160
 1,       5666,       5666,       10,     2013, 0x9d85a85d
-0,        170,        170,        1,   518400, 0x407a5c76
+0,        170,        170,        1,   518400, 0x70db55d8
 1,       5676,       5676,       10,     2013, 0xbbe89fe9
 1,       5686,       5686,       10,     2013, 0xea429fe2
 1,       5702,       5702,       10,     2013, 0x221ca1d4
-0,        171,        171,        1,   518400, 0x1ed73bb2
+0,        171,        171,        1,   518400, 0xc1d9351b
 1,       5712,       5712,       10,     2013, 0x394b925b
 1,       5722,       5722,       10,     2013, 0x556dc26f
 1,       5732,       5732,       10,     2013, 0xce21a5e1
-0,        172,        172,        1,   518400, 0x8467ddb5
+0,        172,        172,        1,   518400, 0xa4b0d717
 1,       5742,       5742,       10,     2013, 0xbc87c0a8
 1,       5752,       5752,       10,     2013, 0xbac4ac07
 1,       5762,       5762,       10,     2013, 0xdeefa4aa
 1,       5772,       5772,       10,     2013, 0x1f15b362
-0,        173,        173,        1,   518400, 0x0523dc73
+0,        173,        173,        1,   518400, 0x3730d5e9
 1,       5787,       5787,       10,     2013, 0x6406b7b2
 1,       5797,       5797,       10,     2013, 0x8030a03d
-0,        174,        174,        1,   518400, 0x81f5e895
+0,        174,        174,        1,   518400, 0x9673e1ec
 1,       5807,       5807,       10,     2013, 0x0373a5b1
 1,       5817,       5817,       10,     2013, 0x34ef93da
 1,       5827,       5827,       10,     2013, 0x94c198fe
 1,       5837,       5837,       10,     2013, 0xfefcabad
-0,        175,        175,        1,   518400, 0xfc74608d
+0,        175,        175,        1,   518400, 0x877959d5
 1,       5847,       5847,       10,     2013, 0x8755b3ec
 1,       5857,       5857,       10,     2013, 0xe436a6fd
 1,       5872,       5872,       10,     2013, 0x9cf5a11e
-0,        176,        176,        1,   518400, 0xc4e0dae0
+0,        176,        176,        1,   518400, 0x04f3d421
 1,       5882,       5882,       10,     2013, 0x03b8a98c
 1,       5892,       5892,       10,     2013, 0x6216a138
 1,       5902,       5902,       10,     2013, 0xd87b9f12
-0,        177,        177,        1,   518400, 0x98367f5b
+0,        177,        177,        1,   518400, 0x4f3078bc
 1,       5912,       5912,       10,     2013, 0x4ce99653
 1,       5922,       5922,       10,     2013, 0x6c2ea9e2
 1,       5932,       5932,       10,     2013, 0x918cae4c
-0,        178,        178,        1,   518400, 0x0f1a869d
+0,        178,        178,        1,   518400, 0x8a127ff8
 1,       5942,       5942,       10,     2013, 0xd19fa5f2
 1,       5958,       5958,       10,     2013, 0x0bdda7c6
 1,       5968,       5968,       10,     2013, 0x0f9ab0ca
-0,        179,        179,        1,   518400, 0x45b6ccf2
+0,        179,        179,        1,   518400, 0x5864c64f
 1,       5978,       5978,       10,     2013, 0x410a92b1
 1,       5988,       5988,       10,     2013, 0xcfbe9d1c
 1,       5998,       5998,       10,     2013, 0x59ed9d15
-0,        180,        180,        1,   518400, 0x5f9ccb77
+0,        180,        180,        1,   518400, 0xdaccc4c0
 1,       6008,       6008,       10,     2013, 0x4e129e27
 1,       6018,       6018,       10,     2013, 0x7bb9ac0a
 1,       6028,       6028,       10,     2013, 0x826ca82b
-0,        181,        181,        1,   518400, 0x5f15ea31
+0,        181,        181,        1,   518400, 0xd999e376
 1,       6043,       6043,       10,     2013, 0x9ad5a74b
 1,       6053,       6053,       10,     2013, 0x6c5f969a
 1,       6063,       6063,       10,     2013, 0x8479a0e5
-0,        182,        182,        1,   518400, 0x86369f27
+0,        182,        182,        1,   518400, 0x8af39876
 1,       6073,       6073,       10,     2013, 0x165298ef
 1,       6083,       6083,       10,     2013, 0xdcadb4a1
 1,       6093,       6093,       10,     2013, 0xa90e987c
 1,       6103,       6103,       10,     2013, 0x1ac5b510
-0,        183,        183,        1,   518400, 0x2e27f9fa
+0,        183,        183,        1,   518400, 0x5e72f33d
 1,       6113,       6113,       10,     2013, 0x66728d85
 1,       6128,       6128,       10,     2013, 0xe4859fc5
 1,       6138,       6138,       10,     2013, 0x9901786e
-0,        184,        184,        1,   518400, 0xc029a44d
+0,        184,        184,        1,   518400, 0x14af9d92
 1,       6148,       6148,       10,     2013, 0x6aebb406
 1,       6158,       6158,       10,     2013, 0x7d13a2cc
 1,       6168,       6168,       10,     2013, 0x99b7a8cc
-0,        185,        185,        1,   518400, 0xebee33b0
+0,        185,        185,        1,   518400, 0x50b82d10
 1,       6178,       6178,       10,     2013, 0x80b8a624
 1,       6188,       6188,       10,     2013, 0xbb6aa271
 1,       6198,       6198,       10,     2013, 0x17af9e4a
-0,        186,        186,        1,   518400, 0x19e5494f
+0,        186,        186,        1,   518400, 0xc068429c
 1,       6214,       6214,       10,     2013, 0xfaf0a8f1
 1,       6224,       6224,       10,     2013, 0xd6849b93
 1,       6234,       6234,       10,     2013, 0xe9829669
-0,        187,        187,        1,   518400, 0xf697bd7c
+0,        187,        187,        1,   518400, 0x8934b6d1
 1,       6244,       6244,       10,     2013, 0x7ec98944
 1,       6254,       6254,       10,     2013, 0x2b2099a4
 1,       6264,       6264,       10,     2013, 0x1033a82f
-0,        188,        188,        1,   518400, 0x82569002
+0,        188,        188,        1,   518400, 0x11d08947
 1,       6274,       6274,       10,     2013, 0x5ec88990
 1,       6284,       6284,       10,     2013, 0xd2a19b3d
 1,       6299,       6299,       10,     2013, 0xa377b268
-0,        189,        189,        1,   518400, 0xfcb6d707
+0,        189,        189,        1,   518400, 0x8a27d041
 1,       6309,       6309,       10,     2013, 0xfa859901
 1,       6319,       6319,       10,     2013, 0x1713955a
 1,       6329,       6329,       10,     2013, 0x70aab0da
 1,       6339,       6339,       10,     2013, 0xcdaea422
-0,        190,        190,        1,   518400, 0x82a9662b
+0,        190,        190,        1,   518400, 0xab265f7d
 1,       6349,       6349,       10,     2013, 0x65c3bf80
 1,       6359,       6359,       10,     2013, 0x1d75a55f
 1,       6369,       6369,       10,     2013, 0xa5bea4de
-0,        191,        191,        1,   518400, 0x212e16ee
+0,        191,        191,        1,   518400, 0xff491040
 1,       6384,       6384,       10,     2013, 0x184db71c
 1,       6394,       6394,       10,     2013, 0x99858ec8
 1,       6404,       6404,       10,     2013, 0xb8f2aee5
-0,        192,        192,        1,   518400, 0x2ca34dca
+0,        192,        192,        1,   518400, 0x822b4704
 1,       6414,       6414,       10,     2013, 0x4435b2ef
 1,       6424,       6424,       10,     2013, 0x8acfa6c7
 1,       6434,       6434,       10,     2013, 0x42b4c01f
-0,        193,        193,        1,   518400, 0xe9ebe0a5
+0,        193,        193,        1,   518400, 0x4523d9f4
 1,       6444,       6444,       10,     2013, 0x6e308c13
 1,       6454,       6454,       10,     2013, 0x8227a0f6
 1,       6470,       6470,       10,     2013, 0x6f12a7a2
-0,        194,        194,        1,   518400, 0x4e6b6917
+0,        194,        194,        1,   518400, 0xfc3c626e
 1,       6480,       6480,       10,     2013, 0x785392be
 1,       6490,       6490,       10,     2013, 0x81849c2b
 1,       6500,       6500,       10,     2013, 0x5cf2af65
-0,        195,        195,        1,   518400, 0x7dcf20ab
+0,        195,        195,        1,   518400, 0x237319e5
 1,       6510,       6510,       10,     2013, 0x0c6ca6b4
 1,       6520,       6520,       10,     2013, 0x412fab9f
 1,       6530,       6530,       10,     2013, 0x08e792b4
-0,        196,        196,        1,   518400, 0xf30fac97
+0,        196,        196,        1,   518400, 0x892ca5d8
 1,       6540,       6540,       10,     2013, 0x407aace3
 1,       6555,       6555,       10,     2013, 0xd26bac16
 1,       6565,       6565,       10,     2013, 0xac8bb295
-0,        197,        197,        1,   518400, 0xcb9fc692
+0,        197,        197,        1,   518400, 0xc4c0bfc7
 1,       6575,       6575,       10,     2013, 0xddd1949c
 1,       6585,       6585,       10,     2013, 0x6b26b868
 1,       6595,       6595,       10,     2013, 0x5eaba587
 1,       6605,       6605,       10,     2013, 0xef0793b9
-0,        198,        198,        1,   518400, 0x5d05601e
+0,        198,        198,        1,   518400, 0x57c85956
 1,       6615,       6615,       10,     2013, 0xdef19bd6
 1,       6625,       6625,       10,     2013, 0xca98a635
-0,        199,        199,        1,   518400, 0x456c1417
+0,        199,        199,        1,   518400, 0xd6300d46
 1,       6640,       6640,       10,     2013, 0x06269a5a
 1,       6650,       6650,       10,     2013, 0x32cb9952
 1,       6660,       6660,       10,     2013, 0xf01fa95a
 1,       6670,       6670,       10,     2013, 0xefab9e55
-0,        200,        200,        1,   518400, 0x9a0fd1ad
+0,        200,        200,        1,   518400, 0xd3dacaec
 1,       6680,       6680,       10,     2013, 0x55a3b63a
 1,       6690,       6690,       10,     2013, 0xcd36a553
 1,       6700,       6700,       10,     2013, 0x2ec19877
-0,        201,        201,        1,   518400, 0x55db9716
+0,        201,        201,        1,   518400, 0x65429052
 1,       6710,       6710,       10,     2013, 0xc18b924c
 1,       6726,       6726,       10,     2013, 0xf132b04c
 1,       6736,       6736,       10,     2013, 0x7975a44d
-0,        202,        202,        1,   518400, 0x1f0d40d6
+0,        202,        202,        1,   518400, 0xec803a15
 1,       6746,       6746,       10,     2013, 0x2aaf94cb
 1,       6756,       6756,       10,     2013, 0x58cfa60f
 1,       6766,       6766,       10,     2013, 0x9757a658
-0,        203,        203,        1,   518400, 0x73695c82
+0,        203,        203,        1,   518400, 0x7a9a55c9
 1,       6776,       6776,       10,     2013, 0x67ebc0d5
 1,       6786,       6786,       10,     2013, 0x3c50a70e
 1,       6796,       6796,       10,     2013, 0x9c5799c6
-0,        204,        204,        1,   518400, 0xb0f10812
+0,        204,        204,        1,   518400, 0xcac30160
 1,       6811,       6811,       10,     2013, 0x018d85b2
 1,       6821,       6821,       10,     2013, 0x5367a956
-0,        205,        205,        1,   518400, 0xdec18505
-0,        208,        208,        1,   518400, 0xb147b947
-0,        240,        240,        1,   518400, 0x9d2e3977
+0,        205,        205,        1,   518400, 0x7e187e4f
+0,        208,        208,        1,   518400, 0x0be0b2a2
+0,        213,        213,        1,   518400, 0x0be0b2a2
diff --git a/tests/ref/fate/sub-dvb b/tests/ref/fate/sub-dvb
index cbd1801d64..8f33c75d70 100644
--- a/tests/ref/fate/sub-dvb
+++ b/tests/ref/fate/sub-dvb
@@ -1,75 +1,93 @@
 #tb 0: 1/1000000
 #media_type 0: subtitle
 #codec_id 0: dvb_subtitle
-0,   15600000,   15600000,   159000,     1168, 0xd0f89d82
-0,   15759000,   15759000,   159000,       14, 0x064900eb
-0,   15760000,   15760000,   239000,     1544, 0xe60f1751
-0,   15999000,   15999000,   239000,       14, 0x0729010b
-0,   16000000,   16000000,   339000,     1658, 0xbe343093
-0,   16339000,   16339000,   339000,       14, 0x0809012b
-0,   16340000,   16340000,   599000,     2343, 0xc68f07ef
-0,   16939000,   16939000,   599000,       14, 0x08e9014b
-0,   16940000,   16940000,   459000,     2568, 0x0ee657b1
-0,   17399000,   17399000,   459000,       14, 0x09c9016b
-0,   17400000,   17400000,   359000,     3422, 0xba5b63ce
-0,   17759000,   17759000,   359000,       14, 0x0aa9018b
-0,   17760000,   17760000,   219000,     5078, 0x95b19902
-0,   17979000,   17979000,   219000,       14, 0x0b8901ab
-0,   17980000,   17980000,   959000,     5808, 0xc9717b89
-0,   18939000,   18939000,   959000,       14, 0x0c6901cb
-0,   18940000,   18940000,   219000,     6015, 0x0becbfac
-0,   19159000,   19159000,   219000,       14, 0x064900eb
-0,   19160000,   19160000,   259000,     6519, 0xfcd24d26
-0,   19419000,   19419000,   259000,       14, 0x0729010b
-0,   19420000,   19420000,    99000,     7061, 0xf0320408
-0,   19519000,   19519000,    99000,       14, 0x0809012b
-0,   19520000,   19520000,   219000,     4773, 0x66c93074
-0,   19739000,   19739000,   219000,       14, 0x08e9014b
-0,   19740000,   19740000,   219000,     5546, 0x06052c81
-0,   19959000,   19959000,   219000,       14, 0x09c9016b
-0,   19960000,   19960000,   239000,     5754, 0x904f7325
-0,   20199000,   20199000,   239000,       14, 0x0aa9018b
-0,   20200000,   20200000,   139000,     6099, 0xe30cde07
-0,   20339000,   20339000,   139000,       14, 0x0b8901ab
-0,   20340000,   20340000,   799000,     6839, 0x770fcb6c
-0,   21139000,   21139000,   799000,       14, 0x0c6901cb
-0,   21140000,   21140000,   239000,     4744, 0xa91e1b41
-0,   21379000,   21379000,   239000,       14, 0x064900eb
-0,   21380000,   21380000,   339000,     5824, 0xcf6d782b
-0,   21719000,   21719000,   339000,       14, 0x0729010b
-0,   21720000,   21720000,  1439000,     6212, 0xabf8f7cf
-0,   23159000,   23159000,  1439000,       14, 0x0809012b
-0,   23160000,   23160000,  1319000,     7082, 0xd7ca10f2
-0,   24479000,   24479000,  1319000,       14, 0x08e9014b
-0,   24480000,   24480000,   219000,     5345, 0x12b2cae0
-0,   24699000,   24699000,   219000,       14, 0x09c9016b
-0,   24700000,   24700000,   219000,     5765, 0xc7d46192
-0,   24919000,   24919000,   219000,       14, 0x0aa9018b
-0,   24920000,   24920000,   599000,     6557, 0xcb995d30
-0,   25519000,   25519000,   599000,       14, 0x0b8901ab
-0,   25520000,   25520000,   219000,     7091, 0xe6ea0559
-0,   25739000,   25739000,   219000,       14, 0x0c6901cb
-0,   25740000,   25740000,   239000,     7305, 0xb66c404e
-0,   25979000,   25979000,   239000,       14, 0x064900eb
-0,   25980000,   25980000,   359000,     7590, 0x0cc2a481
-0,   26339000,   26339000,   359000,       14, 0x0729010b
-0,   26340000,   26340000,   219000,     4629, 0xe18cfea8
-0,   26559000,   26559000,   219000,       14, 0x0809012b
-0,   26560000,   26560000,   719000,     4785, 0x82043fc0
-0,   27279000,   27279000,   719000,       14, 0x08e9014b
-0,   27280000,   27280000,   459000,     6061, 0xbde7d245
-0,   27739000,   27739000,   459000,       14, 0x09c9016b
-0,   27740000,   27740000,   239000,     6301, 0x92d01a51
-0,   27979000,   27979000,   239000,       14, 0x0aa9018b
-0,   27980000,   27980000,    99000,     6736, 0xbd25a134
-0,   28079000,   28079000,    99000,       14, 0x0b8901ab
-0,   28080000,   28080000,   219000,     7214, 0x7ef93c13
-0,   28299000,   28299000,   219000,       14, 0x0c6901cb
-0,   28300000,   28300000,   239000,     7366, 0x5bed7fcd
-0,   28539000,   28539000,   239000,       14, 0x064900eb
-0,   28540000,   28540000,   599000,     4564, 0x7f4c014b
-0,   29139000,   29139000,   599000,       14, 0x0729010b
-0,   29140000,   29140000,   219000,     4637, 0x682626b7
-0,   29359000,   29359000,   219000,       14, 0x0809012b
-0,   29360000,   29360000,  1679000,     5358, 0x29e30c48
-0,   31039000,   31039000,  1679000,       14, 0x08e9014b
+0,          0,          0,   279000,       14, 0x05d900db
+0,     279000,     279000,   279000,       14, 0x064900eb
+0,     280000,     280000,  4999000,       14, 0x06b900fb
+0,    5279000,    5279000,  4999000,       14, 0x0729010b
+0,    5280000,    5280000,  5019000,       14, 0x0799011b
+0,   10299000,   10299000,  5019000,       14, 0x0809012b
+0,   10300000,   10300000,  3599000,       14, 0x0879013b
+0,   13899000,   13899000,  3599000,       14, 0x08e9014b
+0,   13900000,   13900000,   219000,       14, 0x0959015b
+0,   14119000,   14119000,   219000,       14, 0x09c9016b
+0,   14120000,   14120000,  1439000,       14, 0x0a39017b
+0,   15559000,   15559000,  1439000,       14, 0x0aa9018b
+0,   15560000,   15560000,    39000,       14, 0x0b19019b
+0,   15599000,   15599000,    39000,       14, 0x0b8901ab
+0,   15600000,   15600000,   159000,     1168, 0xd69da022
+0,   15759000,   15759000,   159000,       14, 0x0c6901cb
+0,   15760000,   15760000,   239000,     1544, 0xc5f116f1
+0,   15999000,   15999000,   239000,       14, 0x064900eb
+0,   16000000,   16000000,   339000,     1658, 0x73563033
+0,   16339000,   16339000,   339000,       14, 0x0729010b
+0,   16340000,   16340000,   599000,     2343, 0x7ac2078f
+0,   16939000,   16939000,   599000,       14, 0x0809012b
+0,   16940000,   16940000,   459000,     2568, 0x6eaa5751
+0,   17399000,   17399000,   459000,       14, 0x08e9014b
+0,   17400000,   17400000,   359000,     3422, 0xd9d0636e
+0,   17759000,   17759000,   359000,       14, 0x09c9016b
+0,   17760000,   17760000,   219000,     5078, 0x722c9862
+0,   17979000,   17979000,   219000,       14, 0x0aa9018b
+0,   17980000,   17980000,   959000,     5808, 0x38dd7ae9
+0,   18939000,   18939000,   959000,       14, 0x0b8901ab
+0,   18940000,   18940000,   219000,     6015, 0xd4d2c40c
+0,   19159000,   19159000,   219000,       14, 0x0c6901cb
+0,   19160000,   19160000,   259000,     6519, 0x08af4c86
+0,   19419000,   19419000,   259000,       14, 0x064900eb
+0,   19420000,   19420000,    99000,     7061, 0xecf10368
+0,   19519000,   19519000,    99000,       14, 0x0729010b
+0,   19520000,   19520000,   219000,     4773, 0xbee42fd4
+0,   19739000,   19739000,   219000,       14, 0x0809012b
+0,   19740000,   19740000,   219000,     5546, 0xdb822be1
+0,   19959000,   19959000,   219000,       14, 0x08e9014b
+0,   19960000,   19960000,   239000,     5754, 0xfdcc7285
+0,   20199000,   20199000,   239000,       14, 0x09c9016b
+0,   20200000,   20200000,   139000,     6099, 0xa409dd67
+0,   20339000,   20339000,   139000,       14, 0x0aa9018b
+0,   20340000,   20340000,   799000,     6839, 0xc5eecacc
+0,   21139000,   21139000,   799000,       14, 0x0b8901ab
+0,   21140000,   21140000,   239000,     4744, 0x4e451fa1
+0,   21379000,   21379000,   239000,       14, 0x0c6901cb
+0,   21380000,   21380000,   339000,     5824, 0x5299778b
+0,   21719000,   21719000,   339000,       14, 0x064900eb
+0,   21720000,   21720000,  1439000,     6212, 0x6d15f72f
+0,   23159000,   23159000,  1439000,       14, 0x0729010b
+0,   23160000,   23160000,  1319000,     7082, 0xe5c91052
+0,   24479000,   24479000,  1319000,       14, 0x0809012b
+0,   24480000,   24480000,   219000,     5345, 0x2e5eca40
+0,   24699000,   24699000,   219000,       14, 0x08e9014b
+0,   24700000,   24700000,   219000,     5765, 0x118060f2
+0,   24919000,   24919000,   219000,       14, 0x09c9016b
+0,   24920000,   24920000,   599000,     6557, 0x89275c90
+0,   25519000,   25519000,   599000,       14, 0x0aa9018b
+0,   25520000,   25520000,   219000,     7091, 0x996904b9
+0,   25739000,   25739000,   219000,       14, 0x0b8901ab
+0,   25740000,   25740000,   239000,     7305, 0xc23e44ae
+0,   25979000,   25979000,   239000,       14, 0x0c6901cb
+0,   25980000,   25980000,   359000,     7590, 0xc5a3a3e1
+0,   26339000,   26339000,   359000,       14, 0x064900eb
+0,   26340000,   26340000,   219000,     4629, 0x7ad6fe08
+0,   26559000,   26559000,   219000,       14, 0x0729010b
+0,   26560000,   26560000,   719000,     4785, 0xcd3f3f20
+0,   27279000,   27279000,   719000,       14, 0x0809012b
+0,   27280000,   27280000,   459000,     6061, 0x8b04d1a5
+0,   27739000,   27739000,   459000,       14, 0x08e9014b
+0,   27740000,   27740000,   239000,     6301, 0xe7de19b1
+0,   27979000,   27979000,   239000,       14, 0x09c9016b
+0,   27980000,   27980000,    99000,     6736, 0x38b3a094
+0,   28079000,   28079000,    99000,       14, 0x0aa9018b
+0,   28080000,   28080000,   219000,     7214, 0x0b783b73
+0,   28299000,   28299000,   219000,       14, 0x0b8901ab
+0,   28300000,   28300000,   239000,     7366, 0x98bf842d
+0,   28539000,   28539000,   239000,       14, 0x0c6901cb
+0,   28540000,   28540000,   599000,     4564, 0x3d9600ab
+0,   29139000,   29139000,   599000,       14, 0x064900eb
+0,   29140000,   29140000,   219000,     4637, 0x01f02617
+0,   29359000,   29359000,   219000,       14, 0x0729010b
+0,   29360000,   29360000,  1679000,     5358, 0x5b0f0ba8
+0,   31039000,   31039000,  1679000,       14, 0x0809012b
+0,   31040000,   31040000,   359000,       14, 0x0879013b
+0,   31399000,   31399000,   359000,       14, 0x08e9014b
+0,   31400000,   31400000,   479000,       14, 0x0959015b
+0,   31879000,   31879000,   479000,       14, 0x09c9016b
diff --git a/tests/ref/fate/sub2video b/tests/ref/fate/sub2video
index 80abe9c905..7cc6549966 100644
--- a/tests/ref/fate/sub2video
+++ b/tests/ref/fate/sub2video
@@ -58,129 +58,1136 @@
 0,         47,         47,        1,   518400, 0xde69683f
 0,         48,         48,        1,   518400, 0x7df08fba
 0,         49,         49,        1,   518400, 0xbab197ea
+0,         50,         50,        1,   518400, 0xbab197ea
+0,         51,         51,        1,   518400, 0xbab197ea
+0,         52,         52,        1,   518400, 0xbab197ea
+0,         53,         53,        1,   518400, 0xbab197ea
+0,         54,         54,        1,   518400, 0xbab197ea
+0,         55,         55,        1,   518400, 0xbab197ea
+0,         56,         56,        1,   518400, 0xbab197ea
+0,         57,         57,        1,   518400, 0xbab197ea
+0,         58,         58,        1,   518400, 0xbab197ea
+0,         59,         59,        1,   518400, 0xbab197ea
+0,         60,         60,        1,   518400, 0xbab197ea
+0,         61,         61,        1,   518400, 0xbab197ea
+0,         62,         62,        1,   518400, 0xbab197ea
+0,         63,         63,        1,   518400, 0xbab197ea
+0,         64,         64,        1,   518400, 0xbab197ea
+0,         65,         65,        1,   518400, 0xbab197ea
+0,         66,         66,        1,   518400, 0xbab197ea
+0,         67,         67,        1,   518400, 0xbab197ea
+0,         68,         68,        1,   518400, 0xbab197ea
+0,         69,         69,        1,   518400, 0xbab197ea
+0,         70,         70,        1,   518400, 0xbab197ea
+0,         71,         71,        1,   518400, 0xbab197ea
+0,         72,         72,        1,   518400, 0xbab197ea
+0,         73,         73,        1,   518400, 0xbab197ea
+0,         74,         74,        1,   518400, 0xbab197ea
+0,         75,         75,        1,   518400, 0xbab197ea
+0,         76,         76,        1,   518400, 0xbab197ea
 1,   15355000,   15355000,  4733000,     2094, 0x3c171425
 0,         77,         77,        1,   518400, 0x902285d9
-0,        100,        100,        1,   518400, 0xbab197ea
+0,         78,         78,        1,   518400, 0x902285d9
+0,         79,         79,        1,   518400, 0x902285d9
+0,         80,         80,        1,   518400, 0x902285d9
+0,         81,         81,        1,   518400, 0x902285d9
+0,         82,         82,        1,   518400, 0x902285d9
+0,         83,         83,        1,   518400, 0x902285d9
+0,         84,         84,        1,   518400, 0x902285d9
+0,         85,         85,        1,   518400, 0x902285d9
+0,         86,         86,        1,   518400, 0x902285d9
+0,         87,         87,        1,   518400, 0x902285d9
+0,         88,         88,        1,   518400, 0x902285d9
+0,         89,         89,        1,   518400, 0x902285d9
+0,         90,         90,        1,   518400, 0x902285d9
+0,         91,         91,        1,   518400, 0x902285d9
+0,         92,         92,        1,   518400, 0x902285d9
+0,         93,         93,        1,   518400, 0x902285d9
+0,         94,         94,        1,   518400, 0x902285d9
+0,         95,         95,        1,   518400, 0x902285d9
+0,         96,         96,        1,   518400, 0x902285d9
+0,         97,         97,        1,   518400, 0x902285d9
+0,         98,         98,        1,   518400, 0x902285d9
+0,         99,         99,        1,   518400, 0x902285d9
+0,        100,        100,        1,   518400, 0x902285d9
+0,        101,        101,        1,   518400, 0xbab197ea
+0,        102,        102,        1,   518400, 0xbab197ea
+0,        103,        103,        1,   518400, 0xbab197ea
+0,        104,        104,        1,   518400, 0xbab197ea
+0,        105,        105,        1,   518400, 0xbab197ea
+0,        106,        106,        1,   518400, 0xbab197ea
+0,        107,        107,        1,   518400, 0xbab197ea
+0,        108,        108,        1,   518400, 0xbab197ea
+0,        109,        109,        1,   518400, 0xbab197ea
+0,        110,        110,        1,   518400, 0xbab197ea
+0,        111,        111,        1,   518400, 0xbab197ea
+0,        112,        112,        1,   518400, 0xbab197ea
+0,        113,        113,        1,   518400, 0xbab197ea
+0,        114,        114,        1,   518400, 0xbab197ea
+0,        115,        115,        1,   518400, 0xbab197ea
+0,        116,        116,        1,   518400, 0xbab197ea
+0,        117,        117,        1,   518400, 0xbab197ea
+0,        118,        118,        1,   518400, 0xbab197ea
+0,        119,        119,        1,   518400, 0xbab197ea
+0,        120,        120,        1,   518400, 0xbab197ea
+0,        121,        121,        1,   518400, 0xbab197ea
+0,        122,        122,        1,   518400, 0xbab197ea
+0,        123,        123,        1,   518400, 0xbab197ea
+0,        124,        124,        1,   518400, 0xbab197ea
+0,        125,        125,        1,   518400, 0xbab197ea
+0,        126,        126,        1,   518400, 0xbab197ea
+0,        127,        127,        1,   518400, 0xbab197ea
+0,        128,        128,        1,   518400, 0xbab197ea
+0,        129,        129,        1,   518400, 0xbab197ea
+0,        130,        130,        1,   518400, 0xbab197ea
+0,        131,        131,        1,   518400, 0xbab197ea
+0,        132,        132,        1,   518400, 0xbab197ea
+0,        133,        133,        1,   518400, 0xbab197ea
+0,        134,        134,        1,   518400, 0xbab197ea
+0,        135,        135,        1,   518400, 0xbab197ea
+0,        136,        136,        1,   518400, 0xbab197ea
+0,        137,        137,        1,   518400, 0xbab197ea
+0,        138,        138,        1,   518400, 0xbab197ea
+0,        139,        139,        1,   518400, 0xbab197ea
+0,        140,        140,        1,   518400, 0xbab197ea
+0,        141,        141,        1,   518400, 0xbab197ea
+0,        142,        142,        1,   518400, 0xbab197ea
+0,        143,        143,        1,   518400, 0xbab197ea
+0,        144,        144,        1,   518400, 0xbab197ea
+0,        145,        145,        1,   518400, 0xbab197ea
+0,        146,        146,        1,   518400, 0xbab197ea
+0,        147,        147,        1,   518400, 0xbab197ea
+0,        148,        148,        1,   518400, 0xbab197ea
+0,        149,        149,        1,   518400, 0xbab197ea
+0,        150,        150,        1,   518400, 0xbab197ea
+0,        151,        151,        1,   518400, 0xbab197ea
+0,        152,        152,        1,   518400, 0xbab197ea
+0,        153,        153,        1,   518400, 0xbab197ea
+0,        154,        154,        1,   518400, 0xbab197ea
+0,        155,        155,        1,   518400, 0xbab197ea
+0,        156,        156,        1,   518400, 0xbab197ea
+0,        157,        157,        1,   518400, 0xbab197ea
+0,        158,        158,        1,   518400, 0xbab197ea
+0,        159,        159,        1,   518400, 0xbab197ea
+0,        160,        160,        1,   518400, 0xbab197ea
+0,        161,        161,        1,   518400, 0xbab197ea
+0,        162,        162,        1,   518400, 0xbab197ea
+0,        163,        163,        1,   518400, 0xbab197ea
+0,        164,        164,        1,   518400, 0xbab197ea
+0,        165,        165,        1,   518400, 0xbab197ea
+0,        166,        166,        1,   518400, 0xbab197ea
+0,        167,        167,        1,   518400, 0xbab197ea
+0,        168,        168,        1,   518400, 0xbab197ea
+0,        169,        169,        1,   518400, 0xbab197ea
+0,        170,        170,        1,   518400, 0xbab197ea
+0,        171,        171,        1,   518400, 0xbab197ea
+0,        172,        172,        1,   518400, 0xbab197ea
+0,        173,        173,        1,   518400, 0xbab197ea
+0,        174,        174,        1,   518400, 0xbab197ea
+0,        175,        175,        1,   518400, 0xbab197ea
+0,        176,        176,        1,   518400, 0xbab197ea
+0,        177,        177,        1,   518400, 0xbab197ea
+0,        178,        178,        1,   518400, 0xbab197ea
+0,        179,        179,        1,   518400, 0xbab197ea
+0,        180,        180,        1,   518400, 0xbab197ea
+0,        181,        181,        1,   518400, 0xbab197ea
+0,        182,        182,        1,   518400, 0xbab197ea
+0,        183,        183,        1,   518400, 0xbab197ea
+0,        184,        184,        1,   518400, 0xbab197ea
+0,        185,        185,        1,   518400, 0xbab197ea
+0,        186,        186,        1,   518400, 0xbab197ea
+0,        187,        187,        1,   518400, 0xbab197ea
+0,        188,        188,        1,   518400, 0xbab197ea
+0,        189,        189,        1,   518400, 0xbab197ea
+0,        190,        190,        1,   518400, 0xbab197ea
+0,        191,        191,        1,   518400, 0xbab197ea
+0,        192,        192,        1,   518400, 0xbab197ea
+0,        193,        193,        1,   518400, 0xbab197ea
+0,        194,        194,        1,   518400, 0xbab197ea
+0,        195,        195,        1,   518400, 0xbab197ea
+0,        196,        196,        1,   518400, 0xbab197ea
+0,        197,        197,        1,   518400, 0xbab197ea
+0,        198,        198,        1,   518400, 0xbab197ea
+0,        199,        199,        1,   518400, 0xbab197ea
+0,        200,        200,        1,   518400, 0xbab197ea
+0,        201,        201,        1,   518400, 0xbab197ea
+0,        202,        202,        1,   518400, 0xbab197ea
+0,        203,        203,        1,   518400, 0xbab197ea
+0,        204,        204,        1,   518400, 0xbab197ea
+0,        205,        205,        1,   518400, 0xbab197ea
+0,        206,        206,        1,   518400, 0xbab197ea
+0,        207,        207,        1,   518400, 0xbab197ea
+0,        208,        208,        1,   518400, 0xbab197ea
+0,        209,        209,        1,   518400, 0xbab197ea
+0,        210,        210,        1,   518400, 0xbab197ea
+0,        211,        211,        1,   518400, 0xbab197ea
+0,        212,        212,        1,   518400, 0xbab197ea
+0,        213,        213,        1,   518400, 0xbab197ea
+0,        214,        214,        1,   518400, 0xbab197ea
+0,        215,        215,        1,   518400, 0xbab197ea
+0,        216,        216,        1,   518400, 0xbab197ea
+0,        217,        217,        1,   518400, 0xbab197ea
+0,        218,        218,        1,   518400, 0xbab197ea
+0,        219,        219,        1,   518400, 0xbab197ea
+0,        220,        220,        1,   518400, 0xbab197ea
+0,        221,        221,        1,   518400, 0xbab197ea
+0,        222,        222,        1,   518400, 0xbab197ea
+0,        223,        223,        1,   518400, 0xbab197ea
+0,        224,        224,        1,   518400, 0xbab197ea
+0,        225,        225,        1,   518400, 0xbab197ea
+0,        226,        226,        1,   518400, 0xbab197ea
+0,        227,        227,        1,   518400, 0xbab197ea
+0,        228,        228,        1,   518400, 0xbab197ea
+0,        229,        229,        1,   518400, 0xbab197ea
+0,        230,        230,        1,   518400, 0xbab197ea
+0,        231,        231,        1,   518400, 0xbab197ea
+0,        232,        232,        1,   518400, 0xbab197ea
+0,        233,        233,        1,   518400, 0xbab197ea
+0,        234,        234,        1,   518400, 0xbab197ea
+0,        235,        235,        1,   518400, 0xbab197ea
+0,        236,        236,        1,   518400, 0xbab197ea
+0,        237,        237,        1,   518400, 0xbab197ea
+0,        238,        238,        1,   518400, 0xbab197ea
+0,        239,        239,        1,   518400, 0xbab197ea
+0,        240,        240,        1,   518400, 0xbab197ea
+0,        241,        241,        1,   518400, 0xbab197ea
+0,        242,        242,        1,   518400, 0xbab197ea
+0,        243,        243,        1,   518400, 0xbab197ea
 1,   48797000,   48797000,  2560000,     2480, 0x7c0edf21
 0,        244,        244,        1,   518400, 0x7a11c812
-0,        257,        257,        1,   518400, 0xbab197ea
+0,        245,        245,        1,   518400, 0x7a11c812
+0,        246,        246,        1,   518400, 0x7a11c812
+0,        247,        247,        1,   518400, 0x7a11c812
+0,        248,        248,        1,   518400, 0x7a11c812
+0,        249,        249,        1,   518400, 0x7a11c812
+0,        250,        250,        1,   518400, 0x7a11c812
+0,        251,        251,        1,   518400, 0x7a11c812
+0,        252,        252,        1,   518400, 0x7a11c812
+0,        253,        253,        1,   518400, 0x7a11c812
+0,        254,        254,        1,   518400, 0x7a11c812
+0,        255,        255,        1,   518400, 0x7a11c812
+0,        256,        256,        1,   518400, 0x7a11c812
+0,        257,        257,        1,   518400, 0x34cdddee
 1,   51433000,   51433000,  2366000,     3059, 0xc95b8a05
 0,        258,        258,        1,   518400, 0x34cdddee
-0,        269,        269,        1,   518400, 0xbab197ea
+0,        259,        259,        1,   518400, 0x34cdddee
+0,        260,        260,        1,   518400, 0x34cdddee
+0,        261,        261,        1,   518400, 0x34cdddee
+0,        262,        262,        1,   518400, 0x34cdddee
+0,        263,        263,        1,   518400, 0x34cdddee
+0,        264,        264,        1,   518400, 0x34cdddee
+0,        265,        265,        1,   518400, 0x34cdddee
+0,        266,        266,        1,   518400, 0x34cdddee
+0,        267,        267,        1,   518400, 0x34cdddee
+0,        268,        268,        1,   518400, 0x34cdddee
+0,        269,        269,        1,   518400, 0x34cdddee
 1,   53910000,   53910000,  2696000,     2095, 0x61bb15ed
 0,        270,        270,        1,   518400, 0x4db4ce51
-0,        283,        283,        1,   518400, 0xbab197ea
+0,        271,        271,        1,   518400, 0x4db4ce51
+0,        272,        272,        1,   518400, 0x4db4ce51
+0,        273,        273,        1,   518400, 0x4db4ce51
+0,        274,        274,        1,   518400, 0x4db4ce51
+0,        275,        275,        1,   518400, 0x4db4ce51
+0,        276,        276,        1,   518400, 0x4db4ce51
+0,        277,        277,        1,   518400, 0x4db4ce51
+0,        278,        278,        1,   518400, 0x4db4ce51
+0,        279,        279,        1,   518400, 0x4db4ce51
+0,        280,        280,        1,   518400, 0x4db4ce51
+0,        281,        281,        1,   518400, 0x4db4ce51
+0,        282,        282,        1,   518400, 0x4db4ce51
+0,        283,        283,        1,   518400, 0xe6bc0ea9
 1,   56663000,   56663000,  1262000,     1013, 0xc9ae89b7
 0,        284,        284,        1,   518400, 0xe6bc0ea9
-0,        290,        290,        1,   518400, 0xbab197ea
+0,        285,        285,        1,   518400, 0xe6bc0ea9
+0,        286,        286,        1,   518400, 0xe6bc0ea9
+0,        287,        287,        1,   518400, 0xe6bc0ea9
+0,        288,        288,        1,   518400, 0xe6bc0ea9
+0,        289,        289,        1,   518400, 0xe6bc0ea9
+0,        290,        290,        1,   518400, 0xa8643af7
 1,   58014000,   58014000,  1661000,      969, 0xe01878f0
 0,        291,        291,        1,   518400, 0xa8643af7
-0,        298,        298,        1,   518400, 0xbab197ea
+0,        292,        292,        1,   518400, 0xa8643af7
+0,        293,        293,        1,   518400, 0xa8643af7
+0,        294,        294,        1,   518400, 0xa8643af7
+0,        295,        295,        1,   518400, 0xa8643af7
+0,        296,        296,        1,   518400, 0xa8643af7
+0,        297,        297,        1,   518400, 0xa8643af7
+0,        298,        298,        1,   518400, 0xa8643af7
+0,        299,        299,        1,   518400, 0xbab197ea
+0,        300,        300,        1,   518400, 0xbab197ea
+0,        301,        301,        1,   518400, 0xbab197ea
+0,        302,        302,        1,   518400, 0xbab197ea
+0,        303,        303,        1,   518400, 0xbab197ea
+0,        304,        304,        1,   518400, 0xbab197ea
+0,        305,        305,        1,   518400, 0xbab197ea
+0,        306,        306,        1,   518400, 0xbab197ea
+0,        307,        307,        1,   518400, 0xbab197ea
+0,        308,        308,        1,   518400, 0xbab197ea
+0,        309,        309,        1,   518400, 0xbab197ea
+0,        310,        310,        1,   518400, 0xbab197ea
+0,        311,        311,        1,   518400, 0xbab197ea
+0,        312,        312,        1,   518400, 0xbab197ea
+0,        313,        313,        1,   518400, 0xbab197ea
+0,        314,        314,        1,   518400, 0xbab197ea
+0,        315,        315,        1,   518400, 0xbab197ea
+0,        316,        316,        1,   518400, 0xbab197ea
+0,        317,        317,        1,   518400, 0xbab197ea
+0,        318,        318,        1,   518400, 0xbab197ea
+0,        319,        319,        1,   518400, 0xbab197ea
+0,        320,        320,        1,   518400, 0xbab197ea
+0,        321,        321,        1,   518400, 0xbab197ea
+0,        322,        322,        1,   518400, 0xbab197ea
+0,        323,        323,        1,   518400, 0xbab197ea
+0,        324,        324,        1,   518400, 0xbab197ea
+0,        325,        325,        1,   518400, 0xbab197ea
+0,        326,        326,        1,   518400, 0xbab197ea
+0,        327,        327,        1,   518400, 0xbab197ea
+0,        328,        328,        1,   518400, 0xbab197ea
+0,        329,        329,        1,   518400, 0xbab197ea
+0,        330,        330,        1,   518400, 0xbab197ea
+0,        331,        331,        1,   518400, 0xbab197ea
+0,        332,        332,        1,   518400, 0xbab197ea
+0,        333,        333,        1,   518400, 0xbab197ea
+0,        334,        334,        1,   518400, 0xbab197ea
+0,        335,        335,        1,   518400, 0xbab197ea
+0,        336,        336,        1,   518400, 0xbab197ea
+0,        337,        337,        1,   518400, 0xbab197ea
+0,        338,        338,        1,   518400, 0xbab197ea
 1,   67724000,   67724000,  1365000,      844, 0xe7db4fc1
 0,        339,        339,        1,   518400, 0xb1885c67
-0,        345,        345,        1,   518400, 0xbab197ea
+0,        340,        340,        1,   518400, 0xb1885c67
+0,        341,        341,        1,   518400, 0xb1885c67
+0,        342,        342,        1,   518400, 0xb1885c67
+0,        343,        343,        1,   518400, 0xb1885c67
+0,        344,        344,        1,   518400, 0xb1885c67
+0,        345,        345,        1,   518400, 0xb1885c67
 1,   69175000,   69175000,  1558000,      802, 0xf48531ba
 0,        346,        346,        1,   518400, 0x378e3fd0
-0,        354,        354,        1,   518400, 0xbab197ea
+0,        347,        347,        1,   518400, 0x378e3fd0
+0,        348,        348,        1,   518400, 0x378e3fd0
+0,        349,        349,        1,   518400, 0x378e3fd0
+0,        350,        350,        1,   518400, 0x378e3fd0
+0,        351,        351,        1,   518400, 0x378e3fd0
+0,        352,        352,        1,   518400, 0x378e3fd0
+0,        353,        353,        1,   518400, 0x378e3fd0
+0,        354,        354,        1,   518400, 0xa3782469
 1,   70819000,   70819000,  1865000,     1709, 0xb4d5a1bd
 0,        355,        355,        1,   518400, 0xa3782469
-0,        363,        363,        1,   518400, 0xbab197ea
+0,        356,        356,        1,   518400, 0xa3782469
+0,        357,        357,        1,   518400, 0xa3782469
+0,        358,        358,        1,   518400, 0xa3782469
+0,        359,        359,        1,   518400, 0xa3782469
+0,        360,        360,        1,   518400, 0xa3782469
+0,        361,        361,        1,   518400, 0xa3782469
+0,        362,        362,        1,   518400, 0xa3782469
+0,        363,        363,        1,   518400, 0xa3782469
 1,   72762000,   72762000,  1968000,     2438, 0x99d7bc82
 0,        364,        364,        1,   518400, 0xba23a0d5
-0,        374,        374,        1,   518400, 0xbab197ea
+0,        365,        365,        1,   518400, 0xba23a0d5
+0,        366,        366,        1,   518400, 0xba23a0d5
+0,        367,        367,        1,   518400, 0xba23a0d5
+0,        368,        368,        1,   518400, 0xba23a0d5
+0,        369,        369,        1,   518400, 0xba23a0d5
+0,        370,        370,        1,   518400, 0xba23a0d5
+0,        371,        371,        1,   518400, 0xba23a0d5
+0,        372,        372,        1,   518400, 0xba23a0d5
+0,        373,        373,        1,   518400, 0xba23a0d5
+0,        374,        374,        1,   518400, 0x129de2f8
 1,   74806000,   74806000,  1831000,     2116, 0x96514097
 0,        375,        375,        1,   518400, 0x129de2f8
-0,        383,        383,        1,   518400, 0xbab197ea
+0,        376,        376,        1,   518400, 0x129de2f8
+0,        377,        377,        1,   518400, 0x129de2f8
+0,        378,        378,        1,   518400, 0x129de2f8
+0,        379,        379,        1,   518400, 0x129de2f8
+0,        380,        380,        1,   518400, 0x129de2f8
+0,        381,        381,        1,   518400, 0x129de2f8
+0,        382,        382,        1,   518400, 0x129de2f8
+0,        383,        383,        1,   518400, 0x129de2f8
 1,   76716000,   76716000,  1262000,     1822, 0xefccc72e
 0,        384,        384,        1,   518400, 0x19772f0f
-0,        390,        390,        1,   518400, 0xbab197ea
+0,        385,        385,        1,   518400, 0x19772f0f
+0,        386,        386,        1,   518400, 0x19772f0f
+0,        387,        387,        1,   518400, 0x19772f0f
+0,        388,        388,        1,   518400, 0x19772f0f
+0,        389,        389,        1,   518400, 0x19772f0f
+0,        390,        390,        1,   518400, 0x56f54e73
 1,   78051000,   78051000,  1524000,      987, 0x7b927a27
 0,        391,        391,        1,   518400, 0x56f54e73
-0,        398,        398,        1,   518400, 0xbab197ea
+0,        392,        392,        1,   518400, 0x56f54e73
+0,        393,        393,        1,   518400, 0x56f54e73
+0,        394,        394,        1,   518400, 0x56f54e73
+0,        395,        395,        1,   518400, 0x56f54e73
+0,        396,        396,        1,   518400, 0x56f54e73
+0,        397,        397,        1,   518400, 0x56f54e73
+0,        398,        398,        1,   518400, 0x300b5247
 1,   79644000,   79644000,  2662000,     2956, 0x190778f7
 0,        399,        399,        1,   518400, 0x300b5247
+0,        400,        400,        1,   518400, 0x300b5247
+0,        401,        401,        1,   518400, 0x300b5247
+0,        402,        402,        1,   518400, 0x300b5247
+0,        403,        403,        1,   518400, 0x300b5247
+0,        404,        404,        1,   518400, 0x300b5247
+0,        405,        405,        1,   518400, 0x300b5247
+0,        406,        406,        1,   518400, 0x300b5247
+0,        407,        407,        1,   518400, 0x300b5247
+0,        408,        408,        1,   518400, 0x300b5247
+0,        409,        409,        1,   518400, 0x300b5247
+0,        410,        410,        1,   518400, 0x300b5247
+0,        411,        411,        1,   518400, 0x300b5247
 1,   82380000,   82380000,  2764000,     3094, 0xc021b7d3
-0,        412,        412,        1,   518400, 0xbab197ea
+0,        412,        412,        1,   518400, 0x6fd028fa
 0,        413,        413,        1,   518400, 0x6fd028fa
-0,        426,        426,        1,   518400, 0xbab197ea
+0,        414,        414,        1,   518400, 0x6fd028fa
+0,        415,        415,        1,   518400, 0x6fd028fa
+0,        416,        416,        1,   518400, 0x6fd028fa
+0,        417,        417,        1,   518400, 0x6fd028fa
+0,        418,        418,        1,   518400, 0x6fd028fa
+0,        419,        419,        1,   518400, 0x6fd028fa
+0,        420,        420,        1,   518400, 0x6fd028fa
+0,        421,        421,        1,   518400, 0x6fd028fa
+0,        422,        422,        1,   518400, 0x6fd028fa
+0,        423,        423,        1,   518400, 0x6fd028fa
+0,        424,        424,        1,   518400, 0x6fd028fa
+0,        425,        425,        1,   518400, 0x6fd028fa
+0,        426,        426,        1,   518400, 0x01f80e9d
 1,   85225000,   85225000,  2366000,     2585, 0x74d0048f
 0,        427,        427,        1,   518400, 0x01f80e9d
-0,        438,        438,        1,   518400, 0xbab197ea
+0,        428,        428,        1,   518400, 0x01f80e9d
+0,        429,        429,        1,   518400, 0x01f80e9d
+0,        430,        430,        1,   518400, 0x01f80e9d
+0,        431,        431,        1,   518400, 0x01f80e9d
+0,        432,        432,        1,   518400, 0x01f80e9d
+0,        433,        433,        1,   518400, 0x01f80e9d
+0,        434,        434,        1,   518400, 0x01f80e9d
+0,        435,        435,        1,   518400, 0x01f80e9d
+0,        436,        436,        1,   518400, 0x01f80e9d
+0,        437,        437,        1,   518400, 0x01f80e9d
+0,        438,        438,        1,   518400, 0xb48d90c0
 1,   87652000,   87652000,  1831000,      634, 0x8832fda1
 0,        439,        439,        1,   518400, 0xb48d90c0
-0,        447,        447,        1,   518400, 0xbab197ea
+0,        440,        440,        1,   518400, 0xb48d90c0
+0,        441,        441,        1,   518400, 0xb48d90c0
+0,        442,        442,        1,   518400, 0xb48d90c0
+0,        443,        443,        1,   518400, 0xb48d90c0
+0,        444,        444,        1,   518400, 0xb48d90c0
+0,        445,        445,        1,   518400, 0xb48d90c0
+0,        446,        446,        1,   518400, 0xb48d90c0
+0,        447,        447,        1,   518400, 0xb48d90c0
+0,        448,        448,        1,   518400, 0xbab197ea
+0,        449,        449,        1,   518400, 0xbab197ea
+0,        450,        450,        1,   518400, 0xbab197ea
+0,        451,        451,        1,   518400, 0xbab197ea
+0,        452,        452,        1,   518400, 0xbab197ea
+0,        453,        453,        1,   518400, 0xbab197ea
+0,        454,        454,        1,   518400, 0xbab197ea
+0,        455,        455,        1,   518400, 0xbab197ea
+0,        456,        456,        1,   518400, 0xbab197ea
+0,        457,        457,        1,   518400, 0xbab197ea
 1,   91531000,   91531000,  2332000,     2080, 0x97a1146f
 0,        458,        458,        1,   518400, 0xcb5a0173
-0,        469,        469,        1,   518400, 0xbab197ea
+0,        459,        459,        1,   518400, 0xcb5a0173
+0,        460,        460,        1,   518400, 0xcb5a0173
+0,        461,        461,        1,   518400, 0xcb5a0173
+0,        462,        462,        1,   518400, 0xcb5a0173
+0,        463,        463,        1,   518400, 0xcb5a0173
+0,        464,        464,        1,   518400, 0xcb5a0173
+0,        465,        465,        1,   518400, 0xcb5a0173
+0,        466,        466,        1,   518400, 0xcb5a0173
+0,        467,        467,        1,   518400, 0xcb5a0173
+0,        468,        468,        1,   518400, 0xcb5a0173
+0,        469,        469,        1,   518400, 0xcb5a0173
+0,        470,        470,        1,   518400, 0xbab197ea
+0,        471,        471,        1,   518400, 0xbab197ea
+0,        472,        472,        1,   518400, 0xbab197ea
+0,        473,        473,        1,   518400, 0xbab197ea
+0,        474,        474,        1,   518400, 0xbab197ea
+0,        475,        475,        1,   518400, 0xbab197ea
+0,        476,        476,        1,   518400, 0xbab197ea
+0,        477,        477,        1,   518400, 0xbab197ea
 1,   95510000,   95510000,  3299000,     2964, 0x8b8f6684
 0,        478,        478,        1,   518400, 0xb8a323e4
-0,        494,        494,        1,   518400, 0xbab197ea
+0,        479,        479,        1,   518400, 0xb8a323e4
+0,        480,        480,        1,   518400, 0xb8a323e4
+0,        481,        481,        1,   518400, 0xb8a323e4
+0,        482,        482,        1,   518400, 0xb8a323e4
+0,        483,        483,        1,   518400, 0xb8a323e4
+0,        484,        484,        1,   518400, 0xb8a323e4
+0,        485,        485,        1,   518400, 0xb8a323e4
+0,        486,        486,        1,   518400, 0xb8a323e4
+0,        487,        487,        1,   518400, 0xb8a323e4
+0,        488,        488,        1,   518400, 0xb8a323e4
+0,        489,        489,        1,   518400, 0xb8a323e4
+0,        490,        490,        1,   518400, 0xb8a323e4
+0,        491,        491,        1,   518400, 0xb8a323e4
+0,        492,        492,        1,   518400, 0xb8a323e4
+0,        493,        493,        1,   518400, 0xb8a323e4
+0,        494,        494,        1,   518400, 0xc43518ba
 1,   98872000,   98872000,  2161000,     1875, 0x9002ef71
 0,        495,        495,        1,   518400, 0xc43518ba
-0,        505,        505,        1,   518400, 0xbab197ea
+0,        496,        496,        1,   518400, 0xc43518ba
+0,        497,        497,        1,   518400, 0xc43518ba
+0,        498,        498,        1,   518400, 0xc43518ba
+0,        499,        499,        1,   518400, 0xc43518ba
+0,        500,        500,        1,   518400, 0xc43518ba
+0,        501,        501,        1,   518400, 0xc43518ba
+0,        502,        502,        1,   518400, 0xc43518ba
+0,        503,        503,        1,   518400, 0xc43518ba
+0,        504,        504,        1,   518400, 0xc43518ba
+0,        505,        505,        1,   518400, 0xc43518ba
 1,  101124000,  101124000,  4096000,     3872, 0x20c6ed9c
 0,        506,        506,        1,   518400, 0x04e38692
-0,        526,        526,        1,   518400, 0xbab197ea
+0,        507,        507,        1,   518400, 0x04e38692
+0,        508,        508,        1,   518400, 0x04e38692
+0,        509,        509,        1,   518400, 0x04e38692
+0,        510,        510,        1,   518400, 0x04e38692
+0,        511,        511,        1,   518400, 0x04e38692
+0,        512,        512,        1,   518400, 0x04e38692
+0,        513,        513,        1,   518400, 0x04e38692
+0,        514,        514,        1,   518400, 0x04e38692
+0,        515,        515,        1,   518400, 0x04e38692
+0,        516,        516,        1,   518400, 0x04e38692
+0,        517,        517,        1,   518400, 0x04e38692
+0,        518,        518,        1,   518400, 0x04e38692
+0,        519,        519,        1,   518400, 0x04e38692
+0,        520,        520,        1,   518400, 0x04e38692
+0,        521,        521,        1,   518400, 0x04e38692
+0,        522,        522,        1,   518400, 0x04e38692
+0,        523,        523,        1,   518400, 0x04e38692
+0,        524,        524,        1,   518400, 0x04e38692
+0,        525,        525,        1,   518400, 0x04e38692
+0,        526,        526,        1,   518400, 0x04e38692
 1,  105303000,  105303000,  2730000,     3094, 0xf203a663
 0,        527,        527,        1,   518400, 0x856b0ee5
-0,        540,        540,        1,   518400, 0xbab197ea
+0,        528,        528,        1,   518400, 0x856b0ee5
+0,        529,        529,        1,   518400, 0x856b0ee5
+0,        530,        530,        1,   518400, 0x856b0ee5
+0,        531,        531,        1,   518400, 0x856b0ee5
+0,        532,        532,        1,   518400, 0x856b0ee5
+0,        533,        533,        1,   518400, 0x856b0ee5
+0,        534,        534,        1,   518400, 0x856b0ee5
+0,        535,        535,        1,   518400, 0x856b0ee5
+0,        536,        536,        1,   518400, 0x856b0ee5
+0,        537,        537,        1,   518400, 0x856b0ee5
+0,        538,        538,        1,   518400, 0x856b0ee5
+0,        539,        539,        1,   518400, 0x856b0ee5
+0,        540,        540,        1,   518400, 0x856b0ee5
 1,  108106000,  108106000,  2059000,     2404, 0x41a7b429
 0,        541,        541,        1,   518400, 0x3e5beee2
-0,        551,        551,        1,   518400, 0xbab197ea
+0,        542,        542,        1,   518400, 0x3e5beee2
+0,        543,        543,        1,   518400, 0x3e5beee2
+0,        544,        544,        1,   518400, 0x3e5beee2
+0,        545,        545,        1,   518400, 0x3e5beee2
+0,        546,        546,        1,   518400, 0x3e5beee2
+0,        547,        547,        1,   518400, 0x3e5beee2
+0,        548,        548,        1,   518400, 0x3e5beee2
+0,        549,        549,        1,   518400, 0x3e5beee2
+0,        550,        550,        1,   518400, 0x3e5beee2
+0,        551,        551,        1,   518400, 0x3e5beee2
+0,        552,        552,        1,   518400, 0xbab197ea
+0,        553,        553,        1,   518400, 0xbab197ea
+0,        554,        554,        1,   518400, 0xbab197ea
+0,        555,        555,        1,   518400, 0xbab197ea
+0,        556,        556,        1,   518400, 0xbab197ea
+0,        557,        557,        1,   518400, 0xbab197ea
+0,        558,        558,        1,   518400, 0xbab197ea
+0,        559,        559,        1,   518400, 0xbab197ea
+0,        560,        560,        1,   518400, 0xbab197ea
+0,        561,        561,        1,   518400, 0xbab197ea
+0,        562,        562,        1,   518400, 0xbab197ea
+0,        563,        563,        1,   518400, 0xbab197ea
+0,        564,        564,        1,   518400, 0xbab197ea
+0,        565,        565,        1,   518400, 0xbab197ea
+0,        566,        566,        1,   518400, 0xbab197ea
+0,        567,        567,        1,   518400, 0xbab197ea
+0,        568,        568,        1,   518400, 0xbab197ea
+0,        569,        569,        1,   518400, 0xbab197ea
+0,        570,        570,        1,   518400, 0xbab197ea
+0,        571,        571,        1,   518400, 0xbab197ea
+0,        572,        572,        1,   518400, 0xbab197ea
+0,        573,        573,        1,   518400, 0xbab197ea
+0,        574,        574,        1,   518400, 0xbab197ea
+0,        575,        575,        1,   518400, 0xbab197ea
+0,        576,        576,        1,   518400, 0xbab197ea
+0,        577,        577,        1,   518400, 0xbab197ea
+0,        578,        578,        1,   518400, 0xbab197ea
+0,        579,        579,        1,   518400, 0xbab197ea
+0,        580,        580,        1,   518400, 0xbab197ea
+0,        581,        581,        1,   518400, 0xbab197ea
+0,        582,        582,        1,   518400, 0xbab197ea
+0,        583,        583,        1,   518400, 0xbab197ea
+0,        584,        584,        1,   518400, 0xbab197ea
+0,        585,        585,        1,   518400, 0xbab197ea
+0,        586,        586,        1,   518400, 0xbab197ea
+0,        587,        587,        1,   518400, 0xbab197ea
+0,        588,        588,        1,   518400, 0xbab197ea
+0,        589,        589,        1,   518400, 0xbab197ea
+0,        590,        590,        1,   518400, 0xbab197ea
+0,        591,        591,        1,   518400, 0xbab197ea
+0,        592,        592,        1,   518400, 0xbab197ea
+0,        593,        593,        1,   518400, 0xbab197ea
+0,        594,        594,        1,   518400, 0xbab197ea
+0,        595,        595,        1,   518400, 0xbab197ea
+0,        596,        596,        1,   518400, 0xbab197ea
+0,        597,        597,        1,   518400, 0xbab197ea
+0,        598,        598,        1,   518400, 0xbab197ea
+0,        599,        599,        1,   518400, 0xbab197ea
+0,        600,        600,        1,   518400, 0xbab197ea
+0,        601,        601,        1,   518400, 0xbab197ea
+0,        602,        602,        1,   518400, 0xbab197ea
+0,        603,        603,        1,   518400, 0xbab197ea
+0,        604,        604,        1,   518400, 0xbab197ea
+0,        605,        605,        1,   518400, 0xbab197ea
+0,        606,        606,        1,   518400, 0xbab197ea
+0,        607,        607,        1,   518400, 0xbab197ea
+0,        608,        608,        1,   518400, 0xbab197ea
+0,        609,        609,        1,   518400, 0xbab197ea
+0,        610,        610,        1,   518400, 0xbab197ea
+0,        611,        611,        1,   518400, 0xbab197ea
+0,        612,        612,        1,   518400, 0xbab197ea
+0,        613,        613,        1,   518400, 0xbab197ea
+0,        614,        614,        1,   518400, 0xbab197ea
+0,        615,        615,        1,   518400, 0xbab197ea
+0,        616,        616,        1,   518400, 0xbab197ea
+0,        617,        617,        1,   518400, 0xbab197ea
+0,        618,        618,        1,   518400, 0xbab197ea
+0,        619,        619,        1,   518400, 0xbab197ea
+0,        620,        620,        1,   518400, 0xbab197ea
+0,        621,        621,        1,   518400, 0xbab197ea
+0,        622,        622,        1,   518400, 0xbab197ea
+0,        623,        623,        1,   518400, 0xbab197ea
+0,        624,        624,        1,   518400, 0xbab197ea
+0,        625,        625,        1,   518400, 0xbab197ea
+0,        626,        626,        1,   518400, 0xbab197ea
+0,        627,        627,        1,   518400, 0xbab197ea
+0,        628,        628,        1,   518400, 0xbab197ea
+0,        629,        629,        1,   518400, 0xbab197ea
+0,        630,        630,        1,   518400, 0xbab197ea
+0,        631,        631,        1,   518400, 0xbab197ea
+0,        632,        632,        1,   518400, 0xbab197ea
+0,        633,        633,        1,   518400, 0xbab197ea
+0,        634,        634,        1,   518400, 0xbab197ea
+0,        635,        635,        1,   518400, 0xbab197ea
+0,        636,        636,        1,   518400, 0xbab197ea
+0,        637,        637,        1,   518400, 0xbab197ea
+0,        638,        638,        1,   518400, 0xbab197ea
+0,        639,        639,        1,   518400, 0xbab197ea
+0,        640,        640,        1,   518400, 0xbab197ea
+0,        641,        641,        1,   518400, 0xbab197ea
+0,        642,        642,        1,   518400, 0xbab197ea
+0,        643,        643,        1,   518400, 0xbab197ea
+0,        644,        644,        1,   518400, 0xbab197ea
+0,        645,        645,        1,   518400, 0xbab197ea
+0,        646,        646,        1,   518400, 0xbab197ea
+0,        647,        647,        1,   518400, 0xbab197ea
+0,        648,        648,        1,   518400, 0xbab197ea
+0,        649,        649,        1,   518400, 0xbab197ea
+0,        650,        650,        1,   518400, 0xbab197ea
+0,        651,        651,        1,   518400, 0xbab197ea
+0,        652,        652,        1,   518400, 0xbab197ea
+0,        653,        653,        1,   518400, 0xbab197ea
+0,        654,        654,        1,   518400, 0xbab197ea
+0,        655,        655,        1,   518400, 0xbab197ea
+0,        656,        656,        1,   518400, 0xbab197ea
+0,        657,        657,        1,   518400, 0xbab197ea
+0,        658,        658,        1,   518400, 0xbab197ea
+0,        659,        659,        1,   518400, 0xbab197ea
+0,        660,        660,        1,   518400, 0xbab197ea
+0,        661,        661,        1,   518400, 0xbab197ea
+0,        662,        662,        1,   518400, 0xbab197ea
+0,        663,        663,        1,   518400, 0xbab197ea
+0,        664,        664,        1,   518400, 0xbab197ea
+0,        665,        665,        1,   518400, 0xbab197ea
+0,        666,        666,        1,   518400, 0xbab197ea
+0,        667,        667,        1,   518400, 0xbab197ea
+0,        668,        668,        1,   518400, 0xbab197ea
+0,        669,        669,        1,   518400, 0xbab197ea
+0,        670,        670,        1,   518400, 0xbab197ea
+0,        671,        671,        1,   518400, 0xbab197ea
+0,        672,        672,        1,   518400, 0xbab197ea
+0,        673,        673,        1,   518400, 0xbab197ea
+0,        674,        674,        1,   518400, 0xbab197ea
+0,        675,        675,        1,   518400, 0xbab197ea
+0,        676,        676,        1,   518400, 0xbab197ea
+0,        677,        677,        1,   518400, 0xbab197ea
+0,        678,        678,        1,   518400, 0xbab197ea
+0,        679,        679,        1,   518400, 0xbab197ea
+0,        680,        680,        1,   518400, 0xbab197ea
+0,        681,        681,        1,   518400, 0xbab197ea
+0,        682,        682,        1,   518400, 0xbab197ea
+0,        683,        683,        1,   518400, 0xbab197ea
+0,        684,        684,        1,   518400, 0xbab197ea
+0,        685,        685,        1,   518400, 0xbab197ea
+0,        686,        686,        1,   518400, 0xbab197ea
+0,        687,        687,        1,   518400, 0xbab197ea
+0,        688,        688,        1,   518400, 0xbab197ea
+0,        689,        689,        1,   518400, 0xbab197ea
+0,        690,        690,        1,   518400, 0xbab197ea
+0,        691,        691,        1,   518400, 0xbab197ea
+0,        692,        692,        1,   518400, 0xbab197ea
+0,        693,        693,        1,   518400, 0xbab197ea
+0,        694,        694,        1,   518400, 0xbab197ea
+0,        695,        695,        1,   518400, 0xbab197ea
+0,        696,        696,        1,   518400, 0xbab197ea
+0,        697,        697,        1,   518400, 0xbab197ea
+0,        698,        698,        1,   518400, 0xbab197ea
+0,        699,        699,        1,   518400, 0xbab197ea
+0,        700,        700,        1,   518400, 0xbab197ea
+0,        701,        701,        1,   518400, 0xbab197ea
+0,        702,        702,        1,   518400, 0xbab197ea
+0,        703,        703,        1,   518400, 0xbab197ea
+0,        704,        704,        1,   518400, 0xbab197ea
+0,        705,        705,        1,   518400, 0xbab197ea
+0,        706,        706,        1,   518400, 0xbab197ea
+0,        707,        707,        1,   518400, 0xbab197ea
 1,  141556000,  141556000,  1661000,     1088, 0xde20aa20
 0,        708,        708,        1,   518400, 0xb8bc1365
-0,        716,        716,        1,   518400, 0xbab197ea
+0,        709,        709,        1,   518400, 0xb8bc1365
+0,        710,        710,        1,   518400, 0xb8bc1365
+0,        711,        711,        1,   518400, 0xb8bc1365
+0,        712,        712,        1,   518400, 0xb8bc1365
+0,        713,        713,        1,   518400, 0xb8bc1365
+0,        714,        714,        1,   518400, 0xb8bc1365
+0,        715,        715,        1,   518400, 0xb8bc1365
+0,        716,        716,        1,   518400, 0xb8bc1365
+0,        717,        717,        1,   518400, 0xbab197ea
+0,        718,        718,        1,   518400, 0xbab197ea
+0,        719,        719,        1,   518400, 0xbab197ea
+0,        720,        720,        1,   518400, 0xbab197ea
+0,        721,        721,        1,   518400, 0xbab197ea
+0,        722,        722,        1,   518400, 0xbab197ea
+0,        723,        723,        1,   518400, 0xbab197ea
+0,        724,        724,        1,   518400, 0xbab197ea
+0,        725,        725,        1,   518400, 0xbab197ea
+0,        726,        726,        1,   518400, 0xbab197ea
+0,        727,        727,        1,   518400, 0xbab197ea
+0,        728,        728,        1,   518400, 0xbab197ea
+0,        729,        729,        1,   518400, 0xbab197ea
+0,        730,        730,        1,   518400, 0xbab197ea
+0,        731,        731,        1,   518400, 0xbab197ea
+0,        732,        732,        1,   518400, 0xbab197ea
+0,        733,        733,        1,   518400, 0xbab197ea
+0,        734,        734,        1,   518400, 0xbab197ea
+0,        735,        735,        1,   518400, 0xbab197ea
+0,        736,        736,        1,   518400, 0xbab197ea
+0,        737,        737,        1,   518400, 0xbab197ea
+0,        738,        738,        1,   518400, 0xbab197ea
+0,        739,        739,        1,   518400, 0xbab197ea
+0,        740,        740,        1,   518400, 0xbab197ea
+0,        741,        741,        1,   518400, 0xbab197ea
+0,        742,        742,        1,   518400, 0xbab197ea
+0,        743,        743,        1,   518400, 0xbab197ea
+0,        744,        744,        1,   518400, 0xbab197ea
+0,        745,        745,        1,   518400, 0xbab197ea
+0,        746,        746,        1,   518400, 0xbab197ea
+0,        747,        747,        1,   518400, 0xbab197ea
+0,        748,        748,        1,   518400, 0xbab197ea
+0,        749,        749,        1,   518400, 0xbab197ea
+0,        750,        750,        1,   518400, 0xbab197ea
+0,        751,        751,        1,   518400, 0xbab197ea
+0,        752,        752,        1,   518400, 0xbab197ea
+0,        753,        753,        1,   518400, 0xbab197ea
+0,        754,        754,        1,   518400, 0xbab197ea
+0,        755,        755,        1,   518400, 0xbab197ea
+0,        756,        756,        1,   518400, 0xbab197ea
+0,        757,        757,        1,   518400, 0xbab197ea
+0,        758,        758,        1,   518400, 0xbab197ea
+0,        759,        759,        1,   518400, 0xbab197ea
+0,        760,        760,        1,   518400, 0xbab197ea
+0,        761,        761,        1,   518400, 0xbab197ea
+0,        762,        762,        1,   518400, 0xbab197ea
+0,        763,        763,        1,   518400, 0xbab197ea
+0,        764,        764,        1,   518400, 0xbab197ea
+0,        765,        765,        1,   518400, 0xbab197ea
+0,        766,        766,        1,   518400, 0xbab197ea
+0,        767,        767,        1,   518400, 0xbab197ea
+0,        768,        768,        1,   518400, 0xbab197ea
+0,        769,        769,        1,   518400, 0xbab197ea
+0,        770,        770,        1,   518400, 0xbab197ea
+0,        771,        771,        1,   518400, 0xbab197ea
+0,        772,        772,        1,   518400, 0xbab197ea
+0,        773,        773,        1,   518400, 0xbab197ea
+0,        774,        774,        1,   518400, 0xbab197ea
+0,        775,        775,        1,   518400, 0xbab197ea
+0,        776,        776,        1,   518400, 0xbab197ea
+0,        777,        777,        1,   518400, 0xbab197ea
+0,        778,        778,        1,   518400, 0xbab197ea
+0,        779,        779,        1,   518400, 0xbab197ea
+0,        780,        780,        1,   518400, 0xbab197ea
+0,        781,        781,        1,   518400, 0xbab197ea
+0,        782,        782,        1,   518400, 0xbab197ea
+0,        783,        783,        1,   518400, 0xbab197ea
+0,        784,        784,        1,   518400, 0xbab197ea
+0,        785,        785,        1,   518400, 0xbab197ea
+0,        786,        786,        1,   518400, 0xbab197ea
+0,        787,        787,        1,   518400, 0xbab197ea
+0,        788,        788,        1,   518400, 0xbab197ea
+0,        789,        789,        1,   518400, 0xbab197ea
+0,        790,        790,        1,   518400, 0xbab197ea
+0,        791,        791,        1,   518400, 0xbab197ea
+0,        792,        792,        1,   518400, 0xbab197ea
+0,        793,        793,        1,   518400, 0xbab197ea
+0,        794,        794,        1,   518400, 0xbab197ea
+0,        795,        795,        1,   518400, 0xbab197ea
+0,        796,        796,        1,   518400, 0xbab197ea
+0,        797,        797,        1,   518400, 0xbab197ea
+0,        798,        798,        1,   518400, 0xbab197ea
+0,        799,        799,        1,   518400, 0xbab197ea
+0,        800,        800,        1,   518400, 0xbab197ea
+0,        801,        801,        1,   518400, 0xbab197ea
+0,        802,        802,        1,   518400, 0xbab197ea
+0,        803,        803,        1,   518400, 0xbab197ea
+0,        804,        804,        1,   518400, 0xbab197ea
+0,        805,        805,        1,   518400, 0xbab197ea
+0,        806,        806,        1,   518400, 0xbab197ea
+0,        807,        807,        1,   518400, 0xbab197ea
+0,        808,        808,        1,   518400, 0xbab197ea
+0,        809,        809,        1,   518400, 0xbab197ea
+0,        810,        810,        1,   518400, 0xbab197ea
+0,        811,        811,        1,   518400, 0xbab197ea
+0,        812,        812,        1,   518400, 0xbab197ea
+0,        813,        813,        1,   518400, 0xbab197ea
+0,        814,        814,        1,   518400, 0xbab197ea
+0,        815,        815,        1,   518400, 0xbab197ea
+0,        816,        816,        1,   518400, 0xbab197ea
 0,        817,        817,        1,   518400, 0x83efa32d
 1,  163445000,  163445000,  1331000,      339, 0x8bd186ef
-0,        824,        824,        1,   518400, 0xbab197ea
+0,        818,        818,        1,   518400, 0x83efa32d
+0,        819,        819,        1,   518400, 0x83efa32d
+0,        820,        820,        1,   518400, 0x83efa32d
+0,        821,        821,        1,   518400, 0x83efa32d
+0,        822,        822,        1,   518400, 0x83efa32d
+0,        823,        823,        1,   518400, 0x83efa32d
+0,        824,        824,        1,   518400, 0x83efa32d
+0,        825,        825,        1,   518400, 0xbab197ea
+0,        826,        826,        1,   518400, 0xbab197ea
+0,        827,        827,        1,   518400, 0xbab197ea
+0,        828,        828,        1,   518400, 0xbab197ea
+0,        829,        829,        1,   518400, 0xbab197ea
+0,        830,        830,        1,   518400, 0xbab197ea
+0,        831,        831,        1,   518400, 0xbab197ea
+0,        832,        832,        1,   518400, 0xbab197ea
+0,        833,        833,        1,   518400, 0xbab197ea
+0,        834,        834,        1,   518400, 0xbab197ea
+0,        835,        835,        1,   518400, 0xbab197ea
+0,        836,        836,        1,   518400, 0xbab197ea
+0,        837,        837,        1,   518400, 0xbab197ea
+0,        838,        838,        1,   518400, 0xbab197ea
+0,        839,        839,        1,   518400, 0xbab197ea
 0,        840,        840,        1,   518400, 0x03ea0e90
 1,  168049000,  168049000,  1900000,     1312, 0x0bf20e8d
-0,        850,        850,        1,   518400, 0xbab197ea
+0,        841,        841,        1,   518400, 0x03ea0e90
+0,        842,        842,        1,   518400, 0x03ea0e90
+0,        843,        843,        1,   518400, 0x03ea0e90
+0,        844,        844,        1,   518400, 0x03ea0e90
+0,        845,        845,        1,   518400, 0x03ea0e90
+0,        846,        846,        1,   518400, 0x03ea0e90
+0,        847,        847,        1,   518400, 0x03ea0e90
+0,        848,        848,        1,   518400, 0x03ea0e90
+0,        849,        849,        1,   518400, 0x03ea0e90
+0,        850,        850,        1,   518400, 0x8780239e
 1,  170035000,  170035000,  1524000,     1279, 0xb6c2dafe
 0,        851,        851,        1,   518400, 0x8780239e
-0,        858,        858,        1,   518400, 0xbab197ea
+0,        852,        852,        1,   518400, 0x8780239e
+0,        853,        853,        1,   518400, 0x8780239e
+0,        854,        854,        1,   518400, 0x8780239e
+0,        855,        855,        1,   518400, 0x8780239e
+0,        856,        856,        1,   518400, 0x8780239e
+0,        857,        857,        1,   518400, 0x8780239e
+0,        858,        858,        1,   518400, 0x8780239e
+0,        859,        859,        1,   518400, 0xbab197ea
+0,        860,        860,        1,   518400, 0xbab197ea
 0,        861,        861,        1,   518400, 0x6eb72347
 1,  172203000,  172203000,  1695000,     1826, 0x9a1ac769
-0,        869,        869,        1,   518400, 0xbab197ea
+0,        862,        862,        1,   518400, 0x6eb72347
+0,        863,        863,        1,   518400, 0x6eb72347
+0,        864,        864,        1,   518400, 0x6eb72347
+0,        865,        865,        1,   518400, 0x6eb72347
+0,        866,        866,        1,   518400, 0x6eb72347
+0,        867,        867,        1,   518400, 0x6eb72347
+0,        868,        868,        1,   518400, 0x6eb72347
+0,        869,        869,        1,   518400, 0x6eb72347
 1,  173947000,  173947000,  1934000,     1474, 0xa9b03cdc
 0,        870,        870,        1,   518400, 0x9c4a3a3d
-0,        879,        879,        1,   518400, 0xbab197ea
+0,        871,        871,        1,   518400, 0x9c4a3a3d
+0,        872,        872,        1,   518400, 0x9c4a3a3d
+0,        873,        873,        1,   518400, 0x9c4a3a3d
+0,        874,        874,        1,   518400, 0x9c4a3a3d
+0,        875,        875,        1,   518400, 0x9c4a3a3d
+0,        876,        876,        1,   518400, 0x9c4a3a3d
+0,        877,        877,        1,   518400, 0x9c4a3a3d
+0,        878,        878,        1,   518400, 0x9c4a3a3d
+0,        879,        879,        1,   518400, 0x9c4a3a3d
 1,  175957000,  175957000,  1763000,     1019, 0x20409355
 0,        880,        880,        1,   518400, 0xc9ebfa89
-0,        889,        889,        1,   518400, 0xbab197ea
+0,        881,        881,        1,   518400, 0xc9ebfa89
+0,        882,        882,        1,   518400, 0xc9ebfa89
+0,        883,        883,        1,   518400, 0xc9ebfa89
+0,        884,        884,        1,   518400, 0xc9ebfa89
+0,        885,        885,        1,   518400, 0xc9ebfa89
+0,        886,        886,        1,   518400, 0xc9ebfa89
+0,        887,        887,        1,   518400, 0xc9ebfa89
+0,        888,        888,        1,   518400, 0xc9ebfa89
+0,        889,        889,        1,   518400, 0xc9ebfa89
+0,        890,        890,        1,   518400, 0xbab197ea
+0,        891,        891,        1,   518400, 0xbab197ea
+0,        892,        892,        1,   518400, 0xbab197ea
+0,        893,        893,        1,   518400, 0xbab197ea
+0,        894,        894,        1,   518400, 0xbab197ea
+0,        895,        895,        1,   518400, 0xbab197ea
+0,        896,        896,        1,   518400, 0xbab197ea
+0,        897,        897,        1,   518400, 0xbab197ea
+0,        898,        898,        1,   518400, 0xbab197ea
+0,        899,        899,        1,   518400, 0xbab197ea
+0,        900,        900,        1,   518400, 0xbab197ea
+0,        901,        901,        1,   518400, 0xbab197ea
+0,        902,        902,        1,   518400, 0xbab197ea
+0,        903,        903,        1,   518400, 0xbab197ea
+0,        904,        904,        1,   518400, 0xbab197ea
+0,        905,        905,        1,   518400, 0xbab197ea
+0,        906,        906,        1,   518400, 0xbab197ea
+0,        907,        907,        1,   518400, 0xbab197ea
+0,        908,        908,        1,   518400, 0xbab197ea
+0,        909,        909,        1,   518400, 0xbab197ea
+0,        910,        910,        1,   518400, 0xbab197ea
+0,        911,        911,        1,   518400, 0xbab197ea
+0,        912,        912,        1,   518400, 0xbab197ea
+0,        913,        913,        1,   518400, 0xbab197ea
+0,        914,        914,        1,   518400, 0xbab197ea
+0,        915,        915,        1,   518400, 0xbab197ea
+0,        916,        916,        1,   518400, 0xbab197ea
+0,        917,        917,        1,   518400, 0xbab197ea
+0,        918,        918,        1,   518400, 0xbab197ea
+0,        919,        919,        1,   518400, 0xbab197ea
+0,        920,        920,        1,   518400, 0xbab197ea
+0,        921,        921,        1,   518400, 0xbab197ea
+0,        922,        922,        1,   518400, 0xbab197ea
+0,        923,        923,        1,   518400, 0xbab197ea
+0,        924,        924,        1,   518400, 0xbab197ea
+0,        925,        925,        1,   518400, 0xbab197ea
+0,        926,        926,        1,   518400, 0xbab197ea
+0,        927,        927,        1,   518400, 0xbab197ea
+0,        928,        928,        1,   518400, 0xbab197ea
+0,        929,        929,        1,   518400, 0xbab197ea
+0,        930,        930,        1,   518400, 0xbab197ea
+0,        931,        931,        1,   518400, 0xbab197ea
+0,        932,        932,        1,   518400, 0xbab197ea
+0,        933,        933,        1,   518400, 0xbab197ea
+0,        934,        934,        1,   518400, 0xbab197ea
+0,        935,        935,        1,   518400, 0xbab197ea
+0,        936,        936,        1,   518400, 0xbab197ea
+0,        937,        937,        1,   518400, 0xbab197ea
+0,        938,        938,        1,   518400, 0xbab197ea
+0,        939,        939,        1,   518400, 0xbab197ea
+0,        940,        940,        1,   518400, 0xbab197ea
+0,        941,        941,        1,   518400, 0xbab197ea
+0,        942,        942,        1,   518400, 0xbab197ea
+0,        943,        943,        1,   518400, 0xbab197ea
+0,        944,        944,        1,   518400, 0xbab197ea
+0,        945,        945,        1,   518400, 0xbab197ea
 0,        946,        946,        1,   518400, 0xbaf801ef
 1,  189295000,  189295000,  1968000,     1596, 0x408c726e
-0,        956,        956,        1,   518400, 0xbab197ea
+0,        947,        947,        1,   518400, 0xbaf801ef
+0,        948,        948,        1,   518400, 0xbaf801ef
+0,        949,        949,        1,   518400, 0xbaf801ef
+0,        950,        950,        1,   518400, 0xbaf801ef
+0,        951,        951,        1,   518400, 0xbaf801ef
+0,        952,        952,        1,   518400, 0xbaf801ef
+0,        953,        953,        1,   518400, 0xbaf801ef
+0,        954,        954,        1,   518400, 0xbaf801ef
+0,        955,        955,        1,   518400, 0xbaf801ef
+0,        956,        956,        1,   518400, 0xbaf801ef
 1,  191356000,  191356000,  1228000,     1517, 0xae8c5c2b
 0,        957,        957,        1,   518400, 0x59f4e72f
-0,        963,        963,        1,   518400, 0xbab197ea
+0,        958,        958,        1,   518400, 0x59f4e72f
+0,        959,        959,        1,   518400, 0x59f4e72f
+0,        960,        960,        1,   518400, 0x59f4e72f
+0,        961,        961,        1,   518400, 0x59f4e72f
+0,        962,        962,        1,   518400, 0x59f4e72f
+0,        963,        963,        1,   518400, 0x9d5b9d69
 1,  192640000,  192640000,  1763000,     2506, 0xa458d6d4
 0,        964,        964,        1,   518400, 0x9d5b9d69
-0,        972,        972,        1,   518400, 0xbab197ea
+0,        965,        965,        1,   518400, 0x9d5b9d69
+0,        966,        966,        1,   518400, 0x9d5b9d69
+0,        967,        967,        1,   518400, 0x9d5b9d69
+0,        968,        968,        1,   518400, 0x9d5b9d69
+0,        969,        969,        1,   518400, 0x9d5b9d69
+0,        970,        970,        1,   518400, 0x9d5b9d69
+0,        971,        971,        1,   518400, 0x9d5b9d69
+0,        972,        972,        1,   518400, 0x9d5b9d69
+0,        973,        973,        1,   518400, 0xbab197ea
+0,        974,        974,        1,   518400, 0xbab197ea
+0,        975,        975,        1,   518400, 0xbab197ea
 1,  195193000,  195193000,  1092000,     1074, 0x397ba9a8
 0,        976,        976,        1,   518400, 0x923d1ce7
-0,        981,        981,        1,   518400, 0xbab197ea
+0,        977,        977,        1,   518400, 0x923d1ce7
+0,        978,        978,        1,   518400, 0x923d1ce7
+0,        979,        979,        1,   518400, 0x923d1ce7
+0,        980,        980,        1,   518400, 0x923d1ce7
+0,        981,        981,        1,   518400, 0x923d1ce7
 1,  196361000,  196361000,  1524000,     1715, 0x695ca41e
 0,        982,        982,        1,   518400, 0x6e652cd2
-0,        989,        989,        1,   518400, 0xbab197ea
+0,        983,        983,        1,   518400, 0x6e652cd2
+0,        984,        984,        1,   518400, 0x6e652cd2
+0,        985,        985,        1,   518400, 0x6e652cd2
+0,        986,        986,        1,   518400, 0x6e652cd2
+0,        987,        987,        1,   518400, 0x6e652cd2
+0,        988,        988,        1,   518400, 0x6e652cd2
+0,        989,        989,        1,   518400, 0x6e652cd2
 1,  197946000,  197946000,  1160000,      789, 0xc63a189e
 0,        990,        990,        1,   518400, 0x25113966
-0,        996,        996,        1,   518400, 0xbab197ea
+0,        991,        991,        1,   518400, 0x25113966
+0,        992,        992,        1,   518400, 0x25113966
+0,        993,        993,        1,   518400, 0x25113966
+0,        994,        994,        1,   518400, 0x25113966
+0,        995,        995,        1,   518400, 0x25113966
+0,        996,        996,        1,   518400, 0x2dc83609
 1,  199230000,  199230000,  1627000,     1846, 0xeea8c599
 0,        997,        997,        1,   518400, 0x2dc83609
-0,       1004,       1004,        1,   518400, 0xbab197ea
+0,        998,        998,        1,   518400, 0x2dc83609
+0,        999,        999,        1,   518400, 0x2dc83609
+0,       1000,       1000,        1,   518400, 0x2dc83609
+0,       1001,       1001,        1,   518400, 0x2dc83609
+0,       1002,       1002,        1,   518400, 0x2dc83609
+0,       1003,       1003,        1,   518400, 0x2dc83609
+0,       1004,       1004,        1,   518400, 0x2dc83609
 1,  200924000,  200924000,  1763000,      922, 0xd4a87222
 0,       1005,       1005,        1,   518400, 0x90483bc6
-0,       1013,       1013,        1,   518400, 0xbab197ea
+0,       1006,       1006,        1,   518400, 0x90483bc6
+0,       1007,       1007,        1,   518400, 0x90483bc6
+0,       1008,       1008,        1,   518400, 0x90483bc6
+0,       1009,       1009,        1,   518400, 0x90483bc6
+0,       1010,       1010,        1,   518400, 0x90483bc6
+0,       1011,       1011,        1,   518400, 0x90483bc6
+0,       1012,       1012,        1,   518400, 0x90483bc6
+0,       1013,       1013,        1,   518400, 0x90483bc6
+0,       1014,       1014,        1,   518400, 0xbab197ea
+0,       1015,       1015,        1,   518400, 0xbab197ea
+0,       1016,       1016,        1,   518400, 0xbab197ea
+0,       1017,       1017,        1,   518400, 0xbab197ea
+0,       1018,       1018,        1,   518400, 0xbab197ea
+0,       1019,       1019,        1,   518400, 0xbab197ea
+0,       1020,       1020,        1,   518400, 0xbab197ea
+0,       1021,       1021,        1,   518400, 0xbab197ea
+0,       1022,       1022,        1,   518400, 0xbab197ea
+0,       1023,       1023,        1,   518400, 0xbab197ea
+0,       1024,       1024,        1,   518400, 0xbab197ea
+0,       1025,       1025,        1,   518400, 0xbab197ea
+0,       1026,       1026,        1,   518400, 0xbab197ea
+0,       1027,       1027,        1,   518400, 0xbab197ea
+0,       1028,       1028,        1,   518400, 0xbab197ea
+0,       1029,       1029,        1,   518400, 0xbab197ea
+0,       1030,       1030,        1,   518400, 0xbab197ea
+0,       1031,       1031,        1,   518400, 0xbab197ea
+0,       1032,       1032,        1,   518400, 0xbab197ea
+0,       1033,       1033,        1,   518400, 0xbab197ea
+0,       1034,       1034,        1,   518400, 0xbab197ea
+0,       1035,       1035,        1,   518400, 0xbab197ea
+0,       1036,       1036,        1,   518400, 0xbab197ea
+0,       1037,       1037,        1,   518400, 0xbab197ea
+0,       1038,       1038,        1,   518400, 0xbab197ea
+0,       1039,       1039,        1,   518400, 0xbab197ea
+0,       1040,       1040,        1,   518400, 0xbab197ea
+0,       1041,       1041,        1,   518400, 0xbab197ea
+0,       1042,       1042,        1,   518400, 0xbab197ea
+0,       1043,       1043,        1,   518400, 0xbab197ea
+0,       1044,       1044,        1,   518400, 0xbab197ea
+0,       1045,       1045,        1,   518400, 0xbab197ea
+0,       1046,       1046,        1,   518400, 0xbab197ea
+0,       1047,       1047,        1,   518400, 0xbab197ea
+0,       1048,       1048,        1,   518400, 0xbab197ea
+0,       1049,       1049,        1,   518400, 0xbab197ea
+0,       1050,       1050,        1,   518400, 0xbab197ea
+0,       1051,       1051,        1,   518400, 0xbab197ea
+0,       1052,       1052,        1,   518400, 0xbab197ea
 0,       1053,       1053,        1,   518400, 0x3de86ab7
 1,  210600000,  210600000,  1831000,      665, 0x55580135
-0,       1062,       1062,        1,   518400, 0xbab197ea
+0,       1054,       1054,        1,   518400, 0x3de86ab7
+0,       1055,       1055,        1,   518400, 0x3de86ab7
+0,       1056,       1056,        1,   518400, 0x3de86ab7
+0,       1057,       1057,        1,   518400, 0x3de86ab7
+0,       1058,       1058,        1,   518400, 0x3de86ab7
+0,       1059,       1059,        1,   518400, 0x3de86ab7
+0,       1060,       1060,        1,   518400, 0x3de86ab7
+0,       1061,       1061,        1,   518400, 0x3de86ab7
+0,       1062,       1062,        1,   518400, 0x3de86ab7
+0,       1063,       1063,        1,   518400, 0xbab197ea
+0,       1064,       1064,        1,   518400, 0xbab197ea
+0,       1065,       1065,        1,   518400, 0xbab197ea
+0,       1066,       1066,        1,   518400, 0xbab197ea
+0,       1067,       1067,        1,   518400, 0xbab197ea
+0,       1068,       1068,        1,   518400, 0xbab197ea
+0,       1069,       1069,        1,   518400, 0xbab197ea
+0,       1070,       1070,        1,   518400, 0xbab197ea
+0,       1071,       1071,        1,   518400, 0xbab197ea
+0,       1072,       1072,        1,   518400, 0xbab197ea
+0,       1073,       1073,        1,   518400, 0xbab197ea
 1,  214771000,  214771000,  1558000,     1216, 0x50d1f6c5
 0,       1074,       1074,        1,   518400, 0x8c320e68
-0,       1082,       1082,        1,   518400, 0xbab197ea
+0,       1075,       1075,        1,   518400, 0x8c320e68
+0,       1076,       1076,        1,   518400, 0x8c320e68
+0,       1077,       1077,        1,   518400, 0x8c320e68
+0,       1078,       1078,        1,   518400, 0x8c320e68
+0,       1079,       1079,        1,   518400, 0x8c320e68
+0,       1080,       1080,        1,   518400, 0x8c320e68
+0,       1081,       1081,        1,   518400, 0x8c320e68
+0,       1082,       1082,        1,   518400, 0x8c320e68
+0,       1083,       1083,        1,   518400, 0xbab197ea
+0,       1084,       1084,        1,   518400, 0xbab197ea
+0,       1085,       1085,        1,   518400, 0xbab197ea
+0,       1086,       1086,        1,   518400, 0xbab197ea
+0,       1087,       1087,        1,   518400, 0xbab197ea
+0,       1088,       1088,        1,   518400, 0xbab197ea
+0,       1089,       1089,        1,   518400, 0xbab197ea
+0,       1090,       1090,        1,   518400, 0xbab197ea
+0,       1091,       1091,        1,   518400, 0xbab197ea
+0,       1092,       1092,        1,   518400, 0xbab197ea
+0,       1093,       1093,        1,   518400, 0xbab197ea
+0,       1094,       1094,        1,   518400, 0xbab197ea
+0,       1095,       1095,        1,   518400, 0xbab197ea
+0,       1096,       1096,        1,   518400, 0xbab197ea
+0,       1097,       1097,        1,   518400, 0xbab197ea
+0,       1098,       1098,        1,   518400, 0xbab197ea
+0,       1099,       1099,        1,   518400, 0xbab197ea
+0,       1100,       1100,        1,   518400, 0xbab197ea
+0,       1101,       1101,        1,   518400, 0xbab197ea
+0,       1102,       1102,        1,   518400, 0xbab197ea
+0,       1103,       1103,        1,   518400, 0xbab197ea
+0,       1104,       1104,        1,   518400, 0xbab197ea
+0,       1105,       1105,        1,   518400, 0xbab197ea
+0,       1106,       1106,        1,   518400, 0xbab197ea
+0,       1107,       1107,        1,   518400, 0xbab197ea
+0,       1108,       1108,        1,   518400, 0xbab197ea
+0,       1109,       1109,        1,   518400, 0xbab197ea
+0,       1110,       1110,        1,   518400, 0xbab197ea
+0,       1111,       1111,        1,   518400, 0xbab197ea
+0,       1112,       1112,        1,   518400, 0xbab197ea
+0,       1113,       1113,        1,   518400, 0xbab197ea
+0,       1114,       1114,        1,   518400, 0xbab197ea
+0,       1115,       1115,        1,   518400, 0xbab197ea
+0,       1116,       1116,        1,   518400, 0xbab197ea
+0,       1117,       1117,        1,   518400, 0xbab197ea
+0,       1118,       1118,        1,   518400, 0xbab197ea
+0,       1119,       1119,        1,   518400, 0xbab197ea
+0,       1120,       1120,        1,   518400, 0xbab197ea
+0,       1121,       1121,        1,   518400, 0xbab197ea
+0,       1122,       1122,        1,   518400, 0xbab197ea
+0,       1123,       1123,        1,   518400, 0xbab197ea
+0,       1124,       1124,        1,   518400, 0xbab197ea
+0,       1125,       1125,        1,   518400, 0xbab197ea
+0,       1126,       1126,        1,   518400, 0xbab197ea
+0,       1127,       1127,        1,   518400, 0xbab197ea
 0,       1128,       1128,        1,   518400, 0x81e977b2
 1,  225640000,  225640000,  2127000,     2133, 0x670c11a5
-0,       1139,       1139,        1,   518400, 0xbab197ea
+0,       1129,       1129,        1,   518400, 0x81e977b2
+0,       1130,       1130,        1,   518400, 0x81e977b2
+0,       1131,       1131,        1,   518400, 0x81e977b2
+0,       1132,       1132,        1,   518400, 0x81e977b2
+0,       1133,       1133,        1,   518400, 0x81e977b2
+0,       1134,       1134,        1,   518400, 0x81e977b2
+0,       1135,       1135,        1,   518400, 0x81e977b2
+0,       1136,       1136,        1,   518400, 0x81e977b2
+0,       1137,       1137,        1,   518400, 0x81e977b2
+0,       1138,       1138,        1,   518400, 0x81e977b2
+0,       1139,       1139,        1,   518400, 0xb046dd30
 1,  227834000,  227834000,  1262000,     1264, 0xc1d9fc57
 0,       1140,       1140,        1,   518400, 0xb046dd30
-0,       1145,       1145,        1,   518400, 0xbab197ea
diff --git a/tests/ref/fate/sub2video_basic b/tests/ref/fate/sub2video_basic
index 5f72e292c9..9bd4e8b114 100644
--- a/tests/ref/fate/sub2video_basic
+++ b/tests/ref/fate/sub2video_basic
@@ -1,95 +1,1149 @@
-#tb 0: 1/25
+#tb 0: 1/5
 #media_type 0: video
 #codec_id 0: rawvideo
 #dimensions 0: 720x480
-#sar 0: 0/1
-0,       3312,       3312,        1,  1382400, 0x00000000
-0,       3312,       3312,        1,  1382400, 0x8c93c2ba
-0,       3436,       3436,        1,  1382400, 0x00000000
-0,       3684,       3684,        1,  1382400, 0xb02e32ca
-0,       3802,       3802,        1,  1382400, 0x00000000
-0,       4520,       4520,        1,  1382400, 0x83b71116
-0,       4584,       4584,        1,  1382400, 0x00000000
-0,       4586,       4586,        1,  1382400, 0x85547fd1
-0,       4645,       4645,        1,  1382400, 0x00000000
-0,       4648,       4648,        1,  1382400, 0x00000000
-0,       4648,       4648,        1,  1382400, 0xb6a8f181
-0,       4715,       4715,        1,  1382400, 0x00000000
-0,       4717,       4717,        1,  1382400, 0xb64d1a2c
-0,       4748,       4748,        1,  1382400, 0x00000000
-0,       4750,       4750,        1,  1382400, 0x7b37ecf3
-0,       4792,       4792,        1,  1382400, 0x00000000
-0,       4993,       4993,        1,  1382400, 0xdc025bd1
-0,       5027,       5027,        1,  1382400, 0x00000000
-0,       5029,       5029,        1,  1382400, 0x688b294d
-0,       5068,       5068,        1,  1382400, 0x00000000
-0,       5070,       5070,        1,  1382400, 0xa2b33d1b
-0,       5117,       5117,        1,  1382400, 0x00000000
-0,       5119,       5119,        1,  1382400, 0xb3e525e3
-0,       5168,       5168,        1,  1382400, 0x00000000
-0,       5170,       5170,        1,  1382400, 0xaa8fbdd7
-0,       5216,       5216,        1,  1382400, 0x00000000
-0,       5218,       5218,        1,  1382400, 0x7b7f26dd
-0,       5249,       5249,        1,  1382400, 0x00000000
-0,       5251,       5251,        1,  1382400, 0x15e2f836
-0,       5289,       5289,        1,  1382400, 0x00000000
-0,       5291,       5291,        1,  1382400, 0x0fee9b0c
-0,       5358,       5358,        1,  1382400, 0x00000000
-0,       5360,       5360,        1,  1382400, 0x89d62791
-0,       5429,       5429,        1,  1382400, 0x00000000
-0,       5431,       5431,        1,  1382400, 0xa6a9fd74
-0,       5490,       5490,        1,  1382400, 0x00000000
-0,       5491,       5491,        1,  1382400, 0x7896178d
-0,       5537,       5537,        1,  1382400, 0x00000000
-0,       5588,       5588,        1,  1382400, 0x01751a52
-0,       5647,       5647,        1,  1382400, 0x00000000
-0,       5688,       5688,        1,  1382400, 0xa3959c6f
-0,       5770,       5770,        1,  1382400, 0x00000000
-0,       5772,       5772,        1,  1382400, 0x3d3ea47b
-0,       5826,       5826,        1,  1382400, 0x00000000
-0,       5828,       5828,        1,  1382400, 0x593f8b24
-0,       5931,       5931,        1,  1382400, 0x00000000
-0,       5933,       5933,        1,  1382400, 0x171f05ba
-0,       6001,       6001,        1,  1382400, 0x00000000
-0,       6003,       6003,        1,  1382400, 0xb014cdf1
-0,       6054,       6054,        1,  1382400, 0x00000000
-0,       6839,       6839,        1,  1382400, 0xd918e667
-0,       6880,       6880,        1,  1382400, 0x00000000
-0,       7386,       7386,        1,  1382400, 0xc9406331
-0,       7419,       7419,        1,  1382400, 0x00000000
-0,       7501,       7501,        1,  1382400, 0xaf08b10d
-0,       7549,       7549,        1,  1382400, 0x00000000
-0,       7551,       7551,        1,  1382400, 0x00000000
-0,       7551,       7551,        1,  1382400, 0x853a9d93
-0,       7589,       7589,        1,  1382400, 0x00000000
-0,       7605,       7605,        1,  1382400, 0x7491a87d
-0,       7647,       7647,        1,  1382400, 0x00000000
-0,       7649,       7649,        1,  1382400, 0xf7383c58
-0,       7697,       7697,        1,  1382400, 0x00000000
-0,       7699,       7699,        1,  1382400, 0xe66be411
-0,       7743,       7743,        1,  1382400, 0x00000000
-0,       8032,       8032,        1,  1382400, 0xd6850362
-0,       8082,       8082,        1,  1382400, 0x00000000
-0,       8084,       8084,        1,  1382400, 0x3e1ed109
-0,       8115,       8115,        1,  1382400, 0x00000000
-0,       8116,       8116,        1,  1382400, 0x39c1b7bd
-0,       8160,       8160,        1,  1382400, 0x00000000
-0,       8180,       8180,        1,  1382400, 0x35b85f2e
-0,       8207,       8207,        1,  1382400, 0x00000000
-0,       8209,       8209,        1,  1382400, 0x00000000
-0,       8209,       8209,        1,  1382400, 0x83f103e5
-0,       8247,       8247,        1,  1382400, 0x00000000
-0,       8249,       8249,        1,  1382400, 0xbc1ca9b3
-0,       8278,       8278,        1,  1382400, 0x00000000
-0,       8281,       8281,        1,  1382400, 0x94d4a51e
-0,       8321,       8321,        1,  1382400, 0x00000000
-0,       8323,       8323,        1,  1382400, 0xf88cdfde
-0,       8367,       8367,        1,  1382400, 0x00000000
-0,       8565,       8565,        1,  1382400, 0xdd51423b
-0,       8611,       8611,        1,  1382400, 0x00000000
-0,       8669,       8669,        1,  1382400, 0x08259fa4
-0,       8708,       8708,        1,  1382400, 0x00000000
-0,       8941,       8941,        1,  1382400, 0x1663fa34
-0,       8994,       8994,        1,  1382400, 0x00000000
-0,       8996,       8996,        1,  1382400, 0xda2ceb55
-0,       9027,       9027,        1,  1382400, 0x00000000
+#sar 0: 1/1
+0,        662,        662,        1,  1382400, 0xc637b893
+0,        663,        663,        1,  1382400, 0xc637b893
+0,        664,        664,        1,  1382400, 0xc637b893
+0,        665,        665,        1,  1382400, 0xc637b893
+0,        666,        666,        1,  1382400, 0xc637b893
+0,        667,        667,        1,  1382400, 0xc637b893
+0,        668,        668,        1,  1382400, 0xc637b893
+0,        669,        669,        1,  1382400, 0xc637b893
+0,        670,        670,        1,  1382400, 0xc637b893
+0,        671,        671,        1,  1382400, 0xc637b893
+0,        672,        672,        1,  1382400, 0xc637b893
+0,        673,        673,        1,  1382400, 0xc637b893
+0,        674,        674,        1,  1382400, 0xc637b893
+0,        675,        675,        1,  1382400, 0xc637b893
+0,        676,        676,        1,  1382400, 0xc637b893
+0,        677,        677,        1,  1382400, 0xc637b893
+0,        678,        678,        1,  1382400, 0xc637b893
+0,        679,        679,        1,  1382400, 0xc637b893
+0,        680,        680,        1,  1382400, 0xc637b893
+0,        681,        681,        1,  1382400, 0xc637b893
+0,        682,        682,        1,  1382400, 0xc637b893
+0,        683,        683,        1,  1382400, 0xc637b893
+0,        684,        684,        1,  1382400, 0xc637b893
+0,        685,        685,        1,  1382400, 0xc637b893
+0,        686,        686,        1,  1382400, 0xc637b893
+0,        687,        687,        1,  1382400, 0x00000000
+0,        688,        688,        1,  1382400, 0x00000000
+0,        689,        689,        1,  1382400, 0x00000000
+0,        690,        690,        1,  1382400, 0x00000000
+0,        691,        691,        1,  1382400, 0x00000000
+0,        692,        692,        1,  1382400, 0x00000000
+0,        693,        693,        1,  1382400, 0x00000000
+0,        694,        694,        1,  1382400, 0x00000000
+0,        695,        695,        1,  1382400, 0x00000000
+0,        696,        696,        1,  1382400, 0x00000000
+0,        697,        697,        1,  1382400, 0x00000000
+0,        698,        698,        1,  1382400, 0x00000000
+0,        699,        699,        1,  1382400, 0x00000000
+0,        700,        700,        1,  1382400, 0x00000000
+0,        701,        701,        1,  1382400, 0x00000000
+0,        702,        702,        1,  1382400, 0x00000000
+0,        703,        703,        1,  1382400, 0x00000000
+0,        704,        704,        1,  1382400, 0x00000000
+0,        705,        705,        1,  1382400, 0x00000000
+0,        706,        706,        1,  1382400, 0x00000000
+0,        707,        707,        1,  1382400, 0x00000000
+0,        708,        708,        1,  1382400, 0x00000000
+0,        709,        709,        1,  1382400, 0x00000000
+0,        710,        710,        1,  1382400, 0x00000000
+0,        711,        711,        1,  1382400, 0x00000000
+0,        712,        712,        1,  1382400, 0x00000000
+0,        713,        713,        1,  1382400, 0x00000000
+0,        714,        714,        1,  1382400, 0x00000000
+0,        715,        715,        1,  1382400, 0x00000000
+0,        716,        716,        1,  1382400, 0x00000000
+0,        717,        717,        1,  1382400, 0x00000000
+0,        718,        718,        1,  1382400, 0x00000000
+0,        719,        719,        1,  1382400, 0x00000000
+0,        720,        720,        1,  1382400, 0x00000000
+0,        721,        721,        1,  1382400, 0x00000000
+0,        722,        722,        1,  1382400, 0x00000000
+0,        723,        723,        1,  1382400, 0x00000000
+0,        724,        724,        1,  1382400, 0x00000000
+0,        725,        725,        1,  1382400, 0x00000000
+0,        726,        726,        1,  1382400, 0x00000000
+0,        727,        727,        1,  1382400, 0x00000000
+0,        728,        728,        1,  1382400, 0x00000000
+0,        729,        729,        1,  1382400, 0x00000000
+0,        730,        730,        1,  1382400, 0x00000000
+0,        731,        731,        1,  1382400, 0x00000000
+0,        732,        732,        1,  1382400, 0x00000000
+0,        733,        733,        1,  1382400, 0x00000000
+0,        734,        734,        1,  1382400, 0x00000000
+0,        735,        735,        1,  1382400, 0x00000000
+0,        737,        737,        1,  1382400, 0x4c2960ca
+0,        737,        737,        1,  1382400, 0x4c2960ca
+0,        738,        738,        1,  1382400, 0x4c2960ca
+0,        739,        739,        1,  1382400, 0x4c2960ca
+0,        740,        740,        1,  1382400, 0x4c2960ca
+0,        741,        741,        1,  1382400, 0x4c2960ca
+0,        742,        742,        1,  1382400, 0x4c2960ca
+0,        743,        743,        1,  1382400, 0x4c2960ca
+0,        744,        744,        1,  1382400, 0x4c2960ca
+0,        745,        745,        1,  1382400, 0x4c2960ca
+0,        746,        746,        1,  1382400, 0x4c2960ca
+0,        747,        747,        1,  1382400, 0x4c2960ca
+0,        748,        748,        1,  1382400, 0x4c2960ca
+0,        749,        749,        1,  1382400, 0x4c2960ca
+0,        750,        750,        1,  1382400, 0x4c2960ca
+0,        751,        751,        1,  1382400, 0x4c2960ca
+0,        752,        752,        1,  1382400, 0x4c2960ca
+0,        753,        753,        1,  1382400, 0x4c2960ca
+0,        754,        754,        1,  1382400, 0x4c2960ca
+0,        755,        755,        1,  1382400, 0x4c2960ca
+0,        756,        756,        1,  1382400, 0x4c2960ca
+0,        757,        757,        1,  1382400, 0x4c2960ca
+0,        758,        758,        1,  1382400, 0x4c2960ca
+0,        759,        759,        1,  1382400, 0x4c2960ca
+0,        760,        760,        1,  1382400, 0x00000000
+0,        761,        761,        1,  1382400, 0x00000000
+0,        762,        762,        1,  1382400, 0x00000000
+0,        763,        763,        1,  1382400, 0x00000000
+0,        764,        764,        1,  1382400, 0x00000000
+0,        765,        765,        1,  1382400, 0x00000000
+0,        766,        766,        1,  1382400, 0x00000000
+0,        767,        767,        1,  1382400, 0x00000000
+0,        768,        768,        1,  1382400, 0x00000000
+0,        769,        769,        1,  1382400, 0x00000000
+0,        770,        770,        1,  1382400, 0x00000000
+0,        771,        771,        1,  1382400, 0x00000000
+0,        772,        772,        1,  1382400, 0x00000000
+0,        773,        773,        1,  1382400, 0x00000000
+0,        774,        774,        1,  1382400, 0x00000000
+0,        775,        775,        1,  1382400, 0x00000000
+0,        776,        776,        1,  1382400, 0x00000000
+0,        777,        777,        1,  1382400, 0x00000000
+0,        778,        778,        1,  1382400, 0x00000000
+0,        779,        779,        1,  1382400, 0x00000000
+0,        780,        780,        1,  1382400, 0x00000000
+0,        781,        781,        1,  1382400, 0x00000000
+0,        782,        782,        1,  1382400, 0x00000000
+0,        783,        783,        1,  1382400, 0x00000000
+0,        784,        784,        1,  1382400, 0x00000000
+0,        785,        785,        1,  1382400, 0x00000000
+0,        786,        786,        1,  1382400, 0x00000000
+0,        787,        787,        1,  1382400, 0x00000000
+0,        788,        788,        1,  1382400, 0x00000000
+0,        789,        789,        1,  1382400, 0x00000000
+0,        790,        790,        1,  1382400, 0x00000000
+0,        791,        791,        1,  1382400, 0x00000000
+0,        792,        792,        1,  1382400, 0x00000000
+0,        793,        793,        1,  1382400, 0x00000000
+0,        794,        794,        1,  1382400, 0x00000000
+0,        795,        795,        1,  1382400, 0x00000000
+0,        796,        796,        1,  1382400, 0x00000000
+0,        797,        797,        1,  1382400, 0x00000000
+0,        798,        798,        1,  1382400, 0x00000000
+0,        799,        799,        1,  1382400, 0x00000000
+0,        800,        800,        1,  1382400, 0x00000000
+0,        801,        801,        1,  1382400, 0x00000000
+0,        802,        802,        1,  1382400, 0x00000000
+0,        803,        803,        1,  1382400, 0x00000000
+0,        804,        804,        1,  1382400, 0x00000000
+0,        805,        805,        1,  1382400, 0x00000000
+0,        806,        806,        1,  1382400, 0x00000000
+0,        807,        807,        1,  1382400, 0x00000000
+0,        808,        808,        1,  1382400, 0x00000000
+0,        809,        809,        1,  1382400, 0x00000000
+0,        810,        810,        1,  1382400, 0x00000000
+0,        811,        811,        1,  1382400, 0x00000000
+0,        812,        812,        1,  1382400, 0x00000000
+0,        813,        813,        1,  1382400, 0x00000000
+0,        814,        814,        1,  1382400, 0x00000000
+0,        815,        815,        1,  1382400, 0x00000000
+0,        816,        816,        1,  1382400, 0x00000000
+0,        817,        817,        1,  1382400, 0x00000000
+0,        818,        818,        1,  1382400, 0x00000000
+0,        819,        819,        1,  1382400, 0x00000000
+0,        820,        820,        1,  1382400, 0x00000000
+0,        821,        821,        1,  1382400, 0x00000000
+0,        822,        822,        1,  1382400, 0x00000000
+0,        823,        823,        1,  1382400, 0x00000000
+0,        824,        824,        1,  1382400, 0x00000000
+0,        825,        825,        1,  1382400, 0x00000000
+0,        826,        826,        1,  1382400, 0x00000000
+0,        827,        827,        1,  1382400, 0x00000000
+0,        828,        828,        1,  1382400, 0x00000000
+0,        829,        829,        1,  1382400, 0x00000000
+0,        830,        830,        1,  1382400, 0x00000000
+0,        831,        831,        1,  1382400, 0x00000000
+0,        832,        832,        1,  1382400, 0x00000000
+0,        833,        833,        1,  1382400, 0x00000000
+0,        834,        834,        1,  1382400, 0x00000000
+0,        835,        835,        1,  1382400, 0x00000000
+0,        836,        836,        1,  1382400, 0x00000000
+0,        837,        837,        1,  1382400, 0x00000000
+0,        838,        838,        1,  1382400, 0x00000000
+0,        839,        839,        1,  1382400, 0x00000000
+0,        840,        840,        1,  1382400, 0x00000000
+0,        841,        841,        1,  1382400, 0x00000000
+0,        842,        842,        1,  1382400, 0x00000000
+0,        843,        843,        1,  1382400, 0x00000000
+0,        844,        844,        1,  1382400, 0x00000000
+0,        845,        845,        1,  1382400, 0x00000000
+0,        846,        846,        1,  1382400, 0x00000000
+0,        847,        847,        1,  1382400, 0x00000000
+0,        848,        848,        1,  1382400, 0x00000000
+0,        849,        849,        1,  1382400, 0x00000000
+0,        850,        850,        1,  1382400, 0x00000000
+0,        851,        851,        1,  1382400, 0x00000000
+0,        852,        852,        1,  1382400, 0x00000000
+0,        853,        853,        1,  1382400, 0x00000000
+0,        854,        854,        1,  1382400, 0x00000000
+0,        855,        855,        1,  1382400, 0x00000000
+0,        856,        856,        1,  1382400, 0x00000000
+0,        857,        857,        1,  1382400, 0x00000000
+0,        858,        858,        1,  1382400, 0x00000000
+0,        859,        859,        1,  1382400, 0x00000000
+0,        860,        860,        1,  1382400, 0x00000000
+0,        861,        861,        1,  1382400, 0x00000000
+0,        862,        862,        1,  1382400, 0x00000000
+0,        863,        863,        1,  1382400, 0x00000000
+0,        864,        864,        1,  1382400, 0x00000000
+0,        865,        865,        1,  1382400, 0x00000000
+0,        866,        866,        1,  1382400, 0x00000000
+0,        867,        867,        1,  1382400, 0x00000000
+0,        868,        868,        1,  1382400, 0x00000000
+0,        869,        869,        1,  1382400, 0x00000000
+0,        870,        870,        1,  1382400, 0x00000000
+0,        871,        871,        1,  1382400, 0x00000000
+0,        872,        872,        1,  1382400, 0x00000000
+0,        873,        873,        1,  1382400, 0x00000000
+0,        874,        874,        1,  1382400, 0x00000000
+0,        875,        875,        1,  1382400, 0x00000000
+0,        876,        876,        1,  1382400, 0x00000000
+0,        877,        877,        1,  1382400, 0x00000000
+0,        878,        878,        1,  1382400, 0x00000000
+0,        879,        879,        1,  1382400, 0x00000000
+0,        880,        880,        1,  1382400, 0x00000000
+0,        881,        881,        1,  1382400, 0x00000000
+0,        882,        882,        1,  1382400, 0x00000000
+0,        883,        883,        1,  1382400, 0x00000000
+0,        884,        884,        1,  1382400, 0x00000000
+0,        885,        885,        1,  1382400, 0x00000000
+0,        886,        886,        1,  1382400, 0x00000000
+0,        887,        887,        1,  1382400, 0x00000000
+0,        888,        888,        1,  1382400, 0x00000000
+0,        889,        889,        1,  1382400, 0x00000000
+0,        890,        890,        1,  1382400, 0x00000000
+0,        891,        891,        1,  1382400, 0x00000000
+0,        892,        892,        1,  1382400, 0x00000000
+0,        893,        893,        1,  1382400, 0x00000000
+0,        894,        894,        1,  1382400, 0x00000000
+0,        895,        895,        1,  1382400, 0x00000000
+0,        896,        896,        1,  1382400, 0x00000000
+0,        897,        897,        1,  1382400, 0x00000000
+0,        898,        898,        1,  1382400, 0x00000000
+0,        899,        899,        1,  1382400, 0x00000000
+0,        900,        900,        1,  1382400, 0x00000000
+0,        901,        901,        1,  1382400, 0x00000000
+0,        902,        902,        1,  1382400, 0x00000000
+0,        904,        904,        1,  1382400, 0x5fa18966
+0,        904,        904,        1,  1382400, 0x5fa18966
+0,        905,        905,        1,  1382400, 0x5fa18966
+0,        906,        906,        1,  1382400, 0x5fa18966
+0,        907,        907,        1,  1382400, 0x5fa18966
+0,        908,        908,        1,  1382400, 0x5fa18966
+0,        909,        909,        1,  1382400, 0x5fa18966
+0,        910,        910,        1,  1382400, 0x5fa18966
+0,        911,        911,        1,  1382400, 0x5fa18966
+0,        912,        912,        1,  1382400, 0x5fa18966
+0,        913,        913,        1,  1382400, 0x5fa18966
+0,        914,        914,        1,  1382400, 0x5fa18966
+0,        915,        915,        1,  1382400, 0x5fa18966
+0,        916,        916,        1,  1382400, 0x5fa18966
+0,        917,        917,        1,  1382400, 0x55f4b7b1
+0,        918,        918,        1,  1382400, 0x55f4b7b1
+0,        919,        919,        1,  1382400, 0x55f4b7b1
+0,        920,        920,        1,  1382400, 0x55f4b7b1
+0,        921,        921,        1,  1382400, 0x55f4b7b1
+0,        922,        922,        1,  1382400, 0x55f4b7b1
+0,        923,        923,        1,  1382400, 0x55f4b7b1
+0,        924,        924,        1,  1382400, 0x55f4b7b1
+0,        925,        925,        1,  1382400, 0x55f4b7b1
+0,        926,        926,        1,  1382400, 0x55f4b7b1
+0,        927,        927,        1,  1382400, 0x55f4b7b1
+0,        928,        928,        1,  1382400, 0x55f4b7b1
+0,        929,        929,        1,  1382400, 0x00000000
+0,        930,        930,        1,  1382400, 0xdfa4cf32
+0,        931,        931,        1,  1382400, 0xdfa4cf32
+0,        932,        932,        1,  1382400, 0xdfa4cf32
+0,        933,        933,        1,  1382400, 0xdfa4cf32
+0,        934,        934,        1,  1382400, 0xdfa4cf32
+0,        935,        935,        1,  1382400, 0xdfa4cf32
+0,        936,        936,        1,  1382400, 0xdfa4cf32
+0,        937,        937,        1,  1382400, 0xdfa4cf32
+0,        938,        938,        1,  1382400, 0xdfa4cf32
+0,        939,        939,        1,  1382400, 0xdfa4cf32
+0,        940,        940,        1,  1382400, 0xdfa4cf32
+0,        941,        941,        1,  1382400, 0xdfa4cf32
+0,        942,        942,        1,  1382400, 0xdfa4cf32
+0,        943,        943,        1,  1382400, 0x35023df8
+0,        944,        944,        1,  1382400, 0x35023df8
+0,        945,        945,        1,  1382400, 0x35023df8
+0,        946,        946,        1,  1382400, 0x35023df8
+0,        947,        947,        1,  1382400, 0x35023df8
+0,        948,        948,        1,  1382400, 0x35023df8
+0,        949,        949,        1,  1382400, 0x35023df8
+0,        950,        950,        1,  1382400, 0xed933219
+0,        951,        951,        1,  1382400, 0xed933219
+0,        952,        952,        1,  1382400, 0xed933219
+0,        953,        953,        1,  1382400, 0xed933219
+0,        954,        954,        1,  1382400, 0xed933219
+0,        955,        955,        1,  1382400, 0xed933219
+0,        956,        956,        1,  1382400, 0xed933219
+0,        957,        957,        1,  1382400, 0xed933219
+0,        958,        958,        1,  1382400, 0x00000000
+0,        959,        959,        1,  1382400, 0x00000000
+0,        960,        960,        1,  1382400, 0x00000000
+0,        961,        961,        1,  1382400, 0x00000000
+0,        962,        962,        1,  1382400, 0x00000000
+0,        963,        963,        1,  1382400, 0x00000000
+0,        964,        964,        1,  1382400, 0x00000000
+0,        965,        965,        1,  1382400, 0x00000000
+0,        966,        966,        1,  1382400, 0x00000000
+0,        967,        967,        1,  1382400, 0x00000000
+0,        968,        968,        1,  1382400, 0x00000000
+0,        969,        969,        1,  1382400, 0x00000000
+0,        970,        970,        1,  1382400, 0x00000000
+0,        971,        971,        1,  1382400, 0x00000000
+0,        972,        972,        1,  1382400, 0x00000000
+0,        973,        973,        1,  1382400, 0x00000000
+0,        974,        974,        1,  1382400, 0x00000000
+0,        975,        975,        1,  1382400, 0x00000000
+0,        976,        976,        1,  1382400, 0x00000000
+0,        977,        977,        1,  1382400, 0x00000000
+0,        978,        978,        1,  1382400, 0x00000000
+0,        979,        979,        1,  1382400, 0x00000000
+0,        980,        980,        1,  1382400, 0x00000000
+0,        981,        981,        1,  1382400, 0x00000000
+0,        982,        982,        1,  1382400, 0x00000000
+0,        983,        983,        1,  1382400, 0x00000000
+0,        984,        984,        1,  1382400, 0x00000000
+0,        985,        985,        1,  1382400, 0x00000000
+0,        986,        986,        1,  1382400, 0x00000000
+0,        987,        987,        1,  1382400, 0x00000000
+0,        988,        988,        1,  1382400, 0x00000000
+0,        989,        989,        1,  1382400, 0x00000000
+0,        990,        990,        1,  1382400, 0x00000000
+0,        991,        991,        1,  1382400, 0x00000000
+0,        992,        992,        1,  1382400, 0x00000000
+0,        993,        993,        1,  1382400, 0x00000000
+0,        994,        994,        1,  1382400, 0x00000000
+0,        995,        995,        1,  1382400, 0x00000000
+0,        996,        996,        1,  1382400, 0x00000000
+0,        997,        997,        1,  1382400, 0x00000000
+0,        999,        999,        1,  1382400, 0x1b26389a
+0,        999,        999,        1,  1382400, 0x1b26389a
+0,       1000,       1000,        1,  1382400, 0x1b26389a
+0,       1001,       1001,        1,  1382400, 0x1b26389a
+0,       1002,       1002,        1,  1382400, 0x1b26389a
+0,       1003,       1003,        1,  1382400, 0x1b26389a
+0,       1004,       1004,        1,  1382400, 0x1b26389a
+0,       1006,       1006,        1,  1382400, 0xf0c7028b
+0,       1006,       1006,        1,  1382400, 0xf0c7028b
+0,       1007,       1007,        1,  1382400, 0xf0c7028b
+0,       1008,       1008,        1,  1382400, 0xf0c7028b
+0,       1009,       1009,        1,  1382400, 0xf0c7028b
+0,       1010,       1010,        1,  1382400, 0xf0c7028b
+0,       1011,       1011,        1,  1382400, 0xf0c7028b
+0,       1012,       1012,        1,  1382400, 0xf0c7028b
+0,       1013,       1013,        1,  1382400, 0xf0c7028b
+0,       1014,       1014,        1,  1382400, 0x395f521d
+0,       1015,       1015,        1,  1382400, 0x395f521d
+0,       1016,       1016,        1,  1382400, 0x395f521d
+0,       1017,       1017,        1,  1382400, 0x395f521d
+0,       1018,       1018,        1,  1382400, 0x395f521d
+0,       1019,       1019,        1,  1382400, 0x395f521d
+0,       1020,       1020,        1,  1382400, 0x395f521d
+0,       1021,       1021,        1,  1382400, 0x395f521d
+0,       1022,       1022,        1,  1382400, 0x395f521d
+0,       1024,       1024,        1,  1382400, 0x1ea87415
+0,       1024,       1024,        1,  1382400, 0x1ea87415
+0,       1025,       1025,        1,  1382400, 0x1ea87415
+0,       1026,       1026,        1,  1382400, 0x1ea87415
+0,       1027,       1027,        1,  1382400, 0x1ea87415
+0,       1028,       1028,        1,  1382400, 0x1ea87415
+0,       1029,       1029,        1,  1382400, 0x1ea87415
+0,       1030,       1030,        1,  1382400, 0x1ea87415
+0,       1031,       1031,        1,  1382400, 0x1ea87415
+0,       1032,       1032,        1,  1382400, 0x1ea87415
+0,       1033,       1033,        1,  1382400, 0x1ea87415
+0,       1034,       1034,        1,  1382400, 0xc6effdc1
+0,       1035,       1035,        1,  1382400, 0xc6effdc1
+0,       1036,       1036,        1,  1382400, 0xc6effdc1
+0,       1037,       1037,        1,  1382400, 0xc6effdc1
+0,       1038,       1038,        1,  1382400, 0xc6effdc1
+0,       1039,       1039,        1,  1382400, 0xc6effdc1
+0,       1040,       1040,        1,  1382400, 0xc6effdc1
+0,       1041,       1041,        1,  1382400, 0xc6effdc1
+0,       1042,       1042,        1,  1382400, 0xc6effdc1
+0,       1044,       1044,        1,  1382400, 0xba6846f8
+0,       1044,       1044,        1,  1382400, 0xba6846f8
+0,       1045,       1045,        1,  1382400, 0xba6846f8
+0,       1046,       1046,        1,  1382400, 0xba6846f8
+0,       1047,       1047,        1,  1382400, 0xba6846f8
+0,       1048,       1048,        1,  1382400, 0xba6846f8
+0,       1049,       1049,        1,  1382400, 0xba6846f8
+0,       1050,       1050,        1,  1382400, 0x033c5d5b
+0,       1051,       1051,        1,  1382400, 0x033c5d5b
+0,       1052,       1052,        1,  1382400, 0x033c5d5b
+0,       1053,       1053,        1,  1382400, 0x033c5d5b
+0,       1054,       1054,        1,  1382400, 0x033c5d5b
+0,       1055,       1055,        1,  1382400, 0x033c5d5b
+0,       1056,       1056,        1,  1382400, 0x033c5d5b
+0,       1057,       1057,        1,  1382400, 0x033c5d5b
+0,       1058,       1058,        1,  1382400, 0x00000000
+0,       1059,       1059,        1,  1382400, 0xef5abf66
+0,       1060,       1060,        1,  1382400, 0xef5abf66
+0,       1061,       1061,        1,  1382400, 0xef5abf66
+0,       1062,       1062,        1,  1382400, 0xef5abf66
+0,       1063,       1063,        1,  1382400, 0xef5abf66
+0,       1064,       1064,        1,  1382400, 0xef5abf66
+0,       1065,       1065,        1,  1382400, 0xef5abf66
+0,       1066,       1066,        1,  1382400, 0xef5abf66
+0,       1067,       1067,        1,  1382400, 0xef5abf66
+0,       1068,       1068,        1,  1382400, 0xef5abf66
+0,       1069,       1069,        1,  1382400, 0xef5abf66
+0,       1070,       1070,        1,  1382400, 0xef5abf66
+0,       1071,       1071,        1,  1382400, 0xef5abf66
+0,       1072,       1072,        1,  1382400, 0x00000000
+0,       1073,       1073,        1,  1382400, 0xec747954
+0,       1074,       1074,        1,  1382400, 0xec747954
+0,       1075,       1075,        1,  1382400, 0xec747954
+0,       1076,       1076,        1,  1382400, 0xec747954
+0,       1077,       1077,        1,  1382400, 0xec747954
+0,       1078,       1078,        1,  1382400, 0xec747954
+0,       1079,       1079,        1,  1382400, 0xec747954
+0,       1080,       1080,        1,  1382400, 0xec747954
+0,       1081,       1081,        1,  1382400, 0xec747954
+0,       1082,       1082,        1,  1382400, 0xec747954
+0,       1083,       1083,        1,  1382400, 0xec747954
+0,       1084,       1084,        1,  1382400, 0xec747954
+0,       1085,       1085,        1,  1382400, 0xec747954
+0,       1086,       1086,        1,  1382400, 0x00000000
+0,       1087,       1087,        1,  1382400, 0xfa34bcaf
+0,       1088,       1088,        1,  1382400, 0xfa34bcaf
+0,       1089,       1089,        1,  1382400, 0xfa34bcaf
+0,       1090,       1090,        1,  1382400, 0xfa34bcaf
+0,       1091,       1091,        1,  1382400, 0xfa34bcaf
+0,       1092,       1092,        1,  1382400, 0xfa34bcaf
+0,       1093,       1093,        1,  1382400, 0xfa34bcaf
+0,       1094,       1094,        1,  1382400, 0xfa34bcaf
+0,       1095,       1095,        1,  1382400, 0xfa34bcaf
+0,       1096,       1096,        1,  1382400, 0xfa34bcaf
+0,       1097,       1097,        1,  1382400, 0xfa34bcaf
+0,       1098,       1098,        1,  1382400, 0x00000000
+0,       1099,       1099,        1,  1382400, 0x8b7a709b
+0,       1100,       1100,        1,  1382400, 0x8b7a709b
+0,       1101,       1101,        1,  1382400, 0x8b7a709b
+0,       1102,       1102,        1,  1382400, 0x8b7a709b
+0,       1103,       1103,        1,  1382400, 0x8b7a709b
+0,       1104,       1104,        1,  1382400, 0x8b7a709b
+0,       1105,       1105,        1,  1382400, 0x8b7a709b
+0,       1106,       1106,        1,  1382400, 0x8b7a709b
+0,       1107,       1107,        1,  1382400, 0x00000000
+0,       1108,       1108,        1,  1382400, 0x00000000
+0,       1109,       1109,        1,  1382400, 0x00000000
+0,       1110,       1110,        1,  1382400, 0x00000000
+0,       1111,       1111,        1,  1382400, 0x00000000
+0,       1112,       1112,        1,  1382400, 0x00000000
+0,       1113,       1113,        1,  1382400, 0x00000000
+0,       1114,       1114,        1,  1382400, 0x00000000
+0,       1115,       1115,        1,  1382400, 0x00000000
+0,       1116,       1116,        1,  1382400, 0x00000000
+0,       1118,       1118,        1,  1382400, 0xc333382f
+0,       1118,       1118,        1,  1382400, 0xc333382f
+0,       1119,       1119,        1,  1382400, 0xc333382f
+0,       1120,       1120,        1,  1382400, 0xc333382f
+0,       1121,       1121,        1,  1382400, 0xc333382f
+0,       1122,       1122,        1,  1382400, 0xc333382f
+0,       1123,       1123,        1,  1382400, 0xc333382f
+0,       1124,       1124,        1,  1382400, 0xc333382f
+0,       1125,       1125,        1,  1382400, 0xc333382f
+0,       1126,       1126,        1,  1382400, 0xc333382f
+0,       1127,       1127,        1,  1382400, 0xc333382f
+0,       1128,       1128,        1,  1382400, 0xc333382f
+0,       1129,       1129,        1,  1382400, 0x00000000
+0,       1130,       1130,        1,  1382400, 0x00000000
+0,       1131,       1131,        1,  1382400, 0x00000000
+0,       1132,       1132,        1,  1382400, 0x00000000
+0,       1133,       1133,        1,  1382400, 0x00000000
+0,       1134,       1134,        1,  1382400, 0x00000000
+0,       1135,       1135,        1,  1382400, 0x00000000
+0,       1136,       1136,        1,  1382400, 0x00000000
+0,       1138,       1138,        1,  1382400, 0xabe5dfcf
+0,       1138,       1138,        1,  1382400, 0xabe5dfcf
+0,       1139,       1139,        1,  1382400, 0xabe5dfcf
+0,       1140,       1140,        1,  1382400, 0xabe5dfcf
+0,       1141,       1141,        1,  1382400, 0xabe5dfcf
+0,       1142,       1142,        1,  1382400, 0xabe5dfcf
+0,       1143,       1143,        1,  1382400, 0xabe5dfcf
+0,       1144,       1144,        1,  1382400, 0xabe5dfcf
+0,       1145,       1145,        1,  1382400, 0xabe5dfcf
+0,       1146,       1146,        1,  1382400, 0xabe5dfcf
+0,       1147,       1147,        1,  1382400, 0xabe5dfcf
+0,       1148,       1148,        1,  1382400, 0xabe5dfcf
+0,       1149,       1149,        1,  1382400, 0xabe5dfcf
+0,       1150,       1150,        1,  1382400, 0xabe5dfcf
+0,       1151,       1151,        1,  1382400, 0xabe5dfcf
+0,       1152,       1152,        1,  1382400, 0xabe5dfcf
+0,       1153,       1153,        1,  1382400, 0xabe5dfcf
+0,       1154,       1154,        1,  1382400, 0x56948101
+0,       1155,       1155,        1,  1382400, 0x56948101
+0,       1156,       1156,        1,  1382400, 0x56948101
+0,       1157,       1157,        1,  1382400, 0x56948101
+0,       1158,       1158,        1,  1382400, 0x56948101
+0,       1159,       1159,        1,  1382400, 0x56948101
+0,       1160,       1160,        1,  1382400, 0x56948101
+0,       1161,       1161,        1,  1382400, 0x56948101
+0,       1162,       1162,        1,  1382400, 0x56948101
+0,       1163,       1163,        1,  1382400, 0x56948101
+0,       1164,       1164,        1,  1382400, 0x56948101
+0,       1166,       1166,        1,  1382400, 0xb747834a
+0,       1166,       1166,        1,  1382400, 0xb747834a
+0,       1167,       1167,        1,  1382400, 0xb747834a
+0,       1168,       1168,        1,  1382400, 0xb747834a
+0,       1169,       1169,        1,  1382400, 0xb747834a
+0,       1170,       1170,        1,  1382400, 0xb747834a
+0,       1171,       1171,        1,  1382400, 0xb747834a
+0,       1172,       1172,        1,  1382400, 0xb747834a
+0,       1173,       1173,        1,  1382400, 0xb747834a
+0,       1174,       1174,        1,  1382400, 0xb747834a
+0,       1175,       1175,        1,  1382400, 0xb747834a
+0,       1176,       1176,        1,  1382400, 0xb747834a
+0,       1177,       1177,        1,  1382400, 0xb747834a
+0,       1178,       1178,        1,  1382400, 0xb747834a
+0,       1179,       1179,        1,  1382400, 0xb747834a
+0,       1180,       1180,        1,  1382400, 0xb747834a
+0,       1181,       1181,        1,  1382400, 0xb747834a
+0,       1182,       1182,        1,  1382400, 0xb747834a
+0,       1183,       1183,        1,  1382400, 0xb747834a
+0,       1184,       1184,        1,  1382400, 0xb747834a
+0,       1185,       1185,        1,  1382400, 0xb747834a
+0,       1187,       1187,        1,  1382400, 0x3448baad
+0,       1187,       1187,        1,  1382400, 0x3448baad
+0,       1188,       1188,        1,  1382400, 0x3448baad
+0,       1189,       1189,        1,  1382400, 0x3448baad
+0,       1190,       1190,        1,  1382400, 0x3448baad
+0,       1191,       1191,        1,  1382400, 0x3448baad
+0,       1192,       1192,        1,  1382400, 0x3448baad
+0,       1193,       1193,        1,  1382400, 0x3448baad
+0,       1194,       1194,        1,  1382400, 0x3448baad
+0,       1195,       1195,        1,  1382400, 0x3448baad
+0,       1196,       1196,        1,  1382400, 0x3448baad
+0,       1197,       1197,        1,  1382400, 0x3448baad
+0,       1198,       1198,        1,  1382400, 0x3448baad
+0,       1199,       1199,        1,  1382400, 0x3448baad
+0,       1200,       1200,        1,  1382400, 0x00000000
+0,       1201,       1201,        1,  1382400, 0xaabe4f37
+0,       1202,       1202,        1,  1382400, 0xaabe4f37
+0,       1203,       1203,        1,  1382400, 0xaabe4f37
+0,       1204,       1204,        1,  1382400, 0xaabe4f37
+0,       1205,       1205,        1,  1382400, 0xaabe4f37
+0,       1206,       1206,        1,  1382400, 0xaabe4f37
+0,       1207,       1207,        1,  1382400, 0xaabe4f37
+0,       1208,       1208,        1,  1382400, 0xaabe4f37
+0,       1209,       1209,        1,  1382400, 0xaabe4f37
+0,       1210,       1210,        1,  1382400, 0xaabe4f37
+0,       1211,       1211,        1,  1382400, 0x00000000
+0,       1212,       1212,        1,  1382400, 0x00000000
+0,       1213,       1213,        1,  1382400, 0x00000000
+0,       1214,       1214,        1,  1382400, 0x00000000
+0,       1215,       1215,        1,  1382400, 0x00000000
+0,       1216,       1216,        1,  1382400, 0x00000000
+0,       1217,       1217,        1,  1382400, 0x00000000
+0,       1218,       1218,        1,  1382400, 0x00000000
+0,       1219,       1219,        1,  1382400, 0x00000000
+0,       1220,       1220,        1,  1382400, 0x00000000
+0,       1221,       1221,        1,  1382400, 0x00000000
+0,       1222,       1222,        1,  1382400, 0x00000000
+0,       1223,       1223,        1,  1382400, 0x00000000
+0,       1224,       1224,        1,  1382400, 0x00000000
+0,       1225,       1225,        1,  1382400, 0x00000000
+0,       1226,       1226,        1,  1382400, 0x00000000
+0,       1227,       1227,        1,  1382400, 0x00000000
+0,       1228,       1228,        1,  1382400, 0x00000000
+0,       1229,       1229,        1,  1382400, 0x00000000
+0,       1230,       1230,        1,  1382400, 0x00000000
+0,       1231,       1231,        1,  1382400, 0x00000000
+0,       1232,       1232,        1,  1382400, 0x00000000
+0,       1233,       1233,        1,  1382400, 0x00000000
+0,       1234,       1234,        1,  1382400, 0x00000000
+0,       1235,       1235,        1,  1382400, 0x00000000
+0,       1236,       1236,        1,  1382400, 0x00000000
+0,       1237,       1237,        1,  1382400, 0x00000000
+0,       1238,       1238,        1,  1382400, 0x00000000
+0,       1239,       1239,        1,  1382400, 0x00000000
+0,       1240,       1240,        1,  1382400, 0x00000000
+0,       1241,       1241,        1,  1382400, 0x00000000
+0,       1242,       1242,        1,  1382400, 0x00000000
+0,       1243,       1243,        1,  1382400, 0x00000000
+0,       1244,       1244,        1,  1382400, 0x00000000
+0,       1245,       1245,        1,  1382400, 0x00000000
+0,       1246,       1246,        1,  1382400, 0x00000000
+0,       1247,       1247,        1,  1382400, 0x00000000
+0,       1248,       1248,        1,  1382400, 0x00000000
+0,       1249,       1249,        1,  1382400, 0x00000000
+0,       1250,       1250,        1,  1382400, 0x00000000
+0,       1251,       1251,        1,  1382400, 0x00000000
+0,       1252,       1252,        1,  1382400, 0x00000000
+0,       1253,       1253,        1,  1382400, 0x00000000
+0,       1254,       1254,        1,  1382400, 0x00000000
+0,       1255,       1255,        1,  1382400, 0x00000000
+0,       1256,       1256,        1,  1382400, 0x00000000
+0,       1257,       1257,        1,  1382400, 0x00000000
+0,       1258,       1258,        1,  1382400, 0x00000000
+0,       1259,       1259,        1,  1382400, 0x00000000
+0,       1260,       1260,        1,  1382400, 0x00000000
+0,       1261,       1261,        1,  1382400, 0x00000000
+0,       1262,       1262,        1,  1382400, 0x00000000
+0,       1263,       1263,        1,  1382400, 0x00000000
+0,       1264,       1264,        1,  1382400, 0x00000000
+0,       1265,       1265,        1,  1382400, 0x00000000
+0,       1266,       1266,        1,  1382400, 0x00000000
+0,       1267,       1267,        1,  1382400, 0x00000000
+0,       1268,       1268,        1,  1382400, 0x00000000
+0,       1269,       1269,        1,  1382400, 0x00000000
+0,       1270,       1270,        1,  1382400, 0x00000000
+0,       1271,       1271,        1,  1382400, 0x00000000
+0,       1272,       1272,        1,  1382400, 0x00000000
+0,       1273,       1273,        1,  1382400, 0x00000000
+0,       1274,       1274,        1,  1382400, 0x00000000
+0,       1275,       1275,        1,  1382400, 0x00000000
+0,       1276,       1276,        1,  1382400, 0x00000000
+0,       1277,       1277,        1,  1382400, 0x00000000
+0,       1278,       1278,        1,  1382400, 0x00000000
+0,       1279,       1279,        1,  1382400, 0x00000000
+0,       1280,       1280,        1,  1382400, 0x00000000
+0,       1281,       1281,        1,  1382400, 0x00000000
+0,       1282,       1282,        1,  1382400, 0x00000000
+0,       1283,       1283,        1,  1382400, 0x00000000
+0,       1284,       1284,        1,  1382400, 0x00000000
+0,       1285,       1285,        1,  1382400, 0x00000000
+0,       1286,       1286,        1,  1382400, 0x00000000
+0,       1287,       1287,        1,  1382400, 0x00000000
+0,       1288,       1288,        1,  1382400, 0x00000000
+0,       1289,       1289,        1,  1382400, 0x00000000
+0,       1290,       1290,        1,  1382400, 0x00000000
+0,       1291,       1291,        1,  1382400, 0x00000000
+0,       1292,       1292,        1,  1382400, 0x00000000
+0,       1293,       1293,        1,  1382400, 0x00000000
+0,       1294,       1294,        1,  1382400, 0x00000000
+0,       1295,       1295,        1,  1382400, 0x00000000
+0,       1296,       1296,        1,  1382400, 0x00000000
+0,       1297,       1297,        1,  1382400, 0x00000000
+0,       1298,       1298,        1,  1382400, 0x00000000
+0,       1299,       1299,        1,  1382400, 0x00000000
+0,       1300,       1300,        1,  1382400, 0x00000000
+0,       1301,       1301,        1,  1382400, 0x00000000
+0,       1302,       1302,        1,  1382400, 0x00000000
+0,       1303,       1303,        1,  1382400, 0x00000000
+0,       1304,       1304,        1,  1382400, 0x00000000
+0,       1305,       1305,        1,  1382400, 0x00000000
+0,       1306,       1306,        1,  1382400, 0x00000000
+0,       1307,       1307,        1,  1382400, 0x00000000
+0,       1308,       1308,        1,  1382400, 0x00000000
+0,       1309,       1309,        1,  1382400, 0x00000000
+0,       1310,       1310,        1,  1382400, 0x00000000
+0,       1311,       1311,        1,  1382400, 0x00000000
+0,       1312,       1312,        1,  1382400, 0x00000000
+0,       1313,       1313,        1,  1382400, 0x00000000
+0,       1314,       1314,        1,  1382400, 0x00000000
+0,       1315,       1315,        1,  1382400, 0x00000000
+0,       1316,       1316,        1,  1382400, 0x00000000
+0,       1317,       1317,        1,  1382400, 0x00000000
+0,       1318,       1318,        1,  1382400, 0x00000000
+0,       1319,       1319,        1,  1382400, 0x00000000
+0,       1320,       1320,        1,  1382400, 0x00000000
+0,       1321,       1321,        1,  1382400, 0x00000000
+0,       1322,       1322,        1,  1382400, 0x00000000
+0,       1323,       1323,        1,  1382400, 0x00000000
+0,       1324,       1324,        1,  1382400, 0x00000000
+0,       1325,       1325,        1,  1382400, 0x00000000
+0,       1326,       1326,        1,  1382400, 0x00000000
+0,       1327,       1327,        1,  1382400, 0x00000000
+0,       1328,       1328,        1,  1382400, 0x00000000
+0,       1329,       1329,        1,  1382400, 0x00000000
+0,       1330,       1330,        1,  1382400, 0x00000000
+0,       1331,       1331,        1,  1382400, 0x00000000
+0,       1332,       1332,        1,  1382400, 0x00000000
+0,       1333,       1333,        1,  1382400, 0x00000000
+0,       1334,       1334,        1,  1382400, 0x00000000
+0,       1335,       1335,        1,  1382400, 0x00000000
+0,       1336,       1336,        1,  1382400, 0x00000000
+0,       1337,       1337,        1,  1382400, 0x00000000
+0,       1338,       1338,        1,  1382400, 0x00000000
+0,       1339,       1339,        1,  1382400, 0x00000000
+0,       1340,       1340,        1,  1382400, 0x00000000
+0,       1341,       1341,        1,  1382400, 0x00000000
+0,       1342,       1342,        1,  1382400, 0x00000000
+0,       1343,       1343,        1,  1382400, 0x00000000
+0,       1344,       1344,        1,  1382400, 0x00000000
+0,       1345,       1345,        1,  1382400, 0x00000000
+0,       1346,       1346,        1,  1382400, 0x00000000
+0,       1347,       1347,        1,  1382400, 0x00000000
+0,       1348,       1348,        1,  1382400, 0x00000000
+0,       1349,       1349,        1,  1382400, 0x00000000
+0,       1350,       1350,        1,  1382400, 0x00000000
+0,       1351,       1351,        1,  1382400, 0x00000000
+0,       1352,       1352,        1,  1382400, 0x00000000
+0,       1353,       1353,        1,  1382400, 0x00000000
+0,       1354,       1354,        1,  1382400, 0x00000000
+0,       1355,       1355,        1,  1382400, 0x00000000
+0,       1356,       1356,        1,  1382400, 0x00000000
+0,       1357,       1357,        1,  1382400, 0x00000000
+0,       1358,       1358,        1,  1382400, 0x00000000
+0,       1359,       1359,        1,  1382400, 0x00000000
+0,       1360,       1360,        1,  1382400, 0x00000000
+0,       1361,       1361,        1,  1382400, 0x00000000
+0,       1362,       1362,        1,  1382400, 0x00000000
+0,       1363,       1363,        1,  1382400, 0x00000000
+0,       1364,       1364,        1,  1382400, 0x00000000
+0,       1365,       1365,        1,  1382400, 0x00000000
+0,       1366,       1366,        1,  1382400, 0x00000000
+0,       1368,       1368,        1,  1382400, 0x8a48cd6f
+0,       1368,       1368,        1,  1382400, 0x8a48cd6f
+0,       1369,       1369,        1,  1382400, 0x8a48cd6f
+0,       1370,       1370,        1,  1382400, 0x8a48cd6f
+0,       1371,       1371,        1,  1382400, 0x8a48cd6f
+0,       1372,       1372,        1,  1382400, 0x8a48cd6f
+0,       1373,       1373,        1,  1382400, 0x8a48cd6f
+0,       1374,       1374,        1,  1382400, 0x8a48cd6f
+0,       1375,       1375,        1,  1382400, 0x8a48cd6f
+0,       1376,       1376,        1,  1382400, 0x00000000
+0,       1377,       1377,        1,  1382400, 0x00000000
+0,       1378,       1378,        1,  1382400, 0x00000000
+0,       1379,       1379,        1,  1382400, 0x00000000
+0,       1380,       1380,        1,  1382400, 0x00000000
+0,       1381,       1381,        1,  1382400, 0x00000000
+0,       1382,       1382,        1,  1382400, 0x00000000
+0,       1383,       1383,        1,  1382400, 0x00000000
+0,       1384,       1384,        1,  1382400, 0x00000000
+0,       1385,       1385,        1,  1382400, 0x00000000
+0,       1386,       1386,        1,  1382400, 0x00000000
+0,       1387,       1387,        1,  1382400, 0x00000000
+0,       1388,       1388,        1,  1382400, 0x00000000
+0,       1389,       1389,        1,  1382400, 0x00000000
+0,       1390,       1390,        1,  1382400, 0x00000000
+0,       1391,       1391,        1,  1382400, 0x00000000
+0,       1392,       1392,        1,  1382400, 0x00000000
+0,       1393,       1393,        1,  1382400, 0x00000000
+0,       1394,       1394,        1,  1382400, 0x00000000
+0,       1395,       1395,        1,  1382400, 0x00000000
+0,       1396,       1396,        1,  1382400, 0x00000000
+0,       1397,       1397,        1,  1382400, 0x00000000
+0,       1398,       1398,        1,  1382400, 0x00000000
+0,       1399,       1399,        1,  1382400, 0x00000000
+0,       1400,       1400,        1,  1382400, 0x00000000
+0,       1401,       1401,        1,  1382400, 0x00000000
+0,       1402,       1402,        1,  1382400, 0x00000000
+0,       1403,       1403,        1,  1382400, 0x00000000
+0,       1404,       1404,        1,  1382400, 0x00000000
+0,       1405,       1405,        1,  1382400, 0x00000000
+0,       1406,       1406,        1,  1382400, 0x00000000
+0,       1407,       1407,        1,  1382400, 0x00000000
+0,       1408,       1408,        1,  1382400, 0x00000000
+0,       1409,       1409,        1,  1382400, 0x00000000
+0,       1410,       1410,        1,  1382400, 0x00000000
+0,       1411,       1411,        1,  1382400, 0x00000000
+0,       1412,       1412,        1,  1382400, 0x00000000
+0,       1413,       1413,        1,  1382400, 0x00000000
+0,       1414,       1414,        1,  1382400, 0x00000000
+0,       1415,       1415,        1,  1382400, 0x00000000
+0,       1416,       1416,        1,  1382400, 0x00000000
+0,       1417,       1417,        1,  1382400, 0x00000000
+0,       1418,       1418,        1,  1382400, 0x00000000
+0,       1419,       1419,        1,  1382400, 0x00000000
+0,       1420,       1420,        1,  1382400, 0x00000000
+0,       1421,       1421,        1,  1382400, 0x00000000
+0,       1422,       1422,        1,  1382400, 0x00000000
+0,       1423,       1423,        1,  1382400, 0x00000000
+0,       1424,       1424,        1,  1382400, 0x00000000
+0,       1425,       1425,        1,  1382400, 0x00000000
+0,       1426,       1426,        1,  1382400, 0x00000000
+0,       1427,       1427,        1,  1382400, 0x00000000
+0,       1428,       1428,        1,  1382400, 0x00000000
+0,       1429,       1429,        1,  1382400, 0x00000000
+0,       1430,       1430,        1,  1382400, 0x00000000
+0,       1431,       1431,        1,  1382400, 0x00000000
+0,       1432,       1432,        1,  1382400, 0x00000000
+0,       1433,       1433,        1,  1382400, 0x00000000
+0,       1434,       1434,        1,  1382400, 0x00000000
+0,       1435,       1435,        1,  1382400, 0x00000000
+0,       1436,       1436,        1,  1382400, 0x00000000
+0,       1437,       1437,        1,  1382400, 0x00000000
+0,       1438,       1438,        1,  1382400, 0x00000000
+0,       1439,       1439,        1,  1382400, 0x00000000
+0,       1440,       1440,        1,  1382400, 0x00000000
+0,       1441,       1441,        1,  1382400, 0x00000000
+0,       1442,       1442,        1,  1382400, 0x00000000
+0,       1443,       1443,        1,  1382400, 0x00000000
+0,       1444,       1444,        1,  1382400, 0x00000000
+0,       1445,       1445,        1,  1382400, 0x00000000
+0,       1446,       1446,        1,  1382400, 0x00000000
+0,       1447,       1447,        1,  1382400, 0x00000000
+0,       1448,       1448,        1,  1382400, 0x00000000
+0,       1449,       1449,        1,  1382400, 0x00000000
+0,       1450,       1450,        1,  1382400, 0x00000000
+0,       1451,       1451,        1,  1382400, 0x00000000
+0,       1452,       1452,        1,  1382400, 0x00000000
+0,       1453,       1453,        1,  1382400, 0x00000000
+0,       1454,       1454,        1,  1382400, 0x00000000
+0,       1455,       1455,        1,  1382400, 0x00000000
+0,       1456,       1456,        1,  1382400, 0x00000000
+0,       1457,       1457,        1,  1382400, 0x00000000
+0,       1458,       1458,        1,  1382400, 0x00000000
+0,       1459,       1459,        1,  1382400, 0x00000000
+0,       1460,       1460,        1,  1382400, 0x00000000
+0,       1461,       1461,        1,  1382400, 0x00000000
+0,       1462,       1462,        1,  1382400, 0x00000000
+0,       1463,       1463,        1,  1382400, 0x00000000
+0,       1464,       1464,        1,  1382400, 0x00000000
+0,       1465,       1465,        1,  1382400, 0x00000000
+0,       1466,       1466,        1,  1382400, 0x00000000
+0,       1467,       1467,        1,  1382400, 0x00000000
+0,       1468,       1468,        1,  1382400, 0x00000000
+0,       1469,       1469,        1,  1382400, 0x00000000
+0,       1470,       1470,        1,  1382400, 0x00000000
+0,       1471,       1471,        1,  1382400, 0x00000000
+0,       1472,       1472,        1,  1382400, 0x00000000
+0,       1473,       1473,        1,  1382400, 0x00000000
+0,       1474,       1474,        1,  1382400, 0x00000000
+0,       1475,       1475,        1,  1382400, 0x00000000
+0,       1477,       1477,        1,  1382400, 0x49518c43
+0,       1477,       1477,        1,  1382400, 0x49518c43
+0,       1478,       1478,        1,  1382400, 0x49518c43
+0,       1479,       1479,        1,  1382400, 0x49518c43
+0,       1480,       1480,        1,  1382400, 0x49518c43
+0,       1481,       1481,        1,  1382400, 0x49518c43
+0,       1482,       1482,        1,  1382400, 0x49518c43
+0,       1483,       1483,        1,  1382400, 0x49518c43
+0,       1484,       1484,        1,  1382400, 0x00000000
+0,       1485,       1485,        1,  1382400, 0x00000000
+0,       1486,       1486,        1,  1382400, 0x00000000
+0,       1487,       1487,        1,  1382400, 0x00000000
+0,       1488,       1488,        1,  1382400, 0x00000000
+0,       1489,       1489,        1,  1382400, 0x00000000
+0,       1490,       1490,        1,  1382400, 0x00000000
+0,       1491,       1491,        1,  1382400, 0x00000000
+0,       1492,       1492,        1,  1382400, 0x00000000
+0,       1493,       1493,        1,  1382400, 0x00000000
+0,       1494,       1494,        1,  1382400, 0x00000000
+0,       1495,       1495,        1,  1382400, 0x00000000
+0,       1496,       1496,        1,  1382400, 0x00000000
+0,       1497,       1497,        1,  1382400, 0x00000000
+0,       1498,       1498,        1,  1382400, 0x00000000
+0,       1500,       1500,        1,  1382400, 0x4a72fa21
+0,       1500,       1500,        1,  1382400, 0x4a72fa21
+0,       1501,       1501,        1,  1382400, 0x4a72fa21
+0,       1502,       1502,        1,  1382400, 0x4a72fa21
+0,       1503,       1503,        1,  1382400, 0x4a72fa21
+0,       1504,       1504,        1,  1382400, 0x4a72fa21
+0,       1505,       1505,        1,  1382400, 0x4a72fa21
+0,       1506,       1506,        1,  1382400, 0x4a72fa21
+0,       1507,       1507,        1,  1382400, 0x4a72fa21
+0,       1508,       1508,        1,  1382400, 0x4a72fa21
+0,       1509,       1509,        1,  1382400, 0x4a72fa21
+0,       1510,       1510,        1,  1382400, 0x00000000
+0,       1511,       1511,        1,  1382400, 0xa82f7de8
+0,       1512,       1512,        1,  1382400, 0xa82f7de8
+0,       1513,       1513,        1,  1382400, 0xa82f7de8
+0,       1514,       1514,        1,  1382400, 0xa82f7de8
+0,       1515,       1515,        1,  1382400, 0xa82f7de8
+0,       1516,       1516,        1,  1382400, 0xa82f7de8
+0,       1517,       1517,        1,  1382400, 0xa82f7de8
+0,       1518,       1518,        1,  1382400, 0x00000000
+0,       1519,       1519,        1,  1382400, 0x00000000
+0,       1521,       1521,        1,  1382400, 0xeba0b5f3
+0,       1521,       1521,        1,  1382400, 0xeba0b5f3
+0,       1522,       1522,        1,  1382400, 0xeba0b5f3
+0,       1523,       1523,        1,  1382400, 0xeba0b5f3
+0,       1524,       1524,        1,  1382400, 0xeba0b5f3
+0,       1525,       1525,        1,  1382400, 0xeba0b5f3
+0,       1526,       1526,        1,  1382400, 0xeba0b5f3
+0,       1527,       1527,        1,  1382400, 0xeba0b5f3
+0,       1528,       1528,        1,  1382400, 0xeba0b5f3
+0,       1530,       1530,        1,  1382400, 0xd6a91770
+0,       1530,       1530,        1,  1382400, 0xd6a91770
+0,       1531,       1531,        1,  1382400, 0xd6a91770
+0,       1532,       1532,        1,  1382400, 0xd6a91770
+0,       1533,       1533,        1,  1382400, 0xd6a91770
+0,       1534,       1534,        1,  1382400, 0xd6a91770
+0,       1535,       1535,        1,  1382400, 0xd6a91770
+0,       1536,       1536,        1,  1382400, 0xd6a91770
+0,       1537,       1537,        1,  1382400, 0xd6a91770
+0,       1538,       1538,        1,  1382400, 0xd6a91770
+0,       1539,       1539,        1,  1382400, 0x00000000
+0,       1540,       1540,        1,  1382400, 0x222f827c
+0,       1541,       1541,        1,  1382400, 0x222f827c
+0,       1542,       1542,        1,  1382400, 0x222f827c
+0,       1543,       1543,        1,  1382400, 0x222f827c
+0,       1544,       1544,        1,  1382400, 0x222f827c
+0,       1545,       1545,        1,  1382400, 0x222f827c
+0,       1546,       1546,        1,  1382400, 0x222f827c
+0,       1547,       1547,        1,  1382400, 0x222f827c
+0,       1548,       1548,        1,  1382400, 0x222f827c
+0,       1549,       1549,        1,  1382400, 0x00000000
+0,       1550,       1550,        1,  1382400, 0x00000000
+0,       1551,       1551,        1,  1382400, 0x00000000
+0,       1552,       1552,        1,  1382400, 0x00000000
+0,       1553,       1553,        1,  1382400, 0x00000000
+0,       1554,       1554,        1,  1382400, 0x00000000
+0,       1555,       1555,        1,  1382400, 0x00000000
+0,       1556,       1556,        1,  1382400, 0x00000000
+0,       1557,       1557,        1,  1382400, 0x00000000
+0,       1558,       1558,        1,  1382400, 0x00000000
+0,       1559,       1559,        1,  1382400, 0x00000000
+0,       1560,       1560,        1,  1382400, 0x00000000
+0,       1561,       1561,        1,  1382400, 0x00000000
+0,       1562,       1562,        1,  1382400, 0x00000000
+0,       1563,       1563,        1,  1382400, 0x00000000
+0,       1564,       1564,        1,  1382400, 0x00000000
+0,       1565,       1565,        1,  1382400, 0x00000000
+0,       1566,       1566,        1,  1382400, 0x00000000
+0,       1567,       1567,        1,  1382400, 0x00000000
+0,       1568,       1568,        1,  1382400, 0x00000000
+0,       1569,       1569,        1,  1382400, 0x00000000
+0,       1570,       1570,        1,  1382400, 0x00000000
+0,       1571,       1571,        1,  1382400, 0x00000000
+0,       1572,       1572,        1,  1382400, 0x00000000
+0,       1573,       1573,        1,  1382400, 0x00000000
+0,       1574,       1574,        1,  1382400, 0x00000000
+0,       1575,       1575,        1,  1382400, 0x00000000
+0,       1576,       1576,        1,  1382400, 0x00000000
+0,       1577,       1577,        1,  1382400, 0x00000000
+0,       1578,       1578,        1,  1382400, 0x00000000
+0,       1579,       1579,        1,  1382400, 0x00000000
+0,       1580,       1580,        1,  1382400, 0x00000000
+0,       1581,       1581,        1,  1382400, 0x00000000
+0,       1582,       1582,        1,  1382400, 0x00000000
+0,       1583,       1583,        1,  1382400, 0x00000000
+0,       1584,       1584,        1,  1382400, 0x00000000
+0,       1585,       1585,        1,  1382400, 0x00000000
+0,       1586,       1586,        1,  1382400, 0x00000000
+0,       1587,       1587,        1,  1382400, 0x00000000
+0,       1588,       1588,        1,  1382400, 0x00000000
+0,       1589,       1589,        1,  1382400, 0x00000000
+0,       1590,       1590,        1,  1382400, 0x00000000
+0,       1591,       1591,        1,  1382400, 0x00000000
+0,       1592,       1592,        1,  1382400, 0x00000000
+0,       1593,       1593,        1,  1382400, 0x00000000
+0,       1594,       1594,        1,  1382400, 0x00000000
+0,       1595,       1595,        1,  1382400, 0x00000000
+0,       1596,       1596,        1,  1382400, 0x00000000
+0,       1597,       1597,        1,  1382400, 0x00000000
+0,       1598,       1598,        1,  1382400, 0x00000000
+0,       1599,       1599,        1,  1382400, 0x00000000
+0,       1600,       1600,        1,  1382400, 0x00000000
+0,       1601,       1601,        1,  1382400, 0x00000000
+0,       1602,       1602,        1,  1382400, 0x00000000
+0,       1603,       1603,        1,  1382400, 0x00000000
+0,       1604,       1604,        1,  1382400, 0x00000000
+0,       1606,       1606,        1,  1382400, 0x3270f4ff
+0,       1606,       1606,        1,  1382400, 0x3270f4ff
+0,       1607,       1607,        1,  1382400, 0x3270f4ff
+0,       1608,       1608,        1,  1382400, 0x3270f4ff
+0,       1609,       1609,        1,  1382400, 0x3270f4ff
+0,       1610,       1610,        1,  1382400, 0x3270f4ff
+0,       1611,       1611,        1,  1382400, 0x3270f4ff
+0,       1612,       1612,        1,  1382400, 0x3270f4ff
+0,       1613,       1613,        1,  1382400, 0x3270f4ff
+0,       1614,       1614,        1,  1382400, 0x3270f4ff
+0,       1615,       1615,        1,  1382400, 0x3270f4ff
+0,       1617,       1617,        1,  1382400, 0x40813cb3
+0,       1617,       1617,        1,  1382400, 0x40813cb3
+0,       1618,       1618,        1,  1382400, 0x40813cb3
+0,       1619,       1619,        1,  1382400, 0x40813cb3
+0,       1620,       1620,        1,  1382400, 0x40813cb3
+0,       1621,       1621,        1,  1382400, 0x40813cb3
+0,       1622,       1622,        1,  1382400, 0x40813cb3
+0,       1623,       1623,        1,  1382400, 0x9d8fde41
+0,       1624,       1624,        1,  1382400, 0x9d8fde41
+0,       1625,       1625,        1,  1382400, 0x9d8fde41
+0,       1626,       1626,        1,  1382400, 0x9d8fde41
+0,       1627,       1627,        1,  1382400, 0x9d8fde41
+0,       1628,       1628,        1,  1382400, 0x9d8fde41
+0,       1629,       1629,        1,  1382400, 0x9d8fde41
+0,       1630,       1630,        1,  1382400, 0x9d8fde41
+0,       1631,       1631,        1,  1382400, 0x9d8fde41
+0,       1632,       1632,        1,  1382400, 0x00000000
+0,       1633,       1633,        1,  1382400, 0x00000000
+0,       1634,       1634,        1,  1382400, 0x00000000
+0,       1636,       1636,        1,  1382400, 0xc6d7a701
+0,       1636,       1636,        1,  1382400, 0xc6d7a701
+0,       1637,       1637,        1,  1382400, 0xc6d7a701
+0,       1638,       1638,        1,  1382400, 0xc6d7a701
+0,       1639,       1639,        1,  1382400, 0xc6d7a701
+0,       1640,       1640,        1,  1382400, 0xc6d7a701
+0,       1642,       1642,        1,  1382400, 0x9d45f2dc
+0,       1642,       1642,        1,  1382400, 0x9d45f2dc
+0,       1643,       1643,        1,  1382400, 0x9d45f2dc
+0,       1644,       1644,        1,  1382400, 0x9d45f2dc
+0,       1645,       1645,        1,  1382400, 0x9d45f2dc
+0,       1646,       1646,        1,  1382400, 0x9d45f2dc
+0,       1647,       1647,        1,  1382400, 0x9d45f2dc
+0,       1648,       1648,        1,  1382400, 0x9d45f2dc
+0,       1649,       1649,        1,  1382400, 0x00000000
+0,       1650,       1650,        1,  1382400, 0x8525ee40
+0,       1651,       1651,        1,  1382400, 0x8525ee40
+0,       1652,       1652,        1,  1382400, 0x8525ee40
+0,       1653,       1653,        1,  1382400, 0x8525ee40
+0,       1654,       1654,        1,  1382400, 0x8525ee40
+0,       1655,       1655,        1,  1382400, 0x8525ee40
+0,       1656,       1656,        1,  1382400, 0x5b26b98b
+0,       1657,       1657,        1,  1382400, 0x5b26b98b
+0,       1658,       1658,        1,  1382400, 0x5b26b98b
+0,       1659,       1659,        1,  1382400, 0x5b26b98b
+0,       1660,       1660,        1,  1382400, 0x5b26b98b
+0,       1661,       1661,        1,  1382400, 0x5b26b98b
+0,       1662,       1662,        1,  1382400, 0x5b26b98b
+0,       1663,       1663,        1,  1382400, 0x5b26b98b
+0,       1664,       1664,        1,  1382400, 0x00000000
+0,       1665,       1665,        1,  1382400, 0x51be311f
+0,       1666,       1666,        1,  1382400, 0x51be311f
+0,       1667,       1667,        1,  1382400, 0x51be311f
+0,       1668,       1668,        1,  1382400, 0x51be311f
+0,       1669,       1669,        1,  1382400, 0x51be311f
+0,       1670,       1670,        1,  1382400, 0x51be311f
+0,       1671,       1671,        1,  1382400, 0x51be311f
+0,       1672,       1672,        1,  1382400, 0x51be311f
+0,       1673,       1673,        1,  1382400, 0x00000000
+0,       1674,       1674,        1,  1382400, 0x00000000
+0,       1675,       1675,        1,  1382400, 0x00000000
+0,       1676,       1676,        1,  1382400, 0x00000000
+0,       1677,       1677,        1,  1382400, 0x00000000
+0,       1678,       1678,        1,  1382400, 0x00000000
+0,       1679,       1679,        1,  1382400, 0x00000000
+0,       1680,       1680,        1,  1382400, 0x00000000
+0,       1681,       1681,        1,  1382400, 0x00000000
+0,       1682,       1682,        1,  1382400, 0x00000000
+0,       1683,       1683,        1,  1382400, 0x00000000
+0,       1684,       1684,        1,  1382400, 0x00000000
+0,       1685,       1685,        1,  1382400, 0x00000000
+0,       1686,       1686,        1,  1382400, 0x00000000
+0,       1687,       1687,        1,  1382400, 0x00000000
+0,       1688,       1688,        1,  1382400, 0x00000000
+0,       1689,       1689,        1,  1382400, 0x00000000
+0,       1690,       1690,        1,  1382400, 0x00000000
+0,       1691,       1691,        1,  1382400, 0x00000000
+0,       1692,       1692,        1,  1382400, 0x00000000
+0,       1693,       1693,        1,  1382400, 0x00000000
+0,       1694,       1694,        1,  1382400, 0x00000000
+0,       1695,       1695,        1,  1382400, 0x00000000
+0,       1696,       1696,        1,  1382400, 0x00000000
+0,       1697,       1697,        1,  1382400, 0x00000000
+0,       1698,       1698,        1,  1382400, 0x00000000
+0,       1699,       1699,        1,  1382400, 0x00000000
+0,       1700,       1700,        1,  1382400, 0x00000000
+0,       1701,       1701,        1,  1382400, 0x00000000
+0,       1702,       1702,        1,  1382400, 0x00000000
+0,       1703,       1703,        1,  1382400, 0x00000000
+0,       1704,       1704,        1,  1382400, 0x00000000
+0,       1705,       1705,        1,  1382400, 0x00000000
+0,       1706,       1706,        1,  1382400, 0x00000000
+0,       1707,       1707,        1,  1382400, 0x00000000
+0,       1708,       1708,        1,  1382400, 0x00000000
+0,       1709,       1709,        1,  1382400, 0x00000000
+0,       1710,       1710,        1,  1382400, 0x00000000
+0,       1711,       1711,        1,  1382400, 0x00000000
+0,       1713,       1713,        1,  1382400, 0x00a4f2a3
+0,       1713,       1713,        1,  1382400, 0x00a4f2a3
+0,       1714,       1714,        1,  1382400, 0x00a4f2a3
+0,       1715,       1715,        1,  1382400, 0x00a4f2a3
+0,       1716,       1716,        1,  1382400, 0x00a4f2a3
+0,       1717,       1717,        1,  1382400, 0x00a4f2a3
+0,       1718,       1718,        1,  1382400, 0x00a4f2a3
+0,       1719,       1719,        1,  1382400, 0x00a4f2a3
+0,       1720,       1720,        1,  1382400, 0x00a4f2a3
+0,       1721,       1721,        1,  1382400, 0x00a4f2a3
+0,       1722,       1722,        1,  1382400, 0x00000000
+0,       1723,       1723,        1,  1382400, 0x00000000
+0,       1724,       1724,        1,  1382400, 0x00000000
+0,       1725,       1725,        1,  1382400, 0x00000000
+0,       1726,       1726,        1,  1382400, 0x00000000
+0,       1727,       1727,        1,  1382400, 0x00000000
+0,       1728,       1728,        1,  1382400, 0x00000000
+0,       1729,       1729,        1,  1382400, 0x00000000
+0,       1730,       1730,        1,  1382400, 0x00000000
+0,       1731,       1731,        1,  1382400, 0x00000000
+0,       1732,       1732,        1,  1382400, 0x00000000
+0,       1734,       1734,        1,  1382400, 0x40a445e8
+0,       1734,       1734,        1,  1382400, 0x40a445e8
+0,       1735,       1735,        1,  1382400, 0x40a445e8
+0,       1736,       1736,        1,  1382400, 0x40a445e8
+0,       1737,       1737,        1,  1382400, 0x40a445e8
+0,       1738,       1738,        1,  1382400, 0x40a445e8
+0,       1739,       1739,        1,  1382400, 0x40a445e8
+0,       1740,       1740,        1,  1382400, 0x40a445e8
+0,       1741,       1741,        1,  1382400, 0x40a445e8
+0,       1742,       1742,        1,  1382400, 0x00000000
+0,       1743,       1743,        1,  1382400, 0x00000000
+0,       1744,       1744,        1,  1382400, 0x00000000
+0,       1745,       1745,        1,  1382400, 0x00000000
+0,       1746,       1746,        1,  1382400, 0x00000000
+0,       1747,       1747,        1,  1382400, 0x00000000
+0,       1748,       1748,        1,  1382400, 0x00000000
+0,       1749,       1749,        1,  1382400, 0x00000000
+0,       1750,       1750,        1,  1382400, 0x00000000
+0,       1751,       1751,        1,  1382400, 0x00000000
+0,       1752,       1752,        1,  1382400, 0x00000000
+0,       1753,       1753,        1,  1382400, 0x00000000
+0,       1754,       1754,        1,  1382400, 0x00000000
+0,       1755,       1755,        1,  1382400, 0x00000000
+0,       1756,       1756,        1,  1382400, 0x00000000
+0,       1757,       1757,        1,  1382400, 0x00000000
+0,       1758,       1758,        1,  1382400, 0x00000000
+0,       1759,       1759,        1,  1382400, 0x00000000
+0,       1760,       1760,        1,  1382400, 0x00000000
+0,       1761,       1761,        1,  1382400, 0x00000000
+0,       1762,       1762,        1,  1382400, 0x00000000
+0,       1763,       1763,        1,  1382400, 0x00000000
+0,       1764,       1764,        1,  1382400, 0x00000000
+0,       1765,       1765,        1,  1382400, 0x00000000
+0,       1766,       1766,        1,  1382400, 0x00000000
+0,       1767,       1767,        1,  1382400, 0x00000000
+0,       1768,       1768,        1,  1382400, 0x00000000
+0,       1769,       1769,        1,  1382400, 0x00000000
+0,       1770,       1770,        1,  1382400, 0x00000000
+0,       1771,       1771,        1,  1382400, 0x00000000
+0,       1772,       1772,        1,  1382400, 0x00000000
+0,       1773,       1773,        1,  1382400, 0x00000000
+0,       1774,       1774,        1,  1382400, 0x00000000
+0,       1775,       1775,        1,  1382400, 0x00000000
+0,       1776,       1776,        1,  1382400, 0x00000000
+0,       1777,       1777,        1,  1382400, 0x00000000
+0,       1778,       1778,        1,  1382400, 0x00000000
+0,       1779,       1779,        1,  1382400, 0x00000000
+0,       1780,       1780,        1,  1382400, 0x00000000
+0,       1781,       1781,        1,  1382400, 0x00000000
+0,       1782,       1782,        1,  1382400, 0x00000000
+0,       1783,       1783,        1,  1382400, 0x00000000
+0,       1784,       1784,        1,  1382400, 0x00000000
+0,       1785,       1785,        1,  1382400, 0x00000000
+0,       1786,       1786,        1,  1382400, 0x00000000
+0,       1788,       1788,        1,  1382400, 0x43ef5128
+0,       1788,       1788,        1,  1382400, 0x43ef5128
+0,       1789,       1789,        1,  1382400, 0x43ef5128
+0,       1790,       1790,        1,  1382400, 0x43ef5128
+0,       1791,       1791,        1,  1382400, 0x43ef5128
+0,       1792,       1792,        1,  1382400, 0x43ef5128
+0,       1793,       1793,        1,  1382400, 0x43ef5128
+0,       1794,       1794,        1,  1382400, 0x43ef5128
+0,       1795,       1795,        1,  1382400, 0x43ef5128
+0,       1796,       1796,        1,  1382400, 0x43ef5128
+0,       1797,       1797,        1,  1382400, 0x43ef5128
+0,       1798,       1798,        1,  1382400, 0x43ef5128
+0,       1799,       1799,        1,  1382400, 0x3c3e3819
+0,       1800,       1800,        1,  1382400, 0x3c3e3819
+0,       1801,       1801,        1,  1382400, 0x3c3e3819
+0,       1802,       1802,        1,  1382400, 0x3c3e3819
+0,       1803,       1803,        1,  1382400, 0x3c3e3819
+0,       1804,       1804,        1,  1382400, 0x3c3e3819
+0,       1805,       1805,        1,  1382400, 0x00000000
diff --git a/tests/ref/fate/sub2video_time_limited b/tests/ref/fate/sub2video_time_limited
index 9fb6fb06f9..2fbd91a7eb 100644
--- a/tests/ref/fate/sub2video_time_limited
+++ b/tests/ref/fate/sub2video_time_limited
@@ -1,8 +1,80 @@
-#tb 0: 1/25
+#tb 0: 1/5
 #media_type 0: video
 #codec_id 0: rawvideo
 #dimensions 0: 1920x1080
-#sar 0: 0/1
-0,          2,          2,        1,  8294400, 0x00000000
+#sar 0: 1/1
+0,          0,          0,        1,  8294400, 0xa87c518f
+0,          1,          1,        1,  8294400, 0xa87c518f
 0,          2,          2,        1,  8294400, 0xa87c518f
+0,          3,          3,        1,  8294400, 0xa87c518f
+0,          4,          4,        1,  8294400, 0xa87c518f
+0,          5,          5,        1,  8294400, 0xa87c518f
+0,          6,          6,        1,  8294400, 0xa87c518f
+0,          7,          7,        1,  8294400, 0xa87c518f
+0,          8,          8,        1,  8294400, 0xa87c518f
+0,          9,          9,        1,  8294400, 0xa87c518f
 0,         10,         10,        1,  8294400, 0xa87c518f
+0,         11,         11,        1,  8294400, 0xa87c518f
+0,         12,         12,        1,  8294400, 0xa87c518f
+0,         13,         13,        1,  8294400, 0xa87c518f
+0,         14,         14,        1,  8294400, 0xa87c518f
+0,         15,         15,        1,  8294400, 0xa87c518f
+0,         16,         16,        1,  8294400, 0xa87c518f
+0,         17,         17,        1,  8294400, 0xa87c518f
+0,         18,         18,        1,  8294400, 0xa87c518f
+0,         19,         19,        1,  8294400, 0xa87c518f
+0,         20,         20,        1,  8294400, 0xa87c518f
+0,         21,         21,        1,  8294400, 0xa87c518f
+0,         22,         22,        1,  8294400, 0xa87c518f
+0,         23,         23,        1,  8294400, 0xa87c518f
+0,         24,         24,        1,  8294400, 0xa87c518f
+0,         25,         25,        1,  8294400, 0xa87c518f
+0,         26,         26,        1,  8294400, 0xa87c518f
+0,         27,         27,        1,  8294400, 0xa87c518f
+0,         28,         28,        1,  8294400, 0xa87c518f
+0,         29,         29,        1,  8294400, 0xa87c518f
+0,         30,         30,        1,  8294400, 0xa87c518f
+0,         31,         31,        1,  8294400, 0xa87c518f
+0,         32,         32,        1,  8294400, 0xa87c518f
+0,         33,         33,        1,  8294400, 0xa87c518f
+0,         34,         34,        1,  8294400, 0xa87c518f
+0,         35,         35,        1,  8294400, 0xa87c518f
+0,         36,         36,        1,  8294400, 0xa87c518f
+0,         37,         37,        1,  8294400, 0xa87c518f
+0,         38,         38,        1,  8294400, 0xa87c518f
+0,         39,         39,        1,  8294400, 0xa87c518f
+0,         40,         40,        1,  8294400, 0xa87c518f
+0,         41,         41,        1,  8294400, 0xa87c518f
+0,         42,         42,        1,  8294400, 0xa87c518f
+0,         43,         43,        1,  8294400, 0xa87c518f
+0,         44,         44,        1,  8294400, 0xa87c518f
+0,         45,         45,        1,  8294400, 0xa87c518f
+0,         46,         46,        1,  8294400, 0xa87c518f
+0,         47,         47,        1,  8294400, 0xa87c518f
+0,         48,         48,        1,  8294400, 0xa87c518f
+0,         49,         49,        1,  8294400, 0xa87c518f
+0,         50,         50,        1,  8294400, 0xa87c518f
+0,         51,         51,        1,  8294400, 0xa87c518f
+0,         52,         52,        1,  8294400, 0xa87c518f
+0,         53,         53,        1,  8294400, 0xa87c518f
+0,         54,         54,        1,  8294400, 0xa87c518f
+0,         55,         55,        1,  8294400, 0xa87c518f
+0,         56,         56,        1,  8294400, 0xa87c518f
+0,         57,         57,        1,  8294400, 0xa87c518f
+0,         58,         58,        1,  8294400, 0xa87c518f
+0,         59,         59,        1,  8294400, 0xa87c518f
+0,         60,         60,        1,  8294400, 0xa87c518f
+0,         61,         61,        1,  8294400, 0xa87c518f
+0,         62,         62,        1,  8294400, 0xa87c518f
+0,         63,         63,        1,  8294400, 0xa87c518f
+0,         64,         64,        1,  8294400, 0xa87c518f
+0,         65,         65,        1,  8294400, 0xa87c518f
+0,         66,         66,        1,  8294400, 0xa87c518f
+0,         67,         67,        1,  8294400, 0xa87c518f
+0,         68,         68,        1,  8294400, 0xa87c518f
+0,         69,         69,        1,  8294400, 0xa87c518f
+0,         70,         70,        1,  8294400, 0xa87c518f
+0,         71,         71,        1,  8294400, 0xa87c518f
+0,         72,         72,        1,  8294400, 0xa87c518f
+0,         73,         73,        1,  8294400, 0xa87c518f
+0,         74,         74,        1,  8294400, 0xa87c518f
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v4 23/23] avcodec/dvbsubdec: Fix conditions for fallback to default resolution
  2022-05-28 13:25     ` [FFmpeg-devel] [PATCH v4 00/23] Subtitle Filtering 2022 ffmpegagent
                         ` (21 preceding siblings ...)
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 22/23] fftools/ffmpeg: Introduce subtitle filtering and new frame-based subtitle encoding softworkz
@ 2022-05-28 13:25       ` softworkz
  2022-06-25  9:57       ` [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022 ffmpegagent
  23 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-05-28 13:25 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

The previous code expected a segment of type CLUT definition to exist
in order to accept a set of segments to be complete.
This was an incorrect assumption as the presence of a CLUT segment
is not mandatory.
(version 1.6.1 of the spec is probably a bit more clear about this
than earlier versions: https://www.etsi.org/deliver/etsi_en/
300700_300799/300743/01.06.01_20/en_300743v010601a.pdf)

The flawed condition prevented proper fallback to using the default
resolution for the decoding context.

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/dvbsubdec.c | 51 +++++++++++++++++++++++++-----------------
 1 file changed, 30 insertions(+), 21 deletions(-)

diff --git a/libavcodec/dvbsubdec.c b/libavcodec/dvbsubdec.c
index fb54cf3f33..489ba3be68 100644
--- a/libavcodec/dvbsubdec.c
+++ b/libavcodec/dvbsubdec.c
@@ -34,7 +34,7 @@
 #define DVBSUB_CLUT_SEGMENT     0x12
 #define DVBSUB_OBJECT_SEGMENT   0x13
 #define DVBSUB_DISPLAYDEFINITION_SEGMENT 0x14
-#define DVBSUB_DISPLAY_SEGMENT  0x80
+#define DVBSUB_END_DISPLAY_SEGMENT  0x80
 
 #define cm (ff_crop_tab + MAX_NEG_CROP)
 
@@ -1619,8 +1619,12 @@ static int dvbsub_decode(AVCodecContext *avctx, AVSubtitle *sub,
     int segment_length;
     int i;
     int ret = 0;
-    int got_segment = 0;
-    int got_dds = 0;
+    //int got_segment = 0;
+    int got_page = 0;
+    int got_region = 0;
+    int got_object = 0;
+    int got_end_display = 0;
+    int got_displaydef = 0;
 
     ff_dlog(avctx, "DVB sub packet:\n");
 
@@ -1665,34 +1669,28 @@ static int dvbsub_decode(AVCodecContext *avctx, AVSubtitle *sub,
             switch (segment_type) {
             case DVBSUB_PAGE_SEGMENT:
                 ret = dvbsub_parse_page_segment(avctx, p, segment_length, sub, got_sub_ptr);
-                got_segment |= 1;
+                got_page = 1;
                 break;
             case DVBSUB_REGION_SEGMENT:
                 ret = dvbsub_parse_region_segment(avctx, p, segment_length);
-                got_segment |= 2;
+                got_region = 1;
                 break;
             case DVBSUB_CLUT_SEGMENT:
                 ret = dvbsub_parse_clut_segment(avctx, p, segment_length);
                 if (ret < 0) goto end;
-                got_segment |= 4;
                 break;
             case DVBSUB_OBJECT_SEGMENT:
                 ret = dvbsub_parse_object_segment(avctx, p, segment_length);
-                got_segment |= 8;
+                got_object = 1;
                 break;
             case DVBSUB_DISPLAYDEFINITION_SEGMENT:
                 ret = dvbsub_parse_display_definition_segment(avctx, p,
                                                               segment_length);
-                got_dds = 1;
+                got_displaydef = 1;
                 break;
-            case DVBSUB_DISPLAY_SEGMENT:
+            case DVBSUB_END_DISPLAY_SEGMENT:
                 ret = dvbsub_display_end_segment(avctx, p, segment_length, sub, got_sub_ptr);
-                if (got_segment == 15 && !got_dds && !avctx->width && !avctx->height) {
-                    // Default from ETSI EN 300 743 V1.3.1 (7.2.1)
-                    avctx->width  = 720;
-                    avctx->height = 576;
-                }
-                got_segment |= 16;
+                got_end_display = 1;
                 break;
             default:
                 ff_dlog(avctx, "Subtitling segment type 0x%x, page id %d, length %d\n",
@@ -1705,13 +1703,24 @@ static int dvbsub_decode(AVCodecContext *avctx, AVSubtitle *sub,
 
         p += segment_length;
     }
-    // Some streams do not send a display segment but if we have all the other
-    // segments then we need no further data.
-    if (got_segment == 15) {
-        av_log(avctx, AV_LOG_DEBUG, "Missing display_end_segment, emulating\n");
-        dvbsub_display_end_segment(avctx, p, 0, sub, got_sub_ptr);
-    }
 
+    // Even though not mandated by the spec, we're imposing a minimum requirement
+    // for a useful packet to have at least one page, region and object segment.
+    if (got_page && got_region && got_object && got_end_display) {
+
+        if (!got_displaydef && !avctx->width && !avctx->height) {
+            // Default from ETSI EN 300 743 V1.3.1 (7.2.1)
+            avctx->width  = 720;
+            avctx->height = 576;
+        }
+
+        // Some streams do not send an end-of-display segment but if we have all the other
+        // segments then we need no further data.
+        if (!got_end_display) {
+            av_log(avctx, AV_LOG_DEBUG, "Missing display_end_segment, emulating\n");
+            dvbsub_display_end_segment(avctx, p, 0, sub, got_sub_ptr);
+        }
+    }
 end:
     if (ret < 0) {
         return ret;
-- 
ffmpeg-codebot
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-05-28 13:25     ` [FFmpeg-devel] [PATCH v4 00/23] Subtitle Filtering 2022 ffmpegagent
                         ` (22 preceding siblings ...)
  2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 23/23] avcodec/dvbsubdec: Fix conditions for fallback to default resolution softworkz
@ 2022-06-25  9:57       ` ffmpegagent
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 01/25] avcodec, avutil: Move enum AVSubtitleType to avutil, add new and deprecate old values softworkz
                           ` (27 more replies)
  23 siblings, 28 replies; 217+ messages in thread
From: ffmpegagent @ 2022-06-25  9:57 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt


Subtitle Filtering 2022
=======================

This is a substantial update to the earlier subtitle filtering patch series.
A primary goal has been to address others' concerns as much as possible on
one side and to provide more clarity and control over the way things are
working. Clarity is is specifically important to allow for a better
understanding of the need for a subtitle start pts value that can be
different from the frame's pts value. This is done by refactoring the
subtitle timing fields in AVFrame, adding a frame field to indicate repeated
subtitle frames, and finally the full removal of the heartbeat
functionality, replaced by a new 'subfeed' filter that provides different
modes for arbitrating subtitle frames in a filter graph. Finally, each
subtitle filter's documentation has been amended by a section describing the
filter's timeline behavior (in v3 update).


Subtitle Filtering Demos
========================

I published a demonstration of subtitle filtering capabilities with OCR,
text and bitmap subtitle manipulation involved: Demo 1: Text-Manipulation
with Bitmap Subtitles
[https://github.com/softworkz/SubtitleFilteringDemos/tree/master/Demo1]


v5 - Conversion to Graphic Subtitles, and other enhancements
============================================================

 * I'm glad to announce that Traian (@tcoza) has joined the project and
   contributed a new 'text2graphicsub' filter to convert text subtitles to
   graphic subtitles, which can in turn be encoded as dvd, dvb or x-subs
   (and any other encoder for graphic subs that might be added in the
   future). This filter closes the last open "gap" in subtitle processing.
 * stripstyles filter: now allows very fine-grained control over which ASS
   style codes should be preserved or stripped
 * stripstyles: do not drop dialog margin values
 * subfeed filter: eliminates duplicate frames with duplicate start times
   when 'fix_overlap' is specified
 * textmod: do not drop effect values
 * graphicsub2text: reduce font size jitter
 * ass_split: add function to selectively preserve elements when splitting
 * add strim, snull and ssink and further unify subtitle frame handling with
   audio and video
 * ffmpeg_filter: get simple filter notation working for subtitles


v4 - Quality Improvements
=========================

 * finally an updated version
 * includes many improvements from internal testing
 * all FATE tests passed
 * all example commands from the docs verified to work
 * can't list all the detail changes..
 * I have left out the extra commits which can be handled separately, just
   in case somebody wonders why these are missing:
   * avcodec/webvttenc: Don't encode drawing codes and empty lines
   * avcodec/webvttenc: convert hard-space tags to  
   * avutil/ass_split: Add parsing of hard-space tags (\h)
   * avutil/ass_split: Treat all content in curly braces as hidden
   * avutil/ass_split: Fix ass parsing of style codes with comments


v3 - Rebase
===========

due to merge conflicts - apologies.


Changes in v2
=============

 * added .gitattributes file to enforce binary diffs for the test refs that
   cannot be applied when being sent via e-mail
 * perform filter graph re-init due to subtitle "frame size" change only
   when the size was unknown before and not set via -canvas_size
 * overlaytextsubs: Make sure to request frames on the subtitle input
 * avfilter/splitcc: Start parsing cc data on key frames only
 * avcodec/webvttenc: Don't encode ass drawing codes and empty lines
 * stripstyles: fix mem leak
 * gs2t: improve color detection
 * gs2t: empty frames must not be skipped
 * subfeed: fix name
 * textmod: preserve margins
 * added .gitattributes file to enforce binary diffs for the test refs that
   cannot be applied when being sent via e-mail
 * perform filter graph re-init due to subtitle "frame size" change only
   when the size was unknown before and not set via -canvas_size
 * avcodec/dvbsubdec: Fix conditions for fallback to default resolution
 * Made changes suggested by Andreas
 * Fixed failing command line reported by Michael

Changes from previous version v24:


AVFrame
=======

 * Removed sub_start_time The start time is now added to the subtitle
   start_pts during decoding The sub_end_time field is adjusted accordingly
 * Renamed sub_end_time to duration which it is effectively after removing
   the start_time
 * Added a sub-struct 'subtitle_timing' to av frame Contains subtitle_pts
   renamed to 'subtitle_timing.start_pts' and 'subtitle_timing.duration'
 * Change both fields to (fixed) time_base AV_TIMEBASE
 * add repeat_sub field provides a clear indication whether a subtitle frame
   is an actual subtitle event or a repeated subtitle frame in a filter
   graph


Heartbeat Removal
=================

 * completely removed the earlier heartbeat implementation
 * filtering arbitration is now implemented in a new filter: 'subfeed'
 * subfeed will be auto-inserted for compatiblity with sub2video command
   lines
 * the new behavior is not exactly identical to the earlier behavior, but it
   basically allows to achieve the same results
 * there's a small remainder, now named subtitle kickoff which serves to get
   things (in the filter graph) going right from the start


New 'subfeed' Filter
====================

 * a versatile filter for solving all kinds of problems with subtile frame
   flow in filter graphs
 * Can be inserted at any position in a graph
 * Auto-inserted for sub2video command lines (in repeat-mode)
 * Allows duration fixup delay input frames with unknown duration and infer
   duration from start of subsequent frame
 * Provides multiple modes of operation:
   * repeat mode (default) Queues input frames Outputs frames at a fixed
     (configurable) rate Either sends a matching input frame (repeatedly) or
     empty frames otherwise
   * scatter mode similar to repeat mode, but splits input frames by
     duration into small segments with same content
   * forward mode No fixed output rate Useful in combination with duration
     fixup or overlap fixup


ffmpeg Tool Changes
===================

 * delay subtitle output stream initialization (like for audio and video)
   This is needed for example when a format header depends on having
   received an initial frame to derive certain header values from
 * decoding: set subtitle frame size from decoding context
 * re-init graph when subtitle size changes
 * always insert subscale filter for sub2video command lines (to ensure
   correct scaling)


Subtitle Encoding
=================

 * ignore repeated frames for encoding based on repeat_sub field in AVFrame
 * support multi-area encoding for text subtitles Subtitle OCR can create
   multiple areas at different positions. Previously, the texts were always
   squashed into a single area ('subtitle rect'), which was not ideal.
   Multiple text areas are now generally supported:
   * ASS Encoder Changed to use the 'receive_packet' encoding API A single
     frame with multiple text areas will create multiple packets now
   * All other text subtitle encoders A newline is inserted between the text
     from multiple areas


graphicsub2text (OCR)
=====================

 * enhanced preprocessing
   * using elbg algorithm for color quantization
   * detection and removal of text outlines
   * map-based identification of colors per word (text, outline, background)
 * add option for duration fixup
 * add option to dump preprocessing bitmaps
 * Recognize formatting and apply as ASS inline styles
   * per word(!)
   * paragraph alignment
   * positioning
   * font names
   * font size
   * font style (italic, underline, bold)
   * text color, outline color


Other Filter Changes
====================

 * all: Make sure to forward all link properties (time base, frame rate, w,
   h) where appropriate
 * overlaytextsubs: request frames on the subtitle input
 * overlaytextsubs: disable read-order checking
 * overlaytextsubs: improve implementation of render_latest_only
 * overlaytextsubs: ensure equal in/out video formats
 * splitcc: derive framerate from realtime_latency
 * graphicsub2video: implement caching of converted frames
 * graphicsub2video: use 1x1 output frame size as long as subtitle size is
   unknown (0x0)

Plus a dozen of things I forgot..

softworkz (24):
  avcodec,avutil: Move enum AVSubtitleType to avutil, add new and
    deprecate old values
  avutil/frame: Prepare AVFrame for subtitle handling
  avcodec/subtitles: Introduce new frame-based subtitle decoding API
  avcodec/libzvbi: set subtitle type
  avfilter/subtitles: Update vf_subtitles to use new decoding api
  avcodec,avutil: Move ass helper functions to avutil as avpriv_ and
    extend ass dialog parsing
  avcodec/subtitles: Replace deprecated enum values
  fftools/play,probe: Adjust for subtitle changes
  avfilter/subtitles: Add subtitles.c for subtitle frame allocation
  avfilter/avfilter: Handle subtitle frames
  avfilter/avfilter: Fix hardcoded input index
  avfilter/sbuffer: Add sbuffersrc and sbuffersink filters
  avfilter/overlaygraphicsubs: Add overlaygraphicsubs and
    graphicsub2video filters
  avfilter/overlaytextsubs: Add overlaytextsubs and textsubs2video
    filters
  avfilter/textmod: Add textmod, censor and show_speaker filters
  avfilter/stripstyles: Add stripstyles filter
  avfilter/splitcc: Add splitcc filter for closed caption handling
  avfilter/graphicsub2text: Add new graphicsub2text filter (OCR)
  avfilter/subscale: Add filter for scaling and/or re-arranging
    graphical subtitles
  avfilter/subfeed: add subtitle feed filter
  avfilter/snull,strim: Add snull and strim filters
  avcodec/subtitles: Migrate subtitle encoders to frame-based API
  fftools/ffmpeg: Introduce subtitle filtering and new frame-based
    subtitle encoding
  avcodec/dvbsubdec: Fix conditions for fallback to default resolution

tcoza (1):
  avfilter/text2graphicsub: Added text2graphicsub subtitle filter

 configure                                 |   10 +-
 doc/filters.texi                          |  807 ++++++++++++++
 fftools/ffmpeg.c                          |  613 +++++-----
 fftools/ffmpeg.h                          |   17 +-
 fftools/ffmpeg_filter.c                   |  270 +++--
 fftools/ffmpeg_hw.c                       |    2 +-
 fftools/ffmpeg_opt.c                      |   28 +-
 fftools/ffplay.c                          |  102 +-
 fftools/ffprobe.c                         |   47 +-
 libavcodec/Makefile                       |   56 +-
 libavcodec/ass.h                          |  151 +--
 libavcodec/ass_split.h                    |  191 ----
 libavcodec/assdec.c                       |    4 +-
 libavcodec/assenc.c                       |  191 +++-
 libavcodec/avcodec.c                      |    8 +
 libavcodec/avcodec.h                      |   34 +-
 libavcodec/ccaption_dec.c                 |   20 +-
 libavcodec/codec_internal.h               |   12 -
 libavcodec/decode.c                       |   60 +-
 libavcodec/dvbsubdec.c                    |   53 +-
 libavcodec/dvbsubenc.c                    |   96 +-
 libavcodec/dvdsubdec.c                    |    2 +-
 libavcodec/dvdsubenc.c                    |  102 +-
 libavcodec/encode.c                       |   61 +-
 libavcodec/internal.h                     |   16 +
 libavcodec/jacosubdec.c                   |    2 +-
 libavcodec/libaribb24.c                   |    2 +-
 libavcodec/libzvbi-teletextdec.c          |   17 +-
 libavcodec/microdvddec.c                  |    7 +-
 libavcodec/movtextdec.c                   |    3 +-
 libavcodec/movtextenc.c                   |  126 ++-
 libavcodec/mpl2dec.c                      |    2 +-
 libavcodec/pgssubdec.c                    |    2 +-
 libavcodec/realtextdec.c                  |    2 +-
 libavcodec/samidec.c                      |    2 +-
 libavcodec/srtdec.c                       |    2 +-
 libavcodec/srtenc.c                       |  116 +-
 libavcodec/subviewerdec.c                 |    2 +-
 libavcodec/tests/avcodec.c                |    5 +-
 libavcodec/textdec.c                      |    4 +-
 libavcodec/ttmlenc.c                      |  114 +-
 libavcodec/utils.c                        |  185 ++-
 libavcodec/webvttdec.c                    |    2 +-
 libavcodec/webvttenc.c                    |   94 +-
 libavcodec/xsubdec.c                      |    2 +-
 libavcodec/xsubenc.c                      |   88 +-
 libavfilter/Makefile                      |   18 +
 libavfilter/allfilters.c                  |   19 +
 libavfilter/avfilter.c                    |   34 +-
 libavfilter/avfilter.h                    |   11 +
 libavfilter/avfiltergraph.c               |    5 +
 libavfilter/buffersink.c                  |   54 +
 libavfilter/buffersink.h                  |    7 +
 libavfilter/buffersrc.c                   |   72 ++
 libavfilter/buffersrc.h                   |    1 +
 libavfilter/formats.c                     |   16 +
 libavfilter/formats.h                     |    3 +
 libavfilter/internal.h                    |   19 +-
 libavfilter/sf_graphicsub2text.c          | 1137 +++++++++++++++++++
 libavfilter/sf_snull.c                    |   50 +
 libavfilter/sf_splitcc.c                  |  395 +++++++
 libavfilter/sf_stripstyles.c              |  237 ++++
 libavfilter/sf_subfeed.c                  |  412 +++++++
 libavfilter/sf_subscale.c                 |  884 +++++++++++++++
 libavfilter/sf_text2graphicsub.c          |  630 +++++++++++
 libavfilter/sf_textmod.c                  |  710 ++++++++++++
 libavfilter/subtitles.c                   |   63 ++
 libavfilter/subtitles.h                   |   44 +
 libavfilter/trim.c                        |   46 +-
 libavfilter/vf_overlaygraphicsubs.c       |  765 +++++++++++++
 libavfilter/vf_overlaytextsubs.c          |  680 +++++++++++
 libavfilter/vf_subtitles.c                |   67 +-
 libavutil/Makefile                        |    4 +
 {libavcodec => libavutil}/ass.c           |  115 +-
 libavutil/ass_internal.h                  |  135 +++
 {libavcodec => libavutil}/ass_split.c     |  179 ++-
 libavutil/ass_split_internal.h            |  254 +++++
 libavutil/frame.c                         |  206 +++-
 libavutil/frame.h                         |   85 +-
 libavutil/subfmt.c                        |   45 +
 libavutil/subfmt.h                        |  115 ++
 libavutil/version.h                       |    1 +
 tests/ref/fate/filter-overlay-dvdsub-2397 |  182 +--
 tests/ref/fate/sub-dvb                    |  162 +--
 tests/ref/fate/sub-scc                    |    1 -
 tests/ref/fate/sub2video                  | 1091 +++++++++++++++++-
 tests/ref/fate/sub2video_basic            | 1238 +++++++++++++++++++--
 tests/ref/fate/sub2video_time_limited     |   78 +-
 88 files changed, 12398 insertions(+), 1604 deletions(-)
 delete mode 100644 libavcodec/ass_split.h
 create mode 100644 libavfilter/sf_graphicsub2text.c
 create mode 100644 libavfilter/sf_snull.c
 create mode 100644 libavfilter/sf_splitcc.c
 create mode 100644 libavfilter/sf_stripstyles.c
 create mode 100644 libavfilter/sf_subfeed.c
 create mode 100644 libavfilter/sf_subscale.c
 create mode 100644 libavfilter/sf_text2graphicsub.c
 create mode 100644 libavfilter/sf_textmod.c
 create mode 100644 libavfilter/subtitles.c
 create mode 100644 libavfilter/subtitles.h
 create mode 100644 libavfilter/vf_overlaygraphicsubs.c
 create mode 100644 libavfilter/vf_overlaytextsubs.c
 rename {libavcodec => libavutil}/ass.c (59%)
 create mode 100644 libavutil/ass_internal.h
 rename {libavcodec => libavutil}/ass_split.c (71%)
 create mode 100644 libavutil/ass_split_internal.h
 create mode 100644 libavutil/subfmt.c
 create mode 100644 libavutil/subfmt.h


base-commit: 6a82412bf33108111eb3f63076fd5a51349ae114
Published-As: https://github.com/ffstaging/FFmpeg/releases/tag/pr-ffstaging-18%2Fsoftworkz%2Fsubmit_subfiltering-v5
Fetch-It-Via: git fetch https://github.com/ffstaging/FFmpeg pr-ffstaging-18/softworkz/submit_subfiltering-v5
Pull-Request: https://github.com/ffstaging/FFmpeg/pull/18

Range-diff vs v4:

  1:  2f3ba171f5 =  1:  aa32b9048f avcodec,avutil: Move enum AVSubtitleType to avutil, add new and deprecate old values
  2:  ff101f8a76 !  2:  d5ab9d1919 avutil/frame: Prepare AVFrame for subtitle handling
     @@ libavutil/frame.c: FF_ENABLE_DEPRECATION_WARNINGS
      +    case AVMEDIA_TYPE_VIDEO:
               return frame_copy_video(dst, src);
      -    else if (dst->nb_samples > 0 &&
     --             (av_channel_layout_check(&dst->ch_layout)
     --#if FF_API_OLD_CHANNEL_LAYOUT
     --              || dst->channel_layout || dst->channels
     --#endif
     --            ))
      +    case AVMEDIA_TYPE_AUDIO:
     -         return frame_copy_audio(dst, src);
     ++        if (dst->nb_samples > 0 &&
     +              (av_channel_layout_check(&dst->ch_layout)
     + #if FF_API_OLD_CHANNEL_LAYOUT
     +               || dst->channels > 0
     + #endif
     +             ))
     +-        return frame_copy_audio(dst, src);
      -FF_ENABLE_DEPRECATION_WARNINGS
     --
     --    return AVERROR(EINVAL);
     ++            return frame_copy_audio(dst, src);
     ++        break;
      +    case AVMEDIA_TYPE_SUBTITLE:
      +        return frame_copy_subtitles(dst, src, 1);
     -+    default:
     -+        return AVERROR(EINVAL);
      +    }
     - }
       
     - void av_frame_remove_side_data(AVFrame *frame, enum AVFrameSideDataType type)
     +     return AVERROR(EINVAL);
     + }
      
       ## libavutil/frame.h ##
      @@
  3:  b8935d5e68 =  3:  0a685a6b19 avcodec/subtitles: Introduce new frame-based subtitle decoding API
  4:  4b44732e07 =  4:  0b69b1ce19 avcodec/libzvbi: set subtitle type
  5:  8faa7a4043 =  5:  0c2091e57c avfilter/subtitles: Update vf_subtitles to use new decoding api
  6:  1664026d7c !  6:  4903cdd1cd avcodec,avutil: Move ass helper functions to avutil as avpriv_ and extend ass dialog parsing
     @@ Commit message
      
          - hard_space callback (for upcoming fix)
          - extensible callback (for future extension)
     +    - new API which allows tag filtering
      
          Signed-off-by: softworkz <softworkz@hotmail.com>
      
     @@ libavcodec/ass.h
      -                             const char *linebreaks, int keep_ass_markup);
       #endif /* AVCODEC_ASS_H */
      
     + ## libavcodec/ass_split.h (deleted) ##
     +@@
     +-/*
     +- * SSA/ASS spliting functions
     +- * Copyright (c) 2010  Aurelien Jacobs <aurel@gnuage.org>
     +- *
     +- * 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
     +- */
     +-
     +-#ifndef AVCODEC_ASS_SPLIT_H
     +-#define AVCODEC_ASS_SPLIT_H
     +-
     +-/**
     +- * fields extracted from the [Script Info] section
     +- */
     +-typedef struct {
     +-    char *script_type;    /**< SSA script format version (eg. v4.00) */
     +-    char *collisions;     /**< how subtitles are moved to prevent collisions */
     +-    int   play_res_x;     /**< video width that ASS coords are referring to */
     +-    int   play_res_y;     /**< video height that ASS coords are referring to */
     +-    float timer;          /**< time multiplier to apply to SSA clock (in %) */
     +-} ASSScriptInfo;
     +-
     +-/**
     +- * fields extracted from the [V4(+) Styles] section
     +- */
     +-typedef struct {
     +-    char *name;           /**< name of the tyle (case sensitive) */
     +-    char *font_name;      /**< font face (case sensitive) */
     +-    int   font_size;      /**< font height */
     +-    int   primary_color;  /**< color that a subtitle will normally appear in */
     +-    int   secondary_color;
     +-    int   outline_color;  /**< color for outline in ASS, called tertiary in SSA */
     +-    int   back_color;     /**< color of the subtitle outline or shadow */
     +-    int   bold;           /**< whether text is bold (1) or not (0) */
     +-    int   italic;         /**< whether text is italic (1) or not (0) */
     +-    int   underline;      /**< whether text is underlined (1) or not (0) */
     +-    int   strikeout;
     +-    float scalex;
     +-    float scaley;
     +-    float spacing;
     +-    float angle;
     +-    int   border_style;
     +-    float outline;
     +-    float shadow;
     +-    int   alignment;      /**< position of the text (left, center, top...),
     +-                               defined after the layout of the numpad
     +-                               (1-3 sub, 4-6 mid, 7-9 top) */
     +-    int   margin_l;
     +-    int   margin_r;
     +-    int   margin_v;
     +-    int   alpha_level;
     +-    int   encoding;
     +-} ASSStyle;
     +-
     +-/**
     +- * fields extracted from the [Events] section
     +- */
     +-typedef struct {
     +-    int   readorder;
     +-    int   layer;    /**< higher numbered layers are drawn over lower numbered */
     +-    int   start;    /**< start time of the dialog in centiseconds */
     +-    int   end;      /**< end time of the dialog in centiseconds */
     +-    char *style;    /**< name of the ASSStyle to use with this dialog */
     +-    char *name;
     +-    int   margin_l;
     +-    int   margin_r;
     +-    int   margin_v;
     +-    char *effect;
     +-    char *text;     /**< actual text which will be displayed as a subtitle,
     +-                         can include style override control codes (see
     +-                         ff_ass_split_override_codes()) */
     +-} ASSDialog;
     +-
     +-/**
     +- * structure containing the whole split ASS data
     +- */
     +-typedef struct {
     +-    ASSScriptInfo script_info;   /**< general information about the SSA script*/
     +-    ASSStyle     *styles;        /**< array of split out styles */
     +-    int           styles_count;  /**< number of ASSStyle in the styles array */
     +-    ASSDialog    *dialogs;       /**< array of split out dialogs */
     +-    int           dialogs_count; /**< number of ASSDialog in the dialogs array*/
     +-} ASS;
     +-
     +-/**
     +- * This struct can be casted to ASS to access to the split data.
     +- */
     +-typedef struct ASSSplitContext ASSSplitContext;
     +-
     +-/**
     +- * Split a full ASS file or a ASS header from a string buffer and store
     +- * the split structure in a newly allocated context.
     +- *
     +- * @param buf String containing the ASS formatted data.
     +- * @return Newly allocated struct containing split data.
     +- */
     +-ASSSplitContext *ff_ass_split(const char *buf);
     +-
     +-/**
     +- * Free a dialogue obtained from ff_ass_split_dialog().
     +- */
     +-void ff_ass_free_dialog(ASSDialog **dialogp);
     +-
     +-/**
     +- * Split one ASS Dialogue line from a string buffer.
     +- *
     +- * @param ctx Context previously initialized by ff_ass_split().
     +- * @param buf String containing the ASS "Dialogue" line.
     +- * @return Pointer to the split ASSDialog. Must be freed with ff_ass_free_dialog()
     +- */
     +-ASSDialog *ff_ass_split_dialog(ASSSplitContext *ctx, const char *buf);
     +-
     +-/**
     +- * Free all the memory allocated for an ASSSplitContext.
     +- *
     +- * @param ctx Context previously initialized by ff_ass_split().
     +- */
     +-void ff_ass_split_free(ASSSplitContext *ctx);
     +-
     +-
     +-/**
     +- * Set of callback functions corresponding to each override codes that can
     +- * be encountered in a "Dialogue" Text field.
     +- */
     +-typedef struct {
     +-    /**
     +-     * @defgroup ass_styles    ASS styles
     +-     * @{
     +-     */
     +-    void (*text)(void *priv, const char *text, int len);
     +-    void (*new_line)(void *priv, int forced);
     +-    void (*style)(void *priv, char style, int close);
     +-    void (*color)(void *priv, unsigned int /* color */, unsigned int color_id);
     +-    void (*alpha)(void *priv, int alpha, int alpha_id);
     +-    void (*font_name)(void *priv, const char *name);
     +-    void (*font_size)(void *priv, int size);
     +-    void (*alignment)(void *priv, int alignment);
     +-    void (*cancel_overrides)(void *priv, const char *style);
     +-    /** @} */
     +-
     +-    /**
     +-     * @defgroup ass_functions    ASS functions
     +-     * @{
     +-     */
     +-    void (*move)(void *priv, int x1, int y1, int x2, int y2, int t1, int t2);
     +-    void (*origin)(void *priv, int x, int y);
     +-    /** @} */
     +-
     +-    /**
     +-     * @defgroup ass_end    end of Dialogue Event
     +-     * @{
     +-     */
     +-    void (*end)(void *priv);
     +-    /** @} */
     +-} ASSCodesCallbacks;
     +-
     +-/**
     +- * Split override codes out of a ASS "Dialogue" Text field.
     +- *
     +- * @param callbacks Set of callback functions called for each override code
     +- *                  encountered.
     +- * @param priv Opaque pointer passed to the callback functions.
     +- * @param buf The ASS "Dialogue" Text field to split.
     +- * @return >= 0 on success otherwise an error code <0
     +- */
     +-int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
     +-                                const char *buf);
     +-
     +-/**
     +- * Find an ASSStyle structure by its name.
     +- *
     +- * @param ctx Context previously initialized by ff_ass_split().
     +- * @param style name of the style to search for.
     +- * @return the ASSStyle corresponding to style, or NULL if style can't be found
     +- */
     +-ASSStyle *ff_ass_style_get(ASSSplitContext *ctx, const char *style);
     +-
     +-#endif /* AVCODEC_ASS_SPLIT_H */
     +
       ## libavcodec/assdec.c ##
      @@
       #include <string.h>
     @@ libavutil/ass.c: char *ff_ass_get_dialog(int readorder, int layer, const char *s
      -                    const char *speaker)
      +char *avpriv_ass_get_dialog_ex(int readorder, int layer, const char *style,
      +                        const char *speaker, int margin_l, int margin_r,
     -+                        int margin_v, const char *text)
     ++                        int margin_v, const char *effect, const char *text)
       {
      -    return ff_ass_add_rect2(sub, dialog, readorder, layer, style, speaker, NULL);
      -}
     @@ libavutil/ass.c: char *ff_ass_get_dialog(int readorder, int layer, const char *s
      -    FFASSDecoderContext *s = avctx->priv_data;
      -    if (!(avctx->flags2 & AV_CODEC_FLAG2_RO_FLUSH_NOOP))
      -        s->readorder = 0;
     -+    return av_asprintf("%d,%d,%s,%s,%d,%d,%d,,%s",
     ++    return av_asprintf("%d,%d,%s,%s,%d,%d,%d,%s,%s",
      +                       readorder, layer, style ? style : "Default",
      +                       speaker ? speaker : "", margin_l, margin_r,
     -+                       margin_v, text);
     ++                       margin_v, effect ? effect : "", text);
       }
       
      -void ff_ass_bprint_text_event(AVBPrint *buf, const char *p, int size,
     @@ libavutil/ass_internal.h (new)
      + */
      +char *avpriv_ass_get_dialog_ex(int readorder, int layer, const char *style,
      +                        const char *speaker, int margin_l, int margin_r,
     -+                        int margin_v, const char *text);
     ++                        int margin_v, const char *effect, const char *text);
      +
      +/**
      + * Escape a text subtitle using ASS syntax into an AVBPrint buffer.
     @@ libavutil/ass_split.c: ASSDialog *ff_ass_split_dialog(ASSSplitContext *ctx, cons
           if (ctx) {
               int i;
      @@ libavutil/ass_split.c: void ff_ass_split_free(ASSSplitContext *ctx)
     +     }
       }
       
     ++static int ass_remove_empty_braces(AVBPrint* buffer)
     ++{
     ++    char* tmp;
     ++    int ret = 0, n = 0;
       
      -int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
     -+int avpriv_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
     -                                 const char *buf)
     +-                                const char *buf)
     ++    if (buffer == NULL || buffer->len == 0 || !av_bprint_is_complete(buffer))
     ++        return 0;
     ++
     ++    ret = av_bprint_finalize(buffer, &tmp);
     ++    if (ret)
     ++        return ret;
     ++
     ++    for (unsigned i = 0; i < buffer->len; i++) {
     ++        if (tmp[i] == '{' && tmp[i+1] == '}')
     ++            i++;
     ++        else
     ++            tmp[n++] = tmp[i];
     ++    }
     ++
     ++    tmp[n++] = '\0';
     ++
     ++    av_bprint_init(buffer, n, n);
     ++    av_bprint_append_data(buffer, tmp, n - 1);
     ++    av_free(tmp);
     ++
     ++    return ret;
     ++}
     ++
     ++static void ass_write_filtered_line(AVBPrint* buffer, const char *buf, int len, enum ASSSplitComponents keep_flags, enum ASSSplitComponents split_component)
     ++{
     ++    if (buffer == NULL || buf == NULL || len == 0)
     ++        return;
     ++
     ++    if (split_component != ASS_SPLIT_ANY && !(keep_flags & split_component))
     ++        return;
     ++
     ++
     ++    av_bprint_append_data(buffer, buf, len - 1);
     ++}
     ++
     ++int avpriv_ass_filter_override_codes(const ASSCodesCallbacks *callbacks, void *priv, const char *buf, AVBPrint* outbuffer, enum ASSSplitComponents keep_flags)
       {
           const char *text = NULL;
     -@@ libavutil/ass_split.c: int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
     +     char new_line[2];
     +-    int text_len = 0;
     ++    int text_len = 0, ret = 0;
     + 
     +     while (buf && *buf) {
     +-        if (text && callbacks->text &&
     +-            (sscanf(buf, "\\%1[nN]", new_line) == 1 ||
     +-             !strncmp(buf, "{\\", 2))) {
     +-            callbacks->text(priv, text, text_len);
     ++
     ++        if (text && (sscanf(buf, "\\%1[nN]", new_line) == 1 || !strncmp(buf, "{\\", 2))) {
     ++            ass_write_filtered_line(outbuffer, text, text_len + 1, keep_flags, ASS_SPLIT_TEXT | ASS_SPLIT_TEXT2);
     ++
     ++            if (callbacks->text)
     ++                callbacks->text(priv, text, text_len);
     +             text = NULL;
     +         }
     ++
     +         if (sscanf(buf, "\\%1[nN]", new_line) == 1) {
     +             if (callbacks->new_line)
     +                 callbacks->new_line(priv, new_line[0] == 'N');
     ++            ass_write_filtered_line(outbuffer, buf, 3, keep_flags, ASS_SPLIT_ANY);
     +             buf += 2;
     +         } else if (!strncmp(buf, "{\\", 2)) {
     ++            ass_write_filtered_line(outbuffer, buf, 2, keep_flags, ASS_SPLIT_ANY);
     +             buf++;
                   while (*buf == '\\') {
     -                 char style[2], c[2], sep[2], c_num[2] = "0", tmp[128] = {0};
     +-                char style[2], c[2], sep[2], c_num[2] = "0", tmp[128] = {0};
     ++                char style[4], c[2], axis[3], sep[3], c_num[2] = "0", tmp[128] = {0};
                       unsigned int color = 0xFFFFFFFF;
      -                int len, size = -1, an = -1, alpha = -1;
      -                int x1, y1, x2, y2, t1 = -1, t2 = -1;
      +                int len, size = -1, an = -1, alpha = -1, scale = 0;
     -+                int x1, y1, x2, y2, t1 = -1, t2 = -1, accel = 1;
     ++                float f1 = 1;
     ++                int x1, y1, x2, y2, x3, t1 = -1, t2 = -1, t3 = -1, t4 = -1, accel = 1;
                       if (sscanf(buf, "\\%1[bisu]%1[01\\}]%n", style, c, &len) > 1) {
                           int close = c[0] == '0' ? 1 : c[0] == '1' ? 0 : -1;
                           len += close != -1;
     -@@ libavutil/ass_split.c: int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
     ++                    switch (c[0]) {
     ++                    case 'b':
     ++                        ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FONT_BOLD);
     ++                        break;
     ++                    case 'u':
     ++                        ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FONT_UNDERLINE);
     ++                        break;
     ++                    case 'i':
     ++                        ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FONT_ITALIC);
     ++                        break;
     ++                    case 'a':
     ++                        ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FONT_STRIKEOUT);
     ++                        break;
     ++                    }
     +                     if (callbacks->style)
     +                         callbacks->style(priv, style[0], close);
     +                 } else if (sscanf(buf, "\\c%1[\\}]%n", sep, &len) > 0 ||
     +                            sscanf(buf, "\\c&H%X&%1[\\}]%n", &color, sep, &len) > 1 ||
     +                            sscanf(buf, "\\%1[1234]c%1[\\}]%n", c_num, sep, &len) > 1 ||
     +                            sscanf(buf, "\\%1[1234]c&H%X&%1[\\}]%n", c_num, &color, sep, &len) > 2) {
     ++                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_COLOR);
     +                     if (callbacks->color)
     +                         callbacks->color(priv, color, c_num[0] - '0');
     +                 } else if (sscanf(buf, "\\alpha%1[\\}]%n", sep, &len) > 0 ||
     +                            sscanf(buf, "\\alpha&H%2X&%1[\\}]%n", &alpha, sep, &len) > 1 ||
     +                            sscanf(buf, "\\%1[1234]a%1[\\}]%n", c_num, sep, &len) > 1 ||
     +                            sscanf(buf, "\\%1[1234]a&H%2X&%1[\\}]%n", c_num, &alpha, sep, &len) > 2) {
     ++                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_ALPHA);
     +                     if (callbacks->alpha)
     +                         callbacks->alpha(priv, alpha, c_num[0] - '0');
     +                 } else if (sscanf(buf, "\\fn%1[\\}]%n", sep, &len) > 0 ||
     +                            sscanf(buf, "\\fn%127[^\\}]%1[\\}]%n", tmp, sep, &len) > 1) {
     ++                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FONT_NAME);
     +                     if (callbacks->font_name)
     +                         callbacks->font_name(priv, tmp[0] ? tmp : NULL);
     +                 } else if (sscanf(buf, "\\fs%1[\\}]%n", sep, &len) > 0 ||
     +                            sscanf(buf, "\\fs%u%1[\\}]%n", &size, sep, &len) > 1) {
     ++                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FONT_SIZE);
     +                     if (callbacks->font_size)
     +                         callbacks->font_size(priv, size);
     ++                } else if (sscanf(buf, "\\fscx%1[\\}]%n", sep, &len) > 0 ||
     ++                           sscanf(buf, "\\fscx%f%1[\\}]%n", &f1, sep, &len) > 1) {
     ++                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FONT_SCALE);
     ++                } else if (sscanf(buf, "\\fscy%1[\\}]%n", sep, &len) > 0 ||
     ++                           sscanf(buf, "\\fscy%f%1[\\}]%n", &f1, sep, &len) > 1) {
     ++                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FONT_SCALE);
     ++                } else if (sscanf(buf, "\\fsp%1[\\}]%n", sep, &len) > 0 ||
     ++                           sscanf(buf, "\\fsp%u%1[\\}]%n", &size, sep, &len) > 1) {
     ++                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FONT_SPACING);
     ++                } else if (sscanf(buf, "\\fe%1[\\}]%n", sep, &len) > 0 ||
     ++                           sscanf(buf, "\\fe%u%1[\\}]%n", &size, sep, &len) > 1) {
     ++                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FONT_CHARSET);
     ++                } else if (sscanf(buf, "\\bord%1[\\}]%n", sep, &len) > 0 ||
     ++                           sscanf(buf, "\\bord%u%1[\\}]%n", &size, sep, &len) > 1) {
     ++                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_TEXT_BORDER);
     ++                } else if (sscanf(buf, "\\shad%1[\\}]%n", sep, &len) > 0 ||
     ++                           sscanf(buf, "\\shad%u%1[\\}]%n", &size, sep, &len) > 1) {
     ++                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_TEXT_SHADOW);
     ++                } else if (sscanf(buf, "\\fr%1[\\}]%n", sep, &len) > 0 ||
     ++                           sscanf(buf, "\\fr%u%1[\\}]%n", &x1, sep, &len) > 1 ||
     ++                           sscanf(buf, "\\fr%1[xyz]%1[\\}]%n", axis, sep, &len) > 1 ||
     ++                           sscanf(buf, "\\fr%1[xyz]%u%1[\\}]%n", axis, &size, sep, &len) > 2) {
     ++                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_TEXT_ROTATE);
     ++                } else if (sscanf(buf, "\\blur%1[\\}]%n", sep, &len) > 0 ||
     ++                           sscanf(buf, "\\blur%u%1[\\}]%n", &size, sep, &len) > 1) {
     ++                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_TEXT_BLUR);
     ++                } else if (sscanf(buf, "\\be%1[\\}]%n", sep, &len) > 0 ||
     ++                           sscanf(buf, "\\be%u%1[\\}]%n", &size, sep, &len) > 1) {
     ++                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_TEXT_BLUR);
     ++                } else if (sscanf(buf, "\\q%1[\\}]%n", sep, &len) > 0 ||
     ++                           sscanf(buf, "\\q%u%1[\\}]%n", &size, sep, &len) > 1) {
     ++                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_TEXT_WRAP);
     +                 } else if (sscanf(buf, "\\a%1[\\}]%n", sep, &len) > 0 ||
     +                            sscanf(buf, "\\a%2u%1[\\}]%n", &an, sep, &len) > 1 ||
     +                            sscanf(buf, "\\an%1[\\}]%n", sep, &len) > 0 ||
     +                            sscanf(buf, "\\an%1u%1[\\}]%n", &an, sep, &len) > 1) {
     +                     if (an != -1 && buf[2] != 'n')
     +                         an = (an&3) + (an&4 ? 6 : an&8 ? 3 : 0);
     ++                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_TEXT_ALIGNMENT);
     +                     if (callbacks->alignment)
     +                         callbacks->alignment(priv, an);
     +                 } else if (sscanf(buf, "\\r%1[\\}]%n", sep, &len) > 0 ||
     +                            sscanf(buf, "\\r%127[^\\}]%1[\\}]%n", tmp, sep, &len) > 1) {
     ++                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_CANCELLING);
     +                     if (callbacks->cancel_overrides)
     +                         callbacks->cancel_overrides(priv, tmp);
     +                 } else if (sscanf(buf, "\\move(%d,%d,%d,%d)%1[\\}]%n", &x1, &y1, &x2, &y2, sep, &len) > 4 ||
     +                            sscanf(buf, "\\move(%d,%d,%d,%d,%d,%d)%1[\\}]%n", &x1, &y1, &x2, &y2, &t1, &t2, sep, &len) > 6) {
     ++                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_MOVE);
     +                     if (callbacks->move)
     +                         callbacks->move(priv, x1, y1, x2, y2, t1, t2);
     +                 } else if (sscanf(buf, "\\pos(%d,%d)%1[\\}]%n", &x1, &y1, sep, &len) > 2) {
     ++                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_POS);
     +                     if (callbacks->move)
     +                         callbacks->move(priv, x1, y1, x1, y1, -1, -1);
                       } else if (sscanf(buf, "\\org(%d,%d)%1[\\}]%n", &x1, &y1, sep, &len) > 2) {
     ++                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_ORIGIN);
                           if (callbacks->origin)
                               callbacks->origin(priv, x1, y1);
     -+                } else if (sscanf(buf, "\\t(%d,%d,%1[\\}]%n", &t1, &t2, sep, &len) > 2 ||
     ++                } else if (sscanf(buf, "\\t(%1[\\}]%n", sep, &len) > 0 ||
     ++                           sscanf(buf, "\\t(%d,%d,%1[\\}]%n", &t1, &t2, sep, &len) > 2 ||
      +                           sscanf(buf, "\\t(%d,%d,%d,%1[\\}]%n", &t1, &t2, &accel, sep, &len) > 3) {
     ++
     ++                    len = strcspn(buf, ")") + 2;
     ++
     ++                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_ANIMATE);
      +                    if (callbacks->animate)
      +                        callbacks->animate(priv, t1, t2, accel, tmp);
     ++                } else if (sscanf(buf, "\\fade(%d,%d,%d,%d,%d,%d,%d)%1[\\}]%n", &x1, &x2, &x3, &t1, &t2, &t3, &t4, sep, &len) > 7) {
     ++                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FADE);
     ++                } else if (sscanf(buf, "\\fad(%d,%d)%1[\\}]%n", &t1, &t2, sep, &len) > 2) {
     ++                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FADE);
     ++                } else if (sscanf(buf, "\\clip(%d,%d,%d,%d)%1[\\}]%n", &x1, &y1, &x2, &y2, sep, &len) > 4) {
     ++                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_CLIP);
      +                } else if (sscanf(buf, "\\p%1[\\}]%n", sep, &len) > 0 ||
      +                           sscanf(buf, "\\p%u%1[\\}]%n", &scale, sep, &len) > 1) {
     ++                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_DRAW);
      +                    if (callbacks->drawing_mode)
      +                        callbacks->drawing_mode(priv, scale);
                       } else {
     -                     len = strcspn(buf+1, "\\}") + 2;  /* skip unknown code */
     -                 }
     +-                    len = strcspn(buf+1, "\\}") + 2;  /* skip unknown code */
     +-                }
     ++                    len = strcspn(buf+1, "\\}") + 2;  /* unknown code */
     ++                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_UNKNOWN);
     ++             }
     +                 buf += len - 1;
     +             }
     +             if (*buf++ != '}')
     +                 return AVERROR_INVALIDDATA;
     +-        } else {
     ++
     ++            ass_write_filtered_line(outbuffer, "}", 2, keep_flags, ASS_SPLIT_ANY);
     ++     } else {
     +             if (!text) {
     +                 text = buf;
     +                 text_len = 1;
      @@ libavutil/ass_split.c: int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
     -     return 0;
     +             buf++;
     +         }
     +     }
     ++    if (text)
     ++        ass_write_filtered_line(outbuffer, text, text_len + 1, keep_flags, ASS_SPLIT_TEXT | ASS_SPLIT_TEXT2);
     +     if (text && callbacks->text)
     +         callbacks->text(priv, text, text_len);
     +     if (callbacks->end)
     +         callbacks->end(priv);
     +-    return 0;
     ++
     ++    if (outbuffer)
     ++        ret = ass_remove_empty_braces(outbuffer);
     ++
     ++    return ret;
     ++}
     ++
     ++
     ++int avpriv_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
     ++                                const char *buf)
     ++{
     ++    return avpriv_ass_filter_override_codes(callbacks, priv, buf, NULL, 0);
       }
       
      -ASSStyle *ff_ass_style_get(ASSSplitContext *ctx, const char *style)
     @@ libavutil/ass_split.c: int ff_ass_split_override_codes(const ASSCodesCallbacks *
           ASS *ass = &ctx->ass;
           int i;
      
     - ## libavcodec/ass_split.h => libavutil/ass_split_internal.h ##
     + ## libavutil/ass_split_internal.h (new) ##
      @@
     -  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
     -  */
     - 
     --#ifndef AVCODEC_ASS_SPLIT_H
     --#define AVCODEC_ASS_SPLIT_H
     ++/*
     ++ * SSA/ASS spliting functions
     ++ * Copyright (c) 2010  Aurelien Jacobs <aurel@gnuage.org>
     ++ *
     ++ * 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
     ++ */
     ++
      +#ifndef AVUTIL_ASS_SPLIT_INTERNAL_H
      +#define AVUTIL_ASS_SPLIT_INTERNAL_H
     - 
     - /**
     -  * fields extracted from the [Script Info] section
     -@@ libavutil/ass_split_internal.h: typedef struct {
     -     char *effect;
     -     char *text;     /**< actual text which will be displayed as a subtitle,
     -                          can include style override control codes (see
     --                         ff_ass_split_override_codes()) */
     -+                         avpriv_ass_split_override_codes()) */
     - } ASSDialog;
     - 
     - /**
     -@@ libavutil/ass_split_internal.h: typedef struct ASSSplitContext ASSSplitContext;
     -  * @param buf String containing the ASS formatted data.
     -  * @return Newly allocated struct containing split data.
     -  */
     --ASSSplitContext *ff_ass_split(const char *buf);
     ++
     ++#include "bprint.h"
     ++
     ++enum ASSSplitComponents
     ++{
     ++    ASS_SPLIT_ANY = 0,
     ++    ASS_SPLIT_TEXT           = (1 << 0),
     ++    ASS_SPLIT_TEXT2          = (1 << 1), // Same semantics as ASS_SPLIT_TEXT. To work around help output default display.
     ++    ASS_SPLIT_COLOR          = (1 << 2),
     ++    ASS_SPLIT_ALPHA          = (1 << 3),
     ++    ASS_SPLIT_FONT_NAME      = (1 << 4),
     ++    ASS_SPLIT_FONT_SIZE      = (1 << 5),
     ++    ASS_SPLIT_FONT_SCALE     = (1 << 6),
     ++    ASS_SPLIT_FONT_SPACING   = (1 << 7),
     ++    ASS_SPLIT_FONT_CHARSET   = (1 << 8),
     ++    ASS_SPLIT_FONT_BOLD      = (1 << 9),
     ++    ASS_SPLIT_FONT_ITALIC    = (1 << 10),
     ++    ASS_SPLIT_FONT_UNDERLINE = (1 << 11),
     ++    ASS_SPLIT_FONT_STRIKEOUT = (1 << 12),
     ++    ASS_SPLIT_TEXT_BORDER    = (1 << 13),
     ++    ASS_SPLIT_TEXT_SHADOW    = (1 << 14),
     ++    ASS_SPLIT_TEXT_ROTATE    = (1 << 15),
     ++    ASS_SPLIT_TEXT_BLUR      = (1 << 16),
     ++    ASS_SPLIT_TEXT_WRAP      = (1 << 17),
     ++    ASS_SPLIT_TEXT_ALIGNMENT = (1 << 18),
     ++    ASS_SPLIT_CANCELLING     = (1 << 19),
     ++    ASS_SPLIT_MOVE           = (1 << 20),
     ++    ASS_SPLIT_POS            = (1 << 21),
     ++    ASS_SPLIT_ORIGIN         = (1 << 22),
     ++    ASS_SPLIT_DRAW           = (1 << 23),
     ++    ASS_SPLIT_ANIMATE        = (1 << 24),
     ++    ASS_SPLIT_FADE           = (1 << 25),
     ++    ASS_SPLIT_CLIP           = (1 << 26),
     ++    ASS_SPLIT_UNKNOWN        = (1 << 27),
     ++
     ++    ASS_SPLIT_BASIC =  ASS_SPLIT_TEXT2 | ASS_SPLIT_COLOR | ASS_SPLIT_ALPHA | ASS_SPLIT_FONT_NAME | ASS_SPLIT_FONT_SIZE | ASS_SPLIT_FONT_SCALE | ASS_SPLIT_FONT_SPACING | ASS_SPLIT_FONT_CHARSET | ASS_SPLIT_FONT_BOLD | ASS_SPLIT_FONT_ITALIC | ASS_SPLIT_FONT_UNDERLINE | ASS_SPLIT_FONT_STRIKEOUT | ASS_SPLIT_TEXT_BORDER | ASS_SPLIT_TEXT_SHADOW | ASS_SPLIT_TEXT_WRAP | ASS_SPLIT_TEXT_ALIGNMENT | ASS_SPLIT_POS | ASS_SPLIT_CANCELLING,
     ++    ASS_SPLIT_ALL_KNOWN =  ASS_SPLIT_TEXT2 | ASS_SPLIT_COLOR | ASS_SPLIT_ALPHA | ASS_SPLIT_FONT_NAME | ASS_SPLIT_FONT_SIZE | ASS_SPLIT_FONT_SCALE | ASS_SPLIT_FONT_SPACING | ASS_SPLIT_FONT_CHARSET | ASS_SPLIT_FONT_BOLD | ASS_SPLIT_FONT_ITALIC | ASS_SPLIT_FONT_UNDERLINE | ASS_SPLIT_FONT_STRIKEOUT | ASS_SPLIT_TEXT_BORDER | ASS_SPLIT_TEXT_SHADOW | ASS_SPLIT_TEXT_ROTATE | ASS_SPLIT_TEXT_BLUR | ASS_SPLIT_TEXT_WRAP | ASS_SPLIT_TEXT_ALIGNMENT | ASS_SPLIT_CANCELLING | ASS_SPLIT_POS | ASS_SPLIT_MOVE | ASS_SPLIT_ORIGIN | ASS_SPLIT_DRAW | ASS_SPLIT_ANIMATE | ASS_SPLIT_FADE | ASS_SPLIT_CLIP,
     ++};
     ++
     ++    /**
     ++     * fields extracted from the [Script Info] section
     ++     */
     ++    typedef struct {
     ++      char *script_type; /**< SSA script format version (eg. v4.00) */
     ++  char *collisions;  /**< how subtitles are moved to prevent collisions */
     ++  int play_res_x;    /**< video width that ASS coords are referring to */
     ++  int play_res_y;    /**< video height that ASS coords are referring to */
     ++  float timer;       /**< time multiplier to apply to SSA clock (in %) */
     ++} ASSScriptInfo;
     ++
     ++/**
     ++ * fields extracted from the [V4(+) Styles] section
     ++ */
     ++typedef struct {
     ++  char *name;        /**< name of the tyle (case sensitive) */
     ++  char *font_name;   /**< font face (case sensitive) */
     ++  int font_size;     /**< font height */
     ++  int primary_color; /**< color that a subtitle will normally appear in */
     ++  int secondary_color;
     ++  int outline_color; /**< color for outline in ASS, called tertiary in SSA */
     ++  int back_color;    /**< color of the subtitle outline or shadow */
     ++  int bold;          /**< whether text is bold (1) or not (0) */
     ++  int italic;        /**< whether text is italic (1) or not (0) */
     ++  int underline;     /**< whether text is underlined (1) or not (0) */
     ++  int strikeout;
     ++  float scalex;
     ++  float scaley;
     ++  float spacing;
     ++  float angle;
     ++  int border_style;
     ++  float outline;
     ++  float shadow;
     ++  int alignment; /**< position of the text (left, center, top...),
     ++                      defined after the layout of the numpad
     ++                      (1-3 sub, 4-6 mid, 7-9 top) */
     ++  int margin_l;
     ++  int margin_r;
     ++  int margin_v;
     ++  int alpha_level;
     ++  int encoding;
     ++} ASSStyle;
     ++
     ++/**
     ++ * fields extracted from the [Events] section
     ++ */
     ++typedef struct {
     ++  int readorder;
     ++  int layer;   /**< higher numbered layers are drawn over lower numbered */
     ++  int start;   /**< start time of the dialog in centiseconds */
     ++  int end;     /**< end time of the dialog in centiseconds */
     ++  char *style; /**< name of the ASSStyle to use with this dialog */
     ++  char *name;
     ++  int margin_l;
     ++  int margin_r;
     ++  int margin_v;
     ++  char *effect;
     ++  char *text; /**< actual text which will be displayed as a subtitle,
     ++                   can include style override control codes (see
     ++                   avpriv_ass_split_override_codes()) */
     ++} ASSDialog;
     ++
     ++/**
     ++ * structure containing the whole split ASS data
     ++ */
     ++typedef struct {
     ++  ASSScriptInfo script_info; /**< general information about the SSA script*/
     ++  ASSStyle *styles;          /**< array of split out styles */
     ++  int styles_count;          /**< number of ASSStyle in the styles array */
     ++  ASSDialog *dialogs;        /**< array of split out dialogs */
     ++  int dialogs_count;         /**< number of ASSDialog in the dialogs array*/
     ++} ASS;
     ++
     ++/**
     ++ * This struct can be casted to ASS to access to the split data.
     ++ */
     ++typedef struct ASSSplitContext ASSSplitContext;
     ++
     ++/**
     ++ * Split a full ASS file or a ASS header from a string buffer and store
     ++ * the split structure in a newly allocated context.
     ++ *
     ++ * @param buf String containing the ASS formatted data.
     ++ * @return Newly allocated struct containing split data.
     ++ */
      +ASSSplitContext *avpriv_ass_split(const char *buf);
     - 
     - /**
     -- * Free a dialogue obtained from ff_ass_split_dialog().
     ++
     ++/**
      + * Free a dialogue obtained from avpriv_ass_split_dialog().
     -  */
     --void ff_ass_free_dialog(ASSDialog **dialogp);
     ++ */
      +void avpriv_ass_free_dialog(ASSDialog **dialogp);
     - 
     - /**
     -  * Split one ASS Dialogue line from a string buffer.
     -@@ libavutil/ass_split_internal.h: void ff_ass_free_dialog(ASSDialog **dialogp);
     -  * @param buf String containing the ASS "Dialogue" line.
     -  * @return Pointer to the split ASSDialog. Must be freed with ff_ass_free_dialog()
     -  */
     --ASSDialog *ff_ass_split_dialog(ASSSplitContext *ctx, const char *buf);
     ++
     ++/**
     ++ * Split one ASS Dialogue line from a string buffer.
     ++ *
     ++ * @param ctx Context previously initialized by ff_ass_split().
     ++ * @param buf String containing the ASS "Dialogue" line.
     ++ * @return Pointer to the split ASSDialog. Must be freed with
     ++ * ff_ass_free_dialog()
     ++ */
      +ASSDialog *avpriv_ass_split_dialog(ASSSplitContext *ctx, const char *buf);
     - 
     - /**
     -  * Free all the memory allocated for an ASSSplitContext.
     -  *
     -  * @param ctx Context previously initialized by ff_ass_split().
     -  */
     --void ff_ass_split_free(ASSSplitContext *ctx);
     ++
     ++/**
     ++ * Free all the memory allocated for an ASSSplitContext.
     ++ *
     ++ * @param ctx Context previously initialized by ff_ass_split().
     ++ */
      +void avpriv_ass_split_free(ASSSplitContext *ctx);
     - 
     - 
     - /**
     -@@ libavutil/ass_split_internal.h: typedef struct {
     -      * @{
     -      */
     -     void (*text)(void *priv, const char *text, int len);
     -+    void (*hard_space)(void *priv);
     -     void (*new_line)(void *priv, int forced);
     -     void (*style)(void *priv, char style, int close);
     -     void (*color)(void *priv, unsigned int /* color */, unsigned int color_id);
     -@@ libavutil/ass_split_internal.h: typedef struct {
     -      * @{
     -      */
     -     void (*move)(void *priv, int x1, int y1, int x2, int y2, int t1, int t2);
     -+    void (*animate)(void *priv, int t1, int t2, int accel, char *style);
     -     void (*origin)(void *priv, int x, int y);
     -+    void (*drawing_mode)(void *priv, int scale);
     -+    /** @} */
      +
     -+    /**
     -+     * @defgroup ass_ext    ASS extensible parsing callback
     -+     * @{
     -+     */
     -+    void (*ext)(void *priv, int ext_id, const char *text, int p1, int p2);
     -     /** @} */
     - 
     -     /**
     -@@ libavutil/ass_split_internal.h: typedef struct {
     -  * @param buf The ASS "Dialogue" Text field to split.
     -  * @return >= 0 on success otherwise an error code <0
     -  */
     --int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
     -+int avpriv_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
     -                                 const char *buf);
     - 
     - /**
     -@@ libavutil/ass_split_internal.h: int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
     -  * @param style name of the style to search for.
     -  * @return the ASSStyle corresponding to style, or NULL if style can't be found
     -  */
     --ASSStyle *ff_ass_style_get(ASSSplitContext *ctx, const char *style);
     ++/**
     ++ * Set of callback functions corresponding to each override codes that can
     ++ * be encountered in a "Dialogue" Text field.
     ++ */
     ++typedef struct {
     ++  /**
     ++   * @defgroup ass_styles    ASS styles
     ++   * @{
     ++   */
     ++  void (*text)(void *priv, const char *text, int len);
     ++  void (*hard_space)(void *priv);
     ++  void (*new_line)(void *priv, int forced);
     ++  void (*style)(void *priv, char style, int close);
     ++  void (*color)(void *priv, unsigned int /* color */, unsigned int color_id);
     ++  void (*alpha)(void *priv, int alpha, int alpha_id);
     ++  void (*font_name)(void *priv, const char *name);
     ++  void (*font_size)(void *priv, int size);
     ++  void (*alignment)(void *priv, int alignment);
     ++  void (*cancel_overrides)(void *priv, const char *style);
     ++  /** @} */
     ++
     ++  /**
     ++   * @defgroup ass_functions    ASS functions
     ++   * @{
     ++   */
     ++  void (*move)(void *priv, int x1, int y1, int x2, int y2, int t1, int t2);
     ++  void (*animate)(void *priv, int t1, int t2, int accel, char *style);
     ++  void (*origin)(void *priv, int x, int y);
     ++  void (*drawing_mode)(void *priv, int scale);
     ++  /** @} */
     ++
     ++  /**
     ++   * @defgroup ass_ext    ASS extensible parsing callback
     ++   * @{
     ++   */
     ++  void (*ext)(void *priv, int ext_id, const char *text, int p1, int p2);
     ++  /** @} */
     ++
     ++  /**
     ++   * @defgroup ass_end    end of Dialogue Event
     ++   * @{
     ++   */
     ++  void (*end)(void *priv);
     ++  /** @} */
     ++} ASSCodesCallbacks;
     ++
     ++/**
     ++ * Split override codes out of a ASS "Dialogue" Text field.
     ++ *
     ++ * @param callbacks Set of callback functions called for each override code
     ++ *                  encountered.
     ++ * @param priv Opaque pointer passed to the callback functions.
     ++ * @param buf The ASS "Dialogue" Text field to split.
     ++ * @param outbuffer The output buffer.
     ++ * @param keep_flags Flags for filtering ass codes.
     ++ * @return >= 0 on success otherwise an error code <0
     ++ */
     ++int avpriv_ass_filter_override_codes(const ASSCodesCallbacks *callbacks,
     ++                                     void *priv, const char *buf,
     ++                                     AVBPrint *outbuffer, enum ASSSplitComponents keep_flags);
     ++
     ++/**
     ++ * Split override codes out of a ASS "Dialogue" Text field.
     ++ *
     ++ * @param callbacks Set of callback functions called for each override code
     ++ *                  encountered.
     ++ * @param priv Opaque pointer passed to the callback functions.
     ++ * @param buf The ASS "Dialogue" Text field to split.
     ++ * @return >= 0 on success otherwise an error code <0
     ++ */
     ++int avpriv_ass_split_override_codes(const ASSCodesCallbacks *callbacks,
     ++                                    void *priv, const char *buf);
     ++
     ++/**
     ++ * Find an ASSStyle structure by its name.
     ++ *
     ++ * @param ctx Context previously initialized by ff_ass_split().
     ++ * @param style name of the style to search for.
     ++ * @return the ASSStyle corresponding to style, or NULL if style can't be found
     ++ */
      +ASSStyle *avpriv_ass_style_get(ASSSplitContext *ctx, const char *style);
     - 
     --#endif /* AVCODEC_ASS_SPLIT_H */
     ++
      +#endif /* AVUTIL_ASS_SPLIT_INTERNAL_H */
  7:  09d8cf7880 =  7:  98f12ad7e9 avcodec/subtitles: Replace deprecated enum values
  8:  897299bf7f =  8:  12c8a308d3 fftools/play,probe: Adjust for subtitle changes
  9:  ca580c6d21 =  9:  2e55dbe180 avfilter/subtitles: Add subtitles.c for subtitle frame allocation
 10:  0781e974a2 = 10:  c931041103 avfilter/avfilter: Handle subtitle frames
 11:  d9d9f42558 = 11:  36cab55ff2 avfilter/avfilter: Fix hardcoded input index
 12:  af69a4b321 = 12:  f41070479c avfilter/sbuffer: Add sbuffersrc and sbuffersink filters
 13:  f7e5b590a2 ! 13:  9bfaba4ace avfilter/overlaygraphicsubs: Add overlaygraphicsubs and graphicsub2video filters
     @@ libavfilter/allfilters.c: extern const AVFilter ff_vf_overlay_qsv;
       extern const AVFilter ff_vf_owdenoise;
       extern const AVFilter ff_vf_pad;
       extern const AVFilter ff_vf_pad_opencl;
     -@@ libavfilter/allfilters.c: extern const AVFilter ff_avf_showvolume;
     +@@ libavfilter/allfilters.c: extern const AVFilter ff_avf_showspectrumpic;
     + 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_svf_graphicsub2video;
     + extern const AVFilter ff_vaf_spectrumsynth;
       
       /* multimedia sources */
     - extern const AVFilter ff_avsrc_avsynctest;
      
       ## libavfilter/vf_overlaygraphicsubs.c (new) ##
      @@
 14:  4c8092357f ! 14:  918fd9aaf5 avfilter/overlaytextsubs: Add overlaytextsubs and textsubs2video filters
     @@ libavfilter/allfilters.c: extern const AVFilter ff_vf_overlay_vaapi;
       extern const AVFilter ff_vf_owdenoise;
       extern const AVFilter ff_vf_pad;
       extern const AVFilter ff_vf_pad_opencl;
     -@@ libavfilter/allfilters.c: extern const AVFilter ff_avf_showwaves;
     +@@ libavfilter/allfilters.c: 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_svf_graphicsub2video;
      +extern const AVFilter ff_svf_textsub2video;
     + extern const AVFilter ff_vaf_spectrumsynth;
       
       /* multimedia sources */
     - extern const AVFilter ff_avsrc_avsynctest;
      
       ## libavfilter/vf_overlaytextsubs.c (new) ##
      @@
 15:  8fdbdf7c5f ! 15:  a361ad35c5 avfilter/textmod: Add textmod, censor and show_speaker filters
     @@ libavfilter/Makefile: OBJS-$(CONFIG_YUVTESTSRC_FILTER)             += vsrc_tests
       OBJS-$(CONFIG_ADRAWGRAPH_FILTER)             += f_drawgraph.o
      
       ## libavfilter/allfilters.c ##
     -@@ libavfilter/allfilters.c: extern const AVFilter ff_avf_showvolume;
     - extern const AVFilter ff_avf_showwaves;
     - extern const AVFilter ff_avf_showwavespic;
     - extern const AVFilter ff_vaf_spectrumsynth;
     +@@ libavfilter/allfilters.c: extern const AVFilter ff_avsrc_avsynctest;
     + extern const AVFilter ff_avsrc_amovie;
     + extern const AVFilter ff_avsrc_movie;
     + 
     ++/* subtitle filters */
      +extern const AVFilter ff_sf_censor;
      +extern const AVFilter ff_sf_showspeaker;
      +extern const AVFilter ff_sf_textmod;
     - extern const AVFilter ff_svf_graphicsub2video;
     - extern const AVFilter ff_svf_textsub2video;
     - 
     ++
     + /* those filters are part of public or internal API,
     +  * they are formatted to not be found by the grep
     +  * as they are manually added again (due to their 'names'
      
       ## libavfilter/sf_textmod.c (new) ##
      @@
     @@ libavfilter/sf_textmod.c (new)
      +
      +    av_bprint_finalize(&pbuf, &text);
      +
     -+    result = avpriv_ass_get_dialog_ex(dialog->readorder, dialog->layer, dialog->style, dialog->name, dialog->margin_l, dialog->margin_r, dialog->margin_v, text);
     ++    result = avpriv_ass_get_dialog_ex(dialog->readorder, dialog->layer, dialog->style, dialog->name, dialog->margin_l, dialog->margin_r, dialog->margin_v, dialog->effect, text);
      +
      +    av_free(text);
      +    avpriv_ass_free_dialog(&dialog);
     @@ libavfilter/sf_textmod.c (new)
      +    if (!text)
      +        return NULL;
      +
     -+    result = avpriv_ass_get_dialog_ex(dialog->readorder, dialog->layer, dialog->style, dialog->name, dialog->margin_l, dialog->margin_r, dialog->margin_v, text);
     ++    result = avpriv_ass_get_dialog_ex(dialog->readorder, dialog->layer, dialog->style, dialog->name, dialog->margin_l, dialog->margin_r, dialog->margin_v, dialog->effect, text);
      +
      +    av_free(text);
      +    avpriv_ass_free_dialog(&dialog);
 16:  d44b22f15b ! 16:  bca90ebc3e avfilter/stripstyles: Add stripstyles filter
     @@ doc/filters.texi: ffmpeg -i "http://streams.videolan.org/samples/sub/SSA/subtitl
      
       ## libavfilter/Makefile ##
      @@ libavfilter/Makefile: OBJS-$(CONFIG_NULLSINK_FILTER)               += vsink_nullsink.o
     + # subtitle filters
       OBJS-$(CONFIG_CENSOR_FILTER)                 += sf_textmod.o
       OBJS-$(CONFIG_SHOW_SPEAKER_FILTER)           += sf_textmod.o
     - OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
      +OBJS-$(CONFIG_STRIPSTYLES_FILTER)            += sf_stripstyles.o
     + OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
       
       # multimedia filters
     - OBJS-$(CONFIG_ABITSCOPE_FILTER)              += avf_abitscope.o
      
       ## libavfilter/allfilters.c ##
     -@@ libavfilter/allfilters.c: extern const AVFilter ff_avf_showwavespic;
     - extern const AVFilter ff_vaf_spectrumsynth;
     +@@ libavfilter/allfilters.c: extern const AVFilter ff_avsrc_movie;
     + /* subtitle filters */
       extern const AVFilter ff_sf_censor;
       extern const AVFilter ff_sf_showspeaker;
      +extern const AVFilter ff_sf_stripstyles;
       extern const AVFilter ff_sf_textmod;
     - extern const AVFilter ff_svf_graphicsub2video;
     - extern const AVFilter ff_svf_textsub2video;
     + 
     + /* those filters are part of public or internal API,
      
       ## libavfilter/sf_stripstyles.c (new) ##
      @@
     @@ libavfilter/sf_stripstyles.c (new)
      +
      +#include "libavutil/opt.h"
      +#include "internal.h"
     ++#include "libavutil/ass_internal.h"
      +#include "libavutil/ass_split_internal.h"
      +#include "libavutil/bprint.h"
      +
     @@ libavfilter/sf_stripstyles.c (new)
      +    const AVClass *class;
      +    enum AVSubtitleType format;
      +    int remove_animated;
     ++    enum ASSSplitComponents keep_flags;
      +    int select_layer;
      +} StripStylesContext;
      +
     @@ libavfilter/sf_stripstyles.c (new)
      +    AVBPrint buffer;
      +    int drawing_scale;
      +    int is_animated;
     ++    int plain_text_length;
      +} DialogContext;
      +
      +static void dialog_text_cb(void *priv, const char *text, int len)
     @@ libavfilter/sf_stripstyles.c (new)
      +    av_log(s->ss_ctx, AV_LOG_DEBUG, "dialog_text_cb: %s\n", text);
      +
      +    if (!s->drawing_scale && (!s->is_animated || !s->ss_ctx->remove_animated))
     -+        av_bprint_append_data(&s->buffer, text, len);
     ++        s->plain_text_length += len;
     ++        ////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);
     ++        s->plain_text_length += 2;
     ++        ////av_bprint_append_data(&s->buffer, forced ? "\\N" : "\\n", 2);
      +}
      +
      +static void dialog_drawing_mode_cb(void *priv, int scale)
     @@ libavfilter/sf_stripstyles.c (new)
      +    .move             = dialog_move_cb,
      +};
      +
     -+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)
      +{
      +    DialogContext dlg_ctx = { .ss_ctx = s };
     @@ libavfilter/sf_stripstyles.c (new)
      +
      +    dlg_ctx.ss_ctx = s;
      +
     -+    av_bprint_init(&dlg_ctx.buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
     ++    av_bprint_init(&dlg_ctx.buffer, 512, AV_BPRINT_SIZE_UNLIMITED);
      +
     -+    avpriv_ass_split_override_codes(&dialog_callbacks, &dlg_ctx, dialog->text);
     ++    avpriv_ass_filter_override_codes(&dialog_callbacks, &dlg_ctx, dialog->text, &dlg_ctx.buffer, s->keep_flags);
      +
     -+    if (av_bprint_is_complete(&dlg_ctx.buffer)
     -+        && dlg_ctx.buffer.len > 0)
     -+        result = ass_get_line(dialog->readorder, dialog->layer, dialog->style, dialog->name, dialog->effect, dlg_ctx.buffer.str);
     ++    if (av_bprint_is_complete(&dlg_ctx.buffer) && dlg_ctx.buffer.len > 0 && dlg_ctx.plain_text_length > 0)
     ++        result = avpriv_ass_get_dialog_ex(dialog->readorder, dialog->layer, dialog->style, dialog->name, dialog->margin_l, dialog->margin_r, dialog->margin_v, dialog->effect, dlg_ctx.buffer.str);
      +
      +    av_bprint_finalize(&dlg_ctx.buffer, NULL);
      +    avpriv_ass_free_dialog(&dialog);
     @@ libavfilter/sf_stripstyles.c (new)
      +            area->ass = process_dialog(s, area->ass);
      +
      +            if (area->ass) {
     -+                av_log(inlink->dst, AV_LOG_INFO, "original: %d %s\n", i, tmp);
     -+                av_log(inlink->dst, AV_LOG_INFO, "stripped: %d %s\n", i, area->ass);
     ++                av_log(inlink->dst, AV_LOG_DEBUG, "original: %d %s\n", i, tmp);
     ++                av_log(inlink->dst, AV_LOG_DEBUG, "stripped: %d %s\n", i, area->ass);
      +            }
      +            else
      +                area->ass = NULL;
     @@ libavfilter/sf_stripstyles.c (new)
      +#define FLAGS (AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
      +
      +static const AVOption stripstyles_options[] = {
     ++    { "keep_flags", "flags to control which override codes to keep", OFFSET(keep_flags), AV_OPT_TYPE_FLAGS, { .i64 = ASS_SPLIT_TEXT }, .flags = FLAGS, .unit = "keepflags" },
     ++        { "basic",          "keep static style tags only",             .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_BASIC          },  .flags=FLAGS, .unit = "keepflags" },
     ++        { "all_known",      "keep all known tags",                     .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_ALL_KNOWN      },  .flags=FLAGS, .unit = "keepflags" },
     ++        { "text",           "keep text content",                       .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_TEXT           },  .flags=FLAGS, .unit = "keepflags" },
     ++        { "color",          "keep color tags (\\c, \\<n>c)",           .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_COLOR          },  .flags=FLAGS, .unit = "keepflags" },
     ++        { "alpha",          "keep color alpha tags (\\alpha, \\<n>a)", .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_ALPHA          },  .flags=FLAGS, .unit = "keepflags" },
     ++        { "font_name",      "keep font name tags (\\fn)",              .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_NAME      },  .flags=FLAGS, .unit = "keepflags" },
     ++        { "font_size",      "keep font size tags (\\fs)",              .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_SIZE      },  .flags=FLAGS, .unit = "keepflags" },
     ++        { "font_scale",     "keep font scale tags (\\fscx, \\fscy)",   .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_SCALE     },  .flags=FLAGS, .unit = "keepflags" },
     ++        { "font_spacing",   "keep font spacing tags (\\fsp)",          .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_SPACING   },  .flags=FLAGS, .unit = "keepflags" },
     ++        { "font_charset",   "keep font charset tags (\\fe)",           .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_CHARSET   },  .flags=FLAGS, .unit = "keepflags" },
     ++        { "font_bold",      "keep font bold tags (\\b)",               .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_BOLD      },  .flags=FLAGS, .unit = "keepflags" },
     ++        { "font_italic",    "keep font italic tags (\\i)",             .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_ITALIC    },  .flags=FLAGS, .unit = "keepflags" },
     ++        { "font_underline", "keep font underline tags (\\u)",          .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_UNDERLINE },  .flags=FLAGS, .unit = "keepflags" },
     ++        { "font_strikeout", "keep font strikeout tags (\\s)",          .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_STRIKEOUT },  .flags=FLAGS, .unit = "keepflags" },
     ++        { "text_border",    "keep text border tags (\\bord)",          .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_TEXT_BORDER    },  .flags=FLAGS, .unit = "keepflags" },
     ++        { "text_shadow",    "keep text shadow tags (\\shad)",          .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_TEXT_SHADOW    },  .flags=FLAGS, .unit = "keepflags" },
     ++        { "text_rotate",    "keep text rotate tags (\\fr)",            .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_TEXT_ROTATE    },  .flags=FLAGS, .unit = "keepflags" },
     ++        { "text_blur",      "keep text blur tags (\\blur, \\be)",      .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_TEXT_BLUR      },  .flags=FLAGS, .unit = "keepflags" },
     ++        { "text_wrap",      "keep text wrap tags (\\q)",               .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_TEXT_WRAP      },  .flags=FLAGS, .unit = "keepflags" },
     ++        { "text_align",     "keep text align tags (\\a, \\an)",        .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_TEXT_ALIGNMENT },  .flags=FLAGS, .unit = "keepflags" },
     ++        { "reset_override", "keep override reset tags (\\r)",          .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_CANCELLING     },  .flags=FLAGS, .unit = "keepflags" },
     ++        { "move",           "keep move tags (\\move)",                 .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_MOVE           },  .flags=FLAGS, .unit = "keepflags" },
     ++        { "pos",            "keep position tags (\\pos)",              .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_POS            },  .flags=FLAGS, .unit = "keepflags" },
     ++        { "origin",         "keep origin tags (\\org)",                .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_ORIGIN         },  .flags=FLAGS, .unit = "keepflags" },
     ++        { "draw",           "keep drawing tags (\\p)",                 .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_DRAW           },  .flags=FLAGS, .unit = "keepflags" },
     ++        { "animate",        "keep animation tags (\\t)",               .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_ANIMATE        },  .flags=FLAGS, .unit = "keepflags" },
     ++        { "fade",           "keep fade tags (\\fad, \\fade)",          .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FADE           },  .flags=FLAGS, .unit = "keepflags" },
     ++        { "clip",           "keep clip tags (\\clip)",                 .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_CLIP           },  .flags=FLAGS, .unit = "keepflags" },
     ++        { "unknown",        "keep unknown tags",                       .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_UNKNOWN        },  .flags=FLAGS, .unit = "keepflags" },
      +    { "remove_animated", "remove animated text (default: yes)",   OFFSET(remove_animated),  AV_OPT_TYPE_BOOL, {.i64 = 1 },  0, 1, FLAGS, 0 },
     -+    { "select_layer", "process a specific ass layer only",   OFFSET(remove_animated),  AV_OPT_TYPE_INT, {.i64 = -1 }, -1, INT_MAX, FLAGS, 0 },
     ++    { "select_layer", "process a specific ass layer only",   OFFSET(select_layer),  AV_OPT_TYPE_INT, {.i64 = -1 }, -1, INT_MAX, FLAGS, 0 },
      +    { NULL },
      +};
      +
 17:  28d75dc982 ! 17:  6e488e495f avfilter/splitcc: Add splitcc filter for closed caption handling
     @@ doc/filters.texi: ffmpeg -i INPUT -filter_complex "showspeaker=format=colon:styl
      
       ## libavfilter/Makefile ##
      @@ libavfilter/Makefile: OBJS-$(CONFIG_NULLSINK_FILTER)               += vsink_nullsink.o
     + # subtitle filters
       OBJS-$(CONFIG_CENSOR_FILTER)                 += sf_textmod.o
       OBJS-$(CONFIG_SHOW_SPEAKER_FILTER)           += sf_textmod.o
     - OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
      +OBJS-$(CONFIG_SPLITCC_FILTER)                += sf_splitcc.o
       OBJS-$(CONFIG_STRIPSTYLES_FILTER)            += sf_stripstyles.o
     + OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
       
     - # multimedia filters
      
       ## libavfilter/allfilters.c ##
     -@@ libavfilter/allfilters.c: extern const AVFilter ff_avf_showwavespic;
     - extern const AVFilter ff_vaf_spectrumsynth;
     +@@ libavfilter/allfilters.c: extern const AVFilter ff_avsrc_movie;
     + /* subtitle filters */
       extern const AVFilter ff_sf_censor;
       extern const AVFilter ff_sf_showspeaker;
      +extern const AVFilter ff_sf_splitcc;
       extern const AVFilter ff_sf_stripstyles;
       extern const AVFilter ff_sf_textmod;
     - extern const AVFilter ff_svf_graphicsub2video;
     + 
      
       ## libavfilter/sf_splitcc.c (new) ##
      @@
 18:  42d1d1c819 ! 18:  1057dff7da avfilter/graphicsub2text: Add new graphicsub2text filter (OCR)
     @@ libavfilter/Makefile: OBJS-$(CONFIG_GBLUR_FILTER)                  += vf_gblur.o
       OBJS-$(CONFIG_GRAYWORLD_FILTER)              += vf_grayworld.o
      
       ## libavfilter/allfilters.c ##
     -@@ libavfilter/allfilters.c: extern const AVFilter ff_avf_showwaves;
     - extern const AVFilter ff_avf_showwavespic;
     - extern const AVFilter ff_vaf_spectrumsynth;
     +@@ libavfilter/allfilters.c: extern const AVFilter ff_avsrc_movie;
     + 
     + /* subtitle filters */
       extern const AVFilter ff_sf_censor;
      +extern const AVFilter ff_sf_graphicsub2text;
       extern const AVFilter ff_sf_showspeaker;
     @@ libavfilter/sf_graphicsub2text.c (new)
      +                }
      +            }
      +
     -+            if (pointsize != cur_pointsize && s->recognize & RFLAGS_FONTSIZE) {
     -+                av_log(s, AV_LOG_DEBUG, "pointsize - pointsize: %d\n", pointsize);
     -+                in_code = print_code(&s->buffer, in_code, "\\fs%d", (int)(pointsize * font_factor));
     -+                cur_pointsize = pointsize;
     ++            if (pointsize > 0 && pointsize != cur_pointsize && s->recognize & RFLAGS_FONTSIZE) {
     ++                float change_factor = (float)(FFABS(pointsize - cur_pointsize)) / FFMAX(pointsize, cur_pointsize);
     ++
     ++                // Avoid small changes due to recognition variance
     ++                if (change_factor > 0.12f) {
     ++                    av_log(s, AV_LOG_DEBUG, "pointsize - pointsize: %d\n", pointsize);
     ++                    in_code = print_code(&s->buffer, in_code, "\\fs%d", (int)(pointsize * font_factor));
     ++                    cur_pointsize = pointsize;
     ++                }
      +            }
      +
      +            if (is_italic && !cur_is_italic && s->recognize & RFLAGS_FITALIC)
     @@ libavfilter/sf_graphicsub2text.c (new)
      +
      +            const int layer = s->recognize ? i : 0;
      +            char *tmp = area->ass;
     -+            area->ass = avpriv_ass_get_dialog_ex(s->readorder_counter++, layer, "Default", NULL, 0, 0, margin_v, tmp);
     ++            area->ass = avpriv_ass_get_dialog_ex(s->readorder_counter++, layer, "Default", NULL, 0, 0, margin_v, NULL, tmp);
      +            av_free(tmp);
      +        }
      +    }
 19:  7095e8aa26 ! 19:  4e85fb5d2f avfilter/subscale: Add filter for scaling and/or re-arranging graphical subtitles
     @@ doc/filters.texi: Set the rendering margin in pixels.
       @chapter Multimedia Filters
      
       ## libavfilter/Makefile ##
     -@@ libavfilter/Makefile: OBJS-$(CONFIG_SHOW_SPEAKER_FILTER)           += sf_textmod.o
     - OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
     +@@ libavfilter/Makefile: OBJS-$(CONFIG_CENSOR_FILTER)                 += sf_textmod.o
     + OBJS-$(CONFIG_SHOW_SPEAKER_FILTER)           += sf_textmod.o
       OBJS-$(CONFIG_SPLITCC_FILTER)                += sf_splitcc.o
       OBJS-$(CONFIG_STRIPSTYLES_FILTER)            += sf_stripstyles.o
      +OBJS-$(CONFIG_SUBSCALE_FILTER)               += sf_subscale.o
     + OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
       
       # multimedia filters
     - OBJS-$(CONFIG_ABITSCOPE_FILTER)              += avf_abitscope.o
      
       ## libavfilter/allfilters.c ##
      @@ libavfilter/allfilters.c: extern const AVFilter ff_sf_graphicsub2text;
     @@ libavfilter/allfilters.c: extern const AVFilter ff_sf_graphicsub2text;
       extern const AVFilter ff_sf_stripstyles;
      +extern const AVFilter ff_sf_subscale;
       extern const AVFilter ff_sf_textmod;
     - extern const AVFilter ff_svf_graphicsub2video;
     - extern const AVFilter ff_svf_textsub2video;
     + 
     + /* those filters are part of public or internal API,
      
       ## libavfilter/sf_subscale.c (new) ##
      @@
 20:  697939451e ! 20:  88e8adb889 avfilter/subfeed: add subtitle feed filter
     @@ Commit message
          Signed-off-by: softworkz <softworkz@hotmail.com>
      
       ## libavfilter/Makefile ##
     -@@ libavfilter/Makefile: OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
     +@@ libavfilter/Makefile: OBJS-$(CONFIG_CENSOR_FILTER)                 += sf_textmod.o
     + OBJS-$(CONFIG_SHOW_SPEAKER_FILTER)           += sf_textmod.o
       OBJS-$(CONFIG_SPLITCC_FILTER)                += sf_splitcc.o
       OBJS-$(CONFIG_STRIPSTYLES_FILTER)            += sf_stripstyles.o
     - OBJS-$(CONFIG_SUBSCALE_FILTER)               += sf_subscale.o
      +OBJS-$(CONFIG_SUBFEED_FILTER)                += sf_subfeed.o
     + OBJS-$(CONFIG_SUBSCALE_FILTER)               += sf_subscale.o
     + OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
       
     - # multimedia filters
     - OBJS-$(CONFIG_ABITSCOPE_FILTER)              += avf_abitscope.o
      
       ## libavfilter/allfilters.c ##
     -@@ libavfilter/allfilters.c: extern const AVFilter ff_sf_showspeaker;
     +@@ libavfilter/allfilters.c: extern const AVFilter ff_sf_graphicsub2text;
     + extern const AVFilter ff_sf_showspeaker;
       extern const AVFilter ff_sf_splitcc;
       extern const AVFilter ff_sf_stripstyles;
     - extern const AVFilter ff_sf_subscale;
      +extern const AVFilter ff_sf_subfeed;
     + extern const AVFilter ff_sf_subscale;
       extern const AVFilter ff_sf_textmod;
     - extern const AVFilter ff_svf_graphicsub2video;
     - extern const AVFilter ff_svf_textsub2video;
     + 
      
       ## libavfilter/sf_subfeed.c (new) ##
      @@
     @@ libavfilter/sf_subfeed.c (new)
      +        if (pts_diff <= 0) {
      +            av_log(ctx, AV_LOG_WARNING, "The pts_diff to the previous frame (index #%"PRId64")  is <= 0: %"PRId64" ms. The previous frame duration is %"PRId64" ms.\n",
      +                index, avtb_to_ms(pts_diff),  avtb_to_ms(previous_frame->subtitle_timing.duration));
     ++
     ++            if (s->fix_overlap) {
     ++                av_log(ctx, AV_LOG_VERBOSE, "Removing previous frame\n");
     ++                previous_frame = ff_framequeue_take(&s->fifo);
     ++                while (nb_queued_frames > 1) {
     ++                    ff_framequeue_add(&s->fifo, previous_frame);
     ++                    previous_frame = ff_framequeue_take(&s->fifo);
     ++                    nb_queued_frames--;
     ++                }
     ++            }
      +        }
      +    }
      +
  -:  ---------- > 21:  a96bb5c788 avfilter/text2graphicsub: Added text2graphicsub subtitle filter
  -:  ---------- > 22:  c4922f8466 avfilter/snull,strim: Add snull and strim filters
 21:  32e9af0806 = 23:  848f84d5dc avcodec/subtitles: Migrate subtitle encoders to frame-based API
 22:  fa0b5c2077 ! 24:  2645a1a842 fftools/ffmpeg: Introduce subtitle filtering and new frame-based subtitle encoding
     @@ Commit message
            Overlay results have slightly different CRCs due to different
            blending implementation
      
     +    - sub-scc
     +      The first entry is no longer in the output because it is before
     +      the actual start time and the strim filter removes such entries
     +      now (like for video and audio)
     +
          Signed-off-by: softworkz <softworkz@hotmail.com>
      
       ## fftools/ffmpeg.c ##
     @@ fftools/ffmpeg.c: static int init_output_stream(OutputStream *ost, AVFrame *fram
                       return AVERROR_INVALIDDATA;
                   }
               }
     +@@ fftools/ffmpeg.c: static int transcode_init(void)
     +     for (i = 0; i < nb_output_streams; i++) {
     +         if (!output_streams[i]->stream_copy &&
     +             (output_streams[i]->enc_ctx->codec_type == AVMEDIA_TYPE_VIDEO ||
     +-             output_streams[i]->enc_ctx->codec_type == AVMEDIA_TYPE_AUDIO))
     ++             output_streams[i]->enc_ctx->codec_type == AVMEDIA_TYPE_AUDIO ||
     ++             output_streams[i]->enc_ctx->codec_type == AVMEDIA_TYPE_SUBTITLE))
     +             continue;
     + 
     +         ret = init_output_stream_wrapper(output_streams[i], NULL, 0);
      @@ fftools/ffmpeg.c: static OutputStream *choose_output(void)
                              av_rescale_q(ost->last_mux_dts, ost->st->time_base,
                                           AV_TIME_BASE_Q);
     @@ fftools/ffmpeg_filter.c: static void init_input_filter(FilterGraph *fg, AVFilter
                       continue;
                   if (check_stream_specifier(s, s->streams[i], *p == ':' ? p + 1 : p) == 1) {
                       st = s->streams[i];
     +@@ fftools/ffmpeg_filter.c: static int insert_trim(int64_t start_time, int64_t duration,
     +     const char *name = (type == AVMEDIA_TYPE_VIDEO) ? "trim" : "atrim";
     +     int ret = 0;
     + 
     ++    switch (type) {
     ++    case AVMEDIA_TYPE_VIDEO:
     ++        name = "trim";
     ++        break;
     ++    case AVMEDIA_TYPE_AUDIO:
     ++        name = "atrim";
     ++        break;
     ++    case AVMEDIA_TYPE_SUBTITLE:
     ++        name = "strim";
     ++        break;
     ++    default:
     ++        av_log(NULL, AV_LOG_ERROR, "insert_trim: Invalid media type: %d\n", type);
     ++        return AVERROR_INVALIDDATA;
     ++    }
     ++
     +     if (duration == INT64_MAX && start_time == AV_NOPTS_VALUE)
     +         return 0;
     + 
      @@ fftools/ffmpeg_filter.c: static int insert_filter(AVFilterContext **last_filter, int *pad_idx,
           return 0;
       }
     @@ fftools/ffmpeg_filter.c: static int insert_filter(AVFilterContext **last_filter,
      +static int configure_output_subtitle_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out)
      +{
      +    OutputStream *ost = ofilter->ost;
     ++    OutputFile    *of = output_files[ost->file_index];
      +    AVFilterContext *last_filter = out->filter_ctx;
      +    int pad_idx = out->pad_idx;
      +    int ret;
     @@ fftools/ffmpeg_filter.c: static int insert_filter(AVFilterContext **last_filter,
      +        return ret;
      +    }
      +
     -+    ////snprintf(name, sizeof(name), "trim_out_%d_%d",
     -+    ////         ost->file_index, ost->index);
     -+    ////ret = insert_trim(of->start_time, of->recording_time,
     -+    ////                  &last_filter, &pad_idx, name);
     -+    ////if (ret < 0)
     -+    ////    return ret;
     ++    snprintf(name, sizeof(name), "trim_out_%d_%d",
     ++             ost->file_index, ost->index);
     ++    ret = insert_trim(of->start_time, of->recording_time,
     ++                      &last_filter, &pad_idx, name);
     ++    if (ret < 0)
     ++        return ret;
      +
      +    ////ost->st->codecpar->codec_tag = MKTAG('a', 's', 's', 's');
      +
     @@ fftools/ffmpeg_filter.c: void check_filter_outputs(void)
      +    AVFilterContext *last_filter;
      +    const AVFilter *buffer_filt = avfilter_get_by_name("sbuffer");
      +    InputStream *ist = ifilter->ist;
     ++    InputFile     *f = input_files[ist->file_index];
      +    AVBPrint args;
      +    char name[255];
      +    int ret, pad_idx = 0;
      +    int w, h;
     ++    int64_t tsoffset = 0;
      +    AVBufferSrcParameters *par = av_buffersrc_parameters_alloc();
      +    enum AVMediaType media_type;
      +
     @@ fftools/ffmpeg_filter.c: void check_filter_outputs(void)
      -            if (avf->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
      -                w = FFMAX(w, avf->streams[i]->codecpar->width);
      -                h = FFMAX(h, avf->streams[i]->codecpar->height);
     --            }
     --        }
     --        if (!(w && h)) {
     --            w = FFMAX(w, 720);
     --            h = FFMAX(h, 576);
     --        }
     --        av_log(avf, AV_LOG_INFO, "sub2video: using %dx%d canvas\n", w, h);
      +        w = ist->dec_ctx->width;
      +        h = ist->dec_ctx->height;
      +    }
     @@ fftools/ffmpeg_filter.c: void check_filter_outputs(void)
      +        w = ass->script_info.play_res_x;
      +        h = ass->script_info.play_res_y;
      +        avpriv_ass_split_free(ass_ctx);
     -     }
     --    ist->sub2video.w = ifilter->width  = w;
     --    ist->sub2video.h = ifilter->height = h;
     - 
     --    ifilter->width  = ist->dec_ctx->width  ? ist->dec_ctx->width  : ist->sub2video.w;
     --    ifilter->height = ist->dec_ctx->height ? ist->dec_ctx->height : ist->sub2video.h;
     ++    }
     ++
      +    ist->subtitle_kickoff.w = w;
      +    ist->subtitle_kickoff.h = h;
      +    av_log(ifilter, AV_LOG_INFO, "subtitle input filter: decoding size %dx%d\n", ist->subtitle_kickoff.w, ist->subtitle_kickoff.h);
     - 
     --    /* rectangles are AV_PIX_FMT_PAL8, but we have no guarantee that the
     --       palettes for all rectangles are identical or compatible */
     --    ifilter->format = AV_PIX_FMT_RGB32;
     ++
      +    ifilter->width = w;
      +    ifilter->height = h;
      +    ist->dec_ctx->width = w;
      +    ist->dec_ctx->height = h;
     - 
     --    ist->sub2video.frame = av_frame_alloc();
     --    if (!ist->sub2video.frame)
     --        return AVERROR(ENOMEM);
     --    ist->sub2video.last_pts = INT64_MIN;
     --    ist->sub2video.end_pts  = INT64_MIN;
     ++
      +    ist->subtitle_kickoff.last_pts = INT64_MIN;
      +
      +    snprintf(name, sizeof(name), "graph %d subtitle input from stream %d:%d", fg->index,
     @@ fftools/ffmpeg_filter.c: void check_filter_outputs(void)
      +    if ((ret = avfilter_graph_create_filter(&ifilter->filter, buffer_filt, name,
      +                                            args.str, NULL, fg->graph)) < 0)
      +        goto fail;
     - 
     --    /* sub2video structure has been (re-)initialized.
     --       Mark it as such so that the system will be
     --       initialized with the first received heartbeat. */
     --    ist->sub2video.initialize = 1;
     ++
      +    par->hw_frames_ctx = ifilter->hw_frames_ctx;
      +    par->format = ifilter->format;
      +    par->width = ifilter->width;
     @@ fftools/ffmpeg_filter.c: void check_filter_outputs(void)
      +                    subscale_h = input->height;
      +                    break;
      +                }
     -+            }
     -+        }
     -+
     +             }
     +         }
     +-        if (!(w && h)) {
     +-            w = FFMAX(w, 720);
     +-            h = FFMAX(h, 576);
     +-        }
     +-        av_log(avf, AV_LOG_INFO, "sub2video: using %dx%d canvas\n", w, h);
     +-    }
     +-    ist->sub2video.w = ifilter->width  = w;
     +-    ist->sub2video.h = ifilter->height = h;
     + 
     +-    ifilter->width  = ist->dec_ctx->width  ? ist->dec_ctx->width  : ist->sub2video.w;
     +-    ifilter->height = ist->dec_ctx->height ? ist->dec_ctx->height : ist->sub2video.h;
      +        if (subscale_w && subscale_h) {
      +            char subscale_params[64];
      +            snprintf(subscale_params, sizeof(subscale_params), "w=%d:h=%d", subscale_w, subscale_h);
     @@ fftools/ffmpeg_filter.c: void check_filter_outputs(void)
      +            if (ret < 0)
      +                return ret;
      +        }
     -+
     + 
     +-    /* rectangles are AV_PIX_FMT_PAL8, but we have no guarantee that the
     +-       palettes for all rectangles are identical or compatible */
     +-    ifilter->format = AV_PIX_FMT_RGB32;
      +        av_log(NULL, AV_LOG_INFO, "Auto-inserting graphicsub2video filter\n");
      +        ret = insert_filter(&last_filter, &pad_idx, "graphicsub2video", NULL);
      +        if (ret < 0)
      +            return ret;
      +    }
     -+
     + 
     +-    ist->sub2video.frame = av_frame_alloc();
     +-    if (!ist->sub2video.frame)
     +-        return AVERROR(ENOMEM);
     +-    ist->sub2video.last_pts = INT64_MIN;
     +-    ist->sub2video.end_pts  = INT64_MIN;
     ++    if (copy_ts) {
     ++        tsoffset = f->start_time == AV_NOPTS_VALUE ? 0 : f->start_time;
     ++        if (!start_at_zero && f->ctx->start_time != AV_NOPTS_VALUE)
     ++            tsoffset += f->ctx->start_time;
     ++    }
     ++    ret = insert_trim(((f->start_time == AV_NOPTS_VALUE) || !f->accurate_seek) ?
     ++                      AV_NOPTS_VALUE : tsoffset, f->recording_time,
     ++                      &last_filter, &pad_idx, name);
     ++    if (ret < 0)
     ++        return ret;
     + 
     +-    /* sub2video structure has been (re-)initialized.
     +-       Mark it as such so that the system will be
     +-       initialized with the first received heartbeat. */
     +-    ist->sub2video.initialize = 1;
      +    if ((ret = avfilter_link(last_filter, 0, in->filter_ctx, in->pad_idx)) < 0)
      +        return ret;
       
     @@ fftools/ffmpeg_filter.c: static int configure_input_video_filter(FilterGraph *fg
           int64_t tsoffset = 0;
      -    AVBufferSrcParameters *par = av_buffersrc_parameters_alloc();
      +    AVBufferSrcParameters *par;
     - 
     ++
      +    if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_SUBTITLE) {
      +        // Automatically insert conversion filter to retain compatibility
      +        // with sub2video command lines
      +        return configure_input_subtitle_filter(fg, ifilter, in);
      +    }
     -+
     + 
      +    par = av_buffersrc_parameters_alloc();
           if (!par)
               return AVERROR(ENOMEM);
     @@ fftools/ffmpeg_opt.c: static void add_input_streams(OptionsContext *o, AVFormatC
                   break;
               }
               case AVMEDIA_TYPE_ATTACHMENT:
     +@@ fftools/ffmpeg_opt.c: static char *get_ost_filters(OptionsContext *o, AVFormatContext *oc,
     + 
     +     if (ost->filters_script)
     +         return read_file(ost->filters_script);
     +-    else if (ost->filters)
     ++    if (ost->filters)
     +         return av_strdup(ost->filters);
     + 
     +-    return av_strdup(st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ?
     +-                     "null" : "anull");
     ++    switch (st->codecpar->codec_type) {
     ++    case AVMEDIA_TYPE_VIDEO: return av_strdup("null");
     ++    case AVMEDIA_TYPE_AUDIO: return av_strdup("anull");
     ++    case AVMEDIA_TYPE_SUBTITLE: return av_strdup("snull");
     ++    default: av_assert0(0); return NULL;
     ++    }
     + }
     + 
     + static void check_streamcopy_filters(OptionsContext *o, AVFormatContext *oc,
     +@@ fftools/ffmpeg_opt.c: static OutputStream *new_subtitle_stream(OptionsContext *o, AVFormatContext *oc,
     + 
     +     subtitle_enc->codec_type = AVMEDIA_TYPE_SUBTITLE;
     + 
     ++    MATCH_PER_STREAM_OPT(filter_scripts, str, ost->filters_script, oc, st);
     ++    MATCH_PER_STREAM_OPT(filters,        str, ost->filters,        oc, st);
     ++
     +     if (!ost->stream_copy) {
     +         char *frame_size = NULL;
     + 
     +@@ fftools/ffmpeg_opt.c: static OutputStream *new_subtitle_stream(OptionsContext *o, AVFormatContext *oc,
     +             av_log(NULL, AV_LOG_FATAL, "Invalid frame size: %s.\n", frame_size);
     +             exit_program(1);
     +         }
     ++
     ++        ost->avfilter = get_ost_filters(o, oc, ost);
     ++        if (!ost->avfilter)
     ++            exit_program(1);
     +     }
     + 
     +     return ost;
      @@ fftools/ffmpeg_opt.c: static void init_output_filter(OutputFilter *ofilter, OptionsContext *o,
           switch (ofilter->type) {
           case AVMEDIA_TYPE_VIDEO: ost = new_video_stream(o, oc, -1); break;
     @@ fftools/ffmpeg_opt.c: static void init_output_filter(OutputFilter *ofilter, Opti
                      "currently.\n");
               exit_program(1);
           }
     +@@ fftools/ffmpeg_opt.c: loop_end:
     +             ist->processing_needed = 1;
     + 
     +             if (ost->st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ||
     +-                ost->st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
     ++                ost->st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO ||
     ++                ost->st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) {
     +                 err = init_simple_filtergraph(ist, ost);
     +                 if (err < 0) {
     +                     av_log(NULL, AV_LOG_ERROR,
     +@@ fftools/ffmpeg_opt.c: loop_end:
     +                 } else if (ost->enc->ch_layouts) {
     +                     f->ch_layouts = ost->enc->ch_layouts;
     +                 }
     ++                break;
     ++            case AVMEDIA_TYPE_SUBTITLE:
     ++                f->format     = ost->enc_ctx->subtitle_type;
     ++
     +                 break;
     +             }
     +         }
      
       ## tests/ref/fate/filter-overlay-dvdsub-2397 ##
      @@
     @@ tests/ref/fate/sub-dvb
      +0,   31400000,   31400000,   479000,       14, 0x0959015b
      +0,   31879000,   31879000,   479000,       14, 0x09c9016b
      
     + ## tests/ref/fate/sub-scc ##
     +@@ tests/ref/fate/sub-scc: Style: Default,Monospace,16,&Hffffff,&Hffffff,&H0,&H0,0,0,0,0,100,100,0,0,3,1,0,
     + 
     + [Events]
     + Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
     +-Dialogue: 0,0:00:-2.-47,0:00:00.70,Default,,0,0,0,,{\an7}{\pos(76,228)}WE HAVE FOUND A WITCH !\N{\an7}{\pos(76,243)}MAY WE BURN HER ?
     + Dialogue: 0,0:00:00.69,0:00:03.29,Default,,0,0,0,,{\an7}{\pos(115,228)}[ Crowd ]\N{\an7}{\pos(115,243)}BURN HER !  BURN HER !
     + Dialogue: 0,0:00:03.30,0:00:07.07,Default,,0,0,0,,{\an7}{\pos(38,197)}HOW DO YOU KNOW\N{\an7}{\pos(38,213)}SHE IS A WITCH ?\N{\an7}{\pos(153,243)}SHE LOOKS LIKE ONE !
     + Dialogue: 0,0:00:07.07,0:00:09.27,Default,,0,0,0,,{\an7}{\pos(192,228)}[ Shouting\N{\an7}{\pos(192,243)}\h\hAffirmations ]
     +
       ## tests/ref/fate/sub2video ##
      @@
       0,         47,         47,        1,   518400, 0xde69683f
 23:  a66debd96e = 25:  a90a6e1086 avcodec/dvbsubdec: Fix conditions for fallback to default resolution

-- 
ffmpeg-codebot
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v5 01/25] avcodec, avutil: Move enum AVSubtitleType to avutil, add new and deprecate old values
  2022-06-25  9:57       ` [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022 ffmpegagent
@ 2022-06-25  9:57         ` softworkz
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 02/25] avutil/frame: Prepare AVFrame for subtitle handling softworkz
                           ` (26 subsequent siblings)
  27 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-25  9:57 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/avcodec.h | 19 +------------
 libavutil/Makefile   |  1 +
 libavutil/subfmt.h   | 68 ++++++++++++++++++++++++++++++++++++++++++++
 libavutil/version.h  |  1 +
 4 files changed, 71 insertions(+), 18 deletions(-)
 create mode 100644 libavutil/subfmt.h

diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index 4dae23d06e..56d551f92d 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -35,6 +35,7 @@
 #include "libavutil/frame.h"
 #include "libavutil/log.h"
 #include "libavutil/pixfmt.h"
+#include "libavutil/subfmt.h"
 #include "libavutil/rational.h"
 
 #include "codec.h"
@@ -2255,24 +2256,6 @@ typedef struct AVHWAccel {
  * @}
  */
 
-enum AVSubtitleType {
-    SUBTITLE_NONE,
-
-    SUBTITLE_BITMAP,                ///< A bitmap, pict will be set
-
-    /**
-     * Plain text, the text field must be set by the decoder and is
-     * authoritative. ass and pict fields may contain approximations.
-     */
-    SUBTITLE_TEXT,
-
-    /**
-     * Formatted text, the ass field must be set by the decoder and is
-     * authoritative. pict and text fields may contain approximations.
-     */
-    SUBTITLE_ASS,
-};
-
 #define AV_SUBTITLE_FLAG_FORCED 0x00000001
 
 typedef struct AVSubtitleRect {
diff --git a/libavutil/Makefile b/libavutil/Makefile
index 29c170214c..edec708ff5 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -77,6 +77,7 @@ HEADERS = adler32.h                                                     \
           sha512.h                                                      \
           spherical.h                                                   \
           stereo3d.h                                                    \
+          subfmt.h                                                      \
           threadmessage.h                                               \
           time.h                                                        \
           timecode.h                                                    \
diff --git a/libavutil/subfmt.h b/libavutil/subfmt.h
new file mode 100644
index 0000000000..791b45519f
--- /dev/null
+++ b/libavutil/subfmt.h
@@ -0,0 +1,68 @@
+/*
+ * 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
+ */
+
+#ifndef AVUTIL_SUBFMT_H
+#define AVUTIL_SUBFMT_H
+
+#include "version.h"
+
+enum AVSubtitleType {
+
+    /**
+     * Subtitle format unknown.
+     */
+    AV_SUBTITLE_FMT_NONE = -1,
+
+    /**
+     * Subtitle format unknown.
+     */
+    AV_SUBTITLE_FMT_UNKNOWN = 0,
+#if FF_API_OLD_SUBTITLES
+    SUBTITLE_NONE = 0,          ///< Deprecated, use AV_SUBTITLE_FMT_NONE instead.
+#endif
+
+    /**
+     * Bitmap area in AVSubtitleRect.data, pixfmt AV_PIX_FMT_PAL8.
+     */
+    AV_SUBTITLE_FMT_BITMAP = 1,
+#if FF_API_OLD_SUBTITLES
+    SUBTITLE_BITMAP = 1,        ///< Deprecated, use AV_SUBTITLE_FMT_BITMAP instead.
+#endif
+
+    /**
+     * Plain text in AVSubtitleRect.text.
+     */
+    AV_SUBTITLE_FMT_TEXT = 2,
+#if FF_API_OLD_SUBTITLES
+    SUBTITLE_TEXT = 2,          ///< Deprecated, use AV_SUBTITLE_FMT_TEXT instead.
+#endif
+
+    /**
+     * Text Formatted as per ASS specification, contained AVSubtitleRect.ass.
+     */
+    AV_SUBTITLE_FMT_ASS = 3,
+#if FF_API_OLD_SUBTITLES
+    SUBTITLE_ASS = 3,           ///< Deprecated, use AV_SUBTITLE_FMT_ASS instead.
+#endif
+
+    AV_SUBTITLE_FMT_NB,         ///< number of subtitle formats, DO NOT USE THIS if you want to link with shared libav* because the number of formats might differ between versions.
+};
+
+#endif /* AVUTIL_SUBFMT_H */
diff --git a/libavutil/version.h b/libavutil/version.h
index 2e9e02dda8..f9f84801a3 100644
--- a/libavutil/version.h
+++ b/libavutil/version.h
@@ -114,6 +114,7 @@
 #define FF_API_XVMC                     (LIBAVUTIL_VERSION_MAJOR < 58)
 #define FF_API_OLD_CHANNEL_LAYOUT       (LIBAVUTIL_VERSION_MAJOR < 58)
 #define FF_API_AV_FOPEN_UTF8            (LIBAVUTIL_VERSION_MAJOR < 58)
+#define FF_API_OLD_SUBTITLES            (LIBAVUTIL_VERSION_MAJOR < 58)
 
 /**
  * @}
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v5 02/25] avutil/frame: Prepare AVFrame for subtitle handling
  2022-06-25  9:57       ` [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022 ffmpegagent
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 01/25] avcodec, avutil: Move enum AVSubtitleType to avutil, add new and deprecate old values softworkz
@ 2022-06-25  9:57         ` softworkz
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 03/25] avcodec/subtitles: Introduce new frame-based subtitle decoding API softworkz
                           ` (25 subsequent siblings)
  27 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-25  9:57 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Root commit for adding subtitle filtering capabilities.
In detail:

- Add type (AVMediaType) field to AVFrame
  Replaces previous way of distinction which was based on checking
  width and height to determine whether a frame is audio or video
- Add subtitle fields to AVFrame
- Add new struct AVSubtitleArea, similar to AVSubtitleRect, but
  different allocation logic. Cannot and must not be used
  interchangeably, hence the new struct

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavutil/Makefile |   1 +
 libavutil/frame.c  | 206 +++++++++++++++++++++++++++++++++++++++++----
 libavutil/frame.h  |  85 ++++++++++++++++++-
 libavutil/subfmt.c |  45 ++++++++++
 libavutil/subfmt.h |  47 +++++++++++
 5 files changed, 363 insertions(+), 21 deletions(-)
 create mode 100644 libavutil/subfmt.c

diff --git a/libavutil/Makefile b/libavutil/Makefile
index edec708ff5..48f78c81e5 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -165,6 +165,7 @@ OBJS = adler32.o                                                        \
        slicethread.o                                                    \
        spherical.o                                                      \
        stereo3d.o                                                       \
+       subfmt.o                                                         \
        threadmessage.o                                                  \
        time.o                                                           \
        timecode.o                                                       \
diff --git a/libavutil/frame.c b/libavutil/frame.c
index 4c16488c66..eadeabd926 100644
--- a/libavutil/frame.c
+++ b/libavutil/frame.c
@@ -26,6 +26,7 @@
 #include "imgutils.h"
 #include "mem.h"
 #include "samplefmt.h"
+#include "subfmt.h"
 #include "hwcontext.h"
 
 #if FF_API_OLD_CHANNEL_LAYOUT
@@ -52,6 +53,9 @@ const char *av_get_colorspace_name(enum AVColorSpace val)
     return name[val];
 }
 #endif
+
+static int frame_copy_subtitles(AVFrame *dst, const AVFrame *src, int copy_data);
+
 static void get_frame_defaults(AVFrame *frame)
 {
     memset(frame, 0, sizeof(*frame));
@@ -72,7 +76,12 @@ static void get_frame_defaults(AVFrame *frame)
     frame->colorspace          = AVCOL_SPC_UNSPECIFIED;
     frame->color_range         = AVCOL_RANGE_UNSPECIFIED;
     frame->chroma_location     = AVCHROMA_LOC_UNSPECIFIED;
-    frame->flags               = 0;
+    frame->num_subtitle_areas  = 0;
+    frame->subtitle_areas      = NULL;
+    frame->subtitle_header     = NULL;
+    frame->repeat_sub          = 0;
+    frame->subtitle_timing.start_pts = 0;
+    frame->subtitle_timing.duration  = 0;
 }
 
 static void free_side_data(AVFrameSideData **ptr_sd)
@@ -251,6 +260,23 @@ FF_ENABLE_DEPRECATION_WARNINGS
 
 }
 
+static int get_subtitle_buffer(AVFrame *frame)
+{
+    // Buffers in AVFrame->buf[] are not used in case of subtitle frames.
+    // To accomodate with existing code, checking ->buf[0] to determine
+    // whether a frame is ref-counted or has data, we're adding a 1-byte
+    // buffer here, which marks the subtitle frame to contain data.
+    frame->buf[0] = av_buffer_alloc(1);
+    if (!frame->buf[0]) {
+        av_frame_unref(frame);
+        return AVERROR(ENOMEM);
+    }
+
+    frame->extended_data = frame->data;
+
+    return 0;
+}
+
 int av_frame_get_buffer(AVFrame *frame, int align)
 {
     if (frame->format < 0)
@@ -258,23 +284,41 @@ int av_frame_get_buffer(AVFrame *frame, int align)
 
 FF_DISABLE_DEPRECATION_WARNINGS
     if (frame->width > 0 && frame->height > 0)
-        return get_video_buffer(frame, align);
+        frame->type = AVMEDIA_TYPE_VIDEO;
     else if (frame->nb_samples > 0 &&
              (av_channel_layout_check(&frame->ch_layout)
 #if FF_API_OLD_CHANNEL_LAYOUT
               || frame->channel_layout || frame->channels > 0
 #endif
              ))
-        return get_audio_buffer(frame, align);
+        frame->type = AVMEDIA_TYPE_AUDIO;
 FF_ENABLE_DEPRECATION_WARNINGS
 
-    return AVERROR(EINVAL);
+    return av_frame_get_buffer2(frame, align);
+}
+
+int av_frame_get_buffer2(AVFrame *frame, int align)
+{
+    if (frame->format < 0)
+        return AVERROR(EINVAL);
+
+    switch (frame->type) {
+    case AVMEDIA_TYPE_VIDEO:
+        return get_video_buffer(frame, align);
+    case AVMEDIA_TYPE_AUDIO:
+        return get_audio_buffer(frame, align);
+    case AVMEDIA_TYPE_SUBTITLE:
+        return get_subtitle_buffer(frame);
+    default:
+        return AVERROR(EINVAL);
+    }
 }
 
 static int frame_copy_props(AVFrame *dst, const AVFrame *src, int force_copy)
 {
     int ret, i;
 
+    dst->type                   = src->type;
     dst->key_frame              = src->key_frame;
     dst->pict_type              = src->pict_type;
     dst->sample_aspect_ratio    = src->sample_aspect_ratio;
@@ -306,6 +350,12 @@ static int frame_copy_props(AVFrame *dst, const AVFrame *src, int force_copy)
     dst->colorspace             = src->colorspace;
     dst->color_range            = src->color_range;
     dst->chroma_location        = src->chroma_location;
+    dst->repeat_sub             = src->repeat_sub;
+    dst->subtitle_timing.start_pts = src->subtitle_timing.start_pts;
+    dst->subtitle_timing.duration  = src->subtitle_timing.duration;
+
+    if ((ret = av_buffer_replace(&dst->subtitle_header, src->subtitle_header)) < 0)
+        return ret;
 
     av_dict_copy(&dst->metadata, src->metadata, 0);
 
@@ -353,6 +403,7 @@ FF_ENABLE_DEPRECATION_WARNINGS
     av_assert1(dst->ch_layout.nb_channels == 0 &&
                dst->ch_layout.order == AV_CHANNEL_ORDER_UNSPEC);
 
+    dst->type           = src->type;
     dst->format         = src->format;
     dst->width          = src->width;
     dst->height         = src->height;
@@ -385,7 +436,7 @@ FF_ENABLE_DEPRECATION_WARNINGS
 
     /* duplicate the frame data if it's not refcounted */
     if (!src->buf[0]) {
-        ret = av_frame_get_buffer(dst, 0);
+        ret = av_frame_get_buffer2(dst, 0);
         if (ret < 0)
             goto fail;
 
@@ -407,6 +458,10 @@ FF_ENABLE_DEPRECATION_WARNINGS
         }
     }
 
+    /* copy subtitle areas */
+    if (src->type == AVMEDIA_TYPE_SUBTITLE)
+        frame_copy_subtitles(dst, src, 0);
+
     if (src->extended_buf) {
         dst->extended_buf = av_calloc(src->nb_extended_buf,
                                       sizeof(*dst->extended_buf));
@@ -476,7 +531,7 @@ AVFrame *av_frame_clone(const AVFrame *src)
 
 void av_frame_unref(AVFrame *frame)
 {
-    int i;
+    unsigned i, n;
 
     if (!frame)
         return;
@@ -495,6 +550,21 @@ void av_frame_unref(AVFrame *frame)
     av_buffer_unref(&frame->opaque_ref);
     av_buffer_unref(&frame->private_ref);
 
+    av_buffer_unref(&frame->subtitle_header);
+
+    for (i = 0; i < frame->num_subtitle_areas; i++) {
+        AVSubtitleArea *area = frame->subtitle_areas[i];
+
+        for (n = 0; n < FF_ARRAY_ELEMS(area->buf); n++)
+            av_buffer_unref(&area->buf[n]);
+
+        av_freep(&area->text);
+        av_freep(&area->ass);
+        av_free(area);
+    }
+
+    av_freep(&frame->subtitle_areas);
+
     if (frame->extended_data != frame->data)
         av_freep(&frame->extended_data);
 
@@ -522,18 +592,28 @@ FF_ENABLE_DEPRECATION_WARNINGS
 
 int av_frame_is_writable(AVFrame *frame)
 {
-    int i, ret = 1;
+    AVSubtitleArea *area;
+    int ret = 1;
 
     /* assume non-refcounted frames are not writable */
     if (!frame->buf[0])
         return 0;
 
-    for (i = 0; i < FF_ARRAY_ELEMS(frame->buf); i++)
+    for (unsigned i = 0; i < FF_ARRAY_ELEMS(frame->buf); i++)
         if (frame->buf[i])
             ret &= !!av_buffer_is_writable(frame->buf[i]);
-    for (i = 0; i < frame->nb_extended_buf; i++)
+    for (unsigned i = 0; i < frame->nb_extended_buf; i++)
         ret &= !!av_buffer_is_writable(frame->extended_buf[i]);
 
+    for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+        area = frame->subtitle_areas[i];
+        if (area) {
+            for (unsigned n = 0; n < FF_ARRAY_ELEMS(area->buf); n++)
+                if (area->buf[n])
+                    ret &= !!av_buffer_is_writable(area->buf[n]);
+            }
+    }
+
     return ret;
 }
 
@@ -549,6 +629,7 @@ int av_frame_make_writable(AVFrame *frame)
         return 0;
 
     memset(&tmp, 0, sizeof(tmp));
+    tmp.type           = frame->type;
     tmp.format         = frame->format;
     tmp.width          = frame->width;
     tmp.height         = frame->height;
@@ -568,7 +649,7 @@ FF_ENABLE_DEPRECATION_WARNINGS
     if (frame->hw_frames_ctx)
         ret = av_hwframe_get_buffer(frame->hw_frames_ctx, &tmp, 0);
     else
-        ret = av_frame_get_buffer(&tmp, 0);
+        ret = av_frame_get_buffer2(&tmp, 0);
     if (ret < 0)
         return ret;
 
@@ -603,7 +684,12 @@ AVBufferRef *av_frame_get_plane_buffer(AVFrame *frame, int plane)
     uint8_t *data;
     int planes, i;
 
-    if (frame->nb_samples) {
+    switch(frame->type) {
+    case AVMEDIA_TYPE_VIDEO:
+        planes = 4;
+        break;
+    case AVMEDIA_TYPE_AUDIO:
+        {
         int channels = frame->ch_layout.nb_channels;
 
 #if FF_API_OLD_CHANNEL_LAYOUT
@@ -617,8 +703,11 @@ FF_ENABLE_DEPRECATION_WARNINGS
         if (!channels)
             return NULL;
         planes = av_sample_fmt_is_planar(frame->format) ? channels : 1;
-    } else
-        planes = 4;
+        break;
+        }
+    default:
+        return NULL;
+    }
 
     if (plane < 0 || plane >= planes || !frame->extended_data[plane])
         return NULL;
@@ -761,22 +850,103 @@ FF_ENABLE_DEPRECATION_WARNINGS
     return 0;
 }
 
+static int av_subtitle_area2area(AVSubtitleArea *dst, const AVSubtitleArea *src, int copy_data)
+{
+    dst->x         =  src->x;
+    dst->y         =  src->y;
+    dst->w         =  src->w;
+    dst->h         =  src->h;
+    dst->nb_colors =  src->nb_colors;
+    dst->type      =  src->type;
+    dst->flags     =  src->flags;
+
+    switch (dst->type) {
+    case AV_SUBTITLE_FMT_BITMAP:
+
+        for (unsigned i = 0; i < AV_NUM_BUFFER_POINTERS; i++) {
+            if (src->h > 0 && src->w > 0 && src->buf[i]) {
+                dst->buf[0] = av_buffer_ref(src->buf[i]);
+                if (!dst->buf[i])
+                    return AVERROR(ENOMEM);
+
+                if (copy_data) {
+                    const int ret = av_buffer_make_writable(&dst->buf[i]);
+                    if (ret < 0)
+                        return ret;
+                }
+
+                dst->linesize[i] = src->linesize[i];
+            }
+        }
+
+        memcpy(&dst->pal[0], &src->pal[0], sizeof(src->pal[0]) * 256);
+
+        break;
+    case AV_SUBTITLE_FMT_TEXT:
+
+        if (src->text) {
+            dst->text = av_strdup(src->text);
+            if (!dst->text)
+                return AVERROR(ENOMEM);
+        }
+
+        break;
+    case AV_SUBTITLE_FMT_ASS:
+
+        if (src->ass) {
+            dst->ass = av_strdup(src->ass);
+            if (!dst->ass)
+                return AVERROR(ENOMEM);
+        }
+
+        break;
+    default:
+
+        av_log(NULL, AV_LOG_ERROR, "Subtitle area has invalid format: %d", dst->type);
+        return AVERROR(ENOMEM);
+    }
+
+    return 0;
+}
+
+static int frame_copy_subtitles(AVFrame *dst, const AVFrame *src, int copy_data)
+{
+    dst->format = src->format;
+
+    dst->num_subtitle_areas = src->num_subtitle_areas;
+
+    if (src->num_subtitle_areas) {
+        dst->subtitle_areas = av_malloc_array(src->num_subtitle_areas, sizeof(AVSubtitleArea *));
+
+        for (unsigned i = 0; i < src->num_subtitle_areas; i++) {
+            dst->subtitle_areas[i] = av_mallocz(sizeof(AVSubtitleArea));
+            av_subtitle_area2area(dst->subtitle_areas[i], src->subtitle_areas[i], copy_data);
+        }
+    }
+
+    return 0;
+}
+
 int av_frame_copy(AVFrame *dst, const AVFrame *src)
 {
     if (dst->format != src->format || dst->format < 0)
         return AVERROR(EINVAL);
 
-FF_DISABLE_DEPRECATION_WARNINGS
-    if (dst->width > 0 && dst->height > 0)
+    switch(dst->type) {
+    case AVMEDIA_TYPE_VIDEO:
         return frame_copy_video(dst, src);
-    else if (dst->nb_samples > 0 &&
+    case AVMEDIA_TYPE_AUDIO:
+        if (dst->nb_samples > 0 &&
              (av_channel_layout_check(&dst->ch_layout)
 #if FF_API_OLD_CHANNEL_LAYOUT
               || dst->channels > 0
 #endif
             ))
-        return frame_copy_audio(dst, src);
-FF_ENABLE_DEPRECATION_WARNINGS
+            return frame_copy_audio(dst, src);
+        break;
+    case AVMEDIA_TYPE_SUBTITLE:
+        return frame_copy_subtitles(dst, src, 1);
+    }
 
     return AVERROR(EINVAL);
 }
diff --git a/libavutil/frame.h b/libavutil/frame.h
index 33fac2054c..dd36f5b27c 100644
--- a/libavutil/frame.h
+++ b/libavutil/frame.h
@@ -25,7 +25,6 @@
 #ifndef AVUTIL_FRAME_H
 #define AVUTIL_FRAME_H
 
-#include <stddef.h>
 #include <stdint.h>
 
 #include "avutil.h"
@@ -35,6 +34,7 @@
 #include "rational.h"
 #include "samplefmt.h"
 #include "pixfmt.h"
+#include "subfmt.h"
 #include "version.h"
 
 
@@ -293,7 +293,7 @@ typedef struct AVRegionOfInterest {
 } AVRegionOfInterest;
 
 /**
- * This structure describes decoded (raw) audio or video data.
+ * This structure describes decoded (raw) audio, video or subtitle data.
  *
  * AVFrame must be allocated using av_frame_alloc(). Note that this only
  * allocates the AVFrame itself, the buffers for the data must be managed
@@ -407,7 +407,7 @@ typedef struct AVFrame {
     /**
      * format of the frame, -1 if unknown or unset
      * Values correspond to enum AVPixelFormat for video frames,
-     * enum AVSampleFormat for audio)
+     * enum AVSampleFormat for audio, AVSubtitleType for subtitles)
      */
     int format;
 
@@ -702,6 +702,53 @@ typedef struct AVFrame {
      * Channel layout of the audio data.
      */
     AVChannelLayout ch_layout;
+
+    /**
+     * Media type of the frame (audio, video, subtitles..)
+     *
+     * See AVMEDIA_TYPE_xxx
+     */
+    enum AVMediaType type;
+
+    /**
+     * Number of items in the @ref subtitle_areas array.
+     */
+    unsigned num_subtitle_areas;
+
+    /**
+     * Array of subtitle areas, may be empty.
+     */
+    AVSubtitleArea **subtitle_areas;
+
+    /**
+     * Header containing style information for text subtitles.
+     */
+    AVBufferRef *subtitle_header;
+
+    /**
+     * Indicates that a subtitle frame is a repeated frame for arbitrating flow
+     * in a filter graph.
+     * The field subtitle_timing.start_pts always indicates the original presentation
+     * time, while the frame's pts field may be different.
+     */
+    int repeat_sub;
+
+    struct SubtitleTiming
+    {
+        /**
+         * The display start time, in AV_TIME_BASE.
+         *
+         * For subtitle frames, AVFrame.pts is populated from the packet pts value,
+         * which is not always the same as this value.
+         */
+        int64_t start_pts;
+
+        /**
+         * Display duration, in AV_TIME_BASE.
+         */
+        int64_t duration;
+
+    } subtitle_timing;
 } AVFrame;
 
 
@@ -778,6 +825,8 @@ void av_frame_move_ref(AVFrame *dst, AVFrame *src);
 /**
  * Allocate new buffer(s) for audio or video data.
  *
+ * Note: For subtitle data, use av_frame_get_buffer2
+ *
  * The following fields must be set on frame before calling this function:
  * - format (pixel format for video, sample format for audio)
  * - width and height for video
@@ -797,9 +846,39 @@ void av_frame_move_ref(AVFrame *dst, AVFrame *src);
  *              recommended to pass 0 here unless you know what you are doing.
  *
  * @return 0 on success, a negative AVERROR on error.
+ *
+ * @deprecated Use @ref av_frame_get_buffer2 instead and set @ref AVFrame.type
+ * before calling.
  */
+attribute_deprecated
 int av_frame_get_buffer(AVFrame *frame, int align);
 
+/**
+ * Allocate new buffer(s) for audio, video or subtitle data.
+ *
+ * The following fields must be set on frame before calling this function:
+ * - format (pixel format for video, sample format for audio)
+ * - width and height for video
+ * - nb_samples and channel_layout for audio
+ * - type (AVMediaType)
+ *
+ * This function will fill AVFrame.data and AVFrame.buf arrays and, if
+ * necessary, allocate and fill AVFrame.extended_data and AVFrame.extended_buf.
+ * For planar formats, one buffer will be allocated for each plane.
+ *
+ * @warning: if frame already has been allocated, calling this function will
+ *           leak memory. In addition, undefined behavior can occur in certain
+ *           cases.
+ *
+ * @param frame frame in which to store the new buffers.
+ * @param align Required buffer size alignment. If equal to 0, alignment will be
+ *              chosen automatically for the current CPU. It is highly
+ *              recommended to pass 0 here unless you know what you are doing.
+ *
+ * @return 0 on success, a negative AVERROR on error.
+ */
+int av_frame_get_buffer2(AVFrame *frame, int align);
+
 /**
  * Check if the frame data is writable.
  *
diff --git a/libavutil/subfmt.c b/libavutil/subfmt.c
new file mode 100644
index 0000000000..c72ebe2a43
--- /dev/null
+++ b/libavutil/subfmt.c
@@ -0,0 +1,45 @@
+/*
+ * 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
+ */
+
+#include <string.h>
+#include "common.h"
+#include "subfmt.h"
+
+static const char sub_fmt_info[AV_SUBTITLE_FMT_NB][24] = {
+    [AV_SUBTITLE_FMT_UNKNOWN] = "Unknown subtitle format",
+    [AV_SUBTITLE_FMT_BITMAP]  = "Graphical subtitles",
+    [AV_SUBTITLE_FMT_TEXT]    = "Text subtitles (plain)",
+    [AV_SUBTITLE_FMT_ASS]     = "Text subtitles (ass)",
+};
+
+const char *av_get_subtitle_fmt_name(enum AVSubtitleType sub_fmt)
+{
+    if (sub_fmt < 0 || sub_fmt >= AV_SUBTITLE_FMT_NB)
+        return NULL;
+    return sub_fmt_info[sub_fmt];
+}
+
+enum AVSubtitleType av_get_subtitle_fmt(const char *name)
+{
+    for (int i = 0; i < AV_SUBTITLE_FMT_NB; i++)
+        if (!strcmp(sub_fmt_info[i], name))
+            return i;
+    return AV_SUBTITLE_FMT_NONE;
+}
diff --git a/libavutil/subfmt.h b/libavutil/subfmt.h
index 791b45519f..3b8a0613b2 100644
--- a/libavutil/subfmt.h
+++ b/libavutil/subfmt.h
@@ -21,6 +21,9 @@
 #ifndef AVUTIL_SUBFMT_H
 #define AVUTIL_SUBFMT_H
 
+#include <stdint.h>
+
+#include "buffer.h"
 #include "version.h"
 
 enum AVSubtitleType {
@@ -65,4 +68,48 @@ enum AVSubtitleType {
     AV_SUBTITLE_FMT_NB,         ///< number of subtitle formats, DO NOT USE THIS if you want to link with shared libav* because the number of formats might differ between versions.
 };
 
+typedef struct AVSubtitleArea {
+#define AV_NUM_BUFFER_POINTERS 1
+
+    enum AVSubtitleType type;
+    int flags;
+
+    int x;         ///< top left corner  of area.
+    int y;         ///< top left corner  of area.
+    int w;         ///< width            of area.
+    int h;         ///< height           of area.
+    int nb_colors; ///< number of colors in bitmap palette (@ref pal).
+
+    /**
+     * Buffers and line sizes for the bitmap of this subtitle.
+     *
+     * @{
+     */
+    AVBufferRef *buf[AV_NUM_BUFFER_POINTERS];
+    int linesize[AV_NUM_BUFFER_POINTERS];
+    /**
+     * @}
+     */
+
+    uint32_t pal[256]; ///< RGBA palette for the bitmap.
+
+    char *text;        ///< 0-terminated plain UTF-8 text
+    char *ass;         ///< 0-terminated ASS/SSA compatible event line.
+
+} AVSubtitleArea;
+
+/**
+ * Return the name of sub_fmt, or NULL if sub_fmt is not
+ * recognized.
+ */
+const char *av_get_subtitle_fmt_name(enum AVSubtitleType sub_fmt);
+
+/**
+ * Return a subtitle format corresponding to name, or AV_SUBTITLE_FMT_NONE
+ * on error.
+ *
+ * @param name Subtitle format name.
+ */
+enum AVSubtitleType av_get_subtitle_fmt(const char *name);
+
 #endif /* AVUTIL_SUBFMT_H */
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v5 03/25] avcodec/subtitles: Introduce new frame-based subtitle decoding API
  2022-06-25  9:57       ` [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022 ffmpegagent
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 01/25] avcodec, avutil: Move enum AVSubtitleType to avutil, add new and deprecate old values softworkz
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 02/25] avutil/frame: Prepare AVFrame for subtitle handling softworkz
@ 2022-06-25  9:57         ` softworkz
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 04/25] avcodec/libzvbi: set subtitle type softworkz
                           ` (24 subsequent siblings)
  27 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-25  9:57 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

- Modify avcodec_send_packet() to support subtitles via the regular
  frame based decoding API
- Add decode_subtitle_shim() which takes subtitle frames,
  and serves as a compatibility shim to the legacy subtitle decoding
  API until all subtitle decoders are migrated to the frame-based API
- Add additional methods for conversion between old and new API

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/avcodec.c  |   8 ++
 libavcodec/avcodec.h  |  10 ++-
 libavcodec/decode.c   |  60 ++++++++++++--
 libavcodec/internal.h |  16 ++++
 libavcodec/utils.c    | 184 ++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 269 insertions(+), 9 deletions(-)

diff --git a/libavcodec/avcodec.c b/libavcodec/avcodec.c
index 5f6e71a39e..0a1d961fc6 100644
--- a/libavcodec/avcodec.c
+++ b/libavcodec/avcodec.c
@@ -358,6 +358,14 @@ FF_DISABLE_DEPRECATION_WARNINGS
 FF_ENABLE_DEPRECATION_WARNINGS
 #endif
 
+        // Set the subtitle type from the codec descriptor in case the decoder hasn't done itself
+        if (avctx->codec_type == AVMEDIA_TYPE_SUBTITLE && avctx->subtitle_type == AV_SUBTITLE_FMT_UNKNOWN) {
+            if(avctx->codec_descriptor->props & AV_CODEC_PROP_BITMAP_SUB)
+                avctx->subtitle_type = AV_SUBTITLE_FMT_BITMAP;
+            if(avctx->codec_descriptor->props & AV_CODEC_PROP_TEXT_SUB)
+                 avctx->subtitle_type = AV_SUBTITLE_FMT_ASS;
+        }
+
 #if FF_API_AVCTX_TIMEBASE
         if (avctx->framerate.num > 0 && avctx->framerate.den > 0)
             avctx->time_base = av_inv_q(av_mul_q(avctx->framerate, (AVRational){avctx->ticks_per_frame, 1}));
diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index 56d551f92d..de87b0406b 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -1698,7 +1698,7 @@ typedef struct AVCodecContext {
 
     /**
      * Header containing style information for text subtitles.
-     * For SUBTITLE_ASS subtitle type, it should contain the whole ASS
+     * For AV_SUBTITLE_FMT_ASS subtitle type, it should contain the whole ASS
      * [Script Info] and [V4+ Styles] section, plus the [Events] line and
      * the Format line following. It shouldn't include any Dialogue line.
      * - encoding: Set/allocated/freed by user (before avcodec_open2())
@@ -2056,6 +2056,8 @@ typedef struct AVCodecContext {
      *             The decoder can then override during decoding as needed.
      */
     AVChannelLayout ch_layout;
+
+    enum AVSubtitleType subtitle_type;
 } AVCodecContext;
 
 /**
@@ -2432,7 +2434,10 @@ int avcodec_close(AVCodecContext *avctx);
  * Free all allocated data in the given subtitle struct.
  *
  * @param sub AVSubtitle to free.
+ *
+ * @deprecated Use the regular frame based encode and decode APIs instead.
  */
+attribute_deprecated
 void avsubtitle_free(AVSubtitle *sub);
 
 /**
@@ -2525,7 +2530,10 @@ enum AVChromaLocation avcodec_chroma_pos_to_enum(int xpos, int ypos);
  *                 must be freed with avsubtitle_free if *got_sub_ptr is set.
  * @param[in,out] got_sub_ptr Zero if no subtitle could be decompressed, otherwise, it is nonzero.
  * @param[in] avpkt The input AVPacket containing the input buffer.
+ *
+ * @deprecated Use the new decode API (avcodec_send_packet, avcodec_receive_frame) instead.
  */
+attribute_deprecated
 int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub,
                             int *got_sub_ptr,
                             AVPacket *avpkt);
diff --git a/libavcodec/decode.c b/libavcodec/decode.c
index 1893caa6a6..e8ca7b6da4 100644
--- a/libavcodec/decode.c
+++ b/libavcodec/decode.c
@@ -573,6 +573,39 @@ static int decode_receive_frame_internal(AVCodecContext *avctx, AVFrame *frame)
     return ret;
 }
 
+static int decode_subtitle2_priv(AVCodecContext *avctx, AVSubtitle *sub,
+                                 int *got_sub_ptr, AVPacket *avpkt);
+
+static int decode_subtitle_shim(AVCodecContext *avctx, AVFrame *frame, AVPacket *avpkt)
+{
+    int ret, got_sub_ptr = 0;
+    AVSubtitle subtitle = { 0 };
+
+    if (frame->buf[0])
+        return AVERROR(EAGAIN);
+
+    av_frame_unref(frame);
+
+    ret = decode_subtitle2_priv(avctx, &subtitle, &got_sub_ptr, avpkt);
+
+    if (ret >= 0 && got_sub_ptr) {
+        frame->type = AVMEDIA_TYPE_SUBTITLE;
+        frame->format = subtitle.format;
+        ret = av_frame_get_buffer2(frame, 0);
+
+        if (ret >= 0)
+            ret = ff_frame_put_subtitle(frame, &subtitle);
+
+        frame->width = avctx->width;
+        frame->height = avctx->height;
+        frame->pkt_dts = avpkt->dts;
+    }
+
+    avsubtitle_free(&subtitle);
+
+    return ret;
+}
+
 int attribute_align_arg avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt)
 {
     AVCodecInternal *avci = avctx->internal;
@@ -587,6 +620,13 @@ int attribute_align_arg avcodec_send_packet(AVCodecContext *avctx, const AVPacke
     if (avpkt && !avpkt->size && avpkt->data)
         return AVERROR(EINVAL);
 
+    if (avctx->codec_type == AVMEDIA_TYPE_SUBTITLE)
+		// this does not exactly implement the avcodec_send_packet/avcodec_receive_frame API
+	    // but we know that no subtitle decoder produces multiple AVSubtitles per packet through
+		// the legacy API, and this will be changed when migrating the subtitle decoders
+		// to the frame based decoding api
+        return decode_subtitle_shim(avctx, avci->buffer_frame, avpkt);
+
     av_packet_unref(avci->buffer_pkt);
     if (avpkt && (avpkt->data || avpkt->side_data_elems)) {
         ret = av_packet_ref(avci->buffer_pkt, avpkt);
@@ -648,7 +688,9 @@ int attribute_align_arg avcodec_receive_frame(AVCodecContext *avctx, AVFrame *fr
 
     if (avci->buffer_frame->buf[0]) {
         av_frame_move_ref(frame, avci->buffer_frame);
-    } else {
+    } else if (avctx->codec_type == AVMEDIA_TYPE_SUBTITLE)
+        return AVERROR(EAGAIN);
+    else {
         ret = decode_receive_frame_internal(avctx, frame);
         if (ret < 0)
             return ret;
@@ -813,9 +855,8 @@ static int utf8_check(const uint8_t *str)
     return 1;
 }
 
-int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub,
-                             int *got_sub_ptr,
-                             AVPacket *avpkt)
+static int decode_subtitle2_priv(AVCodecContext *avctx, AVSubtitle *sub,
+                             int *got_sub_ptr, AVPacket *avpkt)
 {
     int ret = 0;
 
@@ -861,10 +902,7 @@ int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub,
                                                  avctx->pkt_timebase, ms);
         }
 
-        if (avctx->codec_descriptor->props & AV_CODEC_PROP_BITMAP_SUB)
-            sub->format = 0;
-        else if (avctx->codec_descriptor->props & AV_CODEC_PROP_TEXT_SUB)
-            sub->format = 1;
+        sub->format = (uint16_t)avctx->subtitle_type;
 
         for (unsigned i = 0; i < sub->num_rects; i++) {
             if (avctx->sub_charenc_mode != FF_SUB_CHARENC_MODE_IGNORE &&
@@ -885,6 +923,12 @@ int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub,
     return ret;
 }
 
+int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub,
+                             int *got_sub_ptr, AVPacket *avpkt)
+{
+    return decode_subtitle2_priv(avctx, sub, got_sub_ptr, avpkt);
+}
+
 enum AVPixelFormat avcodec_default_get_format(struct AVCodecContext *avctx,
                                               const enum AVPixelFormat *fmt)
 {
diff --git a/libavcodec/internal.h b/libavcodec/internal.h
index 17e1de8127..69656729d8 100644
--- a/libavcodec/internal.h
+++ b/libavcodec/internal.h
@@ -290,4 +290,20 @@ int ff_int_from_list_or_default(void *ctx, const char * val_name, int val,
 
 void ff_dvdsub_parse_palette(uint32_t *palette, const char *p);
 
+/**
+ * Copies subtitle data from AVSubtitle to AVFrame.
+ *
+ * @deprecated This is a compatibility method for interoperability with
+ * the legacy subtitle API.
+ */
+int ff_frame_put_subtitle(AVFrame* frame, const AVSubtitle* sub);
+
+/**
+ * Copies subtitle data from AVFrame to AVSubtitle.
+ *
+ * @deprecated This is a compatibility method for interoperability with
+ * the legacy subtitle API.
+ */
+int ff_frame_get_subtitle(AVSubtitle* sub, AVFrame* frame);
+
 #endif /* AVCODEC_INTERNAL_H */
diff --git a/libavcodec/utils.c b/libavcodec/utils.c
index eb7e505a62..b67b6b6122 100644
--- a/libavcodec/utils.c
+++ b/libavcodec/utils.c
@@ -827,6 +827,190 @@ FF_ENABLE_DEPRECATION_WARNINGS
     return FFMAX(0, duration);
 }
 
+static int subtitle_area2rect(AVSubtitleRect *dst, const AVSubtitleArea *src)
+{
+    dst->x         =  src->x;
+    dst->y         =  src->y;
+    dst->w         =  src->w;
+    dst->h         =  src->h;
+    dst->nb_colors =  src->nb_colors;
+    dst->type      =  src->type;
+    dst->flags     =  src->flags;
+
+    switch (dst->type) {
+    case AV_SUBTITLE_FMT_BITMAP:
+
+        if (src->h > 0 && src->w > 0 && src->buf[0]) {
+            uint32_t *pal;
+            AVBufferRef *buf = src->buf[0];
+            dst->data[0] = av_mallocz(buf->size);
+            memcpy(dst->data[0], buf->data, buf->size);
+            dst->linesize[0] = src->linesize[0];
+
+            dst->data[1] = av_mallocz(256 * 4);
+            pal = (uint32_t *)dst->data[1];
+
+            for (unsigned i = 0; i < 256; i++) {
+                pal[i] = src->pal[i];
+            }
+        }
+
+        break;
+    case AV_SUBTITLE_FMT_TEXT:
+
+        if (src->text)
+            dst->text = av_strdup(src->text);
+        else
+            dst->text = av_strdup("");
+
+        if (!dst->text)
+            return AVERROR(ENOMEM);
+
+        break;
+    case AV_SUBTITLE_FMT_ASS:
+
+        if (src->ass)
+            dst->ass = av_strdup(src->ass);
+        else
+            dst->ass = av_strdup("");
+
+        if (!dst->ass)
+            return AVERROR(ENOMEM);
+
+        break;
+    default:
+
+        av_log(NULL, AV_LOG_ERROR, "Subtitle rect has invalid format: %d", dst->type);
+        return AVERROR(EINVAL);
+    }
+
+    return 0;
+}
+
+static int subtitle_rect2area(AVSubtitleArea *dst, const AVSubtitleRect *src)
+{
+    dst->x         =  src->x;
+    dst->y         =  src->y;
+    dst->w         =  src->w;
+    dst->h         =  src->h;
+    dst->nb_colors =  src->nb_colors;
+    dst->type      =  src->type;
+    dst->flags     =  src->flags;
+
+    switch (dst->type) {
+    case AV_SUBTITLE_FMT_BITMAP:
+
+        if (src->h > 0 && src->w > 0 && src->data[0]) {
+            AVBufferRef *buf = av_buffer_allocz(src->h * src->linesize[0]);
+            memcpy(buf->data, src->data[0], buf->size);
+
+            dst->buf[0] = buf;
+            dst->linesize[0] = src->linesize[0];
+        }
+
+        if (src->data[1]) {
+            uint32_t *pal = (uint32_t *)src->data[1];
+
+            for (unsigned i = 0; i < 256; i++) {
+                dst->pal[i] = pal[i];
+            }
+        }
+
+        break;
+    case AV_SUBTITLE_FMT_TEXT:
+
+        if (src->text) {
+            dst->text = av_strdup(src->text);
+            if (!dst->text)
+                return AVERROR(ENOMEM);
+        }
+
+        break;
+    case AV_SUBTITLE_FMT_ASS:
+
+        if (src->ass) {
+            dst->ass = av_strdup(src->ass);
+            if (!dst->ass)
+                return AVERROR(ENOMEM);
+        }
+
+        break;
+    default:
+
+        av_log(NULL, AV_LOG_ERROR, "Subtitle area has invalid format: %d", dst->type);
+        return AVERROR(EINVAL);
+    }
+
+    return 0;
+}
+
+/**
+ * Copies subtitle data from AVSubtitle (deprecated) to AVFrame
+ *
+ * @note This is a compatibility method for conversion to the legacy API
+ */
+int ff_frame_put_subtitle(AVFrame *frame, const AVSubtitle *sub)
+{
+    frame->format = sub->format;
+    frame->subtitle_timing.start_pts = sub->pts;
+    frame->subtitle_timing.start_pts += av_rescale_q(sub->start_display_time, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q);
+    frame->subtitle_timing.duration = av_rescale_q(sub->end_display_time - sub->start_display_time, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q);
+
+    if (sub->num_rects) {
+        frame->subtitle_areas = av_malloc_array(sub->num_rects, sizeof(AVSubtitleArea*));
+        if (!frame->subtitle_areas)
+            return AVERROR(ENOMEM);
+
+        for (unsigned i = 0; i < sub->num_rects; i++) {
+            int ret;
+            frame->subtitle_areas[i] = av_mallocz(sizeof(AVSubtitleArea));
+            if (!frame->subtitle_areas[i])
+                return AVERROR(ENOMEM);
+            ret = subtitle_rect2area(frame->subtitle_areas[i], sub->rects[i]);
+            if (ret < 0) {
+                frame->num_subtitle_areas = i;
+                return ret;
+            }
+        }
+    }
+
+    frame->num_subtitle_areas = sub->num_rects;
+    return 0;
+}
+
+/**
+ * Copies subtitle data from AVFrame to AVSubtitle (deprecated)
+ *
+ * @note This is a compatibility method for conversion to the legacy API
+ */
+int ff_frame_get_subtitle(AVSubtitle *sub, AVFrame *frame)
+{
+    const int64_t duration_ms = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, (AVRational){ 1, 1000 });
+
+    sub->start_display_time = 0;
+    sub->end_display_time = (int32_t)duration_ms;
+    sub->pts = frame->subtitle_timing.start_pts;
+
+    if (frame->num_subtitle_areas) {
+        sub->rects = av_malloc_array(frame->num_subtitle_areas, sizeof(AVSubtitleRect*));
+        if (!sub->rects)
+            return AVERROR(ENOMEM);
+
+        for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+            int ret;
+            sub->rects[i] = av_mallocz(sizeof(AVSubtitleRect));
+            ret = subtitle_area2rect(sub->rects[i], frame->subtitle_areas[i]);
+            if (ret < 0) {
+                sub->num_rects = i;
+                return ret;
+            }
+        }
+    }
+
+    sub->num_rects = frame->num_subtitle_areas;
+    return 0;
+}
+
 int av_get_audio_frame_duration2(AVCodecParameters *par, int frame_bytes)
 {
    int channels = par->ch_layout.nb_channels;
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v5 04/25] avcodec/libzvbi: set subtitle type
  2022-06-25  9:57       ` [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022 ffmpegagent
                           ` (2 preceding siblings ...)
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 03/25] avcodec/subtitles: Introduce new frame-based subtitle decoding API softworkz
@ 2022-06-25  9:57         ` softworkz
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 05/25] avfilter/subtitles: Update vf_subtitles to use new decoding api softworkz
                           ` (23 subsequent siblings)
  27 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-25  9:57 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/libzvbi-teletextdec.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libavcodec/libzvbi-teletextdec.c b/libavcodec/libzvbi-teletextdec.c
index 92466cc11e..2aab10a548 100644
--- a/libavcodec/libzvbi-teletextdec.c
+++ b/libavcodec/libzvbi-teletextdec.c
@@ -751,10 +751,13 @@ static int teletext_init_decoder(AVCodecContext *avctx)
 
     switch (ctx->format_id) {
         case 0:
+            avctx->subtitle_type = AV_SUBTITLE_FMT_BITMAP;
             return 0;
         case 1:
+            avctx->subtitle_type = AV_SUBTITLE_FMT_ASS;
             return ff_ass_subtitle_header_default(avctx);
         case 2:
+            avctx->subtitle_type = AV_SUBTITLE_FMT_ASS;
             return my_ass_subtitle_header(avctx);
     }
     return AVERROR_BUG;
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v5 05/25] avfilter/subtitles: Update vf_subtitles to use new decoding api
  2022-06-25  9:57       ` [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022 ffmpegagent
                           ` (3 preceding siblings ...)
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 04/25] avcodec/libzvbi: set subtitle type softworkz
@ 2022-06-25  9:57         ` softworkz
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 06/25] avcodec, avutil: Move ass helper functions to avutil as avpriv_ and extend ass dialog parsing softworkz
                           ` (22 subsequent siblings)
  27 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-25  9:57 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavfilter/vf_subtitles.c | 67 ++++++++++++++++++++++++++++++--------
 1 file changed, 54 insertions(+), 13 deletions(-)

diff --git a/libavfilter/vf_subtitles.c b/libavfilter/vf_subtitles.c
index 82e140e986..0ae156ad07 100644
--- a/libavfilter/vf_subtitles.c
+++ b/libavfilter/vf_subtitles.c
@@ -36,14 +36,12 @@
 # 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"
 
 typedef struct AssContext {
     const AVClass *class;
@@ -304,8 +302,42 @@ static int attachment_is_font(AVStream * st)
     return 0;
 }
 
+static int decode(AVCodecContext *avctx, AVFrame *frame, int *got_frame, AVPacket *pkt)
+{
+    int ret;
+
+    *got_frame = 0;
+
+    if (pkt) {
+        ret = avcodec_send_packet(avctx, pkt);
+        // In particular, we don't expect AVERROR(EAGAIN), because we read all
+        // decoded frames with avcodec_receive_frame() until done.
+        if (ret < 0 && ret != AVERROR_EOF)
+            return ret;
+    }
+
+    ret = avcodec_receive_frame(avctx, frame);
+    if (ret < 0 && ret != AVERROR(EAGAIN))
+        return ret;
+    if (ret >= 0)
+        *got_frame = 1;
+
+    return 0;
+}
+
 AVFILTER_DEFINE_CLASS(subtitles);
 
+static enum AVSubtitleType get_subtitle_format(const AVCodecDescriptor *codec_descriptor)
+{
+    if(codec_descriptor->props & AV_CODEC_PROP_BITMAP_SUB)
+        return AV_SUBTITLE_FMT_BITMAP;
+
+    if(codec_descriptor->props & AV_CODEC_PROP_TEXT_SUB)
+        return AV_SUBTITLE_FMT_ASS;
+
+    return AV_SUBTITLE_FMT_UNKNOWN;
+}
+
 static av_cold int init_subtitles(AVFilterContext *ctx)
 {
     int j, ret, sid;
@@ -318,6 +350,7 @@ static av_cold int init_subtitles(AVFilterContext *ctx)
     AVStream *st;
     AVPacket pkt;
     AssContext *ass = ctx->priv;
+    enum AVSubtitleType subtitle_format;
 
     /* Init libass */
     ret = init(ctx);
@@ -398,13 +431,17 @@ static av_cold int init_subtitles(AVFilterContext *ctx)
         ret = AVERROR_DECODER_NOT_FOUND;
         goto end;
     }
+
     dec_desc = avcodec_descriptor_get(st->codecpar->codec_id);
-    if (dec_desc && !(dec_desc->props & AV_CODEC_PROP_TEXT_SUB)) {
+    subtitle_format = get_subtitle_format(dec_desc);
+
+    if (subtitle_format != AV_SUBTITLE_FMT_ASS) {
         av_log(ctx, AV_LOG_ERROR,
-               "Only text based subtitles are currently supported\n");
-        ret = AVERROR_PATCHWELCOME;
+               "Only text based subtitles are supported by this filter\n");
+        ret = AVERROR_INVALIDDATA;
         goto end;
     }
+
     if (ass->charenc)
         av_dict_set(&codec_opts, "sub_charenc", ass->charenc, 0);
 
@@ -460,27 +497,31 @@ static av_cold int init_subtitles(AVFilterContext *ctx)
                                   dec_ctx->subtitle_header_size);
     while (av_read_frame(fmt, &pkt) >= 0) {
         int i, got_subtitle;
-        AVSubtitle sub = {0};
+        AVFrame *sub = av_frame_alloc();
+        if (!sub) {
+            ret = AVERROR(ENOMEM);
+            goto end;
+        }
 
         if (pkt.stream_index == sid) {
-            ret = avcodec_decode_subtitle2(dec_ctx, &sub, &got_subtitle, &pkt);
+            ret = decode(dec_ctx, sub, &got_subtitle, &pkt);
             if (ret < 0) {
                 av_log(ctx, AV_LOG_WARNING, "Error decoding: %s (ignored)\n",
                        av_err2str(ret));
             } else if (got_subtitle) {
-                const int64_t start_time = av_rescale_q(sub.pts, AV_TIME_BASE_Q, av_make_q(1, 1000));
-                const int64_t duration   = sub.end_display_time;
-                for (i = 0; i < sub.num_rects; i++) {
-                    char *ass_line = sub.rects[i]->ass;
+                const int64_t start_time = av_rescale_q(sub->subtitle_timing.start_pts, AV_TIME_BASE_Q, av_make_q(1, 1000));
+                const int64_t duration   = av_rescale_q(sub->subtitle_timing.duration, AV_TIME_BASE_Q, av_make_q(1, 1000));
+                for (i = 0; i < sub->num_subtitle_areas; i++) {
+                    char *ass_line = sub->subtitle_areas[i]->ass;
                     if (!ass_line)
-                        break;
+                        continue;
                     ass_process_chunk(ass->track, ass_line, strlen(ass_line),
                                       start_time, duration);
                 }
             }
         }
         av_packet_unref(&pkt);
-        avsubtitle_free(&sub);
+        av_frame_free(&sub);
     }
 
 end:
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v5 06/25] avcodec, avutil: Move ass helper functions to avutil as avpriv_ and extend ass dialog parsing
  2022-06-25  9:57       ` [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022 ffmpegagent
                           ` (4 preceding siblings ...)
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 05/25] avfilter/subtitles: Update vf_subtitles to use new decoding api softworkz
@ 2022-06-25  9:57         ` softworkz
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 07/25] avcodec/subtitles: Replace deprecated enum values softworkz
                           ` (21 subsequent siblings)
  27 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-25  9:57 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Also add

- hard_space callback (for upcoming fix)
- extensible callback (for future extension)
- new API which allows tag filtering

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/Makefile                   |  56 +++---
 libavcodec/ass.h                      | 151 +++++----------
 libavcodec/ass_split.h                | 191 -------------------
 libavcodec/assdec.c                   |   2 +-
 libavcodec/assenc.c                   |   2 +-
 libavcodec/ccaption_dec.c             |  20 +-
 libavcodec/jacosubdec.c               |   2 +-
 libavcodec/libaribb24.c               |   2 +-
 libavcodec/libzvbi-teletextdec.c      |  14 +-
 libavcodec/microdvddec.c              |   7 +-
 libavcodec/movtextdec.c               |   3 +-
 libavcodec/movtextenc.c               |  20 +-
 libavcodec/mpl2dec.c                  |   2 +-
 libavcodec/realtextdec.c              |   2 +-
 libavcodec/samidec.c                  |   2 +-
 libavcodec/srtdec.c                   |   2 +-
 libavcodec/srtenc.c                   |  16 +-
 libavcodec/subviewerdec.c             |   2 +-
 libavcodec/textdec.c                  |   4 +-
 libavcodec/ttmlenc.c                  |  15 +-
 libavcodec/webvttdec.c                |   2 +-
 libavcodec/webvttenc.c                |  16 +-
 libavutil/Makefile                    |   2 +
 {libavcodec => libavutil}/ass.c       | 115 ++++--------
 libavutil/ass_internal.h              | 135 ++++++++++++++
 {libavcodec => libavutil}/ass_split.c | 179 +++++++++++++++---
 libavutil/ass_split_internal.h        | 254 ++++++++++++++++++++++++++
 27 files changed, 726 insertions(+), 492 deletions(-)
 delete mode 100644 libavcodec/ass_split.h
 rename {libavcodec => libavutil}/ass.c (59%)
 create mode 100644 libavutil/ass_internal.h
 rename {libavcodec => libavutil}/ass_split.c (71%)
 create mode 100644 libavutil/ass_split_internal.h

diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 3b8f7b5e01..4bfc90b6e9 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -221,10 +221,10 @@ OBJS-$(CONFIG_APNG_DECODER)            += png.o pngdec.o pngdsp.o
 OBJS-$(CONFIG_APNG_ENCODER)            += png.o pngenc.o
 OBJS-$(CONFIG_ARBC_DECODER)            += arbc.o
 OBJS-$(CONFIG_ARGO_DECODER)            += argo.o
-OBJS-$(CONFIG_SSA_DECODER)             += assdec.o ass.o
-OBJS-$(CONFIG_SSA_ENCODER)             += assenc.o ass.o
-OBJS-$(CONFIG_ASS_DECODER)             += assdec.o ass.o
-OBJS-$(CONFIG_ASS_ENCODER)             += assenc.o ass.o
+OBJS-$(CONFIG_SSA_DECODER)             += assdec.o
+OBJS-$(CONFIG_SSA_ENCODER)             += assenc.o
+OBJS-$(CONFIG_ASS_DECODER)             += assdec.o
+OBJS-$(CONFIG_ASS_ENCODER)             += assenc.o
 OBJS-$(CONFIG_ASV1_DECODER)            += asvdec.o asv.o mpeg12data.o
 OBJS-$(CONFIG_ASV1_ENCODER)            += asvenc.o asv.o mpeg12data.o
 OBJS-$(CONFIG_ASV2_DECODER)            += asvdec.o asv.o mpeg12data.o
@@ -265,7 +265,7 @@ OBJS-$(CONFIG_BRENDER_PIX_DECODER)     += brenderpix.o
 OBJS-$(CONFIG_C93_DECODER)             += c93.o
 OBJS-$(CONFIG_CAVS_DECODER)            += cavs.o cavsdec.o cavsdsp.o \
                                           cavsdata.o
-OBJS-$(CONFIG_CCAPTION_DECODER)        += ccaption_dec.o ass.o
+OBJS-$(CONFIG_CCAPTION_DECODER)        += ccaption_dec.o
 OBJS-$(CONFIG_CDGRAPHICS_DECODER)      += cdgraphics.o
 OBJS-$(CONFIG_CDTOONS_DECODER)         += cdtoons.o
 OBJS-$(CONFIG_CDXL_DECODER)            += cdxl.o
@@ -442,7 +442,7 @@ OBJS-$(CONFIG_INTERPLAY_ACM_DECODER)   += interplayacm.o
 OBJS-$(CONFIG_INTERPLAY_DPCM_DECODER)  += dpcm.o
 OBJS-$(CONFIG_INTERPLAY_VIDEO_DECODER) += interplayvideo.o
 OBJS-$(CONFIG_IPU_DECODER)             += mpeg12dec.o mpeg12.o mpeg12data.o
-OBJS-$(CONFIG_JACOSUB_DECODER)         += jacosubdec.o ass.o
+OBJS-$(CONFIG_JACOSUB_DECODER)         += jacosubdec.o
 OBJS-$(CONFIG_JPEG2000_ENCODER)        += j2kenc.o mqcenc.o mqc.o jpeg2000.o \
                                           jpeg2000dwt.o
 OBJS-$(CONFIG_JPEG2000_DECODER)        += jpeg2000dec.o jpeg2000.o jpeg2000dsp.o \
@@ -464,7 +464,7 @@ OBJS-$(CONFIG_MAGICYUV_ENCODER)        += magicyuvenc.o
 OBJS-$(CONFIG_MDEC_DECODER)            += mdec.o mpeg12.o mpeg12data.o
 OBJS-$(CONFIG_METASOUND_DECODER)       += metasound.o metasound_data.o \
                                           twinvq.o
-OBJS-$(CONFIG_MICRODVD_DECODER)        += microdvddec.o ass.o
+OBJS-$(CONFIG_MICRODVD_DECODER)        += microdvddec.o
 OBJS-$(CONFIG_MIMIC_DECODER)           += mimic.o
 OBJS-$(CONFIG_MJPEG_DECODER)           += mjpegdec.o mjpegdec_common.o
 OBJS-$(CONFIG_MJPEG_QSV_DECODER)       += qsvdec.o
@@ -479,8 +479,8 @@ OBJS-$(CONFIG_MLP_ENCODER)             += mlpenc.o mlp.o
 OBJS-$(CONFIG_MMVIDEO_DECODER)         += mmvideo.o
 OBJS-$(CONFIG_MOBICLIP_DECODER)        += mobiclip.o
 OBJS-$(CONFIG_MOTIONPIXELS_DECODER)    += motionpixels.o
-OBJS-$(CONFIG_MOVTEXT_DECODER)         += movtextdec.o ass.o
-OBJS-$(CONFIG_MOVTEXT_ENCODER)         += movtextenc.o ass_split.o
+OBJS-$(CONFIG_MOVTEXT_DECODER)         += movtextdec.o
+OBJS-$(CONFIG_MOVTEXT_ENCODER)         += movtextenc.o
 OBJS-$(CONFIG_MP1_DECODER)             += mpegaudiodec_fixed.o
 OBJS-$(CONFIG_MP1FLOAT_DECODER)        += mpegaudiodec_float.o
 OBJS-$(CONFIG_MP2_DECODER)             += mpegaudiodec_fixed.o
@@ -521,7 +521,7 @@ OBJS-$(CONFIG_MPEG4_MEDIACODEC_DECODER) += mediacodecdec.o
 OBJS-$(CONFIG_MPEG4_OMX_ENCODER)       += omx.o
 OBJS-$(CONFIG_MPEG4_V4L2M2M_DECODER)   += v4l2_m2m_dec.o
 OBJS-$(CONFIG_MPEG4_V4L2M2M_ENCODER)   += v4l2_m2m_enc.o
-OBJS-$(CONFIG_MPL2_DECODER)            += mpl2dec.o ass.o
+OBJS-$(CONFIG_MPL2_DECODER)            += mpl2dec.o
 OBJS-$(CONFIG_MSA1_DECODER)            += mss3.o
 OBJS-$(CONFIG_MSCC_DECODER)            += mscc.o
 OBJS-$(CONFIG_MSMPEG4V1_DECODER)       += msmpeg4dec.o msmpeg4.o msmpeg4data.o
@@ -574,7 +574,7 @@ OBJS-$(CONFIG_PGX_DECODER)             += pgxdec.o
 OBJS-$(CONFIG_PHOTOCD_DECODER)         += photocd.o
 OBJS-$(CONFIG_PICTOR_DECODER)          += pictordec.o cga_data.o
 OBJS-$(CONFIG_PIXLET_DECODER)          += pixlet.o
-OBJS-$(CONFIG_PJS_DECODER)             += textdec.o ass.o
+OBJS-$(CONFIG_PJS_DECODER)             += textdec.o
 OBJS-$(CONFIG_PNG_DECODER)             += png.o pngdec.o pngdsp.o
 OBJS-$(CONFIG_PNG_ENCODER)             += png.o pngenc.o
 OBJS-$(CONFIG_PPM_DECODER)             += pnmdec.o pnm.o
@@ -609,7 +609,7 @@ OBJS-$(CONFIG_RALF_DECODER)            += ralf.o
 OBJS-$(CONFIG_RASC_DECODER)            += rasc.o
 OBJS-$(CONFIG_RAWVIDEO_DECODER)        += rawdec.o
 OBJS-$(CONFIG_RAWVIDEO_ENCODER)        += rawenc.o
-OBJS-$(CONFIG_REALTEXT_DECODER)        += realtextdec.o ass.o
+OBJS-$(CONFIG_REALTEXT_DECODER)        += realtextdec.o
 OBJS-$(CONFIG_RL2_DECODER)             += rl2.o
 OBJS-$(CONFIG_ROQ_DECODER)             += roqvideodec.o roqvideo.o
 OBJS-$(CONFIG_ROQ_ENCODER)             += roqvideoenc.o roqvideo.o elbg.o
@@ -624,7 +624,7 @@ OBJS-$(CONFIG_RV20_DECODER)            += rv10.o
 OBJS-$(CONFIG_RV20_ENCODER)            += rv20enc.o
 OBJS-$(CONFIG_RV30_DECODER)            += rv30.o rv34.o rv30dsp.o
 OBJS-$(CONFIG_RV40_DECODER)            += rv40.o rv34.o rv40dsp.o
-OBJS-$(CONFIG_SAMI_DECODER)            += samidec.o ass.o htmlsubtitles.o
+OBJS-$(CONFIG_SAMI_DECODER)            += samidec.o htmlsubtitles.o
 OBJS-$(CONFIG_S302M_DECODER)           += s302m.o
 OBJS-$(CONFIG_S302M_ENCODER)           += s302menc.o
 OBJS-$(CONFIG_SANM_DECODER)            += sanm.o
@@ -659,13 +659,13 @@ OBJS-$(CONFIG_SPEEDHQ_ENCODER)         += speedhq.o mpeg12data.o mpeg12enc.o spe
 OBJS-$(CONFIG_SPEEX_DECODER)           += speexdec.o
 OBJS-$(CONFIG_SP5X_DECODER)            += sp5xdec.o
 OBJS-$(CONFIG_SRGC_DECODER)            += mscc.o
-OBJS-$(CONFIG_SRT_DECODER)             += srtdec.o ass.o htmlsubtitles.o
-OBJS-$(CONFIG_SRT_ENCODER)             += srtenc.o ass_split.o
-OBJS-$(CONFIG_STL_DECODER)             += textdec.o ass.o
-OBJS-$(CONFIG_SUBRIP_DECODER)          += srtdec.o ass.o htmlsubtitles.o
-OBJS-$(CONFIG_SUBRIP_ENCODER)          += srtenc.o ass_split.o
-OBJS-$(CONFIG_SUBVIEWER1_DECODER)      += textdec.o ass.o
-OBJS-$(CONFIG_SUBVIEWER_DECODER)       += subviewerdec.o ass.o
+OBJS-$(CONFIG_SRT_DECODER)             += srtdec.o htmlsubtitles.o
+OBJS-$(CONFIG_SRT_ENCODER)             += srtenc.o
+OBJS-$(CONFIG_STL_DECODER)             += textdec.o
+OBJS-$(CONFIG_SUBRIP_DECODER)          += srtdec.o htmlsubtitles.o
+OBJS-$(CONFIG_SUBRIP_ENCODER)          += srtenc.o
+OBJS-$(CONFIG_SUBVIEWER1_DECODER)      += textdec.o
+OBJS-$(CONFIG_SUBVIEWER_DECODER)       += subviewerdec.o
 OBJS-$(CONFIG_SUNRAST_DECODER)         += sunrast.o
 OBJS-$(CONFIG_SUNRAST_ENCODER)         += sunrastenc.o
 OBJS-$(CONFIG_LIBRSVG_DECODER)         += librsvgdec.o
@@ -675,8 +675,8 @@ OBJS-$(CONFIG_SVQ1_DECODER)            += svq1dec.o svq1.o h263data.o
 OBJS-$(CONFIG_SVQ1_ENCODER)            += svq1enc.o svq1.o  h263data.o  \
                                           h263.o ituh263enc.o
 OBJS-$(CONFIG_SVQ3_DECODER)            += svq3.o mpegutils.o h264data.o
-OBJS-$(CONFIG_TEXT_DECODER)            += textdec.o ass.o
-OBJS-$(CONFIG_TEXT_ENCODER)            += srtenc.o ass_split.o
+OBJS-$(CONFIG_TEXT_DECODER)            += textdec.o
+OBJS-$(CONFIG_TEXT_ENCODER)            += srtenc.o
 OBJS-$(CONFIG_TAK_DECODER)             += takdec.o tak.o takdsp.o
 OBJS-$(CONFIG_TARGA_DECODER)           += targa.o
 OBJS-$(CONFIG_TARGA_ENCODER)           += targaenc.o rle.o
@@ -696,7 +696,7 @@ OBJS-$(CONFIG_TSCC_DECODER)            += tscc.o msrledec.o
 OBJS-$(CONFIG_TSCC2_DECODER)           += tscc2.o
 OBJS-$(CONFIG_TTA_DECODER)             += tta.o ttadata.o ttadsp.o
 OBJS-$(CONFIG_TTA_ENCODER)             += ttaenc.o ttaencdsp.o ttadata.o
-OBJS-$(CONFIG_TTML_ENCODER)            += ttmlenc.o ass_split.o
+OBJS-$(CONFIG_TTML_ENCODER)            += ttmlenc.o
 OBJS-$(CONFIG_TWINVQ_DECODER)          += twinvqdec.o twinvq.o metasound_data.o
 OBJS-$(CONFIG_TXD_DECODER)             += txd.o
 OBJS-$(CONFIG_ULTI_DECODER)            += ulti.o
@@ -753,15 +753,15 @@ OBJS-$(CONFIG_VP9_MEDIACODEC_DECODER)  += mediacodecdec.o
 OBJS-$(CONFIG_VP9_RKMPP_DECODER)       += rkmppdec.o
 OBJS-$(CONFIG_VP9_VAAPI_ENCODER)       += vaapi_encode_vp9.o
 OBJS-$(CONFIG_VP9_QSV_ENCODER)         += qsvenc_vp9.o
-OBJS-$(CONFIG_VPLAYER_DECODER)         += textdec.o ass.o
+OBJS-$(CONFIG_VPLAYER_DECODER)         += textdec.o
 OBJS-$(CONFIG_VP9_V4L2M2M_DECODER)     += v4l2_m2m_dec.o
 OBJS-$(CONFIG_VQA_DECODER)             += vqavideo.o
 OBJS-$(CONFIG_WAVPACK_DECODER)         += wavpack.o wavpackdata.o dsd.o
 OBJS-$(CONFIG_WAVPACK_ENCODER)         += wavpackdata.o wavpackenc.o
 OBJS-$(CONFIG_WCMV_DECODER)            += wcmv.o
 OBJS-$(CONFIG_WEBP_DECODER)            += webp.o
-OBJS-$(CONFIG_WEBVTT_DECODER)          += webvttdec.o ass.o
-OBJS-$(CONFIG_WEBVTT_ENCODER)          += webvttenc.o ass_split.o
+OBJS-$(CONFIG_WEBVTT_DECODER)          += webvttdec.o
+OBJS-$(CONFIG_WEBVTT_ENCODER)          += webvttenc.o
 OBJS-$(CONFIG_WMALOSSLESS_DECODER)     += wmalosslessdec.o wma_common.o
 OBJS-$(CONFIG_WMAPRO_DECODER)          += wmaprodec.o wma.o wma_common.o
 OBJS-$(CONFIG_WMAV1_DECODER)           += wmadec.o wma.o wma_common.o aactab.o
@@ -1051,7 +1051,7 @@ OBJS-$(CONFIG_PCM_ALAW_AT_ENCODER)        += audiotoolboxenc.o
 OBJS-$(CONFIG_PCM_MULAW_AT_ENCODER)       += audiotoolboxenc.o
 OBJS-$(CONFIG_LIBAOM_AV1_DECODER)         += libaomdec.o
 OBJS-$(CONFIG_LIBAOM_AV1_ENCODER)         += libaomenc.o
-OBJS-$(CONFIG_LIBARIBB24_DECODER)         += libaribb24.o ass.o
+OBJS-$(CONFIG_LIBARIBB24_DECODER)         += libaribb24.o
 OBJS-$(CONFIG_LIBCELT_DECODER)            += libcelt_dec.o
 OBJS-$(CONFIG_LIBCODEC2_DECODER)          += libcodec2.o
 OBJS-$(CONFIG_LIBCODEC2_ENCODER)          += libcodec2.o
@@ -1104,7 +1104,7 @@ OBJS-$(CONFIG_LIBX265_ENCODER)            += libx265.o
 OBJS-$(CONFIG_LIBXAVS_ENCODER)            += libxavs.o
 OBJS-$(CONFIG_LIBXAVS2_ENCODER)           += libxavs2.o
 OBJS-$(CONFIG_LIBXVID_ENCODER)            += libxvid.o
-OBJS-$(CONFIG_LIBZVBI_TELETEXT_DECODER)   += libzvbi-teletextdec.o ass.o
+OBJS-$(CONFIG_LIBZVBI_TELETEXT_DECODER)   += libzvbi-teletextdec.o
 
 # parsers
 OBJS-$(CONFIG_AAC_LATM_PARSER)         += latm_parser.o
diff --git a/libavcodec/ass.h b/libavcodec/ass.h
index 4dffe923d9..8bc13d7ab8 100644
--- a/libavcodec/ass.h
+++ b/libavcodec/ass.h
@@ -23,124 +23,73 @@
 #define AVCODEC_ASS_H
 
 #include "avcodec.h"
-#include "libavutil/bprint.h"
-
-#define ASS_DEFAULT_PLAYRESX 384
-#define ASS_DEFAULT_PLAYRESY 288
-
-/**
- * @name Default values for ASS style
- * @{
- */
-#define ASS_DEFAULT_FONT        "Arial"
-#define ASS_DEFAULT_FONT_SIZE   16
-#define ASS_DEFAULT_COLOR       0xffffff
-#define ASS_DEFAULT_BACK_COLOR  0
-#define ASS_DEFAULT_BOLD        0
-#define ASS_DEFAULT_ITALIC      0
-#define ASS_DEFAULT_UNDERLINE   0
-#define ASS_DEFAULT_ALIGNMENT   2
-#define ASS_DEFAULT_BORDERSTYLE 1
-/** @} */
+#include "libavutil/ass_internal.h"
 
 typedef struct FFASSDecoderContext {
     int readorder;
 } FFASSDecoderContext;
 
-/**
- * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS.
- * Can specify all fields explicitly
- *
- * @param avctx pointer to the AVCodecContext
- * @param play_res_x subtitle frame width
- * @param play_res_y subtitle frame height
- * @param font name of the default font face to use
- * @param font_size default font size to use
- * @param primary_color default text color to use (ABGR)
- * @param secondary_color default secondary text color to use (ABGR)
- * @param outline_color default outline color to use (ABGR)
- * @param back_color default background color to use (ABGR)
- * @param bold 1 for bold text, 0 for normal text
- * @param italic 1 for italic text, 0 for normal text
- * @param underline 1 for underline text, 0 for normal text
- * @param border_style 1 for outline, 3 for opaque box
- * @param alignment position of the text (left, center, top...), defined after
- *                  the layout of the numpad (1-3 sub, 4-6 mid, 7-9 top)
- * @return >= 0 on success otherwise an error code <0
- */
-int ff_ass_subtitle_header_full(AVCodecContext *avctx,
+static inline int ff_ass_subtitle_header_full(AVCodecContext *avctx,
                                 int play_res_x, int play_res_y,
                                 const char *font, int font_size,
                                 int primary_color, int secondary_color,
                                 int outline_color, int back_color,
                                 int bold, int italic, int underline,
-                                int border_style, int alignment);
-/**
- * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS.
- *
- * @param avctx pointer to the AVCodecContext
- * @param font name of the default font face to use
- * @param font_size default font size to use
- * @param color default text color to use (ABGR)
- * @param back_color default background color to use (ABGR)
- * @param bold 1 for bold text, 0 for normal text
- * @param italic 1 for italic text, 0 for normal text
- * @param underline 1 for underline text, 0 for normal text
- * @param alignment position of the text (left, center, top...), defined after
- *                  the layout of the numpad (1-3 sub, 4-6 mid, 7-9 top)
- * @return >= 0 on success otherwise an error code <0
- */
-int ff_ass_subtitle_header(AVCodecContext *avctx,
-                           const char *font, int font_size,
-                           int color, int back_color,
-                           int bold, int italic, int underline,
-                           int border_style, int alignment);
+                                int border_style, int alignment)
+{
+    avctx->subtitle_header = (uint8_t *)avpriv_ass_get_subtitle_header_full(
+                                play_res_x, play_res_y, font, font_size,
+                                primary_color, secondary_color, outline_color,
+                                back_color, bold,italic,underline,border_style,alignment,
+                                !(avctx->flags & AV_CODEC_FLAG_BITEXACT));
 
-/**
- * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS
- * with default style.
- *
- * @param avctx pointer to the AVCodecContext
- * @return >= 0 on success otherwise an error code <0
- */
-int ff_ass_subtitle_header_default(AVCodecContext *avctx);
+    if (!avctx->subtitle_header)
+        return AVERROR(ENOMEM);
+    avctx->subtitle_header_size = strlen((char *)avctx->subtitle_header);
+    return 0;
+}
 
-/**
- * Craft an ASS dialog string.
- */
-char *ff_ass_get_dialog(int readorder, int layer, const char *style,
-                        const char *speaker, const char *text);
+static inline int ff_ass_subtitle_header_default(AVCodecContext *avctx)
+{
+    avctx->subtitle_header = (uint8_t *)avpriv_ass_get_subtitle_header_default(!(avctx->flags & AV_CODEC_FLAG_BITEXACT));
 
-/**
- * Add an ASS dialog to a subtitle.
- */
-int ff_ass_add_rect(AVSubtitle *sub, const char *dialog,
-                    int readorder, int layer, const char *style,
-                    const char *speaker);
+    if (!avctx->subtitle_header)
+        return AVERROR(ENOMEM);
+    avctx->subtitle_header_size = strlen((char *)avctx->subtitle_header);
+    return 0;
+}
+
+static inline void ff_ass_decoder_flush(AVCodecContext *avctx)
+{
+    FFASSDecoderContext *s = avctx->priv_data;
+    if (!(avctx->flags2 & AV_CODEC_FLAG2_RO_FLUSH_NOOP))
+        s->readorder = 0;
+}
 
 /**
  * Add an ASS dialog to a subtitle.
  */
-int ff_ass_add_rect2(AVSubtitle *sub, const char *dialog,
-                     int readorder, int layer, const char *style,
-                     const char *speaker, unsigned *nb_rect_allocated);
+static inline int avpriv_ass_add_rect(AVSubtitle *sub, const char *dialog,
+                    int readorder, int layer, const char *style,
+                    const char *speaker)
+{
+    char *ass_str;
+    AVSubtitleRect **rects;
 
-/**
- * Helper to flush a text subtitles decoder making use of the
- * FFASSDecoderContext.
- */
-void ff_ass_decoder_flush(AVCodecContext *avctx);
+    rects = av_realloc_array(sub->rects, sub->num_rects+1, sizeof(*sub->rects));
+    if (!rects)
+        return AVERROR(ENOMEM);
+    sub->rects = rects;
+    rects[sub->num_rects]       = av_mallocz(sizeof(*rects[0]));
+    if (!rects[sub->num_rects])
+        return AVERROR(ENOMEM);
+    rects[sub->num_rects]->type = SUBTITLE_ASS;
+    ass_str = avpriv_ass_get_dialog(readorder, layer, style, speaker, dialog);
+    if (!ass_str)
+        return AVERROR(ENOMEM);
+    rects[sub->num_rects]->ass = ass_str;
+    sub->num_rects++;
+    return 0;
+}
 
-/**
- * Escape a text subtitle using ASS syntax into an AVBPrint buffer.
- * Newline characters will be escaped to \N.
- *
- * @param buf pointer to an initialized AVBPrint buffer
- * @param p source text
- * @param size size of the source text
- * @param linebreaks additional newline chars, which will be escaped to \N
- * @param keep_ass_markup braces and backslash will not be escaped if set
- */
-void ff_ass_bprint_text_event(AVBPrint *buf, const char *p, int size,
-                             const char *linebreaks, int keep_ass_markup);
 #endif /* AVCODEC_ASS_H */
diff --git a/libavcodec/ass_split.h b/libavcodec/ass_split.h
deleted file mode 100644
index a45fb9b8a1..0000000000
--- a/libavcodec/ass_split.h
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * SSA/ASS spliting functions
- * Copyright (c) 2010  Aurelien Jacobs <aurel@gnuage.org>
- *
- * 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
- */
-
-#ifndef AVCODEC_ASS_SPLIT_H
-#define AVCODEC_ASS_SPLIT_H
-
-/**
- * fields extracted from the [Script Info] section
- */
-typedef struct {
-    char *script_type;    /**< SSA script format version (eg. v4.00) */
-    char *collisions;     /**< how subtitles are moved to prevent collisions */
-    int   play_res_x;     /**< video width that ASS coords are referring to */
-    int   play_res_y;     /**< video height that ASS coords are referring to */
-    float timer;          /**< time multiplier to apply to SSA clock (in %) */
-} ASSScriptInfo;
-
-/**
- * fields extracted from the [V4(+) Styles] section
- */
-typedef struct {
-    char *name;           /**< name of the tyle (case sensitive) */
-    char *font_name;      /**< font face (case sensitive) */
-    int   font_size;      /**< font height */
-    int   primary_color;  /**< color that a subtitle will normally appear in */
-    int   secondary_color;
-    int   outline_color;  /**< color for outline in ASS, called tertiary in SSA */
-    int   back_color;     /**< color of the subtitle outline or shadow */
-    int   bold;           /**< whether text is bold (1) or not (0) */
-    int   italic;         /**< whether text is italic (1) or not (0) */
-    int   underline;      /**< whether text is underlined (1) or not (0) */
-    int   strikeout;
-    float scalex;
-    float scaley;
-    float spacing;
-    float angle;
-    int   border_style;
-    float outline;
-    float shadow;
-    int   alignment;      /**< position of the text (left, center, top...),
-                               defined after the layout of the numpad
-                               (1-3 sub, 4-6 mid, 7-9 top) */
-    int   margin_l;
-    int   margin_r;
-    int   margin_v;
-    int   alpha_level;
-    int   encoding;
-} ASSStyle;
-
-/**
- * fields extracted from the [Events] section
- */
-typedef struct {
-    int   readorder;
-    int   layer;    /**< higher numbered layers are drawn over lower numbered */
-    int   start;    /**< start time of the dialog in centiseconds */
-    int   end;      /**< end time of the dialog in centiseconds */
-    char *style;    /**< name of the ASSStyle to use with this dialog */
-    char *name;
-    int   margin_l;
-    int   margin_r;
-    int   margin_v;
-    char *effect;
-    char *text;     /**< actual text which will be displayed as a subtitle,
-                         can include style override control codes (see
-                         ff_ass_split_override_codes()) */
-} ASSDialog;
-
-/**
- * structure containing the whole split ASS data
- */
-typedef struct {
-    ASSScriptInfo script_info;   /**< general information about the SSA script*/
-    ASSStyle     *styles;        /**< array of split out styles */
-    int           styles_count;  /**< number of ASSStyle in the styles array */
-    ASSDialog    *dialogs;       /**< array of split out dialogs */
-    int           dialogs_count; /**< number of ASSDialog in the dialogs array*/
-} ASS;
-
-/**
- * This struct can be casted to ASS to access to the split data.
- */
-typedef struct ASSSplitContext ASSSplitContext;
-
-/**
- * Split a full ASS file or a ASS header from a string buffer and store
- * the split structure in a newly allocated context.
- *
- * @param buf String containing the ASS formatted data.
- * @return Newly allocated struct containing split data.
- */
-ASSSplitContext *ff_ass_split(const char *buf);
-
-/**
- * Free a dialogue obtained from ff_ass_split_dialog().
- */
-void ff_ass_free_dialog(ASSDialog **dialogp);
-
-/**
- * Split one ASS Dialogue line from a string buffer.
- *
- * @param ctx Context previously initialized by ff_ass_split().
- * @param buf String containing the ASS "Dialogue" line.
- * @return Pointer to the split ASSDialog. Must be freed with ff_ass_free_dialog()
- */
-ASSDialog *ff_ass_split_dialog(ASSSplitContext *ctx, const char *buf);
-
-/**
- * Free all the memory allocated for an ASSSplitContext.
- *
- * @param ctx Context previously initialized by ff_ass_split().
- */
-void ff_ass_split_free(ASSSplitContext *ctx);
-
-
-/**
- * Set of callback functions corresponding to each override codes that can
- * be encountered in a "Dialogue" Text field.
- */
-typedef struct {
-    /**
-     * @defgroup ass_styles    ASS styles
-     * @{
-     */
-    void (*text)(void *priv, const char *text, int len);
-    void (*new_line)(void *priv, int forced);
-    void (*style)(void *priv, char style, int close);
-    void (*color)(void *priv, unsigned int /* color */, unsigned int color_id);
-    void (*alpha)(void *priv, int alpha, int alpha_id);
-    void (*font_name)(void *priv, const char *name);
-    void (*font_size)(void *priv, int size);
-    void (*alignment)(void *priv, int alignment);
-    void (*cancel_overrides)(void *priv, const char *style);
-    /** @} */
-
-    /**
-     * @defgroup ass_functions    ASS functions
-     * @{
-     */
-    void (*move)(void *priv, int x1, int y1, int x2, int y2, int t1, int t2);
-    void (*origin)(void *priv, int x, int y);
-    /** @} */
-
-    /**
-     * @defgroup ass_end    end of Dialogue Event
-     * @{
-     */
-    void (*end)(void *priv);
-    /** @} */
-} ASSCodesCallbacks;
-
-/**
- * Split override codes out of a ASS "Dialogue" Text field.
- *
- * @param callbacks Set of callback functions called for each override code
- *                  encountered.
- * @param priv Opaque pointer passed to the callback functions.
- * @param buf The ASS "Dialogue" Text field to split.
- * @return >= 0 on success otherwise an error code <0
- */
-int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
-                                const char *buf);
-
-/**
- * Find an ASSStyle structure by its name.
- *
- * @param ctx Context previously initialized by ff_ass_split().
- * @param style name of the style to search for.
- * @return the ASSStyle corresponding to style, or NULL if style can't be found
- */
-ASSStyle *ff_ass_style_get(ASSSplitContext *ctx, const char *style);
-
-#endif /* AVCODEC_ASS_SPLIT_H */
diff --git a/libavcodec/assdec.c b/libavcodec/assdec.c
index f43b500aa7..1a1363471d 100644
--- a/libavcodec/assdec.c
+++ b/libavcodec/assdec.c
@@ -22,9 +22,9 @@
 #include <string.h>
 
 #include "avcodec.h"
-#include "ass.h"
 #include "codec_internal.h"
 #include "config_components.h"
+#include "libavutil/ass_internal.h"
 #include "libavutil/internal.h"
 #include "libavutil/mem.h"
 
diff --git a/libavcodec/assenc.c b/libavcodec/assenc.c
index 2ac40d5afe..391d690133 100644
--- a/libavcodec/assenc.c
+++ b/libavcodec/assenc.c
@@ -24,8 +24,8 @@
 #include <string.h>
 
 #include "avcodec.h"
-#include "ass.h"
 #include "codec_internal.h"
+#include "libavutil/ass_internal.h"
 #include "libavutil/avstring.h"
 #include "libavutil/internal.h"
 #include "libavutil/mem.h"
diff --git a/libavcodec/ccaption_dec.c b/libavcodec/ccaption_dec.c
index 34f0513b1a..5f706d985f 100644
--- a/libavcodec/ccaption_dec.c
+++ b/libavcodec/ccaption_dec.c
@@ -272,15 +272,12 @@ static av_cold int init_decoder(AVCodecContext *avctx)
     ctx->bg_color = CCCOL_BLACK;
     ctx->rollup = 2;
     ctx->cursor_row = 10;
-    ret = ff_ass_subtitle_header(avctx, "Monospace",
+    ret = ff_ass_subtitle_header_full(avctx, ASS_DEFAULT_PLAYRESX, ASS_DEFAULT_PLAYRESY, "Monospace",
                                  ASS_DEFAULT_FONT_SIZE,
-                                 ASS_DEFAULT_COLOR,
-                                 ASS_DEFAULT_BACK_COLOR,
-                                 ASS_DEFAULT_BOLD,
-                                 ASS_DEFAULT_ITALIC,
-                                 ASS_DEFAULT_UNDERLINE,
-                                 3,
-                                 ASS_DEFAULT_ALIGNMENT);
+                                 ASS_DEFAULT_COLOR, ASS_DEFAULT_COLOR,
+                                 ASS_DEFAULT_BACK_COLOR, ASS_DEFAULT_BACK_COLOR,
+                                 ASS_DEFAULT_BOLD, ASS_DEFAULT_ITALIC, ASS_DEFAULT_UNDERLINE,
+                                 3, ASS_DEFAULT_ALIGNMENT);
     if (ret < 0) {
         return ret;
     }
@@ -850,7 +847,6 @@ static int decode(AVCodecContext *avctx, AVSubtitle *sub,
     int len = avpkt->size;
     int ret = 0;
     int i;
-    unsigned nb_rect_allocated = 0;
 
     for (i = 0; i < len; i += 3) {
         uint8_t hi, cc_type = bptr[i] & 1;
@@ -887,7 +883,7 @@ static int decode(AVCodecContext *avctx, AVSubtitle *sub,
                                                      AV_TIME_BASE_Q, ms_tb);
             else
                 sub->end_display_time = -1;
-            ret = ff_ass_add_rect2(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL, &nb_rect_allocated);
+            ret = avpriv_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL);
             if (ret < 0)
                 return ret;
             ctx->last_real_time = sub->pts;
@@ -897,7 +893,7 @@ static int decode(AVCodecContext *avctx, AVSubtitle *sub,
 
     if (!bptr && !ctx->real_time && ctx->buffer[!ctx->buffer_index].str[0]) {
         bidx = !ctx->buffer_index;
-        ret = ff_ass_add_rect2(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL, &nb_rect_allocated);
+        ret = avpriv_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL);
         if (ret < 0)
             return ret;
         sub->pts = ctx->buffer_time[1];
@@ -915,7 +911,7 @@ static int decode(AVCodecContext *avctx, AVSubtitle *sub,
         capture_screen(ctx);
         ctx->buffer_changed = 0;
 
-        ret = ff_ass_add_rect2(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL, &nb_rect_allocated);
+        ret = avpriv_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL);
         if (ret < 0)
             return ret;
         sub->end_display_time = -1;
diff --git a/libavcodec/jacosubdec.c b/libavcodec/jacosubdec.c
index e3bf9f4226..40abdebcc6 100644
--- a/libavcodec/jacosubdec.c
+++ b/libavcodec/jacosubdec.c
@@ -182,7 +182,7 @@ static int jacosub_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
 
         av_bprint_init(&buffer, JSS_MAX_LINESIZE, JSS_MAX_LINESIZE);
         jacosub_to_ass(avctx, &buffer, ptr);
-        ret = ff_ass_add_rect(sub, buffer.str, s->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, buffer.str, s->readorder++, 0, NULL, NULL);
         av_bprint_finalize(&buffer, NULL);
         if (ret < 0)
             return ret;
diff --git a/libavcodec/libaribb24.c b/libavcodec/libaribb24.c
index 9658e1d5ac..360e20834b 100644
--- a/libavcodec/libaribb24.c
+++ b/libavcodec/libaribb24.c
@@ -274,7 +274,7 @@ next_region:
         av_log(avctx, AV_LOG_DEBUG, "Styled ASS line: %s\n",
                buf.str);
 
-        ret = ff_ass_add_rect(sub, buf.str, b24->read_order++,
+        ret = avpriv_ass_add_rect(sub, buf.str, b24->read_order++,
                               0, NULL, NULL);
     }
 
diff --git a/libavcodec/libzvbi-teletextdec.c b/libavcodec/libzvbi-teletextdec.c
index 2aab10a548..54a78342f2 100644
--- a/libavcodec/libzvbi-teletextdec.c
+++ b/libavcodec/libzvbi-teletextdec.c
@@ -153,12 +153,12 @@ static char *create_ass_text(TeletextContext *ctx, const char *text)
     AVBPrint buf;
 
     av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
-    ff_ass_bprint_text_event(&buf, text, strlen(text), "", 0);
+    avpriv_ass_bprint_text_event(&buf, text, strlen(text), "", 0);
     if (!av_bprint_is_complete(&buf)) {
         av_bprint_finalize(&buf, NULL);
         return NULL;
     }
-    dialog = ff_ass_get_dialog(ctx->readorder++, 0, NULL, NULL, buf.str);
+    dialog = avpriv_ass_get_dialog(ctx->readorder++, 0, NULL, NULL, buf.str);
     av_bprint_finalize(&buf, NULL);
     return dialog;
 }
@@ -225,7 +225,7 @@ static int gen_sub_text(TeletextContext *ctx, AVSubtitleRect *sub_rect, vbi_page
         }
         av_log(ctx, AV_LOG_DEBUG, "subtext:%s:txetbus\n", sub_rect->ass);
     } else {
-        sub_rect->type = SUBTITLE_NONE;
+        sub_rect->type = AV_SUBTITLE_FMT_NONE;
     }
     av_bprint_finalize(&buf, NULL);
     return 0;
@@ -395,7 +395,7 @@ static int gen_sub_ass(TeletextContext *ctx, AVSubtitleRect *sub_rect, vbi_page
 
     if (buf.len) {
         sub_rect->type = SUBTITLE_ASS;
-        sub_rect->ass = ff_ass_get_dialog(ctx->readorder++, 0, is_subtitle_page ? "Subtitle" : "Teletext", NULL, buf.str);
+        sub_rect->ass = avpriv_ass_get_dialog(ctx->readorder++, 0, is_subtitle_page ? "Subtitle" : "Teletext", NULL, buf.str);
 
         if (!sub_rect->ass) {
             av_bprint_finalize(&buf, NULL);
@@ -403,7 +403,7 @@ static int gen_sub_ass(TeletextContext *ctx, AVSubtitleRect *sub_rect, vbi_page
         }
         av_log(ctx, AV_LOG_DEBUG, "subtext:%s:txetbus\n", sub_rect->ass);
     } else {
-        sub_rect->type = SUBTITLE_NONE;
+        sub_rect->type = AV_SUBTITLE_FMT_NONE;
     }
     av_bprint_finalize(&buf, NULL);
     return 0;
@@ -463,7 +463,7 @@ static int gen_sub_bitmap(TeletextContext *ctx, AVSubtitleRect *sub_rect, vbi_pa
 
     if (vc >= vcend) {
         av_log(ctx, AV_LOG_DEBUG, "dropping empty page %3x\n", page->pgno);
-        sub_rect->type = SUBTITLE_NONE;
+        sub_rect->type = AV_SUBTITLE_FMT_NONE;
         return 0;
     }
 
@@ -696,7 +696,7 @@ static int teletext_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
         sub->num_rects = 0;
         sub->pts = ctx->pages->pts;
 
-        if (ctx->pages->sub_rect->type != SUBTITLE_NONE) {
+        if (ctx->pages->sub_rect->type != AV_SUBTITLE_FMT_NONE) {
             sub->rects = av_malloc(sizeof(*sub->rects));
             if (sub->rects) {
                 sub->num_rects = 1;
diff --git a/libavcodec/microdvddec.c b/libavcodec/microdvddec.c
index f36ad51468..de17400edd 100644
--- a/libavcodec/microdvddec.c
+++ b/libavcodec/microdvddec.c
@@ -309,7 +309,7 @@ static int microdvd_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
         }
     }
     if (new_line.len) {
-        int ret = ff_ass_add_rect(sub, new_line.str, s->readorder++, 0, NULL, NULL);
+        int ret = avpriv_ass_add_rect(sub, new_line.str, s->readorder++, 0, NULL, NULL);
         av_bprint_finalize(&new_line, NULL);
         if (ret < 0)
             return ret;
@@ -362,8 +362,9 @@ static int microdvd_init(AVCodecContext *avctx)
             }
         }
     }
-    return ff_ass_subtitle_header(avctx, font_buf.str, font_size, color,
-                                  ASS_DEFAULT_BACK_COLOR, bold, italic,
+    return ff_ass_subtitle_header_full(avctx, ASS_DEFAULT_PLAYRESX, ASS_DEFAULT_PLAYRESY,
+                                  font_buf.str, font_size, color, color,
+                                  ASS_DEFAULT_BACK_COLOR, ASS_DEFAULT_BACK_COLOR, bold, italic,
                                   underline, ASS_DEFAULT_BORDERSTYLE,
                                   alignment);
 }
diff --git a/libavcodec/movtextdec.c b/libavcodec/movtextdec.c
index 70162b4888..8f2459d45b 100644
--- a/libavcodec/movtextdec.c
+++ b/libavcodec/movtextdec.c
@@ -22,7 +22,6 @@
 #include "avcodec.h"
 #include "ass.h"
 #include "libavutil/opt.h"
-#include "libavutil/avstring.h"
 #include "libavutil/common.h"
 #include "libavutil/bprint.h"
 #include "libavutil/intreadwrite.h"
@@ -553,7 +552,7 @@ static int mov_text_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
     } else
         text_to_ass(&buf, ptr, end, avctx);
 
-    ret = ff_ass_add_rect(sub, buf.str, m->readorder++, 0, NULL, NULL);
+    ret = avpriv_ass_add_rect(sub, buf.str, m->readorder++, 0, NULL, NULL);
     av_bprint_finalize(&buf, NULL);
     if (ret < 0)
         return ret;
diff --git a/libavcodec/movtextenc.c b/libavcodec/movtextenc.c
index 728338f2cc..6f0b7a8a5c 100644
--- a/libavcodec/movtextenc.c
+++ b/libavcodec/movtextenc.c
@@ -26,8 +26,8 @@
 #include "libavutil/intreadwrite.h"
 #include "libavutil/mem.h"
 #include "libavutil/common.h"
-#include "ass_split.h"
-#include "ass.h"
+#include "libavutil/ass_split_internal.h"
+#include "libavutil/ass_internal.h"
 #include "bytestream.h"
 #include "codec_internal.h"
 
@@ -167,7 +167,7 @@ static int mov_text_encode_close(AVCodecContext *avctx)
 {
     MovTextContext *s = avctx->priv_data;
 
-    ff_ass_split_free(s->ass_ctx);
+    avpriv_ass_split_free(s->ass_ctx);
     av_freep(&s->style_attributes);
     av_freep(&s->fonts);
     av_bprint_finalize(&s->buffer, NULL);
@@ -222,7 +222,7 @@ static int encode_sample_description(AVCodecContext *avctx)
     else
         s->font_scale_factor = 1;
 
-    style = ff_ass_style_get(s->ass_ctx, "Default");
+    style = avpriv_ass_style_get(s->ass_ctx, "Default");
     if (!style && ass->styles_count) {
         style = &ass->styles[0];
     }
@@ -329,7 +329,7 @@ static av_cold int mov_text_encode_init(AVCodecContext *avctx)
 
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
 
-    s->ass_ctx = ff_ass_split(avctx->subtitle_header);
+    s->ass_ctx = avpriv_ass_split(avctx->subtitle_header);
     if (!s->ass_ctx)
         return AVERROR_INVALIDDATA;
     ret = encode_sample_description(avctx);
@@ -566,7 +566,7 @@ static void mov_text_ass_style_set(MovTextContext *s, ASSStyle *style)
 
 static void mov_text_dialog(MovTextContext *s, ASSDialog *dialog)
 {
-    ASSStyle *style = ff_ass_style_get(s->ass_ctx, dialog->style);
+    ASSStyle *style = avpriv_ass_style_get(s->ass_ctx, dialog->style);
 
     s->ass_dialog_style = style;
     mov_text_ass_style_set(s, style);
@@ -580,7 +580,7 @@ static void mov_text_cancel_overrides_cb(void *priv, const char *style_name)
     if (!style_name || !*style_name)
         style = s->ass_dialog_style;
     else
-        style= ff_ass_style_get(s->ass_ctx, style_name);
+        style= avpriv_ass_style_get(s->ass_ctx, style_name);
 
     mov_text_ass_style_set(s, style);
 }
@@ -652,12 +652,12 @@ static int mov_text_encode_frame(AVCodecContext *avctx, unsigned char *buf,
             return AVERROR(EINVAL);
         }
 
-        dialog = ff_ass_split_dialog(s->ass_ctx, ass);
+        dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
         if (!dialog)
             return AVERROR(ENOMEM);
         mov_text_dialog(s, dialog);
-        ff_ass_split_override_codes(&mov_text_callbacks, s, dialog->text);
-        ff_ass_free_dialog(&dialog);
+        avpriv_ass_split_override_codes(&mov_text_callbacks, s, dialog->text);
+        avpriv_ass_free_dialog(&dialog);
     }
 
     if (s->buffer.len > UINT16_MAX)
diff --git a/libavcodec/mpl2dec.c b/libavcodec/mpl2dec.c
index 56f008b65c..175bd319e1 100644
--- a/libavcodec/mpl2dec.c
+++ b/libavcodec/mpl2dec.c
@@ -73,7 +73,7 @@ static int mpl2_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
 
     av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
     if (ptr && avpkt->size > 0 && *ptr && !mpl2_event_to_ass(&buf, ptr))
-        ret = ff_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
     av_bprint_finalize(&buf, NULL);
     if (ret < 0)
         return ret;
diff --git a/libavcodec/realtextdec.c b/libavcodec/realtextdec.c
index c3e138a7ba..49d42a1d4d 100644
--- a/libavcodec/realtextdec.c
+++ b/libavcodec/realtextdec.c
@@ -66,7 +66,7 @@ static int realtext_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
 
     av_bprint_init(&buf, 0, 4096);
     if (ptr && avpkt->size > 0 && !rt_event_to_ass(&buf, ptr))
-        ret = ff_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
     av_bprint_finalize(&buf, NULL);
     if (ret < 0)
         return ret;
diff --git a/libavcodec/samidec.c b/libavcodec/samidec.c
index cf5dec955b..f35d312c2b 100644
--- a/libavcodec/samidec.c
+++ b/libavcodec/samidec.c
@@ -143,7 +143,7 @@ static int sami_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
         if (ret < 0)
             return ret;
         // TODO: pass escaped sami->encoded_source.str as source
-        ret = ff_ass_add_rect(sub, sami->full.str, sami->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, sami->full.str, sami->readorder++, 0, NULL, NULL);
         if (ret < 0)
             return ret;
     }
diff --git a/libavcodec/srtdec.c b/libavcodec/srtdec.c
index b2df34474e..ccf5981263 100644
--- a/libavcodec/srtdec.c
+++ b/libavcodec/srtdec.c
@@ -79,7 +79,7 @@ static int srt_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
 
     ret = srt_to_ass(avctx, &buffer, avpkt->data, x1, y1, x2, y2);
     if (ret >= 0)
-        ret = ff_ass_add_rect(sub, buffer.str, s->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, buffer.str, s->readorder++, 0, NULL, NULL);
     av_bprint_finalize(&buffer, NULL);
     if (ret < 0)
         return ret;
diff --git a/libavcodec/srtenc.c b/libavcodec/srtenc.c
index 51456c8b9d..2baa6e70ad 100644
--- a/libavcodec/srtenc.c
+++ b/libavcodec/srtenc.c
@@ -25,9 +25,9 @@
 #include "avcodec.h"
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
-#include "ass_split.h"
-#include "ass.h"
 #include "codec_internal.h"
+#include "libavutil/ass_split_internal.h"
+#include "libavutil/ass_internal.h"
 
 
 #define SRT_STACK_SIZE 64
@@ -96,7 +96,7 @@ static void srt_stack_push_pop(SRTContext *s, const char c, int close)
 
 static void srt_style_apply(SRTContext *s, const char *style)
 {
-    ASSStyle *st = ff_ass_style_get(s->ass_ctx, style);
+    ASSStyle *st = avpriv_ass_style_get(s->ass_ctx, style);
     if (st) {
         int c = st->primary_color & 0xFFFFFF;
         if (st->font_name && strcmp(st->font_name, ASS_DEFAULT_FONT) ||
@@ -137,7 +137,7 @@ static av_cold int srt_encode_init(AVCodecContext *avctx)
 {
     SRTContext *s = avctx->priv_data;
     s->avctx = avctx;
-    s->ass_ctx = ff_ass_split(avctx->subtitle_header);
+    s->ass_ctx = avpriv_ass_split(avctx->subtitle_header);
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
     return s->ass_ctx ? 0 : AVERROR_INVALIDDATA;
 }
@@ -247,14 +247,14 @@ static int encode_frame(AVCodecContext *avctx,
             return AVERROR(EINVAL);
         }
 
-        dialog = ff_ass_split_dialog(s->ass_ctx, ass);
+        dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
         if (!dialog)
             return AVERROR(ENOMEM);
         s->alignment_applied = 0;
         if (avctx->codec_id == AV_CODEC_ID_SUBRIP)
             srt_style_apply(s, dialog->style);
-        ff_ass_split_override_codes(cb, s, dialog->text);
-        ff_ass_free_dialog(&dialog);
+        avpriv_ass_split_override_codes(cb, s, dialog->text);
+        avpriv_ass_free_dialog(&dialog);
     }
 
     if (!av_bprint_is_complete(&s->buffer))
@@ -286,7 +286,7 @@ static int text_encode_frame(AVCodecContext *avctx,
 static int srt_encode_close(AVCodecContext *avctx)
 {
     SRTContext *s = avctx->priv_data;
-    ff_ass_split_free(s->ass_ctx);
+    avpriv_ass_split_free(s->ass_ctx);
     av_bprint_finalize(&s->buffer, NULL);
     return 0;
 }
diff --git a/libavcodec/subviewerdec.c b/libavcodec/subviewerdec.c
index 2bda5fa5c1..8651453e3f 100644
--- a/libavcodec/subviewerdec.c
+++ b/libavcodec/subviewerdec.c
@@ -57,7 +57,7 @@ static int subviewer_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
 
     av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
     if (ptr && avpkt->size > 0 && !subviewer_event_to_ass(&buf, ptr))
-        ret = ff_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
     av_bprint_finalize(&buf, NULL);
     if (ret < 0)
         return ret;
diff --git a/libavcodec/textdec.c b/libavcodec/textdec.c
index d509452391..06a25e7128 100644
--- a/libavcodec/textdec.c
+++ b/libavcodec/textdec.c
@@ -55,8 +55,8 @@ static int text_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
 
     av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
     if (ptr && avpkt->size > 0 && *ptr) {
-        ff_ass_bprint_text_event(&buf, ptr, avpkt->size, text->linebreaks, text->keep_ass_markup);
-        ret = ff_ass_add_rect(sub, buf.str, text->readorder++, 0, NULL, NULL);
+        avpriv_ass_bprint_text_event(&buf, ptr, avpkt->size, text->linebreaks, text->keep_ass_markup);
+        ret = avpriv_ass_add_rect(sub, buf.str, text->readorder++, 0, NULL, NULL);
     }
     av_bprint_finalize(&buf, NULL);
     if (ret < 0)
diff --git a/libavcodec/ttmlenc.c b/libavcodec/ttmlenc.c
index be1d8fb2e8..d4f11a87d2 100644
--- a/libavcodec/ttmlenc.c
+++ b/libavcodec/ttmlenc.c
@@ -32,8 +32,7 @@
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
 #include "libavutil/internal.h"
-#include "ass_split.h"
-#include "ass.h"
+#include "libavutil/ass_split_internal.h"
 #include "ttmlenc.h"
 
 typedef struct {
@@ -95,7 +94,7 @@ static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf,
             return AVERROR(EINVAL);
         }
 
-        dialog = ff_ass_split_dialog(s->ass_ctx, ass);
+        dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
         if (!dialog)
             return AVERROR(ENOMEM);
 
@@ -107,7 +106,7 @@ static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf,
             av_bprintf(&s->buffer, "\">");
         }
 
-        ret = ff_ass_split_override_codes(&ttml_callbacks, s, dialog->text);
+        ret = avpriv_ass_split_override_codes(&ttml_callbacks, s, dialog->text);
         if (ret < 0) {
             int log_level = (ret != AVERROR_INVALIDDATA ||
                              avctx->err_recognition & AV_EF_EXPLODE) ?
@@ -118,7 +117,7 @@ static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf,
                    av_err2str(ret));
 
             if (log_level == AV_LOG_ERROR) {
-                ff_ass_free_dialog(&dialog);
+                avpriv_ass_free_dialog(&dialog);
                 return ret;
             }
         }
@@ -126,7 +125,7 @@ static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf,
         if (dialog->style)
             av_bprintf(&s->buffer, "</span>");
 
-        ff_ass_free_dialog(&dialog);
+        avpriv_ass_free_dialog(&dialog);
     }
 
     if (!av_bprint_is_complete(&s->buffer))
@@ -148,7 +147,7 @@ static av_cold int ttml_encode_close(AVCodecContext *avctx)
 {
     TTMLContext *s = avctx->priv_data;
 
-    ff_ass_split_free(s->ass_ctx);
+    avpriv_ass_split_free(s->ass_ctx);
 
     av_bprint_finalize(&s->buffer, NULL);
 
@@ -372,7 +371,7 @@ static av_cold int ttml_encode_init(AVCodecContext *avctx)
 
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
 
-    if (!(s->ass_ctx = ff_ass_split(avctx->subtitle_header))) {
+    if (!(s->ass_ctx = avpriv_ass_split(avctx->subtitle_header))) {
         return AVERROR_INVALIDDATA;
     }
 
diff --git a/libavcodec/webvttdec.c b/libavcodec/webvttdec.c
index fcf1062d86..549e60fe62 100644
--- a/libavcodec/webvttdec.c
+++ b/libavcodec/webvttdec.c
@@ -90,7 +90,7 @@ static int webvtt_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
 
     av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
     if (ptr && avpkt->size > 0 && !webvtt_event_to_ass(&buf, ptr))
-        ret = ff_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
     av_bprint_finalize(&buf, NULL);
     if (ret < 0)
         return ret;
diff --git a/libavcodec/webvttenc.c b/libavcodec/webvttenc.c
index e433bb4579..24d60c5dc1 100644
--- a/libavcodec/webvttenc.c
+++ b/libavcodec/webvttenc.c
@@ -24,9 +24,9 @@
 #include "avcodec.h"
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
-#include "ass_split.h"
-#include "ass.h"
 #include "codec_internal.h"
+#include "libavutil/ass_split_internal.h"
+#include "libavutil/ass_internal.h"
 
 #define WEBVTT_STACK_SIZE 64
 typedef struct {
@@ -93,7 +93,7 @@ static void webvtt_stack_push_pop(WebVTTContext *s, const char c, int close)
 
 static void webvtt_style_apply(WebVTTContext *s, const char *style)
 {
-    ASSStyle *st = ff_ass_style_get(s->ass_ctx, style);
+    ASSStyle *st = avpriv_ass_style_get(s->ass_ctx, style);
     if (st) {
         if (st->bold != ASS_DEFAULT_BOLD) {
             webvtt_print(s, "<b>");
@@ -172,12 +172,12 @@ static int webvtt_encode_frame(AVCodecContext *avctx,
             return AVERROR(EINVAL);
         }
 
-        dialog = ff_ass_split_dialog(s->ass_ctx, ass);
+        dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
         if (!dialog)
             return AVERROR(ENOMEM);
         webvtt_style_apply(s, dialog->style);
-        ff_ass_split_override_codes(&webvtt_callbacks, s, dialog->text);
-        ff_ass_free_dialog(&dialog);
+        avpriv_ass_split_override_codes(&webvtt_callbacks, s, dialog->text);
+        avpriv_ass_free_dialog(&dialog);
     }
 
     if (!av_bprint_is_complete(&s->buffer))
@@ -197,7 +197,7 @@ static int webvtt_encode_frame(AVCodecContext *avctx,
 static int webvtt_encode_close(AVCodecContext *avctx)
 {
     WebVTTContext *s = avctx->priv_data;
-    ff_ass_split_free(s->ass_ctx);
+    avpriv_ass_split_free(s->ass_ctx);
     av_bprint_finalize(&s->buffer, NULL);
     return 0;
 }
@@ -206,7 +206,7 @@ static av_cold int webvtt_encode_init(AVCodecContext *avctx)
 {
     WebVTTContext *s = avctx->priv_data;
     s->avctx = avctx;
-    s->ass_ctx = ff_ass_split(avctx->subtitle_header);
+    s->ass_ctx = avpriv_ass_split(avctx->subtitle_header);
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
     return s->ass_ctx ? 0 : AVERROR_INVALIDDATA;
 }
diff --git a/libavutil/Makefile b/libavutil/Makefile
index 48f78c81e5..9da830c0b5 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -103,6 +103,8 @@ BUILT_HEADERS = avconfig.h                                              \
 OBJS = adler32.o                                                        \
        aes.o                                                            \
        aes_ctr.o                                                        \
+       ass.o                                                            \
+       ass_split.o                                                      \
        audio_fifo.o                                                     \
        avstring.o                                                       \
        avsscanf.o                                                       \
diff --git a/libavcodec/ass.c b/libavutil/ass.c
similarity index 59%
rename from libavcodec/ass.c
rename to libavutil/ass.c
index a1e560d7ff..6739132acf 100644
--- a/libavcodec/ass.c
+++ b/libavutil/ass.c
@@ -19,21 +19,22 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-#include "avcodec.h"
-#include "ass.h"
+#include "ass_internal.h"
+
+#include "subfmt.h"
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
 #include "libavutil/common.h"
 
-int ff_ass_subtitle_header_full(AVCodecContext *avctx,
-                                int play_res_x, int play_res_y,
-                                const char *font, int font_size,
-                                int primary_color, int secondary_color,
-                                int outline_color, int back_color,
-                                int bold, int italic, int underline,
-                                int border_style, int alignment)
+char* avpriv_ass_get_subtitle_header_full(int play_res_x, int play_res_y,
+                                    const char *font, int font_size,
+                                    int primary_color, int secondary_color,
+                                    int outline_color, int back_color,
+                                    int bold, int italic, int underline,
+                                    int border_style, int alignment,
+                                    int print_av_version)
 {
-    avctx->subtitle_header = av_asprintf(
+    char* header = av_asprintf(
              "[Script Info]\r\n"
              "; Script generated by FFmpeg/Lavc%s\r\n"
              "ScriptType: v4.00+\r\n"
@@ -68,34 +69,31 @@ int ff_ass_subtitle_header_full(AVCodecContext *avctx,
              "\r\n"
              "[Events]\r\n"
              "Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\r\n",
-             !(avctx->flags & AV_CODEC_FLAG_BITEXACT) ? AV_STRINGIFY(LIBAVCODEC_VERSION) : "",
+             print_av_version ? AV_STRINGIFY(LIBAVCODEC_VERSION) : "",
              play_res_x, play_res_y, font, font_size,
              primary_color, secondary_color, outline_color, back_color,
              -bold, -italic, -underline, border_style, alignment);
 
-    if (!avctx->subtitle_header)
-        return AVERROR(ENOMEM);
-    avctx->subtitle_header_size = strlen(avctx->subtitle_header);
-    return 0;
+    return header;
 }
 
-int ff_ass_subtitle_header(AVCodecContext *avctx,
-                           const char *font, int font_size,
+char* avpriv_ass_get_subtitle_header(const char *font, int font_size,
                            int color, int back_color,
                            int bold, int italic, int underline,
-                           int border_style, int alignment)
+                           int border_style, int alignment,
+                           int print_av_version)
 {
-    return ff_ass_subtitle_header_full(avctx,
-                               ASS_DEFAULT_PLAYRESX, ASS_DEFAULT_PLAYRESY,
-                               font, font_size, color, color,
-                               back_color, back_color,
-                               bold, italic, underline,
-                               border_style, alignment);
+    return avpriv_ass_get_subtitle_header_full(ASS_DEFAULT_PLAYRESX, ASS_DEFAULT_PLAYRESY,
+                                       font, font_size, color, color,
+                                       back_color, back_color,
+                                       bold, italic, underline,
+                                       border_style, alignment,
+                                       print_av_version);
 }
 
-int ff_ass_subtitle_header_default(AVCodecContext *avctx)
+char* avpriv_ass_get_subtitle_header_default(int print_av_version)
 {
-    return ff_ass_subtitle_header(avctx, ASS_DEFAULT_FONT,
+    return avpriv_ass_get_subtitle_header(ASS_DEFAULT_FONT,
                                ASS_DEFAULT_FONT_SIZE,
                                ASS_DEFAULT_COLOR,
                                ASS_DEFAULT_BACK_COLOR,
@@ -103,10 +101,11 @@ int ff_ass_subtitle_header_default(AVCodecContext *avctx)
                                ASS_DEFAULT_ITALIC,
                                ASS_DEFAULT_UNDERLINE,
                                ASS_DEFAULT_BORDERSTYLE,
-                               ASS_DEFAULT_ALIGNMENT);
+                               ASS_DEFAULT_ALIGNMENT,
+                               print_av_version);
 }
 
-char *ff_ass_get_dialog(int readorder, int layer, const char *style,
+char *avpriv_ass_get_dialog(int readorder, int layer, const char *style,
                         const char *speaker, const char *text)
 {
     return av_asprintf("%d,%d,%s,%s,0,0,0,,%s",
@@ -114,61 +113,17 @@ char *ff_ass_get_dialog(int readorder, int layer, const char *style,
                        speaker ? speaker : "", text);
 }
 
-int ff_ass_add_rect2(AVSubtitle *sub, const char *dialog,
-                    int readorder, int layer, const char *style,
-                    const char *speaker, unsigned *nb_rect_allocated)
-{
-    AVSubtitleRect **rects = sub->rects, *rect;
-    char *ass_str;
-    uint64_t new_nb = 0;
-
-    if (sub->num_rects >= UINT_MAX)
-        return AVERROR(ENOMEM);
-
-    if (nb_rect_allocated && *nb_rect_allocated <= sub->num_rects) {
-        if (sub->num_rects < UINT_MAX / 17 * 16) {
-            new_nb = sub->num_rects + sub->num_rects/16 + 1;
-        } else
-            new_nb = UINT_MAX;
-    } else if (!nb_rect_allocated)
-        new_nb = sub->num_rects + 1;
-
-    if (new_nb) {
-        rects = av_realloc_array(rects, new_nb, sizeof(*sub->rects));
-        if (!rects)
-            return AVERROR(ENOMEM);
-        if (nb_rect_allocated)
-            *nb_rect_allocated = new_nb;
-        sub->rects = rects;
-    }
-
-    rect       = av_mallocz(sizeof(*rect));
-    if (!rect)
-        return AVERROR(ENOMEM);
-    rects[sub->num_rects++] = rect;
-    rect->type = SUBTITLE_ASS;
-    ass_str = ff_ass_get_dialog(readorder, layer, style, speaker, dialog);
-    if (!ass_str)
-        return AVERROR(ENOMEM);
-    rect->ass = ass_str;
-    return 0;
-}
-
-int ff_ass_add_rect(AVSubtitle *sub, const char *dialog,
-                    int readorder, int layer, const char *style,
-                    const char *speaker)
+char *avpriv_ass_get_dialog_ex(int readorder, int layer, const char *style,
+                        const char *speaker, int margin_l, int margin_r,
+                        int margin_v, const char *effect, const char *text)
 {
-    return ff_ass_add_rect2(sub, dialog, readorder, layer, style, speaker, NULL);
-}
-
-void ff_ass_decoder_flush(AVCodecContext *avctx)
-{
-    FFASSDecoderContext *s = avctx->priv_data;
-    if (!(avctx->flags2 & AV_CODEC_FLAG2_RO_FLUSH_NOOP))
-        s->readorder = 0;
+    return av_asprintf("%d,%d,%s,%s,%d,%d,%d,%s,%s",
+                       readorder, layer, style ? style : "Default",
+                       speaker ? speaker : "", margin_l, margin_r,
+                       margin_v, effect ? effect : "", text);
 }
 
-void ff_ass_bprint_text_event(AVBPrint *buf, const char *p, int size,
+void avpriv_ass_bprint_text_event(AVBPrint *buf, const char *p, int size,
                              const char *linebreaks, int keep_ass_markup)
 {
     const char *p_end = p + size;
diff --git a/libavutil/ass_internal.h b/libavutil/ass_internal.h
new file mode 100644
index 0000000000..fc4e6232fb
--- /dev/null
+++ b/libavutil/ass_internal.h
@@ -0,0 +1,135 @@
+/*
+ * SSA/ASS common functions
+ * Copyright (c) 2010  Aurelien Jacobs <aurel@gnuage.org>
+ *
+ * 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
+ */
+
+#ifndef AVUTIL_ASS_INTERNAL_H
+#define AVUTIL_ASS_INTERNAL_H
+
+#include "subfmt.h"
+#include "libavutil/bprint.h"
+
+#define ASS_DEFAULT_PLAYRESX 384
+#define ASS_DEFAULT_PLAYRESY 288
+
+/**
+ * @name Default values for ASS style
+ * @{
+ */
+#define ASS_DEFAULT_FONT        "Arial"
+#define ASS_DEFAULT_FONT_SIZE   16
+#define ASS_DEFAULT_COLOR       0xffffff
+#define ASS_DEFAULT_BACK_COLOR  0
+#define ASS_DEFAULT_BOLD        0
+#define ASS_DEFAULT_ITALIC      0
+#define ASS_DEFAULT_UNDERLINE   0
+#define ASS_DEFAULT_ALIGNMENT   2
+#define ASS_DEFAULT_BORDERSTYLE 1
+/** @} */
+
+/**
+ * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS.
+ * Can specify all fields explicitly
+ *
+ * @param play_res_x subtitle frame width
+ * @param play_res_y subtitle frame height
+ * @param font name of the default font face to use
+ * @param font_size default font size to use
+ * @param primary_color default text color to use (ABGR)
+ * @param secondary_color default secondary text color to use (ABGR)
+ * @param outline_color default outline color to use (ABGR)
+ * @param back_color default background color to use (ABGR)
+ * @param bold 1 for bold text, 0 for normal text
+ * @param italic 1 for italic text, 0 for normal text
+ * @param underline 1 for underline text, 0 for normal text
+ * @param border_style 1 for outline, 3 for opaque box
+ * @param alignment position of the text (left, center, top...), defined after
+ *                  the layout of the numpad (1-3 sub, 4-6 mid, 7-9 top)
+ * @param print_av_version include library version in header
+ * @return a string containing the subtitle header that needs
+ *         to be released via av_free()
+ */
+char* avpriv_ass_get_subtitle_header_full(int play_res_x, int play_res_y,
+                                  const char *font, int font_size,
+                                  int primary_color, int secondary_color,
+                                  int outline_color, int back_color,
+                                  int bold, int italic, int underline,
+                                  int border_style, int alignment,
+                                  int print_av_version);
+
+/**
+ * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS.
+ *
+ * @param font name of the default font face to use
+ * @param font_size default font size to use
+ * @param color default text color to use (ABGR)
+ * @param back_color default background color to use (ABGR)
+ * @param bold 1 for bold text, 0 for normal text
+ * @param italic 1 for italic text, 0 for normal text
+ * @param underline 1 for underline text, 0 for normal text
+ * @param border_style 1 for outline, 3 for opaque box
+ * @param alignment position of the text (left, center, top...), defined after
+ *                  the layout of the numpad (1-3 sub, 4-6 mid, 7-9 top)
+ * @param print_av_version include library version in header
+ * @return a string containing the subtitle header that needs
+ *         to be released via av_free()
+ */
+char* avpriv_ass_get_subtitle_header(const char *font, int font_size,
+                                int color, int back_color,
+                                int bold, int italic, int underline,
+                                int border_style, int alignment,
+                                int print_av_version);
+
+/**
+ * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS
+ * with default style.
+ *
+ * @param print_av_version include library version in header
+ * @return a string containing the subtitle header that needs
+ *         to be released via av_free()
+ */
+char* avpriv_ass_get_subtitle_header_default(int print_av_version);
+
+/**
+ * Craft an ASS dialog string.
+ */
+char *avpriv_ass_get_dialog(int readorder, int layer, const char *style,
+                        const char *speaker, const char *text);
+
+/**
+ * Craft an ASS dialog string.
+ */
+char *avpriv_ass_get_dialog_ex(int readorder, int layer, const char *style,
+                        const char *speaker, int margin_l, int margin_r,
+                        int margin_v, const char *effect, const char *text);
+
+/**
+ * Escape a text subtitle using ASS syntax into an AVBPrint buffer.
+ * Newline characters will be escaped to \N.
+ *
+ * @param buf pointer to an initialized AVBPrint buffer
+ * @param p source text
+ * @param size size of the source text
+ * @param linebreaks additional newline chars, which will be escaped to \N
+ * @param keep_ass_markup braces and backslash will not be escaped if set
+ */
+void avpriv_ass_bprint_text_event(AVBPrint *buf, const char *p, int size,
+                             const char *linebreaks, int keep_ass_markup);
+
+#endif /* AVUTIL_ASS_INTERNAL_H */
diff --git a/libavcodec/ass_split.c b/libavutil/ass_split.c
similarity index 71%
rename from libavcodec/ass_split.c
rename to libavutil/ass_split.c
index 73ef6196c5..3e5e24e2fa 100644
--- a/libavcodec/ass_split.c
+++ b/libavutil/ass_split.c
@@ -28,7 +28,7 @@
 #include "libavutil/error.h"
 #include "libavutil/macros.h"
 #include "libavutil/mem.h"
-#include "ass_split.h"
+#include "ass_split_internal.h"
 
 typedef enum {
     ASS_STR,
@@ -379,7 +379,7 @@ static int ass_split(ASSSplitContext *ctx, const char *buf)
     return buf ? 0 : AVERROR_INVALIDDATA;
 }
 
-ASSSplitContext *ff_ass_split(const char *buf)
+ASSSplitContext *avpriv_ass_split(const char *buf)
 {
     ASSSplitContext *ctx = av_mallocz(sizeof(*ctx));
     if (!ctx)
@@ -388,7 +388,7 @@ ASSSplitContext *ff_ass_split(const char *buf)
         buf += 3;
     ctx->current_section = -1;
     if (ass_split(ctx, buf) < 0) {
-        ff_ass_split_free(ctx);
+        avpriv_ass_split_free(ctx);
         return NULL;
     }
     return ctx;
@@ -418,7 +418,7 @@ static void free_section(ASSSplitContext *ctx, const ASSSection *section)
         av_freep((uint8_t *)&ctx->ass + section->offset);
 }
 
-void ff_ass_free_dialog(ASSDialog **dialogp)
+void avpriv_ass_free_dialog(ASSDialog **dialogp)
 {
     ASSDialog *dialog = *dialogp;
     if (!dialog)
@@ -430,7 +430,7 @@ void ff_ass_free_dialog(ASSDialog **dialogp)
     av_freep(dialogp);
 }
 
-ASSDialog *ff_ass_split_dialog(ASSSplitContext *ctx, const char *buf)
+ASSDialog *avpriv_ass_split_dialog(ASSSplitContext *ctx, const char *buf)
 {
     int i;
     static const ASSFields fields[] = {
@@ -457,7 +457,7 @@ ASSDialog *ff_ass_split_dialog(ASSSplitContext *ctx, const char *buf)
         buf = skip_space(buf);
         len = last ? strlen(buf) : strcspn(buf, ",");
         if (len >= INT_MAX) {
-            ff_ass_free_dialog(&dialog);
+            avpriv_ass_free_dialog(&dialog);
             return NULL;
         }
         convert_func[type](ptr, buf, len);
@@ -467,7 +467,7 @@ ASSDialog *ff_ass_split_dialog(ASSSplitContext *ctx, const char *buf)
     return dialog;
 }
 
-void ff_ass_split_free(ASSSplitContext *ctx)
+void avpriv_ass_split_free(ASSSplitContext *ctx)
 {
     if (ctx) {
         int i;
@@ -479,87 +479,209 @@ void ff_ass_split_free(ASSSplitContext *ctx)
     }
 }
 
+static int ass_remove_empty_braces(AVBPrint* buffer)
+{
+    char* tmp;
+    int ret = 0, n = 0;
 
-int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
-                                const char *buf)
+    if (buffer == NULL || buffer->len == 0 || !av_bprint_is_complete(buffer))
+        return 0;
+
+    ret = av_bprint_finalize(buffer, &tmp);
+    if (ret)
+        return ret;
+
+    for (unsigned i = 0; i < buffer->len; i++) {
+        if (tmp[i] == '{' && tmp[i+1] == '}')
+            i++;
+        else
+            tmp[n++] = tmp[i];
+    }
+
+    tmp[n++] = '\0';
+
+    av_bprint_init(buffer, n, n);
+    av_bprint_append_data(buffer, tmp, n - 1);
+    av_free(tmp);
+
+    return ret;
+}
+
+static void ass_write_filtered_line(AVBPrint* buffer, const char *buf, int len, enum ASSSplitComponents keep_flags, enum ASSSplitComponents split_component)
+{
+    if (buffer == NULL || buf == NULL || len == 0)
+        return;
+
+    if (split_component != ASS_SPLIT_ANY && !(keep_flags & split_component))
+        return;
+
+
+    av_bprint_append_data(buffer, buf, len - 1);
+}
+
+int avpriv_ass_filter_override_codes(const ASSCodesCallbacks *callbacks, void *priv, const char *buf, AVBPrint* outbuffer, enum ASSSplitComponents keep_flags)
 {
     const char *text = NULL;
     char new_line[2];
-    int text_len = 0;
+    int text_len = 0, ret = 0;
 
     while (buf && *buf) {
-        if (text && callbacks->text &&
-            (sscanf(buf, "\\%1[nN]", new_line) == 1 ||
-             !strncmp(buf, "{\\", 2))) {
-            callbacks->text(priv, text, text_len);
+
+        if (text && (sscanf(buf, "\\%1[nN]", new_line) == 1 || !strncmp(buf, "{\\", 2))) {
+            ass_write_filtered_line(outbuffer, text, text_len + 1, keep_flags, ASS_SPLIT_TEXT | ASS_SPLIT_TEXT2);
+
+            if (callbacks->text)
+                callbacks->text(priv, text, text_len);
             text = NULL;
         }
+
         if (sscanf(buf, "\\%1[nN]", new_line) == 1) {
             if (callbacks->new_line)
                 callbacks->new_line(priv, new_line[0] == 'N');
+            ass_write_filtered_line(outbuffer, buf, 3, keep_flags, ASS_SPLIT_ANY);
             buf += 2;
         } else if (!strncmp(buf, "{\\", 2)) {
+            ass_write_filtered_line(outbuffer, buf, 2, keep_flags, ASS_SPLIT_ANY);
             buf++;
             while (*buf == '\\') {
-                char style[2], c[2], sep[2], c_num[2] = "0", tmp[128] = {0};
+                char style[4], c[2], axis[3], sep[3], c_num[2] = "0", tmp[128] = {0};
                 unsigned int color = 0xFFFFFFFF;
-                int len, size = -1, an = -1, alpha = -1;
-                int x1, y1, x2, y2, t1 = -1, t2 = -1;
+                int len, size = -1, an = -1, alpha = -1, scale = 0;
+                float f1 = 1;
+                int x1, y1, x2, y2, x3, t1 = -1, t2 = -1, t3 = -1, t4 = -1, accel = 1;
                 if (sscanf(buf, "\\%1[bisu]%1[01\\}]%n", style, c, &len) > 1) {
                     int close = c[0] == '0' ? 1 : c[0] == '1' ? 0 : -1;
                     len += close != -1;
+                    switch (c[0]) {
+                    case 'b':
+                        ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FONT_BOLD);
+                        break;
+                    case 'u':
+                        ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FONT_UNDERLINE);
+                        break;
+                    case 'i':
+                        ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FONT_ITALIC);
+                        break;
+                    case 'a':
+                        ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FONT_STRIKEOUT);
+                        break;
+                    }
                     if (callbacks->style)
                         callbacks->style(priv, style[0], close);
                 } else if (sscanf(buf, "\\c%1[\\}]%n", sep, &len) > 0 ||
                            sscanf(buf, "\\c&H%X&%1[\\}]%n", &color, sep, &len) > 1 ||
                            sscanf(buf, "\\%1[1234]c%1[\\}]%n", c_num, sep, &len) > 1 ||
                            sscanf(buf, "\\%1[1234]c&H%X&%1[\\}]%n", c_num, &color, sep, &len) > 2) {
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_COLOR);
                     if (callbacks->color)
                         callbacks->color(priv, color, c_num[0] - '0');
                 } else if (sscanf(buf, "\\alpha%1[\\}]%n", sep, &len) > 0 ||
                            sscanf(buf, "\\alpha&H%2X&%1[\\}]%n", &alpha, sep, &len) > 1 ||
                            sscanf(buf, "\\%1[1234]a%1[\\}]%n", c_num, sep, &len) > 1 ||
                            sscanf(buf, "\\%1[1234]a&H%2X&%1[\\}]%n", c_num, &alpha, sep, &len) > 2) {
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_ALPHA);
                     if (callbacks->alpha)
                         callbacks->alpha(priv, alpha, c_num[0] - '0');
                 } else if (sscanf(buf, "\\fn%1[\\}]%n", sep, &len) > 0 ||
                            sscanf(buf, "\\fn%127[^\\}]%1[\\}]%n", tmp, sep, &len) > 1) {
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FONT_NAME);
                     if (callbacks->font_name)
                         callbacks->font_name(priv, tmp[0] ? tmp : NULL);
                 } else if (sscanf(buf, "\\fs%1[\\}]%n", sep, &len) > 0 ||
                            sscanf(buf, "\\fs%u%1[\\}]%n", &size, sep, &len) > 1) {
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FONT_SIZE);
                     if (callbacks->font_size)
                         callbacks->font_size(priv, size);
+                } else if (sscanf(buf, "\\fscx%1[\\}]%n", sep, &len) > 0 ||
+                           sscanf(buf, "\\fscx%f%1[\\}]%n", &f1, sep, &len) > 1) {
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FONT_SCALE);
+                } else if (sscanf(buf, "\\fscy%1[\\}]%n", sep, &len) > 0 ||
+                           sscanf(buf, "\\fscy%f%1[\\}]%n", &f1, sep, &len) > 1) {
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FONT_SCALE);
+                } else if (sscanf(buf, "\\fsp%1[\\}]%n", sep, &len) > 0 ||
+                           sscanf(buf, "\\fsp%u%1[\\}]%n", &size, sep, &len) > 1) {
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FONT_SPACING);
+                } else if (sscanf(buf, "\\fe%1[\\}]%n", sep, &len) > 0 ||
+                           sscanf(buf, "\\fe%u%1[\\}]%n", &size, sep, &len) > 1) {
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FONT_CHARSET);
+                } else if (sscanf(buf, "\\bord%1[\\}]%n", sep, &len) > 0 ||
+                           sscanf(buf, "\\bord%u%1[\\}]%n", &size, sep, &len) > 1) {
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_TEXT_BORDER);
+                } else if (sscanf(buf, "\\shad%1[\\}]%n", sep, &len) > 0 ||
+                           sscanf(buf, "\\shad%u%1[\\}]%n", &size, sep, &len) > 1) {
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_TEXT_SHADOW);
+                } else if (sscanf(buf, "\\fr%1[\\}]%n", sep, &len) > 0 ||
+                           sscanf(buf, "\\fr%u%1[\\}]%n", &x1, sep, &len) > 1 ||
+                           sscanf(buf, "\\fr%1[xyz]%1[\\}]%n", axis, sep, &len) > 1 ||
+                           sscanf(buf, "\\fr%1[xyz]%u%1[\\}]%n", axis, &size, sep, &len) > 2) {
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_TEXT_ROTATE);
+                } else if (sscanf(buf, "\\blur%1[\\}]%n", sep, &len) > 0 ||
+                           sscanf(buf, "\\blur%u%1[\\}]%n", &size, sep, &len) > 1) {
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_TEXT_BLUR);
+                } else if (sscanf(buf, "\\be%1[\\}]%n", sep, &len) > 0 ||
+                           sscanf(buf, "\\be%u%1[\\}]%n", &size, sep, &len) > 1) {
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_TEXT_BLUR);
+                } else if (sscanf(buf, "\\q%1[\\}]%n", sep, &len) > 0 ||
+                           sscanf(buf, "\\q%u%1[\\}]%n", &size, sep, &len) > 1) {
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_TEXT_WRAP);
                 } else if (sscanf(buf, "\\a%1[\\}]%n", sep, &len) > 0 ||
                            sscanf(buf, "\\a%2u%1[\\}]%n", &an, sep, &len) > 1 ||
                            sscanf(buf, "\\an%1[\\}]%n", sep, &len) > 0 ||
                            sscanf(buf, "\\an%1u%1[\\}]%n", &an, sep, &len) > 1) {
                     if (an != -1 && buf[2] != 'n')
                         an = (an&3) + (an&4 ? 6 : an&8 ? 3 : 0);
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_TEXT_ALIGNMENT);
                     if (callbacks->alignment)
                         callbacks->alignment(priv, an);
                 } else if (sscanf(buf, "\\r%1[\\}]%n", sep, &len) > 0 ||
                            sscanf(buf, "\\r%127[^\\}]%1[\\}]%n", tmp, sep, &len) > 1) {
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_CANCELLING);
                     if (callbacks->cancel_overrides)
                         callbacks->cancel_overrides(priv, tmp);
                 } else if (sscanf(buf, "\\move(%d,%d,%d,%d)%1[\\}]%n", &x1, &y1, &x2, &y2, sep, &len) > 4 ||
                            sscanf(buf, "\\move(%d,%d,%d,%d,%d,%d)%1[\\}]%n", &x1, &y1, &x2, &y2, &t1, &t2, sep, &len) > 6) {
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_MOVE);
                     if (callbacks->move)
                         callbacks->move(priv, x1, y1, x2, y2, t1, t2);
                 } else if (sscanf(buf, "\\pos(%d,%d)%1[\\}]%n", &x1, &y1, sep, &len) > 2) {
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_POS);
                     if (callbacks->move)
                         callbacks->move(priv, x1, y1, x1, y1, -1, -1);
                 } else if (sscanf(buf, "\\org(%d,%d)%1[\\}]%n", &x1, &y1, sep, &len) > 2) {
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_ORIGIN);
                     if (callbacks->origin)
                         callbacks->origin(priv, x1, y1);
+                } else if (sscanf(buf, "\\t(%1[\\}]%n", sep, &len) > 0 ||
+                           sscanf(buf, "\\t(%d,%d,%1[\\}]%n", &t1, &t2, sep, &len) > 2 ||
+                           sscanf(buf, "\\t(%d,%d,%d,%1[\\}]%n", &t1, &t2, &accel, sep, &len) > 3) {
+
+                    len = strcspn(buf, ")") + 2;
+
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_ANIMATE);
+                    if (callbacks->animate)
+                        callbacks->animate(priv, t1, t2, accel, tmp);
+                } else if (sscanf(buf, "\\fade(%d,%d,%d,%d,%d,%d,%d)%1[\\}]%n", &x1, &x2, &x3, &t1, &t2, &t3, &t4, sep, &len) > 7) {
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FADE);
+                } else if (sscanf(buf, "\\fad(%d,%d)%1[\\}]%n", &t1, &t2, sep, &len) > 2) {
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FADE);
+                } else if (sscanf(buf, "\\clip(%d,%d,%d,%d)%1[\\}]%n", &x1, &y1, &x2, &y2, sep, &len) > 4) {
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_CLIP);
+                } else if (sscanf(buf, "\\p%1[\\}]%n", sep, &len) > 0 ||
+                           sscanf(buf, "\\p%u%1[\\}]%n", &scale, sep, &len) > 1) {
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_DRAW);
+                    if (callbacks->drawing_mode)
+                        callbacks->drawing_mode(priv, scale);
                 } else {
-                    len = strcspn(buf+1, "\\}") + 2;  /* skip unknown code */
-                }
+                    len = strcspn(buf+1, "\\}") + 2;  /* unknown code */
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_UNKNOWN);
+             }
                 buf += len - 1;
             }
             if (*buf++ != '}')
                 return AVERROR_INVALIDDATA;
-        } else {
+
+            ass_write_filtered_line(outbuffer, "}", 2, keep_flags, ASS_SPLIT_ANY);
+     } else {
             if (!text) {
                 text = buf;
                 text_len = 1;
@@ -568,14 +690,27 @@ int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
             buf++;
         }
     }
+    if (text)
+        ass_write_filtered_line(outbuffer, text, text_len + 1, keep_flags, ASS_SPLIT_TEXT | ASS_SPLIT_TEXT2);
     if (text && callbacks->text)
         callbacks->text(priv, text, text_len);
     if (callbacks->end)
         callbacks->end(priv);
-    return 0;
+
+    if (outbuffer)
+        ret = ass_remove_empty_braces(outbuffer);
+
+    return ret;
+}
+
+
+int avpriv_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
+                                const char *buf)
+{
+    return avpriv_ass_filter_override_codes(callbacks, priv, buf, NULL, 0);
 }
 
-ASSStyle *ff_ass_style_get(ASSSplitContext *ctx, const char *style)
+ASSStyle *avpriv_ass_style_get(ASSSplitContext *ctx, const char *style)
 {
     ASS *ass = &ctx->ass;
     int i;
diff --git a/libavutil/ass_split_internal.h b/libavutil/ass_split_internal.h
new file mode 100644
index 0000000000..5d82aa8886
--- /dev/null
+++ b/libavutil/ass_split_internal.h
@@ -0,0 +1,254 @@
+/*
+ * SSA/ASS spliting functions
+ * Copyright (c) 2010  Aurelien Jacobs <aurel@gnuage.org>
+ *
+ * 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
+ */
+
+#ifndef AVUTIL_ASS_SPLIT_INTERNAL_H
+#define AVUTIL_ASS_SPLIT_INTERNAL_H
+
+#include "bprint.h"
+
+enum ASSSplitComponents
+{
+    ASS_SPLIT_ANY = 0,
+    ASS_SPLIT_TEXT           = (1 << 0),
+    ASS_SPLIT_TEXT2          = (1 << 1), // Same semantics as ASS_SPLIT_TEXT. To work around help output default display.
+    ASS_SPLIT_COLOR          = (1 << 2),
+    ASS_SPLIT_ALPHA          = (1 << 3),
+    ASS_SPLIT_FONT_NAME      = (1 << 4),
+    ASS_SPLIT_FONT_SIZE      = (1 << 5),
+    ASS_SPLIT_FONT_SCALE     = (1 << 6),
+    ASS_SPLIT_FONT_SPACING   = (1 << 7),
+    ASS_SPLIT_FONT_CHARSET   = (1 << 8),
+    ASS_SPLIT_FONT_BOLD      = (1 << 9),
+    ASS_SPLIT_FONT_ITALIC    = (1 << 10),
+    ASS_SPLIT_FONT_UNDERLINE = (1 << 11),
+    ASS_SPLIT_FONT_STRIKEOUT = (1 << 12),
+    ASS_SPLIT_TEXT_BORDER    = (1 << 13),
+    ASS_SPLIT_TEXT_SHADOW    = (1 << 14),
+    ASS_SPLIT_TEXT_ROTATE    = (1 << 15),
+    ASS_SPLIT_TEXT_BLUR      = (1 << 16),
+    ASS_SPLIT_TEXT_WRAP      = (1 << 17),
+    ASS_SPLIT_TEXT_ALIGNMENT = (1 << 18),
+    ASS_SPLIT_CANCELLING     = (1 << 19),
+    ASS_SPLIT_MOVE           = (1 << 20),
+    ASS_SPLIT_POS            = (1 << 21),
+    ASS_SPLIT_ORIGIN         = (1 << 22),
+    ASS_SPLIT_DRAW           = (1 << 23),
+    ASS_SPLIT_ANIMATE        = (1 << 24),
+    ASS_SPLIT_FADE           = (1 << 25),
+    ASS_SPLIT_CLIP           = (1 << 26),
+    ASS_SPLIT_UNKNOWN        = (1 << 27),
+
+    ASS_SPLIT_BASIC =  ASS_SPLIT_TEXT2 | ASS_SPLIT_COLOR | ASS_SPLIT_ALPHA | ASS_SPLIT_FONT_NAME | ASS_SPLIT_FONT_SIZE | ASS_SPLIT_FONT_SCALE | ASS_SPLIT_FONT_SPACING | ASS_SPLIT_FONT_CHARSET | ASS_SPLIT_FONT_BOLD | ASS_SPLIT_FONT_ITALIC | ASS_SPLIT_FONT_UNDERLINE | ASS_SPLIT_FONT_STRIKEOUT | ASS_SPLIT_TEXT_BORDER | ASS_SPLIT_TEXT_SHADOW | ASS_SPLIT_TEXT_WRAP | ASS_SPLIT_TEXT_ALIGNMENT | ASS_SPLIT_POS | ASS_SPLIT_CANCELLING,
+    ASS_SPLIT_ALL_KNOWN =  ASS_SPLIT_TEXT2 | ASS_SPLIT_COLOR | ASS_SPLIT_ALPHA | ASS_SPLIT_FONT_NAME | ASS_SPLIT_FONT_SIZE | ASS_SPLIT_FONT_SCALE | ASS_SPLIT_FONT_SPACING | ASS_SPLIT_FONT_CHARSET | ASS_SPLIT_FONT_BOLD | ASS_SPLIT_FONT_ITALIC | ASS_SPLIT_FONT_UNDERLINE | ASS_SPLIT_FONT_STRIKEOUT | ASS_SPLIT_TEXT_BORDER | ASS_SPLIT_TEXT_SHADOW | ASS_SPLIT_TEXT_ROTATE | ASS_SPLIT_TEXT_BLUR | ASS_SPLIT_TEXT_WRAP | ASS_SPLIT_TEXT_ALIGNMENT | ASS_SPLIT_CANCELLING | ASS_SPLIT_POS | ASS_SPLIT_MOVE | ASS_SPLIT_ORIGIN | ASS_SPLIT_DRAW | ASS_SPLIT_ANIMATE | ASS_SPLIT_FADE | ASS_SPLIT_CLIP,
+};
+
+    /**
+     * fields extracted from the [Script Info] section
+     */
+    typedef struct {
+      char *script_type; /**< SSA script format version (eg. v4.00) */
+  char *collisions;  /**< how subtitles are moved to prevent collisions */
+  int play_res_x;    /**< video width that ASS coords are referring to */
+  int play_res_y;    /**< video height that ASS coords are referring to */
+  float timer;       /**< time multiplier to apply to SSA clock (in %) */
+} ASSScriptInfo;
+
+/**
+ * fields extracted from the [V4(+) Styles] section
+ */
+typedef struct {
+  char *name;        /**< name of the tyle (case sensitive) */
+  char *font_name;   /**< font face (case sensitive) */
+  int font_size;     /**< font height */
+  int primary_color; /**< color that a subtitle will normally appear in */
+  int secondary_color;
+  int outline_color; /**< color for outline in ASS, called tertiary in SSA */
+  int back_color;    /**< color of the subtitle outline or shadow */
+  int bold;          /**< whether text is bold (1) or not (0) */
+  int italic;        /**< whether text is italic (1) or not (0) */
+  int underline;     /**< whether text is underlined (1) or not (0) */
+  int strikeout;
+  float scalex;
+  float scaley;
+  float spacing;
+  float angle;
+  int border_style;
+  float outline;
+  float shadow;
+  int alignment; /**< position of the text (left, center, top...),
+                      defined after the layout of the numpad
+                      (1-3 sub, 4-6 mid, 7-9 top) */
+  int margin_l;
+  int margin_r;
+  int margin_v;
+  int alpha_level;
+  int encoding;
+} ASSStyle;
+
+/**
+ * fields extracted from the [Events] section
+ */
+typedef struct {
+  int readorder;
+  int layer;   /**< higher numbered layers are drawn over lower numbered */
+  int start;   /**< start time of the dialog in centiseconds */
+  int end;     /**< end time of the dialog in centiseconds */
+  char *style; /**< name of the ASSStyle to use with this dialog */
+  char *name;
+  int margin_l;
+  int margin_r;
+  int margin_v;
+  char *effect;
+  char *text; /**< actual text which will be displayed as a subtitle,
+                   can include style override control codes (see
+                   avpriv_ass_split_override_codes()) */
+} ASSDialog;
+
+/**
+ * structure containing the whole split ASS data
+ */
+typedef struct {
+  ASSScriptInfo script_info; /**< general information about the SSA script*/
+  ASSStyle *styles;          /**< array of split out styles */
+  int styles_count;          /**< number of ASSStyle in the styles array */
+  ASSDialog *dialogs;        /**< array of split out dialogs */
+  int dialogs_count;         /**< number of ASSDialog in the dialogs array*/
+} ASS;
+
+/**
+ * This struct can be casted to ASS to access to the split data.
+ */
+typedef struct ASSSplitContext ASSSplitContext;
+
+/**
+ * Split a full ASS file or a ASS header from a string buffer and store
+ * the split structure in a newly allocated context.
+ *
+ * @param buf String containing the ASS formatted data.
+ * @return Newly allocated struct containing split data.
+ */
+ASSSplitContext *avpriv_ass_split(const char *buf);
+
+/**
+ * Free a dialogue obtained from avpriv_ass_split_dialog().
+ */
+void avpriv_ass_free_dialog(ASSDialog **dialogp);
+
+/**
+ * Split one ASS Dialogue line from a string buffer.
+ *
+ * @param ctx Context previously initialized by ff_ass_split().
+ * @param buf String containing the ASS "Dialogue" line.
+ * @return Pointer to the split ASSDialog. Must be freed with
+ * ff_ass_free_dialog()
+ */
+ASSDialog *avpriv_ass_split_dialog(ASSSplitContext *ctx, const char *buf);
+
+/**
+ * Free all the memory allocated for an ASSSplitContext.
+ *
+ * @param ctx Context previously initialized by ff_ass_split().
+ */
+void avpriv_ass_split_free(ASSSplitContext *ctx);
+
+/**
+ * Set of callback functions corresponding to each override codes that can
+ * be encountered in a "Dialogue" Text field.
+ */
+typedef struct {
+  /**
+   * @defgroup ass_styles    ASS styles
+   * @{
+   */
+  void (*text)(void *priv, const char *text, int len);
+  void (*hard_space)(void *priv);
+  void (*new_line)(void *priv, int forced);
+  void (*style)(void *priv, char style, int close);
+  void (*color)(void *priv, unsigned int /* color */, unsigned int color_id);
+  void (*alpha)(void *priv, int alpha, int alpha_id);
+  void (*font_name)(void *priv, const char *name);
+  void (*font_size)(void *priv, int size);
+  void (*alignment)(void *priv, int alignment);
+  void (*cancel_overrides)(void *priv, const char *style);
+  /** @} */
+
+  /**
+   * @defgroup ass_functions    ASS functions
+   * @{
+   */
+  void (*move)(void *priv, int x1, int y1, int x2, int y2, int t1, int t2);
+  void (*animate)(void *priv, int t1, int t2, int accel, char *style);
+  void (*origin)(void *priv, int x, int y);
+  void (*drawing_mode)(void *priv, int scale);
+  /** @} */
+
+  /**
+   * @defgroup ass_ext    ASS extensible parsing callback
+   * @{
+   */
+  void (*ext)(void *priv, int ext_id, const char *text, int p1, int p2);
+  /** @} */
+
+  /**
+   * @defgroup ass_end    end of Dialogue Event
+   * @{
+   */
+  void (*end)(void *priv);
+  /** @} */
+} ASSCodesCallbacks;
+
+/**
+ * Split override codes out of a ASS "Dialogue" Text field.
+ *
+ * @param callbacks Set of callback functions called for each override code
+ *                  encountered.
+ * @param priv Opaque pointer passed to the callback functions.
+ * @param buf The ASS "Dialogue" Text field to split.
+ * @param outbuffer The output buffer.
+ * @param keep_flags Flags for filtering ass codes.
+ * @return >= 0 on success otherwise an error code <0
+ */
+int avpriv_ass_filter_override_codes(const ASSCodesCallbacks *callbacks,
+                                     void *priv, const char *buf,
+                                     AVBPrint *outbuffer, enum ASSSplitComponents keep_flags);
+
+/**
+ * Split override codes out of a ASS "Dialogue" Text field.
+ *
+ * @param callbacks Set of callback functions called for each override code
+ *                  encountered.
+ * @param priv Opaque pointer passed to the callback functions.
+ * @param buf The ASS "Dialogue" Text field to split.
+ * @return >= 0 on success otherwise an error code <0
+ */
+int avpriv_ass_split_override_codes(const ASSCodesCallbacks *callbacks,
+                                    void *priv, const char *buf);
+
+/**
+ * Find an ASSStyle structure by its name.
+ *
+ * @param ctx Context previously initialized by ff_ass_split().
+ * @param style name of the style to search for.
+ * @return the ASSStyle corresponding to style, or NULL if style can't be found
+ */
+ASSStyle *avpriv_ass_style_get(ASSSplitContext *ctx, const char *style);
+
+#endif /* AVUTIL_ASS_SPLIT_INTERNAL_H */
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v5 07/25] avcodec/subtitles: Replace deprecated enum values
  2022-06-25  9:57       ` [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022 ffmpegagent
                           ` (5 preceding siblings ...)
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 06/25] avcodec, avutil: Move ass helper functions to avutil as avpriv_ and extend ass dialog parsing softworkz
@ 2022-06-25  9:57         ` softworkz
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 08/25] fftools/play, probe: Adjust for subtitle changes softworkz
                           ` (20 subsequent siblings)
  27 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-25  9:57 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/ass.h       | 2 +-
 libavcodec/assdec.c    | 2 +-
 libavcodec/dvbsubdec.c | 2 +-
 libavcodec/dvdsubdec.c | 2 +-
 libavcodec/dvdsubenc.c | 2 +-
 libavcodec/pgssubdec.c | 2 +-
 libavcodec/xsubdec.c   | 2 +-
 7 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/libavcodec/ass.h b/libavcodec/ass.h
index 8bc13d7ab8..43c5ad651a 100644
--- a/libavcodec/ass.h
+++ b/libavcodec/ass.h
@@ -83,7 +83,7 @@ static inline int avpriv_ass_add_rect(AVSubtitle *sub, const char *dialog,
     rects[sub->num_rects]       = av_mallocz(sizeof(*rects[0]));
     if (!rects[sub->num_rects])
         return AVERROR(ENOMEM);
-    rects[sub->num_rects]->type = SUBTITLE_ASS;
+    rects[sub->num_rects]->type = AV_SUBTITLE_FMT_ASS;
     ass_str = avpriv_ass_get_dialog(readorder, layer, style, speaker, dialog);
     if (!ass_str)
         return AVERROR(ENOMEM);
diff --git a/libavcodec/assdec.c b/libavcodec/assdec.c
index 1a1363471d..7bb60c9b26 100644
--- a/libavcodec/assdec.c
+++ b/libavcodec/assdec.c
@@ -53,7 +53,7 @@ static int ass_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
     if (!sub->rects[0])
         return AVERROR(ENOMEM);
     sub->num_rects = 1;
-    sub->rects[0]->type = SUBTITLE_ASS;
+    sub->rects[0]->type = AV_SUBTITLE_FMT_ASS;
     sub->rects[0]->ass  = av_strdup(avpkt->data);
     if (!sub->rects[0]->ass)
         return AVERROR(ENOMEM);
diff --git a/libavcodec/dvbsubdec.c b/libavcodec/dvbsubdec.c
index 4d4007ffd9..8d44529b4a 100644
--- a/libavcodec/dvbsubdec.c
+++ b/libavcodec/dvbsubdec.c
@@ -796,7 +796,7 @@ static int save_subtitle_set(AVCodecContext *avctx, AVSubtitle *sub, int *got_ou
             rect->w = region->width;
             rect->h = region->height;
             rect->nb_colors = (1 << region->depth);
-            rect->type      = SUBTITLE_BITMAP;
+            rect->type      = AV_SUBTITLE_FMT_BITMAP;
             rect->linesize[0] = region->width;
 
             clut = get_clut(ctx, region->clut);
diff --git a/libavcodec/dvdsubdec.c b/libavcodec/dvdsubdec.c
index 97f366cc74..cb6dbbbbbc 100644
--- a/libavcodec/dvdsubdec.c
+++ b/libavcodec/dvdsubdec.c
@@ -404,7 +404,7 @@ static int decode_dvd_subtitles(DVDSubContext *ctx, AVSubtitle *sub_header,
                 sub_header->rects[0]->y = y1;
                 sub_header->rects[0]->w = w;
                 sub_header->rects[0]->h = h;
-                sub_header->rects[0]->type = SUBTITLE_BITMAP;
+                sub_header->rects[0]->type = AV_SUBTITLE_FMT_BITMAP;
                 sub_header->rects[0]->linesize[0] = w;
                 sub_header->rects[0]->flags = is_menu ? AV_SUBTITLE_FLAG_FORCED : 0;
             }
diff --git a/libavcodec/dvdsubenc.c b/libavcodec/dvdsubenc.c
index d29db7d49c..24da94faee 100644
--- a/libavcodec/dvdsubenc.c
+++ b/libavcodec/dvdsubenc.c
@@ -269,7 +269,7 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
     if (rects == 0 || !h->rects)
         return AVERROR(EINVAL);
     for (i = 0; i < rects; i++)
-        if (h->rects[i]->type != SUBTITLE_BITMAP) {
+        if (h->rects[i]->type != AV_SUBTITLE_FMT_BITMAP) {
             av_log(avctx, AV_LOG_ERROR, "Bitmap subtitle required\n");
             return AVERROR(EINVAL);
         }
diff --git a/libavcodec/pgssubdec.c b/libavcodec/pgssubdec.c
index e50c6766c5..05399863b6 100644
--- a/libavcodec/pgssubdec.c
+++ b/libavcodec/pgssubdec.c
@@ -535,7 +535,7 @@ static int display_end_segment(AVCodecContext *avctx, AVSubtitle *sub,
         if (!rect)
             return AVERROR(ENOMEM);
         sub->rects[sub->num_rects++] = rect;
-        rect->type = SUBTITLE_BITMAP;
+        rect->type = AV_SUBTITLE_FMT_BITMAP;
 
         /* Process bitmap */
         object = find_object(ctx->presentation.objects[i].id, &ctx->objects);
diff --git a/libavcodec/xsubdec.c b/libavcodec/xsubdec.c
index d62fa164a5..30c3595c97 100644
--- a/libavcodec/xsubdec.c
+++ b/libavcodec/xsubdec.c
@@ -107,7 +107,7 @@ static int decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
     sub->num_rects = 1;
     rect->x = x; rect->y = y;
     rect->w = w; rect->h = h;
-    rect->type = SUBTITLE_BITMAP;
+    rect->type = AV_SUBTITLE_FMT_BITMAP;
     rect->linesize[0] = w;
     rect->data[0] = av_malloc(w * h);
     rect->nb_colors = 4;
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v5 08/25] fftools/play, probe: Adjust for subtitle changes
  2022-06-25  9:57       ` [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022 ffmpegagent
                           ` (6 preceding siblings ...)
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 07/25] avcodec/subtitles: Replace deprecated enum values softworkz
@ 2022-06-25  9:57         ` softworkz
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 09/25] avfilter/subtitles: Add subtitles.c for subtitle frame allocation softworkz
                           ` (19 subsequent siblings)
  27 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-25  9:57 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 fftools/ffplay.c  | 102 +++++++++++++++++++++-------------------------
 fftools/ffprobe.c |  47 +++++++++++++--------
 2 files changed, 77 insertions(+), 72 deletions(-)

diff --git a/fftools/ffplay.c b/fftools/ffplay.c
index 040afa0189..111e157979 100644
--- a/fftools/ffplay.c
+++ b/fftools/ffplay.c
@@ -153,7 +153,6 @@ typedef struct Clock {
 /* Common struct for handling all types of decoded data and allocated render buffers. */
 typedef struct Frame {
     AVFrame *frame;
-    AVSubtitle sub;
     int serial;
     double pts;           /* presentation timestamp for the frame */
     double duration;      /* estimated duration of the frame */
@@ -574,7 +573,7 @@ static int decoder_init(Decoder *d, AVCodecContext *avctx, PacketQueue *queue, S
     return 0;
 }
 
-static int decoder_decode_frame(Decoder *d, AVFrame *frame, AVSubtitle *sub) {
+static int decoder_decode_frame(Decoder *d, AVFrame *frame) {
     int ret = AVERROR(EAGAIN);
 
     for (;;) {
@@ -608,6 +607,9 @@ static int decoder_decode_frame(Decoder *d, AVFrame *frame, AVSubtitle *sub) {
                             }
                         }
                         break;
+                    case AVMEDIA_TYPE_SUBTITLE:
+                        ret = avcodec_receive_frame(d->avctx, frame);
+                        break;
                 }
                 if (ret == AVERROR_EOF) {
                     d->finished = d->pkt_serial;
@@ -640,25 +642,11 @@ static int decoder_decode_frame(Decoder *d, AVFrame *frame, AVSubtitle *sub) {
             av_packet_unref(d->pkt);
         } while (1);
 
-        if (d->avctx->codec_type == AVMEDIA_TYPE_SUBTITLE) {
-            int got_frame = 0;
-            ret = avcodec_decode_subtitle2(d->avctx, sub, &got_frame, d->pkt);
-            if (ret < 0) {
-                ret = AVERROR(EAGAIN);
-            } else {
-                if (got_frame && !d->pkt->data) {
-                    d->packet_pending = 1;
-                }
-                ret = got_frame ? 0 : (d->pkt->data ? AVERROR(EAGAIN) : AVERROR_EOF);
-            }
-            av_packet_unref(d->pkt);
+        if (avcodec_send_packet(d->avctx, d->pkt) == AVERROR(EAGAIN)) {
+            av_log(d->avctx, AV_LOG_ERROR, "Receive_frame and send_packet both returned EAGAIN, which is an API violation.\n");
+            d->packet_pending = 1;
         } else {
-            if (avcodec_send_packet(d->avctx, d->pkt) == AVERROR(EAGAIN)) {
-                av_log(d->avctx, AV_LOG_ERROR, "Receive_frame and send_packet both returned EAGAIN, which is an API violation.\n");
-                d->packet_pending = 1;
-            } else {
-                av_packet_unref(d->pkt);
-            }
+            av_packet_unref(d->pkt);
         }
     }
 }
@@ -671,7 +659,6 @@ static void decoder_destroy(Decoder *d) {
 static void frame_queue_unref_item(Frame *vp)
 {
     av_frame_unref(vp->frame);
-    avsubtitle_free(&vp->sub);
 }
 
 static int frame_queue_init(FrameQueue *f, PacketQueue *pktq, int max_size, int keep_last)
@@ -969,7 +956,7 @@ static void video_image_display(VideoState *is)
         if (frame_queue_nb_remaining(&is->subpq) > 0) {
             sp = frame_queue_peek(&is->subpq);
 
-            if (vp->pts >= sp->pts + ((float) sp->sub.start_display_time / 1000)) {
+            if (vp->pts >= sp->pts) {
                 if (!sp->uploaded) {
                     uint8_t* pixels[4];
                     int pitch[4];
@@ -981,25 +968,27 @@ static void video_image_display(VideoState *is)
                     if (realloc_texture(&is->sub_texture, SDL_PIXELFORMAT_ARGB8888, sp->width, sp->height, SDL_BLENDMODE_BLEND, 1) < 0)
                         return;
 
-                    for (i = 0; i < sp->sub.num_rects; i++) {
-                        AVSubtitleRect *sub_rect = sp->sub.rects[i];
+                    for (i = 0; i < sp->frame->num_subtitle_areas; i++) {
+                        AVSubtitleArea *area = sp->frame->subtitle_areas[i];
+                        SDL_Rect sdl_rect = { .x = area->x, .y = area->y, .w = area->w, .h = area->h };
 
-                        sub_rect->x = av_clip(sub_rect->x, 0, sp->width );
-                        sub_rect->y = av_clip(sub_rect->y, 0, sp->height);
-                        sub_rect->w = av_clip(sub_rect->w, 0, sp->width  - sub_rect->x);
-                        sub_rect->h = av_clip(sub_rect->h, 0, sp->height - sub_rect->y);
+                        area->x = av_clip(area->x, 0, sp->width );
+                        area->y = av_clip(area->y, 0, sp->height);
+                        area->w = av_clip(area->w, 0, sp->width  - area->x);
+                        area->h = av_clip(area->h, 0, sp->height - area->y);
 
                         is->sub_convert_ctx = sws_getCachedContext(is->sub_convert_ctx,
-                            sub_rect->w, sub_rect->h, AV_PIX_FMT_PAL8,
-                            sub_rect->w, sub_rect->h, AV_PIX_FMT_BGRA,
+                            area->w, area->h, AV_PIX_FMT_PAL8,
+                            area->w, area->h, AV_PIX_FMT_BGRA,
                             0, NULL, NULL, NULL);
                         if (!is->sub_convert_ctx) {
                             av_log(NULL, AV_LOG_FATAL, "Cannot initialize the conversion context\n");
                             return;
                         }
-                        if (!SDL_LockTexture(is->sub_texture, (SDL_Rect *)sub_rect, (void **)pixels, pitch)) {
-                            sws_scale(is->sub_convert_ctx, (const uint8_t * const *)sub_rect->data, sub_rect->linesize,
-                                      0, sub_rect->h, pixels, pitch);
+                        if (!SDL_LockTexture(is->sub_texture, &sdl_rect, (void **)pixels, pitch)) {
+                            const uint8_t* data[2] = { area->buf[0]->data, (uint8_t *)&area->pal };
+                            sws_scale(is->sub_convert_ctx, data, area->linesize,
+                                      0, area->h, pixels, pitch);
                             SDL_UnlockTexture(is->sub_texture);
                         }
                     }
@@ -1026,16 +1015,18 @@ static void video_image_display(VideoState *is)
 #if USE_ONEPASS_SUBTITLE_RENDER
         SDL_RenderCopy(renderer, is->sub_texture, NULL, &rect);
 #else
-        int i;
+        unsigned i;
         double xratio = (double)rect.w / (double)sp->width;
         double yratio = (double)rect.h / (double)sp->height;
-        for (i = 0; i < sp->sub.num_rects; i++) {
-            SDL_Rect *sub_rect = (SDL_Rect*)sp->sub.rects[i];
-            SDL_Rect target = {.x = rect.x + sub_rect->x * xratio,
-                               .y = rect.y + sub_rect->y * yratio,
-                               .w = sub_rect->w * xratio,
-                               .h = sub_rect->h * yratio};
-            SDL_RenderCopy(renderer, is->sub_texture, sub_rect, &target);
+        for (i = 0; i < sp->frame->num_subtitle_areas; i++) {
+            AVSubtitleArea *area = sp->frame->subtitle_areas[i];
+            SDL_Rect sub_rect = { .x = area->x, .y = area->y,
+                                  .w = area->w, .h = area->h};
+            SDL_Rect target = {.x = rect.x + sub_rect.x * xratio,
+                               .y = rect.y + sub_rect.y * yratio,
+                               .w = sub_rect.w * xratio,
+                               .h = sub_rect.h * yratio};
+            SDL_RenderCopy(renderer, is->sub_texture, &sub_rect, &target);
         }
 #endif
     }
@@ -1639,19 +1630,20 @@ retry:
                         sp2 = NULL;
 
                     if (sp->serial != is->subtitleq.serial
-                            || (is->vidclk.pts > (sp->pts + ((float) sp->sub.end_display_time / 1000)))
-                            || (sp2 && is->vidclk.pts > (sp2->pts + ((float) sp2->sub.start_display_time / 1000))))
+                            || (is->vidclk.pts > (sp->pts + ((float) sp->frame->subtitle_timing.duration / AV_TIME_BASE)))
+                            || (sp2 && is->vidclk.pts > (sp2->pts)))
                     {
                         if (sp->uploaded) {
                             int i;
-                            for (i = 0; i < sp->sub.num_rects; i++) {
-                                AVSubtitleRect *sub_rect = sp->sub.rects[i];
+                            for (i = 0; i < sp->frame->num_subtitle_areas; i++) {
+                                AVSubtitleArea *area = sp->frame->subtitle_areas[i];
+                                SDL_Rect sdl_rect = { .x = area->x, .y = area->y, .w = area->w, .h = area->h };
                                 uint8_t *pixels;
                                 int pitch, j;
 
-                                if (!SDL_LockTexture(is->sub_texture, (SDL_Rect *)sub_rect, (void **)&pixels, &pitch)) {
-                                    for (j = 0; j < sub_rect->h; j++, pixels += pitch)
-                                        memset(pixels, 0, sub_rect->w << 2);
+                                if (!SDL_LockTexture(is->sub_texture, &sdl_rect, (void **)&pixels, &pitch)) {
+                                    for (j = 0; j < area->h; j++, pixels += pitch)
+                                        memset(pixels, 0, area->w << 2);
                                     SDL_UnlockTexture(is->sub_texture);
                                 }
                             }
@@ -1762,7 +1754,7 @@ static int get_video_frame(VideoState *is, AVFrame *frame)
 {
     int got_picture;
 
-    if ((got_picture = decoder_decode_frame(&is->viddec, frame, NULL)) < 0)
+    if ((got_picture = decoder_decode_frame(&is->viddec, frame)) < 0)
         return -1;
 
     if (got_picture) {
@@ -2032,7 +2024,7 @@ static int audio_thread(void *arg)
         return AVERROR(ENOMEM);
 
     do {
-        if ((got_frame = decoder_decode_frame(&is->auddec, frame, NULL)) < 0)
+        if ((got_frame = decoder_decode_frame(&is->auddec, frame)) < 0)
             goto the_end;
 
         if (got_frame) {
@@ -2229,14 +2221,14 @@ static int subtitle_thread(void *arg)
         if (!(sp = frame_queue_peek_writable(&is->subpq)))
             return 0;
 
-        if ((got_subtitle = decoder_decode_frame(&is->subdec, NULL, &sp->sub)) < 0)
+        if ((got_subtitle = decoder_decode_frame(&is->subdec, sp->frame)) < 0)
             break;
 
         pts = 0;
 
-        if (got_subtitle && sp->sub.format == 0) {
-            if (sp->sub.pts != AV_NOPTS_VALUE)
-                pts = sp->sub.pts / (double)AV_TIME_BASE;
+        if (got_subtitle && sp->frame->format == AV_SUBTITLE_FMT_BITMAP) {
+            if (sp->frame->subtitle_timing.start_pts != AV_NOPTS_VALUE)
+                pts = (double)sp->frame->subtitle_timing.start_pts / (double)AV_TIME_BASE;
             sp->pts = pts;
             sp->serial = is->subdec.pkt_serial;
             sp->width = is->subdec.avctx->width;
@@ -2246,7 +2238,7 @@ static int subtitle_thread(void *arg)
             /* now we can update the picture count */
             frame_queue_push(&is->subpq);
         } else if (got_subtitle) {
-            avsubtitle_free(&sp->sub);
+            av_frame_free(&sp->frame);
         }
     }
     return 0;
diff --git a/fftools/ffprobe.c b/fftools/ffprobe.c
index 5020ba484c..4668cf5dc6 100644
--- a/fftools/ffprobe.c
+++ b/fftools/ffprobe.c
@@ -2522,22 +2522,42 @@ static void show_packet(WriterContext *w, InputFile *ifile, AVPacket *pkt, int p
     fflush(stdout);
 }
 
-static void show_subtitle(WriterContext *w, AVSubtitle *sub, AVStream *stream,
+static void show_subtitle(WriterContext *w, AVFrame *sub, AVStream *stream,
                           AVFormatContext *fmt_ctx)
 {
     AVBPrint pbuf;
+    const char *s;
 
     av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
 
     writer_print_section_header(w, SECTION_ID_SUBTITLE);
 
     print_str ("media_type",         "subtitle");
-    print_ts  ("pts",                 sub->pts);
-    print_time("pts_time",            sub->pts, &AV_TIME_BASE_Q);
-    print_int ("format",              sub->format);
-    print_int ("start_display_time",  sub->start_display_time);
-    print_int ("end_display_time",    sub->end_display_time);
-    print_int ("num_rects",           sub->num_rects);
+    print_ts  ("pts",                 sub->subtitle_timing.start_pts);
+    print_time("pts_time",            sub->subtitle_timing.start_pts, &AV_TIME_BASE_Q);
+    print_time("duration",            sub->subtitle_timing.duration, &AV_TIME_BASE_Q);
+
+    // Remain compatible with previous outputs
+    switch (sub->format) {
+    case AV_SUBTITLE_FMT_BITMAP:
+        print_int ("format",         0);
+        break;
+    case AV_SUBTITLE_FMT_TEXT:
+        print_int ("format",         1);
+        break;
+    case AV_SUBTITLE_FMT_ASS:
+        print_int ("format",         1);
+        break;
+    default:
+        print_int ("format",         -1);
+        break;
+    }
+
+    s = av_get_subtitle_fmt_name(sub->format);
+    if (s) print_str    ("format_str", s);
+    else   print_str_opt("format_str", "unknown");
+
+    print_int ("num_subtitle_rects",           sub->num_subtitle_areas);
 
     writer_print_section_footer(w);
 
@@ -2705,7 +2725,6 @@ static av_always_inline int process_frame(WriterContext *w,
     AVFormatContext *fmt_ctx = ifile->fmt_ctx;
     AVCodecContext *dec_ctx = ifile->streams[pkt->stream_index].dec_ctx;
     AVCodecParameters *par = ifile->streams[pkt->stream_index].st->codecpar;
-    AVSubtitle sub;
     int ret = 0, got_frame = 0;
 
     clear_log(1);
@@ -2713,6 +2732,7 @@ static av_always_inline int process_frame(WriterContext *w,
         switch (par->codec_type) {
         case AVMEDIA_TYPE_VIDEO:
         case AVMEDIA_TYPE_AUDIO:
+        case AVMEDIA_TYPE_SUBTITLE:
             if (*packet_new) {
                 ret = avcodec_send_packet(dec_ctx, pkt);
                 if (ret == AVERROR(EAGAIN)) {
@@ -2731,12 +2751,6 @@ static av_always_inline int process_frame(WriterContext *w,
                 }
             }
             break;
-
-        case AVMEDIA_TYPE_SUBTITLE:
-            if (*packet_new)
-                ret = avcodec_decode_subtitle2(dec_ctx, &sub, &got_frame, pkt);
-            *packet_new = 0;
-            break;
         default:
             *packet_new = 0;
         }
@@ -2751,12 +2765,11 @@ static av_always_inline int process_frame(WriterContext *w,
         nb_streams_frames[pkt->stream_index]++;
         if (do_show_frames)
             if (is_sub)
-                show_subtitle(w, &sub, ifile->streams[pkt->stream_index].st, fmt_ctx);
+                show_subtitle(w, frame, ifile->streams[pkt->stream_index].st, fmt_ctx);
             else
                 show_frame(w, frame, ifile->streams[pkt->stream_index].st, fmt_ctx);
-        if (is_sub)
-            avsubtitle_free(&sub);
     }
+
     return got_frame || *packet_new;
 }
 
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v5 09/25] avfilter/subtitles: Add subtitles.c for subtitle frame allocation
  2022-06-25  9:57       ` [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022 ffmpegagent
                           ` (7 preceding siblings ...)
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 08/25] fftools/play, probe: Adjust for subtitle changes softworkz
@ 2022-06-25  9:57         ` softworkz
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 10/25] avfilter/avfilter: Handle subtitle frames softworkz
                           ` (18 subsequent siblings)
  27 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-25  9:57 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Analog to avfilter/video.c and avfilter/audio.c

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavfilter/Makefile    |  1 +
 libavfilter/avfilter.c  |  4 +++
 libavfilter/internal.h  |  1 +
 libavfilter/subtitles.c | 63 +++++++++++++++++++++++++++++++++++++++++
 libavfilter/subtitles.h | 44 ++++++++++++++++++++++++++++
 5 files changed, 113 insertions(+)
 create mode 100644 libavfilter/subtitles.c
 create mode 100644 libavfilter/subtitles.h

diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 7ba1c8a861..af52a77ebc 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -20,6 +20,7 @@ OBJS = allfilters.o                                                     \
        framequeue.o                                                     \
        graphdump.o                                                      \
        graphparser.o                                                    \
+       subtitles.o                                                      \
        version.o                                                        \
        video.o                                                          \
 
diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c
index 965f5d0f63..28c5430c3e 100644
--- a/libavfilter/avfilter.c
+++ b/libavfilter/avfilter.c
@@ -42,6 +42,7 @@
 #include "formats.h"
 #include "framepool.h"
 #include "internal.h"
+#include "subtitles.h"
 
 static void tlog_ref(void *ctx, AVFrame *ref, int end)
 {
@@ -1452,6 +1453,9 @@ int ff_inlink_make_frame_writable(AVFilterLink *link, AVFrame **rframe)
     case AVMEDIA_TYPE_AUDIO:
         out = ff_get_audio_buffer(link, frame->nb_samples);
         break;
+    case AVMEDIA_TYPE_SUBTITLE:
+        out = ff_get_subtitles_buffer(link, link->format);
+        break;
     default:
         return AVERROR(EINVAL);
     }
diff --git a/libavfilter/internal.h b/libavfilter/internal.h
index 0f8da367d0..6c8496879a 100644
--- a/libavfilter/internal.h
+++ b/libavfilter/internal.h
@@ -89,6 +89,7 @@ struct AVFilterPad {
     union {
         AVFrame *(*video)(AVFilterLink *link, int w, int h);
         AVFrame *(*audio)(AVFilterLink *link, int nb_samples);
+        AVFrame *(*subtitle)(AVFilterLink *link, int format);
     } get_buffer;
 
     /**
diff --git a/libavfilter/subtitles.c b/libavfilter/subtitles.c
new file mode 100644
index 0000000000..951bfd612c
--- /dev/null
+++ b/libavfilter/subtitles.c
@@ -0,0 +1,63 @@
+/*
+ * 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
+ */
+
+#include "libavutil/common.h"
+
+#include "subtitles.h"
+#include "avfilter.h"
+#include "internal.h"
+
+
+AVFrame *ff_null_get_subtitles_buffer(AVFilterLink *link, int format)
+{
+    return ff_get_subtitles_buffer(link->dst->outputs[0], format);
+}
+
+AVFrame *ff_default_get_subtitles_buffer(AVFilterLink *link, int format)
+{
+    AVFrame *frame;
+
+    frame = av_frame_alloc();
+    if (!frame)
+        return NULL;
+
+    frame->format = format;
+    frame->type = AVMEDIA_TYPE_SUBTITLE;
+
+    if (av_frame_get_buffer2(frame, 0) < 0) {
+        av_frame_free(&frame);
+        return NULL;
+    }
+
+    return frame;
+}
+
+AVFrame *ff_get_subtitles_buffer(AVFilterLink *link, int format)
+{
+    AVFrame *ret = NULL;
+
+    if (link->dstpad->get_buffer.subtitle)
+        ret = link->dstpad->get_buffer.subtitle(link, format);
+
+    if (!ret)
+        ret = ff_default_get_subtitles_buffer(link, format);
+
+    return ret;
+}
diff --git a/libavfilter/subtitles.h b/libavfilter/subtitles.h
new file mode 100644
index 0000000000..4a9115126e
--- /dev/null
+++ b/libavfilter/subtitles.h
@@ -0,0 +1,44 @@
+/*
+ * 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
+ */
+
+#ifndef AVFILTER_SUBTITLES_H
+#define AVFILTER_SUBTITLES_H
+
+#include "avfilter.h"
+#include "internal.h"
+
+/** default handler for get_subtitles_buffer() for subtitle inputs */
+AVFrame *ff_default_get_subtitles_buffer(AVFilterLink *link, int format);
+
+/** get_subtitles_buffer() handler for filters which simply pass subtitles along */
+AVFrame *ff_null_get_subtitles_buffer(AVFilterLink *link, int format);
+
+/**
+ * Request a subtitles frame with a specific set of permissions.
+ *
+ * @param link           the output link to the filter from which the buffer will
+ *                       be requested
+ * @param format         The subtitles format.
+ * @return               A reference to the frame. This must be unreferenced with
+ *                       av_frame_free when you are finished with it.
+*/
+AVFrame *ff_get_subtitles_buffer(AVFilterLink *link, int format);
+
+#endif /* AVFILTER_SUBTITLES_H */
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v5 10/25] avfilter/avfilter: Handle subtitle frames
  2022-06-25  9:57       ` [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022 ffmpegagent
                           ` (8 preceding siblings ...)
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 09/25] avfilter/subtitles: Add subtitles.c for subtitle frame allocation softworkz
@ 2022-06-25  9:57         ` softworkz
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 11/25] avfilter/avfilter: Fix hardcoded input index softworkz
                           ` (17 subsequent siblings)
  27 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-25  9:57 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavfilter/avfilter.c      | 12 +++++++++---
 libavfilter/avfilter.h      | 11 +++++++++++
 libavfilter/avfiltergraph.c |  5 +++++
 libavfilter/formats.c       | 16 ++++++++++++++++
 libavfilter/formats.h       |  3 +++
 libavfilter/internal.h      | 18 +++++++++++++++---
 6 files changed, 59 insertions(+), 6 deletions(-)

diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c
index 28c5430c3e..30745f41cb 100644
--- a/libavfilter/avfilter.c
+++ b/libavfilter/avfilter.c
@@ -52,7 +52,8 @@ static void tlog_ref(void *ctx, AVFrame *ref, int end)
             ref->linesize[0], ref->linesize[1], ref->linesize[2], ref->linesize[3],
             ref->pts, ref->pkt_pos);
 
-    if (ref->width) {
+    switch(ref->type) {
+    case AVMEDIA_TYPE_VIDEO:
         ff_tlog(ctx, " a:%d/%d s:%dx%d i:%c iskey:%d type:%c",
                 ref->sample_aspect_ratio.num, ref->sample_aspect_ratio.den,
                 ref->width, ref->height,
@@ -60,12 +61,13 @@ static void tlog_ref(void *ctx, AVFrame *ref, int end)
                 ref->top_field_first ? 'T' : 'B',    /* Top / Bottom */
                 ref->key_frame,
                 av_get_picture_type_char(ref->pict_type));
-    }
-    if (ref->nb_samples) {
+        break;
+    case AVMEDIA_TYPE_AUDIO:
         ff_tlog(ctx, " cl:%"PRId64"d n:%d r:%d",
                 ref->channel_layout,
                 ref->nb_samples,
                 ref->sample_rate);
+        break;
     }
 
     ff_tlog(ctx, "]%s", end ? "\n" : "");
@@ -1013,6 +1015,10 @@ int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
             av_assert1(frame->width               == link->w);
             av_assert1(frame->height               == link->h);
         }
+    } else if (link->type == AVMEDIA_TYPE_SUBTITLE) {
+        if (frame->format != link->format) {
+            av_log(link->dst, AV_LOG_WARNING, "Subtitle format change from %d to %d\n", link->format, frame->format);
+        }
     } else {
         if (frame->format != link->format) {
             av_log(link->dst, AV_LOG_ERROR, "Format change is not supported\n");
diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h
index 2e8197c9a6..c436f304bc 100644
--- a/libavfilter/avfilter.h
+++ b/libavfilter/avfilter.h
@@ -45,6 +45,7 @@
 #include "libavutil/log.h"
 #include "libavutil/samplefmt.h"
 #include "libavutil/pixfmt.h"
+#include "libavutil/subfmt.h"
 #include "libavutil/rational.h"
 
 #include "libavfilter/version_major.h"
@@ -349,6 +350,12 @@ typedef struct AVFilter {
          * and outputs use the same sample rate and channel count/layout.
          */
         const enum AVSampleFormat *samples_list;
+        /**
+         * Analogous to pixels, but delimited by AV_SUBTITLE_FMT_NONE
+         * and restricted to filters that only have AVMEDIA_TYPE_SUBTITLE
+         * inputs and outputs.
+         */
+        const enum AVSubtitleType *subs_list;
         /**
          * Equivalent to { pix_fmt, AV_PIX_FMT_NONE } as pixels_list.
          */
@@ -357,6 +364,10 @@ typedef struct AVFilter {
          * Equivalent to { sample_fmt, AV_SAMPLE_FMT_NONE } as samples_list.
          */
         enum AVSampleFormat sample_fmt;
+        /**
+         * Equivalent to { sub_fmt, AV_SUBTITLE_FMT_NONE } as subs_list.
+         */
+        enum AVSubtitleType sub_fmt;
     } formats;
 
     int priv_size;      ///< size of private data to allocate for the filter
diff --git a/libavfilter/avfiltergraph.c b/libavfilter/avfiltergraph.c
index b7dbfc063b..f37be0203b 100644
--- a/libavfilter/avfiltergraph.c
+++ b/libavfilter/avfiltergraph.c
@@ -309,6 +309,8 @@ static int filter_link_check_formats(void *log, AVFilterLink *link, AVFilterForm
             return ret;
         break;
 
+    case AVMEDIA_TYPE_SUBTITLE:
+        return 0;
     default:
         av_assert0(!"reached");
     }
@@ -439,6 +441,9 @@ static int query_formats(AVFilterGraph *graph, void *log_ctx)
             if (!link)
                 continue;
 
+            if (link->type == AVMEDIA_TYPE_SUBTITLE)
+                continue;
+
             neg = ff_filter_get_negotiation(link);
             av_assert0(neg);
             for (neg_step = 1; neg_step < neg->nb_mergers; neg_step++) {
diff --git a/libavfilter/formats.c b/libavfilter/formats.c
index e8c2888c0c..12585ed428 100644
--- a/libavfilter/formats.c
+++ b/libavfilter/formats.c
@@ -19,6 +19,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
+#include "libavutil/subfmt.h"
 #include "libavutil/avassert.h"
 #include "libavutil/channel_layout.h"
 #include "libavutil/common.h"
@@ -491,6 +492,13 @@ AVFilterFormats *ff_all_formats(enum AVMediaType type)
                 return NULL;
             fmt++;
         }
+    } else if (type == AVMEDIA_TYPE_SUBTITLE) {
+        if (ff_add_format(&ret, AV_SUBTITLE_FMT_BITMAP) < 0)
+            return NULL;
+        if (ff_add_format(&ret, AV_SUBTITLE_FMT_ASS) < 0)
+            return NULL;
+        if (ff_add_format(&ret, AV_SUBTITLE_FMT_TEXT) < 0)
+            return NULL;
     }
 
     return ret;
@@ -774,6 +782,10 @@ int ff_default_query_formats(AVFilterContext *ctx)
         type    = AVMEDIA_TYPE_AUDIO;
         formats = ff_make_format_list(f->formats.samples_list);
         break;
+    case FF_FILTER_FORMATS_SUBFMTS_LIST:
+        type    = AVMEDIA_TYPE_SUBTITLE;
+        formats = ff_make_format_list(f->formats.subs_list);
+        break;
     case FF_FILTER_FORMATS_SINGLE_PIXFMT:
         type    = AVMEDIA_TYPE_VIDEO;
         formats = ff_make_formats_list_singleton(f->formats.pix_fmt);
@@ -782,6 +794,10 @@ int ff_default_query_formats(AVFilterContext *ctx)
         type    = AVMEDIA_TYPE_AUDIO;
         formats = ff_make_formats_list_singleton(f->formats.sample_fmt);
         break;
+    case FF_FILTER_FORMATS_SINGLE_SUBFMT:
+        type    = AVMEDIA_TYPE_SUBTITLE;
+        formats = ff_make_formats_list_singleton(f->formats.sub_fmt);
+        break;
     default:
         av_assert2(!"Unreachable");
     /* Intended fallthrough */
diff --git a/libavfilter/formats.h b/libavfilter/formats.h
index 22224dce2d..6cf952a059 100644
--- a/libavfilter/formats.h
+++ b/libavfilter/formats.h
@@ -183,6 +183,9 @@ av_warn_unused_result
 int ff_add_channel_layout(AVFilterChannelLayouts **l,
                           const AVChannelLayout *channel_layout);
 
+av_warn_unused_result
+int ff_add_subtitle_type(AVFilterFormats **avff, int64_t fmt);
+
 /**
  * Add *ref as a new reference to f.
  */
diff --git a/libavfilter/internal.h b/libavfilter/internal.h
index 6c8496879a..7972c5c6de 100644
--- a/libavfilter/internal.h
+++ b/libavfilter/internal.h
@@ -148,9 +148,11 @@ static av_always_inline int ff_filter_execute(AVFilterContext *ctx, avfilter_act
 
 enum FilterFormatsState {
     /**
-     * The default value meaning that this filter supports all formats
-     * and (for audio) sample rates and channel layouts/counts as long
-     * as these properties agree for all inputs and outputs.
+     * The default value meaning that this filter supports
+     * - For video:     all formats
+     * - For audio:     all sample rates and channel layouts/counts
+     * - For subtitles: all subtitle formats
+     * as long as these properties agree for all inputs and outputs.
      * This state is only allowed in case all inputs and outputs actually
      * have the same type.
      * The union is unused in this state.
@@ -161,8 +163,10 @@ enum FilterFormatsState {
     FF_FILTER_FORMATS_QUERY_FUNC,       ///< formats.query active.
     FF_FILTER_FORMATS_PIXFMT_LIST,      ///< formats.pixels_list active.
     FF_FILTER_FORMATS_SAMPLEFMTS_LIST,  ///< formats.samples_list active.
+    FF_FILTER_FORMATS_SUBFMTS_LIST,     ///< formats.subs_list active.
     FF_FILTER_FORMATS_SINGLE_PIXFMT,    ///< formats.pix_fmt active
     FF_FILTER_FORMATS_SINGLE_SAMPLEFMT, ///< formats.sample_fmt active.
+    FF_FILTER_FORMATS_SINGLE_SUBFMT,    ///< formats.sub_fmt active.
 };
 
 #define FILTER_QUERY_FUNC(func)        \
@@ -174,16 +178,24 @@ enum FilterFormatsState {
 #define FILTER_SAMPLEFMTS_ARRAY(array) \
         .formats.samples_list = array, \
         .formats_state        = FF_FILTER_FORMATS_SAMPLEFMTS_LIST
+#define FILTER_SUBFMTS_ARRAY(array) \
+        .formats.subs_list = array, \
+        .formats_state        = FF_FILTER_FORMATS_SUBFMTS_LIST
 #define FILTER_PIXFMTS(...)            \
     FILTER_PIXFMTS_ARRAY(((const enum AVPixelFormat []) { __VA_ARGS__, AV_PIX_FMT_NONE }))
 #define FILTER_SAMPLEFMTS(...)         \
     FILTER_SAMPLEFMTS_ARRAY(((const enum AVSampleFormat[]) { __VA_ARGS__, AV_SAMPLE_FMT_NONE }))
+#define FILTER_SUBFMTS(...)         \
+    FILTER_SUBFMTS_ARRAY(((const enum AVSubtitleType[]) { __VA_ARGS__, AV_SUBTITLE_FMT_NONE }))
 #define FILTER_SINGLE_PIXFMT(pix_fmt_)  \
         .formats.pix_fmt = pix_fmt_,    \
         .formats_state   = FF_FILTER_FORMATS_SINGLE_PIXFMT
 #define FILTER_SINGLE_SAMPLEFMT(sample_fmt_) \
         .formats.sample_fmt = sample_fmt_,   \
         .formats_state      = FF_FILTER_FORMATS_SINGLE_SAMPLEFMT
+#define FILTER_SINGLE_SUBFMT(sub_fmt_) \
+        .formats.sub_fmt = sub_fmt_,   \
+        .formats_state      = FF_FILTER_FORMATS_SINGLE_SUBFMT
 
 #define FILTER_INOUTPADS(inout, array) \
        .inout        = array, \
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v5 11/25] avfilter/avfilter: Fix hardcoded input index
  2022-06-25  9:57       ` [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022 ffmpegagent
                           ` (9 preceding siblings ...)
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 10/25] avfilter/avfilter: Handle subtitle frames softworkz
@ 2022-06-25  9:57         ` softworkz
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 12/25] avfilter/sbuffer: Add sbuffersrc and sbuffersink filters softworkz
                           ` (16 subsequent siblings)
  27 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-25  9:57 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

This fix targets (rare) cases where multiple input pads have a
.filter_frame function. ff_request_frame_to_filter needs
to call ff_request_frame with the correct input pad
instead of the hardcoded first one.

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavfilter/avfilter.c | 18 +++++++++++++-----
 1 file changed, 13 insertions(+), 5 deletions(-)

diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c
index 30745f41cb..6cbfc54a85 100644
--- a/libavfilter/avfilter.c
+++ b/libavfilter/avfilter.c
@@ -443,7 +443,7 @@ static int64_t guess_status_pts(AVFilterContext *ctx, int status, AVRational lin
     return AV_NOPTS_VALUE;
 }
 
-static int ff_request_frame_to_filter(AVFilterLink *link)
+static int ff_request_frame_to_filter(AVFilterLink *link, int input_index)
 {
     int ret = -1;
 
@@ -452,8 +452,8 @@ static int ff_request_frame_to_filter(AVFilterLink *link)
     link->frame_blocked_in = 1;
     if (link->srcpad->request_frame)
         ret = link->srcpad->request_frame(link);
-    else if (link->src->inputs[0])
-        ret = ff_request_frame(link->src->inputs[0]);
+    else if (link->src->inputs[input_index])
+        ret = ff_request_frame(link->src->inputs[input_index]);
     if (ret < 0) {
         if (ret != AVERROR(EAGAIN) && ret != link->status_in)
             ff_avfilter_link_set_in_status(link, ret, guess_status_pts(link->src, ret, link->time_base));
@@ -1153,6 +1153,14 @@ static int forward_status_change(AVFilterContext *filter, AVFilterLink *in)
 {
     unsigned out = 0, progress = 0;
     int ret;
+    int input_index = 0;
+
+    for (int i = 0; i < in->dst->nb_inputs; i++) {
+        if (&in->dst->input_pads[i] == in->dstpad) {
+            input_index = i;
+            break;
+        }
+    }
 
     av_assert0(!in->status_out);
     if (!filter->nb_outputs) {
@@ -1162,7 +1170,7 @@ static int forward_status_change(AVFilterContext *filter, AVFilterLink *in)
     while (!in->status_out) {
         if (!filter->outputs[out]->status_in) {
             progress++;
-            ret = ff_request_frame_to_filter(filter->outputs[out]);
+            ret = ff_request_frame_to_filter(filter->outputs[out], input_index);
             if (ret < 0)
                 return ret;
         }
@@ -1199,7 +1207,7 @@ static int ff_filter_activate_default(AVFilterContext *filter)
     for (i = 0; i < filter->nb_outputs; i++) {
         if (filter->outputs[i]->frame_wanted_out &&
             !filter->outputs[i]->frame_blocked_in) {
-            return ff_request_frame_to_filter(filter->outputs[i]);
+            return ff_request_frame_to_filter(filter->outputs[i], 0);
         }
     }
     return FFERROR_NOT_READY;
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v5 12/25] avfilter/sbuffer: Add sbuffersrc and sbuffersink filters
  2022-06-25  9:57       ` [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022 ffmpegagent
                           ` (10 preceding siblings ...)
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 11/25] avfilter/avfilter: Fix hardcoded input index softworkz
@ 2022-06-25  9:57         ` softworkz
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 13/25] avfilter/overlaygraphicsubs: Add overlaygraphicsubs and graphicsub2video filters softworkz
                           ` (15 subsequent siblings)
  27 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-25  9:57 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 configure                |  2 +-
 libavfilter/allfilters.c |  2 ++
 libavfilter/buffersink.c | 54 ++++++++++++++++++++++++++++++
 libavfilter/buffersink.h |  7 ++++
 libavfilter/buffersrc.c  | 72 ++++++++++++++++++++++++++++++++++++++++
 libavfilter/buffersrc.h  |  1 +
 6 files changed, 137 insertions(+), 1 deletion(-)

diff --git a/configure b/configure
index 3a97610209..0580e4a536 100755
--- a/configure
+++ b/configure
@@ -7880,7 +7880,7 @@ print_enabled_components(){
         fi
     done
     if [ "$name" = "filter_list" ]; then
-        for c in asrc_abuffer vsrc_buffer asink_abuffer vsink_buffer; do
+        for c in asrc_abuffer vsrc_buffer ssrc_sbuffer asink_abuffer vsink_buffer ssink_sbuffer; do
             printf "    &ff_%s,\n" $c >> $TMPH
         done
     fi
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index ec70feef11..5288480a55 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -571,8 +571,10 @@ extern const AVFilter ff_avsrc_movie;
  * being the same while having different 'types'). */
 extern  const AVFilter ff_asrc_abuffer;
 extern  const AVFilter ff_vsrc_buffer;
+extern  const AVFilter ff_ssrc_sbuffer;
 extern  const AVFilter ff_asink_abuffer;
 extern  const AVFilter ff_vsink_buffer;
+extern  const AVFilter ff_ssink_sbuffer;
 extern const AVFilter ff_af_afifo;
 extern const AVFilter ff_vf_fifo;
 
diff --git a/libavfilter/buffersink.c b/libavfilter/buffersink.c
index e269cf72d1..204e9bad6c 100644
--- a/libavfilter/buffersink.c
+++ b/libavfilter/buffersink.c
@@ -30,6 +30,8 @@
 #include "libavutil/internal.h"
 #include "libavutil/opt.h"
 
+#include "libavcodec/avcodec.h"
+
 #define FF_INTERNAL_FIELDS 1
 #include "framequeue.h"
 
@@ -61,6 +63,10 @@ typedef struct BufferSinkContext {
     int *sample_rates;                  ///< list of accepted sample rates
     int sample_rates_size;
 
+    /* only used for subtitles */
+    enum AVSubtitleType *subtitle_types;     ///< list of accepted subtitle types, must be terminated with -1
+    int subtitle_types_size;
+
     AVFrame *peeked_frame;
 } BufferSinkContext;
 
@@ -372,6 +378,28 @@ static int asink_query_formats(AVFilterContext *ctx)
     return 0;
 }
 
+static int ssink_query_formats(AVFilterContext *ctx)
+{
+    BufferSinkContext *buf = ctx->priv;
+    AVFilterFormats *formats = NULL;
+    unsigned i;
+    int ret;
+
+    CHECK_LIST_SIZE(subtitle_types)
+    if (buf->subtitle_types_size) {
+        for (i = 0; i < NB_ITEMS(buf->subtitle_types); i++)
+            if ((ret = ff_add_format(&formats, buf->subtitle_types[i])) < 0)
+                return ret;
+        if ((ret = ff_set_common_formats(ctx, formats)) < 0)
+            return ret;
+    } else {
+        if ((ret = ff_default_query_formats(ctx)) < 0)
+            return ret;
+    }
+
+    return 0;
+}
+
 #define OFFSET(x) offsetof(BufferSinkContext, x)
 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
 static const AVOption buffersink_options[] = {
@@ -395,9 +423,16 @@ static const AVOption abuffersink_options[] = {
     { NULL },
 };
 #undef FLAGS
+#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_SUBTITLE_PARAM
+static const AVOption sbuffersink_options[] = {
+    { "subtitle_types", "set the supported subtitle formats", OFFSET(subtitle_types), AV_OPT_TYPE_BINARY, .flags = FLAGS },
+    { NULL },
+};
+#undef FLAGS
 
 AVFILTER_DEFINE_CLASS(buffersink);
 AVFILTER_DEFINE_CLASS(abuffersink);
+AVFILTER_DEFINE_CLASS(sbuffersink);
 
 static const AVFilterPad avfilter_vsink_buffer_inputs[] = {
     {
@@ -436,3 +471,22 @@ const AVFilter ff_asink_abuffer = {
     .outputs       = NULL,
     FILTER_QUERY_FUNC(asink_query_formats),
 };
+
+static const AVFilterPad avfilter_ssink_sbuffer_inputs[] = {
+    {
+        .name = "default",
+        .type = AVMEDIA_TYPE_SUBTITLE,
+    },
+};
+
+const AVFilter ff_ssink_sbuffer = {
+    .name          = "sbuffersink",
+    .description   = NULL_IF_CONFIG_SMALL("Buffer subtitle frames, and make them available to the end of the filter graph."),
+    .priv_class    = &sbuffersink_class,
+    .priv_size     = sizeof(BufferSinkContext),
+    .init          = common_init,
+    .activate      = activate,
+    FILTER_INPUTS(avfilter_ssink_sbuffer_inputs),
+    .outputs       = NULL,
+    FILTER_QUERY_FUNC(ssink_query_formats),
+};
diff --git a/libavfilter/buffersink.h b/libavfilter/buffersink.h
index 01e7c747d8..42c164e670 100644
--- a/libavfilter/buffersink.h
+++ b/libavfilter/buffersink.h
@@ -128,6 +128,13 @@ typedef struct AVABufferSinkParams {
  */
 attribute_deprecated
 AVABufferSinkParams *av_abuffersink_params_alloc(void);
+
+/**
+ * Deprecated and unused struct to use for initializing an sbuffersink context.
+ */
+typedef struct AVSBufferSinkParams {
+    const int *subtitle_type;
+} AVSBufferSinkParams;
 #endif
 
 /**
diff --git a/libavfilter/buffersrc.c b/libavfilter/buffersrc.c
index a3190468bb..1679c54f17 100644
--- a/libavfilter/buffersrc.c
+++ b/libavfilter/buffersrc.c
@@ -39,6 +39,7 @@
 #include "formats.h"
 #include "internal.h"
 #include "video.h"
+#include "libavcodec/avcodec.h"
 
 typedef struct BufferSourceContext {
     const AVClass    *class;
@@ -63,6 +64,9 @@ typedef struct BufferSourceContext {
     char    *channel_layout_str;
     AVChannelLayout ch_layout;
 
+    /* subtitle only */
+    enum AVSubtitleType subtitle_type;
+
     int eof;
 } BufferSourceContext;
 
@@ -143,6 +147,13 @@ FF_ENABLE_DEPRECATION_WARNINGS
                 return ret;
         }
         break;
+    case AVMEDIA_TYPE_SUBTITLE:
+        s->subtitle_type = param->format;
+        if (param->width > 0)
+            s->w = param->width;
+        if (param->height > 0)
+            s->h = param->height;
+        break;
     default:
         return AVERROR_BUG;
     }
@@ -224,6 +235,8 @@ FF_ENABLE_DEPRECATION_WARNINGS
             CHECK_AUDIO_PARAM_CHANGE(ctx, s, frame->sample_rate, frame->ch_layout,
                                      frame->format, frame->pts);
             break;
+        case AVMEDIA_TYPE_SUBTITLE:
+            break;
         default:
             return AVERROR(EINVAL);
         }
@@ -296,6 +309,7 @@ unsigned av_buffersrc_get_nb_failed_requests(AVFilterContext *buffer_src)
 #define OFFSET(x) offsetof(BufferSourceContext, x)
 #define A AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_AUDIO_PARAM
 #define V AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
+#define S AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_SUBTITLE_PARAM
 
 static const AVOption buffer_options[] = {
     { "width",         NULL,                     OFFSET(w),                AV_OPT_TYPE_INT,      { .i64 = 0 }, 0, INT_MAX, V },
@@ -325,6 +339,16 @@ static const AVOption abuffer_options[] = {
 
 AVFILTER_DEFINE_CLASS(abuffer);
 
+static const AVOption sbuffer_options[] = {
+    { "time_base",     NULL, OFFSET(time_base),            AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, 0, INT_MAX, S },
+    { "subtitle_type", NULL, OFFSET(subtitle_type),        AV_OPT_TYPE_INT,      { .i64 = 0 }, 0, INT_MAX, S },
+    { "width",         NULL, OFFSET(w),                    AV_OPT_TYPE_INT,      { .i64 = 0 }, 0, INT_MAX, V },
+    { "height",        NULL, OFFSET(h),                    AV_OPT_TYPE_INT,      { .i64 = 0 }, 0, INT_MAX, V },
+    { NULL },
+};
+
+AVFILTER_DEFINE_CLASS(sbuffer);
+
 static av_cold int init_audio(AVFilterContext *ctx)
 {
     BufferSourceContext *s = ctx->priv;
@@ -393,6 +417,21 @@ FF_ENABLE_DEPRECATION_WARNINGS
     return ret;
 }
 
+static av_cold int init_subtitle(AVFilterContext *ctx)
+{
+    BufferSourceContext *c = ctx->priv;
+
+    if (c->subtitle_type == AV_SUBTITLE_FMT_BITMAP)
+        av_log(ctx, AV_LOG_VERBOSE, "graphical subtitles - w:%d h:%d tb:%d/%d\n",
+               c->w, c->h, c->time_base.num, c->time_base.den);
+    else
+        av_log(ctx, AV_LOG_VERBOSE, "text subtitles -  w:%d h:%d tb:%d/%d\n",
+               c->w, c->h, c->time_base.num, c->time_base.den);
+
+    return 0;
+}
+
+
 static av_cold void uninit(AVFilterContext *ctx)
 {
     BufferSourceContext *s = ctx->priv;
@@ -426,6 +465,11 @@ static int query_formats(AVFilterContext *ctx)
         if ((ret = ff_set_common_channel_layouts(ctx, channel_layouts)) < 0)
             return ret;
         break;
+    case AVMEDIA_TYPE_SUBTITLE:
+        if ((ret = ff_add_format         (&formats, c->subtitle_type)) < 0 ||
+            (ret = ff_set_common_formats (ctx     , formats   )) < 0)
+            return ret;
+        break;
     default:
         return AVERROR(EINVAL);
     }
@@ -456,6 +500,11 @@ static int config_props(AVFilterLink *link)
                 return ret;
         }
         break;
+    case AVMEDIA_TYPE_SUBTITLE:
+        link->format = c->subtitle_type;
+        link->w = c->w;
+        link->h = c->h;
+        break;
     default:
         return AVERROR(EINVAL);
     }
@@ -520,3 +569,26 @@ const AVFilter ff_asrc_abuffer = {
     FILTER_QUERY_FUNC(query_formats),
     .priv_class = &abuffer_class,
 };
+
+static const AVFilterPad avfilter_ssrc_sbuffer_outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_SUBTITLE,
+        .request_frame = request_frame,
+        .config_props  = config_props,
+    },
+};
+
+const AVFilter ff_ssrc_sbuffer = {
+    .name          = "sbuffer",
+    .description   = NULL_IF_CONFIG_SMALL("Buffer subtitle frames, and make them accessible to the filterchain."),
+    .priv_size     = sizeof(BufferSourceContext),
+
+    .init      = init_subtitle,
+    .uninit    = uninit,
+
+    .inputs    = NULL,
+    FILTER_OUTPUTS(avfilter_ssrc_sbuffer_outputs),
+    FILTER_QUERY_FUNC(query_formats),
+    .priv_class = &sbuffer_class,
+};
diff --git a/libavfilter/buffersrc.h b/libavfilter/buffersrc.h
index 3b248b37cd..45a657bbb6 100644
--- a/libavfilter/buffersrc.h
+++ b/libavfilter/buffersrc.h
@@ -74,6 +74,7 @@ typedef struct AVBufferSrcParameters {
     /**
      * video: the pixel format, value corresponds to enum AVPixelFormat
      * audio: the sample format, value corresponds to enum AVSampleFormat
+     * subtitles: the subtitle format, value corresponds to enum AVSubtitleType
      */
     int format;
     /**
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v5 13/25] avfilter/overlaygraphicsubs: Add overlaygraphicsubs and graphicsub2video filters
  2022-06-25  9:57       ` [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022 ffmpegagent
                           ` (11 preceding siblings ...)
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 12/25] avfilter/sbuffer: Add sbuffersrc and sbuffersink filters softworkz
@ 2022-06-25  9:57         ` softworkz
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 14/25] avfilter/overlaytextsubs: Add overlaytextsubs and textsubs2video filters softworkz
                           ` (14 subsequent siblings)
  27 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-25  9:57 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

- overlaygraphicsubs (VS -> V)
  Overlay graphic subtitles onto a video stream

- graphicsub2video {S -> V)
  Converts graphic subtitles to video frames (with alpha)
  Gets auto-inserted for retaining compatibility with
  sub2video command lines

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 doc/filters.texi                    | 118 +++++
 libavfilter/Makefile                |   2 +
 libavfilter/allfilters.c            |   2 +
 libavfilter/vf_overlaygraphicsubs.c | 765 ++++++++++++++++++++++++++++
 4 files changed, 887 insertions(+)
 create mode 100644 libavfilter/vf_overlaygraphicsubs.c

diff --git a/doc/filters.texi b/doc/filters.texi
index e525e87b3c..a77546de3d 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -26990,6 +26990,124 @@ tools.
 
 @c man end VIDEO SINKS
 
+@chapter Subtitle Filters
+@c man begin SUBTITLE FILTERS
+
+When you configure your FFmpeg build, you can disable any of the
+existing filters using @code{--disable-filters}.
+
+Below is a description of the currently available subtitle filters.
+
+@section graphicsub2video
+
+Renders graphic subtitles as video frames.
+
+This filter replaces the previous "sub2video" hack which did the conversion implicitly and up-front as subtitle filtering wasn't possible at that time.
+To retain compatibility with earlier sub2video command lines, this filter is being auto-inserted in those cases.
+
+For overlaying graphicsal subtitles it is recommended to use the 'overlaygraphicsubs' filter which is more efficient and takes less processing resources.
+
+This filter is still useful in cases where the overlay is done with hardware acceleration (e.g. overlay_qsv, overlay_vaapi, overlay_cuda) for preparing the overlay frames.
+
+Inputs:
+@itemize
+@item 0: Subtitles [BITMAP]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Video [RGB32]
+@end itemize
+
+
+It accepts the following parameters:
+
+@table @option
+@item size, s
+Set the size of the output video frame.
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Overlay PGS subtitles
+(not recommended - better use overlaygraphicsubs)
+@example
+ffmpeg -i "https://streams.videolan.org/samples/sub/PGS/Girl_With_The_Dragon_Tattoo_2%3A23%3A56.mkv" -filter_complex "[0:1]graphicsub2video[subs];[0:0][subs]overlay" output.mp4
+@end example
+
+@item
+Overlay PGS subtitles implicitly
+The graphicsub2video is inserted automatically for compatibility with legacy command lines.
+@example
+ffmpeg -i "https://streams.videolan.org/samples/sub/PGS/Girl_With_The_Dragon_Tattoo_2%3A23%3A56.mkv" -filter_complex "[0:0][0:1]overlay" output.mp4
+@end example
+@end itemize
+
+@section overlaygraphicsubs
+
+Overlay graphic subtitles onto a video stream.
+
+This filter can blend graphical subtitles on a video stream directly, i.e. without creating full-size alpha images first.
+The blending operation is limited to the area of the subtitle rectangles, which also means that no processing is done at times where no subtitles are to be displayed.
+
+Inputs:
+@itemize
+@item 0: Video [YUV420P, YUV422P, YUV444P, ARGB, RGBA, ABGR, BGRA, RGB24, BGR24]
+@item 1: Subtitles [BITMAP]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Video (same as input)
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+@item x
+@item y
+Set the expression for the x and y coordinates of the overlaid video
+on the main video. Default value is "0" for both expressions. In case
+the expression is invalid, it is set to a huge value (meaning that the
+overlay will not be displayed within the output visible area).
+
+@item eof_action
+See @ref{framesync}.
+
+@item eval
+Set when the expressions for @option{x}, and @option{y} are evaluated.
+
+It accepts the following values:
+@table @samp
+@item init
+only evaluate expressions once during the filter initialization or
+when a command is processed
+
+@item frame
+evaluate expressions for each incoming frame
+@end table
+
+Default value is @samp{frame}.
+
+@item shortest
+See @ref{framesync}.
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Overlay PGS subtitles
+@example
+ffmpeg -i "https://streams.videolan.org/samples/sub/PGS/Girl_With_The_Dragon_Tattoo_2%3A23%3A56.mkv" -filter_complex "[0:0][0:1]overlaygraphicsubs" output.mp4
+@end example
+@end itemize
+@c man end SUBTITLE FILTERS
+
 @chapter Multimedia Filters
 @c man begin MULTIMEDIA FILTERS
 
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index af52a77ebc..97ec9fc3b3 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -307,6 +307,7 @@ OBJS-$(CONFIG_GBLUR_FILTER)                  += vf_gblur.o
 OBJS-$(CONFIG_GBLUR_VULKAN_FILTER)           += vf_gblur_vulkan.o vulkan.o vulkan_filter.o
 OBJS-$(CONFIG_GEQ_FILTER)                    += vf_geq.o
 OBJS-$(CONFIG_GRADFUN_FILTER)                += vf_gradfun.o
+OBJS-$(CONFIG_GRAPHICSUB2VIDEO_FILTER)       += vf_overlaygraphicsubs.o framesync.o
 OBJS-$(CONFIG_GRAPHMONITOR_FILTER)           += f_graphmonitor.o
 OBJS-$(CONFIG_GRAYWORLD_FILTER)              += vf_grayworld.o
 OBJS-$(CONFIG_GREYEDGE_FILTER)               += vf_colorconstancy.o
@@ -391,6 +392,7 @@ OBJS-$(CONFIG_OVERLAY_OPENCL_FILTER)         += vf_overlay_opencl.o opencl.o \
 OBJS-$(CONFIG_OVERLAY_QSV_FILTER)            += vf_overlay_qsv.o framesync.o
 OBJS-$(CONFIG_OVERLAY_VAAPI_FILTER)          += vf_overlay_vaapi.o framesync.o vaapi_vpp.o
 OBJS-$(CONFIG_OVERLAY_VULKAN_FILTER)         += vf_overlay_vulkan.o vulkan.o vulkan_filter.o
+OBJS-$(CONFIG_OVERLAYGRAPHICSUBS_FILTER)     += vf_overlaygraphicsubs.o framesync.o
 OBJS-$(CONFIG_OWDENOISE_FILTER)              += vf_owdenoise.o
 OBJS-$(CONFIG_PAD_FILTER)                    += vf_pad.o
 OBJS-$(CONFIG_PAD_OPENCL_FILTER)             += vf_pad_opencl.o opencl.o opencl/pad.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 5288480a55..06ac93457d 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -371,6 +371,7 @@ extern const AVFilter ff_vf_overlay_qsv;
 extern const AVFilter ff_vf_overlay_vaapi;
 extern const AVFilter ff_vf_overlay_vulkan;
 extern const AVFilter ff_vf_overlay_cuda;
+extern const AVFilter ff_vf_overlaygraphicsubs;
 extern const AVFilter ff_vf_owdenoise;
 extern const AVFilter ff_vf_pad;
 extern const AVFilter ff_vf_pad_opencl;
@@ -558,6 +559,7 @@ extern const AVFilter ff_avf_showspectrumpic;
 extern const AVFilter ff_avf_showvolume;
 extern const AVFilter ff_avf_showwaves;
 extern const AVFilter ff_avf_showwavespic;
+extern const AVFilter ff_svf_graphicsub2video;
 extern const AVFilter ff_vaf_spectrumsynth;
 
 /* multimedia sources */
diff --git a/libavfilter/vf_overlaygraphicsubs.c b/libavfilter/vf_overlaygraphicsubs.c
new file mode 100644
index 0000000000..7b26d8ef37
--- /dev/null
+++ b/libavfilter/vf_overlaygraphicsubs.c
@@ -0,0 +1,765 @@
+/*
+ * Copyright (c) 2021 softworkz (derived from vf_overlay)
+ * Copyright (c) 2010 Stefano Sabatini
+ * Copyright (c) 2010 Baptiste Coudurier
+ * Copyright (c) 2007 Bobby Bingham
+ *
+ * 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
+ * overlay graphical subtitles on top of a video frame
+ */
+
+#include "libavutil/eval.h"
+#include "libavutil/imgutils.h"
+#include "libavutil/opt.h"
+#include "internal.h"
+#include "drawutils.h"
+#include "framesync.h"
+
+enum var_name {
+    VAR_MAIN_W,    VAR_MW,
+    VAR_MAIN_H,    VAR_MH,
+    VAR_OVERLAY_W, VAR_OW,
+    VAR_OVERLAY_H, VAR_OH,
+    VAR_HSUB,
+    VAR_VSUB,
+    VAR_X,
+    VAR_Y,
+    VAR_N,
+    VAR_POS,
+    VAR_T,
+    VAR_VARS_NB
+};
+
+typedef struct OverlaySubsContext {
+    const AVClass *class;
+    int x, y;                   ///< position of overlaid picture
+    int w, h;
+    AVFrame *outpicref;
+
+    int main_is_packed_rgb;
+    uint8_t main_rgba_map[4];
+    int main_has_alpha;
+    uint8_t overlay_rgba_map[4];
+    int eval_mode;              ///< EvalMode
+    int use_caching;
+    AVFrame *cache_frame;
+
+    FFFrameSync fs;
+
+    int main_pix_step[4];       ///< steps per pixel for each plane of the main output
+    int hsub, vsub;             ///< chroma subsampling values
+    const AVPixFmtDescriptor *main_desc; ///< format descriptor for main input
+
+    double var_values[VAR_VARS_NB];
+    char *x_expr, *y_expr;
+
+    AVExpr *x_pexpr, *y_pexpr;
+
+    int pic_counter;
+} OverlaySubsContext;
+
+static const char *const var_names[] = {
+    "main_w",    "W", ///< width  of the main    video
+    "main_h",    "H", ///< height of the main    video
+    "overlay_w", "w", ///< width  of the overlay video
+    "overlay_h", "h", ///< height of the overlay video
+    "hsub",
+    "vsub",
+    "x",
+    "y",
+    "n",            ///< number of frame
+    "pos",          ///< position in the file
+    "t",            ///< timestamp expressed in seconds
+    NULL
+};
+
+#define MAIN    0
+#define OVERLAY 1
+
+#define R 0
+#define G 1
+#define B 2
+#define A 3
+
+#define Y 0
+#define U 1
+#define V 2
+
+enum EvalMode {
+    EVAL_MODE_INIT,
+    EVAL_MODE_FRAME,
+    EVAL_MODE_NB
+};
+
+static av_cold void overlay_graphicsubs_uninit(AVFilterContext *ctx)
+{
+    OverlaySubsContext *s = ctx->priv;
+
+    av_frame_free(&s->cache_frame);
+    ff_framesync_uninit(&s->fs);
+    av_expr_free(s->x_pexpr); s->x_pexpr = NULL;
+    av_expr_free(s->y_pexpr); s->y_pexpr = NULL;
+}
+
+static inline int normalize_xy(double d, int chroma_sub)
+{
+    if (isnan(d))
+        return INT_MAX;
+    return (int)d & ~((1 << chroma_sub) - 1);
+}
+
+static void eval_expr(AVFilterContext *ctx)
+{
+    OverlaySubsContext *s = ctx->priv;
+
+    s->var_values[VAR_X] = av_expr_eval(s->x_pexpr, s->var_values, NULL);
+    s->var_values[VAR_Y] = av_expr_eval(s->y_pexpr, s->var_values, NULL);
+    /* It is necessary if x is expressed from y  */
+    s->var_values[VAR_X] = av_expr_eval(s->x_pexpr, s->var_values, NULL);
+    s->x = normalize_xy(s->var_values[VAR_X], s->hsub);
+    s->y = normalize_xy(s->var_values[VAR_Y], s->vsub);
+}
+
+static int set_expr(AVExpr **pexpr, const char *expr, const char *option, void *log_ctx)
+{
+    int ret;
+    AVExpr *old = NULL;
+
+    if (*pexpr)
+        old = *pexpr;
+    ret = av_expr_parse(pexpr, expr, var_names,
+                        NULL, NULL, NULL, NULL, 0, log_ctx);
+    if (ret < 0) {
+        av_log(log_ctx, AV_LOG_ERROR,
+               "Error when evaluating the expression '%s' for %s\n",
+               expr, option);
+        *pexpr = old;
+        return ret;
+    }
+
+    av_expr_free(old);
+    return 0;
+}
+
+static int overlay_graphicsubs_query_formats(AVFilterContext *ctx)
+{
+    AVFilterFormats *formats;
+    AVFilterLink *inlink0 = ctx->inputs[0];
+    AVFilterLink *inlink1 = ctx->inputs[1];
+    AVFilterLink *outlink = ctx->outputs[0];
+    int ret;
+    static const enum AVSubtitleType subtitle_fmts[] = { AV_SUBTITLE_FMT_BITMAP, AV_SUBTITLE_FMT_NONE };
+    static const enum AVPixelFormat supported_pix_fmts[] = {
+        AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV444P,
+        AV_PIX_FMT_ARGB,  AV_PIX_FMT_RGBA,
+        AV_PIX_FMT_ABGR,  AV_PIX_FMT_BGRA,
+        AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24,
+        AV_PIX_FMT_NONE
+    };
+
+    /* set input0 video formats */
+    formats = ff_make_format_list(supported_pix_fmts);
+    if ((ret = ff_formats_ref(formats, &inlink0->outcfg.formats)) < 0)
+        return ret;
+
+    /* set output0 video formats */
+    if ((ret = ff_formats_ref(formats, &outlink->incfg.formats)) < 0)
+        return ret;
+
+    /* set input1 subtitle formats */
+    formats = ff_make_format_list(subtitle_fmts);
+    if ((ret = ff_formats_ref(formats, &inlink1->outcfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    OverlaySubsContext *s = ctx->priv;
+    int ret;
+
+    if ((ret = ff_framesync_init_dualinput(&s->fs, ctx)) < 0)
+        return ret;
+
+    outlink->w = ctx->inputs[MAIN]->w;
+    outlink->h = ctx->inputs[MAIN]->h;
+    outlink->time_base = ctx->inputs[MAIN]->time_base;
+    outlink->frame_rate = ctx->inputs[MAIN]->frame_rate;
+
+    return ff_framesync_configure(&s->fs);
+}
+
+// divide by 255 and round to nearest
+// apply a fast variant: (X+127)/255 = ((X+127)*257+257)>>16 = ((X+128)*257)>>16
+#define FAST_DIV255(x) ((((x) + 128) * 257) >> 16)
+
+// calculate the non-pre-multiplied alpha, applying the general equation:
+// alpha = alpha_overlay / ( (alpha_main + alpha_overlay) - (alpha_main * alpha_overlay) )
+// (((x) << 16) - ((x) << 9) + (x)) is a faster version of: 255 * 255 * x
+// ((((x) + (y)) << 8) - ((x) + (y)) - (y) * (x)) is a faster version of: 255 * (x + y)
+#define UNPREMULTIPLY_ALPHA(x, y) ((((x) << 16) - ((x) << 9) + (x)) / ((((x) + (y)) << 8) - ((x) + (y)) - (y) * (x)))
+
+/**
+ * Blend image in src to destination buffer dst at position (x, y).
+ */
+static av_always_inline void blend_packed_rgb(const AVFilterContext *ctx,
+    const AVFrame *dst, const AVSubtitleArea *src,
+    int x, int y,
+    int is_straight)
+{
+    OverlaySubsContext *s = ctx->priv;
+    int i, imax, j, jmax;
+    const int src_w = src->w;
+    const int src_h = src->h;
+    const int dst_w = dst->width;
+    const int dst_h = dst->height;
+    uint8_t alpha;          ///< the amount of overlay to blend on to main
+    const int dr = s->main_rgba_map[R];
+    const int dg = s->main_rgba_map[G];
+    const int db = s->main_rgba_map[B];
+    const int da = s->main_rgba_map[A];
+    const int dstep = s->main_pix_step[0];
+    const int sr = s->overlay_rgba_map[R];
+    const int sg = s->overlay_rgba_map[G];
+    const int sb = s->overlay_rgba_map[B];
+    const int sa = s->overlay_rgba_map[A];
+    int slice_start, slice_end;
+    uint8_t *S, *sp, *d, *dp;
+
+    i = FFMAX(-y, 0);
+    imax = FFMIN3(-y + dst_h, FFMIN(src_h, dst_h), y + src_h);
+
+    slice_start = i;
+    slice_end = i + imax;
+
+    sp = src->buf[0]->data + slice_start       * src->linesize[0];
+    dp = dst->data[0] + (slice_start + y) * dst->linesize[0];
+
+    for (i = slice_start; i < slice_end; i++) {
+        j = FFMAX(-x, 0);
+        S = sp + j;
+        d = dp + ((x + j) * dstep);
+
+        for (jmax = FFMIN(-x + dst_w, src_w); j < jmax; j++) {
+            uint32_t val = src->pal[*S];
+            const uint8_t *sval = (uint8_t *)&val;
+            alpha = sval[sa];
+
+            // if the main channel has an alpha channel, alpha has to be calculated
+            // to create an un-premultiplied (straight) alpha value
+            if (s->main_has_alpha && alpha != 0 && alpha != 255) {
+                const uint8_t alpha_d = d[da];
+                alpha = UNPREMULTIPLY_ALPHA(alpha, alpha_d);
+            }
+
+            switch (alpha) {
+            case 0:
+                break;
+            case 255:
+                d[dr] = sval[sr];
+                d[dg] = sval[sg];
+                d[db] = sval[sb];
+                break;
+            default:
+                // main_value = main_value * (1 - alpha) + overlay_value * alpha
+                // since alpha is in the range 0-255, the result must divided by 255
+                d[dr] = is_straight ? FAST_DIV255(d[dr] * (255 - alpha) + sval[sr] * alpha) :
+                        FFMIN(FAST_DIV255(d[dr] * (255 - alpha)) + sval[sr], 255);
+                d[dg] = is_straight ? FAST_DIV255(d[dg] * (255 - alpha) + sval[sg] * alpha) :
+                        FFMIN(FAST_DIV255(d[dg] * (255 - alpha)) + sval[sg], 255);
+                d[db] = is_straight ? FAST_DIV255(d[db] * (255 - alpha) + sval[sb] * alpha) :
+                        FFMIN(FAST_DIV255(d[db] * (255 - alpha)) + sval[sb], 255);
+            }
+
+            if (s->main_has_alpha) {
+                switch (alpha) {
+                case 0:
+                    break;
+                case 255:
+                    d[da] = sval[sa];
+                    break;
+                default:
+                    // apply alpha compositing: main_alpha += (1-main_alpha) * overlay_alpha
+                    d[da] += FAST_DIV255((255 - d[da]) * S[sa]);
+                }
+            }
+            d += dstep;
+            S += 1;
+        }
+        dp += dst->linesize[0];
+        sp += src->linesize[0];
+    }
+}
+
+static av_always_inline void blend_plane_8_8bits(const AVFilterContext *ctx, const AVFrame *dst, const AVSubtitleArea *area,
+    const uint32_t *yuv_pal, int src_w, int src_h, int dst_w, int dst_h, int plane, int hsub, int vsub,
+    int x, int y, int dst_plane, int dst_offset, int dst_step)
+{
+    const int src_wp = AV_CEIL_RSHIFT(src_w, hsub);
+    const int src_hp = AV_CEIL_RSHIFT(src_h, vsub);
+    const int dst_wp = AV_CEIL_RSHIFT(dst_w, hsub);
+    const int dst_hp = AV_CEIL_RSHIFT(dst_h, vsub);
+    const int yp = y >> vsub;
+    const int xp = x >> hsub;
+    uint8_t *s, *sp, *d, *dp, *dap;
+    int imax, i, j, jmax;
+    int slice_start, slice_end;
+
+    i = FFMAX(-yp, 0);                                                                                     \
+    imax = FFMIN3(-yp + dst_hp, FFMIN(src_hp, dst_hp), yp + src_hp);                                       \
+
+    slice_start = i;
+    slice_end = i + imax;
+
+    sp = area->buf[0]->data + (slice_start << vsub) * area->linesize[0];
+    dp = dst->data[dst_plane] + (yp + slice_start) * dst->linesize[dst_plane] + dst_offset;
+
+    dap = dst->data[3] + ((yp + slice_start) << vsub) * dst->linesize[3];
+
+    for (i = slice_start; i < slice_end; i++) {
+        j = FFMAX(-xp, 0);
+        d = dp + (xp + j) * dst_step;
+        s = sp + (j << hsub);
+        jmax = FFMIN(-xp + dst_wp, src_wp);
+
+        for (; j < jmax; j++) {
+            uint32_t val = yuv_pal[*s];
+            const uint8_t *sval = (uint8_t *)&val;
+            const int alpha = sval[3];
+            const int max = 255, mid = 128;
+            const int d_int = *d;
+            const int sval_int = sval[plane];
+
+            switch (alpha) {
+            case 0:
+                break;
+            case 255:
+                *d = sval[plane];
+                break;
+            default:
+                if (plane > 0)
+                    *d = av_clip(FAST_DIV255((d_int - mid) * (max - alpha) + (sval_int - mid) * alpha) , -mid, mid) + mid;
+                else
+                    *d = FAST_DIV255(d_int * (max - alpha) + sval_int * alpha);
+                break;
+            }
+
+            d += dst_step;
+            s += 1 << hsub;
+        }
+        dp += dst->linesize[dst_plane];
+        sp +=  (1 << vsub) * area->linesize[0];
+        dap += (1 << vsub) * dst->linesize[3];
+    }
+}
+
+#define RGB2Y(r, g, b) (uint8_t)(((66 * (r) + 129 * (g) +  25 * (b) + 128) >> 8) +  16)
+#define RGB2U(r, g, b) (uint8_t)(((-38 * (r) - 74 * (g) + 112 * (b) + 128) >> 8) + 128)
+#define RGB2V(r, g, b) (uint8_t)(((112 * (r) - 94 * (g) -  18 * (b) + 128) >> 8) + 128)
+/* Converts R8 G8 B8 color to YUV. */
+static av_always_inline void rgb_2_yuv(uint8_t r, uint8_t g, uint8_t b, uint8_t* y, uint8_t* u, uint8_t* v)
+{
+    *y = RGB2Y((int)r, (int)g, (int)b);
+    *u = RGB2U((int)r, (int)g, (int)b);
+    *v = RGB2V((int)r, (int)g, (int)b);
+}
+
+
+static av_always_inline void blend_yuv_8_8bits(AVFilterContext *ctx, AVFrame *dst, const AVSubtitleArea *area, int hsub, int vsub, int x, int y)
+{
+    OverlaySubsContext *s = ctx->priv;
+    const int src_w = area->w;
+    const int src_h = area->h;
+    const int dst_w = dst->width;
+    const int dst_h = dst->height;
+    const int sr = s->overlay_rgba_map[R];
+    const int sg = s->overlay_rgba_map[G];
+    const int sb = s->overlay_rgba_map[B];
+    const int sa = s->overlay_rgba_map[A];
+    uint32_t yuvpal[256];
+
+    for (int i = 0; i < 256; ++i) {
+        const uint8_t *rgba = (const uint8_t *)&area->pal[i];
+        uint8_t *yuva = (uint8_t *)&yuvpal[i];
+        rgb_2_yuv(rgba[sr], rgba[sg], rgba[sb], &yuva[Y], &yuva[U], &yuva[V]);
+        yuva[3] = rgba[sa];
+    }
+
+    blend_plane_8_8bits(ctx, dst, area, yuvpal, src_w, src_h, dst_w, dst_h, Y, 0,    0,    x, y, s->main_desc->comp[Y].plane, s->main_desc->comp[Y].offset, s->main_desc->comp[Y].step);
+    blend_plane_8_8bits(ctx, dst, area, yuvpal, src_w, src_h, dst_w, dst_h, U, hsub, vsub, x, y, s->main_desc->comp[U].plane, s->main_desc->comp[U].offset, s->main_desc->comp[U].step);
+    blend_plane_8_8bits(ctx, dst, area, yuvpal, src_w, src_h, dst_w, dst_h, V, hsub, vsub, x, y, s->main_desc->comp[V].plane, s->main_desc->comp[V].offset, s->main_desc->comp[V].step);
+}
+
+static int config_input_main(AVFilterLink *inlink)
+{
+    int ret;
+    AVFilterContext *ctx  = inlink->dst;
+    OverlaySubsContext *s = inlink->dst->priv;
+    const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(inlink->format);
+
+    av_image_fill_max_pixsteps(s->main_pix_step,    NULL, pix_desc);
+    ff_fill_rgba_map(s->overlay_rgba_map, AV_PIX_FMT_RGB32); // it's actually AV_PIX_FMT_PAL8);
+
+    s->hsub = pix_desc->log2_chroma_w;
+    s->vsub = pix_desc->log2_chroma_h;
+
+    s->main_desc = pix_desc;
+
+    s->main_is_packed_rgb = ff_fill_rgba_map(s->main_rgba_map, inlink->format) >= 0;
+    s->main_has_alpha = !!(pix_desc->flags & AV_PIX_FMT_FLAG_ALPHA);
+
+    /* Finish the configuration by evaluating the expressions
+       now when both inputs are configured. */
+    s->var_values[VAR_MAIN_W   ] = s->var_values[VAR_MW] = ctx->inputs[MAIN   ]->w;
+    s->var_values[VAR_MAIN_H   ] = s->var_values[VAR_MH] = ctx->inputs[MAIN   ]->h;
+    s->var_values[VAR_OVERLAY_W] = s->var_values[VAR_OW] = ctx->inputs[OVERLAY]->w;
+    s->var_values[VAR_OVERLAY_H] = s->var_values[VAR_OH] = ctx->inputs[OVERLAY]->h;
+    s->var_values[VAR_HSUB]  = 1<<pix_desc->log2_chroma_w;
+    s->var_values[VAR_VSUB]  = 1<<pix_desc->log2_chroma_h;
+    s->var_values[VAR_X]     = NAN;
+    s->var_values[VAR_Y]     = NAN;
+    s->var_values[VAR_N]     = 0;
+    s->var_values[VAR_T]     = NAN;
+    s->var_values[VAR_POS]   = NAN;
+
+    if ((ret = set_expr(&s->x_pexpr,      s->x_expr,      "x",      ctx)) < 0 ||
+        (ret = set_expr(&s->y_pexpr,      s->y_expr,      "y",      ctx)) < 0)
+        return ret;
+
+    if (s->eval_mode == EVAL_MODE_INIT) {
+        eval_expr(ctx);
+        av_log(ctx, AV_LOG_VERBOSE, "x:%f xi:%d y:%f yi:%d\n",
+               s->var_values[VAR_X], s->x,
+               s->var_values[VAR_Y], s->y);
+    }
+
+    av_log(ctx, AV_LOG_VERBOSE,
+           "main w:%d h:%d fmt:%s overlay w:%d h:%d fmt:%s\n",
+           ctx->inputs[MAIN]->w, ctx->inputs[MAIN]->h,
+           av_get_pix_fmt_name(ctx->inputs[MAIN]->format),
+           ctx->inputs[OVERLAY]->w, ctx->inputs[OVERLAY]->h,
+           av_get_pix_fmt_name(ctx->inputs[OVERLAY]->format));
+    return 0;
+}
+
+static int do_blend(FFFrameSync *fs)
+{
+    AVFilterContext *ctx = fs->parent;
+    AVFrame *mainpic, *second;
+    OverlaySubsContext *s = ctx->priv;
+    AVFilterLink *inlink = ctx->inputs[0];
+    unsigned i;
+    int ret;
+
+    ret = ff_framesync_dualinput_get_writable(fs, &mainpic, &second);
+    if (ret < 0)
+        return ret;
+    if (!second)
+        return ff_filter_frame(ctx->outputs[0], mainpic);
+
+    if (s->eval_mode == EVAL_MODE_FRAME) {
+        int64_t pos = mainpic->pkt_pos;
+
+        s->var_values[VAR_N] = (double)inlink->frame_count_out;
+        s->var_values[VAR_T] = mainpic->pts == AV_NOPTS_VALUE ?
+            NAN :(double)mainpic->pts * av_q2d(inlink->time_base);
+        s->var_values[VAR_POS] = pos == -1 ? NAN : (double)pos;
+
+        s->var_values[VAR_OVERLAY_W] = s->var_values[VAR_OW] = second->width;
+        s->var_values[VAR_OVERLAY_H] = s->var_values[VAR_OH] = second->height;
+        s->var_values[VAR_MAIN_W   ] = s->var_values[VAR_MW] = mainpic->width;
+        s->var_values[VAR_MAIN_H   ] = s->var_values[VAR_MH] = mainpic->height;
+
+        eval_expr(ctx);
+        av_log(ctx, AV_LOG_DEBUG, "n:%f t:%f pos:%f x:%f xi:%d y:%f yi:%d\n",
+               s->var_values[VAR_N], s->var_values[VAR_T], s->var_values[VAR_POS],
+               s->var_values[VAR_X], s->x,
+               s->var_values[VAR_Y], s->y);
+    }
+
+    for (i = 0; i < second->num_subtitle_areas; i++) {
+        const AVSubtitleArea *sub_area = second->subtitle_areas[i];
+
+        if (sub_area->type != AV_SUBTITLE_FMT_BITMAP) {
+            av_log(NULL, AV_LOG_WARNING, "overlay_graphicsubs: non-bitmap subtitle\n");
+            return AVERROR_INVALIDDATA;
+        }
+
+        switch (inlink->format) {
+        case AV_PIX_FMT_YUV420P:
+            blend_yuv_8_8bits(ctx, mainpic, sub_area, 1, 1, sub_area->x + s->x, sub_area->y + s->y);
+            break;
+        case AV_PIX_FMT_YUV422P:
+            blend_yuv_8_8bits(ctx, mainpic, sub_area, 1, 0, sub_area->x + s->x, sub_area->y + s->y);
+            break;
+        case AV_PIX_FMT_YUV444P:
+            blend_yuv_8_8bits(ctx, mainpic, sub_area, 0, 0, sub_area->x + s->x, sub_area->y + s->y);
+            break;
+        case AV_PIX_FMT_RGB24:
+        case AV_PIX_FMT_BGR24:
+        case AV_PIX_FMT_ARGB:
+        case AV_PIX_FMT_RGBA:
+        case AV_PIX_FMT_BGRA:
+        case AV_PIX_FMT_ABGR:
+            blend_packed_rgb(ctx, mainpic, sub_area, sub_area->x + s->x, sub_area->y + s->y, 1);
+            break;
+        default:
+            av_log(NULL, AV_LOG_ERROR, "Unsupported input pix fmt: %d\n", inlink->format);
+            return AVERROR(EINVAL);
+        }
+    }
+
+    return ff_filter_frame(ctx->outputs[0], mainpic);
+}
+
+static av_cold int overlay_graphicsubs_init(AVFilterContext *ctx)
+{
+    OverlaySubsContext *s = ctx->priv;
+
+    s->fs.on_event = do_blend;
+    return 0;
+}
+
+static int overlay_graphicsubs_activate(AVFilterContext *ctx)
+{
+    OverlaySubsContext *s = ctx->priv;
+    return ff_framesync_activate(&s->fs);
+}
+
+static int graphicsub2video_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_BITMAP, AV_SUBTITLE_FMT_NONE };
+    static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_RGB32, AV_PIX_FMT_NONE };
+    int ret;
+
+    /* set input subtitle formats */
+    formats = ff_make_format_list(subtitle_fmts);
+    if ((ret = ff_formats_ref(formats, &inlink->outcfg.formats)) < 0)
+        return ret;
+
+    /* set output video formats */
+    formats = ff_make_format_list(pix_fmts);
+    if ((ret = ff_formats_ref(formats, &outlink->incfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int graphicsub2video_config_input(AVFilterLink *inlink)
+{
+    AVFilterContext *ctx = inlink->dst;
+    OverlaySubsContext *s = ctx->priv;
+
+    if (s->w <= 0 || s->h <= 0) {
+        s->w = inlink->w;
+        s->h = inlink->h;
+    }
+    return 0;
+}
+
+static int graphicsub2video_config_output(AVFilterLink *outlink)
+{
+    const AVFilterContext *ctx  = outlink->src;
+    OverlaySubsContext *s = ctx->priv;
+    const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(outlink->format);
+
+    outlink->w = s->w;
+    outlink->h = s->h;
+
+    if (outlink->w == 0 && outlink->h == 0) {
+        outlink->w = 1;
+        outlink->h = 1;
+    }
+    outlink->sample_aspect_ratio = (AVRational){1,1};
+    outlink->time_base = ctx->inputs[0]->time_base;
+    outlink->frame_rate = ctx->inputs[0]->frame_rate;
+
+    av_image_fill_max_pixsteps(s->main_pix_step, NULL, pix_desc);
+    ff_fill_rgba_map(s->overlay_rgba_map, AV_PIX_FMT_RGB32);
+
+    s->hsub = pix_desc->log2_chroma_w;
+    s->vsub = pix_desc->log2_chroma_h;
+
+    s->main_desc = pix_desc;
+
+    s->main_is_packed_rgb = ff_fill_rgba_map(s->main_rgba_map, outlink->format) >= 0;
+    s->main_has_alpha = !!(pix_desc->flags & AV_PIX_FMT_FLAG_ALPHA);
+
+    return 0;
+}
+
+static int graphicsub2video_filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFilterLink *outlink = inlink->dst->outputs[0];
+    const AVFilterContext *ctx  = outlink->src;
+    OverlaySubsContext *s = ctx->priv;
+    AVFrame *out;
+    const unsigned num_rects = frame->num_subtitle_areas;
+    unsigned int i;
+    int ret;
+
+    if (s->use_caching && s->cache_frame && frame->repeat_sub
+        && s->cache_frame->subtitle_timing.start_pts == frame->subtitle_timing.start_pts) {
+        out = av_frame_clone(s->cache_frame);
+        if (!out)
+            return AVERROR(ENOMEM);
+
+        ret = av_frame_copy_props(out, frame);
+        if (ret < 0)
+            return ret;
+
+        av_log(inlink->dst, AV_LOG_DEBUG, "graphicsub2video CACHED - size %dx%d  pts: %"PRId64"  areas: %d\n", frame->width, frame->height, frame->subtitle_timing.start_pts, frame->num_subtitle_areas);
+        av_frame_free(&frame);
+        return ff_filter_frame(outlink, out);
+    }
+
+
+    out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
+    if (!out) {
+        av_frame_free(&frame);
+        return AVERROR(ENOMEM);
+    }
+
+    memset(out->data[0], 0, (size_t)out->linesize[0] * out->height);
+
+    out->pts = out->pkt_dts = out->best_effort_timestamp = frame->pts;
+    out->coded_picture_number = out->display_picture_number = s->pic_counter++;
+
+    for (i = 0; i < num_rects; i++) {
+        const AVSubtitleArea  *sub_rect = frame->subtitle_areas[i];
+
+        if (sub_rect->type != AV_SUBTITLE_FMT_BITMAP) {
+            av_log(NULL, AV_LOG_WARNING, "graphicsub2video: non-bitmap subtitle\n");
+            av_frame_free(&frame);
+            return AVERROR_INVALIDDATA;
+        }
+
+        blend_packed_rgb(inlink->dst, out, sub_rect, sub_rect->x, sub_rect->y, 1);
+    }
+
+    av_log(inlink->dst, AV_LOG_DEBUG, "graphicsub2video output - size %dx%d  pts: %"PRId64"  areas: %d\n", out->width, out->height, out->pts, frame->num_subtitle_areas);
+
+    if (s->use_caching) {
+        av_frame_free(&s->cache_frame);
+        s->cache_frame = av_frame_clone(out);
+    }
+
+    av_frame_free(&frame);
+    return ff_filter_frame(outlink, out);
+}
+
+#define OFFSET(x) offsetof(OverlaySubsContext, x)
+#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption overlaygraphicsubs_options[] = {
+    { "x", "set the x expression", OFFSET(x_expr), AV_OPT_TYPE_STRING, {.str = "0"}, 0, 0, .flags = FLAGS },
+    { "y", "set the y expression", OFFSET(y_expr), AV_OPT_TYPE_STRING, {.str = "0"}, 0, 0, .flags = FLAGS },
+    { "eof_action", "Action to take when encountering EOF from secondary input ",
+        OFFSET(fs.opt_eof_action), AV_OPT_TYPE_INT, { .i64 = EOF_ACTION_REPEAT },
+        EOF_ACTION_REPEAT, EOF_ACTION_PASS, .flags = FLAGS, "eof_action" },
+        { "repeat", "Repeat the previous frame.",   0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_REPEAT }, .flags = FLAGS, "eof_action" },
+        { "endall", "End both streams.",            0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_ENDALL }, .flags = FLAGS, "eof_action" },
+        { "pass",   "Pass through the main input.", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_PASS },   .flags = FLAGS, "eof_action" },
+    { "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, {.i64 = EVAL_MODE_FRAME}, 0, EVAL_MODE_NB-1, FLAGS, "eval" },
+         { "init",  "eval expressions once during initialization", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_INIT},  .flags = FLAGS, .unit = "eval" },
+         { "frame", "eval expressions per-frame",                  0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_FRAME}, .flags = FLAGS, .unit = "eval" },
+    { "shortest", "force termination when the shortest input terminates", OFFSET(fs.opt_shortest), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, .flags = FLAGS },
+    { "repeatlast", "repeat overlay of the last overlay frame", OFFSET(fs.opt_repeatlast), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, .flags = FLAGS },
+    { NULL }
+};
+
+static const AVOption graphicsub2video_options[] = {
+    { "size",        "set output frame size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, .flags = FLAGS },
+    { "s",           "set output frame size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, .flags = FLAGS },
+    { "use_caching", "Cache output frames",   OFFSET(use_caching), AV_OPT_TYPE_BOOL, { .i64 = 1}, 0, 1, .flags = FLAGS },
+    { NULL }
+};
+
+FRAMESYNC_DEFINE_CLASS(overlaygraphicsubs, OverlaySubsContext, fs);
+
+static const AVFilterPad overlaygraphicsubs_inputs[] = {
+    {
+        .name         = "main",
+        .type         = AVMEDIA_TYPE_VIDEO,
+        .config_props = config_input_main,
+        .flags        = AVFILTERPAD_FLAG_NEEDS_WRITABLE,
+    },
+    {
+        .name         = "overlay",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+    },
+};
+
+static const AVFilterPad overlaygraphicsubs_outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_VIDEO,
+        .config_props  = config_output,
+    },
+};
+
+const AVFilter ff_vf_overlaygraphicsubs = {
+    .name          = "overlaygraphicsubs",
+    .description   = NULL_IF_CONFIG_SMALL("Overlay graphical subtitles on top of the input."),
+    .preinit       = overlaygraphicsubs_framesync_preinit,
+    .init          = overlay_graphicsubs_init,
+    .uninit        = overlay_graphicsubs_uninit,
+    .priv_size     = sizeof(OverlaySubsContext),
+    .priv_class    = &overlaygraphicsubs_class,
+    .activate      = overlay_graphicsubs_activate,
+    FILTER_INPUTS(overlaygraphicsubs_inputs),
+    FILTER_OUTPUTS(overlaygraphicsubs_outputs),
+    FILTER_QUERY_FUNC(overlay_graphicsubs_query_formats),
+};
+
+AVFILTER_DEFINE_CLASS(graphicsub2video);
+
+static const AVFilterPad graphicsub2video_inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .filter_frame = graphicsub2video_filter_frame,
+        .config_props = graphicsub2video_config_input,
+    },
+};
+
+static const AVFilterPad graphicsub2video_outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_VIDEO,
+        .config_props  = graphicsub2video_config_output,
+    },
+};
+
+const AVFilter ff_svf_graphicsub2video = {
+    .name          = "graphicsub2video",
+    .description   = NULL_IF_CONFIG_SMALL("Convert graphical subtitles to video"),
+    .priv_size     = sizeof(OverlaySubsContext),
+    .priv_class    = &graphicsub2video_class,
+    FILTER_INPUTS(graphicsub2video_inputs),
+    FILTER_OUTPUTS(graphicsub2video_outputs),
+    FILTER_QUERY_FUNC(graphicsub2video_query_formats),
+};
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v5 14/25] avfilter/overlaytextsubs: Add overlaytextsubs and textsubs2video filters
  2022-06-25  9:57       ` [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022 ffmpegagent
                           ` (12 preceding siblings ...)
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 13/25] avfilter/overlaygraphicsubs: Add overlaygraphicsubs and graphicsub2video filters softworkz
@ 2022-06-25  9:57         ` softworkz
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 15/25] avfilter/textmod: Add textmod, censor and show_speaker filters softworkz
                           ` (13 subsequent siblings)
  27 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-25  9:57 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

- overlaytextsubs {VS -> V)
  Overlay text subtitles onto a video stream.

- textsubs2video {S -> V)
  Converts text subtitles to video frames

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 configure                        |   2 +
 doc/filters.texi                 | 113 +++++
 libavfilter/Makefile             |   2 +
 libavfilter/allfilters.c         |   2 +
 libavfilter/vf_overlaytextsubs.c | 680 +++++++++++++++++++++++++++++++
 5 files changed, 799 insertions(+)
 create mode 100644 libavfilter/vf_overlaytextsubs.c

diff --git a/configure b/configure
index 0580e4a536..99b4d7c8d7 100755
--- a/configure
+++ b/configure
@@ -3692,6 +3692,7 @@ overlay_qsv_filter_deps="libmfx"
 overlay_qsv_filter_select="qsvvpp"
 overlay_vaapi_filter_deps="vaapi VAProcPipelineCaps_blend_flags"
 overlay_vulkan_filter_deps="vulkan spirv_compiler"
+overlaytextsubs_filter_deps="avcodec libass"
 owdenoise_filter_deps="gpl"
 pad_opencl_filter_deps="opencl"
 pan_filter_deps="swresample"
@@ -3730,6 +3731,7 @@ stereo3d_filter_deps="gpl"
 subtitles_filter_deps="avformat avcodec libass"
 super2xsai_filter_deps="gpl"
 pixfmts_super2xsai_test_deps="super2xsai_filter"
+textsub2video_filter_deps="avcodec libass"
 tinterlace_filter_deps="gpl"
 tinterlace_merge_test_deps="tinterlace_filter"
 tinterlace_pad_test_deps="tinterlace_filter"
diff --git a/doc/filters.texi b/doc/filters.texi
index a77546de3d..b73a380515 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -27106,6 +27106,119 @@ Overlay PGS subtitles
 ffmpeg -i "https://streams.videolan.org/samples/sub/PGS/Girl_With_The_Dragon_Tattoo_2%3A23%3A56.mkv" -filter_complex "[0:0][0:1]overlaygraphicsubs" output.mp4
 @end example
 @end itemize
+
+@section overlaytextsubs
+
+Overlay text subtitles onto a video stream.
+
+This filter supersedes the classic @ref{subtitles} filter opposed to which it does no longer require to open and access the source stream separately, which is often causing problems or doesn't even work for non-local or slow sources.
+
+Inputs:
+@itemize
+@item 0: Video [YUV420P, YUV422P, YUV444P, ARGB, RGBA, ABGR, BGRA, RGB24, BGR24]
+@item 1: Subtitles [TEXT]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Video (same as input)
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+
+@item alpha
+Process alpha channel, by default alpha channel is untouched.
+
+@item fonts_dir
+Set a directory path containing fonts that can be used by the filter.
+These fonts will be used in addition to whatever the font provider uses.
+
+@item default_font_path
+Path to a font file to be used as the default font.
+
+@item font_size
+Set the default font size.
+
+@item fontconfig_file
+Path to ASS fontconfig configuration file.
+
+@item force_style
+Override default style or script info parameters of the subtitles. It accepts a
+string containing ASS style format @code{KEY=VALUE} couples separated by ",".
+
+@item margin
+Set the rendering margin in pixels.
+
+@item render_latest_only
+For rendering, alway use the latest event only, which is covering the given point in time
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Overlay ASS subtitles with animations:
+@example
+ffmpeg -i "http://streams.videolan.org/samples/sub/SSA/subtitle_testing_complex.mkv" -filter_complex "[0:v]overlaytextsubs" -map 0 -y out.mkv
+@end example
+@end itemize
+
+@section textsub2video
+
+Converts text subtitles to video frames.
+
+For overlaying text subtitles onto video frames it is recommended to use the overlaytextsubs filter.
+The textsub2video is useful for for creating transparent text-frames when overlay is done via hw acceleration
+
+Inputs:
+@itemize
+@item 0: Subtitles [TEXT]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Video [RGB32]
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+
+@item rate, r
+Set the framerate for updating overlay frames.
+Normally, overlay frames will only be updated each time when the subtitles to display are changing.
+In cases where subtitles include advanced features (like animation), this parameter determines the frequency by which the overlay frames should be updated.
+
+@item size, s
+Set the output frame size.
+Allows to override the size of output video frames.
+
+@item fonts_dir
+Set a directory path containing fonts that can be used by the filter.
+These fonts will be used in addition to whatever the font provider uses.
+
+@item default_font_path
+Path to a font file to be used as the default font.
+
+@item font_size
+Set the default font size.
+
+@item fontconfig_file
+Path to ASS fontconfig configuration file.
+
+@item force_style
+Override default style or script info parameters of the subtitles. It accepts a
+string containing ASS style format @code{KEY=VALUE} couples separated by ",".
+
+@item margin
+Set the rendering margin in pixels.
+
+@item render_latest_only
+For rendering, alway use the latest event only, which is covering the given point in time.
+@end table
+
 @c man end SUBTITLE FILTERS
 
 @chapter Multimedia Filters
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 97ec9fc3b3..7482df7a2a 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -393,6 +393,7 @@ OBJS-$(CONFIG_OVERLAY_QSV_FILTER)            += vf_overlay_qsv.o framesync.o
 OBJS-$(CONFIG_OVERLAY_VAAPI_FILTER)          += vf_overlay_vaapi.o framesync.o vaapi_vpp.o
 OBJS-$(CONFIG_OVERLAY_VULKAN_FILTER)         += vf_overlay_vulkan.o vulkan.o vulkan_filter.o
 OBJS-$(CONFIG_OVERLAYGRAPHICSUBS_FILTER)     += vf_overlaygraphicsubs.o framesync.o
+OBJS-$(CONFIG_OVERLAYTEXTSUBS_FILTER)        += vf_overlaytextsubs.o
 OBJS-$(CONFIG_OWDENOISE_FILTER)              += vf_owdenoise.o
 OBJS-$(CONFIG_PAD_FILTER)                    += vf_pad.o
 OBJS-$(CONFIG_PAD_OPENCL_FILTER)             += vf_pad_opencl.o opencl.o opencl/pad.o
@@ -484,6 +485,7 @@ OBJS-$(CONFIG_SWAPRECT_FILTER)               += vf_swaprect.o
 OBJS-$(CONFIG_SWAPUV_FILTER)                 += vf_swapuv.o
 OBJS-$(CONFIG_TBLEND_FILTER)                 += vf_blend.o framesync.o
 OBJS-$(CONFIG_TELECINE_FILTER)               += vf_telecine.o
+OBJS-$(CONFIG_TEXTSUB2VIDEO_FILTER)          += vf_overlaytextsubs.o
 OBJS-$(CONFIG_THISTOGRAM_FILTER)             += vf_histogram.o
 OBJS-$(CONFIG_THRESHOLD_FILTER)              += vf_threshold.o framesync.o
 OBJS-$(CONFIG_THUMBNAIL_FILTER)              += vf_thumbnail.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 06ac93457d..e540ec349f 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -372,6 +372,7 @@ extern const AVFilter ff_vf_overlay_vaapi;
 extern const AVFilter ff_vf_overlay_vulkan;
 extern const AVFilter ff_vf_overlay_cuda;
 extern const AVFilter ff_vf_overlaygraphicsubs;
+extern const AVFilter ff_vf_overlaytextsubs;
 extern const AVFilter ff_vf_owdenoise;
 extern const AVFilter ff_vf_pad;
 extern const AVFilter ff_vf_pad_opencl;
@@ -560,6 +561,7 @@ extern const AVFilter ff_avf_showvolume;
 extern const AVFilter ff_avf_showwaves;
 extern const AVFilter ff_avf_showwavespic;
 extern const AVFilter ff_svf_graphicsub2video;
+extern const AVFilter ff_svf_textsub2video;
 extern const AVFilter ff_vaf_spectrumsynth;
 
 /* multimedia sources */
diff --git a/libavfilter/vf_overlaytextsubs.c b/libavfilter/vf_overlaytextsubs.c
new file mode 100644
index 0000000000..1e43289bec
--- /dev/null
+++ b/libavfilter/vf_overlaytextsubs.c
@@ -0,0 +1,680 @@
+/*
+ * 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
+ * overlay text subtitles on top of a video frame
+ */
+
+#include "config_components.h"
+
+#include <ass/ass.h>
+#include "libavutil/ass_internal.h"
+#include "libavutil/thread.h"
+
+#include "drawutils.h"
+#include "filters.h"
+
+#include "libavutil/opt.h"
+#include "libavutil/pixdesc.h"
+
+typedef struct TextSubsContext {
+    const AVClass *class;
+    AVMutex mutex;
+    int is_mutex_initialized;
+
+    ASS_Library   *library;
+    ASS_Renderer  *renderer;
+    ASS_Track     *track;
+
+    char *default_font_path;
+    char *fonts_dir;
+    char *fc_file;
+    double font_size;
+    char *force_style;
+    char *language;
+    int margin;
+    int render_latest_only;
+
+    int alpha;
+    FFDrawContext draw;
+
+    int got_header;
+    int out_w, out_h;
+    AVRational frame_rate;
+    AVFrame *last_frame;
+    int need_frame;
+    int eof;
+} TextSubsContext;
+
+/* libass supports a log level ranging from 0 to 7 */
+static const int ass_libavfilter_log_level_map[] = {
+    AV_LOG_QUIET,               /* 0 */
+    AV_LOG_PANIC,               /* 1 */
+    AV_LOG_FATAL,               /* 2 */
+    AV_LOG_ERROR,               /* 3 */
+    AV_LOG_WARNING,             /* 4 */
+    AV_LOG_INFO,                /* 5 */
+    AV_LOG_VERBOSE,             /* 6 */
+    AV_LOG_DEBUG,               /* 7 */
+};
+
+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 void uninit(AVFilterContext *ctx)
+{
+    TextSubsContext *s = ctx->priv;
+
+    if (s->track)
+        ass_free_track(s->track);
+    if (s->renderer)
+        ass_renderer_done(s->renderer);
+    if (s->library)
+        ass_library_done(s->library);
+
+    s->track = NULL;
+    s->renderer = NULL;
+    s->library = NULL;
+
+    if (s->is_mutex_initialized) {
+        ff_mutex_destroy(&s->mutex);
+        s->is_mutex_initialized = 0;
+    }
+
+    av_frame_free(&s->last_frame);
+}
+
+static int overlay_textsubs_query_formats(AVFilterContext *ctx)
+{
+    AVFilterFormats *formats;
+    AVFilterLink *inlink0 = ctx->inputs[0];
+    AVFilterLink *inlink1 = ctx->inputs[1];
+    AVFilterLink *outlink = ctx->outputs[0];
+    static const enum AVSubtitleType subtitle_fmts[] = { AV_SUBTITLE_FMT_ASS, AV_SUBTITLE_FMT_NONE };
+    int ret;
+
+    /* set input0 and output0 video formats */
+    formats = ff_draw_supported_pixel_formats(0);
+    if ((ret = ff_formats_ref(formats, &inlink0->outcfg.formats)) < 0)
+        return ret;
+    if ((ret = ff_formats_ref(formats, &outlink->incfg.formats)) < 0)
+        return ret;
+
+    /* set input1 subtitle formats */
+    formats = ff_make_format_list(subtitle_fmts);
+    if ((ret = ff_formats_ref(formats, &inlink1->outcfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+
+    outlink->w = ctx->inputs[0]->w;
+    outlink->h = ctx->inputs[0]->h;
+    outlink->time_base = ctx->inputs[0]->time_base;
+    outlink->frame_rate = ctx->inputs[0]->frame_rate;
+
+    return 0;
+}
+
+static int config_input_main(AVFilterLink *inlink)
+{
+    AVFilterContext *ctx  = inlink->dst;
+    TextSubsContext *s = inlink->dst->priv;
+    int ret;
+
+    ret = ff_draw_init(&s->draw, inlink->format, s->alpha ? FF_DRAW_PROCESS_ALPHA : 0);
+    if (ret < 0) {
+        av_log(ctx, AV_LOG_ERROR, "Could not initialize ff_draw.\n");
+        return ret;
+    }
+
+    ass_set_frame_size  (s->renderer, inlink->w, inlink->h);
+    ass_set_pixel_aspect(s->renderer, av_q2d(inlink->sample_aspect_ratio));
+
+    av_log(ctx, AV_LOG_VERBOSE, "Subtitle screen: %dx%d\n\n\n\n", inlink->w, inlink->h);
+
+    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(TextSubsContext *s, 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(&s->draw, &color, rgba_color);
+        ff_blend_mask(&s->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 void process_header(AVFilterContext *link, AVFrame *frame)
+{
+    TextSubsContext *s = link->priv;
+    ASS_Track *track = s->track;
+    ASS_Style *style;
+    int sid = 0;
+
+    if (!track)
+        return;
+
+    if (frame && frame->subtitle_header) {
+        char *subtitle_header = (char *)frame->subtitle_header->data;
+        ass_process_codec_private(s->track, subtitle_header, strlen(subtitle_header));
+    }
+    else {
+        char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+        if (!subtitle_header)
+            return;
+
+        ass_process_codec_private(s->track, subtitle_header, strlen(subtitle_header));
+        av_free(subtitle_header);
+    }
+
+    if (s->language)
+        s->track->Language = av_strdup(s->language);
+
+    if (!s->track->event_format) {
+        s->track->event_format = av_strdup("ReadOrder, Layer, Style, Name, MarginL, MarginR, MarginV, Effect, Text");
+    }
+
+    if (s->track->n_styles == 0) {
+        sid = ass_alloc_style(track);
+        style = &s->track->styles[sid];
+        style->Name             = av_strdup("Default");
+        style->PrimaryColour    = 0xffffff00;
+        style->SecondaryColour  = 0x00ffff00;
+        style->OutlineColour    = 0x00000000;
+        style->BackColour       = 0x00000080;
+        style->Bold             = 200;
+        style->ScaleX           = 1.0;
+        style->ScaleY           = 1.0;
+        style->Spacing          = 0;
+        style->BorderStyle      = 1;
+        style->Outline          = 2;
+        style->Shadow           = 3;
+        style->Alignment        = 2;
+    }
+    else
+        style = &s->track->styles[sid];
+
+    style->FontSize         = s->font_size;
+    style->MarginL = style->MarginR = style->MarginV = s->margin;
+
+    track->default_style = sid;
+
+    s->got_header = 1;
+}
+
+static int filter_video_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFilterContext *ctx = inlink->dst;
+    TextSubsContext *s = ctx->priv;
+    int detect_change = 0;
+    ASS_Image *image;
+
+
+    int64_t time_ms  = (int64_t)((double)frame->pts * av_q2d(inlink->time_base) * 1000);
+    int64_t time_ms1 = (int64_t)((double)ctx->inputs[1]->current_pts * av_q2d(ctx->inputs[1]->time_base) * 1000);
+
+    if (time_ms1 < time_ms + 1000)
+        ff_request_frame(ctx->inputs[1]);
+
+    av_log(ctx, AV_LOG_DEBUG, "filter_video_frame - video: %"PRId64"ms  sub: %"PRId64"ms  rel %d\n", time_ms, time_ms1, (time_ms1 < time_ms));
+
+    ff_mutex_lock(&s->mutex);
+    image = ass_render_frame(s->renderer, s->track, time_ms, &detect_change);
+    ff_mutex_unlock(&s->mutex);
+
+    if (detect_change)
+        av_log(ctx, AV_LOG_DEBUG, "Change happened at time ms:%"PRId64"\n", time_ms);
+
+    overlay_ass_image(s, frame, image);
+
+    return ff_filter_frame(ctx->outputs[0], frame);
+}
+
+static int filter_subtitle_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFilterContext *ctx = inlink->dst;
+    TextSubsContext *s = ctx->priv;
+    const int64_t start_time = av_rescale_q(frame->subtitle_timing.start_pts, AV_TIME_BASE_Q, av_make_q(1, 1000));
+    const int64_t duration   = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, av_make_q(1, 1000));
+    const int64_t frame_time = (int64_t)((double)frame->pts * av_q2d(inlink->time_base) * 1000);
+
+    // Postpone header processing until we receive a frame with content
+    if (!s->got_header && frame->num_subtitle_areas > 0)
+        process_header(ctx, frame);
+
+    av_log(ctx, AV_LOG_DEBUG, "filter_subtitle_frame dur: %"PRId64"ms frame: %"PRId64"ms  sub: %"PRId64"ms  repeat_sub %d\n", duration, frame_time, start_time, frame->repeat_sub);
+
+    if (frame->repeat_sub)
+        goto exit;
+
+    ff_mutex_lock(&s->mutex);
+
+    if (s->render_latest_only && s->track->n_events > 0) {
+        const int64_t previous_start_time = s->track->events[s->track->n_events - 1].Start;
+        const int64_t diff = start_time - previous_start_time;
+        for (int i = s->track->n_events - 1; i >= 0; i--) {
+            if (previous_start_time != s->track->events[i].Start)
+                break;
+
+            if (s->track->events[i].Duration > diff)
+                s->track->events[i].Duration = diff;
+        }
+    }
+
+    for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+        char *ass_line = frame->subtitle_areas[i]->ass;
+        if (!ass_line)
+            continue;
+
+        ass_process_chunk(s->track, ass_line, strlen(ass_line), start_time, duration);
+    }
+
+    ff_mutex_unlock(&s->mutex);
+
+exit:
+    av_frame_free(&frame);
+    return 0;
+}
+
+static av_cold int init(AVFilterContext *ctx)
+{
+    int ret;
+    TextSubsContext *s = ctx->priv;
+
+    s->library = ass_library_init();
+
+    if (!s->library) {
+        av_log(ctx, AV_LOG_ERROR, "Could not initialize libass.\n");
+        return AVERROR(EINVAL);
+    }
+
+    ass_set_message_cb(s->library, ass_log, ctx);
+
+    /* Initialize fonts */
+    if (s->fonts_dir)
+        ass_set_fonts_dir(s->library, s->fonts_dir);
+
+    ass_set_extract_fonts(s->library, 1);
+
+    s->renderer = ass_renderer_init(s->library);
+    if (!s->renderer) {
+        av_log(ctx, AV_LOG_ERROR, "Could not initialize libass renderer.\n");
+        return AVERROR(EINVAL);
+    }
+
+    s->track = ass_new_track(s->library);
+    if (!s->track) {
+        av_log(ctx, AV_LOG_ERROR, "ass_new_track() failed!\n");
+        return AVERROR(EINVAL);
+    }
+
+    ass_set_check_readorder(s->track, 0);
+
+    ass_set_fonts(s->renderer, s->default_font_path, NULL, 1, s->fc_file, 1);
+
+    if (s->force_style) {
+        char **list = NULL;
+        char *temp = NULL;
+        char *ptr = av_strtok(s->force_style, ",", &temp);
+        int i = 0;
+        while (ptr) {
+            av_dynarray_add(&list, &i, ptr);
+            if (!list) {
+                return AVERROR(ENOMEM);
+            }
+            ptr = av_strtok(NULL, ",", &temp);
+        }
+        av_dynarray_add(&list, &i, NULL);
+        if (!list) {
+            return AVERROR(ENOMEM);
+        }
+        ass_set_style_overrides(s->library, list);
+        av_free(list);
+    }
+
+    ret = ff_mutex_init(&s->mutex, NULL);
+    if (ret) {
+        av_log(ctx, AV_LOG_ERROR, "mutex initialiuzation failed! Error code: %d\n", ret);
+        return AVERROR(EINVAL);
+    }
+
+    s->is_mutex_initialized = 1;
+
+    return ret;
+}
+
+static int textsub2video_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 input0 subtitle format */
+    formats = ff_make_format_list(subtitle_fmts);
+    if ((ret = ff_formats_ref(formats, &inlink->outcfg.formats)) < 0)
+        return ret;
+
+    /* set output0 video format */
+    formats = ff_draw_supported_pixel_formats(AV_PIX_FMT_FLAG_ALPHA);
+    if ((ret = ff_formats_ref(formats, &outlink->incfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int textsub2video_config_input(AVFilterLink *inlink)
+{
+    AVFilterContext *ctx = inlink->dst;
+    TextSubsContext *s = ctx->priv;
+
+    if (s->out_w <= 0 || s->out_h <= 0) {
+        s->out_w = inlink->w;
+        s->out_h = inlink->h;
+    }
+
+    return 0;
+}
+
+static int textsub2video_config_output(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    TextSubsContext *s = ctx->priv;
+    int ret;
+
+    ret = ff_draw_init(&s->draw, outlink->format, FF_DRAW_PROCESS_ALPHA);
+    if (ret < 0) {
+        av_log(ctx, AV_LOG_ERROR, "Could not initialize ff_draw.\n");
+        return ret;
+    }
+
+    if (s->out_w <= 0 || s->out_h <= 0) {
+        av_log(ctx, AV_LOG_ERROR, "No output image size set.\n");
+        return AVERROR(EINVAL);
+    }
+
+    ass_set_frame_size  (s->renderer, s->out_w, s->out_h);
+
+    outlink->w = s->out_w;
+    outlink->h = s->out_h;
+    outlink->sample_aspect_ratio = (AVRational){1,1};
+    outlink->frame_rate = s->frame_rate;
+
+    return 0;
+}
+
+static int textsub2video_request_frame(AVFilterLink *outlink)
+{
+    TextSubsContext *s = outlink->src->priv;
+    AVFilterLink *inlink = outlink->src->inputs[0];
+    int64_t last_pts = outlink->current_pts;
+    int64_t next_pts, time_ms;
+    int i, detect_change = 0, status;
+    AVFrame *out;
+    ASS_Image *image;
+
+    status = ff_outlink_get_status(inlink);
+    if (status == AVERROR_EOF)
+        return AVERROR_EOF;
+
+    if (s->eof)
+        return AVERROR_EOF;
+
+    if (inlink->current_pts == AV_NOPTS_VALUE) { // || outlink->current_pts > inlink->current_pts) {
+        int ret = ff_request_frame(inlink);
+        if (ret == AVERROR_EOF) {
+            s->eof = 1;
+        }
+
+        if (ret != 0)
+            av_log(outlink->src, AV_LOG_DEBUG, "ff_request_frame returned: %d\n", ret);
+
+        s->need_frame = 1;
+        return 0;
+    }
+
+    if (last_pts == AV_NOPTS_VALUE)
+        next_pts = last_pts = inlink->current_pts * av_q2d(inlink->time_base) / av_q2d(outlink->time_base);
+    else
+        next_pts = last_pts + (int64_t)(1.0 / av_q2d(outlink->frame_rate) / av_q2d(outlink->time_base));
+
+    time_ms = (int64_t)((double)next_pts * av_q2d(outlink->time_base) * 1000);
+
+    ff_mutex_lock(&s->mutex);
+    image = ass_render_frame(s->renderer, s->track, time_ms, &detect_change);
+    ff_mutex_unlock(&s->mutex);
+
+    if (detect_change)
+        av_log(outlink->src, AV_LOG_VERBOSE, "Change happened at time ms:%"PRId64" pts:%"PRId64"\n", time_ms, next_pts);
+    else if (s->last_frame) {
+        out = av_frame_clone(s->last_frame);
+        if (!out)
+            return AVERROR(ENOMEM);
+
+        out->pts = out->pkt_dts = out->best_effort_timestamp = next_pts;
+        return ff_filter_frame(outlink, out);
+    }
+
+    out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
+    if (!out)
+        return AVERROR(ENOMEM);
+
+    for (i = 0; i < AV_NUM_DATA_POINTERS; i++) {
+        if (out->buf[i] && i != 1)
+            memset(out->buf[i]->data, 0, out->buf[i]->size);
+    }
+
+    out->pts = out->pkt_dts = out->best_effort_timestamp = next_pts;
+
+    if (image)
+        overlay_ass_image(s, out, image);
+
+    av_frame_free(&s->last_frame);
+
+    s->last_frame = av_frame_clone(out);
+
+    return ff_filter_frame(outlink, out);
+}
+
+static int textsub2video_filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFilterContext *ctx = inlink->dst;
+    TextSubsContext *s = ctx->priv;
+    const int64_t start_time = av_rescale_q(frame->subtitle_timing.start_pts, AV_TIME_BASE_Q, av_make_q(1, 1000));
+    const int64_t duration   = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, av_make_q(1, 1000));
+
+    av_log(ctx, AV_LOG_VERBOSE, "textsub2video_filter_frame num_subtitle_rects: %d, start_time_ms: %"PRId64"\n", frame->num_subtitle_areas, start_time);
+
+    if (!s->got_header && frame->num_subtitle_areas > 0)
+        process_header(ctx, frame);
+
+    if (frame->repeat_sub)
+        goto exit;
+
+    ff_mutex_lock(&s->mutex);
+
+    if (s->render_latest_only && s->track->n_events > 0) {
+        const int64_t previous_start_time = s->track->events[s->track->n_events - 1].Start;
+        const int64_t diff = start_time - previous_start_time;
+        for (int i = s->track->n_events - 1; i >= 0; i--) {
+            if (previous_start_time != s->track->events[i].Start)
+                break;
+
+            if (s->track->events[i].Duration > diff)
+                s->track->events[i].Duration = diff;
+
+        }
+    }
+
+    for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+        char *ass_line = frame->subtitle_areas[i]->ass;
+        if (!ass_line)
+            continue;
+
+        ass_process_chunk(s->track, ass_line, strlen(ass_line), start_time, duration);
+    }
+
+
+    ff_mutex_unlock(&s->mutex);
+
+exit:
+    av_frame_free(&frame);
+
+    if (s->need_frame) {
+        s->need_frame = 0;
+        return textsub2video_request_frame(ctx->outputs[0]);
+    }
+
+    return 0;
+}
+
+#define OFFSET(x) offsetof(TextSubsContext, x)
+#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption overlaytextsubs_options[] = {
+    {"alpha",              "enable processing of alpha channel", OFFSET(alpha),              AV_OPT_TYPE_BOOL,   {.i64 = 0   }, 0,         1,        .flags =  FLAGS},
+    {"font_size",          "default font size",                  OFFSET(font_size),          AV_OPT_TYPE_DOUBLE, {.dbl = 18.0}, 0.0,       100.0,    .flags =  FLAGS},
+    {"force_style",        "force subtitle style",               OFFSET(force_style),        AV_OPT_TYPE_STRING, {.str = NULL}, 0,         0,        .flags =  FLAGS},
+    {"margin",             "default margin",                     OFFSET(margin),             AV_OPT_TYPE_INT,    {.i64 = 20  }, 0,         INT_MAX,  .flags =  FLAGS},
+    {"default_font_path",  "path to default font",               OFFSET(default_font_path),  AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"fonts_dir",          "directory to scan for fonts",        OFFSET(fonts_dir),          AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"fontsdir",           "directory to scan for fonts",        OFFSET(fonts_dir),          AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"fontconfig_file",    "fontconfig file to load",            OFFSET(fc_file),            AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"language",           "default language",                   OFFSET(language),           AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"render_latest_only", "newest sub event for each time",     OFFSET(render_latest_only), AV_OPT_TYPE_BOOL,   {.i64 = 0   }, 0,         1,        .flags =  FLAGS},
+    { .name = NULL }
+};
+
+static const AVOption textsub2video_options[] = {
+    {"rate",               "set frame rate",                   OFFSET(frame_rate),         AV_OPT_TYPE_VIDEO_RATE, {.str="8"},   0,         INT_MAX,   .flags =  FLAGS},
+    {"r",                  "set frame rate",                   OFFSET(frame_rate),         AV_OPT_TYPE_VIDEO_RATE, {.str="8"},   0,         INT_MAX,   .flags =  FLAGS},
+    {"size",               "set video size",                   OFFSET(out_w),              AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0,         0,        .flags =  FLAGS},
+    {"s",                  "set video size",                   OFFSET(out_w),              AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0,         0,        .flags =  FLAGS},
+    {"font_size",          "default font size",                OFFSET(font_size),          AV_OPT_TYPE_DOUBLE,     {.dbl = 18.0}, 0.0,       100.0,    .flags =  FLAGS},
+    {"force_style",        "force subtitle style",             OFFSET(force_style),        AV_OPT_TYPE_STRING,     {.str = NULL}, 0,         0,        .flags =  FLAGS},
+    {"margin",             "default margin",                   OFFSET(margin),             AV_OPT_TYPE_INT,        {.i64 = 20  }, 0,         INT_MAX,  .flags =  FLAGS},
+    {"default_font_path",  "path to default font",             OFFSET(default_font_path),  AV_OPT_TYPE_STRING,     {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"fonts_dir",          "directory to scan for fonts",      OFFSET(fonts_dir),          AV_OPT_TYPE_STRING,     {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"fontsdir",           "directory to scan for fonts",      OFFSET(fonts_dir),          AV_OPT_TYPE_STRING,     {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"fontconfig_file",    "fontconfig file to load",          OFFSET(fc_file),            AV_OPT_TYPE_STRING,     {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"language",           "default language",                 OFFSET(language),           AV_OPT_TYPE_STRING,     {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"render_latest_only", "newest sub event for each time",   OFFSET(render_latest_only), AV_OPT_TYPE_BOOL,       {.i64 = 0   }, 0,         1,        .flags =  FLAGS},
+    { .name = NULL }
+};
+
+#if CONFIG_OVERLAYTEXTSUBS_FILTER
+
+AVFILTER_DEFINE_CLASS(overlaytextsubs);
+
+static const AVFilterPad overlaytextsubs_inputs[] = {
+    {
+        .name         = "main",
+        .type         = AVMEDIA_TYPE_VIDEO,
+        .config_props = config_input_main,
+        .flags        = AVFILTERPAD_FLAG_NEEDS_WRITABLE,
+        .filter_frame = filter_video_frame,
+    },
+    {
+        .name         = "overlay",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .filter_frame = filter_subtitle_frame,
+    },
+};
+
+static const AVFilterPad overlaytextsubs_outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_VIDEO,
+        .config_props  = config_output,
+    },
+};
+
+const AVFilter ff_vf_overlaytextsubs = {
+    .name          = "overlaytextsubs",
+    .description   = NULL_IF_CONFIG_SMALL("Overlay textual subtitles on top of the input."),
+    .init          = init,
+    .uninit        = uninit,
+    .priv_size     = sizeof(TextSubsContext),
+    .priv_class    = &overlaytextsubs_class,
+    FILTER_INPUTS(overlaytextsubs_inputs),
+    FILTER_OUTPUTS(overlaytextsubs_outputs),
+    FILTER_QUERY_FUNC(overlay_textsubs_query_formats),
+};
+#endif
+
+#if CONFIG_TEXTSUB2VIDEO_FILTER
+
+AVFILTER_DEFINE_CLASS(textsub2video);
+
+static const AVFilterPad textsub2video_inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .config_props = textsub2video_config_input,
+        .filter_frame = textsub2video_filter_frame,
+    },
+};
+
+static const AVFilterPad textsub2video_outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_VIDEO,
+        .config_props  = textsub2video_config_output,
+        .request_frame = textsub2video_request_frame,
+    },
+};
+
+const AVFilter ff_svf_textsub2video = {
+    .name          = "textsub2video",
+    .description   = NULL_IF_CONFIG_SMALL("Convert textual subtitles to video frames"),
+    .init          = init,
+    .uninit        = uninit,
+    .priv_size     = sizeof(TextSubsContext),
+    .priv_class    = &textsub2video_class,
+    FILTER_INPUTS(textsub2video_inputs),
+    FILTER_OUTPUTS(textsub2video_outputs),
+    FILTER_QUERY_FUNC(textsub2video_query_formats),
+};
+#endif
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v5 15/25] avfilter/textmod: Add textmod, censor and show_speaker filters
  2022-06-25  9:57       ` [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022 ffmpegagent
                           ` (13 preceding siblings ...)
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 14/25] avfilter/overlaytextsubs: Add overlaytextsubs and textsubs2video filters softworkz
@ 2022-06-25  9:57         ` softworkz
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 16/25] avfilter/stripstyles: Add stripstyles filter softworkz
                           ` (12 subsequent siblings)
  27 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-25  9:57 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

- textmod {S -> S)
  Modify subtitle text in a number of ways

- censor {S -> S)
  Censor subtitles using a word list

- show_speaker {S -> S)
  Prepend speaker names from ASS subtitles to the visible text lines

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 doc/filters.texi         | 206 ++++++++++++
 libavfilter/Makefile     |   5 +
 libavfilter/allfilters.c |   5 +
 libavfilter/sf_textmod.c | 710 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 926 insertions(+)
 create mode 100644 libavfilter/sf_textmod.c

diff --git a/doc/filters.texi b/doc/filters.texi
index b73a380515..0d078e2573 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -26998,6 +26998,145 @@ existing filters using @code{--disable-filters}.
 
 Below is a description of the currently available subtitle filters.
 
+
+@section censor
+
+Censor selected words in text subtitles.
+
+Inputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+@item mode
+The censoring mode to apply.
+
+Supported censoring modes are:
+
+@table @var
+@item 0, keep_first_last
+Replace all characters with the 'censor_char' except the first and the last character of a word.
+For words with less than 4 characters, the last character will be replaced as well.
+For words with less than 3 characters, the first character will be replaced as well.
+@item 1, keep_first
+Replace all characters with the 'censor_char' except the first character of a word.
+For words with less than 3 characters, the first character will be replaced as well.
+@item 2, all
+Replace all characters with the 'censor_char'.
+@end table
+
+@item words
+A list of words to censor, separated by 'separator'.
+
+@item words_file
+Specify a file from which to load the contents for the 'words' parameter.
+
+@item censor_char
+Single character used as replacement for censoring.
+
+@item separator
+Delimiter character for words. Used with replace_words and remove_words- Must be a single character.
+The default is '.'.
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Censor a few given words with a pound character.
+@example
+ffmpeg -i "http://streams.videolan.org/samples/sub/SSA/subtitle_testing_complex.mkv" -filter_complex "[0:1]censor=words='diss,louder,hope,beam,word':censor_char='#'" -map 0 -y output.mkv
+@end example
+@end itemize
+
+
+@section textmod
+
+Modify subtitle text in a number of ways.
+
+Inputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+@item mode
+The kind of text modification to apply
+
+Supported operation modes are:
+
+@table @var
+@item 0, leet
+Convert subtitle text to 'leet speak'. It's primarily useful for testing as the modification will be visible with almost all text lines.
+@item 1, to_upper
+Change all text to upper case. Might improve readability.
+@item 2, to_lower
+Change all text to lower case.
+@item 3, replace_chars
+Replace one or more characters. Requires the find and replace parameters to be specified.
+Both need to be equal in length.
+The first char in find is replaced by the first char in replace, same for all subsequent chars.
+@item 4, remove_chars
+Remove certain characters. Requires the find parameter to be specified.
+All chars in the find parameter string will be removed from all subtitle text.
+@item 5, replace_words
+Replace one or more words. Requires the find and replace parameters to be specified. Multiple words must be separated by the delimiter char specified vie the separator parameter (default: ',').
+The number of words in the find and replace parameters needs to be equal.
+The first word in find is replaced by the first word in replace, same for all subsequent words
+@item 6, remove_words
+Remove certain words. Requires the find parameter to be specified. Multiple words must be separated by the delimiter char specified vie the separator parameter (default: ',').
+All words in the find parameter string will be removed from all subtitle text.
+@end table
+
+@item find
+Required for replace_chars, remove_chars, replace_words and remove_words.
+
+@item find_file
+Specify a file from which to load the contents for the 'find' parameter.
+
+@item replace
+Required for replace_chars and replace_words.
+
+@item replace_file
+Specify a file from which to load the contents for the 'replace' parameter.
+
+@item separator
+Delimiter character for words. Used with replace_words and remove_words- Must be a single character.
+The default is '.'.
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Change all characters to upper case while keeping all styles and animations:
+@example
+ffmpeg -i "https://streams.videolan.org/ffmpeg/mkv_subtitles.mkv" -filter_complex "[0:s]textmod=mode=to_upper" -map 0 -y out.mkv
+@end example
+@item
+Remove a set of symbol characters for am improved and smoother visual apperance:
+@example
+ffmpeg -i "https://streams.videolan.org/ffmpeg/mkv_subtitles.mkv" -filter_complex "[0:s]textmod=mode=remove_chars:find='$&#@*§'" -map 0 -y out.mkv
+@end example
+@end itemize
+
 @section graphicsub2video
 
 Renders graphic subtitles as video frames.
@@ -27165,6 +27304,73 @@ ffmpeg -i "http://streams.videolan.org/samples/sub/SSA/subtitle_testing_complex.
 @end example
 @end itemize
 
+@section showspeaker
+
+Prepend speaker names to subtitle lines (when available).
+
+Subtitles in ASS/SSA format are often including the names of the persons
+or character for each subtitle line. The showspeaker filter adds those names
+to the actual subtitle text to make it visible on playback.
+
+Inputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+@item format
+The format for prepending speaker names. Default is 'square_brackets'.
+
+Supported operation modes are:
+
+@table @var
+@item 0, square_brackets
+Enclose the speaker name in square brackets, followed by space ('[speaker] text').
+@item 1, round_brackets
+Enclose the speaker name in round brackets, followed by space ('(speaker) text').
+@item 2, colon
+Separate the speaker name with a colon and space ('speaker: text').
+@item 3, plain
+Separate the speaker name with a space only ('speaker text').
+@end table
+
+@item line_break
+Set thís parameter to insert a line break between speaker name and text instead of the space character.
+
+@item style
+Allows to set a specific style for the speaker name text.
+
+This can be either a named style that exists in the ass subtitle header script (e.g. 'Default') or an ass style override code.
+Example:  @{\\c&HDD0000&\\be1\\i1\\bord10@}
+This sets the color to blue, enables edge blurring, italic font and a border of size 10.
+
+The behavior is as follows:
+
+- When the style parameter is not provided, the filter will find the first position in the event string that is actual text.
+  The speaker name will be inserted at this position. This allows to have the speaker name shown in the same style like the
+  regular text, in case the string would start with a sequence of style codes.
+- When the style parameter is provided, everything will be prepended to the original text:
+  Style Code or Style name >> Speaker Name >> Style Reset Code >> Original Text
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Prepend speaker names with blue text, smooth edges and blend/overlay that onto the video.
+@example
+ffmpeg -i INPUT -filter_complex "showspeaker=format=colon:style='@{\\c&HDD0000&\\be1@}',[0:v]overlaytextsubs"
+@end example
+@end itemize
+
 @section textsub2video
 
 Converts text subtitles to video frames.
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 7482df7a2a..6a68c44e1c 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -577,6 +577,11 @@ OBJS-$(CONFIG_YUVTESTSRC_FILTER)             += vsrc_testsrc.o
 
 OBJS-$(CONFIG_NULLSINK_FILTER)               += vsink_nullsink.o
 
+# subtitle filters
+OBJS-$(CONFIG_CENSOR_FILTER)                 += sf_textmod.o
+OBJS-$(CONFIG_SHOW_SPEAKER_FILTER)           += sf_textmod.o
+OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
+
 # multimedia filters
 OBJS-$(CONFIG_ABITSCOPE_FILTER)              += avf_abitscope.o
 OBJS-$(CONFIG_ADRAWGRAPH_FILTER)             += f_drawgraph.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index e540ec349f..2228e414db 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -569,6 +569,11 @@ extern const AVFilter ff_avsrc_avsynctest;
 extern const AVFilter ff_avsrc_amovie;
 extern const AVFilter ff_avsrc_movie;
 
+/* subtitle filters */
+extern const AVFilter ff_sf_censor;
+extern const AVFilter ff_sf_showspeaker;
+extern const AVFilter ff_sf_textmod;
+
 /* those filters are part of public or internal API,
  * they are formatted to not be found by the grep
  * as they are manually added again (due to their 'names'
diff --git a/libavfilter/sf_textmod.c b/libavfilter/sf_textmod.c
new file mode 100644
index 0000000000..9651e07c65
--- /dev/null
+++ b/libavfilter/sf_textmod.c
@@ -0,0 +1,710 @@
+/*
+ * 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 allows to modify subtitle text in several ways
+ */
+
+#include <libavutil/ass_internal.h>
+
+#include "libavutil/opt.h"
+#include "internal.h"
+#include "libavutil/ass_split_internal.h"
+#include "libavutil/bprint.h"
+#include "libavutil/file.h"
+
+static const char* leet_src = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+static const char* leet_dst = "abcd3f6#1jklmn0pq257uvwxyzAB(D3F6#1JKLMN0PQ257UVWXYZ";
+
+enum TextModFilterType {
+    TM_TEXTMOD,
+    TM_CENSOR,
+    TM_SHOW_SPEAKER,
+};
+
+enum TextModOperation {
+    OP_LEET,
+    OP_TO_UPPER,
+    OP_TO_LOWER,
+    OP_REPLACE_CHARS,
+    OP_REMOVE_CHARS,
+    OP_REPLACE_WORDS,
+    OP_REMOVE_WORDS,
+    NB_OPS,
+};
+
+enum CensorMode {
+    CM_KEEP_FIRST_LAST,
+    CM_KEEP_FIRST,
+    CM_ALL,
+};
+
+enum ShowSpeakerMode {
+    SM_SQUARE_BRACKETS,
+    SM_ROUND_BRACKETS,
+    SM_COLON,
+    SM_PLAIN,
+};
+
+typedef struct TextModContext {
+    const AVClass *class;
+    enum AVSubtitleType format;
+    enum TextModFilterType filter_type;
+    enum TextModOperation operation;
+    enum CensorMode censor_mode;
+    enum ShowSpeakerMode speaker_mode;
+    char *find;
+    char *find_file;
+    char *style;
+    char *replace;
+    char *replace_file;
+    char *separator;
+    char *censor_char;
+    char **find_list;
+    int  line_break;
+    int  nb_find_list;
+    char **replace_list;
+    int  nb_replace_list;
+} TextModContext;
+
+static char **split_string(char *source, int *nb_elems, const char *delim)
+{
+    char **list = NULL;
+    char *temp = NULL;
+    char *ptr = av_strtok(source, delim, &temp);
+
+    while (ptr) {
+        if (strlen(ptr)) {
+            av_dynarray_add(&list, nb_elems, ptr);
+            if (!list)
+                return NULL;
+        }
+
+        ptr = av_strtok(NULL, delim, &temp);
+    }
+
+    if (!list)
+        return NULL;
+
+    for (int i = 0; i < *nb_elems; i++) {
+        list[i] = av_strdup(list[i]);
+        if (!list[i]) {
+            for (int n = 0; n < i; n++)
+                av_free(list[n]);
+            av_free(list);
+            return NULL;
+        }
+    }
+
+    return list;
+}
+
+static int load_text_from_file(AVFilterContext *ctx, const char *file_name, char **text, char separator)
+{
+    int err;
+    uint8_t *textbuf;
+    char *tmp;
+    size_t textbuf_size;
+    int offset = 0;
+
+    if ((err = av_file_map(file_name, &textbuf, &textbuf_size, 0, ctx)) < 0) {
+        av_log(ctx, AV_LOG_ERROR, "The text file '%s' could not be read or is empty\n", file_name);
+        return err;
+    }
+
+    if (textbuf_size > 1 &&
+        (textbuf[0] == 0xFF && textbuf[1] == 0xFE
+        || textbuf[0] == 0xFE && textbuf[1] == 0xFF)) {
+        av_log(ctx, AV_LOG_ERROR, "UTF text files are not supported. File: %s\n", file_name);
+        return AVERROR(EINVAL);
+    }
+
+    if (textbuf_size > 2 && textbuf[0] == 0xEF && textbuf[1] == 0xBB && textbuf[2] == 0xBF)
+        offset = 3; // UTF-8
+
+    if (textbuf_size > SIZE_MAX - 1 || !((tmp = av_strndup((char *)textbuf + offset, textbuf_size - offset)))) {
+        av_file_unmap(textbuf, textbuf_size);
+        return AVERROR(ENOMEM);
+    }
+
+    av_file_unmap(textbuf, textbuf_size);
+
+    for (size_t i = 0; i < strlen(tmp); i++) {
+        switch (tmp[i]) {
+        case '\n':
+        case '\r':
+        case '\f':
+        case '\v':
+            tmp[i] = separator;
+        }
+    }
+
+    *text = tmp;
+
+    return 0;
+}
+
+static int load_files(AVFilterContext *ctx)
+{
+    TextModContext *s = ctx->priv;
+    int ret;
+
+    if (!s->separator || strlen(s->separator) != 1) {
+        av_log(ctx, AV_LOG_ERROR, "A single character needs to be specified for the separator parameter.\n");
+        return AVERROR(EINVAL);
+    }
+
+    if (s->find_file && strlen(s->find_file)) {
+        ret = load_text_from_file(ctx, s->find_file, &s->find, s->separator[0]);
+        if (ret < 0 )
+            return ret;
+    }
+
+    if (s->replace_file && strlen(s->replace_file)) {
+        ret = load_text_from_file(ctx, s->replace_file, &s->replace, s->separator[0]);
+        if (ret < 0 )
+            return ret;
+    }
+
+    return 0;
+}
+
+static int init_censor(AVFilterContext *ctx)
+{
+    TextModContext *s = ctx->priv;
+    int ret;
+
+    s->filter_type = TM_CENSOR;
+    s->operation = OP_REPLACE_WORDS;
+
+    ret = load_files(ctx);
+    if (ret < 0 )
+        return ret;
+
+    if (!s->find || !strlen(s->find)) {
+        av_log(ctx, AV_LOG_ERROR, "Either the 'words' or the 'words_file' parameter needs to be specified\n");
+        return AVERROR(EINVAL);
+    }
+
+    if (!s->censor_char || strlen(s->censor_char) != 1) {
+        av_log(ctx, AV_LOG_ERROR, "A single character needs to be specified for the censor_char parameter\n");
+        return AVERROR(EINVAL);
+    }
+
+    s->find_list = split_string(s->find, &s->nb_find_list, s->separator);
+    if (!s->find_list)
+        return AVERROR(ENOMEM);
+
+    s->replace_list = av_calloc(s->nb_find_list, sizeof(char *));
+    if (!s->replace_list)
+        return AVERROR(ENOMEM);
+
+    for (int i = 0; i < s->nb_find_list; i++) {
+        size_t len, start = 0, end;
+        char *item = av_strdup(s->find_list[i]);
+        if (!item)
+            return AVERROR(ENOMEM);
+
+        len = end = strlen(item);
+
+        switch (s->censor_mode) {
+        case CM_KEEP_FIRST_LAST:
+
+            if (len > 2)
+                start = 1;
+            if (len > 3)
+                end--;
+
+            break;
+        case CM_KEEP_FIRST:
+
+            if (len > 2)
+                start = 1;
+
+            break;
+        }
+
+        for (size_t n = start; n < end; n++)
+            item[n] = s->censor_char[0];
+
+        s->replace_list[i] = item;
+    }
+
+    return 0;
+}
+
+static int init_showspeaker(AVFilterContext *ctx)
+{
+    TextModContext *s = ctx->priv;
+    s->filter_type = TM_SHOW_SPEAKER;
+
+    return 0;
+}
+
+static int init(AVFilterContext *ctx)
+{
+    TextModContext *s = ctx->priv;
+    int ret;
+
+    ret = load_files(ctx);
+    if (ret < 0 )
+        return ret;
+
+    switch (s->operation) {
+    case OP_REPLACE_CHARS:
+    case OP_REMOVE_CHARS:
+    case OP_REPLACE_WORDS:
+    case OP_REMOVE_WORDS:
+        if (!s->find || !strlen(s->find)) {
+            av_log(ctx, AV_LOG_ERROR, "Selected mode requires the 'find' parameter to be specified\n");
+            return AVERROR(EINVAL);
+        }
+        break;
+    }
+
+    switch (s->operation) {
+    case OP_REPLACE_CHARS:
+    case OP_REPLACE_WORDS:
+        if (!s->replace || !strlen(s->replace)) {
+            av_log(ctx, AV_LOG_ERROR, "Selected mode requires the 'replace' parameter to be specified\n");
+            return AVERROR(EINVAL);
+        }
+        break;
+    }
+
+    if (s->operation == OP_REPLACE_CHARS && strlen(s->find) != strlen(s->replace)) {
+        av_log(ctx, AV_LOG_ERROR, "Selected mode requires the 'find' and 'replace' parameters to have the same length\n");
+        return AVERROR(EINVAL);
+    }
+
+    if (s->operation == OP_REPLACE_WORDS || s->operation == OP_REMOVE_WORDS) {
+        if (!s->separator || strlen(s->separator) != 1) {
+            av_log(ctx, AV_LOG_ERROR, "Selected mode requires a single separator char to be specified\n");
+            return AVERROR(EINVAL);
+        }
+
+        s->find_list = split_string(s->find, &s->nb_find_list, s->separator);
+        if (!s->find_list)
+            return AVERROR(ENOMEM);
+
+        if (s->operation == OP_REPLACE_WORDS) {
+
+            s->replace_list = split_string(s->replace, &s->nb_replace_list, s->separator);
+            if (!s->replace_list)
+                return AVERROR(ENOMEM);
+
+            if (s->nb_find_list != s->nb_replace_list) {
+                av_log(ctx, AV_LOG_ERROR, "The number of words in 'find' and 'replace' needs to be equal\n");
+                return AVERROR(EINVAL);
+            }
+        }
+    }
+
+    return 0;
+}
+
+static void uninit(AVFilterContext *ctx)
+{
+    TextModContext *s = ctx->priv;
+
+    for (int i = 0; i < s->nb_find_list; i++)
+        av_freep(&s->find_list[i]);
+
+    s->nb_find_list = 0;
+    av_freep(&s->find_list);
+
+    for (int i = 0; i < s->nb_replace_list; i++)
+        av_freep(&s->replace_list[i]);
+
+    s->nb_replace_list = 0;
+    av_freep(&s->replace_list);
+}
+
+static char *process_text(TextModContext *s, char *text)
+{
+    const char *char_src = s->find;
+    const char *char_dst = s->replace;
+    char *result         = NULL;
+    int escape_level     = 0, k = 0;
+
+    switch (s->operation) {
+    case OP_LEET:
+    case OP_REPLACE_CHARS:
+
+        if (s->operation == OP_LEET) {
+            char_src = leet_src;
+            char_dst = leet_dst;
+        }
+
+        result = av_strdup(text);
+        if (!result)
+            return NULL;
+
+        for (size_t n = 0; n < strlen(result); n++) {
+            if (result[n] == '{')
+                escape_level++;
+
+            if (!escape_level) {
+                size_t len = strlen(char_src);
+                for (size_t t = 0; t < len; t++) {
+                    if (result[n] == char_src[t]) {
+                        result[n] = char_dst[t];
+                        break;
+                    }
+                }
+            }
+
+            if (result[n] == '}')
+                escape_level--;
+        }
+
+        break;
+    case OP_TO_UPPER:
+    case OP_TO_LOWER:
+
+        result = av_strdup(text);
+        if (!result)
+            return NULL;
+
+        for (size_t n = 0; n < strlen(result); n++) {
+            if (result[n] == '{')
+                escape_level++;
+            if (!escape_level)
+                result[n] = s->operation == OP_TO_LOWER ? av_tolower(result[n]) : av_toupper(result[n]);
+            if (result[n] == '}')
+                escape_level--;
+        }
+
+        break;
+    case OP_REMOVE_CHARS:
+
+        result = av_strdup(text);
+        if (!result)
+            return NULL;
+
+        for (size_t n = 0; n < strlen(result); n++) {
+            int skip_char = 0;
+
+            if (result[n] == '{')
+                escape_level++;
+
+            if (!escape_level) {
+                size_t len = strlen(char_src);
+                for (size_t t = 0; t < len; t++) {
+                    if (result[n] == char_src[t]) {
+                        skip_char = 1;
+                        break;
+                    }
+                }
+            }
+
+            if (!skip_char)
+                result[k++] = result[n];
+
+            if (result[n] == '}')
+                escape_level--;
+        }
+
+        result[k] = 0;
+
+        break;
+    case OP_REPLACE_WORDS:
+    case OP_REMOVE_WORDS:
+
+        result = av_strdup(text);
+        if (!result)
+            return NULL;
+
+        for (int n = 0; n < s->nb_find_list; n++) {
+            char *tmp           = result;
+            const char *replace = (s->operation == OP_REPLACE_WORDS) ? s->replace_list[n] : "";
+
+            result = av_strireplace(result, s->find_list[n], replace);
+            if (!result)
+                return NULL;
+
+            av_free(tmp);
+        }
+
+        break;
+    }
+
+    return result;
+}
+
+static char *process_dialog_show_speaker(TextModContext *s, char *ass_line)
+{
+    ASSDialog *dialog = avpriv_ass_split_dialog(NULL, ass_line);
+    int escape_level = 0;
+    unsigned pos = 0, len;
+    char *result, *text;
+    AVBPrint pbuf;
+
+    if (!dialog)
+        return NULL;
+
+    text = process_text(s, dialog->text);
+    if (!text)
+        return NULL;
+
+    if (!dialog->name || !strlen(dialog->name) || !dialog->text || !strlen(dialog->text))
+        return av_strdup(ass_line);
+
+    // Find insertion point in case the line starts with style codes
+    len = (unsigned)strlen(dialog->text);
+    for (unsigned i = 0; i < len; i++) {
+
+        if (dialog->text[i] == '{')
+            escape_level++;
+
+        if (dialog->text[i] == '}')
+            escape_level--;
+
+        if (escape_level == 0) {
+            pos = i;
+            break;
+        }
+    }
+
+    if (s->style && strlen(s->style))
+        // When a style is specified reset the insertion point
+        // (always add speaker plus style at the start in that case)
+        pos = 0;
+
+    if (pos >= len - 1)
+        return av_strdup(ass_line);
+
+    av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
+
+    if (pos > 0) {
+        av_bprint_append_data(&pbuf, dialog->text, pos);
+    }
+
+    if (s->style && strlen(s->style)) {
+        if (s->style[0] == '{')
+            // Assume complete and valid style code, e.g. {\c&HFF0000&}
+            av_bprintf(&pbuf, "%s", s->style);
+        else
+            // Otherwise it must be a style name
+            av_bprintf(&pbuf, "{\\r%s}", s->style);
+    }
+
+    switch (s->speaker_mode) {
+    case SM_SQUARE_BRACKETS:
+        av_bprintf(&pbuf, "[%s]", dialog->name);
+        break;
+    case SM_ROUND_BRACKETS:
+        av_bprintf(&pbuf, "(%s)", dialog->name);
+        break;
+    case SM_COLON:
+        av_bprintf(&pbuf, "%s:", dialog->name);
+        break;
+    case SM_PLAIN:
+        av_bprintf(&pbuf, "%s", dialog->name);
+        break;
+    }
+
+    if (s->style && strlen(s->style)) {
+        // Reset line style
+        if (dialog->style && strlen(dialog->style) && !av_strcasecmp(dialog->style, "default"))
+            av_bprintf(&pbuf, "{\\r%s}", dialog->style);
+        else
+            av_bprintf(&pbuf, "{\\r}");
+    }
+
+    if (s->line_break)
+        av_bprintf(&pbuf, "\\N");
+    else
+        av_bprintf(&pbuf, " ");
+
+    av_bprint_append_data(&pbuf, dialog->text + pos, len - pos);
+
+    av_bprint_finalize(&pbuf, &text);
+
+    result = avpriv_ass_get_dialog_ex(dialog->readorder, dialog->layer, dialog->style, dialog->name, dialog->margin_l, dialog->margin_r, dialog->margin_v, dialog->effect, text);
+
+    av_free(text);
+    avpriv_ass_free_dialog(&dialog);
+    return result;
+}
+
+static char *process_dialog(TextModContext *s, char *ass_line)
+{
+    ASSDialog *dialog;
+    char *result, *text;
+
+    if (s->filter_type == TM_SHOW_SPEAKER)
+        return process_dialog_show_speaker(s, ass_line);
+
+    dialog = avpriv_ass_split_dialog(NULL, ass_line);
+    if (!dialog)
+        return NULL;
+
+    text = process_text(s, dialog->text);
+    if (!text)
+        return NULL;
+
+    result = avpriv_ass_get_dialog_ex(dialog->readorder, dialog->layer, dialog->style, dialog->name, dialog->margin_l, dialog->margin_r, dialog->margin_v, dialog->effect, text);
+
+    av_free(text);
+    avpriv_ass_free_dialog(&dialog);
+    return result;
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    AVFilterLink *inlink = outlink->src->inputs[0];
+
+    outlink->w = inlink->w;
+    outlink->h = inlink->h;
+    outlink->time_base = inlink->time_base;
+    outlink->frame_rate = inlink->frame_rate;
+
+    return 0;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    TextModContext *s = inlink->dst->priv;
+    AVFilterLink *outlink = inlink->dst->outputs[0];
+    int ret;
+
+    outlink->format = inlink->format;
+
+    ret = av_frame_make_writable(frame);
+    if (ret < 0)
+        return ret;
+
+    for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+
+        AVSubtitleArea *area = frame->subtitle_areas[i];
+
+        if (area->ass) {
+            char *tmp = area->ass;
+            area->ass = process_dialog(s, area->ass);
+            av_free(tmp);
+            if (!area->ass)
+                return AVERROR(ENOMEM);
+        }
+    }
+
+    return ff_filter_frame(outlink, frame);
+}
+
+#define OFFSET(x) offsetof(TextModContext, x)
+#define FLAGS (AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption textmod_options[] = {
+    { "mode",             "set operation mode",              OFFSET(operation),    AV_OPT_TYPE_INT,    {.i64=OP_LEET},          OP_LEET, NB_OPS-1, FLAGS, "mode" },
+    {   "leet",           "convert text to 'leet speak'",    0,                    AV_OPT_TYPE_CONST,  {.i64=OP_LEET},          0,       0,        FLAGS, "mode" },
+    {   "to_upper",       "change to upper case",            0,                    AV_OPT_TYPE_CONST,  {.i64=OP_TO_UPPER},      0,       0,        FLAGS, "mode" },
+    {   "to_lower",       "change to lower case",            0,                    AV_OPT_TYPE_CONST,  {.i64=OP_TO_LOWER},      0,       0,        FLAGS, "mode" },
+    {   "replace_chars",  "replace characters",              0,                    AV_OPT_TYPE_CONST,  {.i64=OP_REPLACE_CHARS}, 0,       0,        FLAGS, "mode" },
+    {   "remove_chars",   "remove characters",               0,                    AV_OPT_TYPE_CONST,  {.i64=OP_REMOVE_CHARS},  0,       0,        FLAGS, "mode" },
+    {   "replace_words",  "replace words",                   0,                    AV_OPT_TYPE_CONST,  {.i64=OP_REPLACE_WORDS}, 0,       0,        FLAGS, "mode" },
+    {   "remove_words",   "remove words",                    0,                    AV_OPT_TYPE_CONST,  {.i64=OP_REMOVE_WORDS},  0,       0,        FLAGS, "mode" },
+    { "find",             "chars/words to find or remove",   OFFSET(find),         AV_OPT_TYPE_STRING, {.str = NULL},           0,       0,        FLAGS, NULL   },
+    { "find_file",        "load find param from file",       OFFSET(find_file),    AV_OPT_TYPE_STRING, {.str = NULL},           0,       0,        FLAGS, NULL   },
+    { "replace",          "chars/words to replace",          OFFSET(replace),      AV_OPT_TYPE_STRING, {.str = NULL},           0,       0,        FLAGS, NULL   },
+    { "replace_file",     "load replace param from file",    OFFSET(replace_file), AV_OPT_TYPE_STRING, {.str = NULL},           0,       0,        FLAGS, NULL   },
+    { "separator",        "word separator",                  OFFSET(separator),    AV_OPT_TYPE_STRING, {.str = ","},            0,       0,        FLAGS, NULL   },
+    { NULL },
+};
+
+
+static const AVOption censor_options[] = {
+    { "mode",               "set censoring mode",        OFFSET(censor_mode), AV_OPT_TYPE_INT,    {.i64=CM_KEEP_FIRST_LAST}, 0, 2, FLAGS, "mode" },
+    {   "keep_first_last",  "censor inner chars",        0,                   AV_OPT_TYPE_CONST,  {.i64=CM_KEEP_FIRST_LAST}, 0, 0, FLAGS, "mode" },
+    {   "keep_first",       "censor all but first char", 0,                   AV_OPT_TYPE_CONST,  {.i64=CM_KEEP_FIRST},      0, 0, FLAGS, "mode" },
+    {   "all",              "censor all chars",          0,                   AV_OPT_TYPE_CONST,  {.i64=CM_ALL},             0, 0, FLAGS, "mode" },
+    { "words",              "list of words to censor",   OFFSET(find),        AV_OPT_TYPE_STRING, {.str = NULL},             0, 0, FLAGS, NULL   },
+    { "words_file",         "path to word list file",    OFFSET(find_file),   AV_OPT_TYPE_STRING, {.str = NULL},             0, 0, FLAGS, NULL   },
+    { "separator",          "word separator",            OFFSET(separator),   AV_OPT_TYPE_STRING, {.str = ","},              0, 0, FLAGS, NULL   },
+    { "censor_char",        "replacement character",     OFFSET(censor_char), AV_OPT_TYPE_STRING, {.str = "*"},              0, 0, FLAGS, NULL   },
+    { NULL },
+};
+
+static const AVOption showspeaker_options[] = {
+    { "format",             "speaker name formatting",        OFFSET(speaker_mode), AV_OPT_TYPE_INT,    {.i64=SM_SQUARE_BRACKETS}, 0, 2, FLAGS, "format" },
+    {   "square_brackets",  "[speaker] text",                 0,                    AV_OPT_TYPE_CONST,  {.i64=SM_SQUARE_BRACKETS}, 0, 0, FLAGS, "format" },
+    {   "round_brackets",   "(speaker) text",                 0,                    AV_OPT_TYPE_CONST,  {.i64=SM_ROUND_BRACKETS},  0, 0, FLAGS, "format" },
+    {   "colon",            "speaker: text",                  0,                    AV_OPT_TYPE_CONST,  {.i64=SM_COLON},           0, 0, FLAGS, "format" },
+    {   "plain",            "speaker text",                   0,                    AV_OPT_TYPE_CONST,  {.i64=SM_PLAIN},           0, 0, FLAGS, "format" },
+    { "line_break",         "insert line break",              OFFSET(line_break),   AV_OPT_TYPE_BOOL,   {.i64=0},                  0, 1, FLAGS, NULL     },
+    { "style",              "ass type name or style code",    OFFSET(style),        AV_OPT_TYPE_STRING, {.str = NULL},             0, 0, FLAGS, NULL     },
+    { NULL },
+};
+
+AVFILTER_DEFINE_CLASS(textmod);
+AVFILTER_DEFINE_CLASS(censor);
+AVFILTER_DEFINE_CLASS(showspeaker);
+
+static const AVFilterPad inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .filter_frame = filter_frame,
+    },
+};
+
+static const AVFilterPad outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_SUBTITLE,
+        .config_props  = config_output,
+    },
+};
+
+const AVFilter ff_sf_textmod = {
+    .name          = "textmod",
+    .description   = NULL_IF_CONFIG_SMALL("Modify subtitle text in several ways"),
+    .init          = init,
+    .uninit        = uninit,
+    .priv_size     = sizeof(TextModContext),
+    .priv_class    = &textmod_class,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_SINGLE_SUBFMT(AV_SUBTITLE_FMT_ASS),
+};
+
+const AVFilter ff_sf_censor = {
+    .name          = "censor",
+    .description   = NULL_IF_CONFIG_SMALL("Censor words in subtitle text"),
+    .init          = init_censor,
+    .uninit        = uninit,
+    .priv_size     = sizeof(TextModContext),
+    .priv_class    = &censor_class,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_SINGLE_SUBFMT(AV_SUBTITLE_FMT_ASS),
+};
+
+const AVFilter ff_sf_showspeaker = {
+    .name          = "showspeaker",
+    .description   = NULL_IF_CONFIG_SMALL("Prepend speaker names to text subtitles (when available)"),
+    .init          = init_showspeaker,
+    .uninit        = uninit,
+    .priv_size     = sizeof(TextModContext),
+    .priv_class    = &showspeaker_class,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_SINGLE_SUBFMT(AV_SUBTITLE_FMT_ASS),
+};
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v5 16/25] avfilter/stripstyles: Add stripstyles filter
  2022-06-25  9:57       ` [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022 ffmpegagent
                           ` (14 preceding siblings ...)
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 15/25] avfilter/textmod: Add textmod, censor and show_speaker filters softworkz
@ 2022-06-25  9:57         ` softworkz
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 17/25] avfilter/splitcc: Add splitcc filter for closed caption handling softworkz
                           ` (11 subsequent siblings)
  27 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-25  9:57 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

- stripstyles {S -> S)
  Remove all inline styles from subtitle events

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 doc/filters.texi             |  37 ++++++
 libavfilter/Makefile         |   1 +
 libavfilter/allfilters.c     |   1 +
 libavfilter/sf_stripstyles.c | 237 +++++++++++++++++++++++++++++++++++
 4 files changed, 276 insertions(+)
 create mode 100644 libavfilter/sf_stripstyles.c

diff --git a/doc/filters.texi b/doc/filters.texi
index 0d078e2573..1158df64d2 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -27058,6 +27058,43 @@ ffmpeg -i "http://streams.videolan.org/samples/sub/SSA/subtitle_testing_complex.
 @end example
 @end itemize
 
+@section stripstyles
+
+Remove all inline styles from subtitle events.
+
+Inputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+@item remove_animated
+Also remove text which is subject to animation (default: true)
+Usually, animated text elements are used used in addition to static subtitle lines for creating effects, so in most cases it is safe to remove the animation content.
+If subtitle text is missing, try setting this to false.
+
+@item select_layer
+Process only ASS subtitle events from a specific layer. This allows to filter out certain effects where an ASS author duplicates the text onto multiple layers.
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Remove styles and animations from ASS subtitles and output events from ass layer 0 only. Then convert asn save as SRT stream:
+@example
+ffmpeg -i "https://streams.videolan.org/samples/sub/SSA/subtitle_testing_complex.mkv" -filter_complex "[0:1]stripstyles=select_layer=0" -map 0 -c:s srt output.mkv
+@end example
+@end itemize
+
 
 @section textmod
 
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 6a68c44e1c..a99a0f6583 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -580,6 +580,7 @@ OBJS-$(CONFIG_NULLSINK_FILTER)               += vsink_nullsink.o
 # subtitle filters
 OBJS-$(CONFIG_CENSOR_FILTER)                 += sf_textmod.o
 OBJS-$(CONFIG_SHOW_SPEAKER_FILTER)           += sf_textmod.o
+OBJS-$(CONFIG_STRIPSTYLES_FILTER)            += sf_stripstyles.o
 OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
 
 # multimedia filters
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 2228e414db..7a958b51d4 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -572,6 +572,7 @@ extern const AVFilter ff_avsrc_movie;
 /* subtitle filters */
 extern const AVFilter ff_sf_censor;
 extern const AVFilter ff_sf_showspeaker;
+extern const AVFilter ff_sf_stripstyles;
 extern const AVFilter ff_sf_textmod;
 
 /* those filters are part of public or internal API,
diff --git a/libavfilter/sf_stripstyles.c b/libavfilter/sf_stripstyles.c
new file mode 100644
index 0000000000..78dc6f3ef4
--- /dev/null
+++ b/libavfilter/sf_stripstyles.c
@@ -0,0 +1,237 @@
+/*
+ * 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 "libavutil/opt.h"
+#include "internal.h"
+#include "libavutil/ass_internal.h"
+#include "libavutil/ass_split_internal.h"
+#include "libavutil/bprint.h"
+
+typedef struct StripStylesContext {
+    const AVClass *class;
+    enum AVSubtitleType format;
+    int remove_animated;
+    enum ASSSplitComponents keep_flags;
+    int select_layer;
+} StripStylesContext;
+
+typedef struct DialogContext {
+    StripStylesContext* ss_ctx;
+    AVBPrint buffer;
+    int drawing_scale;
+    int is_animated;
+    int plain_text_length;
+} DialogContext;
+
+static void dialog_text_cb(void *priv, const char *text, int len)
+{
+    DialogContext *s = priv;
+
+    av_log(s->ss_ctx, AV_LOG_DEBUG, "dialog_text_cb: %s\n", text);
+
+    if (!s->drawing_scale && (!s->is_animated || !s->ss_ctx->remove_animated))
+        s->plain_text_length += len;
+        ////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)
+        s->plain_text_length += 2;
+        ////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 char *process_dialog(StripStylesContext *s, const char *ass_line)
+{
+    DialogContext dlg_ctx = { .ss_ctx = s };
+    ASSDialog *dialog = avpriv_ass_split_dialog(NULL, ass_line);
+    char *result = NULL;
+
+    if (!dialog)
+        return NULL;
+
+    if (s->select_layer >= 0 && dialog->layer != s->select_layer) {
+        avpriv_ass_free_dialog(&dialog);
+        return NULL;
+    }
+
+    dlg_ctx.ss_ctx = s;
+
+    av_bprint_init(&dlg_ctx.buffer, 512, AV_BPRINT_SIZE_UNLIMITED);
+
+    avpriv_ass_filter_override_codes(&dialog_callbacks, &dlg_ctx, dialog->text, &dlg_ctx.buffer, s->keep_flags);
+
+    if (av_bprint_is_complete(&dlg_ctx.buffer) && dlg_ctx.buffer.len > 0 && dlg_ctx.plain_text_length > 0)
+        result = avpriv_ass_get_dialog_ex(dialog->readorder, dialog->layer, dialog->style, dialog->name, dialog->margin_l, dialog->margin_r, dialog->margin_v, dialog->effect, dlg_ctx.buffer.str);
+
+    av_bprint_finalize(&dlg_ctx.buffer, NULL);
+    avpriv_ass_free_dialog(&dialog);
+
+    return result;
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    AVFilterLink *inlink = outlink->src->inputs[0];
+
+    outlink->w = inlink->w;
+    outlink->h = inlink->h;
+    outlink->time_base = inlink->time_base;
+    outlink->frame_rate = inlink->frame_rate;
+
+    return 0;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    StripStylesContext *s = inlink->dst->priv;
+    AVFilterLink *outlink = inlink->dst->outputs[0];
+    int ret;
+
+    outlink->format = inlink->format;
+
+    ret = av_frame_make_writable(frame);
+    if (ret <0 ) {
+        av_frame_free(&frame);
+        return AVERROR(ENOMEM);
+    }
+
+    for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+
+        AVSubtitleArea *area = frame->subtitle_areas[i];
+
+        if (area->ass) {
+            char *tmp = area->ass;
+            area->ass = process_dialog(s, area->ass);
+
+            if (area->ass) {
+                av_log(inlink->dst, AV_LOG_DEBUG, "original: %d %s\n", i, tmp);
+                av_log(inlink->dst, AV_LOG_DEBUG, "stripped: %d %s\n", i, area->ass);
+            }
+            else
+                area->ass = NULL;
+
+            av_free(tmp);
+        }
+    }
+
+    return ff_filter_frame(outlink, frame);
+}
+
+#define OFFSET(x) offsetof(StripStylesContext, x)
+#define FLAGS (AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption stripstyles_options[] = {
+    { "keep_flags", "flags to control which override codes to keep", OFFSET(keep_flags), AV_OPT_TYPE_FLAGS, { .i64 = ASS_SPLIT_TEXT }, .flags = FLAGS, .unit = "keepflags" },
+        { "basic",          "keep static style tags only",             .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_BASIC          },  .flags=FLAGS, .unit = "keepflags" },
+        { "all_known",      "keep all known tags",                     .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_ALL_KNOWN      },  .flags=FLAGS, .unit = "keepflags" },
+        { "text",           "keep text content",                       .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_TEXT           },  .flags=FLAGS, .unit = "keepflags" },
+        { "color",          "keep color tags (\\c, \\<n>c)",           .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_COLOR          },  .flags=FLAGS, .unit = "keepflags" },
+        { "alpha",          "keep color alpha tags (\\alpha, \\<n>a)", .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_ALPHA          },  .flags=FLAGS, .unit = "keepflags" },
+        { "font_name",      "keep font name tags (\\fn)",              .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_NAME      },  .flags=FLAGS, .unit = "keepflags" },
+        { "font_size",      "keep font size tags (\\fs)",              .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_SIZE      },  .flags=FLAGS, .unit = "keepflags" },
+        { "font_scale",     "keep font scale tags (\\fscx, \\fscy)",   .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_SCALE     },  .flags=FLAGS, .unit = "keepflags" },
+        { "font_spacing",   "keep font spacing tags (\\fsp)",          .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_SPACING   },  .flags=FLAGS, .unit = "keepflags" },
+        { "font_charset",   "keep font charset tags (\\fe)",           .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_CHARSET   },  .flags=FLAGS, .unit = "keepflags" },
+        { "font_bold",      "keep font bold tags (\\b)",               .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_BOLD      },  .flags=FLAGS, .unit = "keepflags" },
+        { "font_italic",    "keep font italic tags (\\i)",             .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_ITALIC    },  .flags=FLAGS, .unit = "keepflags" },
+        { "font_underline", "keep font underline tags (\\u)",          .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_UNDERLINE },  .flags=FLAGS, .unit = "keepflags" },
+        { "font_strikeout", "keep font strikeout tags (\\s)",          .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_STRIKEOUT },  .flags=FLAGS, .unit = "keepflags" },
+        { "text_border",    "keep text border tags (\\bord)",          .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_TEXT_BORDER    },  .flags=FLAGS, .unit = "keepflags" },
+        { "text_shadow",    "keep text shadow tags (\\shad)",          .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_TEXT_SHADOW    },  .flags=FLAGS, .unit = "keepflags" },
+        { "text_rotate",    "keep text rotate tags (\\fr)",            .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_TEXT_ROTATE    },  .flags=FLAGS, .unit = "keepflags" },
+        { "text_blur",      "keep text blur tags (\\blur, \\be)",      .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_TEXT_BLUR      },  .flags=FLAGS, .unit = "keepflags" },
+        { "text_wrap",      "keep text wrap tags (\\q)",               .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_TEXT_WRAP      },  .flags=FLAGS, .unit = "keepflags" },
+        { "text_align",     "keep text align tags (\\a, \\an)",        .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_TEXT_ALIGNMENT },  .flags=FLAGS, .unit = "keepflags" },
+        { "reset_override", "keep override reset tags (\\r)",          .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_CANCELLING     },  .flags=FLAGS, .unit = "keepflags" },
+        { "move",           "keep move tags (\\move)",                 .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_MOVE           },  .flags=FLAGS, .unit = "keepflags" },
+        { "pos",            "keep position tags (\\pos)",              .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_POS            },  .flags=FLAGS, .unit = "keepflags" },
+        { "origin",         "keep origin tags (\\org)",                .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_ORIGIN         },  .flags=FLAGS, .unit = "keepflags" },
+        { "draw",           "keep drawing tags (\\p)",                 .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_DRAW           },  .flags=FLAGS, .unit = "keepflags" },
+        { "animate",        "keep animation tags (\\t)",               .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_ANIMATE        },  .flags=FLAGS, .unit = "keepflags" },
+        { "fade",           "keep fade tags (\\fad, \\fade)",          .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FADE           },  .flags=FLAGS, .unit = "keepflags" },
+        { "clip",           "keep clip tags (\\clip)",                 .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_CLIP           },  .flags=FLAGS, .unit = "keepflags" },
+        { "unknown",        "keep unknown tags",                       .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_UNKNOWN        },  .flags=FLAGS, .unit = "keepflags" },
+    { "remove_animated", "remove animated text (default: yes)",   OFFSET(remove_animated),  AV_OPT_TYPE_BOOL, {.i64 = 1 },  0, 1, FLAGS, 0 },
+    { "select_layer", "process a specific ass layer only",   OFFSET(select_layer),  AV_OPT_TYPE_INT, {.i64 = -1 }, -1, INT_MAX, 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,
+        .config_props  = config_output,
+    },
+};
+
+const AVFilter ff_sf_stripstyles = {
+    .name          = "stripstyles",
+    .description   = NULL_IF_CONFIG_SMALL("Strip subtitle inline styles"),
+    .priv_size     = sizeof(StripStylesContext),
+    .priv_class    = &stripstyles_class,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_SINGLE_SUBFMT(AV_SUBTITLE_FMT_ASS),
+};
+
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v5 17/25] avfilter/splitcc: Add splitcc filter for closed caption handling
  2022-06-25  9:57       ` [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022 ffmpegagent
                           ` (15 preceding siblings ...)
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 16/25] avfilter/stripstyles: Add stripstyles filter softworkz
@ 2022-06-25  9:57         ` softworkz
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 18/25] avfilter/graphicsub2text: Add new graphicsub2text filter (OCR) softworkz
                           ` (10 subsequent siblings)
  27 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-25  9:57 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

- splitcc {V -> VS)
  Extract closed-caption (A53) data from video
  frames as subtitle Frames

ffmpeg -y -loglevel verbose -i "https://streams.videolan.org/streams
/ts/CC/NewsStream-608-ac3.ts" -filter_complex "[0:v]splitcc[vid1],
textmod=mode=remove_chars:find='@',[vid1]overlay_textsubs" output.mkv

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 configure                |   1 +
 doc/filters.texi         |  63 +++++++
 libavfilter/Makefile     |   1 +
 libavfilter/allfilters.c |   1 +
 libavfilter/sf_splitcc.c | 395 +++++++++++++++++++++++++++++++++++++++
 5 files changed, 461 insertions(+)
 create mode 100644 libavfilter/sf_splitcc.c

diff --git a/configure b/configure
index 99b4d7c8d7..bde13f3e09 100755
--- a/configure
+++ b/configure
@@ -3728,6 +3728,7 @@ spp_filter_select="fft idctdsp fdctdsp me_cmp pixblockdsp"
 sr_filter_deps="avformat swscale"
 sr_filter_select="dnn"
 stereo3d_filter_deps="gpl"
+splitcc_filter_deps="avcodec"
 subtitles_filter_deps="avformat avcodec libass"
 super2xsai_filter_deps="gpl"
 pixfmts_super2xsai_test_deps="super2xsai_filter"
diff --git a/doc/filters.texi b/doc/filters.texi
index 1158df64d2..b4bbbace7f 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -27408,6 +27408,69 @@ ffmpeg -i INPUT -filter_complex "showspeaker=format=colon:style='@{\\c&HDD0000&\
 @end example
 @end itemize
 
+
+@section splitcc
+
+Split-out closed-caption/A53 subtitles from video frame side data.
+
+This filter provides an input and an output for video frames, which are just passed through without modification.
+The second out provides subtitle frames which are extracted from video frame side data.
+
+Inputs:
+@itemize
+@item 0: Video [ALL]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Video (same as input)
+@item 1: Subtitles [TEXT]
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+
+@item use_cc_styles
+Emit closed caption style header.
+This will make closed captions appear in white font with a black rectangle background.
+
+@item real_time
+Emit subtitle events as they are decoded for real-time display.
+
+@item real_time_latency_msec
+Minimum elapsed time between emitting real-time subtitle events.
+Only applies to real_time mode.
+
+@item data_field
+Select data field. Possible values:
+
+@table @samp
+@item auto
+Pick first one that appears.
+@item first
+@item second
+@end table
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Extract closed captions as text subtitle stream and overlay it onto the video in cc style (black bar background):
+@example
+ffmpeg -i "https://streams.videolan.org/streams/ts/CC/NewsStream-608-ac3.ts" -filter_complex  "[0:v:0]splitcc=use_cc_styles=1[vid1][sub1];[vid1][sub1]overlaytextsubs" output.mkv
+@end example
+
+@item
+A nicer variant, using realtime output from cc_dec and rendering it with the render_latest_only parameter from overlaytextsubs to avoid ghosting by timely overlap.
+@example
+ffmpeg -i "https://streams.videolan.org/streams/ts/CC/NewsStream-608-ac3.ts" -filter_complex  "[0:v:0]splitcc=real_time=1:real_time_latency_msec=200[vid1][sub1];[vid1][sub1]overlaytextsubs=render_latest_only=1" output.mkv
+@end example
+@end itemize
+
+
 @section textsub2video
 
 Converts text subtitles to video frames.
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index a99a0f6583..958da451ea 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -580,6 +580,7 @@ OBJS-$(CONFIG_NULLSINK_FILTER)               += vsink_nullsink.o
 # subtitle filters
 OBJS-$(CONFIG_CENSOR_FILTER)                 += sf_textmod.o
 OBJS-$(CONFIG_SHOW_SPEAKER_FILTER)           += sf_textmod.o
+OBJS-$(CONFIG_SPLITCC_FILTER)                += sf_splitcc.o
 OBJS-$(CONFIG_STRIPSTYLES_FILTER)            += sf_stripstyles.o
 OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
 
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 7a958b51d4..3aa1e5ebc0 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -572,6 +572,7 @@ extern const AVFilter ff_avsrc_movie;
 /* subtitle filters */
 extern const AVFilter ff_sf_censor;
 extern const AVFilter ff_sf_showspeaker;
+extern const AVFilter ff_sf_splitcc;
 extern const AVFilter ff_sf_stripstyles;
 extern const AVFilter ff_sf_textmod;
 
diff --git a/libavfilter/sf_splitcc.c b/libavfilter/sf_splitcc.c
new file mode 100644
index 0000000000..14235e822c
--- /dev/null
+++ b/libavfilter/sf_splitcc.c
@@ -0,0 +1,395 @@
+/*
+ * 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
+ * subtitle filter for splitting out closed-caption/A53 subtitles from video frame side data
+ */
+
+#include "filters.h"
+#include "libavutil/opt.h"
+#include "subtitles.h"
+#include "libavcodec/avcodec.h"
+
+static const AVRational ms_tb = {1, 1000};
+
+typedef struct SplitCaptionsContext {
+    const AVClass *class;
+    enum AVSubtitleType format;
+    AVCodecContext *cc_dec;
+    int eof;
+    AVFrame *next_sub_frame;
+    AVFrame *empty_sub_frame;
+    int new_frame;
+    int64_t next_repetition_pts;
+    int had_keyframe;
+    AVBufferRef *subtitle_header;
+    int use_cc_styles;
+    int real_time;
+    int real_time_latency_msec;
+    int data_field;
+    int scatter_realtime_output;
+} SplitCaptionsContext;
+
+static int64_t ms_to_avtb(int64_t ms)
+{
+    return av_rescale_q(ms, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q);
+}
+
+static int init(AVFilterContext *ctx)
+{
+    SplitCaptionsContext *s = ctx->priv;
+    AVDictionary *options = NULL;
+
+    int ret;
+    const AVCodec *codec = avcodec_find_decoder(AV_CODEC_ID_EIA_608);
+    if (!codec) {
+        av_log(ctx, AV_LOG_ERROR, "failed to find EIA-608/708 decoder\n");
+        return AVERROR_DECODER_NOT_FOUND;
+    }
+
+    if (!((s->cc_dec = avcodec_alloc_context3(codec)))) {
+        av_log(ctx, AV_LOG_ERROR, "failed to allocate EIA-608/708 decoder\n");
+        return AVERROR(ENOMEM);
+    }
+
+    av_dict_set_int(&options, "real_time", s->real_time, 0);
+    av_dict_set_int(&options, "real_time_latency_msec", s->real_time_latency_msec, 0);
+    av_dict_set_int(&options, "data_field", s->data_field, 0);
+
+    if ((ret = avcodec_open2(s->cc_dec, codec, &options)) < 0) {
+        av_log(ctx, AV_LOG_ERROR, "failed to open EIA-608/708 decoder: %i\n", ret);
+        return ret;
+    }
+
+    if (s->use_cc_styles && s->cc_dec->subtitle_header && s->cc_dec->subtitle_header[0] != 0) {
+        char* subtitle_header =  av_strdup((char *)s->cc_dec->subtitle_header);
+        if (!subtitle_header)
+            return AVERROR(ENOMEM);
+        s->subtitle_header = av_buffer_create((uint8_t *)subtitle_header, strlen(subtitle_header) + 1, NULL, NULL, 0);
+        if (!s->subtitle_header) {
+            av_free(subtitle_header);
+            return AVERROR(ENOMEM);
+        }
+    }
+
+    return 0;
+}
+
+static void uninit(AVFilterContext *ctx)
+{
+    SplitCaptionsContext *s = ctx->priv;
+    av_frame_free(&s->next_sub_frame);
+    av_frame_free(&s->empty_sub_frame);
+    av_buffer_unref(&s->subtitle_header);
+}
+
+static int config_input(AVFilterLink *link)
+{
+    const SplitCaptionsContext *context = link->dst->priv;
+
+    if (context->cc_dec)
+        context->cc_dec->pkt_timebase = link->time_base;
+
+    return 0;
+}
+
+static int query_formats(AVFilterContext *ctx)
+{
+    AVFilterFormats *formats;
+    AVFilterLink *inlink0 = ctx->inputs[0];
+    AVFilterLink *outlink0 = ctx->outputs[0];
+    AVFilterLink *outlink1 = ctx->outputs[1];
+    static const enum AVSubtitleType subtitle_fmts[] = { AV_SUBTITLE_FMT_ASS, AV_SUBTITLE_FMT_NONE };
+    int ret;
+
+    /* set input0 video formats */
+    formats = ff_all_formats(AVMEDIA_TYPE_VIDEO);
+    if ((ret = ff_formats_ref(formats, &inlink0->outcfg.formats)) < 0)
+        return ret;
+
+    /* set output0 video formats */
+    if ((ret = ff_formats_ref(formats, &outlink0->incfg.formats)) < 0)
+        return ret;
+
+    /* set output1 subtitle formats */
+    formats = ff_make_format_list(subtitle_fmts);
+    if ((ret = ff_formats_ref(formats, &outlink1->incfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int config_video_output(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    const AVFilterLink *inlink = outlink->src->inputs[0];
+
+    outlink->w = ctx->inputs[0]->w;
+    outlink->h = ctx->inputs[0]->h;
+    outlink->frame_rate = ctx->inputs[0]->frame_rate;
+    outlink->time_base = ctx->inputs[0]->time_base;
+    outlink->sample_aspect_ratio = ctx->inputs[0]->sample_aspect_ratio;
+
+    if (inlink->hw_frames_ctx)
+        outlink->hw_frames_ctx = av_buffer_ref(inlink->hw_frames_ctx);
+
+    return 0;
+}
+
+static int config_sub_output(AVFilterLink *outlink)
+{
+    SplitCaptionsContext *s = outlink->src->priv;
+    const AVFilterLink *inlink = outlink->src->inputs[0];
+
+    outlink->time_base = inlink->time_base;
+    outlink->format = AV_SUBTITLE_FMT_ASS;
+    outlink->frame_rate = (AVRational){1000, s->real_time_latency_msec};
+
+    return 0;
+}
+
+static int request_sub_frame(AVFilterLink *outlink)
+{
+    SplitCaptionsContext *s = outlink->src->priv;
+    int status;
+    int64_t pts;
+
+    if (!s->empty_sub_frame) {
+        s->empty_sub_frame = ff_get_subtitles_buffer(outlink, outlink->format);
+        if (!s->empty_sub_frame)
+            return AVERROR(ENOMEM);
+    }
+
+    if (!s->eof && ff_inlink_acknowledge_status(outlink->src->inputs[0], &status, &pts)) {
+        if (status == AVERROR_EOF)
+            s->eof = 1;
+    }
+
+    if (s->eof)
+        return AVERROR_EOF;
+
+    if (s->next_sub_frame) {
+
+        AVFrame *out = NULL;
+        s->next_sub_frame->pts++;
+
+        if (s->new_frame)
+            out = av_frame_clone(s->next_sub_frame);
+        else if (s->empty_sub_frame) {
+            s->empty_sub_frame->pts = s->next_sub_frame->pts;
+            out = av_frame_clone(s->empty_sub_frame);
+            av_frame_copy_props(out, s->next_sub_frame);
+            out->repeat_sub = 1;
+        }
+
+        if (!out)
+            return AVERROR(ENOMEM);
+
+        out->subtitle_timing.start_pts = av_rescale_q(s->next_sub_frame->pts, outlink->time_base, AV_TIME_BASE_Q);
+        s->new_frame = 0;
+
+        return ff_filter_frame(outlink, out);
+    }
+
+    return 0;
+}
+
+static int decode(AVCodecContext *avctx, AVFrame *frame, int *got_frame, AVPacket *pkt)
+{
+    int ret;
+
+    *got_frame = 0;
+
+    if (pkt) {
+        ret = avcodec_send_packet(avctx, pkt);
+        // In particular, we don't expect AVERROR(EAGAIN), because we read all
+        // decoded frames with avcodec_receive_frame() until done.
+        if (ret < 0 && ret != AVERROR_EOF)
+            return ret;
+    }
+
+    ret = avcodec_receive_frame(avctx, frame);
+    if (ret < 0 && ret != AVERROR(EAGAIN))
+        return ret;
+    if (ret >= 0)
+        *got_frame = 1;
+
+    return 0;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFrameSideData *sd;
+    SplitCaptionsContext *s = inlink->dst->priv;
+    AVFilterLink *outlink0 = inlink->dst->outputs[0];
+    AVFilterLink *outlink1 = inlink->dst->outputs[1];
+    AVPacket *pkt = NULL;
+    AVFrame *sub_out = NULL;
+
+    int ret;
+
+    outlink0->format = inlink->format;
+
+    sd = av_frame_get_side_data(frame, AV_FRAME_DATA_A53_CC);
+
+    if (sd && (s->had_keyframe || frame->key_frame)) {
+        int got_output = 0;
+
+        s->had_keyframe = 1;
+        pkt = av_packet_alloc();
+        pkt->buf = av_buffer_ref(sd->buf);
+        if (!pkt->buf) {
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
+
+        pkt->data = pkt->buf->data;
+        pkt->size = pkt->buf->size;
+        pkt->pts  = av_rescale_q(frame->pts, inlink->time_base, AV_TIME_BASE_Q);
+
+        sub_out = ff_get_subtitles_buffer(outlink1, AV_SUBTITLE_FMT_ASS);
+        if (!sub_out) {
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
+
+        if ((ret = av_buffer_replace(&sub_out->subtitle_header, s->subtitle_header)) < 0)
+            goto fail;
+
+        ret = decode(s->cc_dec, sub_out, &got_output, pkt);
+
+        ////if (got_output) {
+        ////    av_log(inlink->dst, AV_LOG_INFO, "CC Packet PTS: %"PRId64" got_output: %d  out_frame_pts: %"PRId64"  out_sub_pts: %"PRId64"\n", pkt->pts, got_output, sub_out->pts, sub_out->subtitle_pts);
+        ////}
+
+        av_packet_free(&pkt);
+
+        if (ret < 0) {
+            av_log(inlink->dst, AV_LOG_ERROR, "Decode error: %d \n", ret);
+            goto fail;
+        }
+
+        if (got_output) {
+            sub_out->pts = frame->pts;
+            av_frame_free(&s->next_sub_frame);
+            s->next_sub_frame = sub_out;
+            sub_out = NULL;
+            s->new_frame = 1;
+            s->next_sub_frame->pts = frame->pts;
+
+            if ((ret = av_buffer_replace(&s->next_sub_frame->subtitle_header, s->subtitle_header)) < 0)
+                goto fail;
+
+            if (s->real_time && s->scatter_realtime_output) {
+                if (s->next_repetition_pts)
+                    s->next_sub_frame->pts = s->next_repetition_pts;
+
+                s->next_sub_frame->subtitle_timing.duration = ms_to_avtb(s->real_time_latency_msec);
+                s->next_repetition_pts = s->next_sub_frame->pts + av_rescale_q(s->real_time_latency_msec, ms_tb, inlink->time_base);
+            }
+
+            ret = request_sub_frame(outlink1);
+            if (ret < 0)
+                goto fail;
+        }
+    }
+
+    if (s->real_time && s->scatter_realtime_output && !s->new_frame && s->next_repetition_pts > 0 && frame->pts > s->next_repetition_pts) {
+        s->new_frame = 1;
+        s->next_sub_frame->pts = s->next_repetition_pts;
+        s->next_repetition_pts = s->next_sub_frame->pts + av_rescale_q(s->real_time_latency_msec, ms_tb, inlink->time_base);
+    }
+
+    if (!s->next_sub_frame) {
+        s->next_sub_frame = ff_get_subtitles_buffer(outlink1, outlink1->format);
+        if (!s->next_sub_frame) {
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
+
+        s->next_sub_frame->subtitle_timing.duration = ms_to_avtb(s->real_time_latency_msec);
+        s->next_sub_frame->pts = frame->pts;
+        s->new_frame = 1;
+
+        if ((ret = av_buffer_replace(&s->next_sub_frame->subtitle_header, s->subtitle_header)) < 0)
+            goto fail;
+    }
+
+    ret = ff_filter_frame(outlink0, frame);
+
+fail:
+    av_packet_free(&pkt);
+    av_frame_free(&sub_out);
+    return ret;
+}
+
+#define OFFSET(x) offsetof(SplitCaptionsContext, x)
+#define FLAGS (AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption split_cc_options[] = {
+    { "use_cc_styles",    "Emit closed caption style header", OFFSET(use_cc_styles),  AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, NULL },
+    { "real_time", "emit subtitle events as they are decoded for real-time display", OFFSET(real_time), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS },
+    { "real_time_latency_msec", "minimum elapsed time between emitting real-time subtitle events", OFFSET(real_time_latency_msec), AV_OPT_TYPE_INT, { .i64 = 200 }, 0, 500, FLAGS },
+    { "scatter_realtime_output", "scatter output events to a duration of real_time_latency_msec", OFFSET(scatter_realtime_output), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS },
+    { "data_field", "select data field", OFFSET(data_field), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1, FLAGS, "data_field" },
+    { "auto",   "pick first one that appears", 0, AV_OPT_TYPE_CONST, { .i64 =-1 }, 0, 0, FLAGS, "data_field" },
+    { "first",  NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, FLAGS, "data_field" },
+    { "second", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, FLAGS, "data_field" },
+    { NULL },
+};
+
+AVFILTER_DEFINE_CLASS(split_cc);
+
+static const AVFilterPad inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_VIDEO,
+        .filter_frame = filter_frame,
+        .config_props = config_input,
+    },
+};
+
+static const AVFilterPad outputs[] = {
+    {
+        .name          = "video_passthrough",
+        .type          = AVMEDIA_TYPE_VIDEO,
+        .config_props  = config_video_output,
+    },
+    {
+        .name          = "subtitles",
+        .type          = AVMEDIA_TYPE_SUBTITLE,
+        .request_frame = request_sub_frame,
+        .config_props  = config_sub_output,
+    },
+};
+
+const AVFilter ff_sf_splitcc = {
+    .name           = "splitcc",
+    .description    = NULL_IF_CONFIG_SMALL("Extract closed-caption (A53) data from video as subtitle stream."),
+    .init           = init,
+    .uninit         = uninit,
+    .priv_size      = sizeof(SplitCaptionsContext),
+    .priv_class     = &split_cc_class,
+    .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_QUERY_FUNC(query_formats),
+};
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v5 18/25] avfilter/graphicsub2text: Add new graphicsub2text filter (OCR)
  2022-06-25  9:57       ` [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022 ffmpegagent
                           ` (16 preceding siblings ...)
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 17/25] avfilter/splitcc: Add splitcc filter for closed caption handling softworkz
@ 2022-06-25  9:57         ` softworkz
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 19/25] avfilter/subscale: Add filter for scaling and/or re-arranging graphical subtitles softworkz
                           ` (9 subsequent siblings)
  27 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-25  9:57 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 configure                        |    1 +
 doc/filters.texi                 |   55 ++
 libavfilter/Makefile             |    1 +
 libavfilter/allfilters.c         |    1 +
 libavfilter/sf_graphicsub2text.c | 1137 ++++++++++++++++++++++++++++++
 5 files changed, 1195 insertions(+)
 create mode 100644 libavfilter/sf_graphicsub2text.c

diff --git a/configure b/configure
index bde13f3e09..462d473c5f 100755
--- a/configure
+++ b/configure
@@ -3663,6 +3663,7 @@ frei0r_filter_deps="frei0r"
 frei0r_src_filter_deps="frei0r"
 fspp_filter_deps="gpl"
 gblur_vulkan_filter_deps="vulkan spirv_compiler"
+graphicsub2text_filter_deps="libtesseract"
 hflip_vulkan_filter_deps="vulkan spirv_compiler"
 histeq_filter_deps="gpl"
 hqdn3d_filter_deps="gpl"
diff --git a/doc/filters.texi b/doc/filters.texi
index b4bbbace7f..4bbec62c02 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -27174,6 +27174,61 @@ ffmpeg -i "https://streams.videolan.org/ffmpeg/mkv_subtitles.mkv" -filter_comple
 @end example
 @end itemize
 
+@section graphicsub2text
+
+Converts graphic subtitles to text subtitles by performing OCR.
+
+For this filter to be available, ffmpeg needs to be compiled with libtesseract (see https://github.com/tesseract-ocr/tesseract).
+Language models need to be downloaded from https://github.com/tesseract-ocr/tessdata and put into as subfolder named 'tessdata' or into a folder specified via the environment variable 'TESSDATA_PREFIX'.
+The path can also be specified via filter option (see below).
+
+Note: These models are including the data for both OCR modes.
+
+Inputs:
+- 0: Subtitles [bitmap]
+
+Outputs:
+- 0: Subtitles [text]
+
+It accepts the following parameters:
+
+@table @option
+@item ocr_mode
+The character recognition mode to use.
+
+Supported OCR modes are:
+
+@table @var
+@item 0, tesseract
+This is the classic libtesseract operation mode. It is fast but less accurate than LSTM.
+@item 1, lstm
+Newer OCR implementation based on ML models. Provides usually better results, requires more processing resources.
+@item 2, both
+Use a combination of both modes.
+@end table
+
+@item tessdata_path
+The path to a folder containing the language models to be used.
+
+@item language
+The recognition language. It needs to match the first three characters of a  language model file in the tessdata path.
+
+@end table
+
+
+@subsection Examples
+
+@itemize
+@item
+Convert DVB graphic subtitles to ASS (text) subtitles
+
+Note: For this to work, you need to have the data file 'eng.traineddata' in a 'tessdata' subfolder (see above).
+@example
+ffmpeg -loglevel verbose -i "https://streams.videolan.org/streams/ts/video_subs_ttxt%2Bdvbsub.ts" -filter_complex "[0:13]graphicsub2text=delay_when_no_duration=1" -c:s ass -y output.mkv
+@end example
+@end itemize
+
+
 @section graphicsub2video
 
 Renders graphic subtitles as video frames.
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 958da451ea..6e6485c99a 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -307,6 +307,7 @@ OBJS-$(CONFIG_GBLUR_FILTER)                  += vf_gblur.o
 OBJS-$(CONFIG_GBLUR_VULKAN_FILTER)           += vf_gblur_vulkan.o vulkan.o vulkan_filter.o
 OBJS-$(CONFIG_GEQ_FILTER)                    += vf_geq.o
 OBJS-$(CONFIG_GRADFUN_FILTER)                += vf_gradfun.o
+OBJS-$(CONFIG_GRAPHICSUB2TEXT_FILTER)        += sf_graphicsub2text.o
 OBJS-$(CONFIG_GRAPHICSUB2VIDEO_FILTER)       += vf_overlaygraphicsubs.o framesync.o
 OBJS-$(CONFIG_GRAPHMONITOR_FILTER)           += f_graphmonitor.o
 OBJS-$(CONFIG_GRAYWORLD_FILTER)              += vf_grayworld.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 3aa1e5ebc0..cbd93c4236 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -571,6 +571,7 @@ extern const AVFilter ff_avsrc_movie;
 
 /* subtitle filters */
 extern const AVFilter ff_sf_censor;
+extern const AVFilter ff_sf_graphicsub2text;
 extern const AVFilter ff_sf_showspeaker;
 extern const AVFilter ff_sf_splitcc;
 extern const AVFilter ff_sf_stripstyles;
diff --git a/libavfilter/sf_graphicsub2text.c b/libavfilter/sf_graphicsub2text.c
new file mode 100644
index 0000000000..47c7030939
--- /dev/null
+++ b/libavfilter/sf_graphicsub2text.c
@@ -0,0 +1,1137 @@
+/*
+ * 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
+ * subtitle filter to convert graphical subs to text subs via OCR
+ */
+
+#include <tesseract/capi.h>
+#include <libavutil/ass_internal.h>
+
+#include "drawutils.h"
+#include "libavutil/opt.h"
+#include "subtitles.h"
+
+#include "libavcodec/elbg.h"
+
+enum {
+    RFLAGS_NONE         = 0,
+    RFLAGS_HALIGN       = 1 << 0,
+    RFLAGS_VALIGN       = 1 << 1,
+    RFLAGS_FBOLD        = 1 << 2,
+    RFLAGS_FITALIC      = 1 << 3,
+    RFLAGS_FUNDERLINE   = 1 << 4,
+    RFLAGS_FONT         = 1 << 5,
+    RFLAGS_FONTSIZE     = 1 << 6,
+    RFLAGS_COLOR        = 1 << 7,
+    RFLAGS_OUTLINECOLOR = 1 << 8,
+    RFLAGS_ALL = RFLAGS_HALIGN | RFLAGS_VALIGN | RFLAGS_FBOLD | RFLAGS_FITALIC | RFLAGS_FUNDERLINE |
+                RFLAGS_FONT | RFLAGS_FONTSIZE | RFLAGS_COLOR | RFLAGS_OUTLINECOLOR,
+};
+
+typedef struct SubOcrContext {
+    const AVClass *class;
+    int w, h;
+
+    TessBaseAPI *tapi;
+    TessOcrEngineMode ocr_mode;
+    char *tessdata_path;
+    char *language;
+    int preprocess_images;
+    int dump_bitmaps;
+    int delay_when_no_duration;
+    int recognize;
+    double font_size_factor;
+
+    int readorder_counter;
+
+    AVFrame *pending_frame;
+    AVBufferRef *subtitle_header;
+    AVBPrint buffer;
+
+    // Color Quantization Fields
+    struct ELBGContext *ctx;
+    AVLFG lfg;
+    int *codeword;
+    int *codeword_closest_codebook_idxs;
+    int *codebook;
+    int r_idx, g_idx, b_idx, a_idx;
+    int64_t last_subtitle_pts;
+} SubOcrContext;
+
+typedef struct OcrImageProps {
+    int background_color_index;
+    int fill_color_index;
+
+} OcrImageProps;
+
+static int64_t ms_to_avtb(int64_t ms)
+{
+    return av_rescale_q(ms, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q);
+}
+
+static int create_ass_header(AVFilterContext* ctx)
+{
+    SubOcrContext* s = ctx->priv;
+
+    if (!(s->w && s->h)) {
+        av_log(ctx, AV_LOG_WARNING, "create_ass_header: no width and height specified!\n");
+        s->w = ASS_DEFAULT_PLAYRESX;
+        s->h = ASS_DEFAULT_PLAYRESY;
+    }
+
+    char* subtitle_header_text = avpriv_ass_get_subtitle_header_full(s->w, s->h, ASS_DEFAULT_FONT, ASS_DEFAULT_FONT_SIZE,
+        ASS_DEFAULT_COLOR, ASS_DEFAULT_COLOR, ASS_DEFAULT_BACK_COLOR, ASS_DEFAULT_BACK_COLOR, ASS_DEFAULT_BOLD,
+        ASS_DEFAULT_ITALIC, ASS_DEFAULT_UNDERLINE, ASS_DEFAULT_BORDERSTYLE, ASS_DEFAULT_ALIGNMENT, 0);
+
+    if (!subtitle_header_text)
+        return AVERROR(ENOMEM);
+
+    s->subtitle_header = av_buffer_create((uint8_t*)subtitle_header_text, strlen(subtitle_header_text) + 1, NULL, NULL, 0);
+
+    if (!s->subtitle_header)
+        return AVERROR(ENOMEM);
+
+    return 0;
+}
+
+static int init(AVFilterContext *ctx)
+{
+    SubOcrContext *s = ctx->priv;
+    const char* tver = TessVersion();
+    uint8_t rgba_map[4];
+    int ret;
+
+    s->tapi = TessBaseAPICreate();
+
+    if (!s->tapi || !tver || !strlen(tver)) {
+        av_log(ctx, AV_LOG_ERROR, "Failed to access libtesseract\n");
+        return AVERROR(ENOSYS);
+    }
+
+    av_log(ctx, AV_LOG_VERBOSE, "Initializing libtesseract, version: %s\n", tver);
+
+    ret = TessBaseAPIInit4(s->tapi, s->tessdata_path, s->language, s->ocr_mode, NULL, 0, NULL, NULL, 0, 1);
+    if (ret < 0 ) {
+        av_log(ctx, AV_LOG_ERROR, "Failed to initialize libtesseract. Error: %d\n", ret);
+        return AVERROR(ENOSYS);
+    }
+
+    ret = TessBaseAPISetVariable(s->tapi, "tessedit_char_blacklist", "|");
+    if (ret < 0 ) {
+        av_log(ctx, AV_LOG_ERROR, "Failed to set 'tessedit_char_blacklist'. Error: %d\n", ret);
+        return AVERROR(EINVAL);
+    }
+
+    av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
+
+    ff_fill_rgba_map(&rgba_map[0], AV_PIX_FMT_RGB32);
+
+    s->r_idx = rgba_map[0]; // R
+    s->g_idx = rgba_map[1]; // G
+    s->b_idx = rgba_map[2]; // B
+    s->a_idx = rgba_map[3]; // A
+
+    av_lfg_init(&s->lfg, 123456789);
+
+    return 0;
+}
+
+static void uninit(AVFilterContext *ctx)
+{
+    SubOcrContext *s = ctx->priv;
+
+    av_buffer_unref(&s->subtitle_header);
+    av_bprint_finalize(&s->buffer, NULL);
+
+    if (s->tapi) {
+        TessBaseAPIEnd(s->tapi);
+        TessBaseAPIDelete(s->tapi);
+    }
+
+    avpriv_elbg_free(&s->ctx);
+}
+
+static int query_formats(AVFilterContext *ctx)
+{
+    AVFilterFormats *formats, *formats2;
+    AVFilterLink *inlink = ctx->inputs[0];
+    AVFilterLink *outlink = ctx->outputs[0];
+    static const enum AVSubtitleType in_fmts[] = { AV_SUBTITLE_FMT_BITMAP, AV_SUBTITLE_FMT_NONE };
+    static const enum AVSubtitleType out_fmts[] = { AV_SUBTITLE_FMT_ASS, AV_SUBTITLE_FMT_NONE };
+    int ret;
+
+    /* set input format */
+    formats = ff_make_format_list(in_fmts);
+    if ((ret = ff_formats_ref(formats, &inlink->outcfg.formats)) < 0)
+        return ret;
+
+    /* set output format */
+    formats2 = ff_make_format_list(out_fmts);
+    if ((ret = ff_formats_ref(formats2, &outlink->incfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int config_input(AVFilterLink *inlink)
+{
+    AVFilterContext *ctx = inlink->dst;
+    SubOcrContext *s = ctx->priv;
+
+    if (s->w <= 0 || s->h <= 0) {
+        s->w = inlink->w;
+        s->h = inlink->h;
+    }
+
+    return create_ass_header(ctx);
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    AVFilterLink *inlink = outlink->src->inputs[0];
+    const AVFilterContext *ctx  = outlink->src;
+    SubOcrContext *s = ctx->priv;
+
+    outlink->format = AV_SUBTITLE_FMT_ASS;
+    outlink->w = s->w;
+    outlink->h = s->h;
+    outlink->time_base = inlink->time_base;
+    outlink->frame_rate = inlink->frame_rate;
+
+    return 0;
+}
+
+static void free_subtitle_area(AVSubtitleArea *area)
+{
+    for (unsigned n = 0; n < FF_ARRAY_ELEMS(area->buf); n++)
+        av_buffer_unref(&area->buf[n]);
+
+    av_freep(&area->text);
+    av_freep(&area->ass);
+    av_free(area);
+
+}
+
+static AVSubtitleArea *copy_subtitle_area(const AVSubtitleArea *src)
+{
+    AVSubtitleArea *dst = av_mallocz(sizeof(AVSubtitleArea));
+
+    if (!dst)
+        return NULL;
+
+    dst->x         =  src->x;
+    dst->y         =  src->y;
+    dst->w         =  src->w;
+    dst->h         =  src->h;
+    dst->nb_colors =  src->nb_colors;
+    dst->type      =  src->type;
+    dst->flags     =  src->flags;
+
+    for (unsigned i = 0; i < AV_NUM_BUFFER_POINTERS; i++) {
+        if (src->h > 0 && src->w > 0 && src->buf[i]) {
+            dst->buf[0] = av_buffer_ref(src->buf[i]);
+            if (!dst->buf[i])
+                return NULL;
+
+            const int ret = av_buffer_make_writable(&dst->buf[i]);
+            if (ret < 0)
+                return NULL;
+
+            dst->linesize[i] = src->linesize[i];
+        }
+    }
+
+    memcpy(&dst->pal[0], &src->pal[0], sizeof(src->pal[0]) * 256);
+
+
+    return dst;
+}
+
+static int quantize_image_colors(SubOcrContext *const s, AVSubtitleArea *subtitle_area)
+{
+    const int num_quantized_colors = 3;
+    int k, ret;
+    const int codeword_length = subtitle_area->w * subtitle_area->h;
+    uint8_t *src_data = subtitle_area->buf[0]->data;
+
+    if (subtitle_area->nb_colors <= num_quantized_colors) {
+        av_log(s, AV_LOG_DEBUG, "No need to quantize colors. Color count: %d\n", subtitle_area->nb_colors);
+        return 0;
+    }
+
+    // Convert palette to grayscale
+    for (int i = 0; i < subtitle_area->nb_colors; i++) {
+        uint8_t *color        = (uint8_t *)&subtitle_area->pal[i];
+        const uint8_t average = (uint8_t)(((int)color[s->r_idx] + color[s->g_idx] + color[s->b_idx]) / 3);
+        color[s->b_idx]       = average;
+        color[s->g_idx]       = average;
+        color[s->r_idx]       = average;
+    }
+
+    /* Re-Initialize */
+    s->codeword = av_realloc_f(s->codeword, codeword_length, 4 * sizeof(*s->codeword));
+    if (!s->codeword)
+        return AVERROR(ENOMEM);
+
+    s->codeword_closest_codebook_idxs = av_realloc_f(s->codeword_closest_codebook_idxs,
+        codeword_length, sizeof(*s->codeword_closest_codebook_idxs));
+    if (!s->codeword_closest_codebook_idxs)
+        return AVERROR(ENOMEM);
+
+    s->codebook = av_realloc_f(s->codebook, num_quantized_colors, 4 * sizeof(*s->codebook));
+    if (!s->codebook)
+        return AVERROR(ENOMEM);
+
+    /* build the codeword */
+    k = 0;
+    for (int i = 0; i < subtitle_area->h; i++) {
+        uint8_t *p = src_data;
+        for (int j = 0; j < subtitle_area->w; j++) {
+            const uint8_t *color = (uint8_t *)&subtitle_area->pal[*p];
+            s->codeword[k++] = color[s->b_idx];
+            s->codeword[k++] = color[s->g_idx];
+            s->codeword[k++] = color[s->r_idx];
+            s->codeword[k++] = color[s->a_idx];
+            p++;
+        }
+        src_data += subtitle_area->linesize[0];
+    }
+
+    /* compute the codebook */
+    ret = avpriv_elbg_do(&s->ctx, s->codeword, 4, codeword_length, s->codebook,
+        num_quantized_colors, 1, s->codeword_closest_codebook_idxs, &s->lfg, 0);
+    if (ret < 0)
+        return ret;
+
+    /* Write Palette */
+    for (int i = 0; i < num_quantized_colors; i++) {
+        subtitle_area->pal[i] = s->codebook[i*4+3] << 24  |
+                    (s->codebook[i*4+2] << 16) |
+                    (s->codebook[i*4+1] <<  8) |
+                    (s->codebook[i*4  ] <<  0);
+    }
+
+
+    av_log(s, AV_LOG_DEBUG, "Quantized colors from %d to %d\n", subtitle_area->nb_colors, num_quantized_colors);
+
+    subtitle_area->nb_colors = num_quantized_colors;
+    src_data = subtitle_area->buf[0]->data;
+
+    /* Write Image */
+    k = 0;
+    for (int i = 0; i < subtitle_area->h; i++) {
+        uint8_t *p = src_data;
+        for (int j = 0; j < subtitle_area->w; j++, p++) {
+            p[0] = (uint8_t)s->codeword_closest_codebook_idxs[k++];
+        }
+
+        src_data += subtitle_area->linesize[0];
+    }
+
+    return ret;
+}
+
+#define MEASURE_LINE_COUNT 6
+
+static uint8_t get_background_color_index(SubOcrContext *const s, const AVSubtitleArea *subtitle_area)
+{
+    const int linesize = subtitle_area->linesize[0];
+    int index_counts[256] = {0};
+    const unsigned int line_offsets[MEASURE_LINE_COUNT] = {
+        0,
+        linesize,
+        2 * linesize,
+        (subtitle_area->h - 3) * linesize,
+        (subtitle_area->h - 2) * linesize,
+        (subtitle_area->h - 1) * linesize
+    };
+
+    const uint8_t *src_data = subtitle_area->buf[0]->data;
+    const uint8_t tl = src_data[0];
+    const uint8_t tr = src_data[subtitle_area->w - 1];
+    const uint8_t bl = src_data[(subtitle_area->h - 1) * linesize + 0];
+    const uint8_t br = src_data[(subtitle_area->h - 1) * linesize + subtitle_area->w - 1];
+    uint8_t max_index = 0;
+    int max_count;
+
+    // When all corner pixels are equal, assume that as background color
+    if (tl == tr == bl == br || subtitle_area->h < 6)
+        return tl;
+
+    for (unsigned int i = 0; i < MEASURE_LINE_COUNT; i++) {
+        uint8_t *p = subtitle_area->buf[0]->data + line_offsets[i];
+        for (int k = 0; k < subtitle_area->w; k++)
+            index_counts[p[k]]++;
+    }
+
+    max_count = index_counts[0];
+
+    for (uint8_t i = 1; i < subtitle_area->nb_colors; i++) {
+        if (index_counts[i] > max_count) {
+            max_count = index_counts[i];
+            max_index = i;
+        }
+    }
+
+    return max_index;
+}
+
+static uint8_t get_text_color_index(SubOcrContext *const s, const AVSubtitleArea *subtitle_area, const uint8_t bg_color_index, uint8_t *outline_color_index)
+{
+    const int linesize = subtitle_area->linesize[0];
+    int index_counts[256] = {0};
+    uint8_t last_index = bg_color_index;
+    int max_count, min_req_count;
+    uint8_t max_index = 0;
+
+    for (int i = 3; i < subtitle_area->h - 3; i += 5) {
+        const uint8_t *p = subtitle_area->buf[0]->data + ((ptrdiff_t)linesize * i);
+        for (int k = 0; k < subtitle_area->w; k++) {
+            const uint8_t cur_index = p[k];
+
+            // When color hasn't changed, continue
+            if (cur_index == last_index)
+                continue;
+
+            if (cur_index != bg_color_index)
+                index_counts[cur_index]++;
+
+            last_index = cur_index;
+        }
+    }
+
+    max_count = index_counts[0];
+
+    for (uint8_t i = 1; i < subtitle_area->nb_colors; i++) {
+        if (index_counts[i] > max_count) {
+            max_count = index_counts[i];
+            max_index = i;
+        }
+    }
+
+    min_req_count = max_count / 3;
+
+    for (uint8_t i = 1; i < subtitle_area->nb_colors; i++) {
+        if (index_counts[i] < min_req_count)
+            index_counts[i] = 0;
+    }
+
+    *outline_color_index = max_index;
+
+    index_counts[max_index] = 0;
+    max_count = 0;
+
+    for (uint8_t i = 0; i < subtitle_area->nb_colors; i++) {
+        if (index_counts[i] > max_count) {
+            max_count = index_counts[i];
+            max_index = i;
+        }
+    }
+
+    if (*outline_color_index == max_index)
+        *outline_color_index = 255;
+
+    return max_index;
+}
+
+static void make_image_binary(SubOcrContext *const s, AVSubtitleArea *subtitle_area, const uint8_t text_color_index)
+{
+    for (int i = 0; i < subtitle_area->nb_colors; i++) {
+
+        if (i != text_color_index)
+            subtitle_area->pal[i] = 0xffffffff;
+        else
+            subtitle_area->pal[i] = 0xff000000;
+    }
+}
+
+static int get_crop_region(SubOcrContext *const s, const AVSubtitleArea *subtitle_area, uint8_t text_color_index, int *x, int *y, int *w, int *h)
+{
+    const int linesize = subtitle_area->linesize[0];
+    int max_y = 0, max_x = 0;
+    int min_y = subtitle_area->h - 1, min_x = subtitle_area->w - 1;
+
+    for (int i = 0; i < subtitle_area->h; i += 3) {
+        const uint8_t *p = subtitle_area->buf[0]->data + ((ptrdiff_t)linesize * i);
+        for (int k = 0; k < subtitle_area->w; k += 2) {
+            if (p[k] == text_color_index) {
+                min_y = FFMIN(min_y, i);
+                min_x = FFMIN(min_x, k);
+                max_y = FFMAX(max_y, i);
+                max_x = FFMAX(max_x, k);
+            }
+        }
+    }
+
+    if (max_y <= min_y || max_x <= min_x) {
+        av_log(s, AV_LOG_WARNING, "Unable to detect crop region\n");
+        *x = 0;
+        *y = 0;
+        *w = subtitle_area->w;
+        *h = subtitle_area->h;
+    }    else {
+        *x = FFMAX(min_x - 10, 0);
+        *y = FFMAX(min_y - 10, 0);
+        *w = FFMIN(max_x + 10 - *x, (subtitle_area->w - *x));
+        *h = FFMIN(max_y + 10 - *y, (subtitle_area->h - *y));
+    }
+
+    return 0;
+}
+
+static int crop_area_bitmap(SubOcrContext *const s, AVSubtitleArea *subtitle_area, int x, int y, int w, int h)
+{
+    const int linesize = subtitle_area->linesize[0];
+    AVBufferRef *dst = av_buffer_allocz(h * w);
+    uint8_t *d;
+
+    if (!dst)
+        return AVERROR(ENOMEM);
+
+    d = dst->data;
+
+    for (int i = y; i < y + h; i++) {
+        const uint8_t *p = subtitle_area->buf[0]->data + ((ptrdiff_t)linesize * i);
+        for (int k = x; k < x + w; k++) {
+            *d = p[k];
+            d++;
+        }
+    }
+
+    subtitle_area->w = w;
+    subtitle_area->h = h;
+    subtitle_area->x += x;
+    subtitle_area->y += y;
+    subtitle_area->linesize[0] = w;
+    av_buffer_replace(&subtitle_area->buf[0], dst);
+
+    av_buffer_unref(&dst);
+    return 0;
+}
+
+#define R 0
+#define G 1
+#define B 2
+#define A 3
+
+static int print_code(AVBPrint *buf, int in_code, const char *fmt, ...)
+{
+    va_list vl;
+
+    if (!in_code)
+        av_bprint_chars(buf, '{', 1);
+
+    va_start(vl, fmt);
+    av_vbprintf(buf, fmt, vl);
+    va_end(vl);
+
+    return 1;
+}
+
+static int end_code(AVBPrint *buf, int in_code)
+{
+    if (in_code)
+        av_bprint_chars(buf, '}', 1);
+    return 0;
+}
+
+static uint8_t* create_grayscale_image(AVFilterContext *ctx, AVSubtitleArea *area, int invert)
+{
+    uint8_t gray_pal[256];
+    const size_t img_size = area->buf[0]->size;
+    const uint8_t* img    = area->buf[0]->data;
+    uint8_t* gs_img       = av_malloc(img_size);
+
+    if (!gs_img)
+        return NULL;
+
+    for (unsigned i = 0; i < 256; i++) {
+        const uint8_t *col = (uint8_t*)&area->pal[i];
+        const int val      = (int)col[3] * FFMAX3(col[0], col[1], col[2]);
+        gray_pal[i]        = (uint8_t)(val >> 8);
+    }
+
+    if (invert)
+        for (unsigned i = 0; i < img_size; i++)
+            gs_img[i]   = 255 - gray_pal[img[i]];
+    else
+        for (unsigned i = 0; i < img_size; i++)
+            gs_img[i]   = gray_pal[img[i]];
+
+    return gs_img;
+}
+
+static uint8_t* create_bitmap_image(AVFilterContext *ctx, AVSubtitleArea *area, const uint8_t text_color_index)
+{
+    const size_t img_size = area->buf[0]->size;
+    const uint8_t* img    = area->buf[0]->data;
+    uint8_t* gs_img       = av_malloc(img_size);
+
+    if (!gs_img)
+        return NULL;
+
+    for (unsigned i = 0; i < img_size; i++) {
+        if (img[i] == text_color_index)
+            gs_img[i]   = 0;
+        else
+            gs_img[i]   = 255;
+    }
+
+    return gs_img;
+}
+
+static void png_save(AVFilterContext *ctx, const char *filename, AVSubtitleArea *area)
+{
+    int x, y;
+    int v;
+    FILE *f;
+    char fname[40];
+    const uint8_t *data = area->buf[0]->data;
+
+    snprintf(fname, sizeof(fname), "%s.ppm", filename);
+
+    f = fopen(fname, "wb");
+    if (!f) {
+        perror(fname);
+        return;
+    }
+    fprintf(f, "P6\n"
+            "%d %d\n"
+            "%d\n",
+            area->w, area->h, 255);
+    for(y = 0; y < area->h; y++) {
+        for(x = 0; x < area->w; x++) {
+            const uint8_t index = data[y * area->linesize[0] + x];
+            v = (int)area->pal[index];
+            putc(v >> 16 & 0xff, f);
+            putc(v >> 8 & 0xff, f);
+            putc(v >> 0 & 0xff, f);
+        }
+    }
+
+    fclose(f);
+}
+
+static int get_max_index(int score[256])
+{
+    int max_val = 0, max_index = 0;
+
+    for (int i = 0; i < 256; i++) {
+        if (score[i] > max_val) {
+            max_val = score[i];
+            max_index = i;
+        }
+    }
+
+    return max_index;
+}
+
+static int get_word_colors(AVFilterContext *ctx, TessResultIterator* ri, const AVSubtitleArea* area, const AVSubtitleArea* original_area,
+                           uint8_t bg_color_index, uint8_t text_color_index, uint8_t outline_color_index,
+                           uint32_t* bg_color, uint32_t* text_color, uint32_t* outline_color)
+{
+    int left = 0, top = 0, right = 0, bottom = 0, ret;
+    int bg_score[256] = {0}, text_score[256] = {0}, outline_score[256] = {0};
+    int max_index;
+
+    ret = TessPageIteratorBoundingBox((TessPageIterator*)ri, RIL_WORD, &left, &top, &right, &bottom);
+    if (ret < 0) {
+        av_log(ctx, AV_LOG_WARNING, "get_word_colors: IteratorBoundingBox failed: %d\n", ret);
+        return  ret;
+    }
+
+    if (left >= area->w || right >= area->w || top >= area->h || bottom >= area->h) {
+        av_log(ctx, AV_LOG_WARNING, "get_word_colors: word bounding box (l: %d, t: %d r: %d, b: %d) out of image bounds (%dx%d)\n", left,top, right, bottom, area->w, area->h);
+        return  AVERROR(EINVAL);
+    }
+
+    for (int y = top; y < bottom; y += 3) {
+        uint8_t *p = area->buf[0]->data + (y * area->linesize[0]) + left;
+        uint8_t *porig = original_area->buf[0]->data + (y * original_area->linesize[0]) + left;
+        uint8_t current_index = 255;
+
+        for (int x = left; x < right; x++, p++, porig++) {
+
+            if (*p == current_index) {
+                if (*p == bg_color_index)
+                    bg_score[*porig]++;
+                if (*p == text_color_index)
+                    text_score[*porig]++;
+                if (*p == outline_color_index)
+                    outline_score[*porig]++;
+            }
+
+            current_index = *p;
+        }
+    }
+
+    max_index = get_max_index(bg_score);
+    if (bg_score[max_index] > 0)
+        *bg_color = original_area->pal[max_index];
+
+    max_index = get_max_index(text_score);
+    if (text_score[max_index] > 0)
+        *text_color = original_area->pal[max_index];
+
+    max_index = get_max_index(outline_score);
+    if (outline_score[max_index] > 0)
+        *outline_color = original_area->pal[max_index];
+
+    return 0;
+}
+
+static int convert_area(AVFilterContext *ctx, AVSubtitleArea *area, const AVFrame *frame, const unsigned area_index, int *margin_v)
+{
+    SubOcrContext *s = ctx->priv;
+    char *ocr_text = NULL;
+    int ret = 0;
+    uint8_t *gs_img;
+    uint8_t bg_color_index;
+    uint8_t text_color_index = 255;
+    uint8_t outline_color_index = 255;
+    char filename[32];
+    AVSubtitleArea *original_area = copy_subtitle_area(area);
+
+    if (!original_area)
+        return AVERROR(ENOMEM);
+
+    if (area->w < 6 || area->h < 6) {
+        area->ass = NULL;
+        goto exit;
+    }
+
+    if (s->dump_bitmaps) {
+        snprintf(filename, sizeof(filename), "graphicsub2text_%"PRId64"_%d_original", frame->subtitle_timing.start_pts, area_index);
+        png_save(ctx, filename, area);
+    }
+
+    if (s->preprocess_images) {
+        ret = quantize_image_colors(s, area);
+        if (ret < 0)
+            goto exit;
+        if (s->dump_bitmaps && original_area->nb_colors != area->nb_colors) {
+            snprintf(filename, sizeof(filename), "graphicsub2text_%"PRId64"_%d_quantized", frame->subtitle_timing.start_pts, area_index);
+            png_save(ctx, filename, area);
+        }
+    }
+
+    bg_color_index = get_background_color_index(s, area);
+
+    if (s->preprocess_images) {
+        int x, y, w, h;
+
+        for (int i = 0; i < area->nb_colors; ++i) {
+            av_log(s, AV_LOG_DEBUG, "Color #%d: %0.8X\n", i, area->pal[i]);
+        }
+
+        text_color_index = get_text_color_index(s, area, bg_color_index, &outline_color_index);
+
+        get_crop_region(s, area, text_color_index, &x, &y, &w, &h);
+
+        if ((ret = crop_area_bitmap(s, area, x, y, w, h) < 0))
+            goto exit;
+
+        if ((ret = crop_area_bitmap(s, original_area, x, y, w, h) < 0))
+            goto exit;
+
+        make_image_binary(s, area, text_color_index);
+
+        if (s->dump_bitmaps) {
+            snprintf(filename, sizeof(filename), "graphicsub2text_%"PRId64"_%d_preprocessed", frame->subtitle_timing.start_pts, area_index);
+            png_save(ctx, filename, area);
+        }
+
+        gs_img = create_bitmap_image(ctx, area, text_color_index);
+    } else
+        gs_img = create_grayscale_image(ctx, area, 1);
+
+    if (!gs_img) {
+        ret = AVERROR(ENOMEM);
+        goto exit;
+    }
+
+    area->type = AV_SUBTITLE_FMT_ASS;
+    TessBaseAPISetImage(s->tapi, gs_img, area->w, area->h, 1, area->linesize[0]);
+
+    TessBaseAPISetSourceResolution(s->tapi, 72);
+
+    ret = TessBaseAPIRecognize(s->tapi, NULL);
+    if (ret == 0)
+        ocr_text = TessBaseAPIGetUTF8Text(s->tapi);
+
+    if (!ocr_text || !strlen(ocr_text)) {
+        av_log(ctx, AV_LOG_WARNING, "OCR didn't return a text. ret=%d\n", ret);
+        area->ass = NULL;
+
+        goto exit;
+    }
+
+    const size_t len = strlen(ocr_text);
+    if (len > 0 && ocr_text[len - 1] == '\n')
+        ocr_text[len - 1] = 0;
+
+    av_log(ctx, AV_LOG_VERBOSE, "OCR Result: %s\n", ocr_text);
+
+    area->ass = av_strdup(ocr_text);
+    TessDeleteText(ocr_text);
+
+    // End of simple recognition
+
+    if (s->recognize != RFLAGS_NONE) {
+        TessResultIterator* ri = 0;
+        const TessPageIteratorLevel level = RIL_WORD;
+        int cur_is_bold = 0, cur_is_italic = 0, cur_is_underlined = 0, cur_pointsize = 0;
+        uint32_t cur_text_color = 0, cur_bg_color = 0, cur_outline_color = 0;
+
+        char *cur_font_name = NULL;
+        int valign = 0; // 0: bottom, 4: top, 8 middle
+        int halign = 2; // 1: left, 2: center, 3: right
+        int in_code = 0;
+        double font_factor = (0.000666 * (s->h - 480) + 1) * s->font_size_factor;
+
+        av_freep(&area->ass);
+        av_bprint_clear(&s->buffer);
+
+        ri = TessBaseAPIGetIterator(s->tapi);
+
+        // Horizontal Alignment
+        if (s->w && s->recognize & RFLAGS_HALIGN) {
+            int left_margin = area->x;
+            int right_margin = s->w - area->x - area->w;
+            double relative_diff = ((double)left_margin - right_margin) / s->w;
+
+            if (FFABS(relative_diff) < 0.1)
+                halign = 2; // center
+            else if (relative_diff > 0)
+                halign = 3; // right
+            else
+                halign = 1; // left
+        }
+
+        // Vertical Alignment
+        if (s->h && frame->height && s->recognize & RFLAGS_VALIGN) {
+            int left = 0, top = 0, right = 0, bottom = 0;
+
+            TessPageIteratorBoundingBox((TessPageIterator*)ri, RIL_TEXTLINE, &left, &top, &right, &bottom);
+            av_log(s, AV_LOG_DEBUG, "RIL_TEXTLINE - TOP: %d  BOTTOM: %d HEIGHT: %d\n", top, bottom, bottom - top);
+
+            TessPageIteratorBoundingBox((TessPageIterator*)ri, RIL_BLOCK, &left, &top, &right, &bottom);
+
+            const int vertical_pos = area->y + area->h / 2;
+            if (vertical_pos < s->h / 3) {
+                *margin_v = area->y + top;
+                valign = 4;
+            }
+            else if (vertical_pos < s->h / 3 * 2) {
+                *margin_v = 0;
+                valign = 8;
+            } else {
+                *margin_v = frame->height - area->y - area->h;
+                valign = 0;
+            }
+        }
+
+        if (*margin_v < 0)
+            *margin_v = 0;
+
+        // Set alignment when not default (2)
+        if ((valign | halign) != 2)
+            in_code = print_code(&s->buffer, in_code, "\\a%d", valign | halign);
+
+        do {
+            int is_bold, is_italic, is_underlined, is_monospace, is_serif, is_smallcaps, pointsize, font_id;
+            char* word;
+            const char *font_name = TessResultIteratorWordFontAttributes(ri, &is_bold, &is_italic, &is_underlined, &is_monospace, &is_serif, &is_smallcaps, &pointsize, &font_id);
+            uint32_t text_color = 0, bg_color = 0, outline_color = 0;
+
+            if (cur_is_underlined && !is_underlined && s->recognize & RFLAGS_FUNDERLINE)
+                in_code = print_code(&s->buffer, in_code, "\\u0");
+
+            if (cur_is_bold && !is_bold && s->recognize & RFLAGS_FBOLD)
+                in_code = print_code(&s->buffer, in_code, "\\b0");
+
+            if (cur_is_italic && !is_italic && s->recognize & RFLAGS_FITALIC)
+                in_code = print_code(&s->buffer, in_code, "\\i0");
+
+
+            if (TessPageIteratorIsAtBeginningOf((TessPageIterator*)ri, RIL_TEXTLINE) && !TessPageIteratorIsAtBeginningOf((TessPageIterator*)ri, RIL_BLOCK)) {
+                in_code = end_code(&s->buffer, in_code);
+                av_bprintf(&s->buffer, "\\N");
+            }
+
+            if (get_word_colors(ctx, ri, area, original_area, bg_color_index, text_color_index, outline_color_index, &bg_color, &text_color, &outline_color) == 0) {
+
+                if (text_color > 0 && cur_text_color != text_color && s->recognize & RFLAGS_COLOR) {
+                    const uint8_t* tval = (uint8_t*)&text_color;
+                    const int color = (int)tval[R] << 16 | (int)tval[G] << 8 | tval[B];
+
+                    in_code = print_code(&s->buffer, in_code, "\\1c&H%0.6X&", color);
+                    if (tval[A] != 255)
+                        in_code = print_code(&s->buffer, in_code, "\\1a&H%0.2X&", 255 - tval[A]);
+                }
+
+                if (outline_color > 0 && cur_outline_color != outline_color && s->recognize & RFLAGS_OUTLINECOLOR) {
+                    const uint8_t* tval = (uint8_t*)&outline_color;
+                    const int color = (int)tval[R] << 16 | (int)tval[G] << 8 | tval[B];
+
+                    in_code = print_code(&s->buffer, in_code, "\\3c&H%0.6X&\\bord2", color);
+                    in_code = print_code(&s->buffer, in_code, "\\3a&H%0.2X&", FFMIN(255 - tval[A], 30));
+                }
+
+                cur_text_color = text_color;
+                cur_outline_color = outline_color;
+            }
+
+            if (font_name && strlen(font_name) && s->recognize & RFLAGS_FONT) {
+                if (!cur_font_name || !strlen(cur_font_name) || strcmp(cur_font_name, font_name) != 0) {
+                    char *sanitized_font_name = av_strireplace(font_name, "_", " ");
+                    if (!sanitized_font_name) {
+                        ret = AVERROR(ENOMEM);
+                        goto exit;
+                    }
+
+                    in_code = print_code(&s->buffer, in_code, "\\fn%s", sanitized_font_name);
+                    av_freep(&sanitized_font_name);
+
+                    if (cur_font_name)
+                        av_freep(&cur_font_name);
+                    cur_font_name = av_strdup(font_name);
+                    if (!cur_font_name) {
+                        ret = AVERROR(ENOMEM);
+                        goto exit;
+                    }
+                }
+            }
+
+            if (pointsize > 0 && pointsize != cur_pointsize && s->recognize & RFLAGS_FONTSIZE) {
+                float change_factor = (float)(FFABS(pointsize - cur_pointsize)) / FFMAX(pointsize, cur_pointsize);
+
+                // Avoid small changes due to recognition variance
+                if (change_factor > 0.12f) {
+                    av_log(s, AV_LOG_DEBUG, "pointsize - pointsize: %d\n", pointsize);
+                    in_code = print_code(&s->buffer, in_code, "\\fs%d", (int)(pointsize * font_factor));
+                    cur_pointsize = pointsize;
+                }
+            }
+
+            if (is_italic && !cur_is_italic && s->recognize & RFLAGS_FITALIC)
+                in_code = print_code(&s->buffer, in_code, "\\i1");
+
+            if (is_bold && !cur_is_bold && s->recognize & RFLAGS_FBOLD)
+                in_code = print_code(&s->buffer, in_code, "\\b1");
+
+            if (is_underlined && !cur_is_underlined && s->recognize & RFLAGS_FUNDERLINE)
+                in_code = print_code(&s->buffer, in_code, "\\u1");
+
+            in_code = end_code(&s->buffer, in_code);
+
+            cur_is_underlined = is_underlined;
+            cur_is_bold = is_bold;
+            cur_is_italic = is_italic;
+
+            if (!TessPageIteratorIsAtBeginningOf((TessPageIterator*)ri, RIL_TEXTLINE))
+                av_bprint_chars(&s->buffer, ' ', 1);
+
+            word = TessResultIteratorGetUTF8Text(ri, level);
+            av_bprint_append_data(&s->buffer, word, strlen(word));
+            TessDeleteText(word);
+
+        } while (TessResultIteratorNext(ri, level));
+
+        if (!av_bprint_is_complete(&s->buffer))
+            ret = AVERROR(ENOMEM);
+        else {
+            av_log(ctx, AV_LOG_VERBOSE, "ASS Result: %s\n", s->buffer.str);
+            area->ass = av_strdup(s->buffer.str);
+        }
+
+        TessResultIteratorDelete(ri);
+        av_freep(&cur_font_name);
+    }
+
+exit:
+    free_subtitle_area(original_area);
+    av_freep(&gs_img);
+    av_buffer_unref(&area->buf[0]);
+    area->type = AV_SUBTITLE_FMT_ASS;
+
+    return ret;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFilterContext *ctx = inlink->dst;
+    SubOcrContext *s = ctx->priv;
+    AVFilterLink *outlink = inlink->dst->outputs[0];
+    int ret, frame_sent = 0;
+
+    if (s->pending_frame && !frame->repeat_sub) {
+        const int64_t pts_diff = frame->subtitle_timing.start_pts - s->pending_frame->subtitle_timing.start_pts;
+
+        if (pts_diff == 0) {
+            // This is just a repetition of the previous frame, ignore it
+            av_frame_free(&frame);
+            return 0;
+        }
+
+        s->pending_frame->subtitle_timing.duration = pts_diff;
+
+        if ((ret = av_buffer_replace(&s->pending_frame->subtitle_header, s->subtitle_header)) < 0)
+            return ret;
+
+        ret = ff_filter_frame(outlink, s->pending_frame);
+        s->pending_frame = NULL;
+        if (ret < 0)
+            return  ret;
+
+        frame_sent = 1;
+        s->last_subtitle_pts = frame->subtitle_timing.start_pts;
+    }
+
+    if (frame->repeat_sub) {
+        // Ignore repeated frame
+        av_frame_free(&frame);
+        return 0;
+    }
+
+    s->last_subtitle_pts = frame->subtitle_timing.start_pts;
+
+    ret = av_frame_make_writable(frame);
+
+    if (ret < 0) {
+        av_frame_free(&frame);
+        return ret;
+    }
+
+    frame->format = AV_SUBTITLE_FMT_ASS;
+
+    av_log(ctx, AV_LOG_VERBOSE, "filter_frame sub_pts: %"PRIu64", duration: %"PRIu64", num_areas: %d\n",
+        frame->subtitle_timing.start_pts, frame->subtitle_timing.duration, frame->num_subtitle_areas);
+
+    if (frame->num_subtitle_areas > 1 &&
+        frame->subtitle_areas[0]->y > frame->subtitle_areas[frame->num_subtitle_areas - 1]->y) {
+
+        for (unsigned i = 0; i < frame->num_subtitle_areas / 2; i++)
+            FFSWAP(AVSubtitleArea*, frame->subtitle_areas[i], frame->subtitle_areas[frame->num_subtitle_areas - i - 1]);
+    }
+
+    for (int i = 0; i < frame->num_subtitle_areas; i++) {
+        AVSubtitleArea *area = frame->subtitle_areas[i];
+        int margin_v = 0;
+
+        ret = convert_area(ctx, area, frame, i, &margin_v);
+        if (ret < 0)
+            return ret;
+
+        if (area->ass && area->ass[0] != '\0') {
+
+            const int layer = s->recognize ? i : 0;
+            char *tmp = area->ass;
+            area->ass = avpriv_ass_get_dialog_ex(s->readorder_counter++, layer, "Default", NULL, 0, 0, margin_v, NULL, tmp);
+            av_free(tmp);
+        }
+    }
+
+    // When decoders can't determine the end time, they are setting it either to UINT32_NAX
+    // or 30s (dvbsub).
+    if (s->delay_when_no_duration && frame->subtitle_timing.duration >= ms_to_avtb(29000)) {
+        // Can't send it without end time, wait for the next frame to determine the end_display time
+        s->pending_frame = frame;
+
+        if (frame_sent)
+            return 0;
+
+        // To keep all going, send an empty frame instead
+        frame = ff_get_subtitles_buffer(outlink, AV_SUBTITLE_FMT_ASS);
+        if (!frame)
+            return AVERROR(ENOMEM);
+
+        av_frame_copy_props(frame, s->pending_frame);
+        frame->subtitle_timing.start_pts = 0;
+        frame->subtitle_timing.duration = 1;
+        frame->repeat_sub = 1;
+    }
+
+    if ((ret = av_buffer_replace(&frame->subtitle_header, s->subtitle_header)) < 0)
+        return ret;
+
+    return ff_filter_frame(outlink, frame);
+}
+
+#define OFFSET(x) offsetof(SubOcrContext, x)
+#define FLAGS (AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption graphicsub2text_options[] = {
+    { "delay_when_no_duration", "delay output when duration is unknown", OFFSET(delay_when_no_duration), AV_OPT_TYPE_BOOL,   { .i64 = 0 },                         0,                  1,       FLAGS, NULL },
+    { "dump_bitmaps",           "save processed bitmaps as .ppm",        OFFSET(dump_bitmaps),           AV_OPT_TYPE_BOOL,   { .i64 = 0 },                         0,                  1,       FLAGS, NULL },
+    { "font_size_factor",       "font size adjustment factor",           OFFSET(font_size_factor),       AV_OPT_TYPE_DOUBLE, { .dbl = 1.0 },                       0.2,                5,       FLAGS, NULL },
+    { "language",               "ocr language",                          OFFSET(language),               AV_OPT_TYPE_STRING, { .str = "eng" },                     0,                  0,       FLAGS, NULL },
+    { "ocr_mode",               "set ocr mode",                          OFFSET(ocr_mode),               AV_OPT_TYPE_INT,    { .i64=OEM_TESSERACT_ONLY },          OEM_TESSERACT_ONLY, 2,       FLAGS, "ocr_mode" },
+    {   "tesseract",            "classic tesseract ocr",                 0,                              AV_OPT_TYPE_CONST,  { .i64=OEM_TESSERACT_ONLY },          0,                  0,       FLAGS, "ocr_mode" },
+    {   "lstm",                 "lstm (ML based)",                       0,                              AV_OPT_TYPE_CONST,  { .i64=OEM_LSTM_ONLY},                0,                  0,       FLAGS, "ocr_mode" },
+    {   "both",                 "use both models combined",              0,                              AV_OPT_TYPE_CONST,  { .i64=OEM_TESSERACT_LSTM_COMBINED }, 0,                  0,       FLAGS, "ocr_mode" },
+    { "preprocess_images",      "reduce colors, remove outlines",        OFFSET(preprocess_images),      AV_OPT_TYPE_BOOL,   { .i64 = 1 },                         0,                  1,       FLAGS, NULL },
+    { "recognize",              "detect fonts, styles and colors",       OFFSET(recognize),              AV_OPT_TYPE_FLAGS,  { .i64 = RFLAGS_ALL},                  0,                  INT_MAX, FLAGS, "reco_flags" },
+        { "none",         "no format detection",  0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_NONE         }, 0, 0, FLAGS, "reco_flags" },
+        { "halign",       "horizontal alignment", 0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_HALIGN       }, 0, 0, FLAGS, "reco_flags" },
+        { "valign",       "vertical alignment",   0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_VALIGN       }, 0, 0, FLAGS, "reco_flags" },
+        { "bold",         "font bold",            0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_FBOLD        }, 0, 0, FLAGS, "reco_flags" },
+        { "italic",       "font italic",          0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_FITALIC      }, 0, 0, FLAGS, "reco_flags" },
+        { "underline",    "font underline",       0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_FUNDERLINE   }, 0, 0, FLAGS, "reco_flags" },
+        { "font",         "font name",            0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_FONT         }, 0, 0, FLAGS, "reco_flags" },
+        { "fontsize",     "font size",            0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_FONTSIZE     }, 0, 0, FLAGS, "reco_flags" },
+        { "color",        "font color",           0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_COLOR        }, 0, 0, FLAGS, "reco_flags" },
+        { "outlinecolor", "outline color",        0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_OUTLINECOLOR }, 0, 0, FLAGS, "reco_flags" },
+    { "tessdata_path",          "path to tesseract data",                OFFSET(tessdata_path),          AV_OPT_TYPE_STRING, { .str = NULL },                      0,                  0,       FLAGS, NULL },
+    { NULL },
+};
+
+AVFILTER_DEFINE_CLASS(graphicsub2text);
+
+static const AVFilterPad inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .filter_frame = filter_frame,
+        .config_props = config_input,
+    },
+};
+
+static const AVFilterPad outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_SUBTITLE,
+        .config_props  = config_output,
+    },
+};
+
+const AVFilter ff_sf_graphicsub2text = {
+    .name          = "graphicsub2text",
+    .description   = NULL_IF_CONFIG_SMALL("Convert graphical subtitles to text subtitles via OCR"),
+    .init          = init,
+    .uninit        = uninit,
+    .priv_size     = sizeof(SubOcrContext),
+    .priv_class    = &graphicsub2text_class,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_QUERY_FUNC(query_formats),
+};
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v5 19/25] avfilter/subscale: Add filter for scaling and/or re-arranging graphical subtitles
  2022-06-25  9:57       ` [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022 ffmpegagent
                           ` (17 preceding siblings ...)
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 18/25] avfilter/graphicsub2text: Add new graphicsub2text filter (OCR) softworkz
@ 2022-06-25  9:57         ` softworkz
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 20/25] avfilter/subfeed: add subtitle feed filter softworkz
                           ` (8 subsequent siblings)
  27 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-25  9:57 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 configure                 |   1 +
 doc/filters.texi          | 164 +++++++
 libavfilter/Makefile      |   1 +
 libavfilter/allfilters.c  |   1 +
 libavfilter/sf_subscale.c | 884 ++++++++++++++++++++++++++++++++++++++
 5 files changed, 1051 insertions(+)
 create mode 100644 libavfilter/sf_subscale.c

diff --git a/configure b/configure
index 462d473c5f..89db322fb8 100755
--- a/configure
+++ b/configure
@@ -3730,6 +3730,7 @@ sr_filter_deps="avformat swscale"
 sr_filter_select="dnn"
 stereo3d_filter_deps="gpl"
 splitcc_filter_deps="avcodec"
+subscale_filter_deps="swscale avcodec"
 subtitles_filter_deps="avformat avcodec libass"
 super2xsai_filter_deps="gpl"
 pixfmts_super2xsai_test_deps="super2xsai_filter"
diff --git a/doc/filters.texi b/doc/filters.texi
index 4bbec62c02..00fa9bbac5 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -27580,6 +27580,170 @@ Set the rendering margin in pixels.
 For rendering, alway use the latest event only, which is covering the given point in time.
 @end table
 
+@section subscale
+
+Provides high-quality scaling and rearranging functionality for graphical subtitles.
+
+The subscale filter provides multiple approaches for manipulating
+the size and position of graphical subtitle rectangles wich can
+be combined or used separately.
+Scaling is performed by converting the palettized subtitle bitmaps
+to RGBA and re-quantization to palette colors afterwards via elbg algorithm.
+
+The two major operations are 'scale' and 're-arrange' with the
+latter being separated as 'arrange_h' and 'arrange_v'.
+
+
+Inputs:
+- 0: Subtitles [bitmap]
+
+Outputs:
+- 0: Subtitles [bitmap]
+
+It accepts the following parameters:
+
+@table @option
+
+@item w, width
+Set the width of the output.
+Width and height in case of graphical subtitles are just indicating
+a virtual size for which the output (consisting of 0-n bitmap rectangles)
+is intended to be displayed on.
+
+@item h, height
+Set the height of the output.
+
+@item margin_h
+Sets a horizontal margin to be preserverved when using any
+of the arrange modes.
+
+@item margin_v
+Sets a vertical margin to be preserverved when using any
+of the arrange modes.
+
+@item force_original_aspect_ratio
+Enable decreasing or increasing output video width or height if necessary to
+keep the original aspect ratio. Possible values:
+
+@table @samp
+@item disable
+Scale the video as specified and disable this feature.
+
+@item decrease
+The output video dimensions will automatically be decreased if needed.
+
+@item increase
+The output video dimensions will automatically be increased if needed.
+
+@end table
+
+
+@item scale_mode
+Specifies how subtitle bitmaps should be scaled.
+The scale factor is determined by the the factor between input
+and output size.
+
+@table @samp
+@item none
+Do not apply any common scaling.
+
+@item uniform
+Uniformly scale all subtitle bitmaps including their positions.
+
+@item uniform_no_reposition
+Uniformly scale all subtitle bitmaps without changing positions.
+
+@end table
+
+
+@item arrange_h
+Specifies how subtitle bitmaps should be arranged horizontally.
+
+@item arrange_v
+Specifies how subtitle bitmaps should be arranged vertically.
+
+
+@table @samp
+@item none
+Do not rearrange subtitle bitmaps.
+
+@item margin_no_scale
+Move subtitle bitmaps to be positioned inside the specified
+margin (margin_h or margin_v) when possible and without scaling.
+
+@item margin_and_scale
+Move subtitle bitmaps to be positioned inside the specified
+margin (margin_h or margin_v) and scale in case it doesn't fit.
+
+@item snapalign_no_scale
+Categorize subtitle bitmap positions as one of left/center/right
+or top/bottom/middle based on original positioning and apply
+these alignments for the target positioning.
+No scaling will be applied.
+
+@item snapalign_and_scale
+Categorize subtitle bitmap positions as one of left/center/right
+or top/bottom/middle based on original positioning and apply
+these alignments for the target positioning.
+Bitmaps that do not fit inside the margins borders are
+scaled to fit.
+@end table
+
+@item eval
+Set evaluation mode for the expressions (@option{width}, @option{height}).
+
+It accepts the following values:
+@table @samp
+@item init
+Evaluate expressions only once during the filter initialization.
+
+@item frame
+Evaluate expressions for each incoming frame. This is way slower than the
+@samp{init} mode since it requires all the scalers to be re-computed, but it
+allows advanced dynamic expressions.
+@end table
+
+Default value is @samp{init}.
+
+
+@item num_colors
+Set the number of palette colors for output images.
+Choose the maximum (256) when further processing is done (e.g.
+overlaying on a video).
+When subtitles will be encoded as bitmap subtitles (e.g. dvbsub),
+a smaller number of palette colors (e.g. 4-16) might need to be used, depending
+on the target format and codec.
+
+@item bitmap_width_align
+@item bitmap_height_align
+Make sure that subtitle bitmap sizes are a multiple of this
+value. Default is 2.
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Uniformly scale down video and bitmap subtitles and encode
+subtitles as dvbsub.
+@example
+ffmpeg -y -loglevel verbose -ss 32 -i "https://streams.videolan.org/samples/sub/largeres_vobsub.mkv" -filter_complex "[0:0]scale=480x270[vid1];[0:10]subscale=w=480:h=270:num_colors=16[sub1]" -map [vid1] -map [sub1] -c:s dvbsub -c:v libx265 output.ts
+@end example
+@item
+Squeeze video vertically and arrange subtitle bitmaps
+inside the video area without scaling, then overlay
+subtitles onto the video.
+@example
+ffmpeg -y -loglevel verbose -ss 32 -i "https://streams.videolan.org/samples/sub/largeres_vobsub.mkv" -filter_complex "[0:0]scale=1920x400,setsar=1[vid1];[0:10]subscale=w=1920:h=400:scale_mode=none:margin_h=50:arrange_h=margin_no_scale:arrange_v=margin_no_scale:margin_v=50:num_colors=256[sub1];[vid1][sub1]overlaygraphicsubs" -c:v libx265 output.ts
+@end example
+@item
+Scale both, a video and its embedded VOB subs, then encode them as separate streams into an MKV.
+@example
+ffmpeg -y -y -loglevel verbose -i "https://streams.videolan.org/samples/sub/largeres_vobsub.mkv" -filter_complex "[0:0]scale=720x576[vid1];[0:7]subscale=w=720:h=576:num_colors=16[sub1]" -map [vid1] -map [sub1] -c:s dvdsub out.mkv
+@end example
+@end itemize
+
 @c man end SUBTITLE FILTERS
 
 @chapter Multimedia Filters
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 6e6485c99a..0f43937205 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -583,6 +583,7 @@ OBJS-$(CONFIG_CENSOR_FILTER)                 += sf_textmod.o
 OBJS-$(CONFIG_SHOW_SPEAKER_FILTER)           += sf_textmod.o
 OBJS-$(CONFIG_SPLITCC_FILTER)                += sf_splitcc.o
 OBJS-$(CONFIG_STRIPSTYLES_FILTER)            += sf_stripstyles.o
+OBJS-$(CONFIG_SUBSCALE_FILTER)               += sf_subscale.o
 OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
 
 # multimedia filters
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index cbd93c4236..6792665730 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -575,6 +575,7 @@ extern const AVFilter ff_sf_graphicsub2text;
 extern const AVFilter ff_sf_showspeaker;
 extern const AVFilter ff_sf_splitcc;
 extern const AVFilter ff_sf_stripstyles;
+extern const AVFilter ff_sf_subscale;
 extern const AVFilter ff_sf_textmod;
 
 /* those filters are part of public or internal API,
diff --git a/libavfilter/sf_subscale.c b/libavfilter/sf_subscale.c
new file mode 100644
index 0000000000..04f0f16c27
--- /dev/null
+++ b/libavfilter/sf_subscale.c
@@ -0,0 +1,884 @@
+/*
+ * 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
+ * scale graphical subtitles filter
+ */
+
+#include <string.h>
+
+#include "drawutils.h"
+#include "internal.h"
+#include "scale_eval.h"
+#include "libavutil/eval.h"
+#include "libavutil/opt.h"
+#include "libswscale/swscale.h"
+
+#include "libavcodec/elbg.h"
+
+static const char *const var_names[] = {
+    "in_w",   "iw",
+    "in_h",   "ih",
+    "out_w",  "ow",
+    "out_h",  "oh",
+    "a",
+    "sar",
+    "dar",
+    "margin_h",
+    "margin_v",
+    NULL
+};
+
+enum var_name {
+    VAR_IN_W,   VAR_IW,
+    VAR_IN_H,   VAR_IH,
+    VAR_OUT_W,  VAR_OW,
+    VAR_OUT_H,  VAR_OH,
+    VAR_A,
+    VAR_SAR,
+    VAR_DAR,
+    VARS_B_H,
+    VARS_B_V,
+    VARS_NB
+};
+
+enum EvalMode {
+    EVAL_MODE_INIT,
+    EVAL_MODE_FRAME,
+    EVAL_MODE_NB
+};
+
+enum SubScaleMode {
+    SSM_NONE,
+    SSM_UNIFORM,
+    SSM_UNIFORM_NO_REPOSITION,
+};
+
+enum SubArrangeMode {
+    SAM_NONE,
+    SAM_ENSUREMARGIN_NO_SCALE,
+    SAM_ENSUREMARGIN_AND_SCALE,
+    SAM_SNAPALIGNMENT_NO_SCALE,
+    SAM_SNAPALIGNMENT_AND_SCALE,
+};
+
+typedef struct SubScaleContext {
+    const AVClass *class;
+    struct SwsContext *sws;
+    AVDictionary *opts;
+
+    int w, h;
+
+    char *w_expr;               ///< width  expression string
+    char *h_expr;               ///< height expression string
+    AVExpr *w_pexpr;
+    AVExpr *h_pexpr;
+    double var_values[VARS_NB];
+
+    int force_original_aspect_ratio;
+    int eval_mode;               ///< expression evaluation mode
+
+    int use_caching;
+
+    // Scale Options
+    enum SubScaleMode scale_mode;
+
+    // Arrange Options
+    enum SubArrangeMode arrange_mode_h;
+    enum SubArrangeMode arrange_mode_v;
+    int margin_h;
+    int margin_v;
+    char *margin_h_expr;
+    char *margin_v_expr;
+    AVExpr *margin_h_pexpr;
+    AVExpr *margin_v_pexpr;
+
+    // Bitmap Options
+    int num_output_colors;
+    int bitmap_width_align;
+    int bitmap_height_align;
+
+    // Color Quantization Fields
+    struct ELBGContext *ctx;
+    AVLFG lfg;
+    int *codeword;
+    int *codeword_closest_codebook_idxs;
+    int *codebook;
+    int r_idx, g_idx, b_idx, a_idx;
+    AVFrame *cache_frame;
+} SubScaleContext;
+
+
+static int config_output(AVFilterLink *outlink);
+
+static int check_exprs(AVFilterContext *ctx)
+{
+    const SubScaleContext *s = ctx->priv;
+    unsigned vars_w[VARS_NB] = { 0 }, vars_h[VARS_NB] = { 0 };
+
+    if (!s->w_pexpr && !s->h_pexpr)
+        return AVERROR(EINVAL);
+
+    if (s->w_pexpr)
+        av_expr_count_vars(s->w_pexpr, vars_w, VARS_NB);
+    if (s->h_pexpr)
+        av_expr_count_vars(s->h_pexpr, vars_h, VARS_NB);
+
+    if (vars_w[VAR_OUT_W] || vars_w[VAR_OW]) {
+        av_log(ctx, AV_LOG_ERROR, "Width expression cannot be self-referencing: '%s'.\n", s->w_expr);
+        return AVERROR(EINVAL);
+    }
+
+    if (vars_h[VAR_OUT_H] || vars_h[VAR_OH]) {
+        av_log(ctx, AV_LOG_ERROR, "Height expression cannot be self-referencing: '%s'.\n", s->h_expr);
+        return AVERROR(EINVAL);
+    }
+
+    if ((vars_w[VAR_OUT_H] || vars_w[VAR_OH]) &&
+        (vars_h[VAR_OUT_W] || vars_h[VAR_OW])) {
+        av_log(ctx, AV_LOG_WARNING, "Circular references detected for width '%s' and height '%s' - possibly invalid.\n", s->w_expr, s->h_expr);
+    }
+
+    if (s->margin_h_pexpr)
+        av_expr_count_vars(s->margin_h_pexpr, vars_w, VARS_NB);
+    if (s->margin_v_pexpr)
+        av_expr_count_vars(s->margin_v_pexpr, vars_h, VARS_NB);
+
+    return 0;
+}
+
+static int scale_parse_expr(AVFilterContext *ctx, char *str_expr, AVExpr **pexpr_ptr, const char *var, const char *args)
+{
+    SubScaleContext *s = ctx->priv;
+    int ret, is_inited = 0;
+    char *old_str_expr = NULL;
+    AVExpr *old_pexpr = NULL;
+
+    if (str_expr) {
+        old_str_expr = av_strdup(str_expr);
+        if (!old_str_expr)
+            return AVERROR(ENOMEM);
+        av_opt_set(s, var, args, 0);
+    }
+
+    if (*pexpr_ptr) {
+        old_pexpr = *pexpr_ptr;
+        *pexpr_ptr = NULL;
+        is_inited = 1;
+    }
+
+    ret = av_expr_parse(pexpr_ptr, args, var_names,
+                        NULL, NULL, NULL, NULL, 0, ctx);
+    if (ret < 0) {
+        av_log(ctx, AV_LOG_ERROR, "Cannot parse expression for %s: '%s'\n", var, args);
+        goto revert;
+    }
+
+    ret = check_exprs(ctx);
+    if (ret < 0)
+        goto revert;
+
+    if (is_inited && (ret = config_output(ctx->outputs[0])) < 0)
+        goto revert;
+
+    av_expr_free(old_pexpr);
+    av_freep(&old_str_expr);
+
+    return 0;
+
+revert:
+    av_expr_free(*pexpr_ptr);
+    *pexpr_ptr = NULL;
+    if (old_str_expr) {
+        av_opt_set(s, var, old_str_expr, 0);
+        av_free(old_str_expr);
+    }
+    if (old_pexpr)
+        *pexpr_ptr = old_pexpr;
+
+    return ret;
+}
+
+static av_cold int init_dict(AVFilterContext *ctx, AVDictionary **opts)
+{
+    SubScaleContext *s = ctx->priv;
+    uint8_t rgba_map[4];
+    int ret;
+
+    if (!s->w_expr)
+        av_opt_set(s, "w", "iw", 0);
+    if (!s->h_expr)
+        av_opt_set(s, "h", "ih", 0);
+
+    ret = scale_parse_expr(ctx, NULL, &s->w_pexpr, "width", s->w_expr);
+    if (ret < 0)
+        return ret;
+
+    ret = scale_parse_expr(ctx, NULL, &s->h_pexpr, "height", s->h_expr);
+    if (ret < 0)
+        return ret;
+
+    av_log(ctx, AV_LOG_VERBOSE, "w:%s h:%s\n",
+           s->w_expr, s->h_expr);
+
+    if (!s->margin_h_expr)
+        av_opt_set(s, "margin_h", "0", 0);
+    if (!s->margin_v_expr)
+        av_opt_set(s, "margin_v", "0", 0);
+
+    ret = scale_parse_expr(ctx, NULL, &s->margin_h_pexpr, "margin_h", s->margin_h_expr);
+    if (ret < 0)
+        return ret;
+
+    ret = scale_parse_expr(ctx, NULL, &s->margin_v_pexpr, "margin_v", s->margin_v_expr);
+    if (ret < 0)
+        return ret;
+
+    s->opts = *opts;
+    *opts = NULL;
+
+    ff_fill_rgba_map(&rgba_map[0], AV_PIX_FMT_RGB32);
+
+    s->r_idx = rgba_map[0]; // R
+    s->g_idx = rgba_map[1]; // G
+    s->b_idx = rgba_map[2]; // B
+    s->a_idx = rgba_map[3]; // A
+
+    av_lfg_init(&s->lfg, 123456789);
+
+
+    return 0;
+}
+
+static av_cold void uninit(AVFilterContext *ctx)
+{
+    SubScaleContext *s = ctx->priv;
+
+    av_frame_free(&s->cache_frame);
+
+    av_expr_free(s->w_pexpr);
+    av_expr_free(s->h_pexpr);
+    s->w_pexpr = s->h_pexpr = NULL;
+
+    av_expr_free(s->margin_h_pexpr);
+    av_expr_free(s->margin_v_pexpr);
+    s->margin_h_pexpr = s->margin_v_pexpr = NULL;
+
+    sws_freeContext(s->sws);
+    s->sws = NULL;
+    av_dict_free(&s->opts);
+
+    avpriv_elbg_free(&s->ctx);
+
+    av_freep(&s->codebook);
+    av_freep(&s->codeword);
+    av_freep(&s->codeword_closest_codebook_idxs);
+}
+
+static int config_input(AVFilterLink *inlink)
+{
+    ////const AVFilterContext *ctx = inlink->dst;
+    ////SubScaleContext *s = ctx->priv;
+
+    ////if (s->w <= 0 || s->h <= 0) {
+    ////    s->w = inlink->w;
+    ////    s->h = inlink->h;
+    ////}
+    return 0;
+}
+
+static int scale_eval_dimensions(AVFilterContext *ctx)
+{
+    SubScaleContext *s = ctx->priv;
+    const AVFilterLink *inlink = ctx->inputs[0];
+    char *expr;
+    int eval_w, eval_h, margin_h, margin_v;
+    int ret;
+    double res;
+
+    s->var_values[VAR_IN_W]  = s->var_values[VAR_IW] = inlink->w;
+    s->var_values[VAR_IN_H]  = s->var_values[VAR_IH] = inlink->h;
+    s->var_values[VAR_OUT_W] = s->var_values[VAR_OW] = NAN;
+    s->var_values[VAR_OUT_H] = s->var_values[VAR_OH] = NAN;
+    s->var_values[VARS_B_H]  = s->var_values[VARS_B_V] = 0;
+    s->var_values[VAR_A]     = (double) inlink->w / inlink->h;
+    s->var_values[VAR_SAR]   = inlink->sample_aspect_ratio.num ?
+        (double) inlink->sample_aspect_ratio.num / inlink->sample_aspect_ratio.den : 1;
+    s->var_values[VAR_DAR]   = s->var_values[VAR_A] * s->var_values[VAR_SAR];
+
+    res = av_expr_eval(s->w_pexpr, s->var_values, NULL);
+    s->var_values[VAR_OUT_W] = s->var_values[VAR_OW] = (int) res == 0 ? inlink->w : (int) res;
+
+    res = av_expr_eval(s->h_pexpr, s->var_values, NULL);
+    if (isnan(res)) {
+        expr = s->h_expr;
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+    eval_h = s->var_values[VAR_OUT_H] = s->var_values[VAR_OH] = (int) res == 0 ? inlink->h : (int) res;
+
+    res = av_expr_eval(s->w_pexpr, s->var_values, NULL);
+    if (isnan(res)) {
+        expr = s->w_expr;
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+    eval_w = s->var_values[VAR_OUT_W] = s->var_values[VAR_OW] = (int) res == 0 ? inlink->w : (int) res;
+
+    s->w = eval_w;
+    s->h = eval_h;
+
+    res = av_expr_eval(s->margin_h_pexpr, s->var_values, NULL);
+    s->var_values[VARS_B_H] = (int)res;
+
+    res = av_expr_eval(s->margin_v_pexpr, s->var_values, NULL);
+    if (isnan(res)) {
+        expr = s->margin_v_expr;
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+    margin_v = s->var_values[VARS_B_V] = (int)res;
+
+    res = av_expr_eval(s->margin_h_pexpr, s->var_values, NULL);
+    if (isnan(res)) {
+        expr = s->margin_h_expr;
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+    margin_h = s->var_values[VARS_B_H] = (int)res;
+
+    s->margin_h = margin_h;
+    s->margin_v = margin_v;
+
+    return 0;
+
+fail:
+    av_log(ctx, AV_LOG_ERROR,
+           "Error when evaluating the expression '%s'.\n", expr);
+    return ret;
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    AVFilterLink *inlink  = outlink->src->inputs[0];
+    SubScaleContext *s = ctx->priv;
+    int ret;
+
+    outlink->format = AV_SUBTITLE_FMT_BITMAP;
+    outlink->time_base = ctx->inputs[0]->time_base;
+    outlink->frame_rate = ctx->inputs[0]->frame_rate;
+
+    if ((ret = scale_eval_dimensions(ctx)) < 0)
+        goto fail;
+
+    ff_scale_adjust_dimensions(inlink, &s->w, &s->h,
+                               s->force_original_aspect_ratio, 2);
+
+    if (s->w > INT_MAX ||
+        s->h > INT_MAX ||
+        (s->h * inlink->w) > INT_MAX ||
+        (s->w * inlink->h) > INT_MAX)
+        av_log(ctx, AV_LOG_ERROR, "Rescaled value for width or height is too big.\n");
+
+    outlink->w = s->w;
+    outlink->h = s->h;
+
+    if (s->sws)
+        sws_freeContext(s->sws);
+
+    s->sws = sws_alloc_context();
+    if (!s->sws)
+        return AVERROR(ENOMEM);
+
+    av_opt_set_pixel_fmt(s->sws, "src_format", AV_PIX_FMT_PAL8, 0);
+    av_opt_set_int(s->sws, "dst_format", AV_PIX_FMT_RGB32, 0);
+    av_opt_set_int(s->sws, "threads", ff_filter_get_nb_threads(ctx), 0);
+
+    if (s->opts) {
+        const AVDictionaryEntry *e = NULL;
+        while ((e = av_dict_get(s->opts, "", e, AV_DICT_IGNORE_SUFFIX))) {
+            if ((ret = av_opt_set(s->sws, e->key, e->value, 0)) < 0)
+                return ret;
+        }
+    }
+
+    if ((ret = sws_init_context(s->sws, NULL, NULL)) < 0)
+        return ret;
+
+    if (inlink->sample_aspect_ratio.num){
+        outlink->sample_aspect_ratio = av_mul_q((AVRational){outlink->h * inlink->w, outlink->w * inlink->h}, inlink->sample_aspect_ratio);
+    } else
+        outlink->sample_aspect_ratio = inlink->sample_aspect_ratio;
+
+    av_log(ctx, AV_LOG_VERBOSE, "Output size set to %dx%d.\n", outlink->w, outlink->h);
+
+    return 0;
+fail:
+    return ret;
+}
+
+static int palettize_image(SubScaleContext *const s, const int w, const int h, const int src_linesize, uint8_t *src_data,
+                          int dst_linesize, uint8_t *dst_data, uint32_t *dst_pal)
+{
+    int k, ret;
+    const int codeword_length = w * h;
+
+    /* Re-Initialize */
+    s->codeword = av_realloc_f(s->codeword, codeword_length, 4 * sizeof(*s->codeword));
+    if (!s->codeword)
+        return AVERROR(ENOMEM);
+
+    s->codeword_closest_codebook_idxs =
+        av_realloc_f(s->codeword_closest_codebook_idxs, codeword_length,
+                     sizeof(*s->codeword_closest_codebook_idxs));
+    if (!s->codeword_closest_codebook_idxs)
+        return AVERROR(ENOMEM);
+
+    s->codebook = av_realloc_f(s->codebook, s->num_output_colors, 4 * sizeof(*s->codebook));
+    if (!s->codebook)
+        return AVERROR(ENOMEM);
+
+    /* build the codeword */
+    k = 0;
+    for (int i = 0; i < h; i++) {
+        uint8_t *p = src_data;
+        for (int j = 0; j < w; j++) {
+            s->codeword[k++] = p[s->b_idx];
+            s->codeword[k++] = p[s->g_idx];
+            s->codeword[k++] = p[s->r_idx];
+            s->codeword[k++] = p[s->a_idx];
+            p += 4;
+        }
+        src_data += src_linesize;
+    }
+
+    /* compute the codebook */
+    ret = avpriv_elbg_do(&s->ctx, s->codeword, 4,
+        codeword_length, s->codebook,
+        s->num_output_colors, 1,
+        s->codeword_closest_codebook_idxs, &s->lfg, 0);
+
+    if (ret < 0)
+        return ret;
+
+    /* Write Palette */
+    for (int i = 0; i < s->num_output_colors; i++) {
+        dst_pal[i] = s->codebook[i*4+3] << 24  |
+                    (s->codebook[i*4+2] << 16) |
+                    (s->codebook[i*4+1] <<  8) |
+                     s->codebook[i*4  ];
+    }
+
+    /* Write Image */
+    k = 0;
+    for (int i = 0; i < h; i++) {
+        uint8_t *p = dst_data;
+        for (int j = 0; j < w; j++, p++) {
+            p[0] = s->codeword_closest_codebook_idxs[k++];
+        }
+
+        dst_data += dst_linesize;
+    }
+
+    return ret;
+}
+
+static int rescale_size(int64_t a, AVRational factor)
+{
+    const int64_t res = av_rescale_rnd(a, factor.num, factor.den, AV_ROUND_NEAR_INF);
+    if (res > INT32_MAX || res < 0)
+        return 0;
+
+    return (int)res;
+}
+
+
+static int scale_area(AVFilterLink *link, AVSubtitleArea *area, const int target_width, const int target_height)
+{
+    const AVFilterContext *ctx = link->dst;
+    SubScaleContext *s = ctx->priv;
+    int ret;
+
+    AVBufferRef *dst_buffer;
+    const uint8_t* data[2]    = { area->buf[0]->data, (uint8_t *)&area->pal };
+    const int dstW            = FFALIGN(target_width, s->bitmap_width_align);
+    const int dstH            = FFALIGN(target_height, s->bitmap_height_align);
+    const int tmp_linesize[2] = { FFALIGN(dstW * 4, 32), 0 };
+    const int dst_linesize[2] = { dstW, 0 };
+    uint8_t* tmp[2] = { 0, 0 };
+
+    AVBufferRef *tmp_buffer = av_buffer_allocz(tmp_linesize[0] * dstH);
+    if (!tmp_buffer)
+        return AVERROR(ENOMEM);
+
+    if (!s->sws)
+        return 0;
+
+    tmp[0] = tmp_buffer->data;
+
+    s->sws = sws_getCachedContext(s->sws, area->w, area->h, AV_PIX_FMT_PAL8,
+        dstW, dstH, AV_PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL);
+    if (!s->sws) {
+        av_log(NULL, AV_LOG_FATAL, "Cannot initialize the conversion context. dstW=%d dstH=%d\n", dstW, dstH);
+        return AVERROR(EINVAL);
+    }
+
+    // Rescale to ARGB
+    ret = sws_scale(s->sws, data, area->linesize, 0, area->h, tmp, tmp_linesize);
+    if (ret < 0) {
+        av_buffer_unref(&tmp_buffer);
+        return ret;
+    }
+
+    // Alloc output buffer
+    dst_buffer = av_buffer_allocz(dst_linesize[0] * dstH);
+    if (!dst_buffer) {
+        av_buffer_unref(&tmp_buffer);
+        return AVERROR(ENOMEM);
+    }
+
+    // Quantize to palettized image
+    ret = palettize_image(s, dstW, dstH, tmp_linesize[0], tmp[0], dst_linesize[0], dst_buffer->data, area->pal);
+    av_buffer_unref(&tmp_buffer);
+
+    if (ret < 0) {
+        av_buffer_unref(&dst_buffer);
+        return ret;
+    }
+
+    av_buffer_unref(&area->buf[0]);
+    ret = av_buffer_replace(&area->buf[0], dst_buffer);
+    if (ret < 0) {
+        av_buffer_unref(&dst_buffer);
+        return ret;
+    }
+
+    area->w = dstW;
+    area->h = dstH;
+    area->linesize[0] = dst_linesize[0];
+    area->nb_colors = s->num_output_colors;
+
+    return ret;
+}
+
+static int process_area(AVFilterLink *inlink, AVSubtitleArea *area, AVRational x_factor, AVRational y_factor)
+{
+    AVFilterContext *ctx     = inlink->dst;
+    const SubScaleContext *s = ctx->priv;
+    int target_w, target_h, target_x, target_y;
+    const int border_l = s->margin_h;
+    const int border_r = s->w - s->margin_h;
+    const int border_t = s->margin_v;
+    const int border_b = s->h - s->margin_v;
+
+    av_log(ctx, AV_LOG_DEBUG, "process_area -  start: x/y: (%d:%d) size: %dx%d scale_mode: %d x-factor: %d:%d y-factor: %d:%d\n",
+        area->x, area->y, area->w, area->h, s->scale_mode, x_factor.num, x_factor.den, y_factor.num, y_factor.den);
+
+    switch (s->scale_mode) {
+    case SSM_NONE:
+        target_w = area->w;
+        target_h = area->h;
+        target_x = area->x;
+        target_y = area->y;
+        break;
+    case SSM_UNIFORM:
+        target_w = rescale_size(area->w, x_factor);
+        target_h = rescale_size(area->h, y_factor);
+        target_x = rescale_size(area->x, x_factor);
+        target_y = rescale_size(area->y, y_factor);
+        break;
+    case SSM_UNIFORM_NO_REPOSITION:
+        target_w = rescale_size(area->w, x_factor);
+        target_h = rescale_size(area->h, y_factor);
+        target_x = area->x;
+        target_y = area->y;
+        break;
+    default:
+        av_log(ctx, AV_LOG_ERROR, "Invalid scale_mode: %d\n", s->scale_mode);
+        return AVERROR(EINVAL);
+    }
+
+    av_log(ctx, AV_LOG_DEBUG, "process_area - scaled: x/y: (%d:%d) size: %dx%d.\n", target_x, target_y, target_w, target_h);
+
+
+    switch (s->arrange_mode_h) {
+    case SAM_ENSUREMARGIN_AND_SCALE:
+    case SAM_SNAPALIGNMENT_AND_SCALE:
+        {
+            // IF it doesn't fit - scale it
+            int max_width = s->w - 2 * s->margin_h;
+
+            if (max_width < 2)
+                max_width = 2;
+
+            if (target_w > max_width) {
+                target_h = (int)av_rescale(target_h, max_width, target_w);
+                target_w = max_width;
+                target_x = s->margin_h;
+            }
+        }
+        break;
+    }
+
+    switch (s->arrange_mode_h) {
+    case SAM_NONE:
+        break;
+    case SAM_ENSUREMARGIN_NO_SCALE:
+    case SAM_ENSUREMARGIN_AND_SCALE:
+        // Left border
+        if (target_x < border_l)
+            target_x = border_l;
+
+        // Right border
+        if (target_x + target_w > border_r)
+            target_x = border_r - target_w;
+
+        break;
+    case SAM_SNAPALIGNMENT_NO_SCALE:
+    case SAM_SNAPALIGNMENT_AND_SCALE:
+        {
+            // Use original values to detect alignment
+            const int left_margin          = area->x;
+            const int right_margin         = inlink->w - area->x - area->w;
+            const AVRational diff_factor_r = { left_margin - right_margin, area->w };
+            const float diff_factor        = (float)av_q2d(diff_factor_r);
+
+            if (diff_factor > 0.2f) {
+                // Right aligned
+                target_x = border_r - target_w;
+            } else if (diff_factor < -0.2f) {
+                // Left aligned
+                target_x = border_l;
+            } else {
+                // Centered
+                target_x = (inlink->w - area->w) / 2;
+            }
+        }
+
+        break;
+    default:
+        av_log(ctx, AV_LOG_ERROR, "Invalid arrange_mode_h: %d\n", s->arrange_mode_h);
+        return AVERROR(EINVAL);
+    }
+
+    av_log(ctx, AV_LOG_DEBUG, "process_area -  arr_h: x/y: (%d:%d) size: %dx%d.\n", target_x, target_y, target_w, target_h);
+
+
+    switch (s->arrange_mode_v) {
+    case SAM_ENSUREMARGIN_AND_SCALE:
+    case SAM_SNAPALIGNMENT_AND_SCALE:
+        {
+            // IF it doesn't fit - scale it
+            int max_height = s->h - 2 * s->margin_v;
+
+            if (max_height < 2)
+                max_height = 2;
+
+            if (target_h > max_height) {
+                target_w = (int)av_rescale(target_w, max_height, target_h);
+                target_h = max_height;
+                target_y = s->margin_v;
+            }
+        }
+        break;
+    }
+
+    switch (s->arrange_mode_v) {
+    case SAM_NONE:
+        break;
+    case SAM_ENSUREMARGIN_NO_SCALE:
+    case SAM_ENSUREMARGIN_AND_SCALE:
+        // Top border
+        if (target_y < border_t)
+            target_y = border_t;
+
+        // Bottom border
+        if (target_y + target_h > border_b)
+            target_y = border_b - target_h;
+
+        break;
+    case SAM_SNAPALIGNMENT_NO_SCALE:
+    case SAM_SNAPALIGNMENT_AND_SCALE:
+        {
+            // Use original values to detect alignment
+            const int top_margin           = area->y;
+            const int bottom_margin        = inlink->h - area->y - area->h;
+            const AVRational diff_factor_r = { top_margin - bottom_margin, area->h };
+            const float diff_factor        = (float)av_q2d(diff_factor_r);
+
+            if (diff_factor > 0.2f) {
+                // Bottom aligned
+                target_y = border_b - target_h;
+            } else if (diff_factor < -0.2f) {
+                // Top aligned
+                target_y = border_t;
+            } else {
+                // Centered
+                target_y = (inlink->h - area->h) / 2;
+            }
+        }
+
+        break;
+    default:
+        av_log(ctx, AV_LOG_ERROR, "Invalid arrange_mode_v: %d\n", s->arrange_mode_v);
+        return AVERROR(EINVAL);
+    }
+
+    av_log(ctx, AV_LOG_VERBOSE, "process_area -  arr_v: x/y: (%d:%d) size: %dx%d.\n", target_x, target_y, target_w, target_h);
+
+    area->x = target_x;
+    area->y = target_y;
+
+    if (area->w != target_w || area->h != target_h)
+        return scale_area(inlink, area, target_w, target_h);
+
+    return 0;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    const AVFilterContext *ctx = inlink->dst;
+    SubScaleContext *s = ctx->priv;
+    AVFilterLink *outlink = ctx->outputs[0];
+    int ret;
+
+    // just forward empty frames
+    if (frame->num_subtitle_areas == 0) {
+        av_frame_free(&s->cache_frame);
+        return ff_filter_frame(outlink, frame);
+    }
+
+    if (s->use_caching && s->cache_frame && frame->repeat_sub
+        && s->cache_frame->subtitle_timing.start_pts == frame->subtitle_timing.start_pts) {
+        AVFrame *out = av_frame_clone(s->cache_frame);
+        if (!out)
+            return AVERROR(ENOMEM);
+
+        ret = av_frame_copy_props(out, frame);
+        if (ret < 0)
+            return ret;
+
+        av_log(inlink->dst, AV_LOG_DEBUG, "subscale CACHED - size %dx%d  pts: %"PRId64"  areas: %d\n", frame->width, frame->height, frame->subtitle_timing.start_pts, frame->num_subtitle_areas);
+        av_frame_free(&frame);
+        return ff_filter_frame(outlink, out);
+    }
+
+    ret = av_frame_make_writable(frame);
+    if (ret >= 0) {
+        const AVRational x_factor = { .num = outlink->w, .den = inlink->w} ;
+        const AVRational y_factor = { .num = outlink->h, .den = inlink->h} ;
+
+        for (unsigned i = 0; i < frame->num_subtitle_areas; ++i) {
+            AVSubtitleArea *area = frame->subtitle_areas[i];
+
+            ret = process_area(inlink, area, x_factor, y_factor);
+            if (ret < 0)
+                return ret;
+        }
+
+        av_log(inlink->dst, AV_LOG_DEBUG, "subscale output - size %dx%d  pts: %"PRId64"  areas: %d\n", frame->width, frame->height, frame->subtitle_timing.start_pts, frame->num_subtitle_areas);
+
+        if (s->use_caching) {
+            av_frame_free(&s->cache_frame);
+            s->cache_frame = av_frame_clone(frame);
+        }
+
+        return ff_filter_frame(outlink, frame);
+    }
+
+    return ret;
+}
+
+#define OFFSET(x) offsetof(SubScaleContext, x)
+#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption subscale_options[] = {
+    { "margin_h", "horizontal border",        OFFSET(margin_h_expr),    AV_OPT_TYPE_STRING,        .flags = FLAGS },
+    { "margin_v", "vertical border",          OFFSET(margin_v_expr),    AV_OPT_TYPE_STRING,        .flags = FLAGS },
+    { "w",     "Output video width",          OFFSET(w_expr),    AV_OPT_TYPE_STRING,        .flags = FLAGS },
+    { "width", "Output video width",          OFFSET(w_expr),    AV_OPT_TYPE_STRING,        .flags = FLAGS },
+    { "h",     "Output video height",         OFFSET(h_expr),    AV_OPT_TYPE_STRING,        .flags = FLAGS },
+    { "height","Output video height",         OFFSET(h_expr),    AV_OPT_TYPE_STRING,        .flags = FLAGS },
+    { "force_original_aspect_ratio", "decrease or increase w/h if necessary to keep the original AR", OFFSET(force_original_aspect_ratio), AV_OPT_TYPE_INT, { .i64 = 0}, 0, 2, FLAGS, "force_oar" },
+    { "disable",  NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, 0, 0, FLAGS, "force_oar" },
+    { "decrease", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 1 }, 0, 0, FLAGS, "force_oar" },
+    { "increase", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 2 }, 0, 0, FLAGS, "force_oar" },
+    { "scale_mode", "specify how to scale subtitles", OFFSET(scale_mode), AV_OPT_TYPE_INT, {.i64 = SSM_UNIFORM}, 0, SSM_UNIFORM_NO_REPOSITION, FLAGS, "scale_mode" },
+         { "none",  "no common scaling", 0, AV_OPT_TYPE_CONST, {.i64=SSM_NONE},  .flags = FLAGS, .unit = "scale_mode" },
+         { "uniform",  "uniformly scale and reposition", 0, AV_OPT_TYPE_CONST, {.i64=SSM_UNIFORM},  .flags = FLAGS, .unit = "scale_mode" },
+         { "uniform_no_reposition",  "uniformly scale but keep positions", 0, AV_OPT_TYPE_CONST, {.i64=SSM_UNIFORM_NO_REPOSITION},  .flags = FLAGS, .unit = "scale_mode" },
+    { "use_caching", "Cache output frames",   OFFSET(use_caching), AV_OPT_TYPE_BOOL, { .i64 = 1}, 0, 1, .flags = FLAGS },
+
+    { "arrange_h", "specify how to arrange subtitles horizontally", OFFSET(arrange_mode_h), AV_OPT_TYPE_INT, {.i64 = SAM_NONE}, 0, SAM_SNAPALIGNMENT_AND_SCALE, FLAGS, "arrange" },
+    { "arrange_v", "specify how to arrange subtitles vertically", OFFSET(arrange_mode_v), AV_OPT_TYPE_INT, {.i64 = SAM_NONE}, 0, SAM_SNAPALIGNMENT_AND_SCALE, FLAGS, "arrange" },
+         { "none",  "no repositioning", 0, AV_OPT_TYPE_CONST, {.i64=SAM_NONE},  .flags = FLAGS, .unit = "arrange" },
+         { "margin_no_scale",  "move subs inside border when possible", 0, AV_OPT_TYPE_CONST, {.i64=SAM_ENSUREMARGIN_NO_SCALE,},  .flags = FLAGS, .unit = "arrange" },
+         { "margin_and_scale",  "move subs inside border and scale as needed", 0, AV_OPT_TYPE_CONST, {.i64=SAM_ENSUREMARGIN_AND_SCALE,},  .flags = FLAGS, .unit = "arrange" },
+         { "snapalign_no_scale",  "snap subs to near/far/center when possible", 0, AV_OPT_TYPE_CONST, {.i64=SAM_SNAPALIGNMENT_NO_SCALE,},  .flags = FLAGS, .unit = "arrange" },
+         { "snapalign_and_scale", "snap subs to near/far/center and scale as needed", 0, AV_OPT_TYPE_CONST, {.i64=SAM_SNAPALIGNMENT_AND_SCALE,}, .flags = FLAGS, .unit = "arrange" },
+
+    { "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, {.i64 = EVAL_MODE_INIT}, 0, EVAL_MODE_NB-1, FLAGS, "eval" },
+         { "init",  "eval expressions once during initialization", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_INIT},  .flags = FLAGS, .unit = "eval" },
+         { "frame", "eval expressions during initialization and per-frame", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_FRAME}, .flags = FLAGS, .unit = "eval" },
+    { "num_colors",     "number of palette colors in output", OFFSET(num_output_colors),  AV_OPT_TYPE_INT, {.i64 = 256 }, 2, 256, .flags = FLAGS },
+    { "bitmap_width_align",     "Bitmap width alignment", OFFSET(bitmap_width_align),  AV_OPT_TYPE_INT, {.i64 = 2 }, 1, 256, .flags = FLAGS },
+    { "bitmap_height_align",     "Bitmap height alignment", OFFSET(bitmap_height_align),  AV_OPT_TYPE_INT, {.i64 = 2 }, 1, 256, .flags = FLAGS },
+    { .name =  NULL }
+};
+
+static const AVClass subscale_class = {
+    .class_name       = "subscale",
+    .item_name        = av_default_item_name,
+    .option           = subscale_options,
+    .version          = LIBAVUTIL_VERSION_INT,
+    .category         = AV_CLASS_CATEGORY_FILTER,
+};
+
+static const AVFilterPad inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .filter_frame = filter_frame,
+        .config_props = config_input,
+    },
+};
+
+static const AVFilterPad outputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .config_props = config_output,
+    },
+};
+
+const AVFilter ff_sf_subscale = {
+    .name            = "subscale",
+    .description     = NULL_IF_CONFIG_SMALL("Scale graphical subtitles."),
+    .init_dict       = init_dict,
+    .uninit          = uninit,
+    .priv_size       = sizeof(SubScaleContext),
+    .priv_class      = &subscale_class,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_SINGLE_SUBFMT(AV_SUBTITLE_FMT_BITMAP),
+};
+
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v5 20/25] avfilter/subfeed: add subtitle feed filter
  2022-06-25  9:57       ` [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022 ffmpegagent
                           ` (18 preceding siblings ...)
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 19/25] avfilter/subscale: Add filter for scaling and/or re-arranging graphical subtitles softworkz
@ 2022-06-25  9:57         ` softworkz
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 21/25] avfilter/text2graphicsub: Added text2graphicsub subtitle filter tcoza
                           ` (7 subsequent siblings)
  27 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-25  9:57 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavfilter/Makefile     |   1 +
 libavfilter/allfilters.c |   1 +
 libavfilter/sf_subfeed.c | 412 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 414 insertions(+)
 create mode 100644 libavfilter/sf_subfeed.c

diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 0f43937205..780e5333a0 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -583,6 +583,7 @@ OBJS-$(CONFIG_CENSOR_FILTER)                 += sf_textmod.o
 OBJS-$(CONFIG_SHOW_SPEAKER_FILTER)           += sf_textmod.o
 OBJS-$(CONFIG_SPLITCC_FILTER)                += sf_splitcc.o
 OBJS-$(CONFIG_STRIPSTYLES_FILTER)            += sf_stripstyles.o
+OBJS-$(CONFIG_SUBFEED_FILTER)                += sf_subfeed.o
 OBJS-$(CONFIG_SUBSCALE_FILTER)               += sf_subscale.o
 OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
 
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 6792665730..ff56661b67 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -575,6 +575,7 @@ extern const AVFilter ff_sf_graphicsub2text;
 extern const AVFilter ff_sf_showspeaker;
 extern const AVFilter ff_sf_splitcc;
 extern const AVFilter ff_sf_stripstyles;
+extern const AVFilter ff_sf_subfeed;
 extern const AVFilter ff_sf_subscale;
 extern const AVFilter ff_sf_textmod;
 
diff --git a/libavfilter/sf_subfeed.c b/libavfilter/sf_subfeed.c
new file mode 100644
index 0000000000..3227861ac0
--- /dev/null
+++ b/libavfilter/sf_subfeed.c
@@ -0,0 +1,412 @@
+/*
+ * 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
+ * subtitle filter for feeding subtitle frames into a filtergraph in a contiguous way
+ *
+ *
+ * also supports
+ *   - duration fixup
+ *     delaying a subtitle event with unknown duration and infer duration from the
+ *     start time of the subsequent subtitle
+ *   - scattering
+ *     splitting a subtitle event with unknown duration into multiple ones with
+ *     a short and fixed duration
+ *
+ */
+
+#include "filters.h"
+#include "libavutil/opt.h"
+#include "subtitles.h"
+#include "libavutil/avassert.h"
+
+enum SubFeedMode {
+    FM_REPEAT,
+    FM_SCATTER,
+    FM_FORWARD,
+};
+
+typedef struct SubFeedContext {
+    const AVClass *class;
+    enum AVSubtitleType format;
+    enum SubFeedMode mode;
+
+    AVRational frame_rate;
+    int fix_durations;
+    int fix_overlap;
+
+    int current_frame_isnew;
+    int eof;
+    int got_first_input;
+    int need_frame;
+    int64_t next_pts_offset;
+    int64_t recent_subtitle_pts;
+
+    int64_t counter;
+
+    /**
+     * Queue of frames waiting to be filtered.
+     */
+    FFFrameQueue fifo;
+
+} SubFeedContext;
+
+static int64_t ms_to_avtb(int64_t ms)
+{
+    return av_rescale_q(ms, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q);
+}
+
+static int64_t avtb_to_ms(int64_t avtb)
+{
+    return av_rescale_q(avtb, AV_TIME_BASE_Q, (AVRational){ 1, 1000 });
+}
+
+static int init(AVFilterContext *ctx)
+{
+    SubFeedContext *s = ctx->priv;
+
+    ff_framequeue_init(&s->fifo, NULL);
+
+    return 0;
+}
+
+static void uninit(AVFilterContext *ctx)
+{
+    SubFeedContext *s = ctx->priv;
+    ff_framequeue_free(&s->fifo);
+}
+
+static int config_input(AVFilterLink *link)
+{
+    ////const subfeedContext *context = link->dst->priv;
+
+    return 0;
+}
+
+static int query_formats(AVFilterContext *ctx)
+{
+    AVFilterFormats *formats;
+    AVFilterLink *inlink0 = ctx->inputs[0];
+    AVFilterLink *outlink0 = ctx->outputs[0];
+    static const enum AVSubtitleType subtitle_fmts[] = { AV_SUBTITLE_FMT_BITMAP, AV_SUBTITLE_FMT_ASS, AV_SUBTITLE_FMT_NB };
+    int ret;
+
+    formats = ff_make_format_list(subtitle_fmts);
+
+    if ((ret = ff_formats_ref(formats, &inlink0->outcfg.formats)) < 0)
+        return ret;
+
+    if ((ret = ff_formats_ref(formats, &outlink0->incfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    SubFeedContext *s = outlink->src->priv;
+    const AVFilterLink *inlink = outlink->src->inputs[0];
+
+    outlink->time_base = AV_TIME_BASE_Q;
+    outlink->format = inlink->format;
+    outlink->w = inlink->w;
+    outlink->h = inlink->h;
+
+    if (s->mode == FM_FORWARD)
+        outlink->frame_rate = (AVRational) { 1, 0 };
+    else
+        outlink->frame_rate = s->frame_rate;
+
+    return 0;
+}
+
+static int request_frame(AVFilterLink *outlink)
+{
+    SubFeedContext *s = outlink->src->priv;
+    AVFilterLink *inlink = outlink->src->inputs[0];
+    int64_t last_pts = outlink->current_pts;
+    int64_t next_pts;
+    int64_t interval = ms_to_avtb((int64_t)(av_q2d(av_inv_q(outlink->frame_rate)) * 1000));
+    AVFrame *out;
+    int status;
+
+    if (s->mode == FM_FORWARD)
+        return ff_request_frame(inlink);
+
+    s->counter++;
+    if (interval == 0)
+        interval =  ms_to_avtb(200);
+
+    status = ff_outlink_get_status(inlink);
+    if (status == AVERROR_EOF)
+        s->eof = 1;
+
+    if (s->eof)
+        return AVERROR_EOF;
+
+    if (!s->got_first_input && inlink->current_pts != AV_NOPTS_VALUE) {
+
+        s->got_first_input = 1;
+        next_pts = av_rescale_q(inlink->current_pts, inlink->time_base, AV_TIME_BASE_Q);
+        if (next_pts < last_pts)
+            next_pts = last_pts + interval;
+
+    } else if (last_pts == AV_NOPTS_VALUE)
+        next_pts = av_rescale_q(inlink->current_pts, inlink->time_base, AV_TIME_BASE_Q);
+    else
+        next_pts = last_pts + interval;
+
+    if (next_pts == AV_NOPTS_VALUE)
+        next_pts = 0;
+
+    if (s->next_pts_offset) {
+        av_log(outlink->src, AV_LOG_VERBOSE, "Subtracting next_pts_offset: %"PRId64" \n", s->next_pts_offset);
+        next_pts -= s->next_pts_offset;
+        s->next_pts_offset = 0;
+    }
+
+retry:
+    if (ff_framequeue_queued_frames(&s->fifo) && !s->current_frame_isnew) {
+
+        const AVFrame *current_frame = ff_framequeue_peek(&s->fifo, 0);
+        const int64_t sub_end_time   = current_frame->subtitle_timing.start_pts + current_frame->subtitle_timing.duration;
+
+        if (ff_framequeue_queued_frames(&s->fifo) > 1) {
+            const AVFrame *next_frame = ff_framequeue_peek(&s->fifo, 1);
+            if (next_pts + interval > next_frame->subtitle_timing.start_pts) {
+                AVFrame *remove_frame = ff_framequeue_take(&s->fifo);
+                av_frame_free(&remove_frame);
+                s->current_frame_isnew = 1;
+                goto retry;
+            }
+        }
+
+        if (next_pts > sub_end_time) {
+            AVFrame *remove_frame = ff_framequeue_take(&s->fifo);
+            av_frame_free(&remove_frame);
+            s->current_frame_isnew = 1;
+            goto retry;
+        }
+    }
+
+    if (ff_framequeue_queued_frames(&s->fifo)) {
+        AVFrame *current_frame = ff_framequeue_peek(&s->fifo, 0);
+
+        if (current_frame && current_frame->subtitle_timing.start_pts <= next_pts + interval) {
+            if (!s->current_frame_isnew)
+                current_frame->repeat_sub++;
+
+            out = av_frame_clone(current_frame);
+
+            if (!out)
+                return AVERROR(ENOMEM);
+
+            if (!s->current_frame_isnew) {
+                out->pts = next_pts;
+            } else {
+                out->pts = out->subtitle_timing.start_pts;
+
+                if (out->pts < next_pts)
+                    out->pts = next_pts;
+
+                s->next_pts_offset = (out->pts - next_pts) % interval;
+            }
+
+            if (s->mode == FM_SCATTER) {
+                const int64_t sub_end_time  = current_frame->subtitle_timing.start_pts + current_frame->subtitle_timing.duration;
+
+                if (s->current_frame_isnew == 1 && current_frame->subtitle_timing.start_pts < out->pts) {
+                    const int64_t diff = out->pts - current_frame->subtitle_timing.start_pts;
+                    current_frame->subtitle_timing.duration -= diff;
+                }
+
+                out->repeat_sub = 0;
+                out->subtitle_timing.start_pts = out->pts;
+                out->subtitle_timing.duration = interval;
+                av_assert1(out->pts >= next_pts);
+                av_assert1(out->pts < next_pts + interval);
+                av_assert1(out->pts < sub_end_time);
+
+                if (out->pts > next_pts)
+                    out->subtitle_timing.duration -= out->pts - next_pts;
+
+                if (sub_end_time < next_pts + interval) {
+                    const int64_t diff = next_pts + interval - sub_end_time;
+                    av_assert1(diff <= out->subtitle_timing.duration);
+                    out->subtitle_timing.duration -= diff;
+                }
+            }
+
+            s->current_frame_isnew = 0;
+            s->recent_subtitle_pts = out->subtitle_timing.start_pts;
+
+            av_log(outlink->src, AV_LOG_DEBUG, "Output1 frame pts: %"PRId64"  subtitle_pts: %"PRId64"  repeat_frame: %d\n",
+                out->pts, out->subtitle_timing.start_pts, out->repeat_sub);
+
+            return ff_filter_frame(outlink, out);
+        }
+    }
+
+    if (ff_framequeue_queued_frames(&s->fifo) == 0) {
+        status = ff_request_frame(inlink);
+        if (status == AVERROR_EOF) {
+            s->eof = 1;
+            return status;
+        }
+
+        if (s->counter > 1 && s->counter % 2)
+            return 0;
+    }
+
+    out = ff_get_subtitles_buffer(outlink, outlink->format);
+    out->pts = next_pts;
+    out->repeat_sub = 1;
+    out->subtitle_timing.start_pts = s->recent_subtitle_pts;
+
+    av_log(outlink->src, AV_LOG_DEBUG, "Output2 frame pts: %"PRId64"  subtitle_pts: %"PRId64"  repeat_frame: %d\n",
+        out->pts, out->subtitle_timing.start_pts, out->repeat_sub);
+
+    return ff_filter_frame(outlink, out);
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFilterContext *ctx        = inlink->dst;
+    SubFeedContext *s           = inlink->dst->priv;
+    AVFilterLink *outlink       = inlink->dst->outputs[0];
+    const int64_t index         = (int64_t)ff_framequeue_queued_frames(&s->fifo) - 1;
+    size_t nb_queued_frames;
+    int ret = 0;
+
+    av_log(ctx, AV_LOG_VERBOSE, "frame.pts: %"PRId64" (AVTB: %"PRId64") -  subtitle_timing.start_pts: %"PRId64" subtitle_timing.duration: %"PRId64" - format: %d\n",
+        frame->pts, av_rescale_q(frame->pts, inlink->time_base, AV_TIME_BASE_Q), frame->subtitle_timing.start_pts, frame->subtitle_timing.duration, frame->format);
+
+    frame->pts = av_rescale_q(frame->pts, inlink->time_base, AV_TIME_BASE_Q);
+
+    if (index < 0) {
+        s->current_frame_isnew = 1;
+    } else if (s->fix_durations || s->fix_overlap) {
+        AVFrame *previous_frame = ff_framequeue_peek(&s->fifo, index);
+        const int64_t pts_diff = frame->subtitle_timing.start_pts - previous_frame->subtitle_timing.start_pts;
+        nb_queued_frames = ff_framequeue_queued_frames(&s->fifo);
+
+        if (s->fix_durations && pts_diff > 0 && previous_frame->subtitle_timing.duration > ms_to_avtb(29000)) {
+            av_log(ctx, AV_LOG_VERBOSE, "Previous frame (index #%"PRId64") has a duration of %"PRId64" ms, setting to  %"PRId64" ms\n",
+                index, avtb_to_ms(previous_frame->subtitle_timing.duration), avtb_to_ms(frame->subtitle_timing.start_pts - previous_frame->subtitle_timing.start_pts));
+            previous_frame->subtitle_timing.duration = frame->subtitle_timing.start_pts - previous_frame->subtitle_timing.start_pts;
+        }
+
+        if (s->fix_overlap && pts_diff > 0 && previous_frame->subtitle_timing.duration > pts_diff) {
+            av_log(ctx, AV_LOG_VERBOSE, "Detected overlap from previous frame (index #%"PRId64") which had a duration of %"PRId64" ms, setting to the pts_diff which is %"PRId64" ms\n",
+                index, avtb_to_ms(previous_frame->subtitle_timing.duration), avtb_to_ms(pts_diff));
+            previous_frame->subtitle_timing.duration = pts_diff;
+        }
+
+        if (pts_diff <= 0) {
+            av_log(ctx, AV_LOG_WARNING, "The pts_diff to the previous frame (index #%"PRId64")  is <= 0: %"PRId64" ms. The previous frame duration is %"PRId64" ms.\n",
+                index, avtb_to_ms(pts_diff),  avtb_to_ms(previous_frame->subtitle_timing.duration));
+
+            if (s->fix_overlap) {
+                av_log(ctx, AV_LOG_VERBOSE, "Removing previous frame\n");
+                previous_frame = ff_framequeue_take(&s->fifo);
+                while (nb_queued_frames > 1) {
+                    ff_framequeue_add(&s->fifo, previous_frame);
+                    previous_frame = ff_framequeue_take(&s->fifo);
+                    nb_queued_frames--;
+                }
+            }
+        }
+    }
+
+    ff_framequeue_add(&s->fifo, frame);
+
+    nb_queued_frames = ff_framequeue_queued_frames(&s->fifo);
+
+    if (nb_queued_frames > 3)
+        av_log(ctx, AV_LOG_WARNING, "frame queue count: %zu\n", nb_queued_frames);
+
+    if (s->mode == FM_FORWARD && nb_queued_frames) {
+
+        AVFrame *first_frame = ff_framequeue_peek(&s->fifo, 0);
+
+        if (s->fix_overlap && nb_queued_frames < 2) {
+          av_log(ctx, AV_LOG_VERBOSE, "Return no frame since we have less than 2\n");
+          return 0;
+        }
+
+        if (s->fix_durations && first_frame->subtitle_timing.duration > ms_to_avtb(29000)) {
+            av_log(ctx, AV_LOG_VERBOSE, "Return no frame because first frame duration is %"PRId64" ms\n", avtb_to_ms(first_frame->subtitle_timing.duration));
+            return 0;
+        }
+
+        first_frame = ff_framequeue_take(&s->fifo);
+        return ff_filter_frame(outlink, first_frame);
+    }
+
+    return ret;
+}
+
+#define OFFSET(x) offsetof(SubFeedContext, x)
+#define FLAGS (AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption subfeed_options[] = {
+    { "fix_durations", "delay output and determine duration from next frame", OFFSET(fix_durations),   AV_OPT_TYPE_BOOL,   { .i64=1 },                  0, 1, FLAGS, NULL     },
+    { "fix_overlap", "delay output and adjust durations to prevent overlap", OFFSET(fix_overlap),   AV_OPT_TYPE_BOOL,   { .i64=0 },                  0, 1, FLAGS, NULL     },
+    { "mode",       "set feed mode",         OFFSET(mode),      AV_OPT_TYPE_INT,                 { .i64=FM_REPEAT },  FM_REPEAT, FM_FORWARD,  FLAGS, "mode" },
+    {   "repeat",     "repeat recent while valid, send empty otherwise",   0, AV_OPT_TYPE_CONST, { .i64=FM_REPEAT },  0,                  0,  FLAGS, "mode" },
+    {   "scatter",    "subdivide subtitles into 1/framerate segments",     0, AV_OPT_TYPE_CONST, { .i64=FM_SCATTER }, 0,                  0,  FLAGS, "mode" },
+    {   "forward",    "forward only (clears output framerate)",            0, AV_OPT_TYPE_CONST, { .i64=FM_FORWARD }, 0,                  0,  FLAGS, "mode" },
+    { "rate",       "output frame rate",     OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE,         { .str = "5"},       0,         INT_MAX,     FLAGS, NULL },\
+    { "r",          "output frame rate",     OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE,         { .str = "5"},       0,         INT_MAX,     FLAGS, NULL },\
+    { NULL },
+};
+
+AVFILTER_DEFINE_CLASS(subfeed);
+
+static const AVFilterPad inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .filter_frame = filter_frame,
+        .config_props = config_input,
+    },
+};
+
+static const AVFilterPad outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_SUBTITLE,
+        .request_frame = request_frame,
+        .config_props  = config_output,
+    },
+};
+
+const AVFilter ff_sf_subfeed = {
+    .name           = "subfeed",
+    .description    = NULL_IF_CONFIG_SMALL("Control subtitle frame timing and flow in a filtergraph"),
+    .init           = init,
+    .uninit         = uninit,
+    .priv_size      = sizeof(SubFeedContext),
+    .priv_class     = &subfeed_class,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_QUERY_FUNC(query_formats),
+};
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v5 21/25] avfilter/text2graphicsub: Added text2graphicsub subtitle filter
  2022-06-25  9:57       ` [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022 ffmpegagent
                           ` (19 preceding siblings ...)
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 20/25] avfilter/subfeed: add subtitle feed filter softworkz
@ 2022-06-25  9:57         ` tcoza
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 22/25] avfilter/snull, strim: Add snull and strim filters softworkz
                           ` (6 subsequent siblings)
  27 siblings, 0 replies; 217+ messages in thread
From: tcoza @ 2022-06-25  9:57 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, tcoza, Andreas Rheinhardt

From: tcoza <traian.coza@gmail.com>

Added a text2graphicsub subtitle filter which converts text-based
subtitle tracks to bitmap-based subtitle tracks. The filter uses libass
to render the subtitles.
It takes as parameters an output height and width, as well as a number
of colors in the output palette as well as sources of fonts. All its
arguments are optional.

Reviewed-by: softworkz <softworkz@hotmail.com>
Signed-off-by: softworkz <softworkz@hotmail.com>
Signed-off-by: tcoza <traian.coza@gmail.com>
---
 configure                        |   1 +
 doc/filters.texi                 |  51 +++
 libavfilter/Makefile             |   1 +
 libavfilter/allfilters.c         |   1 +
 libavfilter/sf_text2graphicsub.c | 630 +++++++++++++++++++++++++++++++
 5 files changed, 684 insertions(+)
 create mode 100644 libavfilter/sf_text2graphicsub.c

diff --git a/configure b/configure
index 89db322fb8..f74077b439 100755
--- a/configure
+++ b/configure
@@ -3734,6 +3734,7 @@ subscale_filter_deps="swscale avcodec"
 subtitles_filter_deps="avformat avcodec libass"
 super2xsai_filter_deps="gpl"
 pixfmts_super2xsai_test_deps="super2xsai_filter"
+text2graphicsub_filter_deps="avformat avcodec libass"
 textsub2video_filter_deps="avcodec libass"
 tinterlace_filter_deps="gpl"
 tinterlace_merge_test_deps="tinterlace_filter"
diff --git a/doc/filters.texi b/doc/filters.texi
index 00fa9bbac5..56da160634 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -27744,6 +27744,57 @@ ffmpeg -y -y -loglevel verbose -i "https://streams.videolan.org/samples/sub/larg
 @end example
 @end itemize
 
+@section text2graphicsub
+
+Converts a text-based subtitle track to a bitmap-based subtitle track.
+
+The text2graphicsub filter uses libass to render all the subtitle
+frames in a text-based subtitle track (such as srt or ass) to allow
+its encoding with a bitmap-based subtitle encoder (such as dvd_subtitle).
+
+Inputs:
+- 0: Subtitles [text]
+
+Outputs:
+- 0: Subtitles [bitmap]
+
+It accepts the following parameters:
+
+@table @option
+
+@item s, size
+Set the size of the output. Default from input track.
+
+@item n, num_colors
+Set the number of palette colors for output images,
+Range [2,256]. Default 16.
+
+@item f, filename
+Set the media container from which to extract fonts required
+for rendering the subtitles, usually the same as the input
+track's media container. Can be omitted.
+
+@item fd, fontsdir
+Set the directory from which to load fonts required for
+rendering the subtitles. Can be used with 'fonts'. Can be omitted.
+
+@item ss, stripstyles
+Remove certain styles from an ass track which do not render well.
+Stripped styles include blurs, fades, and other animations.
+Default yes.
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Convert a video's text-based subtitle track to a dvd_subtitle subtitle track.
+@example
+ffmpeg -i video.mkv -map 0:v -map 0:a -filter_complex [0:s]text2graphicsub=f=video.mkv[dvd_sub] -map [dvd_sub] -c copy -c:s dvd_subtitle output.mkv
+@end example
+@end itemize
+
 @c man end SUBTITLE FILTERS
 
 @chapter Multimedia Filters
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 780e5333a0..adbc16a5a9 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -585,6 +585,7 @@ OBJS-$(CONFIG_SPLITCC_FILTER)                += sf_splitcc.o
 OBJS-$(CONFIG_STRIPSTYLES_FILTER)            += sf_stripstyles.o
 OBJS-$(CONFIG_SUBFEED_FILTER)                += sf_subfeed.o
 OBJS-$(CONFIG_SUBSCALE_FILTER)               += sf_subscale.o
+OBJS-$(CONFIG_TEXT2GRAPHICSUB_FILTER)        += sf_text2graphicsub.o
 OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
 
 # multimedia filters
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index ff56661b67..82297e9657 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -577,6 +577,7 @@ extern const AVFilter ff_sf_splitcc;
 extern const AVFilter ff_sf_stripstyles;
 extern const AVFilter ff_sf_subfeed;
 extern const AVFilter ff_sf_subscale;
+extern const AVFilter ff_sf_text2graphicsub;
 extern const AVFilter ff_sf_textmod;
 
 /* those filters are part of public or internal API,
diff --git a/libavfilter/sf_text2graphicsub.c b/libavfilter/sf_text2graphicsub.c
new file mode 100644
index 0000000000..072d70d51a
--- /dev/null
+++ b/libavfilter/sf_text2graphicsub.c
@@ -0,0 +1,630 @@
+/*
+ * Copyright (c) 2021 tcoza
+ *
+ * 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
+ * convert text subtitles to bitmap subtitles filter
+ */
+
+#include <ass/ass.h>
+#include "libavutil/avstring.h"
+
+#include "internal.h"
+#include "avfilter.h"
+#include "drawutils.h"
+#include "libavutil/opt.h"
+#include "libavutil/buffer.h"
+#include "libavutil/internal.h"
+#include "libavformat/avformat.h"
+#include "libavcodec/elbg.h"
+#include "libavutil/ass_split_internal.h"
+#include "libavutil/ass_internal.h"
+#include "libavutil/lfg.h"
+
+typedef struct PalettizeContext {
+    int *codeword, *codebook;
+    int *codeword_closest_codebook_idxs;
+    int r_idx, g_idx, b_idx, a_idx;
+    struct ELBGContext *elbg;
+    AVLFG lfg;
+} PalettizeContext;
+
+typedef struct Text2GraphicSubContext {
+    const AVClass *class;
+    ASS_Library *library;
+    ASS_Renderer *renderer;
+    ASS_Track *track;
+    PalettizeContext *palettize_context;
+    FFDrawContext draw_context;
+    struct { int width; int height; } size;
+    int num_colors, stripstyles;
+    char *filename, *fontsdir, *force_style;
+    int got_header;
+} Text2GraphicSubContext;
+
+typedef struct DialogContext {
+    int is_animated;
+    int has_text;
+} DialogContext;
+
+static void dialog_text_cb(void *priv, const char *text, int len)
+{
+    DialogContext *s = priv;
+    if (!s->is_animated )
+        s->has_text = 1;
+}
+
+static void dialog_drawing_mode_cb(void *priv, int scale)
+{
+    DialogContext *s = priv;
+    s->is_animated = 1;
+}
+
+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,
+    .drawing_mode     = dialog_drawing_mode_cb,
+    .animate          = dialog_animate_cb,
+    .move             = dialog_move_cb,
+};
+
+static char *process_dialog(const char *ass_line)
+{
+    DialogContext dlg_ctx = { 0 };
+    ASSDialog *dialog = avpriv_ass_split_dialog(NULL, ass_line);
+    AVBPrint buffer;
+    char *result = NULL;
+
+    if (!dialog)
+        return NULL;
+
+    av_bprint_init(&buffer, 512, AV_BPRINT_SIZE_UNLIMITED);
+
+    avpriv_ass_filter_override_codes(&dialog_callbacks, &dlg_ctx, dialog->text, &buffer, ASS_SPLIT_BASIC);
+
+    if (av_bprint_is_complete(&buffer) && buffer.len > 0 && dlg_ctx.has_text > 0)
+        result = avpriv_ass_get_dialog_ex(dialog->readorder, dialog->layer, dialog->style, dialog->name, dialog->margin_l, dialog->margin_r, dialog->margin_v, dialog->effect, buffer.str);
+
+    av_bprint_finalize(&buffer, NULL);
+    avpriv_ass_free_dialog(&dialog);
+
+    return result;
+}
+
+static void palettize_image(PalettizeContext *const s,
+                           const int w, const int h,
+                           uint8_t *src_data, const int src_linesize,
+                           uint8_t *dst_data, const int dst_linesize,
+                           uint32_t *dst_pal, const int num_colors)
+{
+    const int codeword_length = w * h;
+
+    /* Re-Initialize */
+    s->codeword = av_realloc_f(s->codeword, codeword_length, 4 * sizeof(*s->codeword));
+    s->codeword_closest_codebook_idxs = av_realloc_f(
+        s->codeword_closest_codebook_idxs, codeword_length,
+        sizeof(*s->codeword_closest_codebook_idxs));
+    s->codebook = av_realloc_f(s->codebook, num_colors, 4 * sizeof(*s->codebook));
+
+    /* build the codeword */
+    for (int k = 0, i = 0; i < h; i++)
+        for (int j = 0; j < w; j++) {
+            s->codeword[k++] = src_data[i * src_linesize + j * 4 + s->b_idx];
+            s->codeword[k++] = src_data[i * src_linesize + j * 4 + s->g_idx];
+            s->codeword[k++] = src_data[i * src_linesize + j * 4 + s->r_idx];
+            s->codeword[k++] = src_data[i * src_linesize + j * 4 + s->a_idx];
+        }
+
+    /* compute the codebook */
+    avpriv_elbg_do(&s->elbg, s->codeword, 4,
+        codeword_length, s->codebook, num_colors, 1,
+        s->codeword_closest_codebook_idxs, &s->lfg, 0);
+
+    /* Write Palette */
+    for (int i = 0; i < num_colors; i++)
+        dst_pal[i] = (s->codebook[i*4+3] << 24) |
+                     (s->codebook[i*4+2] << 16) |
+                     (s->codebook[i*4+1] <<  8) |
+                     (s->codebook[i*4+0] <<  0);
+
+    /* Write Image */
+    for (int k = 0, i = 0; i < h; i++)
+        for (int j = 0; j < w; j++)
+            dst_data[i * dst_linesize + j] =
+                s->codeword_closest_codebook_idxs[k++];
+}
+
+static void init_palettizecontext(PalettizeContext **palettizecontext)
+{
+    uint8_t rgba_map[4];
+    PalettizeContext *context = (PalettizeContext *)av_malloc(sizeof(PalettizeContext));
+    context->codebook = NULL;
+    context->codeword = NULL;
+    context->codeword_closest_codebook_idxs = NULL;
+    context->elbg = NULL;
+    av_lfg_init(&context->lfg, 0xACBADF);
+    ff_fill_rgba_map(&rgba_map[0], AV_PIX_FMT_RGB32);
+    context->r_idx = rgba_map[0]; // R
+    context->g_idx = rgba_map[1]; // G
+    context->b_idx = rgba_map[2]; // B
+    context->a_idx = rgba_map[3]; // A
+    *palettizecontext = context;
+}
+
+static void free_palettizecontext(PalettizeContext **palettizecontext)
+{
+    PalettizeContext *context = *palettizecontext;
+    av_freep(&context->codebook);
+    av_freep(&context->codeword);
+    av_freep(&context->codeword_closest_codebook_idxs);
+    avpriv_elbg_free(&context->elbg);
+    av_free(context);
+    *palettizecontext = NULL;
+}
+
+/* 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 const char * const font_mimetypes[] = {
+    "font/ttf",
+    "font/otf",
+    "font/sfnt",
+    "font/woff",
+    "font/woff2",
+    "application/font-sfnt",
+    "application/font-woff",
+    "application/x-truetype-font",
+    "application/vnd.ms-opentype",
+    "application/x-font-ttf",
+    NULL
+};
+
+static int stream_is_font(AVStream* st)
+{
+    const AVDictionaryEntry *tag = NULL;
+
+    if (st->codecpar->codec_type != AVMEDIA_TYPE_ATTACHMENT)
+        return 0;
+
+    tag = av_dict_get(st->metadata, "mimetype", NULL, AV_DICT_MATCH_CASE);
+    if (!tag) return 0;
+    for (int i = 0; font_mimetypes[i]; i++)
+        if (av_strcasecmp(font_mimetypes[i], tag->value) == 0)
+            return 1;
+    return 0;
+}
+
+#define AR(c)  (((c)>>24)&0xFF)
+#define AG(c)  (((c)>>16)&0xFF)
+#define AB(c)  (((c)>> 8)&0xFF)
+#define AA(c)  ((0xFF-(c))&0xFF)
+
+static void set_area_bounds(ASS_Image *image, AVSubtitleArea *area)
+{
+    int x_min = INT_MAX, y_min = INT_MAX;
+    int x_max = 0, y_max = 0;
+    int stride_max = 0;
+
+    for (ASS_Image *img = image; img != NULL; img = img->next)
+    {
+        x_min = FFMIN(x_min, img->dst_x);
+        y_min = FFMIN(y_min, img->dst_y);
+        x_max = FFMAX(x_max, img->dst_x + img->w);
+        y_max = FFMAX(y_max, img->dst_y + img->h);
+        stride_max = FFMAX(stride_max, img->dst_x + img->stride);
+    }
+
+    area->x = x_min;
+    area->y = y_min;
+    area->w = FFALIGN(x_max - x_min, 2);
+    area->h = FFALIGN(y_max - y_min, 2);
+    area->linesize[0] = area->w;    //stride_max - x_min;
+}
+
+static void ass_image_to_area_palletization(Text2GraphicSubContext *context, ASS_Image *image, AVSubtitleArea *area)
+{
+    size_t image_rgba_size;
+    uint8_t *image_rgba;
+
+    set_area_bounds(image, area);
+    av_log(context, AV_LOG_VERBOSE, "set_area_bounds %d,%d %dx%d\n", area->x, area->y, area->w, area->h);
+
+    // Create rgba image
+    image_rgba_size = (area->linesize[0] * area->h) * 4 * sizeof(uint8_t);
+    image_rgba = (uint8_t *)av_mallocz(image_rgba_size);
+    for (; image != NULL; image = image->next)
+    {
+        uint8_t rgba_color[] = {AR(image->color), AG(image->color), AB(image->color), AA(image->color)};
+        int linesize = area->linesize[0] * 4;
+        FFDrawColor color;
+        ff_draw_color(&context->draw_context, &color, rgba_color);
+        ff_blend_mask(&context->draw_context, &color,
+                      &image_rgba, &linesize, area->w, area->h,
+                      image->bitmap, image->stride, image->w, image->h,
+                      3, 0, image->dst_x - area->x, image->dst_y - area->y);
+    }
+
+    area->nb_colors = context->num_colors;
+    area->buf[0] = av_buffer_alloc(image_rgba_size / 4);
+
+    palettize_image(context->palettize_context,
+                    area->w, area->h,
+                    image_rgba, area->linesize[0] * 4,
+                    area->buf[0]->data, area->linesize[0],
+                    &area->pal[0], context->num_colors);
+
+    // Clean up
+    av_free(image_rgba);
+}
+
+static void process_header(const AVFilterContext *link, const AVFrame *frame)
+{
+    Text2GraphicSubContext *s = link->priv;
+    ASS_Track *track = s->track;
+    ASS_Style *style;
+    int sid = 0;
+
+    if (!track)
+        return;
+
+    if (frame && frame->subtitle_header) {
+        char *subtitle_header = (char *)frame->subtitle_header->data;
+        ass_process_codec_private(s->track, subtitle_header, strlen(subtitle_header));
+    }
+    else {
+        char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+        if (!subtitle_header)
+            return;
+
+        ass_process_codec_private(s->track, subtitle_header, strlen(subtitle_header));
+        av_free(subtitle_header);
+    }
+
+    if (!s->track->event_format) {
+        s->track->event_format = av_strdup("ReadOrder, Layer, Style, Name, MarginL, MarginR, MarginV, Effect, Text");
+    }
+
+    if (s->track->n_styles == 0) {
+        sid = ass_alloc_style(track);
+        style = &s->track->styles[sid];
+        style->Name             = av_strdup("Default");
+        style->PrimaryColour    = 0xffffff00;
+        style->SecondaryColour  = 0x00ffff00;
+        style->OutlineColour    = 0x00000000;
+        style->BackColour       = 0x00000080;
+        style->Bold             = 200;
+        style->ScaleX           = 1.0;
+        style->ScaleY           = 1.0;
+        style->Spacing          = 0;
+        style->BorderStyle      = 1;
+        style->Outline          = 2;
+        style->Shadow           = 3;
+        style->Alignment        = 2;
+        track->default_style = sid;
+    }
+
+    s->got_header = 1;
+}
+
+// Main interface functions
+
+static av_cold int init(AVFilterContext *ctx)
+{
+    Text2GraphicSubContext *context = ctx->priv;
+
+    context->library = ass_library_init();
+    ass_set_message_cb(context->library, ass_log, context);
+    ass_set_fonts_dir(context->library, context->fontsdir);
+    ass_set_extract_fonts(context->library, 1);
+
+    if (context->filename) {
+        AVFormatContext *video = NULL;
+        int ret = avformat_open_input(&video, context->filename, NULL, NULL);
+        if (ret < 0) {
+            av_log(ctx, AV_LOG_ERROR, "Unable to open %s\n", context->filename);
+            return ret;
+        }
+        for (int i = 0; i < video->nb_streams; i++) {
+            const AVDictionaryEntry *tag;
+            AVStream *st = video->streams[i];
+            if (!stream_is_font(st)) continue;
+            tag = av_dict_get(st->metadata, "filename", NULL, AV_DICT_MATCH_CASE);
+            if (!tag) continue;
+            av_log(NULL, AV_LOG_DEBUG, "Loading attached font: %s\n", tag->value);
+            ass_add_font(context->library, tag->value,
+                 (char *)st->codecpar->extradata,
+                 st->codecpar->extradata_size);
+        }
+        avformat_close_input(&video);
+    }
+
+    context->renderer = ass_renderer_init(context->library);
+    ass_set_pixel_aspect(context->renderer, 1);
+    ass_set_shaper(context->renderer, 0);
+    ass_set_fonts(context->renderer, NULL, NULL, 1, NULL, 1);
+
+    context->track = ass_new_track(context->library);
+    if (!context->track) {
+        av_log(ctx, AV_LOG_ERROR, "ass_new_track() failed!\n");
+        return AVERROR(EINVAL);
+    }
+
+    ass_set_check_readorder(context->track, 0);
+
+    if (context->force_style) {
+        char **list = NULL;
+        char *temp = NULL;
+        char *ptr = av_strtok(context->force_style, ",", &temp);
+        int i = 0;
+        while (ptr) {
+            av_dynarray_add(&list, &i, ptr);
+            if (!list) {
+                return AVERROR(ENOMEM);
+            }
+            ptr = av_strtok(NULL, ",", &temp);
+        }
+        av_dynarray_add(&list, &i, NULL);
+        if (!list) {
+            return AVERROR(ENOMEM);
+        }
+        ass_set_style_overrides(context->library, list);
+        av_free(list);
+    }
+
+    init_palettizecontext(&context->palettize_context);
+
+    ff_draw_init(&context->draw_context, AV_PIX_FMT_BGRA, FF_DRAW_PROCESS_ALPHA);
+
+    return 0;
+}
+
+static int config_input(AVFilterLink *inlink)
+{
+    const AVFilterContext *ctx = inlink->dst;
+    Text2GraphicSubContext *context = ctx->priv;
+    if (!context->size.width)  context->size.width  = inlink->w;
+    if (!context->size.height) context->size.height = inlink->h;
+    if (!context->size.height || !context->size.width) {
+        av_log(NULL, AV_LOG_ERROR, "A positive height and width are required to render subtitles\n");
+        return AVERROR_EXIT;
+    }
+    ass_set_frame_size(context->renderer, context->size.width, context->size.height);
+    ass_set_storage_size(context->renderer, inlink->w, inlink->h);
+
+    return 0;
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    Text2GraphicSubContext *context = ctx->priv;
+
+    outlink->w = context->size.width;
+    outlink->h = context->size.height;
+
+    return 0;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    const AVFilterContext *ctx = inlink->dst;
+    Text2GraphicSubContext *context = ctx->priv;
+    AVFilterLink *outlink = ctx->outputs[0];
+    AVSubtitleArea *area;
+    int ret;
+    unsigned processed_area_cnt = 0;
+    const int64_t start_time = av_rescale_q(frame->subtitle_timing.start_pts, AV_TIME_BASE_Q, av_make_q(1, 1000));
+    const int64_t duration   = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, av_make_q(1, 1000));
+    ASS_Image *image;
+
+    // Postpone header processing until we receive a frame with content
+    if (!context->got_header && frame->num_subtitle_areas > 0)
+        process_header(ctx, frame);
+
+    if (frame->repeat_sub || frame->num_subtitle_areas == 0) {
+        av_frame_free(&frame);
+        return 0;
+    }
+
+    ret = av_frame_make_writable(frame);
+    if (ret < 0)
+        return ret;
+
+    if (context->stripstyles) {
+        for (unsigned r = 0; r < frame->num_subtitle_areas; r++) {
+
+            area = frame->subtitle_areas[r];
+
+            if (area->ass) {
+                char *tmp = area->ass;
+                area->ass = process_dialog(area->ass);
+
+                if (area->ass) {
+                    av_log(inlink->dst, AV_LOG_DEBUG, "original: %d %s\n", r, tmp);
+                    av_log(inlink->dst, AV_LOG_DEBUG, "stripped: %d %s\n", r, area->ass);
+                }
+
+                av_free(tmp);
+            }
+        }
+    }
+
+    for (unsigned r = 0; r < frame->num_subtitle_areas; r++)
+    {
+        area = frame->subtitle_areas[r];
+        if (area->type != AV_SUBTITLE_FMT_ASS || area->ass == NULL)
+            continue;
+
+        ass_process_chunk(context->track, area->ass, strlen(area->ass), start_time, duration);
+        processed_area_cnt++;
+
+    }
+
+    if (processed_area_cnt == 0) {
+        av_frame_free(&frame);
+        return 0;
+    }
+
+    for (unsigned r = 1; r < frame->num_subtitle_areas; r++)
+        av_free(frame->subtitle_areas[r]);
+
+    frame->num_subtitle_areas = 1;
+    area = frame->subtitle_areas[0];
+
+    image = ass_render_frame(context->renderer, context->track, start_time + duration / 2, NULL);
+    if (image == NULL) {
+        av_log(NULL, AV_LOG_WARNING, "failed to render ass: %s\n", area->ass);
+        return 0;
+    }
+
+    // TODO: Split into multiple bitmaps
+
+    ass_image_to_area_palletization(context, image, area);
+    area->type = AV_SUBTITLE_FMT_BITMAP;
+
+    av_log(NULL, AV_LOG_DEBUG, "successfully rendered ass: %s\n", area->ass);
+
+    frame->width = context->size.width;
+    frame->height = context->size.height;
+    frame->format = AV_SUBTITLE_FMT_BITMAP;
+    return ff_filter_frame(outlink, frame);
+}
+
+static av_cold void uninit(AVFilterContext *ctx)
+{
+    Text2GraphicSubContext *context = ctx->priv;
+    free_palettizecontext(&context->palettize_context);
+    if (context->track) ass_free_track(context->track);
+    if (context->renderer) ass_renderer_done(context->renderer);
+    if (context->library) ass_library_done(context->library);
+    context->track = NULL;
+    context->renderer = NULL;
+    context->library = NULL;
+}
+
+// Copied from sf_graphicsub2text, with formats swapped
+static int query_formats(AVFilterContext *ctx)
+{
+    AVFilterFormats *formats, *formats2;
+    AVFilterLink *inlink = ctx->inputs[0];
+    AVFilterLink *outlink = ctx->outputs[0];
+    static const enum AVSubtitleType in_fmts[] = { AV_SUBTITLE_FMT_ASS, AV_SUBTITLE_FMT_NONE };
+    static const enum AVSubtitleType out_fmts[] = { AV_SUBTITLE_FMT_BITMAP, AV_SUBTITLE_FMT_NONE };
+    int ret;
+
+    /* set input format */
+    formats = ff_make_format_list(in_fmts);
+    if ((ret = ff_formats_ref(formats, &inlink->outcfg.formats)) < 0)
+        return ret;
+
+    /* set output format */
+    formats2 = ff_make_format_list(out_fmts);
+    if ((ret = ff_formats_ref(formats2, &outlink->incfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+// Filter structures
+
+#define OFFSET(x) offsetof(Text2GraphicSubContext, x)
+#define FLAGS (AV_OPT_FLAG_SUBTITLE_PARAM|AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption text2graphicsub_options[] = {
+        { "s",           "output size",                          OFFSET(size),        AV_OPT_TYPE_IMAGE_SIZE,                         .default_val.str = NULL, .flags = FLAGS },
+        { "size",        "output size",                          OFFSET(size),        AV_OPT_TYPE_IMAGE_SIZE,                         .default_val.str = NULL, .flags = FLAGS },
+        { "n",           "number of output colors",              OFFSET(num_colors),  AV_OPT_TYPE_INT,        .min = 2, .max = 256,   .default_val.i64 = 16,   .flags = FLAGS },
+        { "num_colors",  "number of output colors",              OFFSET(num_colors),  AV_OPT_TYPE_INT,        .min = 2, .max = 256,   .default_val.i64 = 16,   .flags = FLAGS },
+        { "ss",          "strip animations and blur styles",     OFFSET(stripstyles), AV_OPT_TYPE_BOOL,       .min = 0, .max = 1,     .default_val.i64 = 1,    .flags = FLAGS },
+        { "stripstyles", "strip animations and blur styles",     OFFSET(stripstyles), AV_OPT_TYPE_BOOL,       .min = 0, .max = 1,     .default_val.i64 = 1,    .flags = FLAGS },
+        { "force_style", "enforce subtitle styles",              OFFSET(force_style), AV_OPT_TYPE_STRING,                             .default_val.str = NULL, .flags = FLAGS },
+        { "f",           "media file from which to load fonts",  OFFSET(filename),    AV_OPT_TYPE_STRING,                             .default_val.str = NULL, .flags = FLAGS },
+        { "filename",    "media file from which to load fonts",  OFFSET(filename),    AV_OPT_TYPE_STRING,                             .default_val.str = NULL, .flags = FLAGS },
+        { "fd",          "fonts directory",                      OFFSET(fontsdir),    AV_OPT_TYPE_STRING,                             .default_val.str = NULL, .flags = FLAGS },
+        { "fontsdir",    "fonts directory",                      OFFSET(fontsdir),    AV_OPT_TYPE_STRING,                             .default_val.str = NULL, .flags = FLAGS },
+        { .name =  NULL }
+};
+
+static const AVClass text2graphicsub_class = {
+        .class_name       = "text2graphicsub",
+        .item_name        = av_default_item_name,
+        .option           = text2graphicsub_options,
+        .version          = LIBAVUTIL_VERSION_INT,
+        .category         = AV_CLASS_CATEGORY_FILTER,
+};
+
+static const AVFilterPad inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .filter_frame = filter_frame,
+        .config_props = config_input,
+    },
+};
+
+static const AVFilterPad outputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .config_props = config_output,
+    },
+};
+
+const AVFilter ff_sf_text2graphicsub = {
+        .name            = "text2graphicsub",
+        .description     = NULL_IF_CONFIG_SMALL("Convert text subtitles to bitmap subtitles."),
+        .init            = init,
+        .uninit          = uninit,
+        .priv_size       = sizeof(Text2GraphicSubContext),
+        .priv_class      = &text2graphicsub_class,
+        FILTER_INPUTS(inputs),
+        FILTER_OUTPUTS(outputs),
+        FILTER_QUERY_FUNC(query_formats)
+};
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v5 22/25] avfilter/snull, strim: Add snull and strim filters
  2022-06-25  9:57       ` [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022 ffmpegagent
                           ` (20 preceding siblings ...)
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 21/25] avfilter/text2graphicsub: Added text2graphicsub subtitle filter tcoza
@ 2022-06-25  9:57         ` softworkz
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 23/25] avcodec/subtitles: Migrate subtitle encoders to frame-based API softworkz
                           ` (5 subsequent siblings)
  27 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-25  9:57 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 configure                |  2 +-
 libavfilter/Makefile     |  2 ++
 libavfilter/allfilters.c |  2 ++
 libavfilter/sf_snull.c   | 50 ++++++++++++++++++++++++++++++++++++++++
 libavfilter/trim.c       | 46 +++++++++++++++++++++++++++++++++++-
 5 files changed, 100 insertions(+), 2 deletions(-)
 create mode 100644 libavfilter/sf_snull.c

diff --git a/configure b/configure
index f74077b439..285bdf958e 100755
--- a/configure
+++ b/configure
@@ -3817,7 +3817,7 @@ avutil_extralibs="d3d11va_extralibs nanosleep_extralibs pthreads_extralibs vaapi
 # programs
 ffmpeg_deps="avcodec avfilter avformat"
 ffmpeg_select="aformat_filter anull_filter atrim_filter format_filter
-               hflip_filter null_filter
+               snull_filter strim_filter hflip_filter null_filter
                transpose_filter trim_filter vflip_filter"
 ffmpeg_suggest="ole32 psapi shell32"
 ffplay_deps="avcodec avformat swscale swresample sdl2"
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index adbc16a5a9..697eee57d2 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -581,7 +581,9 @@ OBJS-$(CONFIG_NULLSINK_FILTER)               += vsink_nullsink.o
 # subtitle filters
 OBJS-$(CONFIG_CENSOR_FILTER)                 += sf_textmod.o
 OBJS-$(CONFIG_SHOW_SPEAKER_FILTER)           += sf_textmod.o
+OBJS-$(CONFIG_SNULL_FILTER)                  += sf_snull.o
 OBJS-$(CONFIG_SPLITCC_FILTER)                += sf_splitcc.o
+OBJS-$(CONFIG_STRIM_FILTER)                  += trim.o
 OBJS-$(CONFIG_STRIPSTYLES_FILTER)            += sf_stripstyles.o
 OBJS-$(CONFIG_SUBFEED_FILTER)                += sf_subfeed.o
 OBJS-$(CONFIG_SUBSCALE_FILTER)               += sf_subscale.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 82297e9657..fa7bee3bd8 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -573,7 +573,9 @@ extern const AVFilter ff_avsrc_movie;
 extern const AVFilter ff_sf_censor;
 extern const AVFilter ff_sf_graphicsub2text;
 extern const AVFilter ff_sf_showspeaker;
+extern const AVFilter ff_sf_snull;
 extern const AVFilter ff_sf_splitcc;
+extern const AVFilter ff_sf_strim;
 extern const AVFilter ff_sf_stripstyles;
 extern const AVFilter ff_sf_subfeed;
 extern const AVFilter ff_sf_subscale;
diff --git a/libavfilter/sf_snull.c b/libavfilter/sf_snull.c
new file mode 100644
index 0000000000..064c8d5c3a
--- /dev/null
+++ b/libavfilter/sf_snull.c
@@ -0,0 +1,50 @@
+/*
+ * 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
+ * null subtitle filter
+ */
+
+#include "avfilter.h"
+#include "internal.h"
+#include "libavutil/internal.h"
+
+static const AVFilterPad avfilter_sf_snull_inputs[] = {
+    {
+        .name = "default",
+        .type = AVMEDIA_TYPE_SUBTITLE,
+    },
+};
+
+static const AVFilterPad avfilter_sf_snull_outputs[] = {
+    {
+        .name = "default",
+        .type = AVMEDIA_TYPE_SUBTITLE,
+    },
+};
+
+const AVFilter ff_sf_snull = {
+    .name          = "snull",
+    .description   = NULL_IF_CONFIG_SMALL("Pass the source unchanged to the output."),
+    .flags         = AVFILTER_FLAG_METADATA_ONLY,
+    FILTER_INPUTS(avfilter_sf_snull_inputs),
+    FILTER_OUTPUTS(avfilter_sf_snull_outputs),
+};
diff --git a/libavfilter/trim.c b/libavfilter/trim.c
index ee6e821cd2..5e3d2ba9f8 100644
--- a/libavfilter/trim.c
+++ b/libavfilter/trim.c
@@ -83,7 +83,7 @@ static int config_input(AVFilterLink *inlink)
 {
     AVFilterContext *ctx = inlink->dst;
     TrimContext       *s = ctx->priv;
-    AVRational tb = (inlink->type == AVMEDIA_TYPE_VIDEO) ?
+    AVRational tb = (inlink->type != AVMEDIA_TYPE_AUDIO) ?
                      inlink->time_base : (AVRational){ 1, inlink->sample_rate };
 
     if (s->start_time != INT64_MAX) {
@@ -369,3 +369,47 @@ const AVFilter ff_af_atrim = {
     FILTER_OUTPUTS(atrim_outputs),
 };
 #endif // CONFIG_ATRIM_FILTER
+
+#if CONFIG_STRIM_FILTER
+
+#define FLAGS (AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
+static const AVOption strim_options[] = {
+    COMMON_OPTS
+    { "start_frame", "Number of the first frame that should be passed "
+        "to the output",                                                 OFFSET(start_frame), AV_OPT_TYPE_INT64,  { .i64 = -1 },       -1, INT64_MAX, FLAGS },
+    { "end_frame",   "Number of the first frame that should be dropped "
+        "again",                                                         OFFSET(end_frame),   AV_OPT_TYPE_INT64,  { .i64 = INT64_MAX }, 0, INT64_MAX, FLAGS },
+    { NULL }
+};
+#undef FLAGS
+
+AVFILTER_DEFINE_CLASS(strim);
+
+static const AVFilterPad strim_inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .filter_frame = trim_filter_frame,
+        .config_props = config_input,
+    },
+};
+
+static const AVFilterPad strim_outputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+    },
+};
+
+const AVFilter ff_sf_strim = {
+    .name        = "strim",
+    .description = NULL_IF_CONFIG_SMALL("Pick one continuous section from the input, drop the rest."),
+    .init        = init,
+    .priv_size   = sizeof(TrimContext),
+    .priv_class  = &strim_class,
+    .flags       = AVFILTER_FLAG_METADATA_ONLY,
+    FILTER_INPUTS(strim_inputs),
+    FILTER_OUTPUTS(strim_outputs),
+};
+#endif // CONFIG_STRIM_FILTER
+
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v5 23/25] avcodec/subtitles: Migrate subtitle encoders to frame-based API
  2022-06-25  9:57       ` [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022 ffmpegagent
                           ` (21 preceding siblings ...)
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 22/25] avfilter/snull, strim: Add snull and strim filters softworkz
@ 2022-06-25  9:57         ` softworkz
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 24/25] fftools/ffmpeg: Introduce subtitle filtering and new frame-based subtitle encoding softworkz
                           ` (4 subsequent siblings)
  27 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-25  9:57 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

and provide a compatibility shim for the legacy api

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/assenc.c         | 189 ++++++++++++++++++++++++++++++------
 libavcodec/avcodec.h        |   5 +-
 libavcodec/codec_internal.h |  12 ---
 libavcodec/dvbsubenc.c      |  96 ++++++++++--------
 libavcodec/dvdsubenc.c      | 102 +++++++++++--------
 libavcodec/encode.c         |  61 +++++++++++-
 libavcodec/movtextenc.c     | 114 ++++++++++++++++------
 libavcodec/srtenc.c         | 108 ++++++++++++++-------
 libavcodec/tests/avcodec.c  |   5 +-
 libavcodec/ttmlenc.c        | 101 ++++++++++++++-----
 libavcodec/utils.c          |   1 -
 libavcodec/webvttenc.c      |  86 +++++++++++-----
 libavcodec/xsubenc.c        |  88 ++++++++++-------
 13 files changed, 688 insertions(+), 280 deletions(-)

diff --git a/libavcodec/assenc.c b/libavcodec/assenc.c
index 391d690133..5067244e77 100644
--- a/libavcodec/assenc.c
+++ b/libavcodec/assenc.c
@@ -25,69 +25,194 @@
 
 #include "avcodec.h"
 #include "codec_internal.h"
+#include "encode.h"
 #include "libavutil/ass_internal.h"
 #include "libavutil/avstring.h"
 #include "libavutil/internal.h"
 #include "libavutil/mem.h"
 
+typedef struct {
+    AVCodecContext *avctx;
+    AVFrame* current_frame;
+    int have_frame;
+    int current_area;
+} AssEncContext;
+
+static void check_write_header(AVCodecContext* avctx, const AVFrame* frame)
+{
+    if (avctx->extradata_size)
+        return;
+
+    if (frame->subtitle_header && frame->subtitle_header->size > 0) {
+        const char* subtitle_header = (char*)frame->subtitle_header->data;
+        avctx->extradata_size = strlen(subtitle_header);
+        avctx->extradata = av_mallocz(frame->subtitle_header->size + 1);
+        memcpy(avctx->extradata, subtitle_header, avctx->extradata_size);
+        avctx->extradata[avctx->extradata_size] = 0;
+    }
+
+    if (!avctx->extradata_size) {
+        const char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+        if (!subtitle_header)
+            return;
+
+        avctx->extradata_size = strlen(subtitle_header);
+        avctx->extradata = av_mallocz(avctx->extradata_size + 1);
+        memcpy(avctx->extradata, subtitle_header, avctx->extradata_size);
+        avctx->extradata[avctx->extradata_size] = 0;
+        av_freep(&subtitle_header);
+    }
+}
+
 static av_cold int ass_encode_init(AVCodecContext *avctx)
 {
-    avctx->extradata = av_malloc(avctx->subtitle_header_size + 1);
-    if (!avctx->extradata)
-        return AVERROR(ENOMEM);
-    memcpy(avctx->extradata, avctx->subtitle_header, avctx->subtitle_header_size);
-    avctx->extradata_size = avctx->subtitle_header_size;
-    avctx->extradata[avctx->extradata_size] = 0;
+    AssEncContext *s = avctx->priv_data;
+
+    if (avctx->subtitle_header_size) {
+        avctx->extradata = av_malloc(avctx->subtitle_header_size + 1);
+        if (!avctx->extradata)
+            return AVERROR(ENOMEM);
+        memcpy(avctx->extradata, avctx->subtitle_header, avctx->subtitle_header_size);
+        avctx->extradata_size                   = avctx->subtitle_header_size;
+        avctx->extradata[avctx->extradata_size] = 0;
+    }
+
+    s->current_frame = av_frame_alloc();
+    return 0;
+}
+
+static av_cold int ass_encode_close(AVCodecContext *avctx)
+{
+    AssEncContext *s = avctx->priv_data;
+    av_frame_free(&s->current_frame);
     return 0;
 }
 
-static int ass_encode_frame(AVCodecContext *avctx,
-                            unsigned char *buf, int bufsize,
-                            const AVSubtitle *sub)
+////static int ass_encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+////                            const AVFrame* frame, int* got_packet)
+////{
+////    int ret;
+////    size_t req_len = 0, total_len = 0;
+////
+////    check_write_header(avctx, frame);
+////
+////    for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+////        const char *ass = frame->subtitle_areas[i]->ass;
+////
+////        if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) {
+////            av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
+////            return AVERROR(EINVAL);
+////        }
+////
+////        if (ass)
+////            req_len += strlen(ass);
+////    }
+////
+////    ret = ff_get_encode_buffer(avctx, avpkt, req_len + 1, 0);
+////    if (ret < 0) {
+////        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+////        return ret;
+////    }
+////
+////    for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+////        const char *ass = frame->subtitle_areas[i]->ass;
+////
+////        if (ass) {
+////            size_t len = av_strlcpy((char *)avpkt->data + total_len, ass, avpkt->size - total_len);
+////            total_len += len;
+////        }
+////    }
+////
+////    avpkt->size = total_len;
+////    *got_packet = total_len > 0;
+////
+////    return 0;
+////}
+
+static int ass_receive_packet(AVCodecContext *avctx, AVPacket *avpkt)
 {
-    int i, len, total_len = 0;
+    AssEncContext *s = avctx->priv_data;
+    int ret;
+
+    if (!s->have_frame) {
+        s->current_area = 0;
+        ret = ff_encode_get_frame(avctx, s->current_frame);
+
+        if (ret < 0) {
+            av_frame_unref(s->current_frame);
+            return ret;
+        }
+
+        s->have_frame = 1;
+    }
 
-    for (i=0; i<sub->num_rects; i++) {
-        const char *ass = sub->rects[i]->ass;
+    check_write_header(avctx, s->current_frame);
 
-        if (sub->rects[i]->type != SUBTITLE_ASS) {
-            av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
+    if (s->current_frame->repeat_sub) {
+        av_frame_unref(s->current_frame);
+        s->have_frame = 0;
+        return AVERROR(EAGAIN);
+    }
+
+    if (s->current_area < s->current_frame->num_subtitle_areas) {
+        const AVSubtitleArea *area = s->current_frame->subtitle_areas[s->current_area];
+        const char *ass = area->ass;
+
+        if (area->type != AV_SUBTITLE_FMT_ASS) {
+            av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
             return AVERROR(EINVAL);
         }
 
-        len = av_strlcpy(buf+total_len, ass, bufsize-total_len);
+        if (ass) {
+            size_t len = strlen(ass);
+
+            ret = ff_get_encode_buffer(avctx, avpkt, len + 1, 0);
+            if (ret < 0) {
+                av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+                return ret;
+            }
+
+            len = av_strlcpy((char *)avpkt->data, ass, avpkt->size);
 
-        if (len > bufsize-total_len-1) {
-            av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n");
-            return AVERROR_BUFFER_TOO_SMALL;
+            avpkt->size = len;
         }
 
-        total_len += len;
+        s->current_area++;
     }
 
-    return total_len;
+    if (s->current_area < s->current_frame->num_subtitle_areas)
+        return 0;
+
+    av_frame_unref(s->current_frame);
+    s->have_frame = 0;
+
+    return 0;
 }
 
 #if CONFIG_SSA_ENCODER
 const FFCodec ff_ssa_encoder = {
-    .p.name       = "ssa",
-    .p.long_name  = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"),
-    .p.type       = AVMEDIA_TYPE_SUBTITLE,
-    .p.id         = AV_CODEC_ID_ASS,
-    .init         = ass_encode_init,
-    FF_CODEC_ENCODE_SUB_CB(ass_encode_frame),
+    .p.name           = "ssa",
+    .p.long_name      = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"),
+    .p.type           = AVMEDIA_TYPE_SUBTITLE,
+    .p.id             = AV_CODEC_ID_ASS,
+    .priv_data_size = sizeof(AssEncContext),
+    .init           = ass_encode_init,
+    .close          = ass_encode_close,
+    FF_CODEC_RECEIVE_PACKET_CB(ass_receive_packet),
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
 };
 #endif
 
 #if CONFIG_ASS_ENCODER
 const FFCodec ff_ass_encoder = {
-    .p.name       = "ass",
-    .p.long_name  = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"),
-    .p.type       = AVMEDIA_TYPE_SUBTITLE,
-    .p.id         = AV_CODEC_ID_ASS,
-    .init         = ass_encode_init,
-    FF_CODEC_ENCODE_SUB_CB(ass_encode_frame),
+    .p.name           = "ass",
+    .p.long_name      = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"),
+    .p.type           = AVMEDIA_TYPE_SUBTITLE,
+    .priv_data_size = sizeof(AssEncContext),
+    .p.id             = AV_CODEC_ID_ASS,
+    .init           = ass_encode_init,
+    .close          = ass_encode_close,
+    FF_CODEC_RECEIVE_PACKET_CB(ass_receive_packet),
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
 };
 #endif
diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index de87b0406b..8a8efe6e4b 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -3014,10 +3014,13 @@ void av_parser_close(AVCodecParserContext *s);
  * @{
  */
 
+ /**
+  * @deprecated Use @ref avcodec_encode_subtitle2() instead.
+  */
+attribute_deprecated
 int avcodec_encode_subtitle(AVCodecContext *avctx, uint8_t *buf, int buf_size,
                             const AVSubtitle *sub);
 
-
 /**
  * @}
  */
diff --git a/libavcodec/codec_internal.h b/libavcodec/codec_internal.h
index 5df286ce52..7223f41757 100644
--- a/libavcodec/codec_internal.h
+++ b/libavcodec/codec_internal.h
@@ -101,9 +101,6 @@ enum FFCodecType {
     /* The codec is an encoder using the encode callback;
      * audio and video codecs only. */
     FF_CODEC_CB_TYPE_ENCODE,
-    /* The codec is an encoder using the encode_sub callback;
-     * subtitle codecs only. */
-    FF_CODEC_CB_TYPE_ENCODE_SUB,
     /* The codec is an encoder using the receive_packet callback;
      * audio and video codecs only. */
     FF_CODEC_CB_TYPE_RECEIVE_PACKET,
@@ -206,12 +203,6 @@ typedef struct FFCodec {
          */
         int (*encode)(struct AVCodecContext *avctx, struct AVPacket *avpkt,
                       const struct AVFrame *frame, int *got_packet_ptr);
-        /**
-         * Encode subtitles to a raw buffer.
-         * cb is in this state if cb_type is FF_CODEC_CB_TYPE_ENCODE_SUB.
-         */
-        int (*encode_sub)(struct AVCodecContext *avctx, uint8_t *buf,
-                          int buf_size, const struct AVSubtitle *sub);
         /**
          * Encode API with decoupled frame/packet dataflow.
          * cb is in this state if cb_type is FF_CODEC_CB_TYPE_RECEIVE_PACKET.
@@ -263,9 +254,6 @@ typedef struct FFCodec {
 #define FF_CODEC_ENCODE_CB(func)                          \
     .cb_type           = FF_CODEC_CB_TYPE_ENCODE,         \
     .cb.encode         = (func)
-#define FF_CODEC_ENCODE_SUB_CB(func)                      \
-    .cb_type           = FF_CODEC_CB_TYPE_ENCODE_SUB,     \
-    .cb.encode_sub     = (func)
 #define FF_CODEC_RECEIVE_PACKET_CB(func)                  \
     .cb_type           = FF_CODEC_CB_TYPE_RECEIVE_PACKET, \
     .cb.receive_packet = (func)
diff --git a/libavcodec/dvbsubenc.c b/libavcodec/dvbsubenc.c
index 06087b058d..3f4655bebe 100644
--- a/libavcodec/dvbsubenc.c
+++ b/libavcodec/dvbsubenc.c
@@ -21,6 +21,7 @@
 #include "avcodec.h"
 #include "bytestream.h"
 #include "codec_internal.h"
+#include "encode.h"
 #include "libavutil/colorspace.h"
 
 typedef struct DVBSubtitleContext {
@@ -269,21 +270,29 @@ static int dvb_encode_rle8(uint8_t **pq, int buf_size,
     return len;
 }
 
-static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
-                         const AVSubtitle *h)
+static int dvbsub_encode(AVCodecContext* avctx, AVPacket* avpkt,
+                         const AVFrame* frame, int* got_packet)
 {
     DVBSubtitleContext *s = avctx->priv_data;
     uint8_t *q, *pseg_len;
     int page_id, region_id, clut_id, object_id, i, bpp_index, page_state;
-
-
-    q = outbuf;
+    size_t buf_size;
+    int ret;
 
     page_id = 1;
 
-    if (h->num_rects && !h->rects)
+    if (frame->num_subtitle_areas && !frame->subtitle_areas)
         return AVERROR(EINVAL);
 
+    ret = ff_get_encode_buffer(avctx, avpkt, 1024 * 1024, 0);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+        return ret;
+    }
+
+    buf_size = avpkt->size;
+    q = avpkt->data;
+
     if (avctx->width > 0 && avctx->height > 0) {
         if (buf_size < 11)
             return AVERROR_BUFFER_TOO_SMALL;
@@ -302,7 +311,7 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
 
     /* page composition segment */
 
-    if (buf_size < 8 + h->num_rects * 6)
+    if (buf_size < 8 + frame->num_subtitle_areas * 6)
         return AVERROR_BUFFER_TOO_SMALL;
     *q++ = 0x0f; /* sync_byte */
     *q++ = 0x10; /* segment_type */
@@ -314,30 +323,30 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
     /* page_version = 0 + page_state */
     *q++ = (s->object_version << 4) | (page_state << 2) | 3;
 
-    for (region_id = 0; region_id < h->num_rects; region_id++) {
+    for (region_id = 0; region_id < frame->num_subtitle_areas; region_id++) {
         *q++ = region_id;
         *q++ = 0xff; /* reserved */
-        bytestream_put_be16(&q, h->rects[region_id]->x); /* left pos */
-        bytestream_put_be16(&q, h->rects[region_id]->y); /* top pos */
+        bytestream_put_be16(&q, frame->subtitle_areas[region_id]->x); /* left pos */
+        bytestream_put_be16(&q, frame->subtitle_areas[region_id]->y); /* top pos */
     }
 
     bytestream_put_be16(&pseg_len, q - pseg_len - 2);
-    buf_size -= 8 + h->num_rects * 6;
+    buf_size -= 8 + frame->num_subtitle_areas * 6;
 
-    if (h->num_rects) {
-        for (clut_id = 0; clut_id < h->num_rects; clut_id++) {
-            if (buf_size < 6 + h->rects[clut_id]->nb_colors * 6)
+    if (frame->num_subtitle_areas) {
+        for (clut_id = 0; clut_id < frame->num_subtitle_areas; clut_id++) {
+            if (buf_size < 6 + frame->subtitle_areas[clut_id]->nb_colors * 6)
                 return AVERROR_BUFFER_TOO_SMALL;
 
             /* CLUT segment */
 
-            if (h->rects[clut_id]->nb_colors <= 4) {
+            if (frame->subtitle_areas[clut_id]->nb_colors <= 4) {
                 /* 2 bpp, some decoders do not support it correctly */
                 bpp_index = 0;
-            } else if (h->rects[clut_id]->nb_colors <= 16) {
+            } else if (frame->subtitle_areas[clut_id]->nb_colors <= 16) {
                 /* 4 bpp, standard encoding */
                 bpp_index = 1;
-            } else if (h->rects[clut_id]->nb_colors <= 256) {
+            } else if (frame->subtitle_areas[clut_id]->nb_colors <= 256) {
                 /* 8 bpp, standard encoding */
                 bpp_index = 2;
             } else {
@@ -354,12 +363,12 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
             *q++ = clut_id;
             *q++ = (0 << 4) | 0xf; /* version = 0 */
 
-            for(i = 0; i < h->rects[clut_id]->nb_colors; i++) {
+            for(i = 0; i < frame->subtitle_areas[clut_id]->nb_colors; i++) {
                 *q++ = i; /* clut_entry_id */
                 *q++ = (1 << (7 - bpp_index)) | (0xf << 1) | 1; /* 2 bits/pixel full range */
                 {
                     int a, r, g, b;
-                    uint32_t x= ((uint32_t*)h->rects[clut_id]->data[1])[i];
+                    uint32_t x= ((uint32_t*)frame->subtitle_areas[clut_id]->pal)[i];
                     a = (x >> 24) & 0xff;
                     r = (x >> 16) & 0xff;
                     g = (x >>  8) & 0xff;
@@ -373,22 +382,22 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
             }
 
             bytestream_put_be16(&pseg_len, q - pseg_len - 2);
-            buf_size -= 6 + h->rects[clut_id]->nb_colors * 6;
+            buf_size -= 6 + frame->subtitle_areas[clut_id]->nb_colors * 6;
         }
 
-        if (buf_size < h->num_rects * 22)
+        if (buf_size < frame->num_subtitle_areas * 22)
             return AVERROR_BUFFER_TOO_SMALL;
-        for (region_id = 0; region_id < h->num_rects; region_id++) {
+        for (region_id = 0; region_id < frame->num_subtitle_areas; region_id++) {
 
             /* region composition segment */
 
-            if (h->rects[region_id]->nb_colors <= 4) {
+            if (frame->subtitle_areas[region_id]->nb_colors <= 4) {
                 /* 2 bpp, some decoders do not support it correctly */
                 bpp_index = 0;
-            } else if (h->rects[region_id]->nb_colors <= 16) {
+            } else if (frame->subtitle_areas[region_id]->nb_colors <= 16) {
                 /* 4 bpp, standard encoding */
                 bpp_index = 1;
-            } else if (h->rects[region_id]->nb_colors <= 256) {
+            } else if (frame->subtitle_areas[region_id]->nb_colors <= 256) {
                 /* 8 bpp, standard encoding */
                 bpp_index = 2;
             } else {
@@ -402,8 +411,8 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
             q += 2; /* segment length */
             *q++ = region_id;
             *q++ = (s->object_version << 4) | (0 << 3) | 0x07; /* version , no fill */
-            bytestream_put_be16(&q, h->rects[region_id]->w); /* region width */
-            bytestream_put_be16(&q, h->rects[region_id]->h); /* region height */
+            bytestream_put_be16(&q, frame->subtitle_areas[region_id]->w); /* region width */
+            bytestream_put_be16(&q, frame->subtitle_areas[region_id]->h); /* region height */
             *q++ = ((1 + bpp_index) << 5) | ((1 + bpp_index) << 2) | 0x03;
             *q++ = region_id; /* clut_id == region_id */
             *q++ = 0; /* 8 bit fill colors */
@@ -417,9 +426,9 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
 
             bytestream_put_be16(&pseg_len, q - pseg_len - 2);
         }
-        buf_size -= h->num_rects * 22;
+        buf_size -= frame->num_subtitle_areas * 22;
 
-        for (object_id = 0; object_id < h->num_rects; object_id++) {
+        for (object_id = 0; object_id < frame->num_subtitle_areas; object_id++) {
             int (*dvb_encode_rle)(uint8_t **pq, int buf_size,
                                   const uint8_t *bitmap, int linesize,
                                   int w, int h);
@@ -428,13 +437,13 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
                 return AVERROR_BUFFER_TOO_SMALL;
 
             /* bpp_index maths */
-            if (h->rects[object_id]->nb_colors <= 4) {
+            if (frame->subtitle_areas[object_id]->nb_colors <= 4) {
                 /* 2 bpp, some decoders do not support it correctly */
                 dvb_encode_rle = dvb_encode_rle2;
-            } else if (h->rects[object_id]->nb_colors <= 16) {
+            } else if (frame->subtitle_areas[object_id]->nb_colors <= 16) {
                 /* 4 bpp, standard encoding */
                 dvb_encode_rle = dvb_encode_rle4;
-            } else if (h->rects[object_id]->nb_colors <= 256) {
+            } else if (frame->subtitle_areas[object_id]->nb_colors <= 256) {
                 /* 8 bpp, standard encoding */
                 dvb_encode_rle = dvb_encode_rle8;
             } else {
@@ -464,19 +473,19 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
 
                 top_ptr = q;
                 ret = dvb_encode_rle(&q, buf_size,
-                                     h->rects[object_id]->data[0],
-                                     h->rects[object_id]->w * 2,
-                                     h->rects[object_id]->w,
-                                     h->rects[object_id]->h >> 1);
+                                     frame->subtitle_areas[object_id]->buf[0]->data,
+                                     frame->subtitle_areas[object_id]->w * 2,
+                                     frame->subtitle_areas[object_id]->w,
+                                     frame->subtitle_areas[object_id]->h >> 1);
                 if (ret < 0)
                     return ret;
                 buf_size -= ret;
                 bottom_ptr = q;
                 ret = dvb_encode_rle(&q, buf_size,
-                                     h->rects[object_id]->data[0] + h->rects[object_id]->w,
-                                     h->rects[object_id]->w * 2,
-                                     h->rects[object_id]->w,
-                                     h->rects[object_id]->h >> 1);
+                                     frame->subtitle_areas[object_id]->buf[0]->data + frame->subtitle_areas[object_id]->w,
+                                     frame->subtitle_areas[object_id]->w * 2,
+                                     frame->subtitle_areas[object_id]->w,
+                                     frame->subtitle_areas[object_id]->h >> 1);
                 if (ret < 0)
                     return ret;
                 buf_size -= ret;
@@ -503,7 +512,10 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
     buf_size -= 6;
 
     s->object_version = (s->object_version + 1) & 0xf;
-    return q - outbuf;
+    avpkt->size = q - avpkt->data;
+    *got_packet = 1;
+
+    return 0;
 }
 
 const FFCodec ff_dvbsub_encoder = {
@@ -512,5 +524,5 @@ const FFCodec ff_dvbsub_encoder = {
     .p.type         = AVMEDIA_TYPE_SUBTITLE,
     .p.id           = AV_CODEC_ID_DVB_SUBTITLE,
     .priv_data_size = sizeof(DVBSubtitleContext),
-    FF_CODEC_ENCODE_SUB_CB(dvbsub_encode),
+    FF_CODEC_ENCODE_CB(dvbsub_encode),
 };
diff --git a/libavcodec/dvdsubenc.c b/libavcodec/dvdsubenc.c
index 24da94faee..d8a86ee098 100644
--- a/libavcodec/dvdsubenc.c
+++ b/libavcodec/dvdsubenc.c
@@ -21,6 +21,7 @@
 #include "avcodec.h"
 #include "bytestream.h"
 #include "codec_internal.h"
+#include "encode.h"
 #include "internal.h"
 #include "libavutil/avassert.h"
 #include "libavutil/bprint.h"
@@ -115,15 +116,14 @@ static int color_distance(uint32_t a, uint32_t b)
  * Count colors used in a rectangle, quantizing alpha and grouping by
  * nearest global palette entry.
  */
-static void count_colors(AVCodecContext *avctx, unsigned hits[33],
-                         const AVSubtitleRect *r)
+static void count_colors(const AVCodecContext *avctx, unsigned hits[33],
+                         const AVSubtitleArea *r)
 {
     DVDSubtitleContext *dvdc = avctx->priv_data;
     unsigned count[256] = { 0 };
-    uint32_t *palette = (uint32_t *)r->data[1];
     uint32_t color;
     int x, y, i, j, match, d, best_d, av_uninit(best_j);
-    uint8_t *p = r->data[0];
+    uint8_t *p = r->buf[0]->data;
 
     for (y = 0; y < r->h; y++) {
         for (x = 0; x < r->w; x++)
@@ -133,7 +133,7 @@ static void count_colors(AVCodecContext *avctx, unsigned hits[33],
     for (i = 0; i < 256; i++) {
         if (!count[i]) /* avoid useless search */
             continue;
-        color = palette[i];
+        color = r->pal[i];
         /* 0: transparent, 1-16: semi-transparent, 17-33 opaque */
         match = color < 0x33000000 ? 0 : color < 0xCC000000 ? 1 : 17;
         if (match) {
@@ -233,13 +233,13 @@ static void build_color_map(AVCodecContext *avctx, int cmap[],
     }
 }
 
-static void copy_rectangle(AVSubtitleRect *dst, AVSubtitleRect *src, int cmap[])
+static void copy_rectangle(AVSubtitleArea*dst, AVSubtitleArea *src, int cmap[])
 {
     int x, y;
     uint8_t *p, *q;
 
-    p = src->data[0];
-    q = dst->data[0] + (src->x - dst->x) +
+    p = src->buf[0]->data;
+    q = dst->buf[0]->data + (src->x - dst->x) +
                             (src->y - dst->y) * dst->linesize[0];
     for (y = 0; y < src->h; y++) {
         for (x = 0; x < src->w; x++)
@@ -249,51 +249,57 @@ static void copy_rectangle(AVSubtitleRect *dst, AVSubtitleRect *src, int cmap[])
     }
 }
 
-static int encode_dvd_subtitles(AVCodecContext *avctx,
-                                uint8_t *outbuf, int outbuf_size,
-                                const AVSubtitle *h)
+static int encode_dvd_subtitles(AVCodecContext* avctx, AVPacket* avpkt,
+                                const AVFrame* frame, int* got_packet)
 {
     DVDSubtitleContext *dvdc = avctx->priv_data;
     uint8_t *q, *qq;
     int offset1, offset2;
-    int i, rects = h->num_rects, ret;
+    int ret = 0;
+    unsigned i, rects = frame->num_subtitle_areas;
     unsigned global_palette_hits[33] = { 0 };
     int cmap[256];
     int out_palette[4];
     int out_alpha[4];
-    AVSubtitleRect vrect;
-    uint8_t *vrect_data = NULL;
+    AVSubtitleArea vrect;
+    uint8_t* vrect_data = NULL, *outbuf;
     int x2, y2;
     int forced = 0;
+    int outbuf_size;
+    int64_t duration_ms;
 
-    if (rects == 0 || !h->rects)
+    if (rects == 0)
+        return 0;
+
+    if (!frame->subtitle_areas)
         return AVERROR(EINVAL);
+
     for (i = 0; i < rects; i++)
-        if (h->rects[i]->type != AV_SUBTITLE_FMT_BITMAP) {
+        if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_BITMAP) {
             av_log(avctx, AV_LOG_ERROR, "Bitmap subtitle required\n");
             return AVERROR(EINVAL);
         }
     /* Mark this subtitle forced if any of the rectangles is forced. */
     for (i = 0; i < rects; i++)
-        if ((h->rects[i]->flags & AV_SUBTITLE_FLAG_FORCED) != 0) {
+        if ((frame->subtitle_areas[i]->flags & AV_SUBTITLE_FLAG_FORCED) != 0) {
             forced = 1;
             break;
         }
 
-    vrect = *h->rects[0];
+    vrect = *frame->subtitle_areas[0];
 
     if (rects > 1) {
         /* DVD subtitles can have only one rectangle: build a virtual
            rectangle containing all actual rectangles.
            The data of the rectangles will be copied later, when the palette
            is decided, because the rectangles may have different palettes. */
-        int xmin = h->rects[0]->x, xmax = xmin + h->rects[0]->w;
-        int ymin = h->rects[0]->y, ymax = ymin + h->rects[0]->h;
+        int xmin = frame->subtitle_areas[0]->x, xmax = xmin + frame->subtitle_areas[0]->w;
+        int ymin = frame->subtitle_areas[0]->y, ymax = ymin + frame->subtitle_areas[0]->h;
         for (i = 1; i < rects; i++) {
-            xmin = FFMIN(xmin, h->rects[i]->x);
-            ymin = FFMIN(ymin, h->rects[i]->y);
-            xmax = FFMAX(xmax, h->rects[i]->x + h->rects[i]->w);
-            ymax = FFMAX(ymax, h->rects[i]->y + h->rects[i]->h);
+            xmin = FFMIN(xmin, frame->subtitle_areas[i]->x);
+            ymin = FFMIN(ymin, frame->subtitle_areas[i]->y);
+            xmax = FFMAX(xmax, frame->subtitle_areas[i]->x + frame->subtitle_areas[i]->w);
+            ymax = FFMAX(ymax, frame->subtitle_areas[i]->y + frame->subtitle_areas[i]->h);
         }
         vrect.x = xmin;
         vrect.y = ymin;
@@ -305,27 +311,29 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
         /* Count pixels outside the virtual rectangle as transparent */
         global_palette_hits[0] = vrect.w * vrect.h;
         for (i = 0; i < rects; i++)
-            global_palette_hits[0] -= h->rects[i]->w * h->rects[i]->h;
+            global_palette_hits[0] -= frame->subtitle_areas[i]->w * frame->subtitle_areas[i]->h;
     }
 
     for (i = 0; i < rects; i++)
-        count_colors(avctx, global_palette_hits, h->rects[i]);
+        count_colors(avctx, global_palette_hits, frame->subtitle_areas[i]);
     select_palette(avctx, out_palette, out_alpha, global_palette_hits);
 
     if (rects > 1) {
-        if (!(vrect_data = av_calloc(vrect.w, vrect.h)))
+
+        vrect.buf[0] = av_buffer_allocz((size_t)vrect.w * vrect.h);
+        if (!vrect.buf[0])
             return AVERROR(ENOMEM);
-        vrect.data    [0] = vrect_data;
+
         vrect.linesize[0] = vrect.w;
         for (i = 0; i < rects; i++) {
-            build_color_map(avctx, cmap, (uint32_t *)h->rects[i]->data[1],
+            build_color_map(avctx, cmap, frame->subtitle_areas[i]->pal,
                             out_palette, out_alpha);
-            copy_rectangle(&vrect, h->rects[i], cmap);
+            copy_rectangle(&vrect, frame->subtitle_areas[i], cmap);
         }
         for (i = 0; i < 4; i++)
             cmap[i] = i;
     } else {
-        build_color_map(avctx, cmap, (uint32_t *)h->rects[0]->data[1],
+        build_color_map(avctx, cmap, frame->subtitle_areas[0]->pal,
                         out_palette, out_alpha);
     }
 
@@ -336,6 +344,16 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
                out_palette[i], out_alpha[i] >> 4);
     av_log(avctx, AV_LOG_DEBUG, "\n");
 
+
+    ret = ff_get_encode_buffer(avctx, avpkt, 4 + vrect.w * vrect.h / 2 + 17 + 21, 0);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+        return ret;
+    }
+
+    outbuf_size = avpkt->size;
+    outbuf = avpkt->data;
+
     // encode data block
     q = outbuf + 4;
     offset1 = q - outbuf;
@@ -345,10 +363,10 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
         ret = AVERROR_BUFFER_TOO_SMALL;
         goto fail;
     }
-    dvd_encode_rle(&q, vrect.data[0], vrect.w * 2,
+    dvd_encode_rle(&q, vrect.buf[0]->data, vrect.w * 2,
                    vrect.w, (vrect.h + 1) >> 1, cmap);
     offset2 = q - outbuf;
-    dvd_encode_rle(&q, vrect.data[0] + vrect.w, vrect.w * 2,
+    dvd_encode_rle(&q, vrect.buf[0]->data + vrect.w, vrect.w * 2,
                    vrect.w, vrect.h >> 1, cmap);
 
     if (dvdc->even_rows_fix && (vrect.h & 1)) {
@@ -363,7 +381,7 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
     bytestream_put_be16(&qq, q - outbuf);
 
     // send start display command
-    bytestream_put_be16(&q, (h->start_display_time*90) >> 10);
+    bytestream_put_be16(&q, 0);
     bytestream_put_be16(&q, (q - outbuf) /*- 2 */ + 8 + 12 + 2);
     *q++ = 0x03; // palette - 4 nibbles
     *q++ = (out_palette[3] << 4) | out_palette[2];
@@ -401,7 +419,8 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
     *q++ = 0xff; // terminating command
 
     // send stop display command last
-    bytestream_put_be16(&q, (h->end_display_time*90) >> 10);
+    duration_ms = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, (AVRational){ 1, 1000 });
+    bytestream_put_be16(&q, (duration_ms*90) >> 10);
     bytestream_put_be16(&q, (q - outbuf) - 2 /*+ 4*/);
     *q++ = 0x02; // set end
     *q++ = 0xff; // terminating command
@@ -410,7 +429,9 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
     bytestream_put_be16(&qq, q - outbuf);
 
     av_log(NULL, AV_LOG_DEBUG, "subtitle_packet size=%"PTRDIFF_SPECIFIER"\n", q - outbuf);
-    ret = q - outbuf;
+    avpkt->size = q - outbuf;
+    ret = 0;
+    *got_packet = 1;
 
 fail:
     av_free(vrect_data);
@@ -474,14 +495,13 @@ static int dvdsub_init(AVCodecContext *avctx)
     return 0;
 }
 
-static int dvdsub_encode(AVCodecContext *avctx,
-                         unsigned char *buf, int buf_size,
-                         const AVSubtitle *sub)
+static int dvdsub_encode(struct AVCodecContext* avctx, struct AVPacket* avpkt,
+                         const struct AVFrame* frame, int* got_packet)
 {
     //DVDSubtitleContext *s = avctx->priv_data;
     int ret;
 
-    ret = encode_dvd_subtitles(avctx, buf, buf_size, sub);
+    ret = encode_dvd_subtitles(avctx, avpkt, frame, got_packet);
     return ret;
 }
 
@@ -506,7 +526,7 @@ const FFCodec ff_dvdsub_encoder = {
     .p.type         = AVMEDIA_TYPE_SUBTITLE,
     .p.id           = AV_CODEC_ID_DVD_SUBTITLE,
     .init           = dvdsub_init,
-    FF_CODEC_ENCODE_SUB_CB(dvdsub_encode),
+    FF_CODEC_ENCODE_CB(dvdsub_encode),
     .p.priv_class   = &dvdsubenc_class,
     .priv_data_size = sizeof(DVDSubtitleContext),
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
diff --git a/libavcodec/encode.c b/libavcodec/encode.c
index b68bf1e184..c96f5feb61 100644
--- a/libavcodec/encode.c
+++ b/libavcodec/encode.c
@@ -143,17 +143,70 @@ fail:
     return ret;
 }
 
-int avcodec_encode_subtitle(AVCodecContext *avctx, uint8_t *buf, int buf_size,
-                            const AVSubtitle *sub)
+int avcodec_encode_subtitle(AVCodecContext *avctx, uint8_t *buf, int buf_size, const AVSubtitle *sub)
 {
-    int ret;
+    int ret = 0;
+    AVFrame *frame = NULL;
+    AVPacket* avpkt = NULL;
+
     if (sub->start_display_time) {
         av_log(avctx, AV_LOG_ERROR, "start_display_time must be 0.\n");
         return -1;
     }
 
-    ret = ffcodec(avctx->codec)->cb.encode_sub(avctx, buf, buf_size, sub);
+    memset(buf, 0, buf_size);
+    // Create a temporary frame for calling the regular api:
+    frame = av_frame_alloc();
+    if (!frame) {
+        ret = AVERROR(ENOMEM);
+        goto exit;
+    }
+
+    frame->format = sub->format;
+    frame->type = AVMEDIA_TYPE_SUBTITLE;
+    ret = av_frame_get_buffer2(frame, 0);
+    if (ret < 0)
+        goto exit;
+
+    // Create a temporary packet
+    avpkt = av_packet_alloc();
+    if (!avpkt) {
+        ret = AVERROR(ENOMEM);
+        goto exit;
+    }
+
+    // Copy legacy subtitle data to temp frame
+    ret = ff_frame_put_subtitle(frame, sub);
+    if (ret < 0)
+        goto exit;
+
+    ret = avcodec_send_frame(avctx, frame);
+    if (ret < 0)
+        goto exit;
+
+    ret = avcodec_receive_packet(avctx, avpkt);
+
+    if (ret < 0 && ret != AVERROR(EAGAIN))
+        goto exit;
+
+    //ret = avctx->codec->encode2(avctx, avpkt, frame, &got_packet);
+
     avctx->frame_number++;
+
+    if (avpkt->size) {
+        if (avpkt->size > buf_size) {
+            ret = AVERROR_BUFFER_TOO_SMALL;
+            goto exit;
+        }
+
+        memcpy(buf, avpkt->data, avpkt->size);
+        ret = avpkt->size;
+    }
+
+exit:
+
+    av_packet_free(&avpkt);
+    av_frame_free(&frame);
     return ret;
 }
 
diff --git a/libavcodec/movtextenc.c b/libavcodec/movtextenc.c
index 6f0b7a8a5c..b9e0288403 100644
--- a/libavcodec/movtextenc.c
+++ b/libavcodec/movtextenc.c
@@ -30,6 +30,7 @@
 #include "libavutil/ass_internal.h"
 #include "bytestream.h"
 #include "codec_internal.h"
+#include "encode.h"
 
 #define STYLE_FLAG_BOLD         (1<<0)
 #define STYLE_FLAG_ITALIC       (1<<1)
@@ -73,6 +74,7 @@ typedef struct {
     AVCodecContext *avctx;
 
     ASSSplitContext *ass_ctx;
+    int is_default_ass_context;
     ASSStyle *ass_dialog_style;
     StyleBox *style_attributes;
     unsigned  count;
@@ -329,7 +331,7 @@ static av_cold int mov_text_encode_init(AVCodecContext *avctx)
 
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
 
-    s->ass_ctx = avpriv_ass_split(avctx->subtitle_header);
+    s->ass_ctx = avpriv_ass_split((char*)avctx->subtitle_header);
     if (!s->ass_ctx)
         return AVERROR_INVALIDDATA;
     ret = encode_sample_description(avctx);
@@ -633,56 +635,112 @@ static const ASSCodesCallbacks mov_text_callbacks = {
     .end              = mov_text_end_cb,
 };
 
-static int mov_text_encode_frame(AVCodecContext *avctx, unsigned char *buf,
-                                 int bufsize, const AVSubtitle *sub)
+static void ensure_ass_context(AVCodecContext* avctx, const AVFrame* frame)
+{
+    MovTextContext* s = avctx->priv_data;
+    int ret;
+
+    if (s->ass_ctx && !s->is_default_ass_context)
+        // We already have a (non-default context)
+        return;
+
+    if (!frame->num_subtitle_areas)
+        // Don't need ass context for processing empty subtitle frames
+        return;
+
+    // The frame has content, so we need to set up a context
+    if (frame->subtitle_header && frame->subtitle_header->size > 0) {
+        const char* subtitle_header = (char*)frame->subtitle_header->data;
+        avpriv_ass_split_free(s->ass_ctx);
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 0;
+    }
+    else if (!s->ass_ctx) {
+        char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+        if (!subtitle_header)
+            return;
+
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 1;
+        av_free(subtitle_header);
+    }
+
+    if (s->ass_ctx && !avctx->extradata_size) {
+        ret = encode_sample_description(avctx);
+        if (ret < 0)
+            av_log(avctx, AV_LOG_ERROR, "Error during encode_sample_description().\n");
+    }
+}
+
+static int mov_text_encode_frame(AVCodecContext *avctx, AVPacket *avpkt,
+                                 const AVFrame *frame, int *got_packet)
 {
     MovTextContext *s = avctx->priv_data;
     ASSDialog *dialog;
-    int i, length;
+    int i, ret = 0;
+    size_t j;
+    uint8_t* buf;
+
+    ensure_ass_context(avctx, frame);
 
     s->text_pos = 0;
     s->count = 0;
     s->box_flags = 0;
     av_bprint_clear(&s->buffer);
-    for (i = 0; i < sub->num_rects; i++) {
-        const char *ass = sub->rects[i]->ass;
+    for (i = 0; i < frame->num_subtitle_areas; i++) {
+        const char *ass = frame->subtitle_areas[i]->ass;
 
-        if (sub->rects[i]->type != SUBTITLE_ASS) {
-            av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
+        if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) {
+            av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
             return AVERROR(EINVAL);
         }
 
-        dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
-        if (!dialog)
-            return AVERROR(ENOMEM);
-        mov_text_dialog(s, dialog);
-        avpriv_ass_split_override_codes(&mov_text_callbacks, s, dialog->text);
-        avpriv_ass_free_dialog(&dialog);
+        if (ass) {
+
+            if (i > 0)
+                mov_text_new_line_cb(s, 0);
+
+            dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
+            if (!dialog)
+                return AVERROR(ENOMEM);
+            mov_text_dialog(s, dialog);
+            avpriv_ass_split_override_codes(&mov_text_callbacks, s, dialog->text);
+            avpriv_ass_free_dialog(&dialog);
+
+        }
     }
 
-    if (s->buffer.len > UINT16_MAX)
-        return AVERROR(ERANGE);
-    AV_WB16(buf, s->buffer.len);
-    buf += 2;
+    if (!av_bprint_is_complete(&s->buffer)) {
+        return AVERROR(ENOMEM);
+    }
 
-    for (size_t j = 0; j < box_count; j++)
+    for (j = 0; j < box_count; j++) {
         box_types[j].encode(s);
+    }
 
-    if (!av_bprint_is_complete(&s->buffer))
-        return AVERROR(ENOMEM);
+    ret = ff_get_encode_buffer(avctx, avpkt, s->buffer.len + 3, 0);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+        return ret;
+    }
 
-    if (!s->buffer.len)
-        return 0;
+    buf = avpkt->data;
 
-    if (s->buffer.len > bufsize - 3) {
+    AV_WB16(buf, s->buffer.len);
+    buf += 2;
+
+    if (s->buffer.len > avpkt->size - 3) {
         av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n");
-        return AVERROR_BUFFER_TOO_SMALL;
+        ret = AVERROR_BUFFER_TOO_SMALL;
+        goto exit;
     }
 
     memcpy(buf, s->buffer.str, s->buffer.len);
-    length = s->buffer.len + 2;
+    avpkt->size = s->buffer.len + 2;
+    *got_packet = 1;
 
-    return length;
+exit:
+    return ret;
 }
 
 #define OFFSET(x) offsetof(MovTextContext, x)
@@ -707,7 +765,7 @@ const FFCodec ff_movtext_encoder = {
     .priv_data_size = sizeof(MovTextContext),
     .p.priv_class   = &mov_text_encoder_class,
     .init           = mov_text_encode_init,
-    FF_CODEC_ENCODE_SUB_CB(mov_text_encode_frame),
+    FF_CODEC_ENCODE_CB(mov_text_encode_frame),
     .close          = mov_text_encode_close,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP,
 };
diff --git a/libavcodec/srtenc.c b/libavcodec/srtenc.c
index 2baa6e70ad..bc336ea91e 100644
--- a/libavcodec/srtenc.c
+++ b/libavcodec/srtenc.c
@@ -23,6 +23,7 @@
 
 #include <stdarg.h>
 #include "avcodec.h"
+#include "encode.h"
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
 #include "codec_internal.h"
@@ -35,6 +36,7 @@
 typedef struct {
     AVCodecContext *avctx;
     ASSSplitContext *ass_ctx;
+    int is_default_ass_context;
     AVBPrint buffer;
     char stack[SRT_STACK_SIZE];
     int stack_ptr;
@@ -132,14 +134,13 @@ static void srt_style_apply(SRTContext *s, const char *style)
     }
 }
 
-
 static av_cold int srt_encode_init(AVCodecContext *avctx)
 {
     SRTContext *s = avctx->priv_data;
     s->avctx = avctx;
-    s->ass_ctx = avpriv_ass_split(avctx->subtitle_header);
+    s->ass_ctx = avpriv_ass_split((char *)avctx->subtitle_header);
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
-    return s->ass_ctx ? 0 : AVERROR_INVALIDDATA;
+    return 0;
 }
 
 static void srt_text_cb(void *priv, const char *text, int len)
@@ -229,58 +230,95 @@ static const ASSCodesCallbacks text_callbacks = {
     .new_line         = srt_new_line_cb,
 };
 
-static int encode_frame(AVCodecContext *avctx,
-                        unsigned char *buf, int bufsize, const AVSubtitle *sub,
-                        const ASSCodesCallbacks *cb)
+static void ensure_ass_context(SRTContext* s, const AVFrame* frame)
+{
+    if (s->ass_ctx && !s->is_default_ass_context)
+        // We already have a (non-default context)
+        return;
+
+    if (!frame->num_subtitle_areas)
+        // Don't need ass context for processing empty subtitle frames
+        return;
+
+    // The frame has content, so we need to set up a context
+    if (frame->subtitle_header && frame->subtitle_header->size > 0) {
+        const char* subtitle_header = (char*)frame->subtitle_header->data;
+        avpriv_ass_split_free(s->ass_ctx);
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 0;
+    }
+    else if (!s->ass_ctx) {
+        char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+        if (!subtitle_header)
+            return;
+
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 1;
+        av_free(subtitle_header);
+    }
+}
+
+static int encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+                        const AVFrame* frame, int* got_packet, const ASSCodesCallbacks* cb)
 {
     SRTContext *s = avctx->priv_data;
     ASSDialog *dialog;
-    int i;
+    int i, ret;
+
+    ensure_ass_context(s, frame);
 
     av_bprint_clear(&s->buffer);
 
-    for (i=0; i<sub->num_rects; i++) {
-        const char *ass = sub->rects[i]->ass;
+    for (i=0; i< frame->num_subtitle_areas; i++) {
+        const char *ass = frame->subtitle_areas[i]->ass;
 
-        if (sub->rects[i]->type != SUBTITLE_ASS) {
-            av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
+        if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) {
+            av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
             return AVERROR(EINVAL);
         }
 
-        dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
-        if (!dialog)
-            return AVERROR(ENOMEM);
-        s->alignment_applied = 0;
-        if (avctx->codec_id == AV_CODEC_ID_SUBRIP)
-            srt_style_apply(s, dialog->style);
-        avpriv_ass_split_override_codes(cb, s, dialog->text);
-        avpriv_ass_free_dialog(&dialog);
+        if (ass) {
+
+            if (i > 0)
+                srt_new_line_cb(s, 0);
+
+            dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
+            if (!dialog)
+                return AVERROR(ENOMEM);
+            s->alignment_applied = 0;
+            if (avctx->codec_id == AV_CODEC_ID_SUBRIP)
+                srt_style_apply(s, dialog->style);
+            avpriv_ass_split_override_codes(cb, s, dialog->text);
+            avpriv_ass_free_dialog(&dialog);
+        }
     }
 
     if (!av_bprint_is_complete(&s->buffer))
         return AVERROR(ENOMEM);
-    if (!s->buffer.len)
-        return 0;
 
-    if (s->buffer.len > bufsize) {
-        av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n");
-        return AVERROR_BUFFER_TOO_SMALL;
+    ret = ff_get_encode_buffer(avctx, avpkt, s->buffer.len + 1, 0);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+        return ret;
     }
-    memcpy(buf, s->buffer.str, s->buffer.len);
 
-    return s->buffer.len;
+    memcpy(avpkt->data, s->buffer.str, s->buffer.len);
+    avpkt->size = s->buffer.len;
+    *got_packet = 1;
+
+    return 0;
 }
 
-static int srt_encode_frame(AVCodecContext *avctx,
-                               unsigned char *buf, int bufsize, const AVSubtitle *sub)
+static int srt_encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+                            const AVFrame* frame, int* got_packet)
 {
-    return encode_frame(avctx, buf, bufsize, sub, &srt_callbacks);
+    return encode_frame(avctx, avpkt, frame, got_packet, &srt_callbacks);
 }
 
-static int text_encode_frame(AVCodecContext *avctx,
-                             unsigned char *buf, int bufsize, const AVSubtitle *sub)
+static int text_encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+                             const AVFrame* frame, int* got_packet)
 {
-    return encode_frame(avctx, buf, bufsize, sub, &text_callbacks);
+    return encode_frame(avctx, avpkt, frame, got_packet, &text_callbacks);
 }
 
 static int srt_encode_close(AVCodecContext *avctx)
@@ -300,7 +338,7 @@ const FFCodec ff_srt_encoder = {
     .p.id           = AV_CODEC_ID_SUBRIP,
     .priv_data_size = sizeof(SRTContext),
     .init           = srt_encode_init,
-    FF_CODEC_ENCODE_SUB_CB(srt_encode_frame),
+    FF_CODEC_ENCODE_CB(srt_encode_frame),
     .close          = srt_encode_close,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
 };
@@ -314,7 +352,7 @@ const FFCodec ff_subrip_encoder = {
     .p.id           = AV_CODEC_ID_SUBRIP,
     .priv_data_size = sizeof(SRTContext),
     .init           = srt_encode_init,
-    FF_CODEC_ENCODE_SUB_CB(srt_encode_frame),
+    FF_CODEC_ENCODE_CB(srt_encode_frame),
     .close          = srt_encode_close,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
 };
@@ -328,7 +366,7 @@ const FFCodec ff_text_encoder = {
     .p.id           = AV_CODEC_ID_TEXT,
     .priv_data_size = sizeof(SRTContext),
     .init           = srt_encode_init,
-    FF_CODEC_ENCODE_SUB_CB(text_encode_frame),
+    FF_CODEC_ENCODE_CB(text_encode_frame),
     .close          = srt_encode_close,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
 };
diff --git a/libavcodec/tests/avcodec.c b/libavcodec/tests/avcodec.c
index 08b5fbede1..1469c23f99 100644
--- a/libavcodec/tests/avcodec.c
+++ b/libavcodec/tests/avcodec.c
@@ -109,7 +109,6 @@ int main(void){
             is_decoder = 1;
             break;
         case FF_CODEC_CB_TYPE_ENCODE:
-        case FF_CODEC_CB_TYPE_ENCODE_SUB:
         case FF_CODEC_CB_TYPE_RECEIVE_PACKET:
             is_encoder = 1;
             break;
@@ -125,15 +124,13 @@ int main(void){
 #define CHECK(TYPE, type) (codec2->cb_type == FF_CODEC_CB_TYPE_ ## TYPE && !codec2->cb.type)
         if (CHECK(DECODE, decode) || CHECK(DECODE_SUB, decode_sub) ||
             CHECK(RECEIVE_PACKET, receive_packet) ||
-            CHECK(ENCODE, encode) || CHECK(ENCODE_SUB, encode_sub) ||
+            CHECK(ENCODE, encode) ||
             CHECK(RECEIVE_FRAME, receive_frame)) {
             ERR_EXT("Codec %s does not implement its %s callback.\n",
                     is_decoder ? "decoding" : "encoding");
         }
 #undef CHECK
         if (is_encoder) {
-            if ((codec->type == AVMEDIA_TYPE_SUBTITLE) != (codec2->cb_type == FF_CODEC_CB_TYPE_ENCODE_SUB))
-                ERR("Encoder %s is both subtitle encoder and not subtitle encoder.");
             if (codec2->update_thread_context || codec2->update_thread_context_for_user || codec2->bsfs)
                 ERR("Encoder %s has decoder-only thread functions or bsf.\n");
             if (codec->type == AVMEDIA_TYPE_AUDIO) {
diff --git a/libavcodec/ttmlenc.c b/libavcodec/ttmlenc.c
index d4f11a87d2..035fe33f24 100644
--- a/libavcodec/ttmlenc.c
+++ b/libavcodec/ttmlenc.c
@@ -33,11 +33,17 @@
 #include "libavutil/bprint.h"
 #include "libavutil/internal.h"
 #include "libavutil/ass_split_internal.h"
+#include "libavutil/ass_internal.h"
 #include "ttmlenc.h"
 
+#include "encode.h"
+
+
 typedef struct {
     AVCodecContext *avctx;
     ASSSplitContext *ass_ctx;
+    int is_default_ass_context;
+    int extradata_written;
     AVBPrint buffer;
 } TTMLContext;
 
@@ -76,28 +82,75 @@ static const ASSCodesCallbacks ttml_callbacks = {
     .new_line         = ttml_new_line_cb,
 };
 
-static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf,
-                             int bufsize, const AVSubtitle *sub)
+static int ttml_write_header_content(AVCodecContext* avctx);
+
+static void ensure_ass_context(AVCodecContext* avctx, const AVFrame* frame)
+{
+    TTMLContext* s = avctx->priv_data;
+    int ret;
+
+    if (s->ass_ctx && !s->is_default_ass_context)
+        // We already have a (non-default context)
+        return;
+
+    if (!frame->num_subtitle_areas)
+        // Don't need ass context for processing empty subtitle frames
+        return;
+
+    // The frame has content, so we need to set up a context
+    if (frame->subtitle_header && frame->subtitle_header->size > 0) {
+        const char* subtitle_header = (char*)frame->subtitle_header->data;
+        avpriv_ass_split_free(s->ass_ctx);
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 0;
+    }
+    else if (!s->ass_ctx) {
+        char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+        if (!subtitle_header)
+            return;
+
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 1;
+        av_free(subtitle_header);
+    }
+
+    if (s->ass_ctx && !s->extradata_written) {
+        s->extradata_written = 1;
+        if ((ret = ttml_write_header_content(avctx)) < 0) {
+            av_log(avctx, AV_LOG_ERROR, "Error writing header content.\n");
+        }
+    }
+}
+
+static int ttml_encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+                             const AVFrame* frame, int* got_packet)
 {
     TTMLContext *s = avctx->priv_data;
     ASSDialog *dialog;
-    int i;
+    int i, ret;
+
+    ensure_ass_context(avctx, frame);
 
     av_bprint_clear(&s->buffer);
 
-    for (i=0; i<sub->num_rects; i++) {
-        const char *ass = sub->rects[i]->ass;
-        int ret;
+    for (i=0; i< frame->num_subtitle_areas; i++) {
+        const char *ass = frame->subtitle_areas[i]->ass;
 
-        if (sub->rects[i]->type != SUBTITLE_ASS) {
-            av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
+        if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) {
+            av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
             return AVERROR(EINVAL);
         }
 
+        if (!ass)
+            continue;
+
         dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
         if (!dialog)
             return AVERROR(ENOMEM);
 
+        if (i > 0)
+            ttml_new_line_cb(s, 0);
+
         if (dialog->style) {
             av_bprintf(&s->buffer, "<span region=\"");
             av_bprint_escape(&s->buffer, dialog->style, NULL,
@@ -130,17 +183,19 @@ static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf,
 
     if (!av_bprint_is_complete(&s->buffer))
         return AVERROR(ENOMEM);
-    if (!s->buffer.len)
-        return 0;
-
-    // force null-termination, so in case our destination buffer is
-    // too small, the return value is larger than bufsize minus null.
-    if (av_strlcpy(buf, s->buffer.str, bufsize) > bufsize - 1) {
-        av_log(avctx, AV_LOG_ERROR, "Buffer too small for TTML event.\n");
-        return AVERROR_BUFFER_TOO_SMALL;
+
+    ret = ff_get_encode_buffer(avctx, avpkt, s->buffer.len + 3, 0);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+        return ret;
     }
 
-    return s->buffer.len;
+    av_strlcpy(avpkt->data, s->buffer.str, avpkt->size);
+
+    avpkt->size = s->buffer.len;
+    *got_packet = 1;
+
+    return 0;
 }
 
 static av_cold int ttml_encode_close(AVCodecContext *avctx)
@@ -370,13 +425,13 @@ static av_cold int ttml_encode_init(AVCodecContext *avctx)
     s->avctx   = avctx;
 
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
+    s->ass_ctx = avpriv_ass_split((char*)avctx->subtitle_header);
 
-    if (!(s->ass_ctx = avpriv_ass_split(avctx->subtitle_header))) {
-        return AVERROR_INVALIDDATA;
-    }
+    if (s->ass_ctx) {
+        if (ret = ttml_write_header_content(avctx) < 0)
+            return ret;
 
-    if ((ret = ttml_write_header_content(avctx)) < 0) {
-        return ret;
+        s->extradata_written = 1;
     }
 
     return 0;
@@ -389,7 +444,7 @@ const FFCodec ff_ttml_encoder = {
     .p.id           = AV_CODEC_ID_TTML,
     .priv_data_size = sizeof(TTMLContext),
     .init           = ttml_encode_init,
-    FF_CODEC_ENCODE_SUB_CB(ttml_encode_frame),
+    FF_CODEC_ENCODE_CB(ttml_encode_frame),
     .close          = ttml_encode_close,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP,
 };
diff --git a/libavcodec/utils.c b/libavcodec/utils.c
index b67b6b6122..b9ebe18e32 100644
--- a/libavcodec/utils.c
+++ b/libavcodec/utils.c
@@ -75,7 +75,6 @@ int av_codec_is_encoder(const AVCodec *avcodec)
 {
     const FFCodec *const codec = ffcodec(avcodec);
     return codec && (codec->cb_type == FF_CODEC_CB_TYPE_ENCODE     ||
-                     codec->cb_type == FF_CODEC_CB_TYPE_ENCODE_SUB ||
                      codec->cb_type == FF_CODEC_CB_TYPE_RECEIVE_PACKET);
 }
 
diff --git a/libavcodec/webvttenc.c b/libavcodec/webvttenc.c
index 24d60c5dc1..128607a669 100644
--- a/libavcodec/webvttenc.c
+++ b/libavcodec/webvttenc.c
@@ -22,6 +22,7 @@
 
 #include <stdarg.h>
 #include "avcodec.h"
+#include "encode.h"
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
 #include "codec_internal.h"
@@ -32,6 +33,7 @@
 typedef struct {
     AVCodecContext *avctx;
     ASSSplitContext *ass_ctx;
+    int is_default_ass_context;
     AVBPrint buffer;
     unsigned timestamp_end;
     int count;
@@ -155,43 +157,81 @@ static const ASSCodesCallbacks webvtt_callbacks = {
     .end              = webvtt_end_cb,
 };
 
-static int webvtt_encode_frame(AVCodecContext *avctx,
-                               unsigned char *buf, int bufsize, const AVSubtitle *sub)
+static void ensure_ass_context(WebVTTContext* s, const AVFrame* frame)
+{
+    if (s->ass_ctx && !s->is_default_ass_context)
+        // We already have a (non-default context)
+        return;
+
+    if (!frame->num_subtitle_areas)
+        // Don't need ass context for processing empty subtitle frames
+        return;
+
+    // The frame has content, so we need to set up a context
+    if (frame->subtitle_header && frame->subtitle_header->size > 0) {
+        const char* subtitle_header = (char*)frame->subtitle_header->data;
+        avpriv_ass_split_free(s->ass_ctx);
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 0;
+    }
+    else if (!s->ass_ctx) {
+        char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+        if (!subtitle_header)
+            return;
+
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 1;
+        av_free(subtitle_header);
+    }
+}
+
+static int webvtt_encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+                               const AVFrame* frame, int* got_packet)
 {
     WebVTTContext *s = avctx->priv_data;
     ASSDialog *dialog;
-    int i;
+    int ret, i;
+
+    ensure_ass_context(s, frame);
 
     av_bprint_clear(&s->buffer);
 
-    for (i=0; i<sub->num_rects; i++) {
-        const char *ass = sub->rects[i]->ass;
+    for (i=0; i< frame->num_subtitle_areas; i++) {
+        const char *ass = frame->subtitle_areas[i]->ass;
 
-        if (sub->rects[i]->type != SUBTITLE_ASS) {
-            av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
+        if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) {
+            av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
             return AVERROR(EINVAL);
         }
 
-        dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
-        if (!dialog)
-            return AVERROR(ENOMEM);
-        webvtt_style_apply(s, dialog->style);
-        avpriv_ass_split_override_codes(&webvtt_callbacks, s, dialog->text);
-        avpriv_ass_free_dialog(&dialog);
+        if (ass) {
+
+            if (i > 0)
+                webvtt_new_line_cb(s, 0);
+
+            dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
+            if (!dialog)
+                return AVERROR(ENOMEM);
+            webvtt_style_apply(s, dialog->style);
+            avpriv_ass_split_override_codes(&webvtt_callbacks, s, dialog->text);
+            avpriv_ass_free_dialog(&dialog);
+        }
     }
 
     if (!av_bprint_is_complete(&s->buffer))
         return AVERROR(ENOMEM);
-    if (!s->buffer.len)
-        return 0;
 
-    if (s->buffer.len > bufsize) {
-        av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n");
-        return AVERROR_BUFFER_TOO_SMALL;
+    ret = ff_get_encode_buffer(avctx, avpkt, s->buffer.len + 1, 0);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+        return ret;
     }
-    memcpy(buf, s->buffer.str, s->buffer.len);
 
-    return s->buffer.len;
+    memcpy(avpkt->data, s->buffer.str, s->buffer.len);
+    avpkt->size = s->buffer.len;
+    *got_packet = s->buffer.len > 0;
+
+    return 0;
 }
 
 static int webvtt_encode_close(AVCodecContext *avctx)
@@ -206,9 +246,9 @@ static av_cold int webvtt_encode_init(AVCodecContext *avctx)
 {
     WebVTTContext *s = avctx->priv_data;
     s->avctx = avctx;
-    s->ass_ctx = avpriv_ass_split(avctx->subtitle_header);
+    s->ass_ctx = avpriv_ass_split((char*)avctx->subtitle_header);
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
-    return s->ass_ctx ? 0 : AVERROR_INVALIDDATA;
+    return 0;
 }
 
 const FFCodec ff_webvtt_encoder = {
@@ -218,7 +258,7 @@ const FFCodec ff_webvtt_encoder = {
     .p.id           = AV_CODEC_ID_WEBVTT,
     .priv_data_size = sizeof(WebVTTContext),
     .init           = webvtt_encode_init,
-    FF_CODEC_ENCODE_SUB_CB(webvtt_encode_frame),
+    FF_CODEC_ENCODE_CB(webvtt_encode_frame),
     .close          = webvtt_encode_close,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
 };
diff --git a/libavcodec/xsubenc.c b/libavcodec/xsubenc.c
index 8ca411e5af..19570aee2f 100644
--- a/libavcodec/xsubenc.c
+++ b/libavcodec/xsubenc.c
@@ -23,6 +23,7 @@
 #include "avcodec.h"
 #include "bytestream.h"
 #include "codec_internal.h"
+#include "encode.h"
 #include "put_bits.h"
 
 /**
@@ -111,39 +112,56 @@ static int make_tc(uint64_t ms, int *tc)
     return ms > 99;
 }
 
-static int xsub_encode(AVCodecContext *avctx, unsigned char *buf,
-                       int bufsize, const AVSubtitle *h)
+static int xsub_encode(AVCodecContext* avctx, AVPacket* avpkt,
+                       const AVFrame* frame, int* got_packet)
 {
-    uint64_t startTime = h->pts / 1000; // FIXME: need better solution...
-    uint64_t endTime = startTime + h->end_display_time - h->start_display_time;
+    const int64_t duration_ms = (int64_t)((double)frame->subtitle_timing.duration * av_q2d(AV_TIME_BASE_Q) * 1000);
+    const uint64_t startTime = (int64_t)((double)frame->subtitle_timing.start_pts * av_q2d(AV_TIME_BASE_Q) * 1000);
+    const uint64_t endTime   = startTime + duration_ms;
     int start_tc[4], end_tc[4];
-    uint8_t *hdr = buf + 27; // Point behind the timestamp
+    uint8_t *hdr;
     uint8_t *rlelenptr;
     uint16_t width, height;
-    int i;
+    int i, ret;
     PutBitContext pb;
+    uint8_t* buf;
+    int64_t req_size;
 
-    if (bufsize < 27 + 7*2 + 4*3) {
-        av_log(avctx, AV_LOG_ERROR, "Buffer too small for XSUB header.\n");
-        return AVERROR_BUFFER_TOO_SMALL;
+    if (!frame->num_subtitle_areas) {
+        // Don't encode empty sub events
+        return 0;
     }
 
+    // Estimate size (timestamp 27, header 7*2 + 4*3, padding 10)
+    req_size = 27 + 7*2 + 4*3 + 10;
+    req_size += frame->subtitle_areas[0]->linesize[0] * frame->subtitle_areas[0]->h;
+    req_size += 256; // Palette
+
+    ret = ff_get_encode_buffer(avctx, avpkt, req_size, 0);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+        return ret;
+    }
+
+    buf = avpkt->data;
+    hdr = avpkt->data + 27; // Point behind the timestamp
+
     // TODO: support multiple rects
-    if (h->num_rects != 1)
-        av_log(avctx, AV_LOG_WARNING, "Only single rects supported (%d in subtitle.)\n", h->num_rects);
+    if (frame->num_subtitle_areas != 1)
+        av_log(avctx, AV_LOG_WARNING, "Only single rects supported (%d in subtitle.)\n", frame->num_subtitle_areas);
 
     // TODO: render text-based subtitles into bitmaps
-    if (!h->rects[0]->data[0] || !h->rects[0]->data[1]) {
+    if (!frame->subtitle_areas[0]->buf[0]->data || !frame->subtitle_areas[0]->pal) {
         av_log(avctx, AV_LOG_WARNING, "No subtitle bitmap available.\n");
         return AVERROR(EINVAL);
     }
 
     // TODO: color reduction, similar to dvdsub encoder
-    if (h->rects[0]->nb_colors > 4)
-        av_log(avctx, AV_LOG_WARNING, "No more than 4 subtitle colors supported (%d found.)\n", h->rects[0]->nb_colors);
+    if (frame->subtitle_areas[0]->nb_colors > 4)
+        av_log(avctx, AV_LOG_WARNING, "No more than 4 subtitle colors supported (%d found.)\n", frame->subtitle_areas[0]->nb_colors);
 
     // TODO: Palette swapping if color zero is not transparent
-    if (((uint32_t *)h->rects[0]->data[1])[0] & 0xff000000)
+    if (((uint32_t *)frame->subtitle_areas[0]->pal)[0] & 0xff000000)
         av_log(avctx, AV_LOG_WARNING, "Color index 0 is not transparent. Transparency will be messed up.\n");
 
     if (make_tc(startTime, start_tc) || make_tc(endTime, end_tc)) {
@@ -151,7 +169,7 @@ static int xsub_encode(AVCodecContext *avctx, unsigned char *buf,
         return AVERROR(EINVAL);
     }
 
-    snprintf(buf, 28,
+    snprintf((char *)avpkt->data, 28,
         "[%02d:%02d:%02d.%03d-%02d:%02d:%02d.%03d]",
         start_tc[3], start_tc[2], start_tc[1], start_tc[0],
         end_tc[3],   end_tc[2],   end_tc[1],   end_tc[0]);
@@ -160,45 +178,47 @@ static int xsub_encode(AVCodecContext *avctx, unsigned char *buf,
     // 2 pixels required on either side of subtitle.
     // Possibly due to limitations of hardware renderers.
     // TODO: check if the bitmap is already padded
-    width  = FFALIGN(h->rects[0]->w, 2) + PADDING * 2;
-    height = FFALIGN(h->rects[0]->h, 2);
+    width  = FFALIGN(frame->subtitle_areas[0]->w, 2) + PADDING * 2;
+    height = FFALIGN(frame->subtitle_areas[0]->h, 2);
 
     bytestream_put_le16(&hdr, width);
     bytestream_put_le16(&hdr, height);
-    bytestream_put_le16(&hdr, h->rects[0]->x);
-    bytestream_put_le16(&hdr, h->rects[0]->y);
-    bytestream_put_le16(&hdr, h->rects[0]->x + width -1);
-    bytestream_put_le16(&hdr, h->rects[0]->y + height -1);
+    bytestream_put_le16(&hdr, frame->subtitle_areas[0]->x);
+    bytestream_put_le16(&hdr, frame->subtitle_areas[0]->y);
+    bytestream_put_le16(&hdr, frame->subtitle_areas[0]->x + width -1);
+    bytestream_put_le16(&hdr, frame->subtitle_areas[0]->y + height -1);
 
     rlelenptr = hdr; // Will store length of first field here later.
     hdr+=2;
 
     // Palette
     for (i=0; i<4; i++)
-        bytestream_put_be24(&hdr, ((uint32_t *)h->rects[0]->data[1])[i]);
+        bytestream_put_be24(&hdr, ((uint32_t *)frame->subtitle_areas[0]->pal)[i]);
 
     // Bitmap
     // RLE buffer. Reserve 2 bytes for possible padding after the last row.
-    init_put_bits(&pb, hdr, bufsize - (hdr - buf) - 2);
-    if (xsub_encode_rle(&pb, h->rects[0]->data[0],
-                        h->rects[0]->linesize[0] * 2,
-                        h->rects[0]->w, (h->rects[0]->h + 1) >> 1))
+    init_put_bits(&pb, hdr, avpkt->size - (hdr - buf) - 2);
+    if (xsub_encode_rle(&pb, frame->subtitle_areas[0]->buf[0]->data,
+                        frame->subtitle_areas[0]->linesize[0] * 2,
+                        frame->subtitle_areas[0]->w, (frame->subtitle_areas[0]->h + 1) >> 1))
         return AVERROR_BUFFER_TOO_SMALL;
     bytestream_put_le16(&rlelenptr, put_bytes_count(&pb, 0)); // Length of first field
 
-    if (xsub_encode_rle(&pb, h->rects[0]->data[0] + h->rects[0]->linesize[0],
-                        h->rects[0]->linesize[0] * 2,
-                        h->rects[0]->w, h->rects[0]->h >> 1))
+    if (xsub_encode_rle(&pb, frame->subtitle_areas[0]->buf[0]->data + frame->subtitle_areas[0]->linesize[0],
+                        frame->subtitle_areas[0]->linesize[0] * 2,
+                        frame->subtitle_areas[0]->w, frame->subtitle_areas[0]->h >> 1))
         return AVERROR_BUFFER_TOO_SMALL;
 
     // Enforce total height to be a multiple of 2
-    if (h->rects[0]->h & 1) {
-        put_xsub_rle(&pb, h->rects[0]->w, PADDING_COLOR);
+    if (frame->subtitle_areas[0]->h & 1) {
+        put_xsub_rle(&pb, frame->subtitle_areas[0]->w, PADDING_COLOR);
     }
 
     flush_put_bits(&pb);
 
-    return hdr - buf + put_bytes_output(&pb);
+    avpkt->size = hdr - buf + put_bytes_output(&pb);
+    *got_packet = 1;
+    return 0;
 }
 
 static av_cold int xsub_encoder_init(AVCodecContext *avctx)
@@ -217,6 +237,6 @@ const FFCodec ff_xsub_encoder = {
     .p.type     = AVMEDIA_TYPE_SUBTITLE,
     .p.id       = AV_CODEC_ID_XSUB,
     .init       = xsub_encoder_init,
-    FF_CODEC_ENCODE_SUB_CB(xsub_encode),
+    FF_CODEC_ENCODE_CB(xsub_encode),
     .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE,
 };
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v5 24/25] fftools/ffmpeg: Introduce subtitle filtering and new frame-based subtitle encoding
  2022-06-25  9:57       ` [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022 ffmpegagent
                           ` (22 preceding siblings ...)
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 23/25] avcodec/subtitles: Migrate subtitle encoders to frame-based API softworkz
@ 2022-06-25  9:57         ` softworkz
  2022-06-25 11:34           ` Michael Niedermayer
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 25/25] avcodec/dvbsubdec: Fix conditions for fallback to default resolution softworkz
                           ` (3 subsequent siblings)
  27 siblings, 1 reply; 217+ messages in thread
From: softworkz @ 2022-06-25  9:57 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

This commit actually enables subtitle filtering in ffmpeg by
sending and receiving subtitle frames to and from a filtergraph.

The heartbeat functionality from the previous sub2video implementation
is removed and now provided by the 'subfeed' filter.
The other part of sub2video functionality is retained by
auto-insertion of the new graphicsub2video filter.

Justification for changed test refs:

- sub2video
  The previous results were incorrect. The command line for this test
  specifies -r 5 (5 frames per second), which is now fulfilled by the
  additional frames in the reference output.
  Example: The first subtitle time is 499000, the second is 15355000,
  which means 0.5s and 15.35s with a difference of 14.85s.
  15s * 5fps = 75 frames and that's now the exact number of video
  frames between these two subtitle events.

- sub2video_basic
  The previous results had some incorrect output because multiple
  frames had the same dts
  The non-empty content frames are visually identical, the different
  CRC is due to the different blending algorithm that is being used.

- sub2video_time_limited
  Subtitle frames are emitted to the filter graphs at a 5 fps rate
  by default. The time limit for this test is 15s * 5fps = 75 frames
  which matches the count in the new ref.

- sub-dvb
  Running ffprobe -show_frames on the source file shows that there
  are 7 subtitle frames with 0 rects in the source at the start
  and 2 at the end. This translates to the 14 and 4 additional
  entries in the new test results.

- filter-overlay-dvdsub-2397
  Overlay results have slightly different CRCs due to different
  blending implementation

- sub-scc
  The first entry is no longer in the output because it is before
  the actual start time and the strim filter removes such entries
  now (like for video and audio)

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 fftools/ffmpeg.c                          |  613 +++++-----
 fftools/ffmpeg.h                          |   17 +-
 fftools/ffmpeg_filter.c                   |  270 +++--
 fftools/ffmpeg_hw.c                       |    2 +-
 fftools/ffmpeg_opt.c                      |   28 +-
 tests/ref/fate/filter-overlay-dvdsub-2397 |  182 +--
 tests/ref/fate/sub-dvb                    |  162 +--
 tests/ref/fate/sub-scc                    |    1 -
 tests/ref/fate/sub2video                  | 1091 +++++++++++++++++-
 tests/ref/fate/sub2video_basic            | 1238 +++++++++++++++++++--
 tests/ref/fate/sub2video_time_limited     |   78 +-
 11 files changed, 3010 insertions(+), 672 deletions(-)

diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c
index 09caa3e3c4..40d7868368 100644
--- a/fftools/ffmpeg.c
+++ b/fftools/ffmpeg.c
@@ -142,8 +142,6 @@ int want_sdp = 1;
 static BenchmarkTimeStamps current_time;
 AVIOContext *progress_avio = NULL;
 
-static uint8_t *subtitle_out;
-
 InputStream **input_streams = NULL;
 int        nb_input_streams = 0;
 InputFile   **input_files   = NULL;
@@ -168,163 +166,6 @@ static int restore_tty;
 static void free_input_threads(void);
 #endif
 
-/* sub2video hack:
-   Convert subtitles to video with alpha to insert them in filter graphs.
-   This is a temporary solution until libavfilter gets real subtitles support.
- */
-
-static int sub2video_get_blank_frame(InputStream *ist)
-{
-    int ret;
-    AVFrame *frame = ist->sub2video.frame;
-
-    av_frame_unref(frame);
-    ist->sub2video.frame->width  = ist->dec_ctx->width  ? ist->dec_ctx->width  : ist->sub2video.w;
-    ist->sub2video.frame->height = ist->dec_ctx->height ? ist->dec_ctx->height : ist->sub2video.h;
-    ist->sub2video.frame->format = AV_PIX_FMT_RGB32;
-    if ((ret = av_frame_get_buffer(frame, 0)) < 0)
-        return ret;
-    memset(frame->data[0], 0, frame->height * frame->linesize[0]);
-    return 0;
-}
-
-static void sub2video_copy_rect(uint8_t *dst, int dst_linesize, int w, int h,
-                                AVSubtitleRect *r)
-{
-    uint32_t *pal, *dst2;
-    uint8_t *src, *src2;
-    int x, y;
-
-    if (r->type != SUBTITLE_BITMAP) {
-        av_log(NULL, AV_LOG_WARNING, "sub2video: non-bitmap subtitle\n");
-        return;
-    }
-    if (r->x < 0 || r->x + r->w > w || r->y < 0 || r->y + r->h > h) {
-        av_log(NULL, AV_LOG_WARNING, "sub2video: rectangle (%d %d %d %d) overflowing %d %d\n",
-            r->x, r->y, r->w, r->h, w, h
-        );
-        return;
-    }
-
-    dst += r->y * dst_linesize + r->x * 4;
-    src = r->data[0];
-    pal = (uint32_t *)r->data[1];
-    for (y = 0; y < r->h; y++) {
-        dst2 = (uint32_t *)dst;
-        src2 = src;
-        for (x = 0; x < r->w; x++)
-            *(dst2++) = pal[*(src2++)];
-        dst += dst_linesize;
-        src += r->linesize[0];
-    }
-}
-
-static void sub2video_push_ref(InputStream *ist, int64_t pts)
-{
-    AVFrame *frame = ist->sub2video.frame;
-    int i;
-    int ret;
-
-    av_assert1(frame->data[0]);
-    ist->sub2video.last_pts = frame->pts = pts;
-    for (i = 0; i < ist->nb_filters; i++) {
-        ret = av_buffersrc_add_frame_flags(ist->filters[i]->filter, frame,
-                                           AV_BUFFERSRC_FLAG_KEEP_REF |
-                                           AV_BUFFERSRC_FLAG_PUSH);
-        if (ret != AVERROR_EOF && ret < 0)
-            av_log(NULL, AV_LOG_WARNING, "Error while add the frame to buffer source(%s).\n",
-                   av_err2str(ret));
-    }
-}
-
-void sub2video_update(InputStream *ist, int64_t heartbeat_pts, AVSubtitle *sub)
-{
-    AVFrame *frame = ist->sub2video.frame;
-    int8_t *dst;
-    int     dst_linesize;
-    int num_rects, i;
-    int64_t pts, end_pts;
-
-    if (!frame)
-        return;
-    if (sub) {
-        pts       = av_rescale_q(sub->pts + sub->start_display_time * 1000LL,
-                                 AV_TIME_BASE_Q, ist->st->time_base);
-        end_pts   = av_rescale_q(sub->pts + sub->end_display_time   * 1000LL,
-                                 AV_TIME_BASE_Q, ist->st->time_base);
-        num_rects = sub->num_rects;
-    } else {
-        /* If we are initializing the system, utilize current heartbeat
-           PTS as the start time, and show until the following subpicture
-           is received. Otherwise, utilize the previous subpicture's end time
-           as the fall-back value. */
-        pts       = ist->sub2video.initialize ?
-                    heartbeat_pts : ist->sub2video.end_pts;
-        end_pts   = INT64_MAX;
-        num_rects = 0;
-    }
-    if (sub2video_get_blank_frame(ist) < 0) {
-        av_log(ist->dec_ctx, AV_LOG_ERROR,
-               "Impossible to get a blank canvas.\n");
-        return;
-    }
-    dst          = frame->data    [0];
-    dst_linesize = frame->linesize[0];
-    for (i = 0; i < num_rects; i++)
-        sub2video_copy_rect(dst, dst_linesize, frame->width, frame->height, sub->rects[i]);
-    sub2video_push_ref(ist, pts);
-    ist->sub2video.end_pts = end_pts;
-    ist->sub2video.initialize = 0;
-}
-
-static void sub2video_heartbeat(InputStream *ist, int64_t pts)
-{
-    InputFile *infile = input_files[ist->file_index];
-    int i, j, nb_reqs;
-    int64_t pts2;
-
-    /* When a frame is read from a file, examine all sub2video streams in
-       the same file and send the sub2video frame again. Otherwise, decoded
-       video frames could be accumulating in the filter graph while a filter
-       (possibly overlay) is desperately waiting for a subtitle frame. */
-    for (i = 0; i < infile->nb_streams; i++) {
-        InputStream *ist2 = input_streams[infile->ist_index + i];
-        if (!ist2->sub2video.frame)
-            continue;
-        /* subtitles seem to be usually muxed ahead of other streams;
-           if not, subtracting a larger time here is necessary */
-        pts2 = av_rescale_q(pts, ist->st->time_base, ist2->st->time_base) - 1;
-        /* do not send the heartbeat frame if the subtitle is already ahead */
-        if (pts2 <= ist2->sub2video.last_pts)
-            continue;
-        if (pts2 >= ist2->sub2video.end_pts || ist2->sub2video.initialize)
-            /* if we have hit the end of the current displayed subpicture,
-               or if we need to initialize the system, update the
-               overlayed subpicture and its start/end times */
-            sub2video_update(ist2, pts2 + 1, NULL);
-        for (j = 0, nb_reqs = 0; j < ist2->nb_filters; j++)
-            nb_reqs += av_buffersrc_get_nb_failed_requests(ist2->filters[j]->filter);
-        if (nb_reqs)
-            sub2video_push_ref(ist2, pts2);
-    }
-}
-
-static void sub2video_flush(InputStream *ist)
-{
-    int i;
-    int ret;
-
-    if (ist->sub2video.end_pts < INT64_MAX)
-        sub2video_update(ist, INT64_MAX, NULL);
-    for (i = 0; i < ist->nb_filters; i++) {
-        ret = av_buffersrc_add_frame(ist->filters[i]->filter, NULL);
-        if (ret != AVERROR_EOF && ret < 0)
-            av_log(NULL, AV_LOG_WARNING, "Flush the frame error.\n");
-    }
-}
-
-/* end of sub2video hack */
-
 static void term_exit_sigsafe(void)
 {
 #if HAVE_TERMIOS_H
@@ -525,7 +366,6 @@ static void ffmpeg_cleanup(int ret)
         avfilter_graph_free(&fg->graph);
         for (j = 0; j < fg->nb_inputs; j++) {
             InputFilter *ifilter = fg->inputs[j];
-            struct InputStream *ist = ifilter->ist;
 
             if (ifilter->frame_queue) {
                 AVFrame *frame;
@@ -534,12 +374,6 @@ static void ffmpeg_cleanup(int ret)
                 av_fifo_freep2(&ifilter->frame_queue);
             }
             av_freep(&ifilter->displaymatrix);
-            if (ist->sub2video.sub_queue) {
-                AVSubtitle sub;
-                while (av_fifo_read(ist->sub2video.sub_queue, &sub, 1) >= 0)
-                    avsubtitle_free(&sub);
-                av_fifo_freep2(&ist->sub2video.sub_queue);
-            }
             av_buffer_unref(&ifilter->hw_frames_ctx);
             av_freep(&ifilter->name);
             av_freep(&fg->inputs[j]);
@@ -560,7 +394,7 @@ static void ffmpeg_cleanup(int ret)
     }
     av_freep(&filtergraphs);
 
-    av_freep(&subtitle_out);
+    ////av_freep(&subtitle_out);
 
     /* close files */
     for (i = 0; i < nb_output_files; i++)
@@ -613,15 +447,19 @@ static void ffmpeg_cleanup(int ret)
     for (i = 0; i < nb_input_streams; i++) {
         InputStream *ist = input_streams[i];
 
+        if (ist->prev_sub.subtitle == ist->decoded_frame)
+            // Prevent double-free
+            ist->prev_sub.subtitle = NULL;
+
         av_frame_free(&ist->decoded_frame);
         av_packet_free(&ist->pkt);
         av_dict_free(&ist->decoder_opts);
-        avsubtitle_free(&ist->prev_sub.subtitle);
-        av_frame_free(&ist->sub2video.frame);
+        av_frame_free(&ist->prev_sub.subtitle);
         av_freep(&ist->filters);
         av_freep(&ist->hwaccel_device);
         av_freep(&ist->dts_buffer);
 
+        av_buffer_unref(&ist->subtitle_header);
         avcodec_free_context(&ist->dec_ctx);
 
         av_freep(&input_streams[i]);
@@ -991,33 +829,83 @@ static void do_audio_out(OutputFile *of, OutputStream *ost,
         exit_program(1);
 }
 
-static void do_subtitle_out(OutputFile *of,
-                            OutputStream *ost,
-                            AVSubtitle *sub)
+static void encode_subtitle_frame(OutputFile *of, OutputStream *ost, AVFrame *frame, AVPacket *pkt, int64_t pts_offset)
+{
+    AVCodecContext *enc = ost->enc_ctx;
+    int ret;
+
+        ost->frames_encoded++;
+
+        ret = avcodec_send_frame(enc, frame);
+        if (ret < 0)
+            goto error;
+
+        while (1) {
+            ret = avcodec_receive_packet(enc, pkt);
+            update_benchmark("encode_subtitles %d.%d", ost->file_index, ost->index);
+            if (ret == AVERROR(EAGAIN))
+                break;
+            if (ret < 0)
+                goto error;
+
+            if (debug_ts) {
+                av_log(NULL, AV_LOG_INFO, "encoder -> type:subtitles "
+                       "pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s\n",
+                       av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &enc->time_base),
+                       av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &enc->time_base));
+            }
+
+            pkt->pts  = av_rescale_q(frame->subtitle_timing.start_pts, AV_TIME_BASE_Q, ost->mux_timebase);
+            pkt->duration = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, ost->mux_timebase);
+            pkt->pts += pts_offset;
+
+            pkt->dts = pkt->pts;
+            output_packet(of, pkt, ost, 0);
+        }
+        ost->sync_opts++;
+        ost->frame_number++;
+
+    return;
+error:
+    av_log(NULL, AV_LOG_FATAL, "Subtitle encoding failed - Error code: %d\n", ret);
+    exit_program(1);
+}
+
+static void do_subtitle_out(OutputFile *of, OutputStream *ost, AVFrame *frame)
 {
-    int subtitle_out_max_size = 1024 * 1024;
-    int subtitle_out_size, nb, i;
+    int nb, i;
     AVCodecContext *enc;
     AVPacket *pkt = ost->pkt;
     int64_t pts;
 
-    if (sub->pts == AV_NOPTS_VALUE) {
+    if (!frame)
+        return;
+
+    av_log(NULL, AV_LOG_DEBUG, "do_subtitle_out: sub->pts: %"PRId64"  frame->pts: %"PRId64"\n", frame->subtitle_timing.start_pts, frame->pts);
+
+    if (frame->subtitle_timing.start_pts == AV_NOPTS_VALUE) {
         av_log(NULL, AV_LOG_ERROR, "Subtitle packets must have a pts\n");
         if (exit_on_error)
             exit_program(1);
         return;
     }
 
-    enc = ost->enc_ctx;
-
-    if (!subtitle_out) {
-        subtitle_out = av_malloc(subtitle_out_max_size);
-        if (!subtitle_out) {
-            av_log(NULL, AV_LOG_FATAL, "Failed to allocate subtitle_out\n");
-            exit_program(1);
-        }
+    if (frame->repeat_sub) {
+        av_log(NULL, AV_LOG_WARNING, "Ignoring repeated subtitle frame\n");
+        return;
     }
 
+    ////if (ost->last_subtitle_pts && ost->last_subtitle_pts == frame->subtitle_timing.start_pts) {
+    ////    av_log(NULL, AV_LOG_DEBUG, "Ignoring subtitle frame with duplicate subtitle_pts\n");
+    ////    return;
+    ////}
+
+    ost->last_subtitle_pts = frame->subtitle_timing.start_pts;
+
+    init_output_stream_wrapper(ost, frame, 1);
+
+    enc = ost->enc_ctx;
+
     /* Note: DVB subtitle need one packet to draw them and one other
        packet to clear them */
     /* XXX: signal it in the codec context ? */
@@ -1027,50 +915,38 @@ static void do_subtitle_out(OutputFile *of,
         nb = 1;
 
     /* shift timestamp to honor -ss and make check_recording_time() work with -t */
-    pts = sub->pts;
+    pts = frame->subtitle_timing.start_pts;
     if (output_files[ost->file_index]->start_time != AV_NOPTS_VALUE)
         pts -= output_files[ost->file_index]->start_time;
-    for (i = 0; i < nb; i++) {
-        unsigned save_num_rects = sub->num_rects;
 
-        ost->sync_opts = av_rescale_q(pts, AV_TIME_BASE_Q, enc->time_base);
-        if (!check_recording_time(ost))
-            return;
+    ost->sync_opts = av_rescale_q(pts, AV_TIME_BASE_Q, enc->time_base);
+    if (!check_recording_time(ost))
+        return;
 
-        sub->pts = pts;
-        // start_display_time is required to be 0
-        sub->pts               += av_rescale_q(sub->start_display_time, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q);
-        sub->end_display_time  -= sub->start_display_time;
-        sub->start_display_time = 0;
-        if (i == 1)
-            sub->num_rects = 0;
+    frame->subtitle_timing.start_pts = pts;
+
+    for (i = 0; i < nb; i++) {
+        const unsigned save_num_rects = frame->num_subtitle_areas;
+        int64_t pts_offset = 0;
 
         ost->frames_encoded++;
 
-        subtitle_out_size = avcodec_encode_subtitle(enc, subtitle_out,
-                                                    subtitle_out_max_size, sub);
         if (i == 1)
-            sub->num_rects = save_num_rects;
-        if (subtitle_out_size < 0) {
-            av_log(NULL, AV_LOG_FATAL, "Subtitle encoding failed\n");
-            exit_program(1);
-        }
+            frame->num_subtitle_areas = 0;
 
-        av_packet_unref(pkt);
-        pkt->data = subtitle_out;
-        pkt->size = subtitle_out_size;
-        pkt->pts  = av_rescale_q(sub->pts, AV_TIME_BASE_Q, ost->mux_timebase);
-        pkt->duration = av_rescale_q(sub->end_display_time, (AVRational){ 1, 1000 }, ost->mux_timebase);
         if (enc->codec_id == AV_CODEC_ID_DVB_SUBTITLE) {
             /* XXX: the pts correction is handled here. Maybe handling
                it in the codec would be better */
             if (i == 0)
-                pkt->pts += av_rescale_q(sub->start_display_time, (AVRational){ 1, 1000 }, ost->mux_timebase);
+                pts_offset = 0;
             else
-                pkt->pts += av_rescale_q(sub->end_display_time, (AVRational){ 1, 1000 }, ost->mux_timebase);
+                pts_offset = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, ost->mux_timebase);
         }
-        pkt->dts = pkt->pts;
-        output_packet(of, pkt, ost, 0);
+
+        encode_subtitle_frame(of, ost, frame, pkt, pts_offset);
+
+        if (i == 1)
+            frame->num_subtitle_areas = save_num_rects;
     }
 }
 
@@ -1375,8 +1251,26 @@ static int reap_filters(int flush)
                 }
                 do_audio_out(of, ost, filtered_frame);
                 break;
+            case AVMEDIA_TYPE_SUBTITLE:
+
+                if (filtered_frame->format == AV_SUBTITLE_FMT_ASS && !enc->subtitle_header
+                    && filtered_frame->subtitle_header) {
+                    const char *subtitle_header = (char *)filtered_frame->subtitle_header->data;
+                    enc->subtitle_header = (uint8_t *)av_strdup(subtitle_header);
+                    if (!enc->subtitle_header)
+                        return AVERROR(ENOMEM);
+                    enc->subtitle_header_size = strlen(subtitle_header);
+                }
+
+                if ((ost->enc_ctx->width == 0 || ost->enc_ctx->height == 0)
+                    && filter->inputs[0]->w > 0 && filter->inputs[0]->h > 0 ) {
+                    ost->enc_ctx->width = filter->inputs[0]->w;
+                    ost->enc_ctx->height = filter->inputs[0]->h;
+                }
+
+                do_subtitle_out(of, ost, filtered_frame);
+                break;
             default:
-                // TODO support subtitle filters
                 av_assert0(0);
             }
 
@@ -1928,7 +1822,8 @@ static int ifilter_has_all_input_formats(FilterGraph *fg)
     int i;
     for (i = 0; i < fg->nb_inputs; i++) {
         if (fg->inputs[i]->format < 0 && (fg->inputs[i]->type == AVMEDIA_TYPE_AUDIO ||
-                                          fg->inputs[i]->type == AVMEDIA_TYPE_VIDEO))
+                                          fg->inputs[i]->type == AVMEDIA_TYPE_VIDEO ||
+                                          fg->inputs[i]->type == AVMEDIA_TYPE_SUBTITLE))
             return 0;
     }
     return 1;
@@ -1955,6 +1850,18 @@ static int ifilter_send_frame(InputFilter *ifilter, AVFrame *frame, int keep_ref
     case AVMEDIA_TYPE_VIDEO:
         need_reinit |= ifilter->width  != frame->width ||
                        ifilter->height != frame->height;
+
+        if (need_reinit)
+            need_reinit = 1;
+        break;
+    case AVMEDIA_TYPE_SUBTITLE:
+        need_reinit |= ifilter->width  != frame->width ||
+                       ifilter->height != frame->height;
+
+        need_reinit &= (ifilter->width == 0 || ifilter->height == 0);
+
+        if (need_reinit)
+            need_reinit = 1;
         break;
     }
 
@@ -2026,12 +1933,9 @@ static int ifilter_send_eof(InputFilter *ifilter, int64_t pts)
             return ret;
     } else {
         // the filtergraph was never configured
-        if (ifilter->format < 0) {
-            ret = ifilter_parameters_from_codecpar(ifilter, ifilter->ist->st->codecpar);
-            if (ret < 0)
-                return ret;
-        }
-        if (ifilter->format < 0 && (ifilter->type == AVMEDIA_TYPE_AUDIO || ifilter->type == AVMEDIA_TYPE_VIDEO)) {
+        if (ifilter->format < 0)
+            ifilter_parameters_from_codecpar(ifilter, ifilter->ist->st->codecpar);
+        if (ifilter->format < 0 && (ifilter->type == AVMEDIA_TYPE_AUDIO || ifilter->type == AVMEDIA_TYPE_VIDEO || ifilter->type == AVMEDIA_TYPE_SUBTITLE)) {
             av_log(NULL, AV_LOG_ERROR, "Cannot determine format of input stream %d:%d after EOF\n", ifilter->ist->file_index, ifilter->ist->st->index);
             return AVERROR_INVALIDDATA;
         }
@@ -2069,7 +1973,7 @@ static int decode(AVCodecContext *avctx, AVFrame *frame, int *got_frame, AVPacke
 
 static int send_frame_to_filters(InputStream *ist, AVFrame *decoded_frame)
 {
-    int i, ret;
+    int i, ret = 0;
 
     av_assert1(ist->nb_filters > 0); /* ensure ret is initialized */
     for (i = 0; i < ist->nb_filters; i++) {
@@ -2270,79 +2174,200 @@ fail:
     return err < 0 ? err : ret;
 }
 
-static int transcode_subtitles(InputStream *ist, AVPacket *pkt, int *got_output,
+static void subtitle_send_kickoff(InputStream *ist, int64_t heartbeat_pts)
+{
+    AVFrame *frame;
+    int64_t pts;
+
+    frame = av_frame_alloc();
+    if (!frame) {
+        av_log(ist->dec_ctx, AV_LOG_ERROR, "Unable to alloc frame (out of memory).\n");
+        return;
+    }
+
+    frame->type = AVMEDIA_TYPE_SUBTITLE;
+    av_frame_get_buffer2(frame, 0);
+
+    frame->format = (uint16_t)ist->dec_ctx->subtitle_type;
+
+    pts = FFMAX(heartbeat_pts, ist->subtitle_kickoff.last_pts) + av_rescale_q(10, (AVRational){ 1, 1000 }, ist->st->time_base);
+
+    frame->width = ist->subtitle_kickoff.w;
+    frame->height = ist->subtitle_kickoff.h;
+    frame->subtitle_timing.start_pts = av_rescale_q(pts, ist->st->time_base, AV_TIME_BASE_Q);
+    frame->subtitle_timing.duration =  av_rescale_q(10, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q);
+    frame->pts = pts;
+
+    av_log(NULL, AV_LOG_WARNING, "subtitle_kickoff: call subtitle_resend_current %"PRId64" frame->format: %d\n", pts, frame->format);
+
+    ist->subtitle_kickoff.last_pts = pts;
+
+    send_frame_to_filters(ist, frame);
+
+    av_frame_free(&frame);
+}
+
+// Opposed to the earlier "subtitle hearbeat", this is primarily aimed at
+// sending an initial subtitle frame to the filters for propagating the initial
+// timing values and to avoid that too much time is spent on a single "HW" decoder
+// which could overflow its buffer pool otherwise
+static void subtitle_kickoff(InputStream *ist, int64_t pts)
+{
+    int i;
+    int64_t pts2;
+    int64_t diff;
+
+    if (ist->st->codecpar->codec_type != AVMEDIA_TYPE_VIDEO)
+        return;
+
+    for (i = 0; i < nb_input_streams; i++) {
+        InputStream *ist2 = input_streams[i];
+        unsigned j, nb_reqs;
+
+        if (!ist2->subtitle_kickoff.is_active)
+            continue;
+        /* It's primarily the initial send that matters and which propagates
+         * the right start times to subtitle filters depending on input from decoding */
+        diff = av_rescale_q(5000, (AVRational){ 1, 1000 }, ist2->st->time_base);
+        pts2 = av_rescale_q(pts, ist->st->time_base, ist2->st->time_base);
+
+        /* do not send a kickoff frame if the subtitle is already ahead */
+        if (pts2 - diff <= ist2->subtitle_kickoff.last_pts)
+            continue;
+
+        for (j = 0, nb_reqs = 0; j < ist2->nb_filters; j++) {
+            if (ist2->filters[j]->filter == NULL) {
+                subtitle_send_kickoff(ist2, pts2);
+                return;
+            }
+            nb_reqs += av_buffersrc_get_nb_failed_requests(ist2->filters[j]->filter);
+        }
+        if (nb_reqs) {
+            av_log(NULL, AV_LOG_WARNING, "subtitle_kickoff: resend - pts: %"PRIi64"\n", pts2);
+            subtitle_send_kickoff(ist2, pts2);
+        }
+    }
+}
+
+static InputStream *get_input_stream(OutputStream *ost)
+{
+    if (ost->source_index >= 0)
+        return input_streams[ost->source_index];
+    return NULL;
+}
+
+static int decode_subtitles(InputStream *ist, AVPacket *pkt, int *got_output,
                                int *decode_failed)
 {
-    AVSubtitle subtitle;
-    int free_sub = 1;
-    int i, ret = avcodec_decode_subtitle2(ist->dec_ctx,
-                                          &subtitle, got_output, pkt);
+    AVFrame *decoded_frame;
+    AVCodecContext *avctx = ist->dec_ctx;
+    int i = 0, ret = 0, err = 0;
+    int64_t pts;
+
+    if (!ist->decoded_frame && !((ist->decoded_frame = av_frame_alloc())))
+        return AVERROR(ENOMEM);
+    decoded_frame = ist->decoded_frame;
+    decoded_frame->type = AVMEDIA_TYPE_SUBTITLE;
+    decoded_frame->format = (uint16_t)avctx->subtitle_type;
+
+    if (!ist->subtitle_header && avctx->subtitle_header && avctx->subtitle_header_size > 0) {
+        ist->subtitle_header = av_buffer_allocz(avctx->subtitle_header_size + 1);
+        if (!ist->subtitle_header)
+            return AVERROR(ENOMEM);
+        memcpy(ist->subtitle_header->data, avctx->subtitle_header, avctx->subtitle_header_size);
+    }
+
+    ret = decode(avctx, decoded_frame, got_output, pkt);
 
-    check_decode_result(NULL, got_output, ret);
+    if (ret != AVERROR_EOF)
+        check_decode_result(NULL, got_output, ret);
 
     if (ret < 0 || !*got_output) {
         *decode_failed = 1;
-        if (!pkt->size)
-            sub2video_flush(ist);
+        if (!pkt->size) {
+            // Flush
+            for (i = 0; i < ist->nb_filters; i++) {
+                if (ist->filters[i]->filter != NULL) {
+                    ret = av_buffersrc_add_frame(ist->filters[i]->filter, NULL);
+                    if (ret != AVERROR_EOF && ret < 0)
+                        av_log(NULL, AV_LOG_WARNING, "Flush the frame error.\n");
+                }
+            }
+        }
         return ret;
     }
 
     if (ist->fix_sub_duration) {
-        int end = 1;
-        if (ist->prev_sub.got_output) {
-            end = av_rescale(subtitle.pts - ist->prev_sub.subtitle.pts,
-                             1000, AV_TIME_BASE);
-            if (end < ist->prev_sub.subtitle.end_display_time) {
-                av_log(ist->dec_ctx, AV_LOG_DEBUG,
-                       "Subtitle duration reduced from %"PRId32" to %d%s\n",
-                       ist->prev_sub.subtitle.end_display_time, end,
-                       end <= 0 ? ", dropping it" : "");
-                ist->prev_sub.subtitle.end_display_time = end;
+        int64_t end = 1;
+        if (ist->prev_sub.got_output && ist->prev_sub.subtitle) {
+
+            const int64_t duration = ist->prev_sub.subtitle->subtitle_timing.duration;
+            end = decoded_frame->subtitle_timing.start_pts - ist->prev_sub.subtitle->subtitle_timing.start_pts;
+
+            if (end < duration) {
+                av_log(avctx, AV_LOG_DEBUG, "Subtitle duration reduced from %"PRId64" to %"PRId64"%s\n",
+                    duration, end, end <= 0 ? ", dropping it" : "");
+                ist->prev_sub.subtitle->subtitle_timing.duration = end;
             }
         }
-        FFSWAP(int,        *got_output, ist->prev_sub.got_output);
-        FFSWAP(int,        ret,         ist->prev_sub.ret);
-        FFSWAP(AVSubtitle, subtitle,    ist->prev_sub.subtitle);
+        FFSWAP(int,        *got_output,        ist->prev_sub.got_output);
+        FFSWAP(int,        ret,                ist->prev_sub.ret);
+        FFSWAP(AVFrame*,   ist->decoded_frame, ist->prev_sub.subtitle);
+        decoded_frame = ist->decoded_frame;
         if (end <= 0)
-            goto out;
+            return (int)end;
     }
 
-    if (!*got_output)
+    if (!*got_output || !decoded_frame)
         return ret;
 
-    if (ist->sub2video.frame) {
-        sub2video_update(ist, INT64_MIN, &subtitle);
-    } else if (ist->nb_filters) {
-        if (!ist->sub2video.sub_queue)
-            ist->sub2video.sub_queue = av_fifo_alloc2(8, sizeof(AVSubtitle), AV_FIFO_FLAG_AUTO_GROW);
-        if (!ist->sub2video.sub_queue)
-            exit_program(1);
+    decoded_frame->type = AVMEDIA_TYPE_SUBTITLE;
 
-        ret = av_fifo_write(ist->sub2video.sub_queue, &subtitle, 1);
-        if (ret < 0)
-            exit_program(1);
-        free_sub = 0;
-    }
+    if (decoded_frame->format == AV_SUBTITLE_FMT_UNKNOWN)
+        decoded_frame->format = (uint16_t)avctx->subtitle_type;
+
+    if ((ret = av_buffer_replace(&decoded_frame->subtitle_header, ist->subtitle_header)) < 0)
+        return ret;
 
-    if (!subtitle.num_rects)
-        goto out;
+    decoded_frame->pts = av_rescale_q(decoded_frame->subtitle_timing.start_pts, AV_TIME_BASE_Q, ist->st->time_base);
 
-    ist->frames_decoded++;
+    pts     = av_rescale_q(decoded_frame->subtitle_timing.start_pts,
+                           AV_TIME_BASE_Q, ist->st->time_base);
 
-    for (i = 0; i < nb_output_streams; i++) {
-        OutputStream *ost = output_streams[i];
+    ist->subtitle_kickoff.last_pts = decoded_frame->pts = pts;
 
-        if (!check_output_constraints(ist, ost) || !ost->encoding_needed
-            || ost->enc->type != AVMEDIA_TYPE_SUBTITLE)
-            continue;
+    if (ist->nb_filters > 0) {
+        AVFrame *filter_frame = av_frame_clone(decoded_frame);
+        if (!filter_frame) {
+            err = AVERROR(ENOMEM);
+            goto end;
+        }
 
-        do_subtitle_out(output_files[ost->file_index], ost, &subtitle);
+        err = send_frame_to_filters(ist, filter_frame);
+        av_frame_free(&filter_frame);
     }
 
-out:
-    if (free_sub)
-        avsubtitle_free(&subtitle);
-    return ret;
+    if (err >= 0) {
+        for (i = 0; i < nb_output_streams; i++) {
+            OutputStream *ost = output_streams[i];
+            InputStream *ist_src = get_input_stream(ost);
+
+            if (!ist_src || !check_output_constraints(ist, ost)
+                || ist_src != ist
+                || !ost->encoding_needed
+                || ost->enc->type != AVMEDIA_TYPE_SUBTITLE)
+                continue;
+
+            if (ost->filter && ost->filter->filter->nb_inputs > 0)
+                continue;
+
+            do_subtitle_out(output_files[ost->file_index], ost, decoded_frame);
+        }
+    }
+
+end:
+    av_frame_unref(decoded_frame);
+    return err < 0 ? err : ret;
 }
 
 static int send_filter_eof(InputStream *ist)
@@ -2446,7 +2471,7 @@ static int process_input_packet(InputStream *ist, const AVPacket *pkt, int no_eo
         case AVMEDIA_TYPE_SUBTITLE:
             if (repeating)
                 break;
-            ret = transcode_subtitles(ist, avpkt, &got_output, &decode_failed);
+            ret = decode_subtitles(ist, avpkt, &got_output, &decode_failed);
             if (!pkt && ret >= 0)
                 ret = AVERROR_EOF;
             av_packet_unref(avpkt);
@@ -2663,13 +2688,6 @@ static int init_input_stream(int ist_index, char *error, int error_len)
     return 0;
 }
 
-static InputStream *get_input_stream(OutputStream *ost)
-{
-    if (ost->source_index >= 0)
-        return input_streams[ost->source_index];
-    return NULL;
-}
-
 static int compare_int64(const void *a, const void *b)
 {
     return FFDIFFSIGN(*(const int64_t *)a, *(const int64_t *)b);
@@ -3097,7 +3115,7 @@ static int init_output_stream_encode(OutputStream *ost, AVFrame *frame)
         break;
     case AVMEDIA_TYPE_SUBTITLE:
         enc_ctx->time_base = AV_TIME_BASE_Q;
-        if (!enc_ctx->width) {
+        if (!enc_ctx->width && ost->source_index >= 0) {
             enc_ctx->width     = input_streams[ost->source_index]->st->codecpar->width;
             enc_ctx->height    = input_streams[ost->source_index]->st->codecpar->height;
         }
@@ -3114,6 +3132,17 @@ static int init_output_stream_encode(OutputStream *ost, AVFrame *frame)
     return 0;
 }
 
+static enum AVSubtitleType get_subtitle_format(const AVCodecDescriptor *codec_descriptor)
+{
+    if(codec_descriptor->props & AV_CODEC_PROP_BITMAP_SUB)
+        return AV_SUBTITLE_FMT_BITMAP;
+
+    if(codec_descriptor->props & AV_CODEC_PROP_TEXT_SUB)
+        return AV_SUBTITLE_FMT_ASS;
+
+    return AV_SUBTITLE_FMT_UNKNOWN;
+}
+
 static int init_output_stream(OutputStream *ost, AVFrame *frame,
                               char *error, int error_len)
 {
@@ -3150,19 +3179,13 @@ static int init_output_stream(OutputStream *ost, AVFrame *frame,
         }
 
         if (ist && ist->dec->type == AVMEDIA_TYPE_SUBTITLE && ost->enc->type == AVMEDIA_TYPE_SUBTITLE) {
-            int input_props = 0, output_props = 0;
-            AVCodecDescriptor const *input_descriptor =
-                avcodec_descriptor_get(dec->codec_id);
-            AVCodecDescriptor const *output_descriptor =
-                avcodec_descriptor_get(ost->enc_ctx->codec_id);
-            if (input_descriptor)
-                input_props = input_descriptor->props & (AV_CODEC_PROP_TEXT_SUB | AV_CODEC_PROP_BITMAP_SUB);
-            if (output_descriptor)
-                output_props = output_descriptor->props & (AV_CODEC_PROP_TEXT_SUB | AV_CODEC_PROP_BITMAP_SUB);
-            if (input_props && output_props && input_props != output_props) {
-                snprintf(error, error_len,
-                         "Subtitle encoding currently only possible from text to text "
-                         "or bitmap to bitmap");
+            AVCodecDescriptor const *output_descriptor    = avcodec_descriptor_get(ost->enc_ctx->codec_id);
+            const enum AVSubtitleType in_subtitle_format  = (uint16_t)dec->subtitle_type;
+            const enum AVSubtitleType out_subtitle_format = output_descriptor ? get_subtitle_format(output_descriptor) : AV_SUBTITLE_FMT_UNKNOWN;
+
+            if (ist->nb_filters == 0 && in_subtitle_format != AV_SUBTITLE_FMT_UNKNOWN && out_subtitle_format != AV_SUBTITLE_FMT_UNKNOWN
+                && in_subtitle_format != out_subtitle_format) {
+                snprintf(error, error_len, "Subtitle encoding is only possible from text to text or bitmap to bitmap");
                 return AVERROR_INVALIDDATA;
             }
         }
@@ -3326,7 +3349,8 @@ static int transcode_init(void)
     for (i = 0; i < nb_output_streams; i++) {
         if (!output_streams[i]->stream_copy &&
             (output_streams[i]->enc_ctx->codec_type == AVMEDIA_TYPE_VIDEO ||
-             output_streams[i]->enc_ctx->codec_type == AVMEDIA_TYPE_AUDIO))
+             output_streams[i]->enc_ctx->codec_type == AVMEDIA_TYPE_AUDIO ||
+             output_streams[i]->enc_ctx->codec_type == AVMEDIA_TYPE_SUBTITLE))
             continue;
 
         ret = init_output_stream_wrapper(output_streams[i], NULL, 0);
@@ -3497,7 +3521,7 @@ static OutputStream *choose_output(void)
                        av_rescale_q(ost->last_mux_dts, ost->st->time_base,
                                     AV_TIME_BASE_Q);
         if (ost->last_mux_dts == AV_NOPTS_VALUE)
-            av_log(NULL, AV_LOG_DEBUG,
+            av_log(NULL, AV_LOG_INFO,
                 "cur_dts is invalid st:%d (%d) [init:%d i_done:%d finish:%d] (this is harmless if it occurs once at the start per stream)\n",
                 ost->st->index, ost->st->id, ost->initialized, ost->inputs_done, ost->finished);
 
@@ -4166,7 +4190,7 @@ static int process_input(int file_index)
                av_ts2timestr(input_files[ist->file_index]->ts_offset, &AV_TIME_BASE_Q));
     }
 
-    sub2video_heartbeat(ist, pkt->pts);
+    subtitle_kickoff(ist, pkt->pts);
 
     process_input_packet(ist, pkt, 0);
 
@@ -4378,6 +4402,7 @@ static int transcode(void)
     /* at the end of stream, we must flush the decoder buffers */
     for (i = 0; i < nb_input_streams; i++) {
         ist = input_streams[i];
+        ist->subtitle_kickoff.is_active = 0;
         if (!input_files[ist->file_index]->eof_reached) {
             process_input_packet(ist, NULL, 0);
         }
diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h
index 69a368b8d1..74feff734f 100644
--- a/fftools/ffmpeg.h
+++ b/fftools/ffmpeg.h
@@ -353,17 +353,16 @@ typedef struct InputStream {
     struct { /* previous decoded subtitle and related variables */
         int got_output;
         int ret;
-        AVSubtitle subtitle;
+        AVFrame *subtitle;
     } prev_sub;
 
-    struct sub2video {
+    struct subtitle_kickoff {
+        int is_active;
         int64_t last_pts;
-        int64_t end_pts;
-        AVFifo *sub_queue;    ///< queue of AVSubtitle* before filter init
-        AVFrame *frame;
         int w, h;
-        unsigned int initialize; ///< marks if sub2video_update should force an initialization
-    } sub2video;
+    } subtitle_kickoff;
+
+    AVBufferRef *subtitle_header;
 
     /* decoded data from this stream goes into all those filters
      * currently video and audio only */
@@ -468,6 +467,8 @@ typedef struct OutputStream {
     int64_t first_pts;
     /* dts of the last packet sent to the muxer */
     int64_t last_mux_dts;
+    /* subtitle_pts values of the last subtitle frame having arrived for encoding */
+    int64_t last_subtitle_pts;
     // the timebase of the packets sent to the muxer
     AVRational mux_timebase;
     AVRational enc_timebase;
@@ -675,8 +676,6 @@ int filtergraph_is_simple(FilterGraph *fg);
 int init_simple_filtergraph(InputStream *ist, OutputStream *ost);
 int init_complex_filtergraph(FilterGraph *fg);
 
-void sub2video_update(InputStream *ist, int64_t heartbeat_pts, AVSubtitle *sub);
-
 int ifilter_parameters_from_frame(InputFilter *ifilter, const AVFrame *frame);
 
 int ffmpeg_parse_options(int argc, char **argv);
diff --git a/fftools/ffmpeg_filter.c b/fftools/ffmpeg_filter.c
index 0845c631a5..fe03d06373 100644
--- a/fftools/ffmpeg_filter.c
+++ b/fftools/ffmpeg_filter.c
@@ -22,6 +22,8 @@
 
 #include "ffmpeg.h"
 
+#include "libavutil/ass_split_internal.h"
+
 #include "libavfilter/avfilter.h"
 #include "libavfilter/buffersink.h"
 #include "libavfilter/buffersrc.h"
@@ -30,11 +32,9 @@
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
 #include "libavutil/channel_layout.h"
-#include "libavutil/display.h"
 #include "libavutil/opt.h"
 #include "libavutil/pixdesc.h"
 #include "libavutil/pixfmt.h"
-#include "libavutil/imgutils.h"
 #include "libavutil/samplefmt.h"
 
 // FIXME: YUV420P etc. are actually supported with full color range,
@@ -232,9 +232,8 @@ static void init_input_filter(FilterGraph *fg, AVFilterInOut *in)
     InputFilter *ifilter;
     int i;
 
-    // TODO: support other filter types
-    if (type != AVMEDIA_TYPE_VIDEO && type != AVMEDIA_TYPE_AUDIO) {
-        av_log(NULL, AV_LOG_FATAL, "Only video and audio filters supported "
+    if (type != AVMEDIA_TYPE_VIDEO && type != AVMEDIA_TYPE_AUDIO && type != AVMEDIA_TYPE_SUBTITLE) {
+        av_log(NULL, AV_LOG_FATAL, "Only video, audio and subtitle filters supported "
                "currently.\n");
         exit_program(1);
     }
@@ -255,8 +254,9 @@ static void init_input_filter(FilterGraph *fg, AVFilterInOut *in)
         for (i = 0; i < s->nb_streams; i++) {
             enum AVMediaType stream_type = s->streams[i]->codecpar->codec_type;
             if (stream_type != type &&
-                !(stream_type == AVMEDIA_TYPE_SUBTITLE &&
-                  type == AVMEDIA_TYPE_VIDEO /* sub2video hack */))
+                // in the followng case we auto-insert the graphicsub2video conversion filter
+                // for retaining compatibility with the previous sub2video hack
+                !(stream_type == AVMEDIA_TYPE_SUBTITLE && type == AVMEDIA_TYPE_VIDEO))
                 continue;
             if (check_stream_specifier(s, s->streams[i], *p == ':' ? p + 1 : p) == 1) {
                 st = s->streams[i];
@@ -361,6 +361,21 @@ static int insert_trim(int64_t start_time, int64_t duration,
     const char *name = (type == AVMEDIA_TYPE_VIDEO) ? "trim" : "atrim";
     int ret = 0;
 
+    switch (type) {
+    case AVMEDIA_TYPE_VIDEO:
+        name = "trim";
+        break;
+    case AVMEDIA_TYPE_AUDIO:
+        name = "atrim";
+        break;
+    case AVMEDIA_TYPE_SUBTITLE:
+        name = "strim";
+        break;
+    default:
+        av_log(NULL, AV_LOG_ERROR, "insert_trim: Invalid media type: %d\n", type);
+        return AVERROR_INVALIDDATA;
+    }
+
     if (duration == INT64_MAX && start_time == AV_NOPTS_VALUE)
         return 0;
 
@@ -423,6 +438,40 @@ static int insert_filter(AVFilterContext **last_filter, int *pad_idx,
     return 0;
 }
 
+static int configure_output_subtitle_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out)
+{
+    OutputStream *ost = ofilter->ost;
+    OutputFile    *of = output_files[ost->file_index];
+    AVFilterContext *last_filter = out->filter_ctx;
+    int pad_idx = out->pad_idx;
+    int ret;
+    char name[255];
+
+    snprintf(name, sizeof(name), "out_%d_%d", ost->file_index, ost->index);
+    ret = avfilter_graph_create_filter(&ofilter->filter,
+                                       avfilter_get_by_name("sbuffersink"),
+                                       name, NULL, NULL, fg->graph);
+
+    if (ret < 0) {
+        av_log(NULL, AV_LOG_ERROR, "Unable to create filter sbuffersink\n");
+        return ret;
+    }
+
+    snprintf(name, sizeof(name), "trim_out_%d_%d",
+             ost->file_index, ost->index);
+    ret = insert_trim(of->start_time, of->recording_time,
+                      &last_filter, &pad_idx, name);
+    if (ret < 0)
+        return ret;
+
+    ////ost->st->codecpar->codec_tag = MKTAG('a', 's', 's', 's');
+
+    if ((ret = avfilter_link(last_filter, pad_idx, ofilter->filter, 0)) < 0)
+        return ret;
+
+    return 0;
+}
+
 static int configure_output_video_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out)
 {
     OutputStream *ost = ofilter->ost;
@@ -604,7 +653,8 @@ static int configure_output_audio_filter(FilterGraph *fg, OutputFilter *ofilter,
         int i;
 
         for (i=0; i<of->ctx->nb_streams; i++)
-            if (of->ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
+            if (of->ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ||
+                of->ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)
                 break;
 
         if (i<of->ctx->nb_streams) {
@@ -638,6 +688,7 @@ static int configure_output_filter(FilterGraph *fg, OutputFilter *ofilter,
     switch (avfilter_pad_get_type(out->filter_ctx->output_pads, out->pad_idx)) {
     case AVMEDIA_TYPE_VIDEO: return configure_output_video_filter(fg, ofilter, out);
     case AVMEDIA_TYPE_AUDIO: return configure_output_audio_filter(fg, ofilter, out);
+    case AVMEDIA_TYPE_SUBTITLE: return configure_output_subtitle_filter(fg, ofilter, out);
     default: av_assert0(0); return 0;
     }
 }
@@ -657,51 +708,154 @@ void check_filter_outputs(void)
     }
 }
 
-static int sub2video_prepare(InputStream *ist, InputFilter *ifilter)
+static int configure_input_subtitle_filter(FilterGraph *fg, InputFilter *ifilter,
+                                        AVFilterInOut *in)
 {
-    AVFormatContext *avf = input_files[ist->file_index]->ctx;
-    int i, w, h;
+    AVFilterContext *last_filter;
+    const AVFilter *buffer_filt = avfilter_get_by_name("sbuffer");
+    InputStream *ist = ifilter->ist;
+    InputFile     *f = input_files[ist->file_index];
+    AVBPrint args;
+    char name[255];
+    int ret, pad_idx = 0;
+    int w, h;
+    int64_t tsoffset = 0;
+    AVBufferSrcParameters *par = av_buffersrc_parameters_alloc();
+    enum AVMediaType media_type;
+
+    if (!par)
+        return AVERROR(ENOMEM);
+
+    par->format = AV_PIX_FMT_NONE;
+
+    if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) {
+        av_log(NULL, AV_LOG_ERROR, "Cannot connect subtitle filter to audio input\n");
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+
+    if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO) {
+        av_log(NULL, AV_LOG_ERROR, "Cannot connect subtitle filter to video input\n");
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+
+    if (ist->st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE && ist->dec_ctx) {
+        // For subtitles, we need to set the format here. Would we leave the format
+        // at -1, it would delay processing (ifilter_has_all_input_formats()) until
+        // the first subtile frame arrives, which could never happen in the worst case
+        ifilter->format = (uint16_t)ist->dec_ctx->subtitle_type;
+    }
+
+    ist->subtitle_kickoff.is_active = 1;
 
-    /* Compute the size of the canvas for the subtitles stream.
-       If the subtitles codecpar has set a size, use it. Otherwise use the
-       maximum dimensions of the video streams in the same file. */
     w = ifilter->width;
     h = ifilter->height;
+
     if (!(w && h)) {
-        for (i = 0; i < avf->nb_streams; i++) {
-            if (avf->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
-                w = FFMAX(w, avf->streams[i]->codecpar->width);
-                h = FFMAX(h, avf->streams[i]->codecpar->height);
+        w = ist->dec_ctx->width;
+        h = ist->dec_ctx->height;
+    }
+
+    if (!(w && h) && ist->dec_ctx->subtitle_header) {
+        ASSSplitContext *ass_ctx = avpriv_ass_split((char *)ist->dec_ctx->subtitle_header);
+        ASS *ass = (ASS *)ass_ctx;
+        w = ass->script_info.play_res_x;
+        h = ass->script_info.play_res_y;
+        avpriv_ass_split_free(ass_ctx);
+    }
+
+    ist->subtitle_kickoff.w = w;
+    ist->subtitle_kickoff.h = h;
+    av_log(ifilter, AV_LOG_INFO, "subtitle input filter: decoding size %dx%d\n", ist->subtitle_kickoff.w, ist->subtitle_kickoff.h);
+
+    ifilter->width = w;
+    ifilter->height = h;
+    ist->dec_ctx->width = w;
+    ist->dec_ctx->height = h;
+
+    ist->subtitle_kickoff.last_pts = INT64_MIN;
+
+    snprintf(name, sizeof(name), "graph %d subtitle input from stream %d:%d", fg->index,
+             ist->file_index, ist->st->index);
+
+
+    av_bprint_init(&args, 0, AV_BPRINT_SIZE_AUTOMATIC);
+    av_bprintf(&args,
+             "subtitle_type=%d:width=%d:height=%d:time_base=%d/%d:",
+             ifilter->format, ifilter->width, ifilter->height,
+             ist->st->time_base.num, ist->st->time_base.den);
+    if ((ret = avfilter_graph_create_filter(&ifilter->filter, buffer_filt, name,
+                                            args.str, NULL, fg->graph)) < 0)
+        goto fail;
+
+    par->hw_frames_ctx = ifilter->hw_frames_ctx;
+    par->format = ifilter->format;
+    par->width = ifilter->width;
+    par->height = ifilter->height;
+
+    ret = av_buffersrc_parameters_set(ifilter->filter, par);
+    if (ret < 0)
+        goto fail;
+    av_freep(&par);
+    last_filter = ifilter->filter;
+
+    // This is for sub2video compatibility
+    media_type = avfilter_pad_get_type(in->filter_ctx->input_pads, in->pad_idx);
+    if (media_type == AVMEDIA_TYPE_VIDEO) {
+        int subscale_w = w, subscale_h = h;
+
+        av_log(NULL, AV_LOG_INFO, "Auto-inserting subfeed filter\n");
+        ret = insert_filter(&last_filter, &pad_idx, "subfeed", NULL);
+        if (ret < 0)
+            return ret;
+
+        if (!(subscale_w && subscale_h)) {
+            // If the subtitle frame size is unknown, try to find a video input
+            // and use its size for adding a subscale filter
+            for (int i = 0; i < fg->nb_inputs; i++) {
+                InputFilter *input = fg->inputs[i];
+                if (input->type == AVMEDIA_TYPE_VIDEO && input->width && input->height) {
+                    subscale_w = input->width;
+                    subscale_h = input->height;
+                    break;
+                }
             }
         }
-        if (!(w && h)) {
-            w = FFMAX(w, 720);
-            h = FFMAX(h, 576);
-        }
-        av_log(avf, AV_LOG_INFO, "sub2video: using %dx%d canvas\n", w, h);
-    }
-    ist->sub2video.w = ifilter->width  = w;
-    ist->sub2video.h = ifilter->height = h;
 
-    ifilter->width  = ist->dec_ctx->width  ? ist->dec_ctx->width  : ist->sub2video.w;
-    ifilter->height = ist->dec_ctx->height ? ist->dec_ctx->height : ist->sub2video.h;
+        if (subscale_w && subscale_h) {
+            char subscale_params[64];
+            snprintf(subscale_params, sizeof(subscale_params), "w=%d:h=%d", subscale_w, subscale_h);
+            ret = insert_filter(&last_filter, &pad_idx, "subscale", subscale_params);
+            if (ret < 0)
+                return ret;
+        }
 
-    /* rectangles are AV_PIX_FMT_PAL8, but we have no guarantee that the
-       palettes for all rectangles are identical or compatible */
-    ifilter->format = AV_PIX_FMT_RGB32;
+        av_log(NULL, AV_LOG_INFO, "Auto-inserting graphicsub2video filter\n");
+        ret = insert_filter(&last_filter, &pad_idx, "graphicsub2video", NULL);
+        if (ret < 0)
+            return ret;
+    }
 
-    ist->sub2video.frame = av_frame_alloc();
-    if (!ist->sub2video.frame)
-        return AVERROR(ENOMEM);
-    ist->sub2video.last_pts = INT64_MIN;
-    ist->sub2video.end_pts  = INT64_MIN;
+    if (copy_ts) {
+        tsoffset = f->start_time == AV_NOPTS_VALUE ? 0 : f->start_time;
+        if (!start_at_zero && f->ctx->start_time != AV_NOPTS_VALUE)
+            tsoffset += f->ctx->start_time;
+    }
+    ret = insert_trim(((f->start_time == AV_NOPTS_VALUE) || !f->accurate_seek) ?
+                      AV_NOPTS_VALUE : tsoffset, f->recording_time,
+                      &last_filter, &pad_idx, name);
+    if (ret < 0)
+        return ret;
 
-    /* sub2video structure has been (re-)initialized.
-       Mark it as such so that the system will be
-       initialized with the first received heartbeat. */
-    ist->sub2video.initialize = 1;
+    if ((ret = avfilter_link(last_filter, 0, in->filter_ctx, in->pad_idx)) < 0)
+        return ret;
 
     return 0;
+fail:
+    av_freep(&par);
+
+    return ret;
 }
 
 static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter,
@@ -720,8 +874,15 @@ static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter,
     char name[255];
     int ret, pad_idx = 0;
     int64_t tsoffset = 0;
-    AVBufferSrcParameters *par = av_buffersrc_parameters_alloc();
+    AVBufferSrcParameters *par;
+
+    if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_SUBTITLE) {
+        // Automatically insert conversion filter to retain compatibility
+        // with sub2video command lines
+        return configure_input_subtitle_filter(fg, ifilter, in);
+    }
 
+    par = av_buffersrc_parameters_alloc();
     if (!par)
         return AVERROR(ENOMEM);
     memset(par, 0, sizeof(*par));
@@ -736,12 +897,6 @@ static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter,
     if (!fr.num)
         fr = av_guess_frame_rate(input_files[ist->file_index]->ctx, ist->st, NULL);
 
-    if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_SUBTITLE) {
-        ret = sub2video_prepare(ist, ifilter);
-        if (ret < 0)
-            goto fail;
-    }
-
     sar = ifilter->sample_aspect_ratio;
     if(!sar.den)
         sar = (AVRational){0,1};
@@ -753,7 +908,7 @@ static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter,
              tb.num, tb.den, sar.num, sar.den);
     if (fr.num && fr.den)
         av_bprintf(&args, ":frame_rate=%d/%d", fr.num, fr.den);
-    snprintf(name, sizeof(name), "graph %d input from stream %d:%d", fg->index,
+    snprintf(name, sizeof(name), "graph %d video input from stream %d:%d", fg->index,
              ist->file_index, ist->st->index);
 
 
@@ -952,6 +1107,7 @@ static int configure_input_filter(FilterGraph *fg, InputFilter *ifilter,
     switch (avfilter_pad_get_type(in->filter_ctx->input_pads, in->pad_idx)) {
     case AVMEDIA_TYPE_VIDEO: return configure_input_video_filter(fg, ifilter, in);
     case AVMEDIA_TYPE_AUDIO: return configure_input_audio_filter(fg, ifilter, in);
+    case AVMEDIA_TYPE_SUBTITLE: return configure_input_subtitle_filter(fg, ifilter, in);
     default: av_assert0(0); return 0;
     }
 }
@@ -969,8 +1125,9 @@ static void cleanup_filtergraph(FilterGraph *fg)
 static int filter_is_buffersrc(const AVFilterContext *f)
 {
     return f->nb_inputs == 0 &&
-           (!strcmp(f->filter->name, "buffer") ||
-            !strcmp(f->filter->name, "abuffer"));
+           (!strcmp(f->filter->name, "buffersrc") ||
+            !strcmp(f->filter->name, "abuffersrc") ||
+            !strcmp(f->filter->name, "sbuffersrc"));
 }
 
 static int graph_is_meta(AVFilterGraph *graph)
@@ -1147,18 +1304,6 @@ int configure_filtergraph(FilterGraph *fg)
         }
     }
 
-    /* process queued up subtitle packets */
-    for (i = 0; i < fg->nb_inputs; i++) {
-        InputStream *ist = fg->inputs[i]->ist;
-        if (ist->sub2video.sub_queue && ist->sub2video.frame) {
-            AVSubtitle tmp;
-            while (av_fifo_read(ist->sub2video.sub_queue, &tmp, 1) >= 0) {
-                sub2video_update(ist, INT64_MIN, &tmp);
-                avsubtitle_free(&tmp);
-            }
-        }
-    }
-
     return 0;
 
 fail:
@@ -1178,6 +1323,7 @@ int ifilter_parameters_from_frame(InputFilter *ifilter, const AVFrame *frame)
     ifilter->width               = frame->width;
     ifilter->height              = frame->height;
     ifilter->sample_aspect_ratio = frame->sample_aspect_ratio;
+    ifilter->type                = frame->type;
 
     ifilter->sample_rate         = frame->sample_rate;
     ret = av_channel_layout_copy(&ifilter->ch_layout, &frame->ch_layout);
diff --git a/fftools/ffmpeg_hw.c b/fftools/ffmpeg_hw.c
index 14e702bd92..be69d54aaf 100644
--- a/fftools/ffmpeg_hw.c
+++ b/fftools/ffmpeg_hw.c
@@ -449,7 +449,7 @@ int hw_device_setup_for_encode(OutputStream *ost)
     AVBufferRef *frames_ref = NULL;
     int i;
 
-    if (ost->filter) {
+    if (ost->filter && ost->filter->filter) {
         frames_ref = av_buffersink_get_hw_frames_ctx(ost->filter->filter);
         if (frames_ref &&
             ((AVHWFramesContext*)frames_ref->data)->format ==
diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c
index e08455478f..5a52f355a0 100644
--- a/fftools/ffmpeg_opt.c
+++ b/fftools/ffmpeg_opt.c
@@ -1000,6 +1000,7 @@ static void add_input_streams(OptionsContext *o, AVFormatContext *ic)
                 av_log(NULL, AV_LOG_FATAL, "Invalid canvas size: %s.\n", canvas_size);
                 exit_program(1);
             }
+            ist->subtitle_kickoff.is_active = 1;
             break;
         }
         case AVMEDIA_TYPE_ATTACHMENT:
@@ -1715,11 +1716,15 @@ static char *get_ost_filters(OptionsContext *o, AVFormatContext *oc,
 
     if (ost->filters_script)
         return read_file(ost->filters_script);
-    else if (ost->filters)
+    if (ost->filters)
         return av_strdup(ost->filters);
 
-    return av_strdup(st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ?
-                     "null" : "anull");
+    switch (st->codecpar->codec_type) {
+    case AVMEDIA_TYPE_VIDEO: return av_strdup("null");
+    case AVMEDIA_TYPE_AUDIO: return av_strdup("anull");
+    case AVMEDIA_TYPE_SUBTITLE: return av_strdup("snull");
+    default: av_assert0(0); return NULL;
+    }
 }
 
 static void check_streamcopy_filters(OptionsContext *o, AVFormatContext *oc,
@@ -2120,6 +2125,9 @@ static OutputStream *new_subtitle_stream(OptionsContext *o, AVFormatContext *oc,
 
     subtitle_enc->codec_type = AVMEDIA_TYPE_SUBTITLE;
 
+    MATCH_PER_STREAM_OPT(filter_scripts, str, ost->filters_script, oc, st);
+    MATCH_PER_STREAM_OPT(filters,        str, ost->filters,        oc, st);
+
     if (!ost->stream_copy) {
         char *frame_size = NULL;
 
@@ -2128,6 +2136,10 @@ static OutputStream *new_subtitle_stream(OptionsContext *o, AVFormatContext *oc,
             av_log(NULL, AV_LOG_FATAL, "Invalid frame size: %s.\n", frame_size);
             exit_program(1);
         }
+
+        ost->avfilter = get_ost_filters(o, oc, ost);
+        if (!ost->avfilter)
+            exit_program(1);
     }
 
     return ost;
@@ -2273,8 +2285,9 @@ static void init_output_filter(OutputFilter *ofilter, OptionsContext *o,
     switch (ofilter->type) {
     case AVMEDIA_TYPE_VIDEO: ost = new_video_stream(o, oc, -1); break;
     case AVMEDIA_TYPE_AUDIO: ost = new_audio_stream(o, oc, -1); break;
+    case AVMEDIA_TYPE_SUBTITLE: ost = new_subtitle_stream(o, oc, -1); break;
     default:
-        av_log(NULL, AV_LOG_FATAL, "Only video and audio filters are supported "
+        av_log(NULL, AV_LOG_FATAL, "Only video, audio and subtitle filters are supported "
                "currently.\n");
         exit_program(1);
     }
@@ -2679,7 +2692,8 @@ loop_end:
             ist->processing_needed = 1;
 
             if (ost->st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ||
-                ost->st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
+                ost->st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO ||
+                ost->st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) {
                 err = init_simple_filtergraph(ist, ost);
                 if (err < 0) {
                     av_log(NULL, AV_LOG_ERROR,
@@ -2724,6 +2738,10 @@ loop_end:
                 } else if (ost->enc->ch_layouts) {
                     f->ch_layouts = ost->enc->ch_layouts;
                 }
+                break;
+            case AVMEDIA_TYPE_SUBTITLE:
+                f->format     = ost->enc_ctx->subtitle_type;
+
                 break;
             }
         }
diff --git a/tests/ref/fate/filter-overlay-dvdsub-2397 b/tests/ref/fate/filter-overlay-dvdsub-2397
index 7df4f50776..7b827956f1 100644
--- a/tests/ref/fate/filter-overlay-dvdsub-2397
+++ b/tests/ref/fate/filter-overlay-dvdsub-2397
@@ -489,368 +489,368 @@
 1,       3877,       3877,       10,     2013, 0x95a39f9c
 1,       3887,       3887,       10,     2013, 0x4f7ea123
 1,       3897,       3897,       10,     2013, 0x9efb9ba1
-0,        117,        117,        1,   518400, 0xbf8523da
+0,        117,        117,        1,   518400, 0xc44e1d4c
 1,       3907,       3907,       10,     2013, 0xf395b2cd
 1,       3917,       3917,       10,     2013, 0x261a881e
 1,       3927,       3927,       10,     2013, 0x7f2d9f72
 1,       3937,       3937,       10,     2013, 0x0105b38d
-0,        118,        118,        1,   518400, 0x41890ed6
+0,        118,        118,        1,   518400, 0xad1a084c
 1,       3952,       3952,       10,     2013, 0x0e5db67e
 1,       3962,       3962,       10,     2013, 0xfc9baf97
-0,        119,        119,        1,   518400, 0x588534fc
+0,        119,        119,        1,   518400, 0x52d52e73
 1,       3972,       3972,       10,     2013, 0x8e02a1b1
 1,       3982,       3982,       10,     2013, 0x6eecaac8
 1,       3992,       3992,       10,     2013, 0xf5558f0c
 1,       4002,       4002,       10,     2013, 0x512ba99b
-0,        120,        120,        1,   518400, 0x2145ebc1
+0,        120,        120,        1,   518400, 0xc732e546
 1,       4012,       4012,       10,     2013, 0x932b9932
 1,       4022,       4022,       10,     2013, 0xc01ea987
-0,        121,        121,        1,   518400, 0x28bca595
+0,        121,        121,        1,   518400, 0xc36f9f14
 1,       4038,       4038,       10,     2013, 0x10879cf7
 1,       4048,       4048,       10,     2013, 0x90679338
 1,       4058,       4058,       10,     2013, 0x077d8a9e
 1,       4068,       4068,       10,     2013, 0x969fa57c
-0,        122,        122,        1,   518400, 0x77dc951e
+0,        122,        122,        1,   518400, 0x78428e8b
 1,       4078,       4078,       10,     2013, 0xe049ab07
 1,       4088,       4088,       10,     2013, 0xf535b3b3
 1,       4098,       4098,       10,     2013, 0xfe76bd37
-0,        123,        123,        1,   518400, 0xe8924c17
+0,        123,        123,        1,   518400, 0xf0f8458d
 1,       4108,       4108,       10,     2013, 0xde79ad8c
 1,       4123,       4123,       10,     2013, 0xe89b9c47
 1,       4133,       4133,       10,     2013, 0xc570b0f0
-0,        124,        124,        1,   518400, 0xadb4cccc
+0,        124,        124,        1,   518400, 0x7083c653
 1,       4143,       4143,       10,     2013, 0xee709cd9
 1,       4153,       4153,       10,     2013, 0xcfe5afab
 1,       4163,       4163,       10,     2013, 0x98ff8ce4
-0,        125,        125,        1,   518400, 0x1d7b56ac
+0,        125,        125,        1,   518400, 0xa105502c
 1,       4173,       4173,       10,     2013, 0x9d19b44c
 1,       4183,       4183,       10,     2013, 0x4349917a
 1,       4193,       4193,       10,     2013, 0xbf54a59a
-0,        126,        126,        1,   518400, 0xad5739a4
+0,        126,        126,        1,   518400, 0xd411331a
 1,       4208,       4208,       10,     2013, 0xc4a399e0
 1,       4218,       4218,       10,     2013, 0x1bf58ff0
 1,       4228,       4228,       10,     2013, 0x3518ac56
-0,        127,        127,        1,   518400, 0x2733d35a
+0,        127,        127,        1,   518400, 0x83b0ccdb
 1,       4238,       4238,       10,     2013, 0xcd38c1de
 1,       4248,       4248,       10,     2013, 0xbe7d9c4d
 1,       4258,       4258,       10,     2013, 0xe113a306
 1,       4268,       4268,       10,     2013, 0x083197ea
-0,        128,        128,        1,   518400, 0x78e76da2
+0,        128,        128,        1,   518400, 0xa9be671a
 1,       4278,       4278,       10,     2013, 0x1929b1eb
 1,       4294,       4294,       10,     2013, 0x5d6ea5af
 1,       4304,       4304,       10,     2013, 0x05519d53
-0,        129,        129,        1,   518400, 0x6c076013
+0,        129,        129,        1,   518400, 0xaeb75983
 1,       4314,       4314,       10,     2013, 0x5773b380
 1,       4324,       4324,       10,     2013, 0xaa70a8f5
 1,       4334,       4334,       10,     2013, 0x990db0ec
-0,        130,        130,        1,   518400, 0x7854f2b1
+0,        130,        130,        1,   518400, 0x81f8ec13
 1,       4344,       4344,       10,     2013, 0x91d3a623
 1,       4354,       4354,       10,     2013, 0xc91f9824
 1,       4364,       4364,       10,     2013, 0x1d058abf
-0,        131,        131,        1,   518400, 0xd2ae1ecd
+0,        131,        131,        1,   518400, 0x8aaa1839
 1,       4379,       4379,       10,     2013, 0x8de1b8d5
 1,       4389,       4389,       10,     2013, 0x7872b06b
 1,       4399,       4399,       10,     2013, 0xa084c203
-0,        132,        132,        1,   518400, 0xf5eab38d
+0,        132,        132,        1,   518400, 0xc98bacf5
 1,       4409,       4409,       10,     2013, 0xff90ae8d
 1,       4419,       4419,       10,     2013, 0x61dead8e
 1,       4429,       4429,       10,     2013, 0xee76b284
-0,        133,        133,        1,   518400, 0x994d3e9c
+0,        133,        133,        1,   518400, 0x31083804
 1,       4439,       4439,       10,     2013, 0xe888af7f
 1,       4449,       4449,       10,     2013, 0x5d57b115
 1,       4464,       4464,       10,     2013, 0xcdbfb1d0
-0,        134,        134,        1,   518400, 0x95ab705a
+0,        134,        134,        1,   518400, 0x540a69dc
 1,       4474,       4474,       10,     2013, 0x2e28a952
 1,       4484,       4484,       10,     2013, 0x4795a994
 1,       4494,       4494,       10,     2013, 0x7e7ea304
 1,       4504,       4504,       10,     2013, 0x9502c1e1
-0,        135,        135,        1,   518400, 0x3c83c5ce
+0,        135,        135,        1,   518400, 0x80d3bf46
 1,       4514,       4514,       10,     2013, 0xf7c78ab2
 1,       4524,       4524,       10,     2013, 0x24049816
 1,       4534,       4534,       10,     2013, 0x52089dcf
-0,        136,        136,        1,   518400, 0xfa22c508
+0,        136,        136,        1,   518400, 0x2967be7f
 1,       4550,       4550,       10,     2013, 0x2150a0b1
 1,       4560,       4560,       10,     2013, 0x3c2e9b93
 1,       4570,       4570,       10,     2013, 0x491f932b
-0,        137,        137,        1,   518400, 0xddda1712
+0,        137,        137,        1,   518400, 0x5a3b1092
 1,       4580,       4580,       10,     2013, 0x31359cf8
 1,       4590,       4590,       10,     2013, 0x1b00ac3f
 1,       4600,       4600,       10,     2013, 0x8d7ab3cb
-0,        138,        138,        1,   518400, 0x985a3b93
+0,        138,        138,        1,   518400, 0x8741350b
 1,       4610,       4610,       10,     2013, 0xb2c2a4de
 1,       4620,       4620,       10,     2013, 0x80a4abf2
 1,       4635,       4635,       10,     2013, 0x0701a4ee
-0,        139,        139,        1,   518400, 0xea63c5e7
+0,        139,        139,        1,   518400, 0xd5a9bf60
 1,       4645,       4645,       10,     2013, 0xdc1ba5bc
 1,       4655,       4655,       10,     2013, 0x6083a8a4
 1,       4665,       4665,       10,     2013, 0x6226ad45
-0,        140,        140,        1,   518400, 0xef64983d
+0,        140,        140,        1,   518400, 0xc05f91ba
 1,       4675,       4675,       10,     2013, 0x2732a205
 1,       4685,       4685,       10,     2013, 0x0f62a0d3
 1,       4695,       4695,       10,     2013, 0xc1799249
-0,        141,        141,        1,   518400, 0x747bb193
+0,        141,        141,        1,   518400, 0x3fdaab0b
 1,       4705,       4705,       10,     2013, 0xbccfa9c8
 1,       4720,       4720,       10,     2013, 0xded096e7
 1,       4730,       4730,       10,     2013, 0x7f0daf43
-0,        142,        142,        1,   518400, 0xb8748862
+0,        142,        142,        1,   518400, 0xab7281d9
 1,       4740,       4740,       10,     2013, 0xc47ea682
 1,       4750,       4750,       10,     2013, 0x5a72b07a
 1,       4760,       4760,       10,     2013, 0x386faa8c
 1,       4770,       4770,       10,     2013, 0xf9919a91
-0,        143,        143,        1,   518400, 0xaab55a5f
+0,        143,        143,        1,   518400, 0xc80053d6
 1,       4780,       4780,       10,     2013, 0x4908897e
 1,       4790,       4790,       10,     2013, 0x4882b594
-0,        144,        144,        1,   518400, 0x7b468add
+0,        144,        144,        1,   518400, 0x6526845c
 1,       4806,       4806,       10,     2013, 0x113e98d1
 1,       4816,       4816,       10,     2013, 0x5098b30d
 1,       4826,       4826,       10,     2013, 0x0ef7b857
 1,       4836,       4836,       10,     2013, 0x216ea176
-0,        145,        145,        1,   518400, 0xf2078707
+0,        145,        145,        1,   518400, 0x1b788089
 1,       4846,       4846,       10,     2013, 0xf906944a
 1,       4856,       4856,       10,     2013, 0xee9b92fb
 1,       4866,       4866,       10,     2013, 0xd6029209
-0,        146,        146,        1,   518400, 0x6a2d931e
+0,        146,        146,        1,   518400, 0xfa8e8ca9
 1,       4876,       4876,       10,     2013, 0x2256a12e
 1,       4891,       4891,       10,     2013, 0x89de8e4a
 1,       4901,       4901,       10,     2013, 0x0bf0a584
-0,        147,        147,        1,   518400, 0xbbe3c417
+0,        147,        147,        1,   518400, 0xb278bda1
 1,       4911,       4911,       10,     2013, 0x6a5ebd58
 1,       4921,       4921,       10,     2013, 0x3edd9aa4
 1,       4931,       4931,       10,     2013, 0xbd66ac26
-0,        148,        148,        1,   518400, 0x6294e449
+0,        148,        148,        1,   518400, 0xb0c3ddca
 1,       4941,       4941,       10,     2013, 0x313896ea
 1,       4951,       4951,       10,     2013, 0x6b83a6a0
 1,       4961,       4961,       10,     2013, 0x9aafb109
-0,        149,        149,        1,   518400, 0xa05721e7
+0,        149,        149,        1,   518400, 0x10351b53
 1,       4976,       4976,       10,     2013, 0x5192a85a
 1,       4986,       4986,       10,     2013, 0x1f919f79
 1,       4996,       4996,       10,     2013, 0xc0799c40
-0,        150,        150,        1,   518400, 0x37749183
+0,        150,        150,        1,   518400, 0xc1408aee
 1,       5006,       5006,       10,     2013, 0x2988bcd8
 1,       5016,       5016,       10,     2013, 0x1482913a
 1,       5026,       5026,       10,     2013, 0x74da9a94
 1,       5036,       5036,       10,     2013, 0x763eb709
-0,        151,        151,        1,   518400, 0xf9d9dca0
+0,        151,        151,        1,   518400, 0xf016d615
 1,       5046,       5046,       10,     2013, 0x1285b405
 1,       5062,       5062,       10,     2013, 0xb6ab9dfc
-0,        152,        152,        1,   518400, 0x5f8ccf08
+0,        152,        152,        1,   518400, 0xa768c892
 1,       5072,       5072,       10,     2013, 0xe4c8bf19
 1,       5082,       5082,       10,     2013, 0xabbbade8
 1,       5092,       5092,       10,     2013, 0xf8b69d89
 1,       5102,       5102,       10,     2013, 0xce04a866
-0,        153,        153,        1,   518400, 0x7303f77b
+0,        153,        153,        1,   518400, 0x11c3f11e
 1,       5112,       5112,       10,     2013, 0x07528abf
 1,       5122,       5122,       10,     2013, 0x74fb98bf
 1,       5132,       5132,       10,     2013, 0x579fb1c9
-0,        154,        154,        1,   518400, 0x22b0513f
+0,        154,        154,        1,   518400, 0xcd9a4ac4
 1,       5147,       5147,       10,     2013, 0x7ddea2ed
 1,       5157,       5157,       10,     2013, 0x296caa2c
 1,       5167,       5167,       10,     2013, 0x346d9c4f
-0,        155,        155,        1,   518400, 0x330485d2
+0,        155,        155,        1,   518400, 0x4ade7f5e
 1,       5177,       5177,       10,     2013, 0x3e1fba15
 1,       5187,       5187,       10,     2013, 0x48a2908f
 1,       5197,       5197,       10,     2013, 0xc1938d09
-0,        156,        156,        1,   518400, 0x7f83daea
+0,        156,        156,        1,   518400, 0x655dd46b
 1,       5207,       5207,       10,     2013, 0x0e96a060
 1,       5217,       5217,       10,     2013, 0x7b6a9e06
 1,       5232,       5232,       10,     2013, 0x5b779d28
-0,        157,        157,        1,   518400, 0xee19f2df
+0,        157,        157,        1,   518400, 0x5ab5ec61
 1,       5242,       5242,       10,     2013, 0xf600aca1
 1,       5252,       5252,       10,     2013, 0x3a6c9e68
 1,       5262,       5262,       10,     2013, 0x0c8dc1b0
-0,        158,        158,        1,   518400, 0xb71b1c77
+0,        158,        158,        1,   518400, 0x45dc15e6
 1,       5272,       5272,       10,     2013, 0x26beb245
 1,       5282,       5282,       10,     2013, 0x2bc09557
 1,       5292,       5292,       10,     2013, 0x27fc8845
 1,       5302,       5302,       10,     2013, 0x1025aa47
-0,        159,        159,        1,   518400, 0xbffc1856
+0,        159,        159,        1,   518400, 0x201911d3
 1,       5318,       5318,       10,     2013, 0xc2e69baa
 1,       5328,       5328,       10,     2013, 0xdb249b92
 1,       5338,       5338,       10,     2013, 0x6ccda29e
-0,        160,        160,        1,   518400, 0xabc125aa
+0,        160,        160,        1,   518400, 0x0fbc1f46
 1,       5348,       5348,       10,     2013, 0xeaf6a1cf
 1,       5358,       5358,       10,     2013, 0x509ba397
 1,       5368,       5368,       10,     2013, 0xfaf8a2df
-0,        161,        161,        1,   518400, 0x5ee467f8
+0,        161,        161,        1,   518400, 0x7e316179
 1,       5378,       5378,       10,     2013, 0x41388f28
 1,       5388,       5388,       10,     2013, 0xfe5eab39
 1,       5403,       5403,       10,     2013, 0xd5ffa066
-0,        162,        162,        1,   518400, 0x6c2cf168
+0,        162,        162,        1,   518400, 0x73bbeaed
 1,       5413,       5413,       10,     2013, 0x6813a30a
 1,       5423,       5423,       10,     2013, 0x9be89718
 1,       5433,       5433,       10,     2013, 0xaec3a27b
-0,        163,        163,        1,   518400, 0x63996b26
+0,        163,        163,        1,   518400, 0x3a7c648a
 1,       5446,       5446,       10,     2013, 0x579a983e
 1,       5456,       5456,       10,     2013, 0x98cea21f
 1,       5466,       5466,       10,     2013, 0xca77a58a
-0,        164,        164,        1,   518400, 0xb34d789a
+0,        164,        164,        1,   518400, 0x9f707209
 1,       5476,       5476,       10,     2013, 0xcbc3b1ee
 1,       5486,       5486,       10,     2013, 0xf3bb8f07
 1,       5496,       5496,       10,     2013, 0x6aeebd92
-0,        165,        165,        1,   518400, 0xf49c030f
+0,        165,        165,        1,   518400, 0x9f25fc5c
 1,       5506,       5506,       10,     2013, 0xe955a449
 1,       5516,       5516,       10,     2013, 0x9436aa5b
 1,       5531,       5531,       10,     2013, 0x4f0a8f9f
-0,        166,        166,        1,   518400, 0x092dc41a
+0,        166,        166,        1,   518400, 0x2ed8bd75
 1,       5541,       5541,       10,     2013, 0x3551b22d
 1,       5551,       5551,       10,     2013, 0x0959a3d4
 1,       5561,       5561,       10,     2013, 0x2ed5a11b
 1,       5571,       5571,       10,     2013, 0x8f52a5c3
-0,        167,        167,        1,   518400, 0x4134c577
+0,        167,        167,        1,   518400, 0xb493becb
 1,       5581,       5581,       10,     2013, 0x6552978d
 1,       5591,       5591,       10,     2013, 0x7dcca0c1
 1,       5601,       5601,       10,     2013, 0xbcd4a3c9
-0,        168,        168,        1,   518400, 0x261de1ed
+0,        168,        168,        1,   518400, 0x7df6db57
 1,       5616,       5616,       10,     2013, 0xfe41a8d8
 1,       5626,       5626,       10,     2013, 0xc85aae14
 1,       5636,       5636,       10,     2013, 0x1185b346
-0,        169,        169,        1,   518400, 0xcbc8566a
+0,        169,        169,        1,   518400, 0x1cb94fca
 1,       5646,       5646,       10,     2013, 0xf7429a0d
 1,       5656,       5656,       10,     2013, 0x48c2a160
 1,       5666,       5666,       10,     2013, 0x9d85a85d
-0,        170,        170,        1,   518400, 0x407a5c76
+0,        170,        170,        1,   518400, 0x70db55d8
 1,       5676,       5676,       10,     2013, 0xbbe89fe9
 1,       5686,       5686,       10,     2013, 0xea429fe2
 1,       5702,       5702,       10,     2013, 0x221ca1d4
-0,        171,        171,        1,   518400, 0x1ed73bb2
+0,        171,        171,        1,   518400, 0xc1d9351b
 1,       5712,       5712,       10,     2013, 0x394b925b
 1,       5722,       5722,       10,     2013, 0x556dc26f
 1,       5732,       5732,       10,     2013, 0xce21a5e1
-0,        172,        172,        1,   518400, 0x8467ddb5
+0,        172,        172,        1,   518400, 0xa4b0d717
 1,       5742,       5742,       10,     2013, 0xbc87c0a8
 1,       5752,       5752,       10,     2013, 0xbac4ac07
 1,       5762,       5762,       10,     2013, 0xdeefa4aa
 1,       5772,       5772,       10,     2013, 0x1f15b362
-0,        173,        173,        1,   518400, 0x0523dc73
+0,        173,        173,        1,   518400, 0x3730d5e9
 1,       5787,       5787,       10,     2013, 0x6406b7b2
 1,       5797,       5797,       10,     2013, 0x8030a03d
-0,        174,        174,        1,   518400, 0x81f5e895
+0,        174,        174,        1,   518400, 0x9673e1ec
 1,       5807,       5807,       10,     2013, 0x0373a5b1
 1,       5817,       5817,       10,     2013, 0x34ef93da
 1,       5827,       5827,       10,     2013, 0x94c198fe
 1,       5837,       5837,       10,     2013, 0xfefcabad
-0,        175,        175,        1,   518400, 0xfc74608d
+0,        175,        175,        1,   518400, 0x877959d5
 1,       5847,       5847,       10,     2013, 0x8755b3ec
 1,       5857,       5857,       10,     2013, 0xe436a6fd
 1,       5872,       5872,       10,     2013, 0x9cf5a11e
-0,        176,        176,        1,   518400, 0xc4e0dae0
+0,        176,        176,        1,   518400, 0x04f3d421
 1,       5882,       5882,       10,     2013, 0x03b8a98c
 1,       5892,       5892,       10,     2013, 0x6216a138
 1,       5902,       5902,       10,     2013, 0xd87b9f12
-0,        177,        177,        1,   518400, 0x98367f5b
+0,        177,        177,        1,   518400, 0x4f3078bc
 1,       5912,       5912,       10,     2013, 0x4ce99653
 1,       5922,       5922,       10,     2013, 0x6c2ea9e2
 1,       5932,       5932,       10,     2013, 0x918cae4c
-0,        178,        178,        1,   518400, 0x0f1a869d
+0,        178,        178,        1,   518400, 0x8a127ff8
 1,       5942,       5942,       10,     2013, 0xd19fa5f2
 1,       5958,       5958,       10,     2013, 0x0bdda7c6
 1,       5968,       5968,       10,     2013, 0x0f9ab0ca
-0,        179,        179,        1,   518400, 0x45b6ccf2
+0,        179,        179,        1,   518400, 0x5864c64f
 1,       5978,       5978,       10,     2013, 0x410a92b1
 1,       5988,       5988,       10,     2013, 0xcfbe9d1c
 1,       5998,       5998,       10,     2013, 0x59ed9d15
-0,        180,        180,        1,   518400, 0x5f9ccb77
+0,        180,        180,        1,   518400, 0xdaccc4c0
 1,       6008,       6008,       10,     2013, 0x4e129e27
 1,       6018,       6018,       10,     2013, 0x7bb9ac0a
 1,       6028,       6028,       10,     2013, 0x826ca82b
-0,        181,        181,        1,   518400, 0x5f15ea31
+0,        181,        181,        1,   518400, 0xd999e376
 1,       6043,       6043,       10,     2013, 0x9ad5a74b
 1,       6053,       6053,       10,     2013, 0x6c5f969a
 1,       6063,       6063,       10,     2013, 0x8479a0e5
-0,        182,        182,        1,   518400, 0x86369f27
+0,        182,        182,        1,   518400, 0x8af39876
 1,       6073,       6073,       10,     2013, 0x165298ef
 1,       6083,       6083,       10,     2013, 0xdcadb4a1
 1,       6093,       6093,       10,     2013, 0xa90e987c
 1,       6103,       6103,       10,     2013, 0x1ac5b510
-0,        183,        183,        1,   518400, 0x2e27f9fa
+0,        183,        183,        1,   518400, 0x5e72f33d
 1,       6113,       6113,       10,     2013, 0x66728d85
 1,       6128,       6128,       10,     2013, 0xe4859fc5
 1,       6138,       6138,       10,     2013, 0x9901786e
-0,        184,        184,        1,   518400, 0xc029a44d
+0,        184,        184,        1,   518400, 0x14af9d92
 1,       6148,       6148,       10,     2013, 0x6aebb406
 1,       6158,       6158,       10,     2013, 0x7d13a2cc
 1,       6168,       6168,       10,     2013, 0x99b7a8cc
-0,        185,        185,        1,   518400, 0xebee33b0
+0,        185,        185,        1,   518400, 0x50b82d10
 1,       6178,       6178,       10,     2013, 0x80b8a624
 1,       6188,       6188,       10,     2013, 0xbb6aa271
 1,       6198,       6198,       10,     2013, 0x17af9e4a
-0,        186,        186,        1,   518400, 0x19e5494f
+0,        186,        186,        1,   518400, 0xc068429c
 1,       6214,       6214,       10,     2013, 0xfaf0a8f1
 1,       6224,       6224,       10,     2013, 0xd6849b93
 1,       6234,       6234,       10,     2013, 0xe9829669
-0,        187,        187,        1,   518400, 0xf697bd7c
+0,        187,        187,        1,   518400, 0x8934b6d1
 1,       6244,       6244,       10,     2013, 0x7ec98944
 1,       6254,       6254,       10,     2013, 0x2b2099a4
 1,       6264,       6264,       10,     2013, 0x1033a82f
-0,        188,        188,        1,   518400, 0x82569002
+0,        188,        188,        1,   518400, 0x11d08947
 1,       6274,       6274,       10,     2013, 0x5ec88990
 1,       6284,       6284,       10,     2013, 0xd2a19b3d
 1,       6299,       6299,       10,     2013, 0xa377b268
-0,        189,        189,        1,   518400, 0xfcb6d707
+0,        189,        189,        1,   518400, 0x8a27d041
 1,       6309,       6309,       10,     2013, 0xfa859901
 1,       6319,       6319,       10,     2013, 0x1713955a
 1,       6329,       6329,       10,     2013, 0x70aab0da
 1,       6339,       6339,       10,     2013, 0xcdaea422
-0,        190,        190,        1,   518400, 0x82a9662b
+0,        190,        190,        1,   518400, 0xab265f7d
 1,       6349,       6349,       10,     2013, 0x65c3bf80
 1,       6359,       6359,       10,     2013, 0x1d75a55f
 1,       6369,       6369,       10,     2013, 0xa5bea4de
-0,        191,        191,        1,   518400, 0x212e16ee
+0,        191,        191,        1,   518400, 0xff491040
 1,       6384,       6384,       10,     2013, 0x184db71c
 1,       6394,       6394,       10,     2013, 0x99858ec8
 1,       6404,       6404,       10,     2013, 0xb8f2aee5
-0,        192,        192,        1,   518400, 0x2ca34dca
+0,        192,        192,        1,   518400, 0x822b4704
 1,       6414,       6414,       10,     2013, 0x4435b2ef
 1,       6424,       6424,       10,     2013, 0x8acfa6c7
 1,       6434,       6434,       10,     2013, 0x42b4c01f
-0,        193,        193,        1,   518400, 0xe9ebe0a5
+0,        193,        193,        1,   518400, 0x4523d9f4
 1,       6444,       6444,       10,     2013, 0x6e308c13
 1,       6454,       6454,       10,     2013, 0x8227a0f6
 1,       6470,       6470,       10,     2013, 0x6f12a7a2
-0,        194,        194,        1,   518400, 0x4e6b6917
+0,        194,        194,        1,   518400, 0xfc3c626e
 1,       6480,       6480,       10,     2013, 0x785392be
 1,       6490,       6490,       10,     2013, 0x81849c2b
 1,       6500,       6500,       10,     2013, 0x5cf2af65
-0,        195,        195,        1,   518400, 0x7dcf20ab
+0,        195,        195,        1,   518400, 0x237319e5
 1,       6510,       6510,       10,     2013, 0x0c6ca6b4
 1,       6520,       6520,       10,     2013, 0x412fab9f
 1,       6530,       6530,       10,     2013, 0x08e792b4
-0,        196,        196,        1,   518400, 0xf30fac97
+0,        196,        196,        1,   518400, 0x892ca5d8
 1,       6540,       6540,       10,     2013, 0x407aace3
 1,       6555,       6555,       10,     2013, 0xd26bac16
 1,       6565,       6565,       10,     2013, 0xac8bb295
-0,        197,        197,        1,   518400, 0xcb9fc692
+0,        197,        197,        1,   518400, 0xc4c0bfc7
 1,       6575,       6575,       10,     2013, 0xddd1949c
 1,       6585,       6585,       10,     2013, 0x6b26b868
 1,       6595,       6595,       10,     2013, 0x5eaba587
 1,       6605,       6605,       10,     2013, 0xef0793b9
-0,        198,        198,        1,   518400, 0x5d05601e
+0,        198,        198,        1,   518400, 0x57c85956
 1,       6615,       6615,       10,     2013, 0xdef19bd6
 1,       6625,       6625,       10,     2013, 0xca98a635
-0,        199,        199,        1,   518400, 0x456c1417
+0,        199,        199,        1,   518400, 0xd6300d46
 1,       6640,       6640,       10,     2013, 0x06269a5a
 1,       6650,       6650,       10,     2013, 0x32cb9952
 1,       6660,       6660,       10,     2013, 0xf01fa95a
 1,       6670,       6670,       10,     2013, 0xefab9e55
-0,        200,        200,        1,   518400, 0x9a0fd1ad
+0,        200,        200,        1,   518400, 0xd3dacaec
 1,       6680,       6680,       10,     2013, 0x55a3b63a
 1,       6690,       6690,       10,     2013, 0xcd36a553
 1,       6700,       6700,       10,     2013, 0x2ec19877
-0,        201,        201,        1,   518400, 0x55db9716
+0,        201,        201,        1,   518400, 0x65429052
 1,       6710,       6710,       10,     2013, 0xc18b924c
 1,       6726,       6726,       10,     2013, 0xf132b04c
 1,       6736,       6736,       10,     2013, 0x7975a44d
-0,        202,        202,        1,   518400, 0x1f0d40d6
+0,        202,        202,        1,   518400, 0xec803a15
 1,       6746,       6746,       10,     2013, 0x2aaf94cb
 1,       6756,       6756,       10,     2013, 0x58cfa60f
 1,       6766,       6766,       10,     2013, 0x9757a658
-0,        203,        203,        1,   518400, 0x73695c82
+0,        203,        203,        1,   518400, 0x7a9a55c9
 1,       6776,       6776,       10,     2013, 0x67ebc0d5
 1,       6786,       6786,       10,     2013, 0x3c50a70e
 1,       6796,       6796,       10,     2013, 0x9c5799c6
-0,        204,        204,        1,   518400, 0xb0f10812
+0,        204,        204,        1,   518400, 0xcac30160
 1,       6811,       6811,       10,     2013, 0x018d85b2
 1,       6821,       6821,       10,     2013, 0x5367a956
-0,        205,        205,        1,   518400, 0xdec18505
-0,        208,        208,        1,   518400, 0xb147b947
-0,        240,        240,        1,   518400, 0x9d2e3977
+0,        205,        205,        1,   518400, 0x7e187e4f
+0,        208,        208,        1,   518400, 0x0be0b2a2
+0,        213,        213,        1,   518400, 0x0be0b2a2
diff --git a/tests/ref/fate/sub-dvb b/tests/ref/fate/sub-dvb
index cbd1801d64..8f33c75d70 100644
--- a/tests/ref/fate/sub-dvb
+++ b/tests/ref/fate/sub-dvb
@@ -1,75 +1,93 @@
 #tb 0: 1/1000000
 #media_type 0: subtitle
 #codec_id 0: dvb_subtitle
-0,   15600000,   15600000,   159000,     1168, 0xd0f89d82
-0,   15759000,   15759000,   159000,       14, 0x064900eb
-0,   15760000,   15760000,   239000,     1544, 0xe60f1751
-0,   15999000,   15999000,   239000,       14, 0x0729010b
-0,   16000000,   16000000,   339000,     1658, 0xbe343093
-0,   16339000,   16339000,   339000,       14, 0x0809012b
-0,   16340000,   16340000,   599000,     2343, 0xc68f07ef
-0,   16939000,   16939000,   599000,       14, 0x08e9014b
-0,   16940000,   16940000,   459000,     2568, 0x0ee657b1
-0,   17399000,   17399000,   459000,       14, 0x09c9016b
-0,   17400000,   17400000,   359000,     3422, 0xba5b63ce
-0,   17759000,   17759000,   359000,       14, 0x0aa9018b
-0,   17760000,   17760000,   219000,     5078, 0x95b19902
-0,   17979000,   17979000,   219000,       14, 0x0b8901ab
-0,   17980000,   17980000,   959000,     5808, 0xc9717b89
-0,   18939000,   18939000,   959000,       14, 0x0c6901cb
-0,   18940000,   18940000,   219000,     6015, 0x0becbfac
-0,   19159000,   19159000,   219000,       14, 0x064900eb
-0,   19160000,   19160000,   259000,     6519, 0xfcd24d26
-0,   19419000,   19419000,   259000,       14, 0x0729010b
-0,   19420000,   19420000,    99000,     7061, 0xf0320408
-0,   19519000,   19519000,    99000,       14, 0x0809012b
-0,   19520000,   19520000,   219000,     4773, 0x66c93074
-0,   19739000,   19739000,   219000,       14, 0x08e9014b
-0,   19740000,   19740000,   219000,     5546, 0x06052c81
-0,   19959000,   19959000,   219000,       14, 0x09c9016b
-0,   19960000,   19960000,   239000,     5754, 0x904f7325
-0,   20199000,   20199000,   239000,       14, 0x0aa9018b
-0,   20200000,   20200000,   139000,     6099, 0xe30cde07
-0,   20339000,   20339000,   139000,       14, 0x0b8901ab
-0,   20340000,   20340000,   799000,     6839, 0x770fcb6c
-0,   21139000,   21139000,   799000,       14, 0x0c6901cb
-0,   21140000,   21140000,   239000,     4744, 0xa91e1b41
-0,   21379000,   21379000,   239000,       14, 0x064900eb
-0,   21380000,   21380000,   339000,     5824, 0xcf6d782b
-0,   21719000,   21719000,   339000,       14, 0x0729010b
-0,   21720000,   21720000,  1439000,     6212, 0xabf8f7cf
-0,   23159000,   23159000,  1439000,       14, 0x0809012b
-0,   23160000,   23160000,  1319000,     7082, 0xd7ca10f2
-0,   24479000,   24479000,  1319000,       14, 0x08e9014b
-0,   24480000,   24480000,   219000,     5345, 0x12b2cae0
-0,   24699000,   24699000,   219000,       14, 0x09c9016b
-0,   24700000,   24700000,   219000,     5765, 0xc7d46192
-0,   24919000,   24919000,   219000,       14, 0x0aa9018b
-0,   24920000,   24920000,   599000,     6557, 0xcb995d30
-0,   25519000,   25519000,   599000,       14, 0x0b8901ab
-0,   25520000,   25520000,   219000,     7091, 0xe6ea0559
-0,   25739000,   25739000,   219000,       14, 0x0c6901cb
-0,   25740000,   25740000,   239000,     7305, 0xb66c404e
-0,   25979000,   25979000,   239000,       14, 0x064900eb
-0,   25980000,   25980000,   359000,     7590, 0x0cc2a481
-0,   26339000,   26339000,   359000,       14, 0x0729010b
-0,   26340000,   26340000,   219000,     4629, 0xe18cfea8
-0,   26559000,   26559000,   219000,       14, 0x0809012b
-0,   26560000,   26560000,   719000,     4785, 0x82043fc0
-0,   27279000,   27279000,   719000,       14, 0x08e9014b
-0,   27280000,   27280000,   459000,     6061, 0xbde7d245
-0,   27739000,   27739000,   459000,       14, 0x09c9016b
-0,   27740000,   27740000,   239000,     6301, 0x92d01a51
-0,   27979000,   27979000,   239000,       14, 0x0aa9018b
-0,   27980000,   27980000,    99000,     6736, 0xbd25a134
-0,   28079000,   28079000,    99000,       14, 0x0b8901ab
-0,   28080000,   28080000,   219000,     7214, 0x7ef93c13
-0,   28299000,   28299000,   219000,       14, 0x0c6901cb
-0,   28300000,   28300000,   239000,     7366, 0x5bed7fcd
-0,   28539000,   28539000,   239000,       14, 0x064900eb
-0,   28540000,   28540000,   599000,     4564, 0x7f4c014b
-0,   29139000,   29139000,   599000,       14, 0x0729010b
-0,   29140000,   29140000,   219000,     4637, 0x682626b7
-0,   29359000,   29359000,   219000,       14, 0x0809012b
-0,   29360000,   29360000,  1679000,     5358, 0x29e30c48
-0,   31039000,   31039000,  1679000,       14, 0x08e9014b
+0,          0,          0,   279000,       14, 0x05d900db
+0,     279000,     279000,   279000,       14, 0x064900eb
+0,     280000,     280000,  4999000,       14, 0x06b900fb
+0,    5279000,    5279000,  4999000,       14, 0x0729010b
+0,    5280000,    5280000,  5019000,       14, 0x0799011b
+0,   10299000,   10299000,  5019000,       14, 0x0809012b
+0,   10300000,   10300000,  3599000,       14, 0x0879013b
+0,   13899000,   13899000,  3599000,       14, 0x08e9014b
+0,   13900000,   13900000,   219000,       14, 0x0959015b
+0,   14119000,   14119000,   219000,       14, 0x09c9016b
+0,   14120000,   14120000,  1439000,       14, 0x0a39017b
+0,   15559000,   15559000,  1439000,       14, 0x0aa9018b
+0,   15560000,   15560000,    39000,       14, 0x0b19019b
+0,   15599000,   15599000,    39000,       14, 0x0b8901ab
+0,   15600000,   15600000,   159000,     1168, 0xd69da022
+0,   15759000,   15759000,   159000,       14, 0x0c6901cb
+0,   15760000,   15760000,   239000,     1544, 0xc5f116f1
+0,   15999000,   15999000,   239000,       14, 0x064900eb
+0,   16000000,   16000000,   339000,     1658, 0x73563033
+0,   16339000,   16339000,   339000,       14, 0x0729010b
+0,   16340000,   16340000,   599000,     2343, 0x7ac2078f
+0,   16939000,   16939000,   599000,       14, 0x0809012b
+0,   16940000,   16940000,   459000,     2568, 0x6eaa5751
+0,   17399000,   17399000,   459000,       14, 0x08e9014b
+0,   17400000,   17400000,   359000,     3422, 0xd9d0636e
+0,   17759000,   17759000,   359000,       14, 0x09c9016b
+0,   17760000,   17760000,   219000,     5078, 0x722c9862
+0,   17979000,   17979000,   219000,       14, 0x0aa9018b
+0,   17980000,   17980000,   959000,     5808, 0x38dd7ae9
+0,   18939000,   18939000,   959000,       14, 0x0b8901ab
+0,   18940000,   18940000,   219000,     6015, 0xd4d2c40c
+0,   19159000,   19159000,   219000,       14, 0x0c6901cb
+0,   19160000,   19160000,   259000,     6519, 0x08af4c86
+0,   19419000,   19419000,   259000,       14, 0x064900eb
+0,   19420000,   19420000,    99000,     7061, 0xecf10368
+0,   19519000,   19519000,    99000,       14, 0x0729010b
+0,   19520000,   19520000,   219000,     4773, 0xbee42fd4
+0,   19739000,   19739000,   219000,       14, 0x0809012b
+0,   19740000,   19740000,   219000,     5546, 0xdb822be1
+0,   19959000,   19959000,   219000,       14, 0x08e9014b
+0,   19960000,   19960000,   239000,     5754, 0xfdcc7285
+0,   20199000,   20199000,   239000,       14, 0x09c9016b
+0,   20200000,   20200000,   139000,     6099, 0xa409dd67
+0,   20339000,   20339000,   139000,       14, 0x0aa9018b
+0,   20340000,   20340000,   799000,     6839, 0xc5eecacc
+0,   21139000,   21139000,   799000,       14, 0x0b8901ab
+0,   21140000,   21140000,   239000,     4744, 0x4e451fa1
+0,   21379000,   21379000,   239000,       14, 0x0c6901cb
+0,   21380000,   21380000,   339000,     5824, 0x5299778b
+0,   21719000,   21719000,   339000,       14, 0x064900eb
+0,   21720000,   21720000,  1439000,     6212, 0x6d15f72f
+0,   23159000,   23159000,  1439000,       14, 0x0729010b
+0,   23160000,   23160000,  1319000,     7082, 0xe5c91052
+0,   24479000,   24479000,  1319000,       14, 0x0809012b
+0,   24480000,   24480000,   219000,     5345, 0x2e5eca40
+0,   24699000,   24699000,   219000,       14, 0x08e9014b
+0,   24700000,   24700000,   219000,     5765, 0x118060f2
+0,   24919000,   24919000,   219000,       14, 0x09c9016b
+0,   24920000,   24920000,   599000,     6557, 0x89275c90
+0,   25519000,   25519000,   599000,       14, 0x0aa9018b
+0,   25520000,   25520000,   219000,     7091, 0x996904b9
+0,   25739000,   25739000,   219000,       14, 0x0b8901ab
+0,   25740000,   25740000,   239000,     7305, 0xc23e44ae
+0,   25979000,   25979000,   239000,       14, 0x0c6901cb
+0,   25980000,   25980000,   359000,     7590, 0xc5a3a3e1
+0,   26339000,   26339000,   359000,       14, 0x064900eb
+0,   26340000,   26340000,   219000,     4629, 0x7ad6fe08
+0,   26559000,   26559000,   219000,       14, 0x0729010b
+0,   26560000,   26560000,   719000,     4785, 0xcd3f3f20
+0,   27279000,   27279000,   719000,       14, 0x0809012b
+0,   27280000,   27280000,   459000,     6061, 0x8b04d1a5
+0,   27739000,   27739000,   459000,       14, 0x08e9014b
+0,   27740000,   27740000,   239000,     6301, 0xe7de19b1
+0,   27979000,   27979000,   239000,       14, 0x09c9016b
+0,   27980000,   27980000,    99000,     6736, 0x38b3a094
+0,   28079000,   28079000,    99000,       14, 0x0aa9018b
+0,   28080000,   28080000,   219000,     7214, 0x0b783b73
+0,   28299000,   28299000,   219000,       14, 0x0b8901ab
+0,   28300000,   28300000,   239000,     7366, 0x98bf842d
+0,   28539000,   28539000,   239000,       14, 0x0c6901cb
+0,   28540000,   28540000,   599000,     4564, 0x3d9600ab
+0,   29139000,   29139000,   599000,       14, 0x064900eb
+0,   29140000,   29140000,   219000,     4637, 0x01f02617
+0,   29359000,   29359000,   219000,       14, 0x0729010b
+0,   29360000,   29360000,  1679000,     5358, 0x5b0f0ba8
+0,   31039000,   31039000,  1679000,       14, 0x0809012b
+0,   31040000,   31040000,   359000,       14, 0x0879013b
+0,   31399000,   31399000,   359000,       14, 0x08e9014b
+0,   31400000,   31400000,   479000,       14, 0x0959015b
+0,   31879000,   31879000,   479000,       14, 0x09c9016b
diff --git a/tests/ref/fate/sub-scc b/tests/ref/fate/sub-scc
index 62cbf6fa4a..b02e6e95b5 100644
--- a/tests/ref/fate/sub-scc
+++ b/tests/ref/fate/sub-scc
@@ -11,7 +11,6 @@ Style: Default,Monospace,16,&Hffffff,&Hffffff,&H0,&H0,0,0,0,0,100,100,0,0,3,1,0,
 
 [Events]
 Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
-Dialogue: 0,0:00:-2.-47,0:00:00.70,Default,,0,0,0,,{\an7}{\pos(76,228)}WE HAVE FOUND A WITCH !\N{\an7}{\pos(76,243)}MAY WE BURN HER ?
 Dialogue: 0,0:00:00.69,0:00:03.29,Default,,0,0,0,,{\an7}{\pos(115,228)}[ Crowd ]\N{\an7}{\pos(115,243)}BURN HER !  BURN HER !
 Dialogue: 0,0:00:03.30,0:00:07.07,Default,,0,0,0,,{\an7}{\pos(38,197)}HOW DO YOU KNOW\N{\an7}{\pos(38,213)}SHE IS A WITCH ?\N{\an7}{\pos(153,243)}SHE LOOKS LIKE ONE !
 Dialogue: 0,0:00:07.07,0:00:09.27,Default,,0,0,0,,{\an7}{\pos(192,228)}[ Shouting\N{\an7}{\pos(192,243)}\h\hAffirmations ]
diff --git a/tests/ref/fate/sub2video b/tests/ref/fate/sub2video
index 80abe9c905..7cc6549966 100644
--- a/tests/ref/fate/sub2video
+++ b/tests/ref/fate/sub2video
@@ -58,129 +58,1136 @@
 0,         47,         47,        1,   518400, 0xde69683f
 0,         48,         48,        1,   518400, 0x7df08fba
 0,         49,         49,        1,   518400, 0xbab197ea
+0,         50,         50,        1,   518400, 0xbab197ea
+0,         51,         51,        1,   518400, 0xbab197ea
+0,         52,         52,        1,   518400, 0xbab197ea
+0,         53,         53,        1,   518400, 0xbab197ea
+0,         54,         54,        1,   518400, 0xbab197ea
+0,         55,         55,        1,   518400, 0xbab197ea
+0,         56,         56,        1,   518400, 0xbab197ea
+0,         57,         57,        1,   518400, 0xbab197ea
+0,         58,         58,        1,   518400, 0xbab197ea
+0,         59,         59,        1,   518400, 0xbab197ea
+0,         60,         60,        1,   518400, 0xbab197ea
+0,         61,         61,        1,   518400, 0xbab197ea
+0,         62,         62,        1,   518400, 0xbab197ea
+0,         63,         63,        1,   518400, 0xbab197ea
+0,         64,         64,        1,   518400, 0xbab197ea
+0,         65,         65,        1,   518400, 0xbab197ea
+0,         66,         66,        1,   518400, 0xbab197ea
+0,         67,         67,        1,   518400, 0xbab197ea
+0,         68,         68,        1,   518400, 0xbab197ea
+0,         69,         69,        1,   518400, 0xbab197ea
+0,         70,         70,        1,   518400, 0xbab197ea
+0,         71,         71,        1,   518400, 0xbab197ea
+0,         72,         72,        1,   518400, 0xbab197ea
+0,         73,         73,        1,   518400, 0xbab197ea
+0,         74,         74,        1,   518400, 0xbab197ea
+0,         75,         75,        1,   518400, 0xbab197ea
+0,         76,         76,        1,   518400, 0xbab197ea
 1,   15355000,   15355000,  4733000,     2094, 0x3c171425
 0,         77,         77,        1,   518400, 0x902285d9
-0,        100,        100,        1,   518400, 0xbab197ea
+0,         78,         78,        1,   518400, 0x902285d9
+0,         79,         79,        1,   518400, 0x902285d9
+0,         80,         80,        1,   518400, 0x902285d9
+0,         81,         81,        1,   518400, 0x902285d9
+0,         82,         82,        1,   518400, 0x902285d9
+0,         83,         83,        1,   518400, 0x902285d9
+0,         84,         84,        1,   518400, 0x902285d9
+0,         85,         85,        1,   518400, 0x902285d9
+0,         86,         86,        1,   518400, 0x902285d9
+0,         87,         87,        1,   518400, 0x902285d9
+0,         88,         88,        1,   518400, 0x902285d9
+0,         89,         89,        1,   518400, 0x902285d9
+0,         90,         90,        1,   518400, 0x902285d9
+0,         91,         91,        1,   518400, 0x902285d9
+0,         92,         92,        1,   518400, 0x902285d9
+0,         93,         93,        1,   518400, 0x902285d9
+0,         94,         94,        1,   518400, 0x902285d9
+0,         95,         95,        1,   518400, 0x902285d9
+0,         96,         96,        1,   518400, 0x902285d9
+0,         97,         97,        1,   518400, 0x902285d9
+0,         98,         98,        1,   518400, 0x902285d9
+0,         99,         99,        1,   518400, 0x902285d9
+0,        100,        100,        1,   518400, 0x902285d9
+0,        101,        101,        1,   518400, 0xbab197ea
+0,        102,        102,        1,   518400, 0xbab197ea
+0,        103,        103,        1,   518400, 0xbab197ea
+0,        104,        104,        1,   518400, 0xbab197ea
+0,        105,        105,        1,   518400, 0xbab197ea
+0,        106,        106,        1,   518400, 0xbab197ea
+0,        107,        107,        1,   518400, 0xbab197ea
+0,        108,        108,        1,   518400, 0xbab197ea
+0,        109,        109,        1,   518400, 0xbab197ea
+0,        110,        110,        1,   518400, 0xbab197ea
+0,        111,        111,        1,   518400, 0xbab197ea
+0,        112,        112,        1,   518400, 0xbab197ea
+0,        113,        113,        1,   518400, 0xbab197ea
+0,        114,        114,        1,   518400, 0xbab197ea
+0,        115,        115,        1,   518400, 0xbab197ea
+0,        116,        116,        1,   518400, 0xbab197ea
+0,        117,        117,        1,   518400, 0xbab197ea
+0,        118,        118,        1,   518400, 0xbab197ea
+0,        119,        119,        1,   518400, 0xbab197ea
+0,        120,        120,        1,   518400, 0xbab197ea
+0,        121,        121,        1,   518400, 0xbab197ea
+0,        122,        122,        1,   518400, 0xbab197ea
+0,        123,        123,        1,   518400, 0xbab197ea
+0,        124,        124,        1,   518400, 0xbab197ea
+0,        125,        125,        1,   518400, 0xbab197ea
+0,        126,        126,        1,   518400, 0xbab197ea
+0,        127,        127,        1,   518400, 0xbab197ea
+0,        128,        128,        1,   518400, 0xbab197ea
+0,        129,        129,        1,   518400, 0xbab197ea
+0,        130,        130,        1,   518400, 0xbab197ea
+0,        131,        131,        1,   518400, 0xbab197ea
+0,        132,        132,        1,   518400, 0xbab197ea
+0,        133,        133,        1,   518400, 0xbab197ea
+0,        134,        134,        1,   518400, 0xbab197ea
+0,        135,        135,        1,   518400, 0xbab197ea
+0,        136,        136,        1,   518400, 0xbab197ea
+0,        137,        137,        1,   518400, 0xbab197ea
+0,        138,        138,        1,   518400, 0xbab197ea
+0,        139,        139,        1,   518400, 0xbab197ea
+0,        140,        140,        1,   518400, 0xbab197ea
+0,        141,        141,        1,   518400, 0xbab197ea
+0,        142,        142,        1,   518400, 0xbab197ea
+0,        143,        143,        1,   518400, 0xbab197ea
+0,        144,        144,        1,   518400, 0xbab197ea
+0,        145,        145,        1,   518400, 0xbab197ea
+0,        146,        146,        1,   518400, 0xbab197ea
+0,        147,        147,        1,   518400, 0xbab197ea
+0,        148,        148,        1,   518400, 0xbab197ea
+0,        149,        149,        1,   518400, 0xbab197ea
+0,        150,        150,        1,   518400, 0xbab197ea
+0,        151,        151,        1,   518400, 0xbab197ea
+0,        152,        152,        1,   518400, 0xbab197ea
+0,        153,        153,        1,   518400, 0xbab197ea
+0,        154,        154,        1,   518400, 0xbab197ea
+0,        155,        155,        1,   518400, 0xbab197ea
+0,        156,        156,        1,   518400, 0xbab197ea
+0,        157,        157,        1,   518400, 0xbab197ea
+0,        158,        158,        1,   518400, 0xbab197ea
+0,        159,        159,        1,   518400, 0xbab197ea
+0,        160,        160,        1,   518400, 0xbab197ea
+0,        161,        161,        1,   518400, 0xbab197ea
+0,        162,        162,        1,   518400, 0xbab197ea
+0,        163,        163,        1,   518400, 0xbab197ea
+0,        164,        164,        1,   518400, 0xbab197ea
+0,        165,        165,        1,   518400, 0xbab197ea
+0,        166,        166,        1,   518400, 0xbab197ea
+0,        167,        167,        1,   518400, 0xbab197ea
+0,        168,        168,        1,   518400, 0xbab197ea
+0,        169,        169,        1,   518400, 0xbab197ea
+0,        170,        170,        1,   518400, 0xbab197ea
+0,        171,        171,        1,   518400, 0xbab197ea
+0,        172,        172,        1,   518400, 0xbab197ea
+0,        173,        173,        1,   518400, 0xbab197ea
+0,        174,        174,        1,   518400, 0xbab197ea
+0,        175,        175,        1,   518400, 0xbab197ea
+0,        176,        176,        1,   518400, 0xbab197ea
+0,        177,        177,        1,   518400, 0xbab197ea
+0,        178,        178,        1,   518400, 0xbab197ea
+0,        179,        179,        1,   518400, 0xbab197ea
+0,        180,        180,        1,   518400, 0xbab197ea
+0,        181,        181,        1,   518400, 0xbab197ea
+0,        182,        182,        1,   518400, 0xbab197ea
+0,        183,        183,        1,   518400, 0xbab197ea
+0,        184,        184,        1,   518400, 0xbab197ea
+0,        185,        185,        1,   518400, 0xbab197ea
+0,        186,        186,        1,   518400, 0xbab197ea
+0,        187,        187,        1,   518400, 0xbab197ea
+0,        188,        188,        1,   518400, 0xbab197ea
+0,        189,        189,        1,   518400, 0xbab197ea
+0,        190,        190,        1,   518400, 0xbab197ea
+0,        191,        191,        1,   518400, 0xbab197ea
+0,        192,        192,        1,   518400, 0xbab197ea
+0,        193,        193,        1,   518400, 0xbab197ea
+0,        194,        194,        1,   518400, 0xbab197ea
+0,        195,        195,        1,   518400, 0xbab197ea
+0,        196,        196,        1,   518400, 0xbab197ea
+0,        197,        197,        1,   518400, 0xbab197ea
+0,        198,        198,        1,   518400, 0xbab197ea
+0,        199,        199,        1,   518400, 0xbab197ea
+0,        200,        200,        1,   518400, 0xbab197ea
+0,        201,        201,        1,   518400, 0xbab197ea
+0,        202,        202,        1,   518400, 0xbab197ea
+0,        203,        203,        1,   518400, 0xbab197ea
+0,        204,        204,        1,   518400, 0xbab197ea
+0,        205,        205,        1,   518400, 0xbab197ea
+0,        206,        206,        1,   518400, 0xbab197ea
+0,        207,        207,        1,   518400, 0xbab197ea
+0,        208,        208,        1,   518400, 0xbab197ea
+0,        209,        209,        1,   518400, 0xbab197ea
+0,        210,        210,        1,   518400, 0xbab197ea
+0,        211,        211,        1,   518400, 0xbab197ea
+0,        212,        212,        1,   518400, 0xbab197ea
+0,        213,        213,        1,   518400, 0xbab197ea
+0,        214,        214,        1,   518400, 0xbab197ea
+0,        215,        215,        1,   518400, 0xbab197ea
+0,        216,        216,        1,   518400, 0xbab197ea
+0,        217,        217,        1,   518400, 0xbab197ea
+0,        218,        218,        1,   518400, 0xbab197ea
+0,        219,        219,        1,   518400, 0xbab197ea
+0,        220,        220,        1,   518400, 0xbab197ea
+0,        221,        221,        1,   518400, 0xbab197ea
+0,        222,        222,        1,   518400, 0xbab197ea
+0,        223,        223,        1,   518400, 0xbab197ea
+0,        224,        224,        1,   518400, 0xbab197ea
+0,        225,        225,        1,   518400, 0xbab197ea
+0,        226,        226,        1,   518400, 0xbab197ea
+0,        227,        227,        1,   518400, 0xbab197ea
+0,        228,        228,        1,   518400, 0xbab197ea
+0,        229,        229,        1,   518400, 0xbab197ea
+0,        230,        230,        1,   518400, 0xbab197ea
+0,        231,        231,        1,   518400, 0xbab197ea
+0,        232,        232,        1,   518400, 0xbab197ea
+0,        233,        233,        1,   518400, 0xbab197ea
+0,        234,        234,        1,   518400, 0xbab197ea
+0,        235,        235,        1,   518400, 0xbab197ea
+0,        236,        236,        1,   518400, 0xbab197ea
+0,        237,        237,        1,   518400, 0xbab197ea
+0,        238,        238,        1,   518400, 0xbab197ea
+0,        239,        239,        1,   518400, 0xbab197ea
+0,        240,        240,        1,   518400, 0xbab197ea
+0,        241,        241,        1,   518400, 0xbab197ea
+0,        242,        242,        1,   518400, 0xbab197ea
+0,        243,        243,        1,   518400, 0xbab197ea
 1,   48797000,   48797000,  2560000,     2480, 0x7c0edf21
 0,        244,        244,        1,   518400, 0x7a11c812
-0,        257,        257,        1,   518400, 0xbab197ea
+0,        245,        245,        1,   518400, 0x7a11c812
+0,        246,        246,        1,   518400, 0x7a11c812
+0,        247,        247,        1,   518400, 0x7a11c812
+0,        248,        248,        1,   518400, 0x7a11c812
+0,        249,        249,        1,   518400, 0x7a11c812
+0,        250,        250,        1,   518400, 0x7a11c812
+0,        251,        251,        1,   518400, 0x7a11c812
+0,        252,        252,        1,   518400, 0x7a11c812
+0,        253,        253,        1,   518400, 0x7a11c812
+0,        254,        254,        1,   518400, 0x7a11c812
+0,        255,        255,        1,   518400, 0x7a11c812
+0,        256,        256,        1,   518400, 0x7a11c812
+0,        257,        257,        1,   518400, 0x34cdddee
 1,   51433000,   51433000,  2366000,     3059, 0xc95b8a05
 0,        258,        258,        1,   518400, 0x34cdddee
-0,        269,        269,        1,   518400, 0xbab197ea
+0,        259,        259,        1,   518400, 0x34cdddee
+0,        260,        260,        1,   518400, 0x34cdddee
+0,        261,        261,        1,   518400, 0x34cdddee
+0,        262,        262,        1,   518400, 0x34cdddee
+0,        263,        263,        1,   518400, 0x34cdddee
+0,        264,        264,        1,   518400, 0x34cdddee
+0,        265,        265,        1,   518400, 0x34cdddee
+0,        266,        266,        1,   518400, 0x34cdddee
+0,        267,        267,        1,   518400, 0x34cdddee
+0,        268,        268,        1,   518400, 0x34cdddee
+0,        269,        269,        1,   518400, 0x34cdddee
 1,   53910000,   53910000,  2696000,     2095, 0x61bb15ed
 0,        270,        270,        1,   518400, 0x4db4ce51
-0,        283,        283,        1,   518400, 0xbab197ea
+0,        271,        271,        1,   518400, 0x4db4ce51
+0,        272,        272,        1,   518400, 0x4db4ce51
+0,        273,        273,        1,   518400, 0x4db4ce51
+0,        274,        274,        1,   518400, 0x4db4ce51
+0,        275,        275,        1,   518400, 0x4db4ce51
+0,        276,        276,        1,   518400, 0x4db4ce51
+0,        277,        277,        1,   518400, 0x4db4ce51
+0,        278,        278,        1,   518400, 0x4db4ce51
+0,        279,        279,        1,   518400, 0x4db4ce51
+0,        280,        280,        1,   518400, 0x4db4ce51
+0,        281,        281,        1,   518400, 0x4db4ce51
+0,        282,        282,        1,   518400, 0x4db4ce51
+0,        283,        283,        1,   518400, 0xe6bc0ea9
 1,   56663000,   56663000,  1262000,     1013, 0xc9ae89b7
 0,        284,        284,        1,   518400, 0xe6bc0ea9
-0,        290,        290,        1,   518400, 0xbab197ea
+0,        285,        285,        1,   518400, 0xe6bc0ea9
+0,        286,        286,        1,   518400, 0xe6bc0ea9
+0,        287,        287,        1,   518400, 0xe6bc0ea9
+0,        288,        288,        1,   518400, 0xe6bc0ea9
+0,        289,        289,        1,   518400, 0xe6bc0ea9
+0,        290,        290,        1,   518400, 0xa8643af7
 1,   58014000,   58014000,  1661000,      969, 0xe01878f0
 0,        291,        291,        1,   518400, 0xa8643af7
-0,        298,        298,        1,   518400, 0xbab197ea
+0,        292,        292,        1,   518400, 0xa8643af7
+0,        293,        293,        1,   518400, 0xa8643af7
+0,        294,        294,        1,   518400, 0xa8643af7
+0,        295,        295,        1,   518400, 0xa8643af7
+0,        296,        296,        1,   518400, 0xa8643af7
+0,        297,        297,        1,   518400, 0xa8643af7
+0,        298,        298,        1,   518400, 0xa8643af7
+0,        299,        299,        1,   518400, 0xbab197ea
+0,        300,        300,        1,   518400, 0xbab197ea
+0,        301,        301,        1,   518400, 0xbab197ea
+0,        302,        302,        1,   518400, 0xbab197ea
+0,        303,        303,        1,   518400, 0xbab197ea
+0,        304,        304,        1,   518400, 0xbab197ea
+0,        305,        305,        1,   518400, 0xbab197ea
+0,        306,        306,        1,   518400, 0xbab197ea
+0,        307,        307,        1,   518400, 0xbab197ea
+0,        308,        308,        1,   518400, 0xbab197ea
+0,        309,        309,        1,   518400, 0xbab197ea
+0,        310,        310,        1,   518400, 0xbab197ea
+0,        311,        311,        1,   518400, 0xbab197ea
+0,        312,        312,        1,   518400, 0xbab197ea
+0,        313,        313,        1,   518400, 0xbab197ea
+0,        314,        314,        1,   518400, 0xbab197ea
+0,        315,        315,        1,   518400, 0xbab197ea
+0,        316,        316,        1,   518400, 0xbab197ea
+0,        317,        317,        1,   518400, 0xbab197ea
+0,        318,        318,        1,   518400, 0xbab197ea
+0,        319,        319,        1,   518400, 0xbab197ea
+0,        320,        320,        1,   518400, 0xbab197ea
+0,        321,        321,        1,   518400, 0xbab197ea
+0,        322,        322,        1,   518400, 0xbab197ea
+0,        323,        323,        1,   518400, 0xbab197ea
+0,        324,        324,        1,   518400, 0xbab197ea
+0,        325,        325,        1,   518400, 0xbab197ea
+0,        326,        326,        1,   518400, 0xbab197ea
+0,        327,        327,        1,   518400, 0xbab197ea
+0,        328,        328,        1,   518400, 0xbab197ea
+0,        329,        329,        1,   518400, 0xbab197ea
+0,        330,        330,        1,   518400, 0xbab197ea
+0,        331,        331,        1,   518400, 0xbab197ea
+0,        332,        332,        1,   518400, 0xbab197ea
+0,        333,        333,        1,   518400, 0xbab197ea
+0,        334,        334,        1,   518400, 0xbab197ea
+0,        335,        335,        1,   518400, 0xbab197ea
+0,        336,        336,        1,   518400, 0xbab197ea
+0,        337,        337,        1,   518400, 0xbab197ea
+0,        338,        338,        1,   518400, 0xbab197ea
 1,   67724000,   67724000,  1365000,      844, 0xe7db4fc1
 0,        339,        339,        1,   518400, 0xb1885c67
-0,        345,        345,        1,   518400, 0xbab197ea
+0,        340,        340,        1,   518400, 0xb1885c67
+0,        341,        341,        1,   518400, 0xb1885c67
+0,        342,        342,        1,   518400, 0xb1885c67
+0,        343,        343,        1,   518400, 0xb1885c67
+0,        344,        344,        1,   518400, 0xb1885c67
+0,        345,        345,        1,   518400, 0xb1885c67
 1,   69175000,   69175000,  1558000,      802, 0xf48531ba
 0,        346,        346,        1,   518400, 0x378e3fd0
-0,        354,        354,        1,   518400, 0xbab197ea
+0,        347,        347,        1,   518400, 0x378e3fd0
+0,        348,        348,        1,   518400, 0x378e3fd0
+0,        349,        349,        1,   518400, 0x378e3fd0
+0,        350,        350,        1,   518400, 0x378e3fd0
+0,        351,        351,        1,   518400, 0x378e3fd0
+0,        352,        352,        1,   518400, 0x378e3fd0
+0,        353,        353,        1,   518400, 0x378e3fd0
+0,        354,        354,        1,   518400, 0xa3782469
 1,   70819000,   70819000,  1865000,     1709, 0xb4d5a1bd
 0,        355,        355,        1,   518400, 0xa3782469
-0,        363,        363,        1,   518400, 0xbab197ea
+0,        356,        356,        1,   518400, 0xa3782469
+0,        357,        357,        1,   518400, 0xa3782469
+0,        358,        358,        1,   518400, 0xa3782469
+0,        359,        359,        1,   518400, 0xa3782469
+0,        360,        360,        1,   518400, 0xa3782469
+0,        361,        361,        1,   518400, 0xa3782469
+0,        362,        362,        1,   518400, 0xa3782469
+0,        363,        363,        1,   518400, 0xa3782469
 1,   72762000,   72762000,  1968000,     2438, 0x99d7bc82
 0,        364,        364,        1,   518400, 0xba23a0d5
-0,        374,        374,        1,   518400, 0xbab197ea
+0,        365,        365,        1,   518400, 0xba23a0d5
+0,        366,        366,        1,   518400, 0xba23a0d5
+0,        367,        367,        1,   518400, 0xba23a0d5
+0,        368,        368,        1,   518400, 0xba23a0d5
+0,        369,        369,        1,   518400, 0xba23a0d5
+0,        370,        370,        1,   518400, 0xba23a0d5
+0,        371,        371,        1,   518400, 0xba23a0d5
+0,        372,        372,        1,   518400, 0xba23a0d5
+0,        373,        373,        1,   518400, 0xba23a0d5
+0,        374,        374,        1,   518400, 0x129de2f8
 1,   74806000,   74806000,  1831000,     2116, 0x96514097
 0,        375,        375,        1,   518400, 0x129de2f8
-0,        383,        383,        1,   518400, 0xbab197ea
+0,        376,        376,        1,   518400, 0x129de2f8
+0,        377,        377,        1,   518400, 0x129de2f8
+0,        378,        378,        1,   518400, 0x129de2f8
+0,        379,        379,        1,   518400, 0x129de2f8
+0,        380,        380,        1,   518400, 0x129de2f8
+0,        381,        381,        1,   518400, 0x129de2f8
+0,        382,        382,        1,   518400, 0x129de2f8
+0,        383,        383,        1,   518400, 0x129de2f8
 1,   76716000,   76716000,  1262000,     1822, 0xefccc72e
 0,        384,        384,        1,   518400, 0x19772f0f
-0,        390,        390,        1,   518400, 0xbab197ea
+0,        385,        385,        1,   518400, 0x19772f0f
+0,        386,        386,        1,   518400, 0x19772f0f
+0,        387,        387,        1,   518400, 0x19772f0f
+0,        388,        388,        1,   518400, 0x19772f0f
+0,        389,        389,        1,   518400, 0x19772f0f
+0,        390,        390,        1,   518400, 0x56f54e73
 1,   78051000,   78051000,  1524000,      987, 0x7b927a27
 0,        391,        391,        1,   518400, 0x56f54e73
-0,        398,        398,        1,   518400, 0xbab197ea
+0,        392,        392,        1,   518400, 0x56f54e73
+0,        393,        393,        1,   518400, 0x56f54e73
+0,        394,        394,        1,   518400, 0x56f54e73
+0,        395,        395,        1,   518400, 0x56f54e73
+0,        396,        396,        1,   518400, 0x56f54e73
+0,        397,        397,        1,   518400, 0x56f54e73
+0,        398,        398,        1,   518400, 0x300b5247
 1,   79644000,   79644000,  2662000,     2956, 0x190778f7
 0,        399,        399,        1,   518400, 0x300b5247
+0,        400,        400,        1,   518400, 0x300b5247
+0,        401,        401,        1,   518400, 0x300b5247
+0,        402,        402,        1,   518400, 0x300b5247
+0,        403,        403,        1,   518400, 0x300b5247
+0,        404,        404,        1,   518400, 0x300b5247
+0,        405,        405,        1,   518400, 0x300b5247
+0,        406,        406,        1,   518400, 0x300b5247
+0,        407,        407,        1,   518400, 0x300b5247
+0,        408,        408,        1,   518400, 0x300b5247
+0,        409,        409,        1,   518400, 0x300b5247
+0,        410,        410,        1,   518400, 0x300b5247
+0,        411,        411,        1,   518400, 0x300b5247
 1,   82380000,   82380000,  2764000,     3094, 0xc021b7d3
-0,        412,        412,        1,   518400, 0xbab197ea
+0,        412,        412,        1,   518400, 0x6fd028fa
 0,        413,        413,        1,   518400, 0x6fd028fa
-0,        426,        426,        1,   518400, 0xbab197ea
+0,        414,        414,        1,   518400, 0x6fd028fa
+0,        415,        415,        1,   518400, 0x6fd028fa
+0,        416,        416,        1,   518400, 0x6fd028fa
+0,        417,        417,        1,   518400, 0x6fd028fa
+0,        418,        418,        1,   518400, 0x6fd028fa
+0,        419,        419,        1,   518400, 0x6fd028fa
+0,        420,        420,        1,   518400, 0x6fd028fa
+0,        421,        421,        1,   518400, 0x6fd028fa
+0,        422,        422,        1,   518400, 0x6fd028fa
+0,        423,        423,        1,   518400, 0x6fd028fa
+0,        424,        424,        1,   518400, 0x6fd028fa
+0,        425,        425,        1,   518400, 0x6fd028fa
+0,        426,        426,        1,   518400, 0x01f80e9d
 1,   85225000,   85225000,  2366000,     2585, 0x74d0048f
 0,        427,        427,        1,   518400, 0x01f80e9d
-0,        438,        438,        1,   518400, 0xbab197ea
+0,        428,        428,        1,   518400, 0x01f80e9d
+0,        429,        429,        1,   518400, 0x01f80e9d
+0,        430,        430,        1,   518400, 0x01f80e9d
+0,        431,        431,        1,   518400, 0x01f80e9d
+0,        432,        432,        1,   518400, 0x01f80e9d
+0,        433,        433,        1,   518400, 0x01f80e9d
+0,        434,        434,        1,   518400, 0x01f80e9d
+0,        435,        435,        1,   518400, 0x01f80e9d
+0,        436,        436,        1,   518400, 0x01f80e9d
+0,        437,        437,        1,   518400, 0x01f80e9d
+0,        438,        438,        1,   518400, 0xb48d90c0
 1,   87652000,   87652000,  1831000,      634, 0x8832fda1
 0,        439,        439,        1,   518400, 0xb48d90c0
-0,        447,        447,        1,   518400, 0xbab197ea
+0,        440,        440,        1,   518400, 0xb48d90c0
+0,        441,        441,        1,   518400, 0xb48d90c0
+0,        442,        442,        1,   518400, 0xb48d90c0
+0,        443,        443,        1,   518400, 0xb48d90c0
+0,        444,        444,        1,   518400, 0xb48d90c0
+0,        445,        445,        1,   518400, 0xb48d90c0
+0,        446,        446,        1,   518400, 0xb48d90c0
+0,        447,        447,        1,   518400, 0xb48d90c0
+0,        448,        448,        1,   518400, 0xbab197ea
+0,        449,        449,        1,   518400, 0xbab197ea
+0,        450,        450,        1,   518400, 0xbab197ea
+0,        451,        451,        1,   518400, 0xbab197ea
+0,        452,        452,        1,   518400, 0xbab197ea
+0,        453,        453,        1,   518400, 0xbab197ea
+0,        454,        454,        1,   518400, 0xbab197ea
+0,        455,        455,        1,   518400, 0xbab197ea
+0,        456,        456,        1,   518400, 0xbab197ea
+0,        457,        457,        1,   518400, 0xbab197ea
 1,   91531000,   91531000,  2332000,     2080, 0x97a1146f
 0,        458,        458,        1,   518400, 0xcb5a0173
-0,        469,        469,        1,   518400, 0xbab197ea
+0,        459,        459,        1,   518400, 0xcb5a0173
+0,        460,        460,        1,   518400, 0xcb5a0173
+0,        461,        461,        1,   518400, 0xcb5a0173
+0,        462,        462,        1,   518400, 0xcb5a0173
+0,        463,        463,        1,   518400, 0xcb5a0173
+0,        464,        464,        1,   518400, 0xcb5a0173
+0,        465,        465,        1,   518400, 0xcb5a0173
+0,        466,        466,        1,   518400, 0xcb5a0173
+0,        467,        467,        1,   518400, 0xcb5a0173
+0,        468,        468,        1,   518400, 0xcb5a0173
+0,        469,        469,        1,   518400, 0xcb5a0173
+0,        470,        470,        1,   518400, 0xbab197ea
+0,        471,        471,        1,   518400, 0xbab197ea
+0,        472,        472,        1,   518400, 0xbab197ea
+0,        473,        473,        1,   518400, 0xbab197ea
+0,        474,        474,        1,   518400, 0xbab197ea
+0,        475,        475,        1,   518400, 0xbab197ea
+0,        476,        476,        1,   518400, 0xbab197ea
+0,        477,        477,        1,   518400, 0xbab197ea
 1,   95510000,   95510000,  3299000,     2964, 0x8b8f6684
 0,        478,        478,        1,   518400, 0xb8a323e4
-0,        494,        494,        1,   518400, 0xbab197ea
+0,        479,        479,        1,   518400, 0xb8a323e4
+0,        480,        480,        1,   518400, 0xb8a323e4
+0,        481,        481,        1,   518400, 0xb8a323e4
+0,        482,        482,        1,   518400, 0xb8a323e4
+0,        483,        483,        1,   518400, 0xb8a323e4
+0,        484,        484,        1,   518400, 0xb8a323e4
+0,        485,        485,        1,   518400, 0xb8a323e4
+0,        486,        486,        1,   518400, 0xb8a323e4
+0,        487,        487,        1,   518400, 0xb8a323e4
+0,        488,        488,        1,   518400, 0xb8a323e4
+0,        489,        489,        1,   518400, 0xb8a323e4
+0,        490,        490,        1,   518400, 0xb8a323e4
+0,        491,        491,        1,   518400, 0xb8a323e4
+0,        492,        492,        1,   518400, 0xb8a323e4
+0,        493,        493,        1,   518400, 0xb8a323e4
+0,        494,        494,        1,   518400, 0xc43518ba
 1,   98872000,   98872000,  2161000,     1875, 0x9002ef71
 0,        495,        495,        1,   518400, 0xc43518ba
-0,        505,        505,        1,   518400, 0xbab197ea
+0,        496,        496,        1,   518400, 0xc43518ba
+0,        497,        497,        1,   518400, 0xc43518ba
+0,        498,        498,        1,   518400, 0xc43518ba
+0,        499,        499,        1,   518400, 0xc43518ba
+0,        500,        500,        1,   518400, 0xc43518ba
+0,        501,        501,        1,   518400, 0xc43518ba
+0,        502,        502,        1,   518400, 0xc43518ba
+0,        503,        503,        1,   518400, 0xc43518ba
+0,        504,        504,        1,   518400, 0xc43518ba
+0,        505,        505,        1,   518400, 0xc43518ba
 1,  101124000,  101124000,  4096000,     3872, 0x20c6ed9c
 0,        506,        506,        1,   518400, 0x04e38692
-0,        526,        526,        1,   518400, 0xbab197ea
+0,        507,        507,        1,   518400, 0x04e38692
+0,        508,        508,        1,   518400, 0x04e38692
+0,        509,        509,        1,   518400, 0x04e38692
+0,        510,        510,        1,   518400, 0x04e38692
+0,        511,        511,        1,   518400, 0x04e38692
+0,        512,        512,        1,   518400, 0x04e38692
+0,        513,        513,        1,   518400, 0x04e38692
+0,        514,        514,        1,   518400, 0x04e38692
+0,        515,        515,        1,   518400, 0x04e38692
+0,        516,        516,        1,   518400, 0x04e38692
+0,        517,        517,        1,   518400, 0x04e38692
+0,        518,        518,        1,   518400, 0x04e38692
+0,        519,        519,        1,   518400, 0x04e38692
+0,        520,        520,        1,   518400, 0x04e38692
+0,        521,        521,        1,   518400, 0x04e38692
+0,        522,        522,        1,   518400, 0x04e38692
+0,        523,        523,        1,   518400, 0x04e38692
+0,        524,        524,        1,   518400, 0x04e38692
+0,        525,        525,        1,   518400, 0x04e38692
+0,        526,        526,        1,   518400, 0x04e38692
 1,  105303000,  105303000,  2730000,     3094, 0xf203a663
 0,        527,        527,        1,   518400, 0x856b0ee5
-0,        540,        540,        1,   518400, 0xbab197ea
+0,        528,        528,        1,   518400, 0x856b0ee5
+0,        529,        529,        1,   518400, 0x856b0ee5
+0,        530,        530,        1,   518400, 0x856b0ee5
+0,        531,        531,        1,   518400, 0x856b0ee5
+0,        532,        532,        1,   518400, 0x856b0ee5
+0,        533,        533,        1,   518400, 0x856b0ee5
+0,        534,        534,        1,   518400, 0x856b0ee5
+0,        535,        535,        1,   518400, 0x856b0ee5
+0,        536,        536,        1,   518400, 0x856b0ee5
+0,        537,        537,        1,   518400, 0x856b0ee5
+0,        538,        538,        1,   518400, 0x856b0ee5
+0,        539,        539,        1,   518400, 0x856b0ee5
+0,        540,        540,        1,   518400, 0x856b0ee5
 1,  108106000,  108106000,  2059000,     2404, 0x41a7b429
 0,        541,        541,        1,   518400, 0x3e5beee2
-0,        551,        551,        1,   518400, 0xbab197ea
+0,        542,        542,        1,   518400, 0x3e5beee2
+0,        543,        543,        1,   518400, 0x3e5beee2
+0,        544,        544,        1,   518400, 0x3e5beee2
+0,        545,        545,        1,   518400, 0x3e5beee2
+0,        546,        546,        1,   518400, 0x3e5beee2
+0,        547,        547,        1,   518400, 0x3e5beee2
+0,        548,        548,        1,   518400, 0x3e5beee2
+0,        549,        549,        1,   518400, 0x3e5beee2
+0,        550,        550,        1,   518400, 0x3e5beee2
+0,        551,        551,        1,   518400, 0x3e5beee2
+0,        552,        552,        1,   518400, 0xbab197ea
+0,        553,        553,        1,   518400, 0xbab197ea
+0,        554,        554,        1,   518400, 0xbab197ea
+0,        555,        555,        1,   518400, 0xbab197ea
+0,        556,        556,        1,   518400, 0xbab197ea
+0,        557,        557,        1,   518400, 0xbab197ea
+0,        558,        558,        1,   518400, 0xbab197ea
+0,        559,        559,        1,   518400, 0xbab197ea
+0,        560,        560,        1,   518400, 0xbab197ea
+0,        561,        561,        1,   518400, 0xbab197ea
+0,        562,        562,        1,   518400, 0xbab197ea
+0,        563,        563,        1,   518400, 0xbab197ea
+0,        564,        564,        1,   518400, 0xbab197ea
+0,        565,        565,        1,   518400, 0xbab197ea
+0,        566,        566,        1,   518400, 0xbab197ea
+0,        567,        567,        1,   518400, 0xbab197ea
+0,        568,        568,        1,   518400, 0xbab197ea
+0,        569,        569,        1,   518400, 0xbab197ea
+0,        570,        570,        1,   518400, 0xbab197ea
+0,        571,        571,        1,   518400, 0xbab197ea
+0,        572,        572,        1,   518400, 0xbab197ea
+0,        573,        573,        1,   518400, 0xbab197ea
+0,        574,        574,        1,   518400, 0xbab197ea
+0,        575,        575,        1,   518400, 0xbab197ea
+0,        576,        576,        1,   518400, 0xbab197ea
+0,        577,        577,        1,   518400, 0xbab197ea
+0,        578,        578,        1,   518400, 0xbab197ea
+0,        579,        579,        1,   518400, 0xbab197ea
+0,        580,        580,        1,   518400, 0xbab197ea
+0,        581,        581,        1,   518400, 0xbab197ea
+0,        582,        582,        1,   518400, 0xbab197ea
+0,        583,        583,        1,   518400, 0xbab197ea
+0,        584,        584,        1,   518400, 0xbab197ea
+0,        585,        585,        1,   518400, 0xbab197ea
+0,        586,        586,        1,   518400, 0xbab197ea
+0,        587,        587,        1,   518400, 0xbab197ea
+0,        588,        588,        1,   518400, 0xbab197ea
+0,        589,        589,        1,   518400, 0xbab197ea
+0,        590,        590,        1,   518400, 0xbab197ea
+0,        591,        591,        1,   518400, 0xbab197ea
+0,        592,        592,        1,   518400, 0xbab197ea
+0,        593,        593,        1,   518400, 0xbab197ea
+0,        594,        594,        1,   518400, 0xbab197ea
+0,        595,        595,        1,   518400, 0xbab197ea
+0,        596,        596,        1,   518400, 0xbab197ea
+0,        597,        597,        1,   518400, 0xbab197ea
+0,        598,        598,        1,   518400, 0xbab197ea
+0,        599,        599,        1,   518400, 0xbab197ea
+0,        600,        600,        1,   518400, 0xbab197ea
+0,        601,        601,        1,   518400, 0xbab197ea
+0,        602,        602,        1,   518400, 0xbab197ea
+0,        603,        603,        1,   518400, 0xbab197ea
+0,        604,        604,        1,   518400, 0xbab197ea
+0,        605,        605,        1,   518400, 0xbab197ea
+0,        606,        606,        1,   518400, 0xbab197ea
+0,        607,        607,        1,   518400, 0xbab197ea
+0,        608,        608,        1,   518400, 0xbab197ea
+0,        609,        609,        1,   518400, 0xbab197ea
+0,        610,        610,        1,   518400, 0xbab197ea
+0,        611,        611,        1,   518400, 0xbab197ea
+0,        612,        612,        1,   518400, 0xbab197ea
+0,        613,        613,        1,   518400, 0xbab197ea
+0,        614,        614,        1,   518400, 0xbab197ea
+0,        615,        615,        1,   518400, 0xbab197ea
+0,        616,        616,        1,   518400, 0xbab197ea
+0,        617,        617,        1,   518400, 0xbab197ea
+0,        618,        618,        1,   518400, 0xbab197ea
+0,        619,        619,        1,   518400, 0xbab197ea
+0,        620,        620,        1,   518400, 0xbab197ea
+0,        621,        621,        1,   518400, 0xbab197ea
+0,        622,        622,        1,   518400, 0xbab197ea
+0,        623,        623,        1,   518400, 0xbab197ea
+0,        624,        624,        1,   518400, 0xbab197ea
+0,        625,        625,        1,   518400, 0xbab197ea
+0,        626,        626,        1,   518400, 0xbab197ea
+0,        627,        627,        1,   518400, 0xbab197ea
+0,        628,        628,        1,   518400, 0xbab197ea
+0,        629,        629,        1,   518400, 0xbab197ea
+0,        630,        630,        1,   518400, 0xbab197ea
+0,        631,        631,        1,   518400, 0xbab197ea
+0,        632,        632,        1,   518400, 0xbab197ea
+0,        633,        633,        1,   518400, 0xbab197ea
+0,        634,        634,        1,   518400, 0xbab197ea
+0,        635,        635,        1,   518400, 0xbab197ea
+0,        636,        636,        1,   518400, 0xbab197ea
+0,        637,        637,        1,   518400, 0xbab197ea
+0,        638,        638,        1,   518400, 0xbab197ea
+0,        639,        639,        1,   518400, 0xbab197ea
+0,        640,        640,        1,   518400, 0xbab197ea
+0,        641,        641,        1,   518400, 0xbab197ea
+0,        642,        642,        1,   518400, 0xbab197ea
+0,        643,        643,        1,   518400, 0xbab197ea
+0,        644,        644,        1,   518400, 0xbab197ea
+0,        645,        645,        1,   518400, 0xbab197ea
+0,        646,        646,        1,   518400, 0xbab197ea
+0,        647,        647,        1,   518400, 0xbab197ea
+0,        648,        648,        1,   518400, 0xbab197ea
+0,        649,        649,        1,   518400, 0xbab197ea
+0,        650,        650,        1,   518400, 0xbab197ea
+0,        651,        651,        1,   518400, 0xbab197ea
+0,        652,        652,        1,   518400, 0xbab197ea
+0,        653,        653,        1,   518400, 0xbab197ea
+0,        654,        654,        1,   518400, 0xbab197ea
+0,        655,        655,        1,   518400, 0xbab197ea
+0,        656,        656,        1,   518400, 0xbab197ea
+0,        657,        657,        1,   518400, 0xbab197ea
+0,        658,        658,        1,   518400, 0xbab197ea
+0,        659,        659,        1,   518400, 0xbab197ea
+0,        660,        660,        1,   518400, 0xbab197ea
+0,        661,        661,        1,   518400, 0xbab197ea
+0,        662,        662,        1,   518400, 0xbab197ea
+0,        663,        663,        1,   518400, 0xbab197ea
+0,        664,        664,        1,   518400, 0xbab197ea
+0,        665,        665,        1,   518400, 0xbab197ea
+0,        666,        666,        1,   518400, 0xbab197ea
+0,        667,        667,        1,   518400, 0xbab197ea
+0,        668,        668,        1,   518400, 0xbab197ea
+0,        669,        669,        1,   518400, 0xbab197ea
+0,        670,        670,        1,   518400, 0xbab197ea
+0,        671,        671,        1,   518400, 0xbab197ea
+0,        672,        672,        1,   518400, 0xbab197ea
+0,        673,        673,        1,   518400, 0xbab197ea
+0,        674,        674,        1,   518400, 0xbab197ea
+0,        675,        675,        1,   518400, 0xbab197ea
+0,        676,        676,        1,   518400, 0xbab197ea
+0,        677,        677,        1,   518400, 0xbab197ea
+0,        678,        678,        1,   518400, 0xbab197ea
+0,        679,        679,        1,   518400, 0xbab197ea
+0,        680,        680,        1,   518400, 0xbab197ea
+0,        681,        681,        1,   518400, 0xbab197ea
+0,        682,        682,        1,   518400, 0xbab197ea
+0,        683,        683,        1,   518400, 0xbab197ea
+0,        684,        684,        1,   518400, 0xbab197ea
+0,        685,        685,        1,   518400, 0xbab197ea
+0,        686,        686,        1,   518400, 0xbab197ea
+0,        687,        687,        1,   518400, 0xbab197ea
+0,        688,        688,        1,   518400, 0xbab197ea
+0,        689,        689,        1,   518400, 0xbab197ea
+0,        690,        690,        1,   518400, 0xbab197ea
+0,        691,        691,        1,   518400, 0xbab197ea
+0,        692,        692,        1,   518400, 0xbab197ea
+0,        693,        693,        1,   518400, 0xbab197ea
+0,        694,        694,        1,   518400, 0xbab197ea
+0,        695,        695,        1,   518400, 0xbab197ea
+0,        696,        696,        1,   518400, 0xbab197ea
+0,        697,        697,        1,   518400, 0xbab197ea
+0,        698,        698,        1,   518400, 0xbab197ea
+0,        699,        699,        1,   518400, 0xbab197ea
+0,        700,        700,        1,   518400, 0xbab197ea
+0,        701,        701,        1,   518400, 0xbab197ea
+0,        702,        702,        1,   518400, 0xbab197ea
+0,        703,        703,        1,   518400, 0xbab197ea
+0,        704,        704,        1,   518400, 0xbab197ea
+0,        705,        705,        1,   518400, 0xbab197ea
+0,        706,        706,        1,   518400, 0xbab197ea
+0,        707,        707,        1,   518400, 0xbab197ea
 1,  141556000,  141556000,  1661000,     1088, 0xde20aa20
 0,        708,        708,        1,   518400, 0xb8bc1365
-0,        716,        716,        1,   518400, 0xbab197ea
+0,        709,        709,        1,   518400, 0xb8bc1365
+0,        710,        710,        1,   518400, 0xb8bc1365
+0,        711,        711,        1,   518400, 0xb8bc1365
+0,        712,        712,        1,   518400, 0xb8bc1365
+0,        713,        713,        1,   518400, 0xb8bc1365
+0,        714,        714,        1,   518400, 0xb8bc1365
+0,        715,        715,        1,   518400, 0xb8bc1365
+0,        716,        716,        1,   518400, 0xb8bc1365
+0,        717,        717,        1,   518400, 0xbab197ea
+0,        718,        718,        1,   518400, 0xbab197ea
+0,        719,        719,        1,   518400, 0xbab197ea
+0,        720,        720,        1,   518400, 0xbab197ea
+0,        721,        721,        1,   518400, 0xbab197ea
+0,        722,        722,        1,   518400, 0xbab197ea
+0,        723,        723,        1,   518400, 0xbab197ea
+0,        724,        724,        1,   518400, 0xbab197ea
+0,        725,        725,        1,   518400, 0xbab197ea
+0,        726,        726,        1,   518400, 0xbab197ea
+0,        727,        727,        1,   518400, 0xbab197ea
+0,        728,        728,        1,   518400, 0xbab197ea
+0,        729,        729,        1,   518400, 0xbab197ea
+0,        730,        730,        1,   518400, 0xbab197ea
+0,        731,        731,        1,   518400, 0xbab197ea
+0,        732,        732,        1,   518400, 0xbab197ea
+0,        733,        733,        1,   518400, 0xbab197ea
+0,        734,        734,        1,   518400, 0xbab197ea
+0,        735,        735,        1,   518400, 0xbab197ea
+0,        736,        736,        1,   518400, 0xbab197ea
+0,        737,        737,        1,   518400, 0xbab197ea
+0,        738,        738,        1,   518400, 0xbab197ea
+0,        739,        739,        1,   518400, 0xbab197ea
+0,        740,        740,        1,   518400, 0xbab197ea
+0,        741,        741,        1,   518400, 0xbab197ea
+0,        742,        742,        1,   518400, 0xbab197ea
+0,        743,        743,        1,   518400, 0xbab197ea
+0,        744,        744,        1,   518400, 0xbab197ea
+0,        745,        745,        1,   518400, 0xbab197ea
+0,        746,        746,        1,   518400, 0xbab197ea
+0,        747,        747,        1,   518400, 0xbab197ea
+0,        748,        748,        1,   518400, 0xbab197ea
+0,        749,        749,        1,   518400, 0xbab197ea
+0,        750,        750,        1,   518400, 0xbab197ea
+0,        751,        751,        1,   518400, 0xbab197ea
+0,        752,        752,        1,   518400, 0xbab197ea
+0,        753,        753,        1,   518400, 0xbab197ea
+0,        754,        754,        1,   518400, 0xbab197ea
+0,        755,        755,        1,   518400, 0xbab197ea
+0,        756,        756,        1,   518400, 0xbab197ea
+0,        757,        757,        1,   518400, 0xbab197ea
+0,        758,        758,        1,   518400, 0xbab197ea
+0,        759,        759,        1,   518400, 0xbab197ea
+0,        760,        760,        1,   518400, 0xbab197ea
+0,        761,        761,        1,   518400, 0xbab197ea
+0,        762,        762,        1,   518400, 0xbab197ea
+0,        763,        763,        1,   518400, 0xbab197ea
+0,        764,        764,        1,   518400, 0xbab197ea
+0,        765,        765,        1,   518400, 0xbab197ea
+0,        766,        766,        1,   518400, 0xbab197ea
+0,        767,        767,        1,   518400, 0xbab197ea
+0,        768,        768,        1,   518400, 0xbab197ea
+0,        769,        769,        1,   518400, 0xbab197ea
+0,        770,        770,        1,   518400, 0xbab197ea
+0,        771,        771,        1,   518400, 0xbab197ea
+0,        772,        772,        1,   518400, 0xbab197ea
+0,        773,        773,        1,   518400, 0xbab197ea
+0,        774,        774,        1,   518400, 0xbab197ea
+0,        775,        775,        1,   518400, 0xbab197ea
+0,        776,        776,        1,   518400, 0xbab197ea
+0,        777,        777,        1,   518400, 0xbab197ea
+0,        778,        778,        1,   518400, 0xbab197ea
+0,        779,        779,        1,   518400, 0xbab197ea
+0,        780,        780,        1,   518400, 0xbab197ea
+0,        781,        781,        1,   518400, 0xbab197ea
+0,        782,        782,        1,   518400, 0xbab197ea
+0,        783,        783,        1,   518400, 0xbab197ea
+0,        784,        784,        1,   518400, 0xbab197ea
+0,        785,        785,        1,   518400, 0xbab197ea
+0,        786,        786,        1,   518400, 0xbab197ea
+0,        787,        787,        1,   518400, 0xbab197ea
+0,        788,        788,        1,   518400, 0xbab197ea
+0,        789,        789,        1,   518400, 0xbab197ea
+0,        790,        790,        1,   518400, 0xbab197ea
+0,        791,        791,        1,   518400, 0xbab197ea
+0,        792,        792,        1,   518400, 0xbab197ea
+0,        793,        793,        1,   518400, 0xbab197ea
+0,        794,        794,        1,   518400, 0xbab197ea
+0,        795,        795,        1,   518400, 0xbab197ea
+0,        796,        796,        1,   518400, 0xbab197ea
+0,        797,        797,        1,   518400, 0xbab197ea
+0,        798,        798,        1,   518400, 0xbab197ea
+0,        799,        799,        1,   518400, 0xbab197ea
+0,        800,        800,        1,   518400, 0xbab197ea
+0,        801,        801,        1,   518400, 0xbab197ea
+0,        802,        802,        1,   518400, 0xbab197ea
+0,        803,        803,        1,   518400, 0xbab197ea
+0,        804,        804,        1,   518400, 0xbab197ea
+0,        805,        805,        1,   518400, 0xbab197ea
+0,        806,        806,        1,   518400, 0xbab197ea
+0,        807,        807,        1,   518400, 0xbab197ea
+0,        808,        808,        1,   518400, 0xbab197ea
+0,        809,        809,        1,   518400, 0xbab197ea
+0,        810,        810,        1,   518400, 0xbab197ea
+0,        811,        811,        1,   518400, 0xbab197ea
+0,        812,        812,        1,   518400, 0xbab197ea
+0,        813,        813,        1,   518400, 0xbab197ea
+0,        814,        814,        1,   518400, 0xbab197ea
+0,        815,        815,        1,   518400, 0xbab197ea
+0,        816,        816,        1,   518400, 0xbab197ea
 0,        817,        817,        1,   518400, 0x83efa32d
 1,  163445000,  163445000,  1331000,      339, 0x8bd186ef
-0,        824,        824,        1,   518400, 0xbab197ea
+0,        818,        818,        1,   518400, 0x83efa32d
+0,        819,        819,        1,   518400, 0x83efa32d
+0,        820,        820,        1,   518400, 0x83efa32d
+0,        821,        821,        1,   518400, 0x83efa32d
+0,        822,        822,        1,   518400, 0x83efa32d
+0,        823,        823,        1,   518400, 0x83efa32d
+0,        824,        824,        1,   518400, 0x83efa32d
+0,        825,        825,        1,   518400, 0xbab197ea
+0,        826,        826,        1,   518400, 0xbab197ea
+0,        827,        827,        1,   518400, 0xbab197ea
+0,        828,        828,        1,   518400, 0xbab197ea
+0,        829,        829,        1,   518400, 0xbab197ea
+0,        830,        830,        1,   518400, 0xbab197ea
+0,        831,        831,        1,   518400, 0xbab197ea
+0,        832,        832,        1,   518400, 0xbab197ea
+0,        833,        833,        1,   518400, 0xbab197ea
+0,        834,        834,        1,   518400, 0xbab197ea
+0,        835,        835,        1,   518400, 0xbab197ea
+0,        836,        836,        1,   518400, 0xbab197ea
+0,        837,        837,        1,   518400, 0xbab197ea
+0,        838,        838,        1,   518400, 0xbab197ea
+0,        839,        839,        1,   518400, 0xbab197ea
 0,        840,        840,        1,   518400, 0x03ea0e90
 1,  168049000,  168049000,  1900000,     1312, 0x0bf20e8d
-0,        850,        850,        1,   518400, 0xbab197ea
+0,        841,        841,        1,   518400, 0x03ea0e90
+0,        842,        842,        1,   518400, 0x03ea0e90
+0,        843,        843,        1,   518400, 0x03ea0e90
+0,        844,        844,        1,   518400, 0x03ea0e90
+0,        845,        845,        1,   518400, 0x03ea0e90
+0,        846,        846,        1,   518400, 0x03ea0e90
+0,        847,        847,        1,   518400, 0x03ea0e90
+0,        848,        848,        1,   518400, 0x03ea0e90
+0,        849,        849,        1,   518400, 0x03ea0e90
+0,        850,        850,        1,   518400, 0x8780239e
 1,  170035000,  170035000,  1524000,     1279, 0xb6c2dafe
 0,        851,        851,        1,   518400, 0x8780239e
-0,        858,        858,        1,   518400, 0xbab197ea
+0,        852,        852,        1,   518400, 0x8780239e
+0,        853,        853,        1,   518400, 0x8780239e
+0,        854,        854,        1,   518400, 0x8780239e
+0,        855,        855,        1,   518400, 0x8780239e
+0,        856,        856,        1,   518400, 0x8780239e
+0,        857,        857,        1,   518400, 0x8780239e
+0,        858,        858,        1,   518400, 0x8780239e
+0,        859,        859,        1,   518400, 0xbab197ea
+0,        860,        860,        1,   518400, 0xbab197ea
 0,        861,        861,        1,   518400, 0x6eb72347
 1,  172203000,  172203000,  1695000,     1826, 0x9a1ac769
-0,        869,        869,        1,   518400, 0xbab197ea
+0,        862,        862,        1,   518400, 0x6eb72347
+0,        863,        863,        1,   518400, 0x6eb72347
+0,        864,        864,        1,   518400, 0x6eb72347
+0,        865,        865,        1,   518400, 0x6eb72347
+0,        866,        866,        1,   518400, 0x6eb72347
+0,        867,        867,        1,   518400, 0x6eb72347
+0,        868,        868,        1,   518400, 0x6eb72347
+0,        869,        869,        1,   518400, 0x6eb72347
 1,  173947000,  173947000,  1934000,     1474, 0xa9b03cdc
 0,        870,        870,        1,   518400, 0x9c4a3a3d
-0,        879,        879,        1,   518400, 0xbab197ea
+0,        871,        871,        1,   518400, 0x9c4a3a3d
+0,        872,        872,        1,   518400, 0x9c4a3a3d
+0,        873,        873,        1,   518400, 0x9c4a3a3d
+0,        874,        874,        1,   518400, 0x9c4a3a3d
+0,        875,        875,        1,   518400, 0x9c4a3a3d
+0,        876,        876,        1,   518400, 0x9c4a3a3d
+0,        877,        877,        1,   518400, 0x9c4a3a3d
+0,        878,        878,        1,   518400, 0x9c4a3a3d
+0,        879,        879,        1,   518400, 0x9c4a3a3d
 1,  175957000,  175957000,  1763000,     1019, 0x20409355
 0,        880,        880,        1,   518400, 0xc9ebfa89
-0,        889,        889,        1,   518400, 0xbab197ea
+0,        881,        881,        1,   518400, 0xc9ebfa89
+0,        882,        882,        1,   518400, 0xc9ebfa89
+0,        883,        883,        1,   518400, 0xc9ebfa89
+0,        884,        884,        1,   518400, 0xc9ebfa89
+0,        885,        885,        1,   518400, 0xc9ebfa89
+0,        886,        886,        1,   518400, 0xc9ebfa89
+0,        887,        887,        1,   518400, 0xc9ebfa89
+0,        888,        888,        1,   518400, 0xc9ebfa89
+0,        889,        889,        1,   518400, 0xc9ebfa89
+0,        890,        890,        1,   518400, 0xbab197ea
+0,        891,        891,        1,   518400, 0xbab197ea
+0,        892,        892,        1,   518400, 0xbab197ea
+0,        893,        893,        1,   518400, 0xbab197ea
+0,        894,        894,        1,   518400, 0xbab197ea
+0,        895,        895,        1,   518400, 0xbab197ea
+0,        896,        896,        1,   518400, 0xbab197ea
+0,        897,        897,        1,   518400, 0xbab197ea
+0,        898,        898,        1,   518400, 0xbab197ea
+0,        899,        899,        1,   518400, 0xbab197ea
+0,        900,        900,        1,   518400, 0xbab197ea
+0,        901,        901,        1,   518400, 0xbab197ea
+0,        902,        902,        1,   518400, 0xbab197ea
+0,        903,        903,        1,   518400, 0xbab197ea
+0,        904,        904,        1,   518400, 0xbab197ea
+0,        905,        905,        1,   518400, 0xbab197ea
+0,        906,        906,        1,   518400, 0xbab197ea
+0,        907,        907,        1,   518400, 0xbab197ea
+0,        908,        908,        1,   518400, 0xbab197ea
+0,        909,        909,        1,   518400, 0xbab197ea
+0,        910,        910,        1,   518400, 0xbab197ea
+0,        911,        911,        1,   518400, 0xbab197ea
+0,        912,        912,        1,   518400, 0xbab197ea
+0,        913,        913,        1,   518400, 0xbab197ea
+0,        914,        914,        1,   518400, 0xbab197ea
+0,        915,        915,        1,   518400, 0xbab197ea
+0,        916,        916,        1,   518400, 0xbab197ea
+0,        917,        917,        1,   518400, 0xbab197ea
+0,        918,        918,        1,   518400, 0xbab197ea
+0,        919,        919,        1,   518400, 0xbab197ea
+0,        920,        920,        1,   518400, 0xbab197ea
+0,        921,        921,        1,   518400, 0xbab197ea
+0,        922,        922,        1,   518400, 0xbab197ea
+0,        923,        923,        1,   518400, 0xbab197ea
+0,        924,        924,        1,   518400, 0xbab197ea
+0,        925,        925,        1,   518400, 0xbab197ea
+0,        926,        926,        1,   518400, 0xbab197ea
+0,        927,        927,        1,   518400, 0xbab197ea
+0,        928,        928,        1,   518400, 0xbab197ea
+0,        929,        929,        1,   518400, 0xbab197ea
+0,        930,        930,        1,   518400, 0xbab197ea
+0,        931,        931,        1,   518400, 0xbab197ea
+0,        932,        932,        1,   518400, 0xbab197ea
+0,        933,        933,        1,   518400, 0xbab197ea
+0,        934,        934,        1,   518400, 0xbab197ea
+0,        935,        935,        1,   518400, 0xbab197ea
+0,        936,        936,        1,   518400, 0xbab197ea
+0,        937,        937,        1,   518400, 0xbab197ea
+0,        938,        938,        1,   518400, 0xbab197ea
+0,        939,        939,        1,   518400, 0xbab197ea
+0,        940,        940,        1,   518400, 0xbab197ea
+0,        941,        941,        1,   518400, 0xbab197ea
+0,        942,        942,        1,   518400, 0xbab197ea
+0,        943,        943,        1,   518400, 0xbab197ea
+0,        944,        944,        1,   518400, 0xbab197ea
+0,        945,        945,        1,   518400, 0xbab197ea
 0,        946,        946,        1,   518400, 0xbaf801ef
 1,  189295000,  189295000,  1968000,     1596, 0x408c726e
-0,        956,        956,        1,   518400, 0xbab197ea
+0,        947,        947,        1,   518400, 0xbaf801ef
+0,        948,        948,        1,   518400, 0xbaf801ef
+0,        949,        949,        1,   518400, 0xbaf801ef
+0,        950,        950,        1,   518400, 0xbaf801ef
+0,        951,        951,        1,   518400, 0xbaf801ef
+0,        952,        952,        1,   518400, 0xbaf801ef
+0,        953,        953,        1,   518400, 0xbaf801ef
+0,        954,        954,        1,   518400, 0xbaf801ef
+0,        955,        955,        1,   518400, 0xbaf801ef
+0,        956,        956,        1,   518400, 0xbaf801ef
 1,  191356000,  191356000,  1228000,     1517, 0xae8c5c2b
 0,        957,        957,        1,   518400, 0x59f4e72f
-0,        963,        963,        1,   518400, 0xbab197ea
+0,        958,        958,        1,   518400, 0x59f4e72f
+0,        959,        959,        1,   518400, 0x59f4e72f
+0,        960,        960,        1,   518400, 0x59f4e72f
+0,        961,        961,        1,   518400, 0x59f4e72f
+0,        962,        962,        1,   518400, 0x59f4e72f
+0,        963,        963,        1,   518400, 0x9d5b9d69
 1,  192640000,  192640000,  1763000,     2506, 0xa458d6d4
 0,        964,        964,        1,   518400, 0x9d5b9d69
-0,        972,        972,        1,   518400, 0xbab197ea
+0,        965,        965,        1,   518400, 0x9d5b9d69
+0,        966,        966,        1,   518400, 0x9d5b9d69
+0,        967,        967,        1,   518400, 0x9d5b9d69
+0,        968,        968,        1,   518400, 0x9d5b9d69
+0,        969,        969,        1,   518400, 0x9d5b9d69
+0,        970,        970,        1,   518400, 0x9d5b9d69
+0,        971,        971,        1,   518400, 0x9d5b9d69
+0,        972,        972,        1,   518400, 0x9d5b9d69
+0,        973,        973,        1,   518400, 0xbab197ea
+0,        974,        974,        1,   518400, 0xbab197ea
+0,        975,        975,        1,   518400, 0xbab197ea
 1,  195193000,  195193000,  1092000,     1074, 0x397ba9a8
 0,        976,        976,        1,   518400, 0x923d1ce7
-0,        981,        981,        1,   518400, 0xbab197ea
+0,        977,        977,        1,   518400, 0x923d1ce7
+0,        978,        978,        1,   518400, 0x923d1ce7
+0,        979,        979,        1,   518400, 0x923d1ce7
+0,        980,        980,        1,   518400, 0x923d1ce7
+0,        981,        981,        1,   518400, 0x923d1ce7
 1,  196361000,  196361000,  1524000,     1715, 0x695ca41e
 0,        982,        982,        1,   518400, 0x6e652cd2
-0,        989,        989,        1,   518400, 0xbab197ea
+0,        983,        983,        1,   518400, 0x6e652cd2
+0,        984,        984,        1,   518400, 0x6e652cd2
+0,        985,        985,        1,   518400, 0x6e652cd2
+0,        986,        986,        1,   518400, 0x6e652cd2
+0,        987,        987,        1,   518400, 0x6e652cd2
+0,        988,        988,        1,   518400, 0x6e652cd2
+0,        989,        989,        1,   518400, 0x6e652cd2
 1,  197946000,  197946000,  1160000,      789, 0xc63a189e
 0,        990,        990,        1,   518400, 0x25113966
-0,        996,        996,        1,   518400, 0xbab197ea
+0,        991,        991,        1,   518400, 0x25113966
+0,        992,        992,        1,   518400, 0x25113966
+0,        993,        993,        1,   518400, 0x25113966
+0,        994,        994,        1,   518400, 0x25113966
+0,        995,        995,        1,   518400, 0x25113966
+0,        996,        996,        1,   518400, 0x2dc83609
 1,  199230000,  199230000,  1627000,     1846, 0xeea8c599
 0,        997,        997,        1,   518400, 0x2dc83609
-0,       1004,       1004,        1,   518400, 0xbab197ea
+0,        998,        998,        1,   518400, 0x2dc83609
+0,        999,        999,        1,   518400, 0x2dc83609
+0,       1000,       1000,        1,   518400, 0x2dc83609
+0,       1001,       1001,        1,   518400, 0x2dc83609
+0,       1002,       1002,        1,   518400, 0x2dc83609
+0,       1003,       1003,        1,   518400, 0x2dc83609
+0,       1004,       1004,        1,   518400, 0x2dc83609
 1,  200924000,  200924000,  1763000,      922, 0xd4a87222
 0,       1005,       1005,        1,   518400, 0x90483bc6
-0,       1013,       1013,        1,   518400, 0xbab197ea
+0,       1006,       1006,        1,   518400, 0x90483bc6
+0,       1007,       1007,        1,   518400, 0x90483bc6
+0,       1008,       1008,        1,   518400, 0x90483bc6
+0,       1009,       1009,        1,   518400, 0x90483bc6
+0,       1010,       1010,        1,   518400, 0x90483bc6
+0,       1011,       1011,        1,   518400, 0x90483bc6
+0,       1012,       1012,        1,   518400, 0x90483bc6
+0,       1013,       1013,        1,   518400, 0x90483bc6
+0,       1014,       1014,        1,   518400, 0xbab197ea
+0,       1015,       1015,        1,   518400, 0xbab197ea
+0,       1016,       1016,        1,   518400, 0xbab197ea
+0,       1017,       1017,        1,   518400, 0xbab197ea
+0,       1018,       1018,        1,   518400, 0xbab197ea
+0,       1019,       1019,        1,   518400, 0xbab197ea
+0,       1020,       1020,        1,   518400, 0xbab197ea
+0,       1021,       1021,        1,   518400, 0xbab197ea
+0,       1022,       1022,        1,   518400, 0xbab197ea
+0,       1023,       1023,        1,   518400, 0xbab197ea
+0,       1024,       1024,        1,   518400, 0xbab197ea
+0,       1025,       1025,        1,   518400, 0xbab197ea
+0,       1026,       1026,        1,   518400, 0xbab197ea
+0,       1027,       1027,        1,   518400, 0xbab197ea
+0,       1028,       1028,        1,   518400, 0xbab197ea
+0,       1029,       1029,        1,   518400, 0xbab197ea
+0,       1030,       1030,        1,   518400, 0xbab197ea
+0,       1031,       1031,        1,   518400, 0xbab197ea
+0,       1032,       1032,        1,   518400, 0xbab197ea
+0,       1033,       1033,        1,   518400, 0xbab197ea
+0,       1034,       1034,        1,   518400, 0xbab197ea
+0,       1035,       1035,        1,   518400, 0xbab197ea
+0,       1036,       1036,        1,   518400, 0xbab197ea
+0,       1037,       1037,        1,   518400, 0xbab197ea
+0,       1038,       1038,        1,   518400, 0xbab197ea
+0,       1039,       1039,        1,   518400, 0xbab197ea
+0,       1040,       1040,        1,   518400, 0xbab197ea
+0,       1041,       1041,        1,   518400, 0xbab197ea
+0,       1042,       1042,        1,   518400, 0xbab197ea
+0,       1043,       1043,        1,   518400, 0xbab197ea
+0,       1044,       1044,        1,   518400, 0xbab197ea
+0,       1045,       1045,        1,   518400, 0xbab197ea
+0,       1046,       1046,        1,   518400, 0xbab197ea
+0,       1047,       1047,        1,   518400, 0xbab197ea
+0,       1048,       1048,        1,   518400, 0xbab197ea
+0,       1049,       1049,        1,   518400, 0xbab197ea
+0,       1050,       1050,        1,   518400, 0xbab197ea
+0,       1051,       1051,        1,   518400, 0xbab197ea
+0,       1052,       1052,        1,   518400, 0xbab197ea
 0,       1053,       1053,        1,   518400, 0x3de86ab7
 1,  210600000,  210600000,  1831000,      665, 0x55580135
-0,       1062,       1062,        1,   518400, 0xbab197ea
+0,       1054,       1054,        1,   518400, 0x3de86ab7
+0,       1055,       1055,        1,   518400, 0x3de86ab7
+0,       1056,       1056,        1,   518400, 0x3de86ab7
+0,       1057,       1057,        1,   518400, 0x3de86ab7
+0,       1058,       1058,        1,   518400, 0x3de86ab7
+0,       1059,       1059,        1,   518400, 0x3de86ab7
+0,       1060,       1060,        1,   518400, 0x3de86ab7
+0,       1061,       1061,        1,   518400, 0x3de86ab7
+0,       1062,       1062,        1,   518400, 0x3de86ab7
+0,       1063,       1063,        1,   518400, 0xbab197ea
+0,       1064,       1064,        1,   518400, 0xbab197ea
+0,       1065,       1065,        1,   518400, 0xbab197ea
+0,       1066,       1066,        1,   518400, 0xbab197ea
+0,       1067,       1067,        1,   518400, 0xbab197ea
+0,       1068,       1068,        1,   518400, 0xbab197ea
+0,       1069,       1069,        1,   518400, 0xbab197ea
+0,       1070,       1070,        1,   518400, 0xbab197ea
+0,       1071,       1071,        1,   518400, 0xbab197ea
+0,       1072,       1072,        1,   518400, 0xbab197ea
+0,       1073,       1073,        1,   518400, 0xbab197ea
 1,  214771000,  214771000,  1558000,     1216, 0x50d1f6c5
 0,       1074,       1074,        1,   518400, 0x8c320e68
-0,       1082,       1082,        1,   518400, 0xbab197ea
+0,       1075,       1075,        1,   518400, 0x8c320e68
+0,       1076,       1076,        1,   518400, 0x8c320e68
+0,       1077,       1077,        1,   518400, 0x8c320e68
+0,       1078,       1078,        1,   518400, 0x8c320e68
+0,       1079,       1079,        1,   518400, 0x8c320e68
+0,       1080,       1080,        1,   518400, 0x8c320e68
+0,       1081,       1081,        1,   518400, 0x8c320e68
+0,       1082,       1082,        1,   518400, 0x8c320e68
+0,       1083,       1083,        1,   518400, 0xbab197ea
+0,       1084,       1084,        1,   518400, 0xbab197ea
+0,       1085,       1085,        1,   518400, 0xbab197ea
+0,       1086,       1086,        1,   518400, 0xbab197ea
+0,       1087,       1087,        1,   518400, 0xbab197ea
+0,       1088,       1088,        1,   518400, 0xbab197ea
+0,       1089,       1089,        1,   518400, 0xbab197ea
+0,       1090,       1090,        1,   518400, 0xbab197ea
+0,       1091,       1091,        1,   518400, 0xbab197ea
+0,       1092,       1092,        1,   518400, 0xbab197ea
+0,       1093,       1093,        1,   518400, 0xbab197ea
+0,       1094,       1094,        1,   518400, 0xbab197ea
+0,       1095,       1095,        1,   518400, 0xbab197ea
+0,       1096,       1096,        1,   518400, 0xbab197ea
+0,       1097,       1097,        1,   518400, 0xbab197ea
+0,       1098,       1098,        1,   518400, 0xbab197ea
+0,       1099,       1099,        1,   518400, 0xbab197ea
+0,       1100,       1100,        1,   518400, 0xbab197ea
+0,       1101,       1101,        1,   518400, 0xbab197ea
+0,       1102,       1102,        1,   518400, 0xbab197ea
+0,       1103,       1103,        1,   518400, 0xbab197ea
+0,       1104,       1104,        1,   518400, 0xbab197ea
+0,       1105,       1105,        1,   518400, 0xbab197ea
+0,       1106,       1106,        1,   518400, 0xbab197ea
+0,       1107,       1107,        1,   518400, 0xbab197ea
+0,       1108,       1108,        1,   518400, 0xbab197ea
+0,       1109,       1109,        1,   518400, 0xbab197ea
+0,       1110,       1110,        1,   518400, 0xbab197ea
+0,       1111,       1111,        1,   518400, 0xbab197ea
+0,       1112,       1112,        1,   518400, 0xbab197ea
+0,       1113,       1113,        1,   518400, 0xbab197ea
+0,       1114,       1114,        1,   518400, 0xbab197ea
+0,       1115,       1115,        1,   518400, 0xbab197ea
+0,       1116,       1116,        1,   518400, 0xbab197ea
+0,       1117,       1117,        1,   518400, 0xbab197ea
+0,       1118,       1118,        1,   518400, 0xbab197ea
+0,       1119,       1119,        1,   518400, 0xbab197ea
+0,       1120,       1120,        1,   518400, 0xbab197ea
+0,       1121,       1121,        1,   518400, 0xbab197ea
+0,       1122,       1122,        1,   518400, 0xbab197ea
+0,       1123,       1123,        1,   518400, 0xbab197ea
+0,       1124,       1124,        1,   518400, 0xbab197ea
+0,       1125,       1125,        1,   518400, 0xbab197ea
+0,       1126,       1126,        1,   518400, 0xbab197ea
+0,       1127,       1127,        1,   518400, 0xbab197ea
 0,       1128,       1128,        1,   518400, 0x81e977b2
 1,  225640000,  225640000,  2127000,     2133, 0x670c11a5
-0,       1139,       1139,        1,   518400, 0xbab197ea
+0,       1129,       1129,        1,   518400, 0x81e977b2
+0,       1130,       1130,        1,   518400, 0x81e977b2
+0,       1131,       1131,        1,   518400, 0x81e977b2
+0,       1132,       1132,        1,   518400, 0x81e977b2
+0,       1133,       1133,        1,   518400, 0x81e977b2
+0,       1134,       1134,        1,   518400, 0x81e977b2
+0,       1135,       1135,        1,   518400, 0x81e977b2
+0,       1136,       1136,        1,   518400, 0x81e977b2
+0,       1137,       1137,        1,   518400, 0x81e977b2
+0,       1138,       1138,        1,   518400, 0x81e977b2
+0,       1139,       1139,        1,   518400, 0xb046dd30
 1,  227834000,  227834000,  1262000,     1264, 0xc1d9fc57
 0,       1140,       1140,        1,   518400, 0xb046dd30
-0,       1145,       1145,        1,   518400, 0xbab197ea
diff --git a/tests/ref/fate/sub2video_basic b/tests/ref/fate/sub2video_basic
index 5f72e292c9..9bd4e8b114 100644
--- a/tests/ref/fate/sub2video_basic
+++ b/tests/ref/fate/sub2video_basic
@@ -1,95 +1,1149 @@
-#tb 0: 1/25
+#tb 0: 1/5
 #media_type 0: video
 #codec_id 0: rawvideo
 #dimensions 0: 720x480
-#sar 0: 0/1
-0,       3312,       3312,        1,  1382400, 0x00000000
-0,       3312,       3312,        1,  1382400, 0x8c93c2ba
-0,       3436,       3436,        1,  1382400, 0x00000000
-0,       3684,       3684,        1,  1382400, 0xb02e32ca
-0,       3802,       3802,        1,  1382400, 0x00000000
-0,       4520,       4520,        1,  1382400, 0x83b71116
-0,       4584,       4584,        1,  1382400, 0x00000000
-0,       4586,       4586,        1,  1382400, 0x85547fd1
-0,       4645,       4645,        1,  1382400, 0x00000000
-0,       4648,       4648,        1,  1382400, 0x00000000
-0,       4648,       4648,        1,  1382400, 0xb6a8f181
-0,       4715,       4715,        1,  1382400, 0x00000000
-0,       4717,       4717,        1,  1382400, 0xb64d1a2c
-0,       4748,       4748,        1,  1382400, 0x00000000
-0,       4750,       4750,        1,  1382400, 0x7b37ecf3
-0,       4792,       4792,        1,  1382400, 0x00000000
-0,       4993,       4993,        1,  1382400, 0xdc025bd1
-0,       5027,       5027,        1,  1382400, 0x00000000
-0,       5029,       5029,        1,  1382400, 0x688b294d
-0,       5068,       5068,        1,  1382400, 0x00000000
-0,       5070,       5070,        1,  1382400, 0xa2b33d1b
-0,       5117,       5117,        1,  1382400, 0x00000000
-0,       5119,       5119,        1,  1382400, 0xb3e525e3
-0,       5168,       5168,        1,  1382400, 0x00000000
-0,       5170,       5170,        1,  1382400, 0xaa8fbdd7
-0,       5216,       5216,        1,  1382400, 0x00000000
-0,       5218,       5218,        1,  1382400, 0x7b7f26dd
-0,       5249,       5249,        1,  1382400, 0x00000000
-0,       5251,       5251,        1,  1382400, 0x15e2f836
-0,       5289,       5289,        1,  1382400, 0x00000000
-0,       5291,       5291,        1,  1382400, 0x0fee9b0c
-0,       5358,       5358,        1,  1382400, 0x00000000
-0,       5360,       5360,        1,  1382400, 0x89d62791
-0,       5429,       5429,        1,  1382400, 0x00000000
-0,       5431,       5431,        1,  1382400, 0xa6a9fd74
-0,       5490,       5490,        1,  1382400, 0x00000000
-0,       5491,       5491,        1,  1382400, 0x7896178d
-0,       5537,       5537,        1,  1382400, 0x00000000
-0,       5588,       5588,        1,  1382400, 0x01751a52
-0,       5647,       5647,        1,  1382400, 0x00000000
-0,       5688,       5688,        1,  1382400, 0xa3959c6f
-0,       5770,       5770,        1,  1382400, 0x00000000
-0,       5772,       5772,        1,  1382400, 0x3d3ea47b
-0,       5826,       5826,        1,  1382400, 0x00000000
-0,       5828,       5828,        1,  1382400, 0x593f8b24
-0,       5931,       5931,        1,  1382400, 0x00000000
-0,       5933,       5933,        1,  1382400, 0x171f05ba
-0,       6001,       6001,        1,  1382400, 0x00000000
-0,       6003,       6003,        1,  1382400, 0xb014cdf1
-0,       6054,       6054,        1,  1382400, 0x00000000
-0,       6839,       6839,        1,  1382400, 0xd918e667
-0,       6880,       6880,        1,  1382400, 0x00000000
-0,       7386,       7386,        1,  1382400, 0xc9406331
-0,       7419,       7419,        1,  1382400, 0x00000000
-0,       7501,       7501,        1,  1382400, 0xaf08b10d
-0,       7549,       7549,        1,  1382400, 0x00000000
-0,       7551,       7551,        1,  1382400, 0x00000000
-0,       7551,       7551,        1,  1382400, 0x853a9d93
-0,       7589,       7589,        1,  1382400, 0x00000000
-0,       7605,       7605,        1,  1382400, 0x7491a87d
-0,       7647,       7647,        1,  1382400, 0x00000000
-0,       7649,       7649,        1,  1382400, 0xf7383c58
-0,       7697,       7697,        1,  1382400, 0x00000000
-0,       7699,       7699,        1,  1382400, 0xe66be411
-0,       7743,       7743,        1,  1382400, 0x00000000
-0,       8032,       8032,        1,  1382400, 0xd6850362
-0,       8082,       8082,        1,  1382400, 0x00000000
-0,       8084,       8084,        1,  1382400, 0x3e1ed109
-0,       8115,       8115,        1,  1382400, 0x00000000
-0,       8116,       8116,        1,  1382400, 0x39c1b7bd
-0,       8160,       8160,        1,  1382400, 0x00000000
-0,       8180,       8180,        1,  1382400, 0x35b85f2e
-0,       8207,       8207,        1,  1382400, 0x00000000
-0,       8209,       8209,        1,  1382400, 0x00000000
-0,       8209,       8209,        1,  1382400, 0x83f103e5
-0,       8247,       8247,        1,  1382400, 0x00000000
-0,       8249,       8249,        1,  1382400, 0xbc1ca9b3
-0,       8278,       8278,        1,  1382400, 0x00000000
-0,       8281,       8281,        1,  1382400, 0x94d4a51e
-0,       8321,       8321,        1,  1382400, 0x00000000
-0,       8323,       8323,        1,  1382400, 0xf88cdfde
-0,       8367,       8367,        1,  1382400, 0x00000000
-0,       8565,       8565,        1,  1382400, 0xdd51423b
-0,       8611,       8611,        1,  1382400, 0x00000000
-0,       8669,       8669,        1,  1382400, 0x08259fa4
-0,       8708,       8708,        1,  1382400, 0x00000000
-0,       8941,       8941,        1,  1382400, 0x1663fa34
-0,       8994,       8994,        1,  1382400, 0x00000000
-0,       8996,       8996,        1,  1382400, 0xda2ceb55
-0,       9027,       9027,        1,  1382400, 0x00000000
+#sar 0: 1/1
+0,        662,        662,        1,  1382400, 0xc637b893
+0,        663,        663,        1,  1382400, 0xc637b893
+0,        664,        664,        1,  1382400, 0xc637b893
+0,        665,        665,        1,  1382400, 0xc637b893
+0,        666,        666,        1,  1382400, 0xc637b893
+0,        667,        667,        1,  1382400, 0xc637b893
+0,        668,        668,        1,  1382400, 0xc637b893
+0,        669,        669,        1,  1382400, 0xc637b893
+0,        670,        670,        1,  1382400, 0xc637b893
+0,        671,        671,        1,  1382400, 0xc637b893
+0,        672,        672,        1,  1382400, 0xc637b893
+0,        673,        673,        1,  1382400, 0xc637b893
+0,        674,        674,        1,  1382400, 0xc637b893
+0,        675,        675,        1,  1382400, 0xc637b893
+0,        676,        676,        1,  1382400, 0xc637b893
+0,        677,        677,        1,  1382400, 0xc637b893
+0,        678,        678,        1,  1382400, 0xc637b893
+0,        679,        679,        1,  1382400, 0xc637b893
+0,        680,        680,        1,  1382400, 0xc637b893
+0,        681,        681,        1,  1382400, 0xc637b893
+0,        682,        682,        1,  1382400, 0xc637b893
+0,        683,        683,        1,  1382400, 0xc637b893
+0,        684,        684,        1,  1382400, 0xc637b893
+0,        685,        685,        1,  1382400, 0xc637b893
+0,        686,        686,        1,  1382400, 0xc637b893
+0,        687,        687,        1,  1382400, 0x00000000
+0,        688,        688,        1,  1382400, 0x00000000
+0,        689,        689,        1,  1382400, 0x00000000
+0,        690,        690,        1,  1382400, 0x00000000
+0,        691,        691,        1,  1382400, 0x00000000
+0,        692,        692,        1,  1382400, 0x00000000
+0,        693,        693,        1,  1382400, 0x00000000
+0,        694,        694,        1,  1382400, 0x00000000
+0,        695,        695,        1,  1382400, 0x00000000
+0,        696,        696,        1,  1382400, 0x00000000
+0,        697,        697,        1,  1382400, 0x00000000
+0,        698,        698,        1,  1382400, 0x00000000
+0,        699,        699,        1,  1382400, 0x00000000
+0,        700,        700,        1,  1382400, 0x00000000
+0,        701,        701,        1,  1382400, 0x00000000
+0,        702,        702,        1,  1382400, 0x00000000
+0,        703,        703,        1,  1382400, 0x00000000
+0,        704,        704,        1,  1382400, 0x00000000
+0,        705,        705,        1,  1382400, 0x00000000
+0,        706,        706,        1,  1382400, 0x00000000
+0,        707,        707,        1,  1382400, 0x00000000
+0,        708,        708,        1,  1382400, 0x00000000
+0,        709,        709,        1,  1382400, 0x00000000
+0,        710,        710,        1,  1382400, 0x00000000
+0,        711,        711,        1,  1382400, 0x00000000
+0,        712,        712,        1,  1382400, 0x00000000
+0,        713,        713,        1,  1382400, 0x00000000
+0,        714,        714,        1,  1382400, 0x00000000
+0,        715,        715,        1,  1382400, 0x00000000
+0,        716,        716,        1,  1382400, 0x00000000
+0,        717,        717,        1,  1382400, 0x00000000
+0,        718,        718,        1,  1382400, 0x00000000
+0,        719,        719,        1,  1382400, 0x00000000
+0,        720,        720,        1,  1382400, 0x00000000
+0,        721,        721,        1,  1382400, 0x00000000
+0,        722,        722,        1,  1382400, 0x00000000
+0,        723,        723,        1,  1382400, 0x00000000
+0,        724,        724,        1,  1382400, 0x00000000
+0,        725,        725,        1,  1382400, 0x00000000
+0,        726,        726,        1,  1382400, 0x00000000
+0,        727,        727,        1,  1382400, 0x00000000
+0,        728,        728,        1,  1382400, 0x00000000
+0,        729,        729,        1,  1382400, 0x00000000
+0,        730,        730,        1,  1382400, 0x00000000
+0,        731,        731,        1,  1382400, 0x00000000
+0,        732,        732,        1,  1382400, 0x00000000
+0,        733,        733,        1,  1382400, 0x00000000
+0,        734,        734,        1,  1382400, 0x00000000
+0,        735,        735,        1,  1382400, 0x00000000
+0,        737,        737,        1,  1382400, 0x4c2960ca
+0,        737,        737,        1,  1382400, 0x4c2960ca
+0,        738,        738,        1,  1382400, 0x4c2960ca
+0,        739,        739,        1,  1382400, 0x4c2960ca
+0,        740,        740,        1,  1382400, 0x4c2960ca
+0,        741,        741,        1,  1382400, 0x4c2960ca
+0,        742,        742,        1,  1382400, 0x4c2960ca
+0,        743,        743,        1,  1382400, 0x4c2960ca
+0,        744,        744,        1,  1382400, 0x4c2960ca
+0,        745,        745,        1,  1382400, 0x4c2960ca
+0,        746,        746,        1,  1382400, 0x4c2960ca
+0,        747,        747,        1,  1382400, 0x4c2960ca
+0,        748,        748,        1,  1382400, 0x4c2960ca
+0,        749,        749,        1,  1382400, 0x4c2960ca
+0,        750,        750,        1,  1382400, 0x4c2960ca
+0,        751,        751,        1,  1382400, 0x4c2960ca
+0,        752,        752,        1,  1382400, 0x4c2960ca
+0,        753,        753,        1,  1382400, 0x4c2960ca
+0,        754,        754,        1,  1382400, 0x4c2960ca
+0,        755,        755,        1,  1382400, 0x4c2960ca
+0,        756,        756,        1,  1382400, 0x4c2960ca
+0,        757,        757,        1,  1382400, 0x4c2960ca
+0,        758,        758,        1,  1382400, 0x4c2960ca
+0,        759,        759,        1,  1382400, 0x4c2960ca
+0,        760,        760,        1,  1382400, 0x00000000
+0,        761,        761,        1,  1382400, 0x00000000
+0,        762,        762,        1,  1382400, 0x00000000
+0,        763,        763,        1,  1382400, 0x00000000
+0,        764,        764,        1,  1382400, 0x00000000
+0,        765,        765,        1,  1382400, 0x00000000
+0,        766,        766,        1,  1382400, 0x00000000
+0,        767,        767,        1,  1382400, 0x00000000
+0,        768,        768,        1,  1382400, 0x00000000
+0,        769,        769,        1,  1382400, 0x00000000
+0,        770,        770,        1,  1382400, 0x00000000
+0,        771,        771,        1,  1382400, 0x00000000
+0,        772,        772,        1,  1382400, 0x00000000
+0,        773,        773,        1,  1382400, 0x00000000
+0,        774,        774,        1,  1382400, 0x00000000
+0,        775,        775,        1,  1382400, 0x00000000
+0,        776,        776,        1,  1382400, 0x00000000
+0,        777,        777,        1,  1382400, 0x00000000
+0,        778,        778,        1,  1382400, 0x00000000
+0,        779,        779,        1,  1382400, 0x00000000
+0,        780,        780,        1,  1382400, 0x00000000
+0,        781,        781,        1,  1382400, 0x00000000
+0,        782,        782,        1,  1382400, 0x00000000
+0,        783,        783,        1,  1382400, 0x00000000
+0,        784,        784,        1,  1382400, 0x00000000
+0,        785,        785,        1,  1382400, 0x00000000
+0,        786,        786,        1,  1382400, 0x00000000
+0,        787,        787,        1,  1382400, 0x00000000
+0,        788,        788,        1,  1382400, 0x00000000
+0,        789,        789,        1,  1382400, 0x00000000
+0,        790,        790,        1,  1382400, 0x00000000
+0,        791,        791,        1,  1382400, 0x00000000
+0,        792,        792,        1,  1382400, 0x00000000
+0,        793,        793,        1,  1382400, 0x00000000
+0,        794,        794,        1,  1382400, 0x00000000
+0,        795,        795,        1,  1382400, 0x00000000
+0,        796,        796,        1,  1382400, 0x00000000
+0,        797,        797,        1,  1382400, 0x00000000
+0,        798,        798,        1,  1382400, 0x00000000
+0,        799,        799,        1,  1382400, 0x00000000
+0,        800,        800,        1,  1382400, 0x00000000
+0,        801,        801,        1,  1382400, 0x00000000
+0,        802,        802,        1,  1382400, 0x00000000
+0,        803,        803,        1,  1382400, 0x00000000
+0,        804,        804,        1,  1382400, 0x00000000
+0,        805,        805,        1,  1382400, 0x00000000
+0,        806,        806,        1,  1382400, 0x00000000
+0,        807,        807,        1,  1382400, 0x00000000
+0,        808,        808,        1,  1382400, 0x00000000
+0,        809,        809,        1,  1382400, 0x00000000
+0,        810,        810,        1,  1382400, 0x00000000
+0,        811,        811,        1,  1382400, 0x00000000
+0,        812,        812,        1,  1382400, 0x00000000
+0,        813,        813,        1,  1382400, 0x00000000
+0,        814,        814,        1,  1382400, 0x00000000
+0,        815,        815,        1,  1382400, 0x00000000
+0,        816,        816,        1,  1382400, 0x00000000
+0,        817,        817,        1,  1382400, 0x00000000
+0,        818,        818,        1,  1382400, 0x00000000
+0,        819,        819,        1,  1382400, 0x00000000
+0,        820,        820,        1,  1382400, 0x00000000
+0,        821,        821,        1,  1382400, 0x00000000
+0,        822,        822,        1,  1382400, 0x00000000
+0,        823,        823,        1,  1382400, 0x00000000
+0,        824,        824,        1,  1382400, 0x00000000
+0,        825,        825,        1,  1382400, 0x00000000
+0,        826,        826,        1,  1382400, 0x00000000
+0,        827,        827,        1,  1382400, 0x00000000
+0,        828,        828,        1,  1382400, 0x00000000
+0,        829,        829,        1,  1382400, 0x00000000
+0,        830,        830,        1,  1382400, 0x00000000
+0,        831,        831,        1,  1382400, 0x00000000
+0,        832,        832,        1,  1382400, 0x00000000
+0,        833,        833,        1,  1382400, 0x00000000
+0,        834,        834,        1,  1382400, 0x00000000
+0,        835,        835,        1,  1382400, 0x00000000
+0,        836,        836,        1,  1382400, 0x00000000
+0,        837,        837,        1,  1382400, 0x00000000
+0,        838,        838,        1,  1382400, 0x00000000
+0,        839,        839,        1,  1382400, 0x00000000
+0,        840,        840,        1,  1382400, 0x00000000
+0,        841,        841,        1,  1382400, 0x00000000
+0,        842,        842,        1,  1382400, 0x00000000
+0,        843,        843,        1,  1382400, 0x00000000
+0,        844,        844,        1,  1382400, 0x00000000
+0,        845,        845,        1,  1382400, 0x00000000
+0,        846,        846,        1,  1382400, 0x00000000
+0,        847,        847,        1,  1382400, 0x00000000
+0,        848,        848,        1,  1382400, 0x00000000
+0,        849,        849,        1,  1382400, 0x00000000
+0,        850,        850,        1,  1382400, 0x00000000
+0,        851,        851,        1,  1382400, 0x00000000
+0,        852,        852,        1,  1382400, 0x00000000
+0,        853,        853,        1,  1382400, 0x00000000
+0,        854,        854,        1,  1382400, 0x00000000
+0,        855,        855,        1,  1382400, 0x00000000
+0,        856,        856,        1,  1382400, 0x00000000
+0,        857,        857,        1,  1382400, 0x00000000
+0,        858,        858,        1,  1382400, 0x00000000
+0,        859,        859,        1,  1382400, 0x00000000
+0,        860,        860,        1,  1382400, 0x00000000
+0,        861,        861,        1,  1382400, 0x00000000
+0,        862,        862,        1,  1382400, 0x00000000
+0,        863,        863,        1,  1382400, 0x00000000
+0,        864,        864,        1,  1382400, 0x00000000
+0,        865,        865,        1,  1382400, 0x00000000
+0,        866,        866,        1,  1382400, 0x00000000
+0,        867,        867,        1,  1382400, 0x00000000
+0,        868,        868,        1,  1382400, 0x00000000
+0,        869,        869,        1,  1382400, 0x00000000
+0,        870,        870,        1,  1382400, 0x00000000
+0,        871,        871,        1,  1382400, 0x00000000
+0,        872,        872,        1,  1382400, 0x00000000
+0,        873,        873,        1,  1382400, 0x00000000
+0,        874,        874,        1,  1382400, 0x00000000
+0,        875,        875,        1,  1382400, 0x00000000
+0,        876,        876,        1,  1382400, 0x00000000
+0,        877,        877,        1,  1382400, 0x00000000
+0,        878,        878,        1,  1382400, 0x00000000
+0,        879,        879,        1,  1382400, 0x00000000
+0,        880,        880,        1,  1382400, 0x00000000
+0,        881,        881,        1,  1382400, 0x00000000
+0,        882,        882,        1,  1382400, 0x00000000
+0,        883,        883,        1,  1382400, 0x00000000
+0,        884,        884,        1,  1382400, 0x00000000
+0,        885,        885,        1,  1382400, 0x00000000
+0,        886,        886,        1,  1382400, 0x00000000
+0,        887,        887,        1,  1382400, 0x00000000
+0,        888,        888,        1,  1382400, 0x00000000
+0,        889,        889,        1,  1382400, 0x00000000
+0,        890,        890,        1,  1382400, 0x00000000
+0,        891,        891,        1,  1382400, 0x00000000
+0,        892,        892,        1,  1382400, 0x00000000
+0,        893,        893,        1,  1382400, 0x00000000
+0,        894,        894,        1,  1382400, 0x00000000
+0,        895,        895,        1,  1382400, 0x00000000
+0,        896,        896,        1,  1382400, 0x00000000
+0,        897,        897,        1,  1382400, 0x00000000
+0,        898,        898,        1,  1382400, 0x00000000
+0,        899,        899,        1,  1382400, 0x00000000
+0,        900,        900,        1,  1382400, 0x00000000
+0,        901,        901,        1,  1382400, 0x00000000
+0,        902,        902,        1,  1382400, 0x00000000
+0,        904,        904,        1,  1382400, 0x5fa18966
+0,        904,        904,        1,  1382400, 0x5fa18966
+0,        905,        905,        1,  1382400, 0x5fa18966
+0,        906,        906,        1,  1382400, 0x5fa18966
+0,        907,        907,        1,  1382400, 0x5fa18966
+0,        908,        908,        1,  1382400, 0x5fa18966
+0,        909,        909,        1,  1382400, 0x5fa18966
+0,        910,        910,        1,  1382400, 0x5fa18966
+0,        911,        911,        1,  1382400, 0x5fa18966
+0,        912,        912,        1,  1382400, 0x5fa18966
+0,        913,        913,        1,  1382400, 0x5fa18966
+0,        914,        914,        1,  1382400, 0x5fa18966
+0,        915,        915,        1,  1382400, 0x5fa18966
+0,        916,        916,        1,  1382400, 0x5fa18966
+0,        917,        917,        1,  1382400, 0x55f4b7b1
+0,        918,        918,        1,  1382400, 0x55f4b7b1
+0,        919,        919,        1,  1382400, 0x55f4b7b1
+0,        920,        920,        1,  1382400, 0x55f4b7b1
+0,        921,        921,        1,  1382400, 0x55f4b7b1
+0,        922,        922,        1,  1382400, 0x55f4b7b1
+0,        923,        923,        1,  1382400, 0x55f4b7b1
+0,        924,        924,        1,  1382400, 0x55f4b7b1
+0,        925,        925,        1,  1382400, 0x55f4b7b1
+0,        926,        926,        1,  1382400, 0x55f4b7b1
+0,        927,        927,        1,  1382400, 0x55f4b7b1
+0,        928,        928,        1,  1382400, 0x55f4b7b1
+0,        929,        929,        1,  1382400, 0x00000000
+0,        930,        930,        1,  1382400, 0xdfa4cf32
+0,        931,        931,        1,  1382400, 0xdfa4cf32
+0,        932,        932,        1,  1382400, 0xdfa4cf32
+0,        933,        933,        1,  1382400, 0xdfa4cf32
+0,        934,        934,        1,  1382400, 0xdfa4cf32
+0,        935,        935,        1,  1382400, 0xdfa4cf32
+0,        936,        936,        1,  1382400, 0xdfa4cf32
+0,        937,        937,        1,  1382400, 0xdfa4cf32
+0,        938,        938,        1,  1382400, 0xdfa4cf32
+0,        939,        939,        1,  1382400, 0xdfa4cf32
+0,        940,        940,        1,  1382400, 0xdfa4cf32
+0,        941,        941,        1,  1382400, 0xdfa4cf32
+0,        942,        942,        1,  1382400, 0xdfa4cf32
+0,        943,        943,        1,  1382400, 0x35023df8
+0,        944,        944,        1,  1382400, 0x35023df8
+0,        945,        945,        1,  1382400, 0x35023df8
+0,        946,        946,        1,  1382400, 0x35023df8
+0,        947,        947,        1,  1382400, 0x35023df8
+0,        948,        948,        1,  1382400, 0x35023df8
+0,        949,        949,        1,  1382400, 0x35023df8
+0,        950,        950,        1,  1382400, 0xed933219
+0,        951,        951,        1,  1382400, 0xed933219
+0,        952,        952,        1,  1382400, 0xed933219
+0,        953,        953,        1,  1382400, 0xed933219
+0,        954,        954,        1,  1382400, 0xed933219
+0,        955,        955,        1,  1382400, 0xed933219
+0,        956,        956,        1,  1382400, 0xed933219
+0,        957,        957,        1,  1382400, 0xed933219
+0,        958,        958,        1,  1382400, 0x00000000
+0,        959,        959,        1,  1382400, 0x00000000
+0,        960,        960,        1,  1382400, 0x00000000
+0,        961,        961,        1,  1382400, 0x00000000
+0,        962,        962,        1,  1382400, 0x00000000
+0,        963,        963,        1,  1382400, 0x00000000
+0,        964,        964,        1,  1382400, 0x00000000
+0,        965,        965,        1,  1382400, 0x00000000
+0,        966,        966,        1,  1382400, 0x00000000
+0,        967,        967,        1,  1382400, 0x00000000
+0,        968,        968,        1,  1382400, 0x00000000
+0,        969,        969,        1,  1382400, 0x00000000
+0,        970,        970,        1,  1382400, 0x00000000
+0,        971,        971,        1,  1382400, 0x00000000
+0,        972,        972,        1,  1382400, 0x00000000
+0,        973,        973,        1,  1382400, 0x00000000
+0,        974,        974,        1,  1382400, 0x00000000
+0,        975,        975,        1,  1382400, 0x00000000
+0,        976,        976,        1,  1382400, 0x00000000
+0,        977,        977,        1,  1382400, 0x00000000
+0,        978,        978,        1,  1382400, 0x00000000
+0,        979,        979,        1,  1382400, 0x00000000
+0,        980,        980,        1,  1382400, 0x00000000
+0,        981,        981,        1,  1382400, 0x00000000
+0,        982,        982,        1,  1382400, 0x00000000
+0,        983,        983,        1,  1382400, 0x00000000
+0,        984,        984,        1,  1382400, 0x00000000
+0,        985,        985,        1,  1382400, 0x00000000
+0,        986,        986,        1,  1382400, 0x00000000
+0,        987,        987,        1,  1382400, 0x00000000
+0,        988,        988,        1,  1382400, 0x00000000
+0,        989,        989,        1,  1382400, 0x00000000
+0,        990,        990,        1,  1382400, 0x00000000
+0,        991,        991,        1,  1382400, 0x00000000
+0,        992,        992,        1,  1382400, 0x00000000
+0,        993,        993,        1,  1382400, 0x00000000
+0,        994,        994,        1,  1382400, 0x00000000
+0,        995,        995,        1,  1382400, 0x00000000
+0,        996,        996,        1,  1382400, 0x00000000
+0,        997,        997,        1,  1382400, 0x00000000
+0,        999,        999,        1,  1382400, 0x1b26389a
+0,        999,        999,        1,  1382400, 0x1b26389a
+0,       1000,       1000,        1,  1382400, 0x1b26389a
+0,       1001,       1001,        1,  1382400, 0x1b26389a
+0,       1002,       1002,        1,  1382400, 0x1b26389a
+0,       1003,       1003,        1,  1382400, 0x1b26389a
+0,       1004,       1004,        1,  1382400, 0x1b26389a
+0,       1006,       1006,        1,  1382400, 0xf0c7028b
+0,       1006,       1006,        1,  1382400, 0xf0c7028b
+0,       1007,       1007,        1,  1382400, 0xf0c7028b
+0,       1008,       1008,        1,  1382400, 0xf0c7028b
+0,       1009,       1009,        1,  1382400, 0xf0c7028b
+0,       1010,       1010,        1,  1382400, 0xf0c7028b
+0,       1011,       1011,        1,  1382400, 0xf0c7028b
+0,       1012,       1012,        1,  1382400, 0xf0c7028b
+0,       1013,       1013,        1,  1382400, 0xf0c7028b
+0,       1014,       1014,        1,  1382400, 0x395f521d
+0,       1015,       1015,        1,  1382400, 0x395f521d
+0,       1016,       1016,        1,  1382400, 0x395f521d
+0,       1017,       1017,        1,  1382400, 0x395f521d
+0,       1018,       1018,        1,  1382400, 0x395f521d
+0,       1019,       1019,        1,  1382400, 0x395f521d
+0,       1020,       1020,        1,  1382400, 0x395f521d
+0,       1021,       1021,        1,  1382400, 0x395f521d
+0,       1022,       1022,        1,  1382400, 0x395f521d
+0,       1024,       1024,        1,  1382400, 0x1ea87415
+0,       1024,       1024,        1,  1382400, 0x1ea87415
+0,       1025,       1025,        1,  1382400, 0x1ea87415
+0,       1026,       1026,        1,  1382400, 0x1ea87415
+0,       1027,       1027,        1,  1382400, 0x1ea87415
+0,       1028,       1028,        1,  1382400, 0x1ea87415
+0,       1029,       1029,        1,  1382400, 0x1ea87415
+0,       1030,       1030,        1,  1382400, 0x1ea87415
+0,       1031,       1031,        1,  1382400, 0x1ea87415
+0,       1032,       1032,        1,  1382400, 0x1ea87415
+0,       1033,       1033,        1,  1382400, 0x1ea87415
+0,       1034,       1034,        1,  1382400, 0xc6effdc1
+0,       1035,       1035,        1,  1382400, 0xc6effdc1
+0,       1036,       1036,        1,  1382400, 0xc6effdc1
+0,       1037,       1037,        1,  1382400, 0xc6effdc1
+0,       1038,       1038,        1,  1382400, 0xc6effdc1
+0,       1039,       1039,        1,  1382400, 0xc6effdc1
+0,       1040,       1040,        1,  1382400, 0xc6effdc1
+0,       1041,       1041,        1,  1382400, 0xc6effdc1
+0,       1042,       1042,        1,  1382400, 0xc6effdc1
+0,       1044,       1044,        1,  1382400, 0xba6846f8
+0,       1044,       1044,        1,  1382400, 0xba6846f8
+0,       1045,       1045,        1,  1382400, 0xba6846f8
+0,       1046,       1046,        1,  1382400, 0xba6846f8
+0,       1047,       1047,        1,  1382400, 0xba6846f8
+0,       1048,       1048,        1,  1382400, 0xba6846f8
+0,       1049,       1049,        1,  1382400, 0xba6846f8
+0,       1050,       1050,        1,  1382400, 0x033c5d5b
+0,       1051,       1051,        1,  1382400, 0x033c5d5b
+0,       1052,       1052,        1,  1382400, 0x033c5d5b
+0,       1053,       1053,        1,  1382400, 0x033c5d5b
+0,       1054,       1054,        1,  1382400, 0x033c5d5b
+0,       1055,       1055,        1,  1382400, 0x033c5d5b
+0,       1056,       1056,        1,  1382400, 0x033c5d5b
+0,       1057,       1057,        1,  1382400, 0x033c5d5b
+0,       1058,       1058,        1,  1382400, 0x00000000
+0,       1059,       1059,        1,  1382400, 0xef5abf66
+0,       1060,       1060,        1,  1382400, 0xef5abf66
+0,       1061,       1061,        1,  1382400, 0xef5abf66
+0,       1062,       1062,        1,  1382400, 0xef5abf66
+0,       1063,       1063,        1,  1382400, 0xef5abf66
+0,       1064,       1064,        1,  1382400, 0xef5abf66
+0,       1065,       1065,        1,  1382400, 0xef5abf66
+0,       1066,       1066,        1,  1382400, 0xef5abf66
+0,       1067,       1067,        1,  1382400, 0xef5abf66
+0,       1068,       1068,        1,  1382400, 0xef5abf66
+0,       1069,       1069,        1,  1382400, 0xef5abf66
+0,       1070,       1070,        1,  1382400, 0xef5abf66
+0,       1071,       1071,        1,  1382400, 0xef5abf66
+0,       1072,       1072,        1,  1382400, 0x00000000
+0,       1073,       1073,        1,  1382400, 0xec747954
+0,       1074,       1074,        1,  1382400, 0xec747954
+0,       1075,       1075,        1,  1382400, 0xec747954
+0,       1076,       1076,        1,  1382400, 0xec747954
+0,       1077,       1077,        1,  1382400, 0xec747954
+0,       1078,       1078,        1,  1382400, 0xec747954
+0,       1079,       1079,        1,  1382400, 0xec747954
+0,       1080,       1080,        1,  1382400, 0xec747954
+0,       1081,       1081,        1,  1382400, 0xec747954
+0,       1082,       1082,        1,  1382400, 0xec747954
+0,       1083,       1083,        1,  1382400, 0xec747954
+0,       1084,       1084,        1,  1382400, 0xec747954
+0,       1085,       1085,        1,  1382400, 0xec747954
+0,       1086,       1086,        1,  1382400, 0x00000000
+0,       1087,       1087,        1,  1382400, 0xfa34bcaf
+0,       1088,       1088,        1,  1382400, 0xfa34bcaf
+0,       1089,       1089,        1,  1382400, 0xfa34bcaf
+0,       1090,       1090,        1,  1382400, 0xfa34bcaf
+0,       1091,       1091,        1,  1382400, 0xfa34bcaf
+0,       1092,       1092,        1,  1382400, 0xfa34bcaf
+0,       1093,       1093,        1,  1382400, 0xfa34bcaf
+0,       1094,       1094,        1,  1382400, 0xfa34bcaf
+0,       1095,       1095,        1,  1382400, 0xfa34bcaf
+0,       1096,       1096,        1,  1382400, 0xfa34bcaf
+0,       1097,       1097,        1,  1382400, 0xfa34bcaf
+0,       1098,       1098,        1,  1382400, 0x00000000
+0,       1099,       1099,        1,  1382400, 0x8b7a709b
+0,       1100,       1100,        1,  1382400, 0x8b7a709b
+0,       1101,       1101,        1,  1382400, 0x8b7a709b
+0,       1102,       1102,        1,  1382400, 0x8b7a709b
+0,       1103,       1103,        1,  1382400, 0x8b7a709b
+0,       1104,       1104,        1,  1382400, 0x8b7a709b
+0,       1105,       1105,        1,  1382400, 0x8b7a709b
+0,       1106,       1106,        1,  1382400, 0x8b7a709b
+0,       1107,       1107,        1,  1382400, 0x00000000
+0,       1108,       1108,        1,  1382400, 0x00000000
+0,       1109,       1109,        1,  1382400, 0x00000000
+0,       1110,       1110,        1,  1382400, 0x00000000
+0,       1111,       1111,        1,  1382400, 0x00000000
+0,       1112,       1112,        1,  1382400, 0x00000000
+0,       1113,       1113,        1,  1382400, 0x00000000
+0,       1114,       1114,        1,  1382400, 0x00000000
+0,       1115,       1115,        1,  1382400, 0x00000000
+0,       1116,       1116,        1,  1382400, 0x00000000
+0,       1118,       1118,        1,  1382400, 0xc333382f
+0,       1118,       1118,        1,  1382400, 0xc333382f
+0,       1119,       1119,        1,  1382400, 0xc333382f
+0,       1120,       1120,        1,  1382400, 0xc333382f
+0,       1121,       1121,        1,  1382400, 0xc333382f
+0,       1122,       1122,        1,  1382400, 0xc333382f
+0,       1123,       1123,        1,  1382400, 0xc333382f
+0,       1124,       1124,        1,  1382400, 0xc333382f
+0,       1125,       1125,        1,  1382400, 0xc333382f
+0,       1126,       1126,        1,  1382400, 0xc333382f
+0,       1127,       1127,        1,  1382400, 0xc333382f
+0,       1128,       1128,        1,  1382400, 0xc333382f
+0,       1129,       1129,        1,  1382400, 0x00000000
+0,       1130,       1130,        1,  1382400, 0x00000000
+0,       1131,       1131,        1,  1382400, 0x00000000
+0,       1132,       1132,        1,  1382400, 0x00000000
+0,       1133,       1133,        1,  1382400, 0x00000000
+0,       1134,       1134,        1,  1382400, 0x00000000
+0,       1135,       1135,        1,  1382400, 0x00000000
+0,       1136,       1136,        1,  1382400, 0x00000000
+0,       1138,       1138,        1,  1382400, 0xabe5dfcf
+0,       1138,       1138,        1,  1382400, 0xabe5dfcf
+0,       1139,       1139,        1,  1382400, 0xabe5dfcf
+0,       1140,       1140,        1,  1382400, 0xabe5dfcf
+0,       1141,       1141,        1,  1382400, 0xabe5dfcf
+0,       1142,       1142,        1,  1382400, 0xabe5dfcf
+0,       1143,       1143,        1,  1382400, 0xabe5dfcf
+0,       1144,       1144,        1,  1382400, 0xabe5dfcf
+0,       1145,       1145,        1,  1382400, 0xabe5dfcf
+0,       1146,       1146,        1,  1382400, 0xabe5dfcf
+0,       1147,       1147,        1,  1382400, 0xabe5dfcf
+0,       1148,       1148,        1,  1382400, 0xabe5dfcf
+0,       1149,       1149,        1,  1382400, 0xabe5dfcf
+0,       1150,       1150,        1,  1382400, 0xabe5dfcf
+0,       1151,       1151,        1,  1382400, 0xabe5dfcf
+0,       1152,       1152,        1,  1382400, 0xabe5dfcf
+0,       1153,       1153,        1,  1382400, 0xabe5dfcf
+0,       1154,       1154,        1,  1382400, 0x56948101
+0,       1155,       1155,        1,  1382400, 0x56948101
+0,       1156,       1156,        1,  1382400, 0x56948101
+0,       1157,       1157,        1,  1382400, 0x56948101
+0,       1158,       1158,        1,  1382400, 0x56948101
+0,       1159,       1159,        1,  1382400, 0x56948101
+0,       1160,       1160,        1,  1382400, 0x56948101
+0,       1161,       1161,        1,  1382400, 0x56948101
+0,       1162,       1162,        1,  1382400, 0x56948101
+0,       1163,       1163,        1,  1382400, 0x56948101
+0,       1164,       1164,        1,  1382400, 0x56948101
+0,       1166,       1166,        1,  1382400, 0xb747834a
+0,       1166,       1166,        1,  1382400, 0xb747834a
+0,       1167,       1167,        1,  1382400, 0xb747834a
+0,       1168,       1168,        1,  1382400, 0xb747834a
+0,       1169,       1169,        1,  1382400, 0xb747834a
+0,       1170,       1170,        1,  1382400, 0xb747834a
+0,       1171,       1171,        1,  1382400, 0xb747834a
+0,       1172,       1172,        1,  1382400, 0xb747834a
+0,       1173,       1173,        1,  1382400, 0xb747834a
+0,       1174,       1174,        1,  1382400, 0xb747834a
+0,       1175,       1175,        1,  1382400, 0xb747834a
+0,       1176,       1176,        1,  1382400, 0xb747834a
+0,       1177,       1177,        1,  1382400, 0xb747834a
+0,       1178,       1178,        1,  1382400, 0xb747834a
+0,       1179,       1179,        1,  1382400, 0xb747834a
+0,       1180,       1180,        1,  1382400, 0xb747834a
+0,       1181,       1181,        1,  1382400, 0xb747834a
+0,       1182,       1182,        1,  1382400, 0xb747834a
+0,       1183,       1183,        1,  1382400, 0xb747834a
+0,       1184,       1184,        1,  1382400, 0xb747834a
+0,       1185,       1185,        1,  1382400, 0xb747834a
+0,       1187,       1187,        1,  1382400, 0x3448baad
+0,       1187,       1187,        1,  1382400, 0x3448baad
+0,       1188,       1188,        1,  1382400, 0x3448baad
+0,       1189,       1189,        1,  1382400, 0x3448baad
+0,       1190,       1190,        1,  1382400, 0x3448baad
+0,       1191,       1191,        1,  1382400, 0x3448baad
+0,       1192,       1192,        1,  1382400, 0x3448baad
+0,       1193,       1193,        1,  1382400, 0x3448baad
+0,       1194,       1194,        1,  1382400, 0x3448baad
+0,       1195,       1195,        1,  1382400, 0x3448baad
+0,       1196,       1196,        1,  1382400, 0x3448baad
+0,       1197,       1197,        1,  1382400, 0x3448baad
+0,       1198,       1198,        1,  1382400, 0x3448baad
+0,       1199,       1199,        1,  1382400, 0x3448baad
+0,       1200,       1200,        1,  1382400, 0x00000000
+0,       1201,       1201,        1,  1382400, 0xaabe4f37
+0,       1202,       1202,        1,  1382400, 0xaabe4f37
+0,       1203,       1203,        1,  1382400, 0xaabe4f37
+0,       1204,       1204,        1,  1382400, 0xaabe4f37
+0,       1205,       1205,        1,  1382400, 0xaabe4f37
+0,       1206,       1206,        1,  1382400, 0xaabe4f37
+0,       1207,       1207,        1,  1382400, 0xaabe4f37
+0,       1208,       1208,        1,  1382400, 0xaabe4f37
+0,       1209,       1209,        1,  1382400, 0xaabe4f37
+0,       1210,       1210,        1,  1382400, 0xaabe4f37
+0,       1211,       1211,        1,  1382400, 0x00000000
+0,       1212,       1212,        1,  1382400, 0x00000000
+0,       1213,       1213,        1,  1382400, 0x00000000
+0,       1214,       1214,        1,  1382400, 0x00000000
+0,       1215,       1215,        1,  1382400, 0x00000000
+0,       1216,       1216,        1,  1382400, 0x00000000
+0,       1217,       1217,        1,  1382400, 0x00000000
+0,       1218,       1218,        1,  1382400, 0x00000000
+0,       1219,       1219,        1,  1382400, 0x00000000
+0,       1220,       1220,        1,  1382400, 0x00000000
+0,       1221,       1221,        1,  1382400, 0x00000000
+0,       1222,       1222,        1,  1382400, 0x00000000
+0,       1223,       1223,        1,  1382400, 0x00000000
+0,       1224,       1224,        1,  1382400, 0x00000000
+0,       1225,       1225,        1,  1382400, 0x00000000
+0,       1226,       1226,        1,  1382400, 0x00000000
+0,       1227,       1227,        1,  1382400, 0x00000000
+0,       1228,       1228,        1,  1382400, 0x00000000
+0,       1229,       1229,        1,  1382400, 0x00000000
+0,       1230,       1230,        1,  1382400, 0x00000000
+0,       1231,       1231,        1,  1382400, 0x00000000
+0,       1232,       1232,        1,  1382400, 0x00000000
+0,       1233,       1233,        1,  1382400, 0x00000000
+0,       1234,       1234,        1,  1382400, 0x00000000
+0,       1235,       1235,        1,  1382400, 0x00000000
+0,       1236,       1236,        1,  1382400, 0x00000000
+0,       1237,       1237,        1,  1382400, 0x00000000
+0,       1238,       1238,        1,  1382400, 0x00000000
+0,       1239,       1239,        1,  1382400, 0x00000000
+0,       1240,       1240,        1,  1382400, 0x00000000
+0,       1241,       1241,        1,  1382400, 0x00000000
+0,       1242,       1242,        1,  1382400, 0x00000000
+0,       1243,       1243,        1,  1382400, 0x00000000
+0,       1244,       1244,        1,  1382400, 0x00000000
+0,       1245,       1245,        1,  1382400, 0x00000000
+0,       1246,       1246,        1,  1382400, 0x00000000
+0,       1247,       1247,        1,  1382400, 0x00000000
+0,       1248,       1248,        1,  1382400, 0x00000000
+0,       1249,       1249,        1,  1382400, 0x00000000
+0,       1250,       1250,        1,  1382400, 0x00000000
+0,       1251,       1251,        1,  1382400, 0x00000000
+0,       1252,       1252,        1,  1382400, 0x00000000
+0,       1253,       1253,        1,  1382400, 0x00000000
+0,       1254,       1254,        1,  1382400, 0x00000000
+0,       1255,       1255,        1,  1382400, 0x00000000
+0,       1256,       1256,        1,  1382400, 0x00000000
+0,       1257,       1257,        1,  1382400, 0x00000000
+0,       1258,       1258,        1,  1382400, 0x00000000
+0,       1259,       1259,        1,  1382400, 0x00000000
+0,       1260,       1260,        1,  1382400, 0x00000000
+0,       1261,       1261,        1,  1382400, 0x00000000
+0,       1262,       1262,        1,  1382400, 0x00000000
+0,       1263,       1263,        1,  1382400, 0x00000000
+0,       1264,       1264,        1,  1382400, 0x00000000
+0,       1265,       1265,        1,  1382400, 0x00000000
+0,       1266,       1266,        1,  1382400, 0x00000000
+0,       1267,       1267,        1,  1382400, 0x00000000
+0,       1268,       1268,        1,  1382400, 0x00000000
+0,       1269,       1269,        1,  1382400, 0x00000000
+0,       1270,       1270,        1,  1382400, 0x00000000
+0,       1271,       1271,        1,  1382400, 0x00000000
+0,       1272,       1272,        1,  1382400, 0x00000000
+0,       1273,       1273,        1,  1382400, 0x00000000
+0,       1274,       1274,        1,  1382400, 0x00000000
+0,       1275,       1275,        1,  1382400, 0x00000000
+0,       1276,       1276,        1,  1382400, 0x00000000
+0,       1277,       1277,        1,  1382400, 0x00000000
+0,       1278,       1278,        1,  1382400, 0x00000000
+0,       1279,       1279,        1,  1382400, 0x00000000
+0,       1280,       1280,        1,  1382400, 0x00000000
+0,       1281,       1281,        1,  1382400, 0x00000000
+0,       1282,       1282,        1,  1382400, 0x00000000
+0,       1283,       1283,        1,  1382400, 0x00000000
+0,       1284,       1284,        1,  1382400, 0x00000000
+0,       1285,       1285,        1,  1382400, 0x00000000
+0,       1286,       1286,        1,  1382400, 0x00000000
+0,       1287,       1287,        1,  1382400, 0x00000000
+0,       1288,       1288,        1,  1382400, 0x00000000
+0,       1289,       1289,        1,  1382400, 0x00000000
+0,       1290,       1290,        1,  1382400, 0x00000000
+0,       1291,       1291,        1,  1382400, 0x00000000
+0,       1292,       1292,        1,  1382400, 0x00000000
+0,       1293,       1293,        1,  1382400, 0x00000000
+0,       1294,       1294,        1,  1382400, 0x00000000
+0,       1295,       1295,        1,  1382400, 0x00000000
+0,       1296,       1296,        1,  1382400, 0x00000000
+0,       1297,       1297,        1,  1382400, 0x00000000
+0,       1298,       1298,        1,  1382400, 0x00000000
+0,       1299,       1299,        1,  1382400, 0x00000000
+0,       1300,       1300,        1,  1382400, 0x00000000
+0,       1301,       1301,        1,  1382400, 0x00000000
+0,       1302,       1302,        1,  1382400, 0x00000000
+0,       1303,       1303,        1,  1382400, 0x00000000
+0,       1304,       1304,        1,  1382400, 0x00000000
+0,       1305,       1305,        1,  1382400, 0x00000000
+0,       1306,       1306,        1,  1382400, 0x00000000
+0,       1307,       1307,        1,  1382400, 0x00000000
+0,       1308,       1308,        1,  1382400, 0x00000000
+0,       1309,       1309,        1,  1382400, 0x00000000
+0,       1310,       1310,        1,  1382400, 0x00000000
+0,       1311,       1311,        1,  1382400, 0x00000000
+0,       1312,       1312,        1,  1382400, 0x00000000
+0,       1313,       1313,        1,  1382400, 0x00000000
+0,       1314,       1314,        1,  1382400, 0x00000000
+0,       1315,       1315,        1,  1382400, 0x00000000
+0,       1316,       1316,        1,  1382400, 0x00000000
+0,       1317,       1317,        1,  1382400, 0x00000000
+0,       1318,       1318,        1,  1382400, 0x00000000
+0,       1319,       1319,        1,  1382400, 0x00000000
+0,       1320,       1320,        1,  1382400, 0x00000000
+0,       1321,       1321,        1,  1382400, 0x00000000
+0,       1322,       1322,        1,  1382400, 0x00000000
+0,       1323,       1323,        1,  1382400, 0x00000000
+0,       1324,       1324,        1,  1382400, 0x00000000
+0,       1325,       1325,        1,  1382400, 0x00000000
+0,       1326,       1326,        1,  1382400, 0x00000000
+0,       1327,       1327,        1,  1382400, 0x00000000
+0,       1328,       1328,        1,  1382400, 0x00000000
+0,       1329,       1329,        1,  1382400, 0x00000000
+0,       1330,       1330,        1,  1382400, 0x00000000
+0,       1331,       1331,        1,  1382400, 0x00000000
+0,       1332,       1332,        1,  1382400, 0x00000000
+0,       1333,       1333,        1,  1382400, 0x00000000
+0,       1334,       1334,        1,  1382400, 0x00000000
+0,       1335,       1335,        1,  1382400, 0x00000000
+0,       1336,       1336,        1,  1382400, 0x00000000
+0,       1337,       1337,        1,  1382400, 0x00000000
+0,       1338,       1338,        1,  1382400, 0x00000000
+0,       1339,       1339,        1,  1382400, 0x00000000
+0,       1340,       1340,        1,  1382400, 0x00000000
+0,       1341,       1341,        1,  1382400, 0x00000000
+0,       1342,       1342,        1,  1382400, 0x00000000
+0,       1343,       1343,        1,  1382400, 0x00000000
+0,       1344,       1344,        1,  1382400, 0x00000000
+0,       1345,       1345,        1,  1382400, 0x00000000
+0,       1346,       1346,        1,  1382400, 0x00000000
+0,       1347,       1347,        1,  1382400, 0x00000000
+0,       1348,       1348,        1,  1382400, 0x00000000
+0,       1349,       1349,        1,  1382400, 0x00000000
+0,       1350,       1350,        1,  1382400, 0x00000000
+0,       1351,       1351,        1,  1382400, 0x00000000
+0,       1352,       1352,        1,  1382400, 0x00000000
+0,       1353,       1353,        1,  1382400, 0x00000000
+0,       1354,       1354,        1,  1382400, 0x00000000
+0,       1355,       1355,        1,  1382400, 0x00000000
+0,       1356,       1356,        1,  1382400, 0x00000000
+0,       1357,       1357,        1,  1382400, 0x00000000
+0,       1358,       1358,        1,  1382400, 0x00000000
+0,       1359,       1359,        1,  1382400, 0x00000000
+0,       1360,       1360,        1,  1382400, 0x00000000
+0,       1361,       1361,        1,  1382400, 0x00000000
+0,       1362,       1362,        1,  1382400, 0x00000000
+0,       1363,       1363,        1,  1382400, 0x00000000
+0,       1364,       1364,        1,  1382400, 0x00000000
+0,       1365,       1365,        1,  1382400, 0x00000000
+0,       1366,       1366,        1,  1382400, 0x00000000
+0,       1368,       1368,        1,  1382400, 0x8a48cd6f
+0,       1368,       1368,        1,  1382400, 0x8a48cd6f
+0,       1369,       1369,        1,  1382400, 0x8a48cd6f
+0,       1370,       1370,        1,  1382400, 0x8a48cd6f
+0,       1371,       1371,        1,  1382400, 0x8a48cd6f
+0,       1372,       1372,        1,  1382400, 0x8a48cd6f
+0,       1373,       1373,        1,  1382400, 0x8a48cd6f
+0,       1374,       1374,        1,  1382400, 0x8a48cd6f
+0,       1375,       1375,        1,  1382400, 0x8a48cd6f
+0,       1376,       1376,        1,  1382400, 0x00000000
+0,       1377,       1377,        1,  1382400, 0x00000000
+0,       1378,       1378,        1,  1382400, 0x00000000
+0,       1379,       1379,        1,  1382400, 0x00000000
+0,       1380,       1380,        1,  1382400, 0x00000000
+0,       1381,       1381,        1,  1382400, 0x00000000
+0,       1382,       1382,        1,  1382400, 0x00000000
+0,       1383,       1383,        1,  1382400, 0x00000000
+0,       1384,       1384,        1,  1382400, 0x00000000
+0,       1385,       1385,        1,  1382400, 0x00000000
+0,       1386,       1386,        1,  1382400, 0x00000000
+0,       1387,       1387,        1,  1382400, 0x00000000
+0,       1388,       1388,        1,  1382400, 0x00000000
+0,       1389,       1389,        1,  1382400, 0x00000000
+0,       1390,       1390,        1,  1382400, 0x00000000
+0,       1391,       1391,        1,  1382400, 0x00000000
+0,       1392,       1392,        1,  1382400, 0x00000000
+0,       1393,       1393,        1,  1382400, 0x00000000
+0,       1394,       1394,        1,  1382400, 0x00000000
+0,       1395,       1395,        1,  1382400, 0x00000000
+0,       1396,       1396,        1,  1382400, 0x00000000
+0,       1397,       1397,        1,  1382400, 0x00000000
+0,       1398,       1398,        1,  1382400, 0x00000000
+0,       1399,       1399,        1,  1382400, 0x00000000
+0,       1400,       1400,        1,  1382400, 0x00000000
+0,       1401,       1401,        1,  1382400, 0x00000000
+0,       1402,       1402,        1,  1382400, 0x00000000
+0,       1403,       1403,        1,  1382400, 0x00000000
+0,       1404,       1404,        1,  1382400, 0x00000000
+0,       1405,       1405,        1,  1382400, 0x00000000
+0,       1406,       1406,        1,  1382400, 0x00000000
+0,       1407,       1407,        1,  1382400, 0x00000000
+0,       1408,       1408,        1,  1382400, 0x00000000
+0,       1409,       1409,        1,  1382400, 0x00000000
+0,       1410,       1410,        1,  1382400, 0x00000000
+0,       1411,       1411,        1,  1382400, 0x00000000
+0,       1412,       1412,        1,  1382400, 0x00000000
+0,       1413,       1413,        1,  1382400, 0x00000000
+0,       1414,       1414,        1,  1382400, 0x00000000
+0,       1415,       1415,        1,  1382400, 0x00000000
+0,       1416,       1416,        1,  1382400, 0x00000000
+0,       1417,       1417,        1,  1382400, 0x00000000
+0,       1418,       1418,        1,  1382400, 0x00000000
+0,       1419,       1419,        1,  1382400, 0x00000000
+0,       1420,       1420,        1,  1382400, 0x00000000
+0,       1421,       1421,        1,  1382400, 0x00000000
+0,       1422,       1422,        1,  1382400, 0x00000000
+0,       1423,       1423,        1,  1382400, 0x00000000
+0,       1424,       1424,        1,  1382400, 0x00000000
+0,       1425,       1425,        1,  1382400, 0x00000000
+0,       1426,       1426,        1,  1382400, 0x00000000
+0,       1427,       1427,        1,  1382400, 0x00000000
+0,       1428,       1428,        1,  1382400, 0x00000000
+0,       1429,       1429,        1,  1382400, 0x00000000
+0,       1430,       1430,        1,  1382400, 0x00000000
+0,       1431,       1431,        1,  1382400, 0x00000000
+0,       1432,       1432,        1,  1382400, 0x00000000
+0,       1433,       1433,        1,  1382400, 0x00000000
+0,       1434,       1434,        1,  1382400, 0x00000000
+0,       1435,       1435,        1,  1382400, 0x00000000
+0,       1436,       1436,        1,  1382400, 0x00000000
+0,       1437,       1437,        1,  1382400, 0x00000000
+0,       1438,       1438,        1,  1382400, 0x00000000
+0,       1439,       1439,        1,  1382400, 0x00000000
+0,       1440,       1440,        1,  1382400, 0x00000000
+0,       1441,       1441,        1,  1382400, 0x00000000
+0,       1442,       1442,        1,  1382400, 0x00000000
+0,       1443,       1443,        1,  1382400, 0x00000000
+0,       1444,       1444,        1,  1382400, 0x00000000
+0,       1445,       1445,        1,  1382400, 0x00000000
+0,       1446,       1446,        1,  1382400, 0x00000000
+0,       1447,       1447,        1,  1382400, 0x00000000
+0,       1448,       1448,        1,  1382400, 0x00000000
+0,       1449,       1449,        1,  1382400, 0x00000000
+0,       1450,       1450,        1,  1382400, 0x00000000
+0,       1451,       1451,        1,  1382400, 0x00000000
+0,       1452,       1452,        1,  1382400, 0x00000000
+0,       1453,       1453,        1,  1382400, 0x00000000
+0,       1454,       1454,        1,  1382400, 0x00000000
+0,       1455,       1455,        1,  1382400, 0x00000000
+0,       1456,       1456,        1,  1382400, 0x00000000
+0,       1457,       1457,        1,  1382400, 0x00000000
+0,       1458,       1458,        1,  1382400, 0x00000000
+0,       1459,       1459,        1,  1382400, 0x00000000
+0,       1460,       1460,        1,  1382400, 0x00000000
+0,       1461,       1461,        1,  1382400, 0x00000000
+0,       1462,       1462,        1,  1382400, 0x00000000
+0,       1463,       1463,        1,  1382400, 0x00000000
+0,       1464,       1464,        1,  1382400, 0x00000000
+0,       1465,       1465,        1,  1382400, 0x00000000
+0,       1466,       1466,        1,  1382400, 0x00000000
+0,       1467,       1467,        1,  1382400, 0x00000000
+0,       1468,       1468,        1,  1382400, 0x00000000
+0,       1469,       1469,        1,  1382400, 0x00000000
+0,       1470,       1470,        1,  1382400, 0x00000000
+0,       1471,       1471,        1,  1382400, 0x00000000
+0,       1472,       1472,        1,  1382400, 0x00000000
+0,       1473,       1473,        1,  1382400, 0x00000000
+0,       1474,       1474,        1,  1382400, 0x00000000
+0,       1475,       1475,        1,  1382400, 0x00000000
+0,       1477,       1477,        1,  1382400, 0x49518c43
+0,       1477,       1477,        1,  1382400, 0x49518c43
+0,       1478,       1478,        1,  1382400, 0x49518c43
+0,       1479,       1479,        1,  1382400, 0x49518c43
+0,       1480,       1480,        1,  1382400, 0x49518c43
+0,       1481,       1481,        1,  1382400, 0x49518c43
+0,       1482,       1482,        1,  1382400, 0x49518c43
+0,       1483,       1483,        1,  1382400, 0x49518c43
+0,       1484,       1484,        1,  1382400, 0x00000000
+0,       1485,       1485,        1,  1382400, 0x00000000
+0,       1486,       1486,        1,  1382400, 0x00000000
+0,       1487,       1487,        1,  1382400, 0x00000000
+0,       1488,       1488,        1,  1382400, 0x00000000
+0,       1489,       1489,        1,  1382400, 0x00000000
+0,       1490,       1490,        1,  1382400, 0x00000000
+0,       1491,       1491,        1,  1382400, 0x00000000
+0,       1492,       1492,        1,  1382400, 0x00000000
+0,       1493,       1493,        1,  1382400, 0x00000000
+0,       1494,       1494,        1,  1382400, 0x00000000
+0,       1495,       1495,        1,  1382400, 0x00000000
+0,       1496,       1496,        1,  1382400, 0x00000000
+0,       1497,       1497,        1,  1382400, 0x00000000
+0,       1498,       1498,        1,  1382400, 0x00000000
+0,       1500,       1500,        1,  1382400, 0x4a72fa21
+0,       1500,       1500,        1,  1382400, 0x4a72fa21
+0,       1501,       1501,        1,  1382400, 0x4a72fa21
+0,       1502,       1502,        1,  1382400, 0x4a72fa21
+0,       1503,       1503,        1,  1382400, 0x4a72fa21
+0,       1504,       1504,        1,  1382400, 0x4a72fa21
+0,       1505,       1505,        1,  1382400, 0x4a72fa21
+0,       1506,       1506,        1,  1382400, 0x4a72fa21
+0,       1507,       1507,        1,  1382400, 0x4a72fa21
+0,       1508,       1508,        1,  1382400, 0x4a72fa21
+0,       1509,       1509,        1,  1382400, 0x4a72fa21
+0,       1510,       1510,        1,  1382400, 0x00000000
+0,       1511,       1511,        1,  1382400, 0xa82f7de8
+0,       1512,       1512,        1,  1382400, 0xa82f7de8
+0,       1513,       1513,        1,  1382400, 0xa82f7de8
+0,       1514,       1514,        1,  1382400, 0xa82f7de8
+0,       1515,       1515,        1,  1382400, 0xa82f7de8
+0,       1516,       1516,        1,  1382400, 0xa82f7de8
+0,       1517,       1517,        1,  1382400, 0xa82f7de8
+0,       1518,       1518,        1,  1382400, 0x00000000
+0,       1519,       1519,        1,  1382400, 0x00000000
+0,       1521,       1521,        1,  1382400, 0xeba0b5f3
+0,       1521,       1521,        1,  1382400, 0xeba0b5f3
+0,       1522,       1522,        1,  1382400, 0xeba0b5f3
+0,       1523,       1523,        1,  1382400, 0xeba0b5f3
+0,       1524,       1524,        1,  1382400, 0xeba0b5f3
+0,       1525,       1525,        1,  1382400, 0xeba0b5f3
+0,       1526,       1526,        1,  1382400, 0xeba0b5f3
+0,       1527,       1527,        1,  1382400, 0xeba0b5f3
+0,       1528,       1528,        1,  1382400, 0xeba0b5f3
+0,       1530,       1530,        1,  1382400, 0xd6a91770
+0,       1530,       1530,        1,  1382400, 0xd6a91770
+0,       1531,       1531,        1,  1382400, 0xd6a91770
+0,       1532,       1532,        1,  1382400, 0xd6a91770
+0,       1533,       1533,        1,  1382400, 0xd6a91770
+0,       1534,       1534,        1,  1382400, 0xd6a91770
+0,       1535,       1535,        1,  1382400, 0xd6a91770
+0,       1536,       1536,        1,  1382400, 0xd6a91770
+0,       1537,       1537,        1,  1382400, 0xd6a91770
+0,       1538,       1538,        1,  1382400, 0xd6a91770
+0,       1539,       1539,        1,  1382400, 0x00000000
+0,       1540,       1540,        1,  1382400, 0x222f827c
+0,       1541,       1541,        1,  1382400, 0x222f827c
+0,       1542,       1542,        1,  1382400, 0x222f827c
+0,       1543,       1543,        1,  1382400, 0x222f827c
+0,       1544,       1544,        1,  1382400, 0x222f827c
+0,       1545,       1545,        1,  1382400, 0x222f827c
+0,       1546,       1546,        1,  1382400, 0x222f827c
+0,       1547,       1547,        1,  1382400, 0x222f827c
+0,       1548,       1548,        1,  1382400, 0x222f827c
+0,       1549,       1549,        1,  1382400, 0x00000000
+0,       1550,       1550,        1,  1382400, 0x00000000
+0,       1551,       1551,        1,  1382400, 0x00000000
+0,       1552,       1552,        1,  1382400, 0x00000000
+0,       1553,       1553,        1,  1382400, 0x00000000
+0,       1554,       1554,        1,  1382400, 0x00000000
+0,       1555,       1555,        1,  1382400, 0x00000000
+0,       1556,       1556,        1,  1382400, 0x00000000
+0,       1557,       1557,        1,  1382400, 0x00000000
+0,       1558,       1558,        1,  1382400, 0x00000000
+0,       1559,       1559,        1,  1382400, 0x00000000
+0,       1560,       1560,        1,  1382400, 0x00000000
+0,       1561,       1561,        1,  1382400, 0x00000000
+0,       1562,       1562,        1,  1382400, 0x00000000
+0,       1563,       1563,        1,  1382400, 0x00000000
+0,       1564,       1564,        1,  1382400, 0x00000000
+0,       1565,       1565,        1,  1382400, 0x00000000
+0,       1566,       1566,        1,  1382400, 0x00000000
+0,       1567,       1567,        1,  1382400, 0x00000000
+0,       1568,       1568,        1,  1382400, 0x00000000
+0,       1569,       1569,        1,  1382400, 0x00000000
+0,       1570,       1570,        1,  1382400, 0x00000000
+0,       1571,       1571,        1,  1382400, 0x00000000
+0,       1572,       1572,        1,  1382400, 0x00000000
+0,       1573,       1573,        1,  1382400, 0x00000000
+0,       1574,       1574,        1,  1382400, 0x00000000
+0,       1575,       1575,        1,  1382400, 0x00000000
+0,       1576,       1576,        1,  1382400, 0x00000000
+0,       1577,       1577,        1,  1382400, 0x00000000
+0,       1578,       1578,        1,  1382400, 0x00000000
+0,       1579,       1579,        1,  1382400, 0x00000000
+0,       1580,       1580,        1,  1382400, 0x00000000
+0,       1581,       1581,        1,  1382400, 0x00000000
+0,       1582,       1582,        1,  1382400, 0x00000000
+0,       1583,       1583,        1,  1382400, 0x00000000
+0,       1584,       1584,        1,  1382400, 0x00000000
+0,       1585,       1585,        1,  1382400, 0x00000000
+0,       1586,       1586,        1,  1382400, 0x00000000
+0,       1587,       1587,        1,  1382400, 0x00000000
+0,       1588,       1588,        1,  1382400, 0x00000000
+0,       1589,       1589,        1,  1382400, 0x00000000
+0,       1590,       1590,        1,  1382400, 0x00000000
+0,       1591,       1591,        1,  1382400, 0x00000000
+0,       1592,       1592,        1,  1382400, 0x00000000
+0,       1593,       1593,        1,  1382400, 0x00000000
+0,       1594,       1594,        1,  1382400, 0x00000000
+0,       1595,       1595,        1,  1382400, 0x00000000
+0,       1596,       1596,        1,  1382400, 0x00000000
+0,       1597,       1597,        1,  1382400, 0x00000000
+0,       1598,       1598,        1,  1382400, 0x00000000
+0,       1599,       1599,        1,  1382400, 0x00000000
+0,       1600,       1600,        1,  1382400, 0x00000000
+0,       1601,       1601,        1,  1382400, 0x00000000
+0,       1602,       1602,        1,  1382400, 0x00000000
+0,       1603,       1603,        1,  1382400, 0x00000000
+0,       1604,       1604,        1,  1382400, 0x00000000
+0,       1606,       1606,        1,  1382400, 0x3270f4ff
+0,       1606,       1606,        1,  1382400, 0x3270f4ff
+0,       1607,       1607,        1,  1382400, 0x3270f4ff
+0,       1608,       1608,        1,  1382400, 0x3270f4ff
+0,       1609,       1609,        1,  1382400, 0x3270f4ff
+0,       1610,       1610,        1,  1382400, 0x3270f4ff
+0,       1611,       1611,        1,  1382400, 0x3270f4ff
+0,       1612,       1612,        1,  1382400, 0x3270f4ff
+0,       1613,       1613,        1,  1382400, 0x3270f4ff
+0,       1614,       1614,        1,  1382400, 0x3270f4ff
+0,       1615,       1615,        1,  1382400, 0x3270f4ff
+0,       1617,       1617,        1,  1382400, 0x40813cb3
+0,       1617,       1617,        1,  1382400, 0x40813cb3
+0,       1618,       1618,        1,  1382400, 0x40813cb3
+0,       1619,       1619,        1,  1382400, 0x40813cb3
+0,       1620,       1620,        1,  1382400, 0x40813cb3
+0,       1621,       1621,        1,  1382400, 0x40813cb3
+0,       1622,       1622,        1,  1382400, 0x40813cb3
+0,       1623,       1623,        1,  1382400, 0x9d8fde41
+0,       1624,       1624,        1,  1382400, 0x9d8fde41
+0,       1625,       1625,        1,  1382400, 0x9d8fde41
+0,       1626,       1626,        1,  1382400, 0x9d8fde41
+0,       1627,       1627,        1,  1382400, 0x9d8fde41
+0,       1628,       1628,        1,  1382400, 0x9d8fde41
+0,       1629,       1629,        1,  1382400, 0x9d8fde41
+0,       1630,       1630,        1,  1382400, 0x9d8fde41
+0,       1631,       1631,        1,  1382400, 0x9d8fde41
+0,       1632,       1632,        1,  1382400, 0x00000000
+0,       1633,       1633,        1,  1382400, 0x00000000
+0,       1634,       1634,        1,  1382400, 0x00000000
+0,       1636,       1636,        1,  1382400, 0xc6d7a701
+0,       1636,       1636,        1,  1382400, 0xc6d7a701
+0,       1637,       1637,        1,  1382400, 0xc6d7a701
+0,       1638,       1638,        1,  1382400, 0xc6d7a701
+0,       1639,       1639,        1,  1382400, 0xc6d7a701
+0,       1640,       1640,        1,  1382400, 0xc6d7a701
+0,       1642,       1642,        1,  1382400, 0x9d45f2dc
+0,       1642,       1642,        1,  1382400, 0x9d45f2dc
+0,       1643,       1643,        1,  1382400, 0x9d45f2dc
+0,       1644,       1644,        1,  1382400, 0x9d45f2dc
+0,       1645,       1645,        1,  1382400, 0x9d45f2dc
+0,       1646,       1646,        1,  1382400, 0x9d45f2dc
+0,       1647,       1647,        1,  1382400, 0x9d45f2dc
+0,       1648,       1648,        1,  1382400, 0x9d45f2dc
+0,       1649,       1649,        1,  1382400, 0x00000000
+0,       1650,       1650,        1,  1382400, 0x8525ee40
+0,       1651,       1651,        1,  1382400, 0x8525ee40
+0,       1652,       1652,        1,  1382400, 0x8525ee40
+0,       1653,       1653,        1,  1382400, 0x8525ee40
+0,       1654,       1654,        1,  1382400, 0x8525ee40
+0,       1655,       1655,        1,  1382400, 0x8525ee40
+0,       1656,       1656,        1,  1382400, 0x5b26b98b
+0,       1657,       1657,        1,  1382400, 0x5b26b98b
+0,       1658,       1658,        1,  1382400, 0x5b26b98b
+0,       1659,       1659,        1,  1382400, 0x5b26b98b
+0,       1660,       1660,        1,  1382400, 0x5b26b98b
+0,       1661,       1661,        1,  1382400, 0x5b26b98b
+0,       1662,       1662,        1,  1382400, 0x5b26b98b
+0,       1663,       1663,        1,  1382400, 0x5b26b98b
+0,       1664,       1664,        1,  1382400, 0x00000000
+0,       1665,       1665,        1,  1382400, 0x51be311f
+0,       1666,       1666,        1,  1382400, 0x51be311f
+0,       1667,       1667,        1,  1382400, 0x51be311f
+0,       1668,       1668,        1,  1382400, 0x51be311f
+0,       1669,       1669,        1,  1382400, 0x51be311f
+0,       1670,       1670,        1,  1382400, 0x51be311f
+0,       1671,       1671,        1,  1382400, 0x51be311f
+0,       1672,       1672,        1,  1382400, 0x51be311f
+0,       1673,       1673,        1,  1382400, 0x00000000
+0,       1674,       1674,        1,  1382400, 0x00000000
+0,       1675,       1675,        1,  1382400, 0x00000000
+0,       1676,       1676,        1,  1382400, 0x00000000
+0,       1677,       1677,        1,  1382400, 0x00000000
+0,       1678,       1678,        1,  1382400, 0x00000000
+0,       1679,       1679,        1,  1382400, 0x00000000
+0,       1680,       1680,        1,  1382400, 0x00000000
+0,       1681,       1681,        1,  1382400, 0x00000000
+0,       1682,       1682,        1,  1382400, 0x00000000
+0,       1683,       1683,        1,  1382400, 0x00000000
+0,       1684,       1684,        1,  1382400, 0x00000000
+0,       1685,       1685,        1,  1382400, 0x00000000
+0,       1686,       1686,        1,  1382400, 0x00000000
+0,       1687,       1687,        1,  1382400, 0x00000000
+0,       1688,       1688,        1,  1382400, 0x00000000
+0,       1689,       1689,        1,  1382400, 0x00000000
+0,       1690,       1690,        1,  1382400, 0x00000000
+0,       1691,       1691,        1,  1382400, 0x00000000
+0,       1692,       1692,        1,  1382400, 0x00000000
+0,       1693,       1693,        1,  1382400, 0x00000000
+0,       1694,       1694,        1,  1382400, 0x00000000
+0,       1695,       1695,        1,  1382400, 0x00000000
+0,       1696,       1696,        1,  1382400, 0x00000000
+0,       1697,       1697,        1,  1382400, 0x00000000
+0,       1698,       1698,        1,  1382400, 0x00000000
+0,       1699,       1699,        1,  1382400, 0x00000000
+0,       1700,       1700,        1,  1382400, 0x00000000
+0,       1701,       1701,        1,  1382400, 0x00000000
+0,       1702,       1702,        1,  1382400, 0x00000000
+0,       1703,       1703,        1,  1382400, 0x00000000
+0,       1704,       1704,        1,  1382400, 0x00000000
+0,       1705,       1705,        1,  1382400, 0x00000000
+0,       1706,       1706,        1,  1382400, 0x00000000
+0,       1707,       1707,        1,  1382400, 0x00000000
+0,       1708,       1708,        1,  1382400, 0x00000000
+0,       1709,       1709,        1,  1382400, 0x00000000
+0,       1710,       1710,        1,  1382400, 0x00000000
+0,       1711,       1711,        1,  1382400, 0x00000000
+0,       1713,       1713,        1,  1382400, 0x00a4f2a3
+0,       1713,       1713,        1,  1382400, 0x00a4f2a3
+0,       1714,       1714,        1,  1382400, 0x00a4f2a3
+0,       1715,       1715,        1,  1382400, 0x00a4f2a3
+0,       1716,       1716,        1,  1382400, 0x00a4f2a3
+0,       1717,       1717,        1,  1382400, 0x00a4f2a3
+0,       1718,       1718,        1,  1382400, 0x00a4f2a3
+0,       1719,       1719,        1,  1382400, 0x00a4f2a3
+0,       1720,       1720,        1,  1382400, 0x00a4f2a3
+0,       1721,       1721,        1,  1382400, 0x00a4f2a3
+0,       1722,       1722,        1,  1382400, 0x00000000
+0,       1723,       1723,        1,  1382400, 0x00000000
+0,       1724,       1724,        1,  1382400, 0x00000000
+0,       1725,       1725,        1,  1382400, 0x00000000
+0,       1726,       1726,        1,  1382400, 0x00000000
+0,       1727,       1727,        1,  1382400, 0x00000000
+0,       1728,       1728,        1,  1382400, 0x00000000
+0,       1729,       1729,        1,  1382400, 0x00000000
+0,       1730,       1730,        1,  1382400, 0x00000000
+0,       1731,       1731,        1,  1382400, 0x00000000
+0,       1732,       1732,        1,  1382400, 0x00000000
+0,       1734,       1734,        1,  1382400, 0x40a445e8
+0,       1734,       1734,        1,  1382400, 0x40a445e8
+0,       1735,       1735,        1,  1382400, 0x40a445e8
+0,       1736,       1736,        1,  1382400, 0x40a445e8
+0,       1737,       1737,        1,  1382400, 0x40a445e8
+0,       1738,       1738,        1,  1382400, 0x40a445e8
+0,       1739,       1739,        1,  1382400, 0x40a445e8
+0,       1740,       1740,        1,  1382400, 0x40a445e8
+0,       1741,       1741,        1,  1382400, 0x40a445e8
+0,       1742,       1742,        1,  1382400, 0x00000000
+0,       1743,       1743,        1,  1382400, 0x00000000
+0,       1744,       1744,        1,  1382400, 0x00000000
+0,       1745,       1745,        1,  1382400, 0x00000000
+0,       1746,       1746,        1,  1382400, 0x00000000
+0,       1747,       1747,        1,  1382400, 0x00000000
+0,       1748,       1748,        1,  1382400, 0x00000000
+0,       1749,       1749,        1,  1382400, 0x00000000
+0,       1750,       1750,        1,  1382400, 0x00000000
+0,       1751,       1751,        1,  1382400, 0x00000000
+0,       1752,       1752,        1,  1382400, 0x00000000
+0,       1753,       1753,        1,  1382400, 0x00000000
+0,       1754,       1754,        1,  1382400, 0x00000000
+0,       1755,       1755,        1,  1382400, 0x00000000
+0,       1756,       1756,        1,  1382400, 0x00000000
+0,       1757,       1757,        1,  1382400, 0x00000000
+0,       1758,       1758,        1,  1382400, 0x00000000
+0,       1759,       1759,        1,  1382400, 0x00000000
+0,       1760,       1760,        1,  1382400, 0x00000000
+0,       1761,       1761,        1,  1382400, 0x00000000
+0,       1762,       1762,        1,  1382400, 0x00000000
+0,       1763,       1763,        1,  1382400, 0x00000000
+0,       1764,       1764,        1,  1382400, 0x00000000
+0,       1765,       1765,        1,  1382400, 0x00000000
+0,       1766,       1766,        1,  1382400, 0x00000000
+0,       1767,       1767,        1,  1382400, 0x00000000
+0,       1768,       1768,        1,  1382400, 0x00000000
+0,       1769,       1769,        1,  1382400, 0x00000000
+0,       1770,       1770,        1,  1382400, 0x00000000
+0,       1771,       1771,        1,  1382400, 0x00000000
+0,       1772,       1772,        1,  1382400, 0x00000000
+0,       1773,       1773,        1,  1382400, 0x00000000
+0,       1774,       1774,        1,  1382400, 0x00000000
+0,       1775,       1775,        1,  1382400, 0x00000000
+0,       1776,       1776,        1,  1382400, 0x00000000
+0,       1777,       1777,        1,  1382400, 0x00000000
+0,       1778,       1778,        1,  1382400, 0x00000000
+0,       1779,       1779,        1,  1382400, 0x00000000
+0,       1780,       1780,        1,  1382400, 0x00000000
+0,       1781,       1781,        1,  1382400, 0x00000000
+0,       1782,       1782,        1,  1382400, 0x00000000
+0,       1783,       1783,        1,  1382400, 0x00000000
+0,       1784,       1784,        1,  1382400, 0x00000000
+0,       1785,       1785,        1,  1382400, 0x00000000
+0,       1786,       1786,        1,  1382400, 0x00000000
+0,       1788,       1788,        1,  1382400, 0x43ef5128
+0,       1788,       1788,        1,  1382400, 0x43ef5128
+0,       1789,       1789,        1,  1382400, 0x43ef5128
+0,       1790,       1790,        1,  1382400, 0x43ef5128
+0,       1791,       1791,        1,  1382400, 0x43ef5128
+0,       1792,       1792,        1,  1382400, 0x43ef5128
+0,       1793,       1793,        1,  1382400, 0x43ef5128
+0,       1794,       1794,        1,  1382400, 0x43ef5128
+0,       1795,       1795,        1,  1382400, 0x43ef5128
+0,       1796,       1796,        1,  1382400, 0x43ef5128
+0,       1797,       1797,        1,  1382400, 0x43ef5128
+0,       1798,       1798,        1,  1382400, 0x43ef5128
+0,       1799,       1799,        1,  1382400, 0x3c3e3819
+0,       1800,       1800,        1,  1382400, 0x3c3e3819
+0,       1801,       1801,        1,  1382400, 0x3c3e3819
+0,       1802,       1802,        1,  1382400, 0x3c3e3819
+0,       1803,       1803,        1,  1382400, 0x3c3e3819
+0,       1804,       1804,        1,  1382400, 0x3c3e3819
+0,       1805,       1805,        1,  1382400, 0x00000000
diff --git a/tests/ref/fate/sub2video_time_limited b/tests/ref/fate/sub2video_time_limited
index 9fb6fb06f9..2fbd91a7eb 100644
--- a/tests/ref/fate/sub2video_time_limited
+++ b/tests/ref/fate/sub2video_time_limited
@@ -1,8 +1,80 @@
-#tb 0: 1/25
+#tb 0: 1/5
 #media_type 0: video
 #codec_id 0: rawvideo
 #dimensions 0: 1920x1080
-#sar 0: 0/1
-0,          2,          2,        1,  8294400, 0x00000000
+#sar 0: 1/1
+0,          0,          0,        1,  8294400, 0xa87c518f
+0,          1,          1,        1,  8294400, 0xa87c518f
 0,          2,          2,        1,  8294400, 0xa87c518f
+0,          3,          3,        1,  8294400, 0xa87c518f
+0,          4,          4,        1,  8294400, 0xa87c518f
+0,          5,          5,        1,  8294400, 0xa87c518f
+0,          6,          6,        1,  8294400, 0xa87c518f
+0,          7,          7,        1,  8294400, 0xa87c518f
+0,          8,          8,        1,  8294400, 0xa87c518f
+0,          9,          9,        1,  8294400, 0xa87c518f
 0,         10,         10,        1,  8294400, 0xa87c518f
+0,         11,         11,        1,  8294400, 0xa87c518f
+0,         12,         12,        1,  8294400, 0xa87c518f
+0,         13,         13,        1,  8294400, 0xa87c518f
+0,         14,         14,        1,  8294400, 0xa87c518f
+0,         15,         15,        1,  8294400, 0xa87c518f
+0,         16,         16,        1,  8294400, 0xa87c518f
+0,         17,         17,        1,  8294400, 0xa87c518f
+0,         18,         18,        1,  8294400, 0xa87c518f
+0,         19,         19,        1,  8294400, 0xa87c518f
+0,         20,         20,        1,  8294400, 0xa87c518f
+0,         21,         21,        1,  8294400, 0xa87c518f
+0,         22,         22,        1,  8294400, 0xa87c518f
+0,         23,         23,        1,  8294400, 0xa87c518f
+0,         24,         24,        1,  8294400, 0xa87c518f
+0,         25,         25,        1,  8294400, 0xa87c518f
+0,         26,         26,        1,  8294400, 0xa87c518f
+0,         27,         27,        1,  8294400, 0xa87c518f
+0,         28,         28,        1,  8294400, 0xa87c518f
+0,         29,         29,        1,  8294400, 0xa87c518f
+0,         30,         30,        1,  8294400, 0xa87c518f
+0,         31,         31,        1,  8294400, 0xa87c518f
+0,         32,         32,        1,  8294400, 0xa87c518f
+0,         33,         33,        1,  8294400, 0xa87c518f
+0,         34,         34,        1,  8294400, 0xa87c518f
+0,         35,         35,        1,  8294400, 0xa87c518f
+0,         36,         36,        1,  8294400, 0xa87c518f
+0,         37,         37,        1,  8294400, 0xa87c518f
+0,         38,         38,        1,  8294400, 0xa87c518f
+0,         39,         39,        1,  8294400, 0xa87c518f
+0,         40,         40,        1,  8294400, 0xa87c518f
+0,         41,         41,        1,  8294400, 0xa87c518f
+0,         42,         42,        1,  8294400, 0xa87c518f
+0,         43,         43,        1,  8294400, 0xa87c518f
+0,         44,         44,        1,  8294400, 0xa87c518f
+0,         45,         45,        1,  8294400, 0xa87c518f
+0,         46,         46,        1,  8294400, 0xa87c518f
+0,         47,         47,        1,  8294400, 0xa87c518f
+0,         48,         48,        1,  8294400, 0xa87c518f
+0,         49,         49,        1,  8294400, 0xa87c518f
+0,         50,         50,        1,  8294400, 0xa87c518f
+0,         51,         51,        1,  8294400, 0xa87c518f
+0,         52,         52,        1,  8294400, 0xa87c518f
+0,         53,         53,        1,  8294400, 0xa87c518f
+0,         54,         54,        1,  8294400, 0xa87c518f
+0,         55,         55,        1,  8294400, 0xa87c518f
+0,         56,         56,        1,  8294400, 0xa87c518f
+0,         57,         57,        1,  8294400, 0xa87c518f
+0,         58,         58,        1,  8294400, 0xa87c518f
+0,         59,         59,        1,  8294400, 0xa87c518f
+0,         60,         60,        1,  8294400, 0xa87c518f
+0,         61,         61,        1,  8294400, 0xa87c518f
+0,         62,         62,        1,  8294400, 0xa87c518f
+0,         63,         63,        1,  8294400, 0xa87c518f
+0,         64,         64,        1,  8294400, 0xa87c518f
+0,         65,         65,        1,  8294400, 0xa87c518f
+0,         66,         66,        1,  8294400, 0xa87c518f
+0,         67,         67,        1,  8294400, 0xa87c518f
+0,         68,         68,        1,  8294400, 0xa87c518f
+0,         69,         69,        1,  8294400, 0xa87c518f
+0,         70,         70,        1,  8294400, 0xa87c518f
+0,         71,         71,        1,  8294400, 0xa87c518f
+0,         72,         72,        1,  8294400, 0xa87c518f
+0,         73,         73,        1,  8294400, 0xa87c518f
+0,         74,         74,        1,  8294400, 0xa87c518f
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v5 25/25] avcodec/dvbsubdec: Fix conditions for fallback to default resolution
  2022-06-25  9:57       ` [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022 ffmpegagent
                           ` (23 preceding siblings ...)
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 24/25] fftools/ffmpeg: Introduce subtitle filtering and new frame-based subtitle encoding softworkz
@ 2022-06-25  9:57         ` softworkz
  2022-06-26 16:34         ` [FFmpeg-devel] [PATCH v6 00/25] Subtitle Filtering 2022 ffmpegagent
                           ` (2 subsequent siblings)
  27 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-25  9:57 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

The previous code expected a segment of type CLUT definition to exist
in order to accept a set of segments to be complete.
This was an incorrect assumption as the presence of a CLUT segment
is not mandatory.
(version 1.6.1 of the spec is probably a bit more clear about this
than earlier versions: https://www.etsi.org/deliver/etsi_en/
300700_300799/300743/01.06.01_20/en_300743v010601a.pdf)

The flawed condition prevented proper fallback to using the default
resolution for the decoding context.

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/dvbsubdec.c | 51 +++++++++++++++++++++++++-----------------
 1 file changed, 30 insertions(+), 21 deletions(-)

diff --git a/libavcodec/dvbsubdec.c b/libavcodec/dvbsubdec.c
index 8d44529b4a..06f39161a8 100644
--- a/libavcodec/dvbsubdec.c
+++ b/libavcodec/dvbsubdec.c
@@ -34,7 +34,7 @@
 #define DVBSUB_CLUT_SEGMENT     0x12
 #define DVBSUB_OBJECT_SEGMENT   0x13
 #define DVBSUB_DISPLAYDEFINITION_SEGMENT 0x14
-#define DVBSUB_DISPLAY_SEGMENT  0x80
+#define DVBSUB_END_DISPLAY_SEGMENT  0x80
 
 #define cm (ff_crop_tab + MAX_NEG_CROP)
 
@@ -1450,8 +1450,12 @@ static int dvbsub_decode(AVCodecContext *avctx, AVSubtitle *sub,
     int segment_length;
     int i;
     int ret = 0;
-    int got_segment = 0;
-    int got_dds = 0;
+    //int got_segment = 0;
+    int got_page = 0;
+    int got_region = 0;
+    int got_object = 0;
+    int got_end_display = 0;
+    int got_displaydef = 0;
 
     ff_dlog(avctx, "DVB sub packet:\n");
 
@@ -1496,34 +1500,28 @@ static int dvbsub_decode(AVCodecContext *avctx, AVSubtitle *sub,
             switch (segment_type) {
             case DVBSUB_PAGE_SEGMENT:
                 ret = dvbsub_parse_page_segment(avctx, p, segment_length, sub, got_sub_ptr);
-                got_segment |= 1;
+                got_page = 1;
                 break;
             case DVBSUB_REGION_SEGMENT:
                 ret = dvbsub_parse_region_segment(avctx, p, segment_length);
-                got_segment |= 2;
+                got_region = 1;
                 break;
             case DVBSUB_CLUT_SEGMENT:
                 ret = dvbsub_parse_clut_segment(avctx, p, segment_length);
                 if (ret < 0) goto end;
-                got_segment |= 4;
                 break;
             case DVBSUB_OBJECT_SEGMENT:
                 ret = dvbsub_parse_object_segment(avctx, p, segment_length);
-                got_segment |= 8;
+                got_object = 1;
                 break;
             case DVBSUB_DISPLAYDEFINITION_SEGMENT:
                 ret = dvbsub_parse_display_definition_segment(avctx, p,
                                                               segment_length);
-                got_dds = 1;
+                got_displaydef = 1;
                 break;
-            case DVBSUB_DISPLAY_SEGMENT:
+            case DVBSUB_END_DISPLAY_SEGMENT:
                 ret = dvbsub_display_end_segment(avctx, p, segment_length, sub, got_sub_ptr);
-                if (got_segment == 15 && !got_dds && !avctx->width && !avctx->height) {
-                    // Default from ETSI EN 300 743 V1.3.1 (7.2.1)
-                    avctx->width  = 720;
-                    avctx->height = 576;
-                }
-                got_segment |= 16;
+                got_end_display = 1;
                 break;
             default:
                 ff_dlog(avctx, "Subtitling segment type 0x%x, page id %d, length %d\n",
@@ -1536,13 +1534,24 @@ static int dvbsub_decode(AVCodecContext *avctx, AVSubtitle *sub,
 
         p += segment_length;
     }
-    // Some streams do not send a display segment but if we have all the other
-    // segments then we need no further data.
-    if (got_segment == 15) {
-        av_log(avctx, AV_LOG_DEBUG, "Missing display_end_segment, emulating\n");
-        dvbsub_display_end_segment(avctx, p, 0, sub, got_sub_ptr);
-    }
 
+    // Even though not mandated by the spec, we're imposing a minimum requirement
+    // for a useful packet to have at least one page, region and object segment.
+    if (got_page && got_region && got_object && got_end_display) {
+
+        if (!got_displaydef && !avctx->width && !avctx->height) {
+            // Default from ETSI EN 300 743 V1.3.1 (7.2.1)
+            avctx->width  = 720;
+            avctx->height = 576;
+        }
+
+        // Some streams do not send an end-of-display segment but if we have all the other
+        // segments then we need no further data.
+        if (!got_end_display) {
+            av_log(avctx, AV_LOG_DEBUG, "Missing display_end_segment, emulating\n");
+            dvbsub_display_end_segment(avctx, p, 0, sub, got_sub_ptr);
+        }
+    }
 end:
     if (ret < 0) {
         return ret;
-- 
ffmpeg-codebot
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 24/25] fftools/ffmpeg: Introduce subtitle filtering and new frame-based subtitle encoding
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 24/25] fftools/ffmpeg: Introduce subtitle filtering and new frame-based subtitle encoding softworkz
@ 2022-06-25 11:34           ` Michael Niedermayer
  2022-06-25 11:42             ` Andreas Rheinhardt
  0 siblings, 1 reply; 217+ messages in thread
From: Michael Niedermayer @ 2022-06-25 11:34 UTC (permalink / raw)
  To: FFmpeg development discussions and patches


[-- Attachment #1.1: Type: text/plain, Size: 4474 bytes --]

On Sat, Jun 25, 2022 at 09:57:56AM +0000, softworkz wrote:
> From: softworkz <softworkz@hotmail.com>
> 
> This commit actually enables subtitle filtering in ffmpeg by
> sending and receiving subtitle frames to and from a filtergraph.
> 
> The heartbeat functionality from the previous sub2video implementation
> is removed and now provided by the 'subfeed' filter.
> The other part of sub2video functionality is retained by
> auto-insertion of the new graphicsub2video filter.
> 
> Justification for changed test refs:
> 
> - sub2video
>   The previous results were incorrect. The command line for this test
>   specifies -r 5 (5 frames per second), which is now fulfilled by the
>   additional frames in the reference output.
>   Example: The first subtitle time is 499000, the second is 15355000,
>   which means 0.5s and 15.35s with a difference of 14.85s.
>   15s * 5fps = 75 frames and that's now the exact number of video
>   frames between these two subtitle events.
> 
> - sub2video_basic
>   The previous results had some incorrect output because multiple
>   frames had the same dts
>   The non-empty content frames are visually identical, the different
>   CRC is due to the different blending algorithm that is being used.
> 
> - sub2video_time_limited
>   Subtitle frames are emitted to the filter graphs at a 5 fps rate
>   by default. The time limit for this test is 15s * 5fps = 75 frames
>   which matches the count in the new ref.
> 
> - sub-dvb
>   Running ffprobe -show_frames on the source file shows that there
>   are 7 subtitle frames with 0 rects in the source at the start
>   and 2 at the end. This translates to the 14 and 4 additional
>   entries in the new test results.
> 
> - filter-overlay-dvdsub-2397
>   Overlay results have slightly different CRCs due to different
>   blending implementation
> 
> - sub-scc
>   The first entry is no longer in the output because it is before
>   the actual start time and the strim filter removes such entries
>   now (like for video and audio)
> 
> Signed-off-by: softworkz <softworkz@hotmail.com>
> ---
>  fftools/ffmpeg.c                          |  613 +++++-----
>  fftools/ffmpeg.h                          |   17 +-
>  fftools/ffmpeg_filter.c                   |  270 +++--
>  fftools/ffmpeg_hw.c                       |    2 +-
>  fftools/ffmpeg_opt.c                      |   28 +-
>  tests/ref/fate/filter-overlay-dvdsub-2397 |  182 +--
>  tests/ref/fate/sub-dvb                    |  162 +--
>  tests/ref/fate/sub-scc                    |    1 -
>  tests/ref/fate/sub2video                  | 1091 +++++++++++++++++-
>  tests/ref/fate/sub2video_basic            | 1238 +++++++++++++++++++--
>  tests/ref/fate/sub2video_time_limited     |   78 +-
>  11 files changed, 3010 insertions(+), 672 deletions(-)

seems to break fate

Input #0, ass, from 'fate-suite//sub/1ededcbd7b.ass':
  Duration: N/A, bitrate: N/A
  Stream #0:0: Subtitle: ass
Codec AVOption threads (set the number of threads) specified for input file #0 (fate-suite//sub/1ededcbd7b.ass) has not been used for any stream. The most likely reason is either wrong type (e.g. a video option with no video streams) or that it is a private option of some decoder which was not actually used for any stream.
Codec AVOption thread_type (select multithreading type) specified for input file #0 (fate-suite//sub/1ededcbd7b.ass) has not been used for any stream. The most likely reason is either wrong type (e.g. a video option with no video streams) or that it is a private option of some decoder which was not actually used for any stream.
Stream mapping:
  Stream #0:0 -> #0:0 (ass (ssa) -> ass (ssa))
cur_dts is invalid st:0 (0) [init:0 i_done:0 finish:0] (this is harmless if it occurs once at the start per stream)
subtitle input filter: decoding size 1280x720
[out_0_0 @ 0x557cab64bbc0] Subtitle format change from 1 to 3
Assertion c > 0 failed at libavutil/mathematics.c:61
Aborted (core dumped)
tests/Makefile:304: recipe for target 'fate-sub-ass-to-ass-transcode' failed
make: *** [fate-sub-ass-to-ass-transcode] Error 134

thx

[...]
-- 
Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB

The day soldiers stop bringing you their problems is the day you have stopped 
leading them. They have either lost confidence that you can help or concluded 
you do not care. Either case is a failure of leadership. - Colin Powell

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 195 bytes --]

[-- Attachment #2: Type: text/plain, Size: 251 bytes --]

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 24/25] fftools/ffmpeg: Introduce subtitle filtering and new frame-based subtitle encoding
  2022-06-25 11:34           ` Michael Niedermayer
@ 2022-06-25 11:42             ` Andreas Rheinhardt
  2022-06-25 12:31               ` Soft Works
  0 siblings, 1 reply; 217+ messages in thread
From: Andreas Rheinhardt @ 2022-06-25 11:42 UTC (permalink / raw)
  To: ffmpeg-devel

Michael Niedermayer:
> On Sat, Jun 25, 2022 at 09:57:56AM +0000, softworkz wrote:
>> From: softworkz <softworkz@hotmail.com>
>>
>> This commit actually enables subtitle filtering in ffmpeg by
>> sending and receiving subtitle frames to and from a filtergraph.
>>
>> The heartbeat functionality from the previous sub2video implementation
>> is removed and now provided by the 'subfeed' filter.
>> The other part of sub2video functionality is retained by
>> auto-insertion of the new graphicsub2video filter.
>>
>> Justification for changed test refs:
>>
>> - sub2video
>>   The previous results were incorrect. The command line for this test
>>   specifies -r 5 (5 frames per second), which is now fulfilled by the
>>   additional frames in the reference output.
>>   Example: The first subtitle time is 499000, the second is 15355000,
>>   which means 0.5s and 15.35s with a difference of 14.85s.
>>   15s * 5fps = 75 frames and that's now the exact number of video
>>   frames between these two subtitle events.
>>
>> - sub2video_basic
>>   The previous results had some incorrect output because multiple
>>   frames had the same dts
>>   The non-empty content frames are visually identical, the different
>>   CRC is due to the different blending algorithm that is being used.
>>
>> - sub2video_time_limited
>>   Subtitle frames are emitted to the filter graphs at a 5 fps rate
>>   by default. The time limit for this test is 15s * 5fps = 75 frames
>>   which matches the count in the new ref.
>>
>> - sub-dvb
>>   Running ffprobe -show_frames on the source file shows that there
>>   are 7 subtitle frames with 0 rects in the source at the start
>>   and 2 at the end. This translates to the 14 and 4 additional
>>   entries in the new test results.
>>
>> - filter-overlay-dvdsub-2397
>>   Overlay results have slightly different CRCs due to different
>>   blending implementation
>>
>> - sub-scc
>>   The first entry is no longer in the output because it is before
>>   the actual start time and the strim filter removes such entries
>>   now (like for video and audio)
>>
>> Signed-off-by: softworkz <softworkz@hotmail.com>
>> ---
>>  fftools/ffmpeg.c                          |  613 +++++-----
>>  fftools/ffmpeg.h                          |   17 +-
>>  fftools/ffmpeg_filter.c                   |  270 +++--
>>  fftools/ffmpeg_hw.c                       |    2 +-
>>  fftools/ffmpeg_opt.c                      |   28 +-
>>  tests/ref/fate/filter-overlay-dvdsub-2397 |  182 +--
>>  tests/ref/fate/sub-dvb                    |  162 +--
>>  tests/ref/fate/sub-scc                    |    1 -
>>  tests/ref/fate/sub2video                  | 1091 +++++++++++++++++-
>>  tests/ref/fate/sub2video_basic            | 1238 +++++++++++++++++++--
>>  tests/ref/fate/sub2video_time_limited     |   78 +-
>>  11 files changed, 3010 insertions(+), 672 deletions(-)
> 
> seems to break fate
> 
> Input #0, ass, from 'fate-suite//sub/1ededcbd7b.ass':
>   Duration: N/A, bitrate: N/A
>   Stream #0:0: Subtitle: ass
> Codec AVOption threads (set the number of threads) specified for input file #0 (fate-suite//sub/1ededcbd7b.ass) has not been used for any stream. The most likely reason is either wrong type (e.g. a video option with no video streams) or that it is a private option of some decoder which was not actually used for any stream.
> Codec AVOption thread_type (select multithreading type) specified for input file #0 (fate-suite//sub/1ededcbd7b.ass) has not been used for any stream. The most likely reason is either wrong type (e.g. a video option with no video streams) or that it is a private option of some decoder which was not actually used for any stream.
> Stream mapping:
>   Stream #0:0 -> #0:0 (ass (ssa) -> ass (ssa))
> cur_dts is invalid st:0 (0) [init:0 i_done:0 finish:0] (this is harmless if it occurs once at the start per stream)
> subtitle input filter: decoding size 1280x720
> [out_0_0 @ 0x557cab64bbc0] Subtitle format change from 1 to 3
> Assertion c > 0 failed at libavutil/mathematics.c:61

That's an av_assert2-assert. softworkz probably didn't test these.

> Aborted (core dumped)
> tests/Makefile:304: recipe for target 'fate-sub-ass-to-ass-transcode' failed
> make: *** [fate-sub-ass-to-ass-transcode] Error 134
> 
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 24/25] fftools/ffmpeg: Introduce subtitle filtering and new frame-based subtitle encoding
  2022-06-25 11:42             ` Andreas Rheinhardt
@ 2022-06-25 12:31               ` Soft Works
  2022-06-25 13:00                 ` Andreas Rheinhardt
  0 siblings, 1 reply; 217+ messages in thread
From: Soft Works @ 2022-06-25 12:31 UTC (permalink / raw)
  To: FFmpeg development discussions and patches



________________________________________
From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> on behalf of Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
Sent: Saturday, June 25, 2022 1:42 PM
To: ffmpeg-devel@ffmpeg.org
Subject: Re: [FFmpeg-devel] [PATCH v5 24/25] fftools/ffmpeg: Introduce subtitle filtering and new frame-based subtitle encoding

Michael Niedermayer:
> On Sat, Jun 25, 2022 at 09:57:56AM +0000, softworkz wrote:
>> From: softworkz <softworkz@hotmail.com>
>>
>> This commit actually enables subtitle filtering in ffmpeg by
>> sending and receiving subtitle frames to and from a filtergraph.
>>
>> The heartbeat functionality from the previous sub2video implementation
>> is removed and now provided by the 'subfeed' filter.
>> The other part of sub2video functionality is retained by
>> auto-insertion of the new graphicsub2video filter.
>>
>> Justification for changed test refs:
>>
>> - sub2video
>>   The previous results were incorrect. The command line for this test
>>   specifies -r 5 (5 frames per second), which is now fulfilled by the
>>   additional frames in the reference output.
>>   Example: The first subtitle time is 499000, the second is 15355000,
>>   which means 0.5s and 15.35s with a difference of 14.85s.
>>   15s * 5fps = 75 frames and that's now the exact number of video
>>   frames between these two subtitle events.
>>
>> - sub2video_basic
>>   The previous results had some incorrect output because multiple
>>   frames had the same dts
>>   The non-empty content frames are visually identical, the different
>>   CRC is due to the different blending algorithm that is being used.
>>
>> - sub2video_time_limited
>>   Subtitle frames are emitted to the filter graphs at a 5 fps rate
>>   by default. The time limit for this test is 15s * 5fps = 75 frames
>>   which matches the count in the new ref.
>>
>> - sub-dvb
>>   Running ffprobe -show_frames on the source file shows that there
>>   are 7 subtitle frames with 0 rects in the source at the start
>>   and 2 at the end. This translates to the 14 and 4 additional
>>   entries in the new test results.
>>
>> - filter-overlay-dvdsub-2397
>>   Overlay results have slightly different CRCs due to different
>>   blending implementation
>>
>> - sub-scc
>>   The first entry is no longer in the output because it is before
>>   the actual start time and the strim filter removes such entries
>>   now (like for video and audio)
>>
>> Signed-off-by: softworkz <softworkz@hotmail.com>
>> ---
>>  fftools/ffmpeg.c                          |  613 +++++-----
>>  fftools/ffmpeg.h                          |   17 +-
>>  fftools/ffmpeg_filter.c                   |  270 +++--
>>  fftools/ffmpeg_hw.c                       |    2 +-
>>  fftools/ffmpeg_opt.c                      |   28 +-
>>  tests/ref/fate/filter-overlay-dvdsub-2397 |  182 +--
>>  tests/ref/fate/sub-dvb                    |  162 +--
>>  tests/ref/fate/sub-scc                    |    1 -
>>  tests/ref/fate/sub2video                  | 1091 +++++++++++++++++-
>>  tests/ref/fate/sub2video_basic            | 1238 +++++++++++++++++++--
>>  tests/ref/fate/sub2video_time_limited     |   78 +-
>>  11 files changed, 3010 insertions(+), 672 deletions(-)
>
> seems to break fate
>
> Input #0, ass, from 'fate-suite//sub/1ededcbd7b.ass':
>   Duration: N/A, bitrate: N/A
>   Stream #0:0: Subtitle: ass
> Codec AVOption threads (set the number of threads) specified for input file #0 (fate-suite//sub/1ededcbd7b.ass) has not been used for any stream. The most likely reason is either wrong type (e.g. a video option with no video streams) or that it is a private option of some decoder which was not actually used for any stream.
> Codec AVOption thread_type (select multithreading type) specified for input file #0 (fate-suite//sub/1ededcbd7b.ass) has not been used for any stream. The most likely reason is either wrong type (e.g. a video option with no video streams) or that it is a private option of some decoder which was not actually used for any stream.
> Stream mapping:
>   Stream #0:0 -> #0:0 (ass (ssa) -> ass (ssa))
> cur_dts is invalid st:0 (0) [init:0 i_done:0 finish:0] (this is harmless if it occurs once at the start per stream)
> subtitle input filter: decoding size 1280x720
> [out_0_0 @ 0x557cab64bbc0] Subtitle format change from 1 to 3
> Assertion c > 0 failed at libavutil/mathematics.c:61

That's an av_assert2-assert. softworkz probably didn't test these.


Yup. To be honest, I wasn't even aware that I'm supposed to. It seems patchwork doesn't do either?

Do I need a different configure or make fate?

Thanks,
softworkz


_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 24/25] fftools/ffmpeg: Introduce subtitle filtering and new frame-based subtitle encoding
  2022-06-25 12:31               ` Soft Works
@ 2022-06-25 13:00                 ` Andreas Rheinhardt
  2022-06-26 16:27                   ` Soft Works
  0 siblings, 1 reply; 217+ messages in thread
From: Andreas Rheinhardt @ 2022-06-25 13:00 UTC (permalink / raw)
  To: ffmpeg-devel

Soft Works:
> 
> 
> ________________________________________
> From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> on behalf of Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
> Sent: Saturday, June 25, 2022 1:42 PM
> To: ffmpeg-devel@ffmpeg.org
> Subject: Re: [FFmpeg-devel] [PATCH v5 24/25] fftools/ffmpeg: Introduce subtitle filtering and new frame-based subtitle encoding
> 
> Michael Niedermayer:
>> On Sat, Jun 25, 2022 at 09:57:56AM +0000, softworkz wrote:
>>> From: softworkz <softworkz@hotmail.com>
>>>
>>> This commit actually enables subtitle filtering in ffmpeg by
>>> sending and receiving subtitle frames to and from a filtergraph.
>>>
>>> The heartbeat functionality from the previous sub2video implementation
>>> is removed and now provided by the 'subfeed' filter.
>>> The other part of sub2video functionality is retained by
>>> auto-insertion of the new graphicsub2video filter.
>>>
>>> Justification for changed test refs:
>>>
>>> - sub2video
>>>   The previous results were incorrect. The command line for this test
>>>   specifies -r 5 (5 frames per second), which is now fulfilled by the
>>>   additional frames in the reference output.
>>>   Example: The first subtitle time is 499000, the second is 15355000,
>>>   which means 0.5s and 15.35s with a difference of 14.85s.
>>>   15s * 5fps = 75 frames and that's now the exact number of video
>>>   frames between these two subtitle events.
>>>
>>> - sub2video_basic
>>>   The previous results had some incorrect output because multiple
>>>   frames had the same dts
>>>   The non-empty content frames are visually identical, the different
>>>   CRC is due to the different blending algorithm that is being used.
>>>
>>> - sub2video_time_limited
>>>   Subtitle frames are emitted to the filter graphs at a 5 fps rate
>>>   by default. The time limit for this test is 15s * 5fps = 75 frames
>>>   which matches the count in the new ref.
>>>
>>> - sub-dvb
>>>   Running ffprobe -show_frames on the source file shows that there
>>>   are 7 subtitle frames with 0 rects in the source at the start
>>>   and 2 at the end. This translates to the 14 and 4 additional
>>>   entries in the new test results.
>>>
>>> - filter-overlay-dvdsub-2397
>>>   Overlay results have slightly different CRCs due to different
>>>   blending implementation
>>>
>>> - sub-scc
>>>   The first entry is no longer in the output because it is before
>>>   the actual start time and the strim filter removes such entries
>>>   now (like for video and audio)
>>>
>>> Signed-off-by: softworkz <softworkz@hotmail.com>
>>> ---
>>>  fftools/ffmpeg.c                          |  613 +++++-----
>>>  fftools/ffmpeg.h                          |   17 +-
>>>  fftools/ffmpeg_filter.c                   |  270 +++--
>>>  fftools/ffmpeg_hw.c                       |    2 +-
>>>  fftools/ffmpeg_opt.c                      |   28 +-
>>>  tests/ref/fate/filter-overlay-dvdsub-2397 |  182 +--
>>>  tests/ref/fate/sub-dvb                    |  162 +--
>>>  tests/ref/fate/sub-scc                    |    1 -
>>>  tests/ref/fate/sub2video                  | 1091 +++++++++++++++++-
>>>  tests/ref/fate/sub2video_basic            | 1238 +++++++++++++++++++--
>>>  tests/ref/fate/sub2video_time_limited     |   78 +-
>>>  11 files changed, 3010 insertions(+), 672 deletions(-)
>>
>> seems to break fate
>>
>> Input #0, ass, from 'fate-suite//sub/1ededcbd7b.ass':
>>   Duration: N/A, bitrate: N/A
>>   Stream #0:0: Subtitle: ass
>> Codec AVOption threads (set the number of threads) specified for input file #0 (fate-suite//sub/1ededcbd7b.ass) has not been used for any stream. The most likely reason is either wrong type (e.g. a video option with no video streams) or that it is a private option of some decoder which was not actually used for any stream.
>> Codec AVOption thread_type (select multithreading type) specified for input file #0 (fate-suite//sub/1ededcbd7b.ass) has not been used for any stream. The most likely reason is either wrong type (e.g. a video option with no video streams) or that it is a private option of some decoder which was not actually used for any stream.
>> Stream mapping:
>>   Stream #0:0 -> #0:0 (ass (ssa) -> ass (ssa))
>> cur_dts is invalid st:0 (0) [init:0 i_done:0 finish:0] (this is harmless if it occurs once at the start per stream)
>> subtitle input filter: decoding size 1280x720
>> [out_0_0 @ 0x557cab64bbc0] Subtitle format change from 1 to 3
>> Assertion c > 0 failed at libavutil/mathematics.c:61
> 
> That's an av_assert2-assert. softworkz probably didn't test these.
> 
> 
> Yup. To be honest, I wasn't even aware that I'm supposed to. It seems patchwork doesn't do either?
> 
> Do I need a different configure or make fate?
> 

The supported way to set it is to pass --assert-level=2 to configure.
(You can also modify config.h by adding "#define ASSERT_LEVEL 2"; if you
have not used assert-level during configure, you should have no
ASSERT_LEVEL in config.h, but better check this.)

- Andreas
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 24/25] fftools/ffmpeg: Introduce subtitle filtering and new frame-based subtitle encoding
  2022-06-25 13:00                 ` Andreas Rheinhardt
@ 2022-06-26 16:27                   ` Soft Works
  0 siblings, 0 replies; 217+ messages in thread
From: Soft Works @ 2022-06-26 16:27 UTC (permalink / raw)
  To: FFmpeg development discussions and patches



> -----Original Message-----
> From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of
> Andreas Rheinhardt
> Sent: Saturday, June 25, 2022 3:01 PM
> To: ffmpeg-devel@ffmpeg.org
> Subject: Re: [FFmpeg-devel] [PATCH v5 24/25] fftools/ffmpeg:
> Introduce subtitle filtering and new frame-based subtitle encoding
> 
> Soft Works:
> >
> >
> > ________________________________________
> > From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> on behalf of
> Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
> > Sent: Saturday, June 25, 2022 1:42 PM
> > To: ffmpeg-devel@ffmpeg.org
> > Subject: Re: [FFmpeg-devel] [PATCH v5 24/25] fftools/ffmpeg:
> Introduce subtitle filtering and new frame-based subtitle encoding
> >
> > Michael Niedermayer:
> >> On Sat, Jun 25, 2022 at 09:57:56AM +0000, softworkz wrote:
> >>> From: softworkz <softworkz@hotmail.com>
> >>>
> >>> This commit actually enables subtitle filtering in ffmpeg by
> >>> sending and receiving subtitle frames to and from a filtergraph.
> >>>
> >>> The heartbeat functionality from the previous sub2video
> implementation
> >>> is removed and now provided by the 'subfeed' filter.
> >>> The other part of sub2video functionality is retained by
> >>> auto-insertion of the new graphicsub2video filter.
> >>>
> >>> Justification for changed test refs:
> >>>
> >>> - sub2video
> >>>   The previous results were incorrect. The command line for this
> test
> >>>   specifies -r 5 (5 frames per second), which is now fulfilled by
> the
> >>>   additional frames in the reference output.
> >>>   Example: The first subtitle time is 499000, the second is
> 15355000,
> >>>   which means 0.5s and 15.35s with a difference of 14.85s.
> >>>   15s * 5fps = 75 frames and that's now the exact number of video
> >>>   frames between these two subtitle events.
> >>>
> >>> - sub2video_basic
> >>>   The previous results had some incorrect output because multiple
> >>>   frames had the same dts
> >>>   The non-empty content frames are visually identical, the
> different
> >>>   CRC is due to the different blending algorithm that is being
> used.
> >>>
> >>> - sub2video_time_limited
> >>>   Subtitle frames are emitted to the filter graphs at a 5 fps
> rate
> >>>   by default. The time limit for this test is 15s * 5fps = 75
> frames
> >>>   which matches the count in the new ref.
> >>>
> >>> - sub-dvb
> >>>   Running ffprobe -show_frames on the source file shows that
> there
> >>>   are 7 subtitle frames with 0 rects in the source at the start
> >>>   and 2 at the end. This translates to the 14 and 4 additional
> >>>   entries in the new test results.
> >>>
> >>> - filter-overlay-dvdsub-2397
> >>>   Overlay results have slightly different CRCs due to different
> >>>   blending implementation
> >>>
> >>> - sub-scc
> >>>   The first entry is no longer in the output because it is before
> >>>   the actual start time and the strim filter removes such entries
> >>>   now (like for video and audio)
> >>>
> >>> Signed-off-by: softworkz <softworkz@hotmail.com>
> >>> ---
> >>>  fftools/ffmpeg.c                          |  613 +++++-----
> >>>  fftools/ffmpeg.h                          |   17 +-
> >>>  fftools/ffmpeg_filter.c                   |  270 +++--
> >>>  fftools/ffmpeg_hw.c                       |    2 +-
> >>>  fftools/ffmpeg_opt.c                      |   28 +-
> >>>  tests/ref/fate/filter-overlay-dvdsub-2397 |  182 +--
> >>>  tests/ref/fate/sub-dvb                    |  162 +--
> >>>  tests/ref/fate/sub-scc                    |    1 -
> >>>  tests/ref/fate/sub2video                  | 1091
> +++++++++++++++++-
> >>>  tests/ref/fate/sub2video_basic            | 1238
> +++++++++++++++++++--
> >>>  tests/ref/fate/sub2video_time_limited     |   78 +-
> >>>  11 files changed, 3010 insertions(+), 672 deletions(-)
> >>
> >> seems to break fate
> >>
> >> Input #0, ass, from 'fate-suite//sub/1ededcbd7b.ass':
> >>   Duration: N/A, bitrate: N/A
> >>   Stream #0:0: Subtitle: ass
> >> Codec AVOption threads (set the number of threads) specified for
> input file #0 (fate-suite//sub/1ededcbd7b.ass) has not been used for
> any stream. The most likely reason is either wrong type (e.g. a video
> option with no video streams) or that it is a private option of some
> decoder which was not actually used for any stream.
> >> Codec AVOption thread_type (select multithreading type) specified
> for input file #0 (fate-suite//sub/1ededcbd7b.ass) has not been used
> for any stream. The most likely reason is either wrong type (e.g. a
> video option with no video streams) or that it is a private option of
> some decoder which was not actually used for any stream.
> >> Stream mapping:
> >>   Stream #0:0 -> #0:0 (ass (ssa) -> ass (ssa))
> >> cur_dts is invalid st:0 (0) [init:0 i_done:0 finish:0] (this is
> harmless if it occurs once at the start per stream)
> >> subtitle input filter: decoding size 1280x720
> >> [out_0_0 @ 0x557cab64bbc0] Subtitle format change from 1 to 3
> >> Assertion c > 0 failed at libavutil/mathematics.c:61
> >
> > That's an av_assert2-assert. softworkz probably didn't test these.
> >
> >
> > Yup. To be honest, I wasn't even aware that I'm supposed to. It
> seems patchwork doesn't do either?
> >
> > Do I need a different configure or make fate?
> >
> 
> The supported way to set it is to pass --assert-level=2 to configure.
> (You can also modify config.h by adding "#define ASSERT_LEVEL 2"; if
> you
> have not used assert-level during configure, you should have no
> ASSERT_LEVEL in config.h, but better check this.)

Both ways working fine, thanks a lot for the explanation.

@Michael - thanks for testing locally. I was able to identify the issue
which came from adding the new strim filter.

Update is on the way.

Thanks,
softworkz



_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v6 00/25] Subtitle Filtering 2022
  2022-06-25  9:57       ` [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022 ffmpegagent
                           ` (24 preceding siblings ...)
  2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 25/25] avcodec/dvbsubdec: Fix conditions for fallback to default resolution softworkz
@ 2022-06-26 16:34         ` ffmpegagent
  2022-06-26 16:34           ` [FFmpeg-devel] [PATCH v6 01/25] avcodec, avutil: Move enum AVSubtitleType to avutil, add new and deprecate old values softworkz
                             ` (24 more replies)
  2022-07-02 16:39         ` [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022 Paul B Mahol
  2022-08-11 22:50         ` Soft Works
  27 siblings, 25 replies; 217+ messages in thread
From: ffmpegagent @ 2022-06-26 16:34 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt


Subtitle Filtering 2022
=======================

This is a substantial update to the earlier subtitle filtering patch series.
A primary goal has been to address others' concerns as much as possible on
one side and to provide more clarity and control over the way things are
working. Clarity is is specifically important to allow for a better
understanding of the need for a subtitle start pts value that can be
different from the frame's pts value. This is done by refactoring the
subtitle timing fields in AVFrame, adding a frame field to indicate repeated
subtitle frames, and finally the full removal of the heartbeat
functionality, replaced by a new 'subfeed' filter that provides different
modes for arbitrating subtitle frames in a filter graph. Finally, each
subtitle filter's documentation has been amended by a section describing the
filter's timeline behavior (in v3 update).


Subtitle Filtering Demos
========================

I published a demonstration of subtitle filtering capabilities with OCR,
text and bitmap subtitle manipulation involved: Demo 1: Text-Manipulation
with Bitmap Subtitles
[https://github.com/softworkz/SubtitleFilteringDemos/tree/master/Demo1]


v6 - Fix assertion errors
=========================

 * text2graphicsub: fix null point on uninit after error
 * strim: propagate width and height
 * avfilter: add default propagation time_base from inlink to outlink


v5 - Conversion to Graphic Subtitles, and other enhancements
============================================================

 * I'm glad to announce that Traian (@tcoza) has joined the project and
   contributed a new 'text2graphicsub' filter to convert text subtitles to
   graphic subtitles, which can in turn be encoded as dvd, dvb or x-subs
   (and any other encoder for graphic subs that might be added in the
   future). This filter closes the last open "gap" in subtitle processing.
 * stripstyles filter: now allows very fine-grained control over which ASS
   style codes should be preserved or stripped
 * stripstyles: do not drop dialog margin values
 * subfeed filter: eliminates duplicate frames with duplicate start times
   when 'fix_overlap' is specified
 * textmod: do not drop effect values
 * graphicsub2text: reduce font size jitter
 * ass_split: add function to selectively preserve elements when splitting
 * add strim, snull and ssink and further unify subtitle frame handling with
   audio and video
 * ffmpeg_filter: get simple filter notation working for subtitles


v4 - Quality Improvements
=========================

 * finally an updated version
 * includes many improvements from internal testing
 * all FATE tests passed
 * all example commands from the docs verified to work
 * can't list all the detail changes..
 * I have left out the extra commits which can be handled separately, just
   in case somebody wonders why these are missing:
   * avcodec/webvttenc: Don't encode drawing codes and empty lines
   * avcodec/webvttenc: convert hard-space tags to  
   * avutil/ass_split: Add parsing of hard-space tags (\h)
   * avutil/ass_split: Treat all content in curly braces as hidden
   * avutil/ass_split: Fix ass parsing of style codes with comments


v3 - Rebase
===========

due to merge conflicts - apologies.


Changes in v2
=============

 * added .gitattributes file to enforce binary diffs for the test refs that
   cannot be applied when being sent via e-mail
 * perform filter graph re-init due to subtitle "frame size" change only
   when the size was unknown before and not set via -canvas_size
 * overlaytextsubs: Make sure to request frames on the subtitle input
 * avfilter/splitcc: Start parsing cc data on key frames only
 * avcodec/webvttenc: Don't encode ass drawing codes and empty lines
 * stripstyles: fix mem leak
 * gs2t: improve color detection
 * gs2t: empty frames must not be skipped
 * subfeed: fix name
 * textmod: preserve margins
 * added .gitattributes file to enforce binary diffs for the test refs that
   cannot be applied when being sent via e-mail
 * perform filter graph re-init due to subtitle "frame size" change only
   when the size was unknown before and not set via -canvas_size
 * avcodec/dvbsubdec: Fix conditions for fallback to default resolution
 * Made changes suggested by Andreas
 * Fixed failing command line reported by Michael

Changes from previous version v24:


AVFrame
=======

 * Removed sub_start_time The start time is now added to the subtitle
   start_pts during decoding The sub_end_time field is adjusted accordingly
 * Renamed sub_end_time to duration which it is effectively after removing
   the start_time
 * Added a sub-struct 'subtitle_timing' to av frame Contains subtitle_pts
   renamed to 'subtitle_timing.start_pts' and 'subtitle_timing.duration'
 * Change both fields to (fixed) time_base AV_TIMEBASE
 * add repeat_sub field provides a clear indication whether a subtitle frame
   is an actual subtitle event or a repeated subtitle frame in a filter
   graph


Heartbeat Removal
=================

 * completely removed the earlier heartbeat implementation
 * filtering arbitration is now implemented in a new filter: 'subfeed'
 * subfeed will be auto-inserted for compatiblity with sub2video command
   lines
 * the new behavior is not exactly identical to the earlier behavior, but it
   basically allows to achieve the same results
 * there's a small remainder, now named subtitle kickoff which serves to get
   things (in the filter graph) going right from the start


New 'subfeed' Filter
====================

 * a versatile filter for solving all kinds of problems with subtile frame
   flow in filter graphs
 * Can be inserted at any position in a graph
 * Auto-inserted for sub2video command lines (in repeat-mode)
 * Allows duration fixup delay input frames with unknown duration and infer
   duration from start of subsequent frame
 * Provides multiple modes of operation:
   * repeat mode (default) Queues input frames Outputs frames at a fixed
     (configurable) rate Either sends a matching input frame (repeatedly) or
     empty frames otherwise
   * scatter mode similar to repeat mode, but splits input frames by
     duration into small segments with same content
   * forward mode No fixed output rate Useful in combination with duration
     fixup or overlap fixup


ffmpeg Tool Changes
===================

 * delay subtitle output stream initialization (like for audio and video)
   This is needed for example when a format header depends on having
   received an initial frame to derive certain header values from
 * decoding: set subtitle frame size from decoding context
 * re-init graph when subtitle size changes
 * always insert subscale filter for sub2video command lines (to ensure
   correct scaling)


Subtitle Encoding
=================

 * ignore repeated frames for encoding based on repeat_sub field in AVFrame
 * support multi-area encoding for text subtitles Subtitle OCR can create
   multiple areas at different positions. Previously, the texts were always
   squashed into a single area ('subtitle rect'), which was not ideal.
   Multiple text areas are now generally supported:
   * ASS Encoder Changed to use the 'receive_packet' encoding API A single
     frame with multiple text areas will create multiple packets now
   * All other text subtitle encoders A newline is inserted between the text
     from multiple areas


graphicsub2text (OCR)
=====================

 * enhanced preprocessing
   * using elbg algorithm for color quantization
   * detection and removal of text outlines
   * map-based identification of colors per word (text, outline, background)
 * add option for duration fixup
 * add option to dump preprocessing bitmaps
 * Recognize formatting and apply as ASS inline styles
   * per word(!)
   * paragraph alignment
   * positioning
   * font names
   * font size
   * font style (italic, underline, bold)
   * text color, outline color


Other Filter Changes
====================

 * all: Make sure to forward all link properties (time base, frame rate, w,
   h) where appropriate
 * overlaytextsubs: request frames on the subtitle input
 * overlaytextsubs: disable read-order checking
 * overlaytextsubs: improve implementation of render_latest_only
 * overlaytextsubs: ensure equal in/out video formats
 * splitcc: derive framerate from realtime_latency
 * graphicsub2video: implement caching of converted frames
 * graphicsub2video: use 1x1 output frame size as long as subtitle size is
   unknown (0x0)

Plus a dozen of things I forgot..

softworkz (25):
  avcodec,avutil: Move enum AVSubtitleType to avutil, add new and
    deprecate old values
  avutil/frame: Prepare AVFrame for subtitle handling
  avcodec/subtitles: Introduce new frame-based subtitle decoding API
  avcodec/libzvbi: set subtitle type
  avfilter/subtitles: Update vf_subtitles to use new decoding api
  avcodec,avutil: Move ass helper functions to avutil as avpriv_ and
    extend ass dialog parsing
  avcodec/subtitles: Replace deprecated enum values
  fftools/play,probe: Adjust for subtitle changes
  avfilter/subtitles: Add subtitles.c for subtitle frame allocation
  avfilter/avfilter: Handle subtitle frames
  avfilter/avfilter: Fix hardcoded input index
  avfilter/sbuffer: Add sbuffersrc and sbuffersink filters
  avfilter/overlaygraphicsubs: Add overlaygraphicsubs and
    graphicsub2video filters
  avfilter/overlaytextsubs: Add overlaytextsubs and textsubs2video
    filters
  avfilter/textmod: Add textmod, censor and show_speaker filters
  avfilter/stripstyles: Add stripstyles filter
  avfilter/splitcc: Add splitcc filter for closed caption handling
  avfilter/graphicsub2text: Add new graphicsub2text filter (OCR)
  avfilter/subscale: Add filter for scaling and/or re-arranging
    graphical subtitles
  avfilter/subfeed: add subtitle feed filter
  avfilter/text2graphicsub: Added text2graphicsub subtitle filter
  avfilter/snull,strim: Add snull and strim filters
  avcodec/subtitles: Migrate subtitle encoders to frame-based API
  fftools/ffmpeg: Introduce subtitle filtering and new frame-based
    subtitle encoding
  avcodec/dvbsubdec: Fix conditions for fallback to default resolution

 configure                                 |   10 +-
 doc/filters.texi                          |  807 ++++++++++++++
 fftools/ffmpeg.c                          |  613 +++++-----
 fftools/ffmpeg.h                          |   17 +-
 fftools/ffmpeg_filter.c                   |  270 +++--
 fftools/ffmpeg_hw.c                       |    2 +-
 fftools/ffmpeg_opt.c                      |   28 +-
 fftools/ffplay.c                          |  102 +-
 fftools/ffprobe.c                         |   47 +-
 libavcodec/Makefile                       |   56 +-
 libavcodec/ass.h                          |  151 +--
 libavcodec/ass_split.h                    |  191 ----
 libavcodec/assdec.c                       |    4 +-
 libavcodec/assenc.c                       |  191 +++-
 libavcodec/avcodec.c                      |    8 +
 libavcodec/avcodec.h                      |   34 +-
 libavcodec/ccaption_dec.c                 |   20 +-
 libavcodec/codec_internal.h               |   12 -
 libavcodec/decode.c                       |   60 +-
 libavcodec/dvbsubdec.c                    |   53 +-
 libavcodec/dvbsubenc.c                    |   96 +-
 libavcodec/dvdsubdec.c                    |    2 +-
 libavcodec/dvdsubenc.c                    |  102 +-
 libavcodec/encode.c                       |   61 +-
 libavcodec/internal.h                     |   16 +
 libavcodec/jacosubdec.c                   |    2 +-
 libavcodec/libaribb24.c                   |    2 +-
 libavcodec/libzvbi-teletextdec.c          |   17 +-
 libavcodec/microdvddec.c                  |    7 +-
 libavcodec/movtextdec.c                   |    3 +-
 libavcodec/movtextenc.c                   |  126 ++-
 libavcodec/mpl2dec.c                      |    2 +-
 libavcodec/pgssubdec.c                    |    2 +-
 libavcodec/realtextdec.c                  |    2 +-
 libavcodec/samidec.c                      |    2 +-
 libavcodec/srtdec.c                       |    2 +-
 libavcodec/srtenc.c                       |  116 +-
 libavcodec/subviewerdec.c                 |    2 +-
 libavcodec/tests/avcodec.c                |    5 +-
 libavcodec/textdec.c                      |    4 +-
 libavcodec/ttmlenc.c                      |  114 +-
 libavcodec/utils.c                        |  185 ++-
 libavcodec/webvttdec.c                    |    2 +-
 libavcodec/webvttenc.c                    |   94 +-
 libavcodec/xsubdec.c                      |    2 +-
 libavcodec/xsubenc.c                      |   88 +-
 libavfilter/Makefile                      |   18 +
 libavfilter/allfilters.c                  |   19 +
 libavfilter/avfilter.c                    |   42 +-
 libavfilter/avfilter.h                    |   11 +
 libavfilter/avfiltergraph.c               |    5 +
 libavfilter/buffersink.c                  |   54 +
 libavfilter/buffersink.h                  |    7 +
 libavfilter/buffersrc.c                   |   72 ++
 libavfilter/buffersrc.h                   |    1 +
 libavfilter/formats.c                     |   16 +
 libavfilter/formats.h                     |    3 +
 libavfilter/internal.h                    |   19 +-
 libavfilter/sf_graphicsub2text.c          | 1137 +++++++++++++++++++
 libavfilter/sf_snull.c                    |   50 +
 libavfilter/sf_splitcc.c                  |  395 +++++++
 libavfilter/sf_stripstyles.c              |  237 ++++
 libavfilter/sf_subfeed.c                  |  412 +++++++
 libavfilter/sf_subscale.c                 |  884 +++++++++++++++
 libavfilter/sf_text2graphicsub.c          |  634 +++++++++++
 libavfilter/sf_textmod.c                  |  710 ++++++++++++
 libavfilter/subtitles.c                   |   63 ++
 libavfilter/subtitles.h                   |   44 +
 libavfilter/trim.c                        |   60 +-
 libavfilter/vf_overlaygraphicsubs.c       |  765 +++++++++++++
 libavfilter/vf_overlaytextsubs.c          |  680 +++++++++++
 libavfilter/vf_subtitles.c                |   67 +-
 libavutil/Makefile                        |    4 +
 {libavcodec => libavutil}/ass.c           |  115 +-
 libavutil/ass_internal.h                  |  135 +++
 {libavcodec => libavutil}/ass_split.c     |  179 ++-
 libavutil/ass_split_internal.h            |  254 +++++
 libavutil/frame.c                         |  206 +++-
 libavutil/frame.h                         |   85 +-
 libavutil/subfmt.c                        |   45 +
 libavutil/subfmt.h                        |  115 ++
 libavutil/version.h                       |    1 +
 tests/ref/fate/filter-overlay-dvdsub-2397 |  182 +--
 tests/ref/fate/sub-dvb                    |  162 +--
 tests/ref/fate/sub-scc                    |    1 -
 tests/ref/fate/sub2video                  | 1091 +++++++++++++++++-
 tests/ref/fate/sub2video_basic            | 1238 +++++++++++++++++++--
 tests/ref/fate/sub2video_time_limited     |   78 +-
 88 files changed, 12424 insertions(+), 1604 deletions(-)
 delete mode 100644 libavcodec/ass_split.h
 create mode 100644 libavfilter/sf_graphicsub2text.c
 create mode 100644 libavfilter/sf_snull.c
 create mode 100644 libavfilter/sf_splitcc.c
 create mode 100644 libavfilter/sf_stripstyles.c
 create mode 100644 libavfilter/sf_subfeed.c
 create mode 100644 libavfilter/sf_subscale.c
 create mode 100644 libavfilter/sf_text2graphicsub.c
 create mode 100644 libavfilter/sf_textmod.c
 create mode 100644 libavfilter/subtitles.c
 create mode 100644 libavfilter/subtitles.h
 create mode 100644 libavfilter/vf_overlaygraphicsubs.c
 create mode 100644 libavfilter/vf_overlaytextsubs.c
 rename {libavcodec => libavutil}/ass.c (59%)
 create mode 100644 libavutil/ass_internal.h
 rename {libavcodec => libavutil}/ass_split.c (71%)
 create mode 100644 libavutil/ass_split_internal.h
 create mode 100644 libavutil/subfmt.c
 create mode 100644 libavutil/subfmt.h


base-commit: 6a82412bf33108111eb3f63076fd5a51349ae114
Published-As: https://github.com/ffstaging/FFmpeg/releases/tag/pr-ffstaging-18%2Fsoftworkz%2Fsubmit_subfiltering-v6
Fetch-It-Via: git fetch https://github.com/ffstaging/FFmpeg pr-ffstaging-18/softworkz/submit_subfiltering-v6
Pull-Request: https://github.com/ffstaging/FFmpeg/pull/18

Range-diff vs v5:

  1:  aa32b9048f =  1:  aa32b9048f avcodec,avutil: Move enum AVSubtitleType to avutil, add new and deprecate old values
  2:  d5ab9d1919 =  2:  d5ab9d1919 avutil/frame: Prepare AVFrame for subtitle handling
  3:  0a685a6b19 =  3:  0a685a6b19 avcodec/subtitles: Introduce new frame-based subtitle decoding API
  4:  0b69b1ce19 =  4:  0b69b1ce19 avcodec/libzvbi: set subtitle type
  5:  0c2091e57c =  5:  0c2091e57c avfilter/subtitles: Update vf_subtitles to use new decoding api
  6:  4903cdd1cd =  6:  4903cdd1cd avcodec,avutil: Move ass helper functions to avutil as avpriv_ and extend ass dialog parsing
  7:  98f12ad7e9 =  7:  98f12ad7e9 avcodec/subtitles: Replace deprecated enum values
  8:  12c8a308d3 =  8:  12c8a308d3 fftools/play,probe: Adjust for subtitle changes
  9:  2e55dbe180 =  9:  2e55dbe180 avfilter/subtitles: Add subtitles.c for subtitle frame allocation
 10:  c931041103 ! 10:  0d953dedcb avfilter/avfilter: Handle subtitle frames
     @@ libavfilter/avfilter.c: static void tlog_ref(void *ctx, AVFrame *ref, int end)
           }
       
           ff_tlog(ctx, "]%s", end ? "\n" : "");
     +@@ libavfilter/avfilter.c: int avfilter_config_links(AVFilterContext *filter)
     + 
     +                 if (!link->time_base.num && !link->time_base.den)
     +                     link->time_base = (AVRational) {1, link->sample_rate};
     ++
     ++                break;
     ++
     ++            case AVMEDIA_TYPE_SUBTITLE:
     ++                if (!link->time_base.num && !link->time_base.den)
     ++                    link->time_base = inlink ? inlink->time_base : AV_TIME_BASE_Q;
     ++
     ++                break;
     +             }
     + 
     +             if (link->src->nb_inputs && link->src->inputs[0]->hw_frames_ctx &&
      @@ libavfilter/avfilter.c: int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
                   av_assert1(frame->width               == link->w);
                   av_assert1(frame->height               == link->h);
 11:  36cab55ff2 = 11:  b462fa2c2f avfilter/avfilter: Fix hardcoded input index
 12:  f41070479c = 12:  fcabb53750 avfilter/sbuffer: Add sbuffersrc and sbuffersink filters
 13:  9bfaba4ace = 13:  9e16dbcecd avfilter/overlaygraphicsubs: Add overlaygraphicsubs and graphicsub2video filters
 14:  918fd9aaf5 = 14:  a17048cfff avfilter/overlaytextsubs: Add overlaytextsubs and textsubs2video filters
 15:  a361ad35c5 = 15:  6330a337b2 avfilter/textmod: Add textmod, censor and show_speaker filters
 16:  bca90ebc3e = 16:  732e2fbf7d avfilter/stripstyles: Add stripstyles filter
 17:  6e488e495f = 17:  4df0d12130 avfilter/splitcc: Add splitcc filter for closed caption handling
 18:  1057dff7da = 18:  27bf505078 avfilter/graphicsub2text: Add new graphicsub2text filter (OCR)
 19:  4e85fb5d2f = 19:  8b98a32895 avfilter/subscale: Add filter for scaling and/or re-arranging graphical subtitles
 20:  88e8adb889 = 20:  de1d1db41c avfilter/subfeed: add subtitle feed filter
 21:  a96bb5c788 ! 21:  f33df64eb4 avfilter/text2graphicsub: Added text2graphicsub subtitle filter
     @@
       ## Metadata ##
     -Author: tcoza <traian.coza@gmail.com>
     +Author: softworkz <softworkz@hotmail.com>
      
       ## Commit message ##
          avfilter/text2graphicsub: Added text2graphicsub subtitle filter
     @@ libavfilter/sf_text2graphicsub.c (new)
      +static void free_palettizecontext(PalettizeContext **palettizecontext)
      +{
      +    PalettizeContext *context = *palettizecontext;
     -+    av_freep(&context->codebook);
     -+    av_freep(&context->codeword);
     -+    av_freep(&context->codeword_closest_codebook_idxs);
     -+    avpriv_elbg_free(&context->elbg);
     -+    av_free(context);
     -+    *palettizecontext = NULL;
     ++    if (context) {
     ++        av_freep(&context->codebook);
     ++        av_freep(&context->codeword);
     ++        av_freep(&context->codeword_closest_codebook_idxs);
     ++        avpriv_elbg_free(&context->elbg);
     ++        av_free(context);
     ++        *palettizecontext = NULL;
     ++    }
      +}
      +
      +/* libass supports a log level ranging from 0 to 7 */
     @@ libavfilter/sf_text2graphicsub.c (new)
      +    AVFilterContext *ctx = outlink->src;
      +    Text2GraphicSubContext *context = ctx->priv;
      +
     ++    outlink->time_base = AV_TIME_BASE_Q;
     ++    outlink->format = AV_SUBTITLE_FMT_BITMAP;
      +    outlink->w = context->size.width;
      +    outlink->h = context->size.height;
      +
 22:  c4922f8466 ! 22:  22d81747d1 avfilter/snull,strim: Add snull and strim filters
     @@ libavfilter/trim.c: const AVFilter ff_af_atrim = {
      +
      +#if CONFIG_STRIM_FILTER
      +
     ++static int sconfig_output(AVFilterLink *outlink)
     ++{
     ++    AVFilterContext *ctx = outlink->src;
     ++    AVFilterLink *inlink = ctx->inputs[0];
     ++
     ++    outlink->format = inlink->format;
     ++    outlink->w = inlink->w;
     ++    outlink->h = inlink->h;
     ++
     ++    return 0;
     ++}
     ++
     ++
      +#define FLAGS (AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
      +static const AVOption strim_options[] = {
      +    COMMON_OPTS
     @@ libavfilter/trim.c: const AVFilter ff_af_atrim = {
      +    {
      +        .name         = "default",
      +        .type         = AVMEDIA_TYPE_SUBTITLE,
     ++        .config_props = sconfig_output,
      +    },
      +};
      +
 23:  848f84d5dc = 23:  6d8532d73d avcodec/subtitles: Migrate subtitle encoders to frame-based API
 24:  2645a1a842 = 24:  1e2fc0d09f fftools/ffmpeg: Introduce subtitle filtering and new frame-based subtitle encoding
 25:  a90a6e1086 = 25:  61f775e35f avcodec/dvbsubdec: Fix conditions for fallback to default resolution

-- 
ffmpeg-codebot
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v6 01/25] avcodec, avutil: Move enum AVSubtitleType to avutil, add new and deprecate old values
  2022-06-26 16:34         ` [FFmpeg-devel] [PATCH v6 00/25] Subtitle Filtering 2022 ffmpegagent
@ 2022-06-26 16:34           ` softworkz
  2022-06-26 16:34           ` [FFmpeg-devel] [PATCH v6 02/25] avutil/frame: Prepare AVFrame for subtitle handling softworkz
                             ` (23 subsequent siblings)
  24 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-26 16:34 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/avcodec.h | 19 +------------
 libavutil/Makefile   |  1 +
 libavutil/subfmt.h   | 68 ++++++++++++++++++++++++++++++++++++++++++++
 libavutil/version.h  |  1 +
 4 files changed, 71 insertions(+), 18 deletions(-)
 create mode 100644 libavutil/subfmt.h

diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index 4dae23d06e..56d551f92d 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -35,6 +35,7 @@
 #include "libavutil/frame.h"
 #include "libavutil/log.h"
 #include "libavutil/pixfmt.h"
+#include "libavutil/subfmt.h"
 #include "libavutil/rational.h"
 
 #include "codec.h"
@@ -2255,24 +2256,6 @@ typedef struct AVHWAccel {
  * @}
  */
 
-enum AVSubtitleType {
-    SUBTITLE_NONE,
-
-    SUBTITLE_BITMAP,                ///< A bitmap, pict will be set
-
-    /**
-     * Plain text, the text field must be set by the decoder and is
-     * authoritative. ass and pict fields may contain approximations.
-     */
-    SUBTITLE_TEXT,
-
-    /**
-     * Formatted text, the ass field must be set by the decoder and is
-     * authoritative. pict and text fields may contain approximations.
-     */
-    SUBTITLE_ASS,
-};
-
 #define AV_SUBTITLE_FLAG_FORCED 0x00000001
 
 typedef struct AVSubtitleRect {
diff --git a/libavutil/Makefile b/libavutil/Makefile
index 29c170214c..edec708ff5 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -77,6 +77,7 @@ HEADERS = adler32.h                                                     \
           sha512.h                                                      \
           spherical.h                                                   \
           stereo3d.h                                                    \
+          subfmt.h                                                      \
           threadmessage.h                                               \
           time.h                                                        \
           timecode.h                                                    \
diff --git a/libavutil/subfmt.h b/libavutil/subfmt.h
new file mode 100644
index 0000000000..791b45519f
--- /dev/null
+++ b/libavutil/subfmt.h
@@ -0,0 +1,68 @@
+/*
+ * 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
+ */
+
+#ifndef AVUTIL_SUBFMT_H
+#define AVUTIL_SUBFMT_H
+
+#include "version.h"
+
+enum AVSubtitleType {
+
+    /**
+     * Subtitle format unknown.
+     */
+    AV_SUBTITLE_FMT_NONE = -1,
+
+    /**
+     * Subtitle format unknown.
+     */
+    AV_SUBTITLE_FMT_UNKNOWN = 0,
+#if FF_API_OLD_SUBTITLES
+    SUBTITLE_NONE = 0,          ///< Deprecated, use AV_SUBTITLE_FMT_NONE instead.
+#endif
+
+    /**
+     * Bitmap area in AVSubtitleRect.data, pixfmt AV_PIX_FMT_PAL8.
+     */
+    AV_SUBTITLE_FMT_BITMAP = 1,
+#if FF_API_OLD_SUBTITLES
+    SUBTITLE_BITMAP = 1,        ///< Deprecated, use AV_SUBTITLE_FMT_BITMAP instead.
+#endif
+
+    /**
+     * Plain text in AVSubtitleRect.text.
+     */
+    AV_SUBTITLE_FMT_TEXT = 2,
+#if FF_API_OLD_SUBTITLES
+    SUBTITLE_TEXT = 2,          ///< Deprecated, use AV_SUBTITLE_FMT_TEXT instead.
+#endif
+
+    /**
+     * Text Formatted as per ASS specification, contained AVSubtitleRect.ass.
+     */
+    AV_SUBTITLE_FMT_ASS = 3,
+#if FF_API_OLD_SUBTITLES
+    SUBTITLE_ASS = 3,           ///< Deprecated, use AV_SUBTITLE_FMT_ASS instead.
+#endif
+
+    AV_SUBTITLE_FMT_NB,         ///< number of subtitle formats, DO NOT USE THIS if you want to link with shared libav* because the number of formats might differ between versions.
+};
+
+#endif /* AVUTIL_SUBFMT_H */
diff --git a/libavutil/version.h b/libavutil/version.h
index 2e9e02dda8..f9f84801a3 100644
--- a/libavutil/version.h
+++ b/libavutil/version.h
@@ -114,6 +114,7 @@
 #define FF_API_XVMC                     (LIBAVUTIL_VERSION_MAJOR < 58)
 #define FF_API_OLD_CHANNEL_LAYOUT       (LIBAVUTIL_VERSION_MAJOR < 58)
 #define FF_API_AV_FOPEN_UTF8            (LIBAVUTIL_VERSION_MAJOR < 58)
+#define FF_API_OLD_SUBTITLES            (LIBAVUTIL_VERSION_MAJOR < 58)
 
 /**
  * @}
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v6 02/25] avutil/frame: Prepare AVFrame for subtitle handling
  2022-06-26 16:34         ` [FFmpeg-devel] [PATCH v6 00/25] Subtitle Filtering 2022 ffmpegagent
  2022-06-26 16:34           ` [FFmpeg-devel] [PATCH v6 01/25] avcodec, avutil: Move enum AVSubtitleType to avutil, add new and deprecate old values softworkz
@ 2022-06-26 16:34           ` softworkz
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 03/25] avcodec/subtitles: Introduce new frame-based subtitle decoding API softworkz
                             ` (22 subsequent siblings)
  24 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-26 16:34 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Root commit for adding subtitle filtering capabilities.
In detail:

- Add type (AVMediaType) field to AVFrame
  Replaces previous way of distinction which was based on checking
  width and height to determine whether a frame is audio or video
- Add subtitle fields to AVFrame
- Add new struct AVSubtitleArea, similar to AVSubtitleRect, but
  different allocation logic. Cannot and must not be used
  interchangeably, hence the new struct

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavutil/Makefile |   1 +
 libavutil/frame.c  | 206 +++++++++++++++++++++++++++++++++++++++++----
 libavutil/frame.h  |  85 ++++++++++++++++++-
 libavutil/subfmt.c |  45 ++++++++++
 libavutil/subfmt.h |  47 +++++++++++
 5 files changed, 363 insertions(+), 21 deletions(-)
 create mode 100644 libavutil/subfmt.c

diff --git a/libavutil/Makefile b/libavutil/Makefile
index edec708ff5..48f78c81e5 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -165,6 +165,7 @@ OBJS = adler32.o                                                        \
        slicethread.o                                                    \
        spherical.o                                                      \
        stereo3d.o                                                       \
+       subfmt.o                                                         \
        threadmessage.o                                                  \
        time.o                                                           \
        timecode.o                                                       \
diff --git a/libavutil/frame.c b/libavutil/frame.c
index 4c16488c66..eadeabd926 100644
--- a/libavutil/frame.c
+++ b/libavutil/frame.c
@@ -26,6 +26,7 @@
 #include "imgutils.h"
 #include "mem.h"
 #include "samplefmt.h"
+#include "subfmt.h"
 #include "hwcontext.h"
 
 #if FF_API_OLD_CHANNEL_LAYOUT
@@ -52,6 +53,9 @@ const char *av_get_colorspace_name(enum AVColorSpace val)
     return name[val];
 }
 #endif
+
+static int frame_copy_subtitles(AVFrame *dst, const AVFrame *src, int copy_data);
+
 static void get_frame_defaults(AVFrame *frame)
 {
     memset(frame, 0, sizeof(*frame));
@@ -72,7 +76,12 @@ static void get_frame_defaults(AVFrame *frame)
     frame->colorspace          = AVCOL_SPC_UNSPECIFIED;
     frame->color_range         = AVCOL_RANGE_UNSPECIFIED;
     frame->chroma_location     = AVCHROMA_LOC_UNSPECIFIED;
-    frame->flags               = 0;
+    frame->num_subtitle_areas  = 0;
+    frame->subtitle_areas      = NULL;
+    frame->subtitle_header     = NULL;
+    frame->repeat_sub          = 0;
+    frame->subtitle_timing.start_pts = 0;
+    frame->subtitle_timing.duration  = 0;
 }
 
 static void free_side_data(AVFrameSideData **ptr_sd)
@@ -251,6 +260,23 @@ FF_ENABLE_DEPRECATION_WARNINGS
 
 }
 
+static int get_subtitle_buffer(AVFrame *frame)
+{
+    // Buffers in AVFrame->buf[] are not used in case of subtitle frames.
+    // To accomodate with existing code, checking ->buf[0] to determine
+    // whether a frame is ref-counted or has data, we're adding a 1-byte
+    // buffer here, which marks the subtitle frame to contain data.
+    frame->buf[0] = av_buffer_alloc(1);
+    if (!frame->buf[0]) {
+        av_frame_unref(frame);
+        return AVERROR(ENOMEM);
+    }
+
+    frame->extended_data = frame->data;
+
+    return 0;
+}
+
 int av_frame_get_buffer(AVFrame *frame, int align)
 {
     if (frame->format < 0)
@@ -258,23 +284,41 @@ int av_frame_get_buffer(AVFrame *frame, int align)
 
 FF_DISABLE_DEPRECATION_WARNINGS
     if (frame->width > 0 && frame->height > 0)
-        return get_video_buffer(frame, align);
+        frame->type = AVMEDIA_TYPE_VIDEO;
     else if (frame->nb_samples > 0 &&
              (av_channel_layout_check(&frame->ch_layout)
 #if FF_API_OLD_CHANNEL_LAYOUT
               || frame->channel_layout || frame->channels > 0
 #endif
              ))
-        return get_audio_buffer(frame, align);
+        frame->type = AVMEDIA_TYPE_AUDIO;
 FF_ENABLE_DEPRECATION_WARNINGS
 
-    return AVERROR(EINVAL);
+    return av_frame_get_buffer2(frame, align);
+}
+
+int av_frame_get_buffer2(AVFrame *frame, int align)
+{
+    if (frame->format < 0)
+        return AVERROR(EINVAL);
+
+    switch (frame->type) {
+    case AVMEDIA_TYPE_VIDEO:
+        return get_video_buffer(frame, align);
+    case AVMEDIA_TYPE_AUDIO:
+        return get_audio_buffer(frame, align);
+    case AVMEDIA_TYPE_SUBTITLE:
+        return get_subtitle_buffer(frame);
+    default:
+        return AVERROR(EINVAL);
+    }
 }
 
 static int frame_copy_props(AVFrame *dst, const AVFrame *src, int force_copy)
 {
     int ret, i;
 
+    dst->type                   = src->type;
     dst->key_frame              = src->key_frame;
     dst->pict_type              = src->pict_type;
     dst->sample_aspect_ratio    = src->sample_aspect_ratio;
@@ -306,6 +350,12 @@ static int frame_copy_props(AVFrame *dst, const AVFrame *src, int force_copy)
     dst->colorspace             = src->colorspace;
     dst->color_range            = src->color_range;
     dst->chroma_location        = src->chroma_location;
+    dst->repeat_sub             = src->repeat_sub;
+    dst->subtitle_timing.start_pts = src->subtitle_timing.start_pts;
+    dst->subtitle_timing.duration  = src->subtitle_timing.duration;
+
+    if ((ret = av_buffer_replace(&dst->subtitle_header, src->subtitle_header)) < 0)
+        return ret;
 
     av_dict_copy(&dst->metadata, src->metadata, 0);
 
@@ -353,6 +403,7 @@ FF_ENABLE_DEPRECATION_WARNINGS
     av_assert1(dst->ch_layout.nb_channels == 0 &&
                dst->ch_layout.order == AV_CHANNEL_ORDER_UNSPEC);
 
+    dst->type           = src->type;
     dst->format         = src->format;
     dst->width          = src->width;
     dst->height         = src->height;
@@ -385,7 +436,7 @@ FF_ENABLE_DEPRECATION_WARNINGS
 
     /* duplicate the frame data if it's not refcounted */
     if (!src->buf[0]) {
-        ret = av_frame_get_buffer(dst, 0);
+        ret = av_frame_get_buffer2(dst, 0);
         if (ret < 0)
             goto fail;
 
@@ -407,6 +458,10 @@ FF_ENABLE_DEPRECATION_WARNINGS
         }
     }
 
+    /* copy subtitle areas */
+    if (src->type == AVMEDIA_TYPE_SUBTITLE)
+        frame_copy_subtitles(dst, src, 0);
+
     if (src->extended_buf) {
         dst->extended_buf = av_calloc(src->nb_extended_buf,
                                       sizeof(*dst->extended_buf));
@@ -476,7 +531,7 @@ AVFrame *av_frame_clone(const AVFrame *src)
 
 void av_frame_unref(AVFrame *frame)
 {
-    int i;
+    unsigned i, n;
 
     if (!frame)
         return;
@@ -495,6 +550,21 @@ void av_frame_unref(AVFrame *frame)
     av_buffer_unref(&frame->opaque_ref);
     av_buffer_unref(&frame->private_ref);
 
+    av_buffer_unref(&frame->subtitle_header);
+
+    for (i = 0; i < frame->num_subtitle_areas; i++) {
+        AVSubtitleArea *area = frame->subtitle_areas[i];
+
+        for (n = 0; n < FF_ARRAY_ELEMS(area->buf); n++)
+            av_buffer_unref(&area->buf[n]);
+
+        av_freep(&area->text);
+        av_freep(&area->ass);
+        av_free(area);
+    }
+
+    av_freep(&frame->subtitle_areas);
+
     if (frame->extended_data != frame->data)
         av_freep(&frame->extended_data);
 
@@ -522,18 +592,28 @@ FF_ENABLE_DEPRECATION_WARNINGS
 
 int av_frame_is_writable(AVFrame *frame)
 {
-    int i, ret = 1;
+    AVSubtitleArea *area;
+    int ret = 1;
 
     /* assume non-refcounted frames are not writable */
     if (!frame->buf[0])
         return 0;
 
-    for (i = 0; i < FF_ARRAY_ELEMS(frame->buf); i++)
+    for (unsigned i = 0; i < FF_ARRAY_ELEMS(frame->buf); i++)
         if (frame->buf[i])
             ret &= !!av_buffer_is_writable(frame->buf[i]);
-    for (i = 0; i < frame->nb_extended_buf; i++)
+    for (unsigned i = 0; i < frame->nb_extended_buf; i++)
         ret &= !!av_buffer_is_writable(frame->extended_buf[i]);
 
+    for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+        area = frame->subtitle_areas[i];
+        if (area) {
+            for (unsigned n = 0; n < FF_ARRAY_ELEMS(area->buf); n++)
+                if (area->buf[n])
+                    ret &= !!av_buffer_is_writable(area->buf[n]);
+            }
+    }
+
     return ret;
 }
 
@@ -549,6 +629,7 @@ int av_frame_make_writable(AVFrame *frame)
         return 0;
 
     memset(&tmp, 0, sizeof(tmp));
+    tmp.type           = frame->type;
     tmp.format         = frame->format;
     tmp.width          = frame->width;
     tmp.height         = frame->height;
@@ -568,7 +649,7 @@ FF_ENABLE_DEPRECATION_WARNINGS
     if (frame->hw_frames_ctx)
         ret = av_hwframe_get_buffer(frame->hw_frames_ctx, &tmp, 0);
     else
-        ret = av_frame_get_buffer(&tmp, 0);
+        ret = av_frame_get_buffer2(&tmp, 0);
     if (ret < 0)
         return ret;
 
@@ -603,7 +684,12 @@ AVBufferRef *av_frame_get_plane_buffer(AVFrame *frame, int plane)
     uint8_t *data;
     int planes, i;
 
-    if (frame->nb_samples) {
+    switch(frame->type) {
+    case AVMEDIA_TYPE_VIDEO:
+        planes = 4;
+        break;
+    case AVMEDIA_TYPE_AUDIO:
+        {
         int channels = frame->ch_layout.nb_channels;
 
 #if FF_API_OLD_CHANNEL_LAYOUT
@@ -617,8 +703,11 @@ FF_ENABLE_DEPRECATION_WARNINGS
         if (!channels)
             return NULL;
         planes = av_sample_fmt_is_planar(frame->format) ? channels : 1;
-    } else
-        planes = 4;
+        break;
+        }
+    default:
+        return NULL;
+    }
 
     if (plane < 0 || plane >= planes || !frame->extended_data[plane])
         return NULL;
@@ -761,22 +850,103 @@ FF_ENABLE_DEPRECATION_WARNINGS
     return 0;
 }
 
+static int av_subtitle_area2area(AVSubtitleArea *dst, const AVSubtitleArea *src, int copy_data)
+{
+    dst->x         =  src->x;
+    dst->y         =  src->y;
+    dst->w         =  src->w;
+    dst->h         =  src->h;
+    dst->nb_colors =  src->nb_colors;
+    dst->type      =  src->type;
+    dst->flags     =  src->flags;
+
+    switch (dst->type) {
+    case AV_SUBTITLE_FMT_BITMAP:
+
+        for (unsigned i = 0; i < AV_NUM_BUFFER_POINTERS; i++) {
+            if (src->h > 0 && src->w > 0 && src->buf[i]) {
+                dst->buf[0] = av_buffer_ref(src->buf[i]);
+                if (!dst->buf[i])
+                    return AVERROR(ENOMEM);
+
+                if (copy_data) {
+                    const int ret = av_buffer_make_writable(&dst->buf[i]);
+                    if (ret < 0)
+                        return ret;
+                }
+
+                dst->linesize[i] = src->linesize[i];
+            }
+        }
+
+        memcpy(&dst->pal[0], &src->pal[0], sizeof(src->pal[0]) * 256);
+
+        break;
+    case AV_SUBTITLE_FMT_TEXT:
+
+        if (src->text) {
+            dst->text = av_strdup(src->text);
+            if (!dst->text)
+                return AVERROR(ENOMEM);
+        }
+
+        break;
+    case AV_SUBTITLE_FMT_ASS:
+
+        if (src->ass) {
+            dst->ass = av_strdup(src->ass);
+            if (!dst->ass)
+                return AVERROR(ENOMEM);
+        }
+
+        break;
+    default:
+
+        av_log(NULL, AV_LOG_ERROR, "Subtitle area has invalid format: %d", dst->type);
+        return AVERROR(ENOMEM);
+    }
+
+    return 0;
+}
+
+static int frame_copy_subtitles(AVFrame *dst, const AVFrame *src, int copy_data)
+{
+    dst->format = src->format;
+
+    dst->num_subtitle_areas = src->num_subtitle_areas;
+
+    if (src->num_subtitle_areas) {
+        dst->subtitle_areas = av_malloc_array(src->num_subtitle_areas, sizeof(AVSubtitleArea *));
+
+        for (unsigned i = 0; i < src->num_subtitle_areas; i++) {
+            dst->subtitle_areas[i] = av_mallocz(sizeof(AVSubtitleArea));
+            av_subtitle_area2area(dst->subtitle_areas[i], src->subtitle_areas[i], copy_data);
+        }
+    }
+
+    return 0;
+}
+
 int av_frame_copy(AVFrame *dst, const AVFrame *src)
 {
     if (dst->format != src->format || dst->format < 0)
         return AVERROR(EINVAL);
 
-FF_DISABLE_DEPRECATION_WARNINGS
-    if (dst->width > 0 && dst->height > 0)
+    switch(dst->type) {
+    case AVMEDIA_TYPE_VIDEO:
         return frame_copy_video(dst, src);
-    else if (dst->nb_samples > 0 &&
+    case AVMEDIA_TYPE_AUDIO:
+        if (dst->nb_samples > 0 &&
              (av_channel_layout_check(&dst->ch_layout)
 #if FF_API_OLD_CHANNEL_LAYOUT
               || dst->channels > 0
 #endif
             ))
-        return frame_copy_audio(dst, src);
-FF_ENABLE_DEPRECATION_WARNINGS
+            return frame_copy_audio(dst, src);
+        break;
+    case AVMEDIA_TYPE_SUBTITLE:
+        return frame_copy_subtitles(dst, src, 1);
+    }
 
     return AVERROR(EINVAL);
 }
diff --git a/libavutil/frame.h b/libavutil/frame.h
index 33fac2054c..dd36f5b27c 100644
--- a/libavutil/frame.h
+++ b/libavutil/frame.h
@@ -25,7 +25,6 @@
 #ifndef AVUTIL_FRAME_H
 #define AVUTIL_FRAME_H
 
-#include <stddef.h>
 #include <stdint.h>
 
 #include "avutil.h"
@@ -35,6 +34,7 @@
 #include "rational.h"
 #include "samplefmt.h"
 #include "pixfmt.h"
+#include "subfmt.h"
 #include "version.h"
 
 
@@ -293,7 +293,7 @@ typedef struct AVRegionOfInterest {
 } AVRegionOfInterest;
 
 /**
- * This structure describes decoded (raw) audio or video data.
+ * This structure describes decoded (raw) audio, video or subtitle data.
  *
  * AVFrame must be allocated using av_frame_alloc(). Note that this only
  * allocates the AVFrame itself, the buffers for the data must be managed
@@ -407,7 +407,7 @@ typedef struct AVFrame {
     /**
      * format of the frame, -1 if unknown or unset
      * Values correspond to enum AVPixelFormat for video frames,
-     * enum AVSampleFormat for audio)
+     * enum AVSampleFormat for audio, AVSubtitleType for subtitles)
      */
     int format;
 
@@ -702,6 +702,53 @@ typedef struct AVFrame {
      * Channel layout of the audio data.
      */
     AVChannelLayout ch_layout;
+
+    /**
+     * Media type of the frame (audio, video, subtitles..)
+     *
+     * See AVMEDIA_TYPE_xxx
+     */
+    enum AVMediaType type;
+
+    /**
+     * Number of items in the @ref subtitle_areas array.
+     */
+    unsigned num_subtitle_areas;
+
+    /**
+     * Array of subtitle areas, may be empty.
+     */
+    AVSubtitleArea **subtitle_areas;
+
+    /**
+     * Header containing style information for text subtitles.
+     */
+    AVBufferRef *subtitle_header;
+
+    /**
+     * Indicates that a subtitle frame is a repeated frame for arbitrating flow
+     * in a filter graph.
+     * The field subtitle_timing.start_pts always indicates the original presentation
+     * time, while the frame's pts field may be different.
+     */
+    int repeat_sub;
+
+    struct SubtitleTiming
+    {
+        /**
+         * The display start time, in AV_TIME_BASE.
+         *
+         * For subtitle frames, AVFrame.pts is populated from the packet pts value,
+         * which is not always the same as this value.
+         */
+        int64_t start_pts;
+
+        /**
+         * Display duration, in AV_TIME_BASE.
+         */
+        int64_t duration;
+
+    } subtitle_timing;
 } AVFrame;
 
 
@@ -778,6 +825,8 @@ void av_frame_move_ref(AVFrame *dst, AVFrame *src);
 /**
  * Allocate new buffer(s) for audio or video data.
  *
+ * Note: For subtitle data, use av_frame_get_buffer2
+ *
  * The following fields must be set on frame before calling this function:
  * - format (pixel format for video, sample format for audio)
  * - width and height for video
@@ -797,9 +846,39 @@ void av_frame_move_ref(AVFrame *dst, AVFrame *src);
  *              recommended to pass 0 here unless you know what you are doing.
  *
  * @return 0 on success, a negative AVERROR on error.
+ *
+ * @deprecated Use @ref av_frame_get_buffer2 instead and set @ref AVFrame.type
+ * before calling.
  */
+attribute_deprecated
 int av_frame_get_buffer(AVFrame *frame, int align);
 
+/**
+ * Allocate new buffer(s) for audio, video or subtitle data.
+ *
+ * The following fields must be set on frame before calling this function:
+ * - format (pixel format for video, sample format for audio)
+ * - width and height for video
+ * - nb_samples and channel_layout for audio
+ * - type (AVMediaType)
+ *
+ * This function will fill AVFrame.data and AVFrame.buf arrays and, if
+ * necessary, allocate and fill AVFrame.extended_data and AVFrame.extended_buf.
+ * For planar formats, one buffer will be allocated for each plane.
+ *
+ * @warning: if frame already has been allocated, calling this function will
+ *           leak memory. In addition, undefined behavior can occur in certain
+ *           cases.
+ *
+ * @param frame frame in which to store the new buffers.
+ * @param align Required buffer size alignment. If equal to 0, alignment will be
+ *              chosen automatically for the current CPU. It is highly
+ *              recommended to pass 0 here unless you know what you are doing.
+ *
+ * @return 0 on success, a negative AVERROR on error.
+ */
+int av_frame_get_buffer2(AVFrame *frame, int align);
+
 /**
  * Check if the frame data is writable.
  *
diff --git a/libavutil/subfmt.c b/libavutil/subfmt.c
new file mode 100644
index 0000000000..c72ebe2a43
--- /dev/null
+++ b/libavutil/subfmt.c
@@ -0,0 +1,45 @@
+/*
+ * 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
+ */
+
+#include <string.h>
+#include "common.h"
+#include "subfmt.h"
+
+static const char sub_fmt_info[AV_SUBTITLE_FMT_NB][24] = {
+    [AV_SUBTITLE_FMT_UNKNOWN] = "Unknown subtitle format",
+    [AV_SUBTITLE_FMT_BITMAP]  = "Graphical subtitles",
+    [AV_SUBTITLE_FMT_TEXT]    = "Text subtitles (plain)",
+    [AV_SUBTITLE_FMT_ASS]     = "Text subtitles (ass)",
+};
+
+const char *av_get_subtitle_fmt_name(enum AVSubtitleType sub_fmt)
+{
+    if (sub_fmt < 0 || sub_fmt >= AV_SUBTITLE_FMT_NB)
+        return NULL;
+    return sub_fmt_info[sub_fmt];
+}
+
+enum AVSubtitleType av_get_subtitle_fmt(const char *name)
+{
+    for (int i = 0; i < AV_SUBTITLE_FMT_NB; i++)
+        if (!strcmp(sub_fmt_info[i], name))
+            return i;
+    return AV_SUBTITLE_FMT_NONE;
+}
diff --git a/libavutil/subfmt.h b/libavutil/subfmt.h
index 791b45519f..3b8a0613b2 100644
--- a/libavutil/subfmt.h
+++ b/libavutil/subfmt.h
@@ -21,6 +21,9 @@
 #ifndef AVUTIL_SUBFMT_H
 #define AVUTIL_SUBFMT_H
 
+#include <stdint.h>
+
+#include "buffer.h"
 #include "version.h"
 
 enum AVSubtitleType {
@@ -65,4 +68,48 @@ enum AVSubtitleType {
     AV_SUBTITLE_FMT_NB,         ///< number of subtitle formats, DO NOT USE THIS if you want to link with shared libav* because the number of formats might differ between versions.
 };
 
+typedef struct AVSubtitleArea {
+#define AV_NUM_BUFFER_POINTERS 1
+
+    enum AVSubtitleType type;
+    int flags;
+
+    int x;         ///< top left corner  of area.
+    int y;         ///< top left corner  of area.
+    int w;         ///< width            of area.
+    int h;         ///< height           of area.
+    int nb_colors; ///< number of colors in bitmap palette (@ref pal).
+
+    /**
+     * Buffers and line sizes for the bitmap of this subtitle.
+     *
+     * @{
+     */
+    AVBufferRef *buf[AV_NUM_BUFFER_POINTERS];
+    int linesize[AV_NUM_BUFFER_POINTERS];
+    /**
+     * @}
+     */
+
+    uint32_t pal[256]; ///< RGBA palette for the bitmap.
+
+    char *text;        ///< 0-terminated plain UTF-8 text
+    char *ass;         ///< 0-terminated ASS/SSA compatible event line.
+
+} AVSubtitleArea;
+
+/**
+ * Return the name of sub_fmt, or NULL if sub_fmt is not
+ * recognized.
+ */
+const char *av_get_subtitle_fmt_name(enum AVSubtitleType sub_fmt);
+
+/**
+ * Return a subtitle format corresponding to name, or AV_SUBTITLE_FMT_NONE
+ * on error.
+ *
+ * @param name Subtitle format name.
+ */
+enum AVSubtitleType av_get_subtitle_fmt(const char *name);
+
 #endif /* AVUTIL_SUBFMT_H */
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v6 03/25] avcodec/subtitles: Introduce new frame-based subtitle decoding API
  2022-06-26 16:34         ` [FFmpeg-devel] [PATCH v6 00/25] Subtitle Filtering 2022 ffmpegagent
  2022-06-26 16:34           ` [FFmpeg-devel] [PATCH v6 01/25] avcodec, avutil: Move enum AVSubtitleType to avutil, add new and deprecate old values softworkz
  2022-06-26 16:34           ` [FFmpeg-devel] [PATCH v6 02/25] avutil/frame: Prepare AVFrame for subtitle handling softworkz
@ 2022-06-26 16:35           ` softworkz
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 04/25] avcodec/libzvbi: set subtitle type softworkz
                             ` (21 subsequent siblings)
  24 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-26 16:35 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

- Modify avcodec_send_packet() to support subtitles via the regular
  frame based decoding API
- Add decode_subtitle_shim() which takes subtitle frames,
  and serves as a compatibility shim to the legacy subtitle decoding
  API until all subtitle decoders are migrated to the frame-based API
- Add additional methods for conversion between old and new API

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/avcodec.c  |   8 ++
 libavcodec/avcodec.h  |  10 ++-
 libavcodec/decode.c   |  60 ++++++++++++--
 libavcodec/internal.h |  16 ++++
 libavcodec/utils.c    | 184 ++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 269 insertions(+), 9 deletions(-)

diff --git a/libavcodec/avcodec.c b/libavcodec/avcodec.c
index 5f6e71a39e..0a1d961fc6 100644
--- a/libavcodec/avcodec.c
+++ b/libavcodec/avcodec.c
@@ -358,6 +358,14 @@ FF_DISABLE_DEPRECATION_WARNINGS
 FF_ENABLE_DEPRECATION_WARNINGS
 #endif
 
+        // Set the subtitle type from the codec descriptor in case the decoder hasn't done itself
+        if (avctx->codec_type == AVMEDIA_TYPE_SUBTITLE && avctx->subtitle_type == AV_SUBTITLE_FMT_UNKNOWN) {
+            if(avctx->codec_descriptor->props & AV_CODEC_PROP_BITMAP_SUB)
+                avctx->subtitle_type = AV_SUBTITLE_FMT_BITMAP;
+            if(avctx->codec_descriptor->props & AV_CODEC_PROP_TEXT_SUB)
+                 avctx->subtitle_type = AV_SUBTITLE_FMT_ASS;
+        }
+
 #if FF_API_AVCTX_TIMEBASE
         if (avctx->framerate.num > 0 && avctx->framerate.den > 0)
             avctx->time_base = av_inv_q(av_mul_q(avctx->framerate, (AVRational){avctx->ticks_per_frame, 1}));
diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index 56d551f92d..de87b0406b 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -1698,7 +1698,7 @@ typedef struct AVCodecContext {
 
     /**
      * Header containing style information for text subtitles.
-     * For SUBTITLE_ASS subtitle type, it should contain the whole ASS
+     * For AV_SUBTITLE_FMT_ASS subtitle type, it should contain the whole ASS
      * [Script Info] and [V4+ Styles] section, plus the [Events] line and
      * the Format line following. It shouldn't include any Dialogue line.
      * - encoding: Set/allocated/freed by user (before avcodec_open2())
@@ -2056,6 +2056,8 @@ typedef struct AVCodecContext {
      *             The decoder can then override during decoding as needed.
      */
     AVChannelLayout ch_layout;
+
+    enum AVSubtitleType subtitle_type;
 } AVCodecContext;
 
 /**
@@ -2432,7 +2434,10 @@ int avcodec_close(AVCodecContext *avctx);
  * Free all allocated data in the given subtitle struct.
  *
  * @param sub AVSubtitle to free.
+ *
+ * @deprecated Use the regular frame based encode and decode APIs instead.
  */
+attribute_deprecated
 void avsubtitle_free(AVSubtitle *sub);
 
 /**
@@ -2525,7 +2530,10 @@ enum AVChromaLocation avcodec_chroma_pos_to_enum(int xpos, int ypos);
  *                 must be freed with avsubtitle_free if *got_sub_ptr is set.
  * @param[in,out] got_sub_ptr Zero if no subtitle could be decompressed, otherwise, it is nonzero.
  * @param[in] avpkt The input AVPacket containing the input buffer.
+ *
+ * @deprecated Use the new decode API (avcodec_send_packet, avcodec_receive_frame) instead.
  */
+attribute_deprecated
 int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub,
                             int *got_sub_ptr,
                             AVPacket *avpkt);
diff --git a/libavcodec/decode.c b/libavcodec/decode.c
index 1893caa6a6..e8ca7b6da4 100644
--- a/libavcodec/decode.c
+++ b/libavcodec/decode.c
@@ -573,6 +573,39 @@ static int decode_receive_frame_internal(AVCodecContext *avctx, AVFrame *frame)
     return ret;
 }
 
+static int decode_subtitle2_priv(AVCodecContext *avctx, AVSubtitle *sub,
+                                 int *got_sub_ptr, AVPacket *avpkt);
+
+static int decode_subtitle_shim(AVCodecContext *avctx, AVFrame *frame, AVPacket *avpkt)
+{
+    int ret, got_sub_ptr = 0;
+    AVSubtitle subtitle = { 0 };
+
+    if (frame->buf[0])
+        return AVERROR(EAGAIN);
+
+    av_frame_unref(frame);
+
+    ret = decode_subtitle2_priv(avctx, &subtitle, &got_sub_ptr, avpkt);
+
+    if (ret >= 0 && got_sub_ptr) {
+        frame->type = AVMEDIA_TYPE_SUBTITLE;
+        frame->format = subtitle.format;
+        ret = av_frame_get_buffer2(frame, 0);
+
+        if (ret >= 0)
+            ret = ff_frame_put_subtitle(frame, &subtitle);
+
+        frame->width = avctx->width;
+        frame->height = avctx->height;
+        frame->pkt_dts = avpkt->dts;
+    }
+
+    avsubtitle_free(&subtitle);
+
+    return ret;
+}
+
 int attribute_align_arg avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt)
 {
     AVCodecInternal *avci = avctx->internal;
@@ -587,6 +620,13 @@ int attribute_align_arg avcodec_send_packet(AVCodecContext *avctx, const AVPacke
     if (avpkt && !avpkt->size && avpkt->data)
         return AVERROR(EINVAL);
 
+    if (avctx->codec_type == AVMEDIA_TYPE_SUBTITLE)
+		// this does not exactly implement the avcodec_send_packet/avcodec_receive_frame API
+	    // but we know that no subtitle decoder produces multiple AVSubtitles per packet through
+		// the legacy API, and this will be changed when migrating the subtitle decoders
+		// to the frame based decoding api
+        return decode_subtitle_shim(avctx, avci->buffer_frame, avpkt);
+
     av_packet_unref(avci->buffer_pkt);
     if (avpkt && (avpkt->data || avpkt->side_data_elems)) {
         ret = av_packet_ref(avci->buffer_pkt, avpkt);
@@ -648,7 +688,9 @@ int attribute_align_arg avcodec_receive_frame(AVCodecContext *avctx, AVFrame *fr
 
     if (avci->buffer_frame->buf[0]) {
         av_frame_move_ref(frame, avci->buffer_frame);
-    } else {
+    } else if (avctx->codec_type == AVMEDIA_TYPE_SUBTITLE)
+        return AVERROR(EAGAIN);
+    else {
         ret = decode_receive_frame_internal(avctx, frame);
         if (ret < 0)
             return ret;
@@ -813,9 +855,8 @@ static int utf8_check(const uint8_t *str)
     return 1;
 }
 
-int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub,
-                             int *got_sub_ptr,
-                             AVPacket *avpkt)
+static int decode_subtitle2_priv(AVCodecContext *avctx, AVSubtitle *sub,
+                             int *got_sub_ptr, AVPacket *avpkt)
 {
     int ret = 0;
 
@@ -861,10 +902,7 @@ int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub,
                                                  avctx->pkt_timebase, ms);
         }
 
-        if (avctx->codec_descriptor->props & AV_CODEC_PROP_BITMAP_SUB)
-            sub->format = 0;
-        else if (avctx->codec_descriptor->props & AV_CODEC_PROP_TEXT_SUB)
-            sub->format = 1;
+        sub->format = (uint16_t)avctx->subtitle_type;
 
         for (unsigned i = 0; i < sub->num_rects; i++) {
             if (avctx->sub_charenc_mode != FF_SUB_CHARENC_MODE_IGNORE &&
@@ -885,6 +923,12 @@ int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub,
     return ret;
 }
 
+int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub,
+                             int *got_sub_ptr, AVPacket *avpkt)
+{
+    return decode_subtitle2_priv(avctx, sub, got_sub_ptr, avpkt);
+}
+
 enum AVPixelFormat avcodec_default_get_format(struct AVCodecContext *avctx,
                                               const enum AVPixelFormat *fmt)
 {
diff --git a/libavcodec/internal.h b/libavcodec/internal.h
index 17e1de8127..69656729d8 100644
--- a/libavcodec/internal.h
+++ b/libavcodec/internal.h
@@ -290,4 +290,20 @@ int ff_int_from_list_or_default(void *ctx, const char * val_name, int val,
 
 void ff_dvdsub_parse_palette(uint32_t *palette, const char *p);
 
+/**
+ * Copies subtitle data from AVSubtitle to AVFrame.
+ *
+ * @deprecated This is a compatibility method for interoperability with
+ * the legacy subtitle API.
+ */
+int ff_frame_put_subtitle(AVFrame* frame, const AVSubtitle* sub);
+
+/**
+ * Copies subtitle data from AVFrame to AVSubtitle.
+ *
+ * @deprecated This is a compatibility method for interoperability with
+ * the legacy subtitle API.
+ */
+int ff_frame_get_subtitle(AVSubtitle* sub, AVFrame* frame);
+
 #endif /* AVCODEC_INTERNAL_H */
diff --git a/libavcodec/utils.c b/libavcodec/utils.c
index eb7e505a62..b67b6b6122 100644
--- a/libavcodec/utils.c
+++ b/libavcodec/utils.c
@@ -827,6 +827,190 @@ FF_ENABLE_DEPRECATION_WARNINGS
     return FFMAX(0, duration);
 }
 
+static int subtitle_area2rect(AVSubtitleRect *dst, const AVSubtitleArea *src)
+{
+    dst->x         =  src->x;
+    dst->y         =  src->y;
+    dst->w         =  src->w;
+    dst->h         =  src->h;
+    dst->nb_colors =  src->nb_colors;
+    dst->type      =  src->type;
+    dst->flags     =  src->flags;
+
+    switch (dst->type) {
+    case AV_SUBTITLE_FMT_BITMAP:
+
+        if (src->h > 0 && src->w > 0 && src->buf[0]) {
+            uint32_t *pal;
+            AVBufferRef *buf = src->buf[0];
+            dst->data[0] = av_mallocz(buf->size);
+            memcpy(dst->data[0], buf->data, buf->size);
+            dst->linesize[0] = src->linesize[0];
+
+            dst->data[1] = av_mallocz(256 * 4);
+            pal = (uint32_t *)dst->data[1];
+
+            for (unsigned i = 0; i < 256; i++) {
+                pal[i] = src->pal[i];
+            }
+        }
+
+        break;
+    case AV_SUBTITLE_FMT_TEXT:
+
+        if (src->text)
+            dst->text = av_strdup(src->text);
+        else
+            dst->text = av_strdup("");
+
+        if (!dst->text)
+            return AVERROR(ENOMEM);
+
+        break;
+    case AV_SUBTITLE_FMT_ASS:
+
+        if (src->ass)
+            dst->ass = av_strdup(src->ass);
+        else
+            dst->ass = av_strdup("");
+
+        if (!dst->ass)
+            return AVERROR(ENOMEM);
+
+        break;
+    default:
+
+        av_log(NULL, AV_LOG_ERROR, "Subtitle rect has invalid format: %d", dst->type);
+        return AVERROR(EINVAL);
+    }
+
+    return 0;
+}
+
+static int subtitle_rect2area(AVSubtitleArea *dst, const AVSubtitleRect *src)
+{
+    dst->x         =  src->x;
+    dst->y         =  src->y;
+    dst->w         =  src->w;
+    dst->h         =  src->h;
+    dst->nb_colors =  src->nb_colors;
+    dst->type      =  src->type;
+    dst->flags     =  src->flags;
+
+    switch (dst->type) {
+    case AV_SUBTITLE_FMT_BITMAP:
+
+        if (src->h > 0 && src->w > 0 && src->data[0]) {
+            AVBufferRef *buf = av_buffer_allocz(src->h * src->linesize[0]);
+            memcpy(buf->data, src->data[0], buf->size);
+
+            dst->buf[0] = buf;
+            dst->linesize[0] = src->linesize[0];
+        }
+
+        if (src->data[1]) {
+            uint32_t *pal = (uint32_t *)src->data[1];
+
+            for (unsigned i = 0; i < 256; i++) {
+                dst->pal[i] = pal[i];
+            }
+        }
+
+        break;
+    case AV_SUBTITLE_FMT_TEXT:
+
+        if (src->text) {
+            dst->text = av_strdup(src->text);
+            if (!dst->text)
+                return AVERROR(ENOMEM);
+        }
+
+        break;
+    case AV_SUBTITLE_FMT_ASS:
+
+        if (src->ass) {
+            dst->ass = av_strdup(src->ass);
+            if (!dst->ass)
+                return AVERROR(ENOMEM);
+        }
+
+        break;
+    default:
+
+        av_log(NULL, AV_LOG_ERROR, "Subtitle area has invalid format: %d", dst->type);
+        return AVERROR(EINVAL);
+    }
+
+    return 0;
+}
+
+/**
+ * Copies subtitle data from AVSubtitle (deprecated) to AVFrame
+ *
+ * @note This is a compatibility method for conversion to the legacy API
+ */
+int ff_frame_put_subtitle(AVFrame *frame, const AVSubtitle *sub)
+{
+    frame->format = sub->format;
+    frame->subtitle_timing.start_pts = sub->pts;
+    frame->subtitle_timing.start_pts += av_rescale_q(sub->start_display_time, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q);
+    frame->subtitle_timing.duration = av_rescale_q(sub->end_display_time - sub->start_display_time, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q);
+
+    if (sub->num_rects) {
+        frame->subtitle_areas = av_malloc_array(sub->num_rects, sizeof(AVSubtitleArea*));
+        if (!frame->subtitle_areas)
+            return AVERROR(ENOMEM);
+
+        for (unsigned i = 0; i < sub->num_rects; i++) {
+            int ret;
+            frame->subtitle_areas[i] = av_mallocz(sizeof(AVSubtitleArea));
+            if (!frame->subtitle_areas[i])
+                return AVERROR(ENOMEM);
+            ret = subtitle_rect2area(frame->subtitle_areas[i], sub->rects[i]);
+            if (ret < 0) {
+                frame->num_subtitle_areas = i;
+                return ret;
+            }
+        }
+    }
+
+    frame->num_subtitle_areas = sub->num_rects;
+    return 0;
+}
+
+/**
+ * Copies subtitle data from AVFrame to AVSubtitle (deprecated)
+ *
+ * @note This is a compatibility method for conversion to the legacy API
+ */
+int ff_frame_get_subtitle(AVSubtitle *sub, AVFrame *frame)
+{
+    const int64_t duration_ms = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, (AVRational){ 1, 1000 });
+
+    sub->start_display_time = 0;
+    sub->end_display_time = (int32_t)duration_ms;
+    sub->pts = frame->subtitle_timing.start_pts;
+
+    if (frame->num_subtitle_areas) {
+        sub->rects = av_malloc_array(frame->num_subtitle_areas, sizeof(AVSubtitleRect*));
+        if (!sub->rects)
+            return AVERROR(ENOMEM);
+
+        for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+            int ret;
+            sub->rects[i] = av_mallocz(sizeof(AVSubtitleRect));
+            ret = subtitle_area2rect(sub->rects[i], frame->subtitle_areas[i]);
+            if (ret < 0) {
+                sub->num_rects = i;
+                return ret;
+            }
+        }
+    }
+
+    sub->num_rects = frame->num_subtitle_areas;
+    return 0;
+}
+
 int av_get_audio_frame_duration2(AVCodecParameters *par, int frame_bytes)
 {
    int channels = par->ch_layout.nb_channels;
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v6 04/25] avcodec/libzvbi: set subtitle type
  2022-06-26 16:34         ` [FFmpeg-devel] [PATCH v6 00/25] Subtitle Filtering 2022 ffmpegagent
                             ` (2 preceding siblings ...)
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 03/25] avcodec/subtitles: Introduce new frame-based subtitle decoding API softworkz
@ 2022-06-26 16:35           ` softworkz
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 05/25] avfilter/subtitles: Update vf_subtitles to use new decoding api softworkz
                             ` (20 subsequent siblings)
  24 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-26 16:35 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/libzvbi-teletextdec.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libavcodec/libzvbi-teletextdec.c b/libavcodec/libzvbi-teletextdec.c
index 92466cc11e..2aab10a548 100644
--- a/libavcodec/libzvbi-teletextdec.c
+++ b/libavcodec/libzvbi-teletextdec.c
@@ -751,10 +751,13 @@ static int teletext_init_decoder(AVCodecContext *avctx)
 
     switch (ctx->format_id) {
         case 0:
+            avctx->subtitle_type = AV_SUBTITLE_FMT_BITMAP;
             return 0;
         case 1:
+            avctx->subtitle_type = AV_SUBTITLE_FMT_ASS;
             return ff_ass_subtitle_header_default(avctx);
         case 2:
+            avctx->subtitle_type = AV_SUBTITLE_FMT_ASS;
             return my_ass_subtitle_header(avctx);
     }
     return AVERROR_BUG;
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v6 05/25] avfilter/subtitles: Update vf_subtitles to use new decoding api
  2022-06-26 16:34         ` [FFmpeg-devel] [PATCH v6 00/25] Subtitle Filtering 2022 ffmpegagent
                             ` (3 preceding siblings ...)
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 04/25] avcodec/libzvbi: set subtitle type softworkz
@ 2022-06-26 16:35           ` softworkz
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 06/25] avcodec, avutil: Move ass helper functions to avutil as avpriv_ and extend ass dialog parsing softworkz
                             ` (19 subsequent siblings)
  24 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-26 16:35 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavfilter/vf_subtitles.c | 67 ++++++++++++++++++++++++++++++--------
 1 file changed, 54 insertions(+), 13 deletions(-)

diff --git a/libavfilter/vf_subtitles.c b/libavfilter/vf_subtitles.c
index 82e140e986..0ae156ad07 100644
--- a/libavfilter/vf_subtitles.c
+++ b/libavfilter/vf_subtitles.c
@@ -36,14 +36,12 @@
 # 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"
 
 typedef struct AssContext {
     const AVClass *class;
@@ -304,8 +302,42 @@ static int attachment_is_font(AVStream * st)
     return 0;
 }
 
+static int decode(AVCodecContext *avctx, AVFrame *frame, int *got_frame, AVPacket *pkt)
+{
+    int ret;
+
+    *got_frame = 0;
+
+    if (pkt) {
+        ret = avcodec_send_packet(avctx, pkt);
+        // In particular, we don't expect AVERROR(EAGAIN), because we read all
+        // decoded frames with avcodec_receive_frame() until done.
+        if (ret < 0 && ret != AVERROR_EOF)
+            return ret;
+    }
+
+    ret = avcodec_receive_frame(avctx, frame);
+    if (ret < 0 && ret != AVERROR(EAGAIN))
+        return ret;
+    if (ret >= 0)
+        *got_frame = 1;
+
+    return 0;
+}
+
 AVFILTER_DEFINE_CLASS(subtitles);
 
+static enum AVSubtitleType get_subtitle_format(const AVCodecDescriptor *codec_descriptor)
+{
+    if(codec_descriptor->props & AV_CODEC_PROP_BITMAP_SUB)
+        return AV_SUBTITLE_FMT_BITMAP;
+
+    if(codec_descriptor->props & AV_CODEC_PROP_TEXT_SUB)
+        return AV_SUBTITLE_FMT_ASS;
+
+    return AV_SUBTITLE_FMT_UNKNOWN;
+}
+
 static av_cold int init_subtitles(AVFilterContext *ctx)
 {
     int j, ret, sid;
@@ -318,6 +350,7 @@ static av_cold int init_subtitles(AVFilterContext *ctx)
     AVStream *st;
     AVPacket pkt;
     AssContext *ass = ctx->priv;
+    enum AVSubtitleType subtitle_format;
 
     /* Init libass */
     ret = init(ctx);
@@ -398,13 +431,17 @@ static av_cold int init_subtitles(AVFilterContext *ctx)
         ret = AVERROR_DECODER_NOT_FOUND;
         goto end;
     }
+
     dec_desc = avcodec_descriptor_get(st->codecpar->codec_id);
-    if (dec_desc && !(dec_desc->props & AV_CODEC_PROP_TEXT_SUB)) {
+    subtitle_format = get_subtitle_format(dec_desc);
+
+    if (subtitle_format != AV_SUBTITLE_FMT_ASS) {
         av_log(ctx, AV_LOG_ERROR,
-               "Only text based subtitles are currently supported\n");
-        ret = AVERROR_PATCHWELCOME;
+               "Only text based subtitles are supported by this filter\n");
+        ret = AVERROR_INVALIDDATA;
         goto end;
     }
+
     if (ass->charenc)
         av_dict_set(&codec_opts, "sub_charenc", ass->charenc, 0);
 
@@ -460,27 +497,31 @@ static av_cold int init_subtitles(AVFilterContext *ctx)
                                   dec_ctx->subtitle_header_size);
     while (av_read_frame(fmt, &pkt) >= 0) {
         int i, got_subtitle;
-        AVSubtitle sub = {0};
+        AVFrame *sub = av_frame_alloc();
+        if (!sub) {
+            ret = AVERROR(ENOMEM);
+            goto end;
+        }
 
         if (pkt.stream_index == sid) {
-            ret = avcodec_decode_subtitle2(dec_ctx, &sub, &got_subtitle, &pkt);
+            ret = decode(dec_ctx, sub, &got_subtitle, &pkt);
             if (ret < 0) {
                 av_log(ctx, AV_LOG_WARNING, "Error decoding: %s (ignored)\n",
                        av_err2str(ret));
             } else if (got_subtitle) {
-                const int64_t start_time = av_rescale_q(sub.pts, AV_TIME_BASE_Q, av_make_q(1, 1000));
-                const int64_t duration   = sub.end_display_time;
-                for (i = 0; i < sub.num_rects; i++) {
-                    char *ass_line = sub.rects[i]->ass;
+                const int64_t start_time = av_rescale_q(sub->subtitle_timing.start_pts, AV_TIME_BASE_Q, av_make_q(1, 1000));
+                const int64_t duration   = av_rescale_q(sub->subtitle_timing.duration, AV_TIME_BASE_Q, av_make_q(1, 1000));
+                for (i = 0; i < sub->num_subtitle_areas; i++) {
+                    char *ass_line = sub->subtitle_areas[i]->ass;
                     if (!ass_line)
-                        break;
+                        continue;
                     ass_process_chunk(ass->track, ass_line, strlen(ass_line),
                                       start_time, duration);
                 }
             }
         }
         av_packet_unref(&pkt);
-        avsubtitle_free(&sub);
+        av_frame_free(&sub);
     }
 
 end:
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v6 06/25] avcodec, avutil: Move ass helper functions to avutil as avpriv_ and extend ass dialog parsing
  2022-06-26 16:34         ` [FFmpeg-devel] [PATCH v6 00/25] Subtitle Filtering 2022 ffmpegagent
                             ` (4 preceding siblings ...)
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 05/25] avfilter/subtitles: Update vf_subtitles to use new decoding api softworkz
@ 2022-06-26 16:35           ` softworkz
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 07/25] avcodec/subtitles: Replace deprecated enum values softworkz
                             ` (18 subsequent siblings)
  24 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-26 16:35 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Also add

- hard_space callback (for upcoming fix)
- extensible callback (for future extension)
- new API which allows tag filtering

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/Makefile                   |  56 +++---
 libavcodec/ass.h                      | 151 +++++----------
 libavcodec/ass_split.h                | 191 -------------------
 libavcodec/assdec.c                   |   2 +-
 libavcodec/assenc.c                   |   2 +-
 libavcodec/ccaption_dec.c             |  20 +-
 libavcodec/jacosubdec.c               |   2 +-
 libavcodec/libaribb24.c               |   2 +-
 libavcodec/libzvbi-teletextdec.c      |  14 +-
 libavcodec/microdvddec.c              |   7 +-
 libavcodec/movtextdec.c               |   3 +-
 libavcodec/movtextenc.c               |  20 +-
 libavcodec/mpl2dec.c                  |   2 +-
 libavcodec/realtextdec.c              |   2 +-
 libavcodec/samidec.c                  |   2 +-
 libavcodec/srtdec.c                   |   2 +-
 libavcodec/srtenc.c                   |  16 +-
 libavcodec/subviewerdec.c             |   2 +-
 libavcodec/textdec.c                  |   4 +-
 libavcodec/ttmlenc.c                  |  15 +-
 libavcodec/webvttdec.c                |   2 +-
 libavcodec/webvttenc.c                |  16 +-
 libavutil/Makefile                    |   2 +
 {libavcodec => libavutil}/ass.c       | 115 ++++--------
 libavutil/ass_internal.h              | 135 ++++++++++++++
 {libavcodec => libavutil}/ass_split.c | 179 +++++++++++++++---
 libavutil/ass_split_internal.h        | 254 ++++++++++++++++++++++++++
 27 files changed, 726 insertions(+), 492 deletions(-)
 delete mode 100644 libavcodec/ass_split.h
 rename {libavcodec => libavutil}/ass.c (59%)
 create mode 100644 libavutil/ass_internal.h
 rename {libavcodec => libavutil}/ass_split.c (71%)
 create mode 100644 libavutil/ass_split_internal.h

diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 3b8f7b5e01..4bfc90b6e9 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -221,10 +221,10 @@ OBJS-$(CONFIG_APNG_DECODER)            += png.o pngdec.o pngdsp.o
 OBJS-$(CONFIG_APNG_ENCODER)            += png.o pngenc.o
 OBJS-$(CONFIG_ARBC_DECODER)            += arbc.o
 OBJS-$(CONFIG_ARGO_DECODER)            += argo.o
-OBJS-$(CONFIG_SSA_DECODER)             += assdec.o ass.o
-OBJS-$(CONFIG_SSA_ENCODER)             += assenc.o ass.o
-OBJS-$(CONFIG_ASS_DECODER)             += assdec.o ass.o
-OBJS-$(CONFIG_ASS_ENCODER)             += assenc.o ass.o
+OBJS-$(CONFIG_SSA_DECODER)             += assdec.o
+OBJS-$(CONFIG_SSA_ENCODER)             += assenc.o
+OBJS-$(CONFIG_ASS_DECODER)             += assdec.o
+OBJS-$(CONFIG_ASS_ENCODER)             += assenc.o
 OBJS-$(CONFIG_ASV1_DECODER)            += asvdec.o asv.o mpeg12data.o
 OBJS-$(CONFIG_ASV1_ENCODER)            += asvenc.o asv.o mpeg12data.o
 OBJS-$(CONFIG_ASV2_DECODER)            += asvdec.o asv.o mpeg12data.o
@@ -265,7 +265,7 @@ OBJS-$(CONFIG_BRENDER_PIX_DECODER)     += brenderpix.o
 OBJS-$(CONFIG_C93_DECODER)             += c93.o
 OBJS-$(CONFIG_CAVS_DECODER)            += cavs.o cavsdec.o cavsdsp.o \
                                           cavsdata.o
-OBJS-$(CONFIG_CCAPTION_DECODER)        += ccaption_dec.o ass.o
+OBJS-$(CONFIG_CCAPTION_DECODER)        += ccaption_dec.o
 OBJS-$(CONFIG_CDGRAPHICS_DECODER)      += cdgraphics.o
 OBJS-$(CONFIG_CDTOONS_DECODER)         += cdtoons.o
 OBJS-$(CONFIG_CDXL_DECODER)            += cdxl.o
@@ -442,7 +442,7 @@ OBJS-$(CONFIG_INTERPLAY_ACM_DECODER)   += interplayacm.o
 OBJS-$(CONFIG_INTERPLAY_DPCM_DECODER)  += dpcm.o
 OBJS-$(CONFIG_INTERPLAY_VIDEO_DECODER) += interplayvideo.o
 OBJS-$(CONFIG_IPU_DECODER)             += mpeg12dec.o mpeg12.o mpeg12data.o
-OBJS-$(CONFIG_JACOSUB_DECODER)         += jacosubdec.o ass.o
+OBJS-$(CONFIG_JACOSUB_DECODER)         += jacosubdec.o
 OBJS-$(CONFIG_JPEG2000_ENCODER)        += j2kenc.o mqcenc.o mqc.o jpeg2000.o \
                                           jpeg2000dwt.o
 OBJS-$(CONFIG_JPEG2000_DECODER)        += jpeg2000dec.o jpeg2000.o jpeg2000dsp.o \
@@ -464,7 +464,7 @@ OBJS-$(CONFIG_MAGICYUV_ENCODER)        += magicyuvenc.o
 OBJS-$(CONFIG_MDEC_DECODER)            += mdec.o mpeg12.o mpeg12data.o
 OBJS-$(CONFIG_METASOUND_DECODER)       += metasound.o metasound_data.o \
                                           twinvq.o
-OBJS-$(CONFIG_MICRODVD_DECODER)        += microdvddec.o ass.o
+OBJS-$(CONFIG_MICRODVD_DECODER)        += microdvddec.o
 OBJS-$(CONFIG_MIMIC_DECODER)           += mimic.o
 OBJS-$(CONFIG_MJPEG_DECODER)           += mjpegdec.o mjpegdec_common.o
 OBJS-$(CONFIG_MJPEG_QSV_DECODER)       += qsvdec.o
@@ -479,8 +479,8 @@ OBJS-$(CONFIG_MLP_ENCODER)             += mlpenc.o mlp.o
 OBJS-$(CONFIG_MMVIDEO_DECODER)         += mmvideo.o
 OBJS-$(CONFIG_MOBICLIP_DECODER)        += mobiclip.o
 OBJS-$(CONFIG_MOTIONPIXELS_DECODER)    += motionpixels.o
-OBJS-$(CONFIG_MOVTEXT_DECODER)         += movtextdec.o ass.o
-OBJS-$(CONFIG_MOVTEXT_ENCODER)         += movtextenc.o ass_split.o
+OBJS-$(CONFIG_MOVTEXT_DECODER)         += movtextdec.o
+OBJS-$(CONFIG_MOVTEXT_ENCODER)         += movtextenc.o
 OBJS-$(CONFIG_MP1_DECODER)             += mpegaudiodec_fixed.o
 OBJS-$(CONFIG_MP1FLOAT_DECODER)        += mpegaudiodec_float.o
 OBJS-$(CONFIG_MP2_DECODER)             += mpegaudiodec_fixed.o
@@ -521,7 +521,7 @@ OBJS-$(CONFIG_MPEG4_MEDIACODEC_DECODER) += mediacodecdec.o
 OBJS-$(CONFIG_MPEG4_OMX_ENCODER)       += omx.o
 OBJS-$(CONFIG_MPEG4_V4L2M2M_DECODER)   += v4l2_m2m_dec.o
 OBJS-$(CONFIG_MPEG4_V4L2M2M_ENCODER)   += v4l2_m2m_enc.o
-OBJS-$(CONFIG_MPL2_DECODER)            += mpl2dec.o ass.o
+OBJS-$(CONFIG_MPL2_DECODER)            += mpl2dec.o
 OBJS-$(CONFIG_MSA1_DECODER)            += mss3.o
 OBJS-$(CONFIG_MSCC_DECODER)            += mscc.o
 OBJS-$(CONFIG_MSMPEG4V1_DECODER)       += msmpeg4dec.o msmpeg4.o msmpeg4data.o
@@ -574,7 +574,7 @@ OBJS-$(CONFIG_PGX_DECODER)             += pgxdec.o
 OBJS-$(CONFIG_PHOTOCD_DECODER)         += photocd.o
 OBJS-$(CONFIG_PICTOR_DECODER)          += pictordec.o cga_data.o
 OBJS-$(CONFIG_PIXLET_DECODER)          += pixlet.o
-OBJS-$(CONFIG_PJS_DECODER)             += textdec.o ass.o
+OBJS-$(CONFIG_PJS_DECODER)             += textdec.o
 OBJS-$(CONFIG_PNG_DECODER)             += png.o pngdec.o pngdsp.o
 OBJS-$(CONFIG_PNG_ENCODER)             += png.o pngenc.o
 OBJS-$(CONFIG_PPM_DECODER)             += pnmdec.o pnm.o
@@ -609,7 +609,7 @@ OBJS-$(CONFIG_RALF_DECODER)            += ralf.o
 OBJS-$(CONFIG_RASC_DECODER)            += rasc.o
 OBJS-$(CONFIG_RAWVIDEO_DECODER)        += rawdec.o
 OBJS-$(CONFIG_RAWVIDEO_ENCODER)        += rawenc.o
-OBJS-$(CONFIG_REALTEXT_DECODER)        += realtextdec.o ass.o
+OBJS-$(CONFIG_REALTEXT_DECODER)        += realtextdec.o
 OBJS-$(CONFIG_RL2_DECODER)             += rl2.o
 OBJS-$(CONFIG_ROQ_DECODER)             += roqvideodec.o roqvideo.o
 OBJS-$(CONFIG_ROQ_ENCODER)             += roqvideoenc.o roqvideo.o elbg.o
@@ -624,7 +624,7 @@ OBJS-$(CONFIG_RV20_DECODER)            += rv10.o
 OBJS-$(CONFIG_RV20_ENCODER)            += rv20enc.o
 OBJS-$(CONFIG_RV30_DECODER)            += rv30.o rv34.o rv30dsp.o
 OBJS-$(CONFIG_RV40_DECODER)            += rv40.o rv34.o rv40dsp.o
-OBJS-$(CONFIG_SAMI_DECODER)            += samidec.o ass.o htmlsubtitles.o
+OBJS-$(CONFIG_SAMI_DECODER)            += samidec.o htmlsubtitles.o
 OBJS-$(CONFIG_S302M_DECODER)           += s302m.o
 OBJS-$(CONFIG_S302M_ENCODER)           += s302menc.o
 OBJS-$(CONFIG_SANM_DECODER)            += sanm.o
@@ -659,13 +659,13 @@ OBJS-$(CONFIG_SPEEDHQ_ENCODER)         += speedhq.o mpeg12data.o mpeg12enc.o spe
 OBJS-$(CONFIG_SPEEX_DECODER)           += speexdec.o
 OBJS-$(CONFIG_SP5X_DECODER)            += sp5xdec.o
 OBJS-$(CONFIG_SRGC_DECODER)            += mscc.o
-OBJS-$(CONFIG_SRT_DECODER)             += srtdec.o ass.o htmlsubtitles.o
-OBJS-$(CONFIG_SRT_ENCODER)             += srtenc.o ass_split.o
-OBJS-$(CONFIG_STL_DECODER)             += textdec.o ass.o
-OBJS-$(CONFIG_SUBRIP_DECODER)          += srtdec.o ass.o htmlsubtitles.o
-OBJS-$(CONFIG_SUBRIP_ENCODER)          += srtenc.o ass_split.o
-OBJS-$(CONFIG_SUBVIEWER1_DECODER)      += textdec.o ass.o
-OBJS-$(CONFIG_SUBVIEWER_DECODER)       += subviewerdec.o ass.o
+OBJS-$(CONFIG_SRT_DECODER)             += srtdec.o htmlsubtitles.o
+OBJS-$(CONFIG_SRT_ENCODER)             += srtenc.o
+OBJS-$(CONFIG_STL_DECODER)             += textdec.o
+OBJS-$(CONFIG_SUBRIP_DECODER)          += srtdec.o htmlsubtitles.o
+OBJS-$(CONFIG_SUBRIP_ENCODER)          += srtenc.o
+OBJS-$(CONFIG_SUBVIEWER1_DECODER)      += textdec.o
+OBJS-$(CONFIG_SUBVIEWER_DECODER)       += subviewerdec.o
 OBJS-$(CONFIG_SUNRAST_DECODER)         += sunrast.o
 OBJS-$(CONFIG_SUNRAST_ENCODER)         += sunrastenc.o
 OBJS-$(CONFIG_LIBRSVG_DECODER)         += librsvgdec.o
@@ -675,8 +675,8 @@ OBJS-$(CONFIG_SVQ1_DECODER)            += svq1dec.o svq1.o h263data.o
 OBJS-$(CONFIG_SVQ1_ENCODER)            += svq1enc.o svq1.o  h263data.o  \
                                           h263.o ituh263enc.o
 OBJS-$(CONFIG_SVQ3_DECODER)            += svq3.o mpegutils.o h264data.o
-OBJS-$(CONFIG_TEXT_DECODER)            += textdec.o ass.o
-OBJS-$(CONFIG_TEXT_ENCODER)            += srtenc.o ass_split.o
+OBJS-$(CONFIG_TEXT_DECODER)            += textdec.o
+OBJS-$(CONFIG_TEXT_ENCODER)            += srtenc.o
 OBJS-$(CONFIG_TAK_DECODER)             += takdec.o tak.o takdsp.o
 OBJS-$(CONFIG_TARGA_DECODER)           += targa.o
 OBJS-$(CONFIG_TARGA_ENCODER)           += targaenc.o rle.o
@@ -696,7 +696,7 @@ OBJS-$(CONFIG_TSCC_DECODER)            += tscc.o msrledec.o
 OBJS-$(CONFIG_TSCC2_DECODER)           += tscc2.o
 OBJS-$(CONFIG_TTA_DECODER)             += tta.o ttadata.o ttadsp.o
 OBJS-$(CONFIG_TTA_ENCODER)             += ttaenc.o ttaencdsp.o ttadata.o
-OBJS-$(CONFIG_TTML_ENCODER)            += ttmlenc.o ass_split.o
+OBJS-$(CONFIG_TTML_ENCODER)            += ttmlenc.o
 OBJS-$(CONFIG_TWINVQ_DECODER)          += twinvqdec.o twinvq.o metasound_data.o
 OBJS-$(CONFIG_TXD_DECODER)             += txd.o
 OBJS-$(CONFIG_ULTI_DECODER)            += ulti.o
@@ -753,15 +753,15 @@ OBJS-$(CONFIG_VP9_MEDIACODEC_DECODER)  += mediacodecdec.o
 OBJS-$(CONFIG_VP9_RKMPP_DECODER)       += rkmppdec.o
 OBJS-$(CONFIG_VP9_VAAPI_ENCODER)       += vaapi_encode_vp9.o
 OBJS-$(CONFIG_VP9_QSV_ENCODER)         += qsvenc_vp9.o
-OBJS-$(CONFIG_VPLAYER_DECODER)         += textdec.o ass.o
+OBJS-$(CONFIG_VPLAYER_DECODER)         += textdec.o
 OBJS-$(CONFIG_VP9_V4L2M2M_DECODER)     += v4l2_m2m_dec.o
 OBJS-$(CONFIG_VQA_DECODER)             += vqavideo.o
 OBJS-$(CONFIG_WAVPACK_DECODER)         += wavpack.o wavpackdata.o dsd.o
 OBJS-$(CONFIG_WAVPACK_ENCODER)         += wavpackdata.o wavpackenc.o
 OBJS-$(CONFIG_WCMV_DECODER)            += wcmv.o
 OBJS-$(CONFIG_WEBP_DECODER)            += webp.o
-OBJS-$(CONFIG_WEBVTT_DECODER)          += webvttdec.o ass.o
-OBJS-$(CONFIG_WEBVTT_ENCODER)          += webvttenc.o ass_split.o
+OBJS-$(CONFIG_WEBVTT_DECODER)          += webvttdec.o
+OBJS-$(CONFIG_WEBVTT_ENCODER)          += webvttenc.o
 OBJS-$(CONFIG_WMALOSSLESS_DECODER)     += wmalosslessdec.o wma_common.o
 OBJS-$(CONFIG_WMAPRO_DECODER)          += wmaprodec.o wma.o wma_common.o
 OBJS-$(CONFIG_WMAV1_DECODER)           += wmadec.o wma.o wma_common.o aactab.o
@@ -1051,7 +1051,7 @@ OBJS-$(CONFIG_PCM_ALAW_AT_ENCODER)        += audiotoolboxenc.o
 OBJS-$(CONFIG_PCM_MULAW_AT_ENCODER)       += audiotoolboxenc.o
 OBJS-$(CONFIG_LIBAOM_AV1_DECODER)         += libaomdec.o
 OBJS-$(CONFIG_LIBAOM_AV1_ENCODER)         += libaomenc.o
-OBJS-$(CONFIG_LIBARIBB24_DECODER)         += libaribb24.o ass.o
+OBJS-$(CONFIG_LIBARIBB24_DECODER)         += libaribb24.o
 OBJS-$(CONFIG_LIBCELT_DECODER)            += libcelt_dec.o
 OBJS-$(CONFIG_LIBCODEC2_DECODER)          += libcodec2.o
 OBJS-$(CONFIG_LIBCODEC2_ENCODER)          += libcodec2.o
@@ -1104,7 +1104,7 @@ OBJS-$(CONFIG_LIBX265_ENCODER)            += libx265.o
 OBJS-$(CONFIG_LIBXAVS_ENCODER)            += libxavs.o
 OBJS-$(CONFIG_LIBXAVS2_ENCODER)           += libxavs2.o
 OBJS-$(CONFIG_LIBXVID_ENCODER)            += libxvid.o
-OBJS-$(CONFIG_LIBZVBI_TELETEXT_DECODER)   += libzvbi-teletextdec.o ass.o
+OBJS-$(CONFIG_LIBZVBI_TELETEXT_DECODER)   += libzvbi-teletextdec.o
 
 # parsers
 OBJS-$(CONFIG_AAC_LATM_PARSER)         += latm_parser.o
diff --git a/libavcodec/ass.h b/libavcodec/ass.h
index 4dffe923d9..8bc13d7ab8 100644
--- a/libavcodec/ass.h
+++ b/libavcodec/ass.h
@@ -23,124 +23,73 @@
 #define AVCODEC_ASS_H
 
 #include "avcodec.h"
-#include "libavutil/bprint.h"
-
-#define ASS_DEFAULT_PLAYRESX 384
-#define ASS_DEFAULT_PLAYRESY 288
-
-/**
- * @name Default values for ASS style
- * @{
- */
-#define ASS_DEFAULT_FONT        "Arial"
-#define ASS_DEFAULT_FONT_SIZE   16
-#define ASS_DEFAULT_COLOR       0xffffff
-#define ASS_DEFAULT_BACK_COLOR  0
-#define ASS_DEFAULT_BOLD        0
-#define ASS_DEFAULT_ITALIC      0
-#define ASS_DEFAULT_UNDERLINE   0
-#define ASS_DEFAULT_ALIGNMENT   2
-#define ASS_DEFAULT_BORDERSTYLE 1
-/** @} */
+#include "libavutil/ass_internal.h"
 
 typedef struct FFASSDecoderContext {
     int readorder;
 } FFASSDecoderContext;
 
-/**
- * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS.
- * Can specify all fields explicitly
- *
- * @param avctx pointer to the AVCodecContext
- * @param play_res_x subtitle frame width
- * @param play_res_y subtitle frame height
- * @param font name of the default font face to use
- * @param font_size default font size to use
- * @param primary_color default text color to use (ABGR)
- * @param secondary_color default secondary text color to use (ABGR)
- * @param outline_color default outline color to use (ABGR)
- * @param back_color default background color to use (ABGR)
- * @param bold 1 for bold text, 0 for normal text
- * @param italic 1 for italic text, 0 for normal text
- * @param underline 1 for underline text, 0 for normal text
- * @param border_style 1 for outline, 3 for opaque box
- * @param alignment position of the text (left, center, top...), defined after
- *                  the layout of the numpad (1-3 sub, 4-6 mid, 7-9 top)
- * @return >= 0 on success otherwise an error code <0
- */
-int ff_ass_subtitle_header_full(AVCodecContext *avctx,
+static inline int ff_ass_subtitle_header_full(AVCodecContext *avctx,
                                 int play_res_x, int play_res_y,
                                 const char *font, int font_size,
                                 int primary_color, int secondary_color,
                                 int outline_color, int back_color,
                                 int bold, int italic, int underline,
-                                int border_style, int alignment);
-/**
- * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS.
- *
- * @param avctx pointer to the AVCodecContext
- * @param font name of the default font face to use
- * @param font_size default font size to use
- * @param color default text color to use (ABGR)
- * @param back_color default background color to use (ABGR)
- * @param bold 1 for bold text, 0 for normal text
- * @param italic 1 for italic text, 0 for normal text
- * @param underline 1 for underline text, 0 for normal text
- * @param alignment position of the text (left, center, top...), defined after
- *                  the layout of the numpad (1-3 sub, 4-6 mid, 7-9 top)
- * @return >= 0 on success otherwise an error code <0
- */
-int ff_ass_subtitle_header(AVCodecContext *avctx,
-                           const char *font, int font_size,
-                           int color, int back_color,
-                           int bold, int italic, int underline,
-                           int border_style, int alignment);
+                                int border_style, int alignment)
+{
+    avctx->subtitle_header = (uint8_t *)avpriv_ass_get_subtitle_header_full(
+                                play_res_x, play_res_y, font, font_size,
+                                primary_color, secondary_color, outline_color,
+                                back_color, bold,italic,underline,border_style,alignment,
+                                !(avctx->flags & AV_CODEC_FLAG_BITEXACT));
 
-/**
- * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS
- * with default style.
- *
- * @param avctx pointer to the AVCodecContext
- * @return >= 0 on success otherwise an error code <0
- */
-int ff_ass_subtitle_header_default(AVCodecContext *avctx);
+    if (!avctx->subtitle_header)
+        return AVERROR(ENOMEM);
+    avctx->subtitle_header_size = strlen((char *)avctx->subtitle_header);
+    return 0;
+}
 
-/**
- * Craft an ASS dialog string.
- */
-char *ff_ass_get_dialog(int readorder, int layer, const char *style,
-                        const char *speaker, const char *text);
+static inline int ff_ass_subtitle_header_default(AVCodecContext *avctx)
+{
+    avctx->subtitle_header = (uint8_t *)avpriv_ass_get_subtitle_header_default(!(avctx->flags & AV_CODEC_FLAG_BITEXACT));
 
-/**
- * Add an ASS dialog to a subtitle.
- */
-int ff_ass_add_rect(AVSubtitle *sub, const char *dialog,
-                    int readorder, int layer, const char *style,
-                    const char *speaker);
+    if (!avctx->subtitle_header)
+        return AVERROR(ENOMEM);
+    avctx->subtitle_header_size = strlen((char *)avctx->subtitle_header);
+    return 0;
+}
+
+static inline void ff_ass_decoder_flush(AVCodecContext *avctx)
+{
+    FFASSDecoderContext *s = avctx->priv_data;
+    if (!(avctx->flags2 & AV_CODEC_FLAG2_RO_FLUSH_NOOP))
+        s->readorder = 0;
+}
 
 /**
  * Add an ASS dialog to a subtitle.
  */
-int ff_ass_add_rect2(AVSubtitle *sub, const char *dialog,
-                     int readorder, int layer, const char *style,
-                     const char *speaker, unsigned *nb_rect_allocated);
+static inline int avpriv_ass_add_rect(AVSubtitle *sub, const char *dialog,
+                    int readorder, int layer, const char *style,
+                    const char *speaker)
+{
+    char *ass_str;
+    AVSubtitleRect **rects;
 
-/**
- * Helper to flush a text subtitles decoder making use of the
- * FFASSDecoderContext.
- */
-void ff_ass_decoder_flush(AVCodecContext *avctx);
+    rects = av_realloc_array(sub->rects, sub->num_rects+1, sizeof(*sub->rects));
+    if (!rects)
+        return AVERROR(ENOMEM);
+    sub->rects = rects;
+    rects[sub->num_rects]       = av_mallocz(sizeof(*rects[0]));
+    if (!rects[sub->num_rects])
+        return AVERROR(ENOMEM);
+    rects[sub->num_rects]->type = SUBTITLE_ASS;
+    ass_str = avpriv_ass_get_dialog(readorder, layer, style, speaker, dialog);
+    if (!ass_str)
+        return AVERROR(ENOMEM);
+    rects[sub->num_rects]->ass = ass_str;
+    sub->num_rects++;
+    return 0;
+}
 
-/**
- * Escape a text subtitle using ASS syntax into an AVBPrint buffer.
- * Newline characters will be escaped to \N.
- *
- * @param buf pointer to an initialized AVBPrint buffer
- * @param p source text
- * @param size size of the source text
- * @param linebreaks additional newline chars, which will be escaped to \N
- * @param keep_ass_markup braces and backslash will not be escaped if set
- */
-void ff_ass_bprint_text_event(AVBPrint *buf, const char *p, int size,
-                             const char *linebreaks, int keep_ass_markup);
 #endif /* AVCODEC_ASS_H */
diff --git a/libavcodec/ass_split.h b/libavcodec/ass_split.h
deleted file mode 100644
index a45fb9b8a1..0000000000
--- a/libavcodec/ass_split.h
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * SSA/ASS spliting functions
- * Copyright (c) 2010  Aurelien Jacobs <aurel@gnuage.org>
- *
- * 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
- */
-
-#ifndef AVCODEC_ASS_SPLIT_H
-#define AVCODEC_ASS_SPLIT_H
-
-/**
- * fields extracted from the [Script Info] section
- */
-typedef struct {
-    char *script_type;    /**< SSA script format version (eg. v4.00) */
-    char *collisions;     /**< how subtitles are moved to prevent collisions */
-    int   play_res_x;     /**< video width that ASS coords are referring to */
-    int   play_res_y;     /**< video height that ASS coords are referring to */
-    float timer;          /**< time multiplier to apply to SSA clock (in %) */
-} ASSScriptInfo;
-
-/**
- * fields extracted from the [V4(+) Styles] section
- */
-typedef struct {
-    char *name;           /**< name of the tyle (case sensitive) */
-    char *font_name;      /**< font face (case sensitive) */
-    int   font_size;      /**< font height */
-    int   primary_color;  /**< color that a subtitle will normally appear in */
-    int   secondary_color;
-    int   outline_color;  /**< color for outline in ASS, called tertiary in SSA */
-    int   back_color;     /**< color of the subtitle outline or shadow */
-    int   bold;           /**< whether text is bold (1) or not (0) */
-    int   italic;         /**< whether text is italic (1) or not (0) */
-    int   underline;      /**< whether text is underlined (1) or not (0) */
-    int   strikeout;
-    float scalex;
-    float scaley;
-    float spacing;
-    float angle;
-    int   border_style;
-    float outline;
-    float shadow;
-    int   alignment;      /**< position of the text (left, center, top...),
-                               defined after the layout of the numpad
-                               (1-3 sub, 4-6 mid, 7-9 top) */
-    int   margin_l;
-    int   margin_r;
-    int   margin_v;
-    int   alpha_level;
-    int   encoding;
-} ASSStyle;
-
-/**
- * fields extracted from the [Events] section
- */
-typedef struct {
-    int   readorder;
-    int   layer;    /**< higher numbered layers are drawn over lower numbered */
-    int   start;    /**< start time of the dialog in centiseconds */
-    int   end;      /**< end time of the dialog in centiseconds */
-    char *style;    /**< name of the ASSStyle to use with this dialog */
-    char *name;
-    int   margin_l;
-    int   margin_r;
-    int   margin_v;
-    char *effect;
-    char *text;     /**< actual text which will be displayed as a subtitle,
-                         can include style override control codes (see
-                         ff_ass_split_override_codes()) */
-} ASSDialog;
-
-/**
- * structure containing the whole split ASS data
- */
-typedef struct {
-    ASSScriptInfo script_info;   /**< general information about the SSA script*/
-    ASSStyle     *styles;        /**< array of split out styles */
-    int           styles_count;  /**< number of ASSStyle in the styles array */
-    ASSDialog    *dialogs;       /**< array of split out dialogs */
-    int           dialogs_count; /**< number of ASSDialog in the dialogs array*/
-} ASS;
-
-/**
- * This struct can be casted to ASS to access to the split data.
- */
-typedef struct ASSSplitContext ASSSplitContext;
-
-/**
- * Split a full ASS file or a ASS header from a string buffer and store
- * the split structure in a newly allocated context.
- *
- * @param buf String containing the ASS formatted data.
- * @return Newly allocated struct containing split data.
- */
-ASSSplitContext *ff_ass_split(const char *buf);
-
-/**
- * Free a dialogue obtained from ff_ass_split_dialog().
- */
-void ff_ass_free_dialog(ASSDialog **dialogp);
-
-/**
- * Split one ASS Dialogue line from a string buffer.
- *
- * @param ctx Context previously initialized by ff_ass_split().
- * @param buf String containing the ASS "Dialogue" line.
- * @return Pointer to the split ASSDialog. Must be freed with ff_ass_free_dialog()
- */
-ASSDialog *ff_ass_split_dialog(ASSSplitContext *ctx, const char *buf);
-
-/**
- * Free all the memory allocated for an ASSSplitContext.
- *
- * @param ctx Context previously initialized by ff_ass_split().
- */
-void ff_ass_split_free(ASSSplitContext *ctx);
-
-
-/**
- * Set of callback functions corresponding to each override codes that can
- * be encountered in a "Dialogue" Text field.
- */
-typedef struct {
-    /**
-     * @defgroup ass_styles    ASS styles
-     * @{
-     */
-    void (*text)(void *priv, const char *text, int len);
-    void (*new_line)(void *priv, int forced);
-    void (*style)(void *priv, char style, int close);
-    void (*color)(void *priv, unsigned int /* color */, unsigned int color_id);
-    void (*alpha)(void *priv, int alpha, int alpha_id);
-    void (*font_name)(void *priv, const char *name);
-    void (*font_size)(void *priv, int size);
-    void (*alignment)(void *priv, int alignment);
-    void (*cancel_overrides)(void *priv, const char *style);
-    /** @} */
-
-    /**
-     * @defgroup ass_functions    ASS functions
-     * @{
-     */
-    void (*move)(void *priv, int x1, int y1, int x2, int y2, int t1, int t2);
-    void (*origin)(void *priv, int x, int y);
-    /** @} */
-
-    /**
-     * @defgroup ass_end    end of Dialogue Event
-     * @{
-     */
-    void (*end)(void *priv);
-    /** @} */
-} ASSCodesCallbacks;
-
-/**
- * Split override codes out of a ASS "Dialogue" Text field.
- *
- * @param callbacks Set of callback functions called for each override code
- *                  encountered.
- * @param priv Opaque pointer passed to the callback functions.
- * @param buf The ASS "Dialogue" Text field to split.
- * @return >= 0 on success otherwise an error code <0
- */
-int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
-                                const char *buf);
-
-/**
- * Find an ASSStyle structure by its name.
- *
- * @param ctx Context previously initialized by ff_ass_split().
- * @param style name of the style to search for.
- * @return the ASSStyle corresponding to style, or NULL if style can't be found
- */
-ASSStyle *ff_ass_style_get(ASSSplitContext *ctx, const char *style);
-
-#endif /* AVCODEC_ASS_SPLIT_H */
diff --git a/libavcodec/assdec.c b/libavcodec/assdec.c
index f43b500aa7..1a1363471d 100644
--- a/libavcodec/assdec.c
+++ b/libavcodec/assdec.c
@@ -22,9 +22,9 @@
 #include <string.h>
 
 #include "avcodec.h"
-#include "ass.h"
 #include "codec_internal.h"
 #include "config_components.h"
+#include "libavutil/ass_internal.h"
 #include "libavutil/internal.h"
 #include "libavutil/mem.h"
 
diff --git a/libavcodec/assenc.c b/libavcodec/assenc.c
index 2ac40d5afe..391d690133 100644
--- a/libavcodec/assenc.c
+++ b/libavcodec/assenc.c
@@ -24,8 +24,8 @@
 #include <string.h>
 
 #include "avcodec.h"
-#include "ass.h"
 #include "codec_internal.h"
+#include "libavutil/ass_internal.h"
 #include "libavutil/avstring.h"
 #include "libavutil/internal.h"
 #include "libavutil/mem.h"
diff --git a/libavcodec/ccaption_dec.c b/libavcodec/ccaption_dec.c
index 34f0513b1a..5f706d985f 100644
--- a/libavcodec/ccaption_dec.c
+++ b/libavcodec/ccaption_dec.c
@@ -272,15 +272,12 @@ static av_cold int init_decoder(AVCodecContext *avctx)
     ctx->bg_color = CCCOL_BLACK;
     ctx->rollup = 2;
     ctx->cursor_row = 10;
-    ret = ff_ass_subtitle_header(avctx, "Monospace",
+    ret = ff_ass_subtitle_header_full(avctx, ASS_DEFAULT_PLAYRESX, ASS_DEFAULT_PLAYRESY, "Monospace",
                                  ASS_DEFAULT_FONT_SIZE,
-                                 ASS_DEFAULT_COLOR,
-                                 ASS_DEFAULT_BACK_COLOR,
-                                 ASS_DEFAULT_BOLD,
-                                 ASS_DEFAULT_ITALIC,
-                                 ASS_DEFAULT_UNDERLINE,
-                                 3,
-                                 ASS_DEFAULT_ALIGNMENT);
+                                 ASS_DEFAULT_COLOR, ASS_DEFAULT_COLOR,
+                                 ASS_DEFAULT_BACK_COLOR, ASS_DEFAULT_BACK_COLOR,
+                                 ASS_DEFAULT_BOLD, ASS_DEFAULT_ITALIC, ASS_DEFAULT_UNDERLINE,
+                                 3, ASS_DEFAULT_ALIGNMENT);
     if (ret < 0) {
         return ret;
     }
@@ -850,7 +847,6 @@ static int decode(AVCodecContext *avctx, AVSubtitle *sub,
     int len = avpkt->size;
     int ret = 0;
     int i;
-    unsigned nb_rect_allocated = 0;
 
     for (i = 0; i < len; i += 3) {
         uint8_t hi, cc_type = bptr[i] & 1;
@@ -887,7 +883,7 @@ static int decode(AVCodecContext *avctx, AVSubtitle *sub,
                                                      AV_TIME_BASE_Q, ms_tb);
             else
                 sub->end_display_time = -1;
-            ret = ff_ass_add_rect2(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL, &nb_rect_allocated);
+            ret = avpriv_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL);
             if (ret < 0)
                 return ret;
             ctx->last_real_time = sub->pts;
@@ -897,7 +893,7 @@ static int decode(AVCodecContext *avctx, AVSubtitle *sub,
 
     if (!bptr && !ctx->real_time && ctx->buffer[!ctx->buffer_index].str[0]) {
         bidx = !ctx->buffer_index;
-        ret = ff_ass_add_rect2(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL, &nb_rect_allocated);
+        ret = avpriv_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL);
         if (ret < 0)
             return ret;
         sub->pts = ctx->buffer_time[1];
@@ -915,7 +911,7 @@ static int decode(AVCodecContext *avctx, AVSubtitle *sub,
         capture_screen(ctx);
         ctx->buffer_changed = 0;
 
-        ret = ff_ass_add_rect2(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL, &nb_rect_allocated);
+        ret = avpriv_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL);
         if (ret < 0)
             return ret;
         sub->end_display_time = -1;
diff --git a/libavcodec/jacosubdec.c b/libavcodec/jacosubdec.c
index e3bf9f4226..40abdebcc6 100644
--- a/libavcodec/jacosubdec.c
+++ b/libavcodec/jacosubdec.c
@@ -182,7 +182,7 @@ static int jacosub_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
 
         av_bprint_init(&buffer, JSS_MAX_LINESIZE, JSS_MAX_LINESIZE);
         jacosub_to_ass(avctx, &buffer, ptr);
-        ret = ff_ass_add_rect(sub, buffer.str, s->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, buffer.str, s->readorder++, 0, NULL, NULL);
         av_bprint_finalize(&buffer, NULL);
         if (ret < 0)
             return ret;
diff --git a/libavcodec/libaribb24.c b/libavcodec/libaribb24.c
index 9658e1d5ac..360e20834b 100644
--- a/libavcodec/libaribb24.c
+++ b/libavcodec/libaribb24.c
@@ -274,7 +274,7 @@ next_region:
         av_log(avctx, AV_LOG_DEBUG, "Styled ASS line: %s\n",
                buf.str);
 
-        ret = ff_ass_add_rect(sub, buf.str, b24->read_order++,
+        ret = avpriv_ass_add_rect(sub, buf.str, b24->read_order++,
                               0, NULL, NULL);
     }
 
diff --git a/libavcodec/libzvbi-teletextdec.c b/libavcodec/libzvbi-teletextdec.c
index 2aab10a548..54a78342f2 100644
--- a/libavcodec/libzvbi-teletextdec.c
+++ b/libavcodec/libzvbi-teletextdec.c
@@ -153,12 +153,12 @@ static char *create_ass_text(TeletextContext *ctx, const char *text)
     AVBPrint buf;
 
     av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
-    ff_ass_bprint_text_event(&buf, text, strlen(text), "", 0);
+    avpriv_ass_bprint_text_event(&buf, text, strlen(text), "", 0);
     if (!av_bprint_is_complete(&buf)) {
         av_bprint_finalize(&buf, NULL);
         return NULL;
     }
-    dialog = ff_ass_get_dialog(ctx->readorder++, 0, NULL, NULL, buf.str);
+    dialog = avpriv_ass_get_dialog(ctx->readorder++, 0, NULL, NULL, buf.str);
     av_bprint_finalize(&buf, NULL);
     return dialog;
 }
@@ -225,7 +225,7 @@ static int gen_sub_text(TeletextContext *ctx, AVSubtitleRect *sub_rect, vbi_page
         }
         av_log(ctx, AV_LOG_DEBUG, "subtext:%s:txetbus\n", sub_rect->ass);
     } else {
-        sub_rect->type = SUBTITLE_NONE;
+        sub_rect->type = AV_SUBTITLE_FMT_NONE;
     }
     av_bprint_finalize(&buf, NULL);
     return 0;
@@ -395,7 +395,7 @@ static int gen_sub_ass(TeletextContext *ctx, AVSubtitleRect *sub_rect, vbi_page
 
     if (buf.len) {
         sub_rect->type = SUBTITLE_ASS;
-        sub_rect->ass = ff_ass_get_dialog(ctx->readorder++, 0, is_subtitle_page ? "Subtitle" : "Teletext", NULL, buf.str);
+        sub_rect->ass = avpriv_ass_get_dialog(ctx->readorder++, 0, is_subtitle_page ? "Subtitle" : "Teletext", NULL, buf.str);
 
         if (!sub_rect->ass) {
             av_bprint_finalize(&buf, NULL);
@@ -403,7 +403,7 @@ static int gen_sub_ass(TeletextContext *ctx, AVSubtitleRect *sub_rect, vbi_page
         }
         av_log(ctx, AV_LOG_DEBUG, "subtext:%s:txetbus\n", sub_rect->ass);
     } else {
-        sub_rect->type = SUBTITLE_NONE;
+        sub_rect->type = AV_SUBTITLE_FMT_NONE;
     }
     av_bprint_finalize(&buf, NULL);
     return 0;
@@ -463,7 +463,7 @@ static int gen_sub_bitmap(TeletextContext *ctx, AVSubtitleRect *sub_rect, vbi_pa
 
     if (vc >= vcend) {
         av_log(ctx, AV_LOG_DEBUG, "dropping empty page %3x\n", page->pgno);
-        sub_rect->type = SUBTITLE_NONE;
+        sub_rect->type = AV_SUBTITLE_FMT_NONE;
         return 0;
     }
 
@@ -696,7 +696,7 @@ static int teletext_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
         sub->num_rects = 0;
         sub->pts = ctx->pages->pts;
 
-        if (ctx->pages->sub_rect->type != SUBTITLE_NONE) {
+        if (ctx->pages->sub_rect->type != AV_SUBTITLE_FMT_NONE) {
             sub->rects = av_malloc(sizeof(*sub->rects));
             if (sub->rects) {
                 sub->num_rects = 1;
diff --git a/libavcodec/microdvddec.c b/libavcodec/microdvddec.c
index f36ad51468..de17400edd 100644
--- a/libavcodec/microdvddec.c
+++ b/libavcodec/microdvddec.c
@@ -309,7 +309,7 @@ static int microdvd_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
         }
     }
     if (new_line.len) {
-        int ret = ff_ass_add_rect(sub, new_line.str, s->readorder++, 0, NULL, NULL);
+        int ret = avpriv_ass_add_rect(sub, new_line.str, s->readorder++, 0, NULL, NULL);
         av_bprint_finalize(&new_line, NULL);
         if (ret < 0)
             return ret;
@@ -362,8 +362,9 @@ static int microdvd_init(AVCodecContext *avctx)
             }
         }
     }
-    return ff_ass_subtitle_header(avctx, font_buf.str, font_size, color,
-                                  ASS_DEFAULT_BACK_COLOR, bold, italic,
+    return ff_ass_subtitle_header_full(avctx, ASS_DEFAULT_PLAYRESX, ASS_DEFAULT_PLAYRESY,
+                                  font_buf.str, font_size, color, color,
+                                  ASS_DEFAULT_BACK_COLOR, ASS_DEFAULT_BACK_COLOR, bold, italic,
                                   underline, ASS_DEFAULT_BORDERSTYLE,
                                   alignment);
 }
diff --git a/libavcodec/movtextdec.c b/libavcodec/movtextdec.c
index 70162b4888..8f2459d45b 100644
--- a/libavcodec/movtextdec.c
+++ b/libavcodec/movtextdec.c
@@ -22,7 +22,6 @@
 #include "avcodec.h"
 #include "ass.h"
 #include "libavutil/opt.h"
-#include "libavutil/avstring.h"
 #include "libavutil/common.h"
 #include "libavutil/bprint.h"
 #include "libavutil/intreadwrite.h"
@@ -553,7 +552,7 @@ static int mov_text_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
     } else
         text_to_ass(&buf, ptr, end, avctx);
 
-    ret = ff_ass_add_rect(sub, buf.str, m->readorder++, 0, NULL, NULL);
+    ret = avpriv_ass_add_rect(sub, buf.str, m->readorder++, 0, NULL, NULL);
     av_bprint_finalize(&buf, NULL);
     if (ret < 0)
         return ret;
diff --git a/libavcodec/movtextenc.c b/libavcodec/movtextenc.c
index 728338f2cc..6f0b7a8a5c 100644
--- a/libavcodec/movtextenc.c
+++ b/libavcodec/movtextenc.c
@@ -26,8 +26,8 @@
 #include "libavutil/intreadwrite.h"
 #include "libavutil/mem.h"
 #include "libavutil/common.h"
-#include "ass_split.h"
-#include "ass.h"
+#include "libavutil/ass_split_internal.h"
+#include "libavutil/ass_internal.h"
 #include "bytestream.h"
 #include "codec_internal.h"
 
@@ -167,7 +167,7 @@ static int mov_text_encode_close(AVCodecContext *avctx)
 {
     MovTextContext *s = avctx->priv_data;
 
-    ff_ass_split_free(s->ass_ctx);
+    avpriv_ass_split_free(s->ass_ctx);
     av_freep(&s->style_attributes);
     av_freep(&s->fonts);
     av_bprint_finalize(&s->buffer, NULL);
@@ -222,7 +222,7 @@ static int encode_sample_description(AVCodecContext *avctx)
     else
         s->font_scale_factor = 1;
 
-    style = ff_ass_style_get(s->ass_ctx, "Default");
+    style = avpriv_ass_style_get(s->ass_ctx, "Default");
     if (!style && ass->styles_count) {
         style = &ass->styles[0];
     }
@@ -329,7 +329,7 @@ static av_cold int mov_text_encode_init(AVCodecContext *avctx)
 
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
 
-    s->ass_ctx = ff_ass_split(avctx->subtitle_header);
+    s->ass_ctx = avpriv_ass_split(avctx->subtitle_header);
     if (!s->ass_ctx)
         return AVERROR_INVALIDDATA;
     ret = encode_sample_description(avctx);
@@ -566,7 +566,7 @@ static void mov_text_ass_style_set(MovTextContext *s, ASSStyle *style)
 
 static void mov_text_dialog(MovTextContext *s, ASSDialog *dialog)
 {
-    ASSStyle *style = ff_ass_style_get(s->ass_ctx, dialog->style);
+    ASSStyle *style = avpriv_ass_style_get(s->ass_ctx, dialog->style);
 
     s->ass_dialog_style = style;
     mov_text_ass_style_set(s, style);
@@ -580,7 +580,7 @@ static void mov_text_cancel_overrides_cb(void *priv, const char *style_name)
     if (!style_name || !*style_name)
         style = s->ass_dialog_style;
     else
-        style= ff_ass_style_get(s->ass_ctx, style_name);
+        style= avpriv_ass_style_get(s->ass_ctx, style_name);
 
     mov_text_ass_style_set(s, style);
 }
@@ -652,12 +652,12 @@ static int mov_text_encode_frame(AVCodecContext *avctx, unsigned char *buf,
             return AVERROR(EINVAL);
         }
 
-        dialog = ff_ass_split_dialog(s->ass_ctx, ass);
+        dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
         if (!dialog)
             return AVERROR(ENOMEM);
         mov_text_dialog(s, dialog);
-        ff_ass_split_override_codes(&mov_text_callbacks, s, dialog->text);
-        ff_ass_free_dialog(&dialog);
+        avpriv_ass_split_override_codes(&mov_text_callbacks, s, dialog->text);
+        avpriv_ass_free_dialog(&dialog);
     }
 
     if (s->buffer.len > UINT16_MAX)
diff --git a/libavcodec/mpl2dec.c b/libavcodec/mpl2dec.c
index 56f008b65c..175bd319e1 100644
--- a/libavcodec/mpl2dec.c
+++ b/libavcodec/mpl2dec.c
@@ -73,7 +73,7 @@ static int mpl2_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
 
     av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
     if (ptr && avpkt->size > 0 && *ptr && !mpl2_event_to_ass(&buf, ptr))
-        ret = ff_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
     av_bprint_finalize(&buf, NULL);
     if (ret < 0)
         return ret;
diff --git a/libavcodec/realtextdec.c b/libavcodec/realtextdec.c
index c3e138a7ba..49d42a1d4d 100644
--- a/libavcodec/realtextdec.c
+++ b/libavcodec/realtextdec.c
@@ -66,7 +66,7 @@ static int realtext_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
 
     av_bprint_init(&buf, 0, 4096);
     if (ptr && avpkt->size > 0 && !rt_event_to_ass(&buf, ptr))
-        ret = ff_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
     av_bprint_finalize(&buf, NULL);
     if (ret < 0)
         return ret;
diff --git a/libavcodec/samidec.c b/libavcodec/samidec.c
index cf5dec955b..f35d312c2b 100644
--- a/libavcodec/samidec.c
+++ b/libavcodec/samidec.c
@@ -143,7 +143,7 @@ static int sami_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
         if (ret < 0)
             return ret;
         // TODO: pass escaped sami->encoded_source.str as source
-        ret = ff_ass_add_rect(sub, sami->full.str, sami->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, sami->full.str, sami->readorder++, 0, NULL, NULL);
         if (ret < 0)
             return ret;
     }
diff --git a/libavcodec/srtdec.c b/libavcodec/srtdec.c
index b2df34474e..ccf5981263 100644
--- a/libavcodec/srtdec.c
+++ b/libavcodec/srtdec.c
@@ -79,7 +79,7 @@ static int srt_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
 
     ret = srt_to_ass(avctx, &buffer, avpkt->data, x1, y1, x2, y2);
     if (ret >= 0)
-        ret = ff_ass_add_rect(sub, buffer.str, s->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, buffer.str, s->readorder++, 0, NULL, NULL);
     av_bprint_finalize(&buffer, NULL);
     if (ret < 0)
         return ret;
diff --git a/libavcodec/srtenc.c b/libavcodec/srtenc.c
index 51456c8b9d..2baa6e70ad 100644
--- a/libavcodec/srtenc.c
+++ b/libavcodec/srtenc.c
@@ -25,9 +25,9 @@
 #include "avcodec.h"
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
-#include "ass_split.h"
-#include "ass.h"
 #include "codec_internal.h"
+#include "libavutil/ass_split_internal.h"
+#include "libavutil/ass_internal.h"
 
 
 #define SRT_STACK_SIZE 64
@@ -96,7 +96,7 @@ static void srt_stack_push_pop(SRTContext *s, const char c, int close)
 
 static void srt_style_apply(SRTContext *s, const char *style)
 {
-    ASSStyle *st = ff_ass_style_get(s->ass_ctx, style);
+    ASSStyle *st = avpriv_ass_style_get(s->ass_ctx, style);
     if (st) {
         int c = st->primary_color & 0xFFFFFF;
         if (st->font_name && strcmp(st->font_name, ASS_DEFAULT_FONT) ||
@@ -137,7 +137,7 @@ static av_cold int srt_encode_init(AVCodecContext *avctx)
 {
     SRTContext *s = avctx->priv_data;
     s->avctx = avctx;
-    s->ass_ctx = ff_ass_split(avctx->subtitle_header);
+    s->ass_ctx = avpriv_ass_split(avctx->subtitle_header);
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
     return s->ass_ctx ? 0 : AVERROR_INVALIDDATA;
 }
@@ -247,14 +247,14 @@ static int encode_frame(AVCodecContext *avctx,
             return AVERROR(EINVAL);
         }
 
-        dialog = ff_ass_split_dialog(s->ass_ctx, ass);
+        dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
         if (!dialog)
             return AVERROR(ENOMEM);
         s->alignment_applied = 0;
         if (avctx->codec_id == AV_CODEC_ID_SUBRIP)
             srt_style_apply(s, dialog->style);
-        ff_ass_split_override_codes(cb, s, dialog->text);
-        ff_ass_free_dialog(&dialog);
+        avpriv_ass_split_override_codes(cb, s, dialog->text);
+        avpriv_ass_free_dialog(&dialog);
     }
 
     if (!av_bprint_is_complete(&s->buffer))
@@ -286,7 +286,7 @@ static int text_encode_frame(AVCodecContext *avctx,
 static int srt_encode_close(AVCodecContext *avctx)
 {
     SRTContext *s = avctx->priv_data;
-    ff_ass_split_free(s->ass_ctx);
+    avpriv_ass_split_free(s->ass_ctx);
     av_bprint_finalize(&s->buffer, NULL);
     return 0;
 }
diff --git a/libavcodec/subviewerdec.c b/libavcodec/subviewerdec.c
index 2bda5fa5c1..8651453e3f 100644
--- a/libavcodec/subviewerdec.c
+++ b/libavcodec/subviewerdec.c
@@ -57,7 +57,7 @@ static int subviewer_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
 
     av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
     if (ptr && avpkt->size > 0 && !subviewer_event_to_ass(&buf, ptr))
-        ret = ff_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
     av_bprint_finalize(&buf, NULL);
     if (ret < 0)
         return ret;
diff --git a/libavcodec/textdec.c b/libavcodec/textdec.c
index d509452391..06a25e7128 100644
--- a/libavcodec/textdec.c
+++ b/libavcodec/textdec.c
@@ -55,8 +55,8 @@ static int text_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
 
     av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
     if (ptr && avpkt->size > 0 && *ptr) {
-        ff_ass_bprint_text_event(&buf, ptr, avpkt->size, text->linebreaks, text->keep_ass_markup);
-        ret = ff_ass_add_rect(sub, buf.str, text->readorder++, 0, NULL, NULL);
+        avpriv_ass_bprint_text_event(&buf, ptr, avpkt->size, text->linebreaks, text->keep_ass_markup);
+        ret = avpriv_ass_add_rect(sub, buf.str, text->readorder++, 0, NULL, NULL);
     }
     av_bprint_finalize(&buf, NULL);
     if (ret < 0)
diff --git a/libavcodec/ttmlenc.c b/libavcodec/ttmlenc.c
index be1d8fb2e8..d4f11a87d2 100644
--- a/libavcodec/ttmlenc.c
+++ b/libavcodec/ttmlenc.c
@@ -32,8 +32,7 @@
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
 #include "libavutil/internal.h"
-#include "ass_split.h"
-#include "ass.h"
+#include "libavutil/ass_split_internal.h"
 #include "ttmlenc.h"
 
 typedef struct {
@@ -95,7 +94,7 @@ static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf,
             return AVERROR(EINVAL);
         }
 
-        dialog = ff_ass_split_dialog(s->ass_ctx, ass);
+        dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
         if (!dialog)
             return AVERROR(ENOMEM);
 
@@ -107,7 +106,7 @@ static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf,
             av_bprintf(&s->buffer, "\">");
         }
 
-        ret = ff_ass_split_override_codes(&ttml_callbacks, s, dialog->text);
+        ret = avpriv_ass_split_override_codes(&ttml_callbacks, s, dialog->text);
         if (ret < 0) {
             int log_level = (ret != AVERROR_INVALIDDATA ||
                              avctx->err_recognition & AV_EF_EXPLODE) ?
@@ -118,7 +117,7 @@ static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf,
                    av_err2str(ret));
 
             if (log_level == AV_LOG_ERROR) {
-                ff_ass_free_dialog(&dialog);
+                avpriv_ass_free_dialog(&dialog);
                 return ret;
             }
         }
@@ -126,7 +125,7 @@ static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf,
         if (dialog->style)
             av_bprintf(&s->buffer, "</span>");
 
-        ff_ass_free_dialog(&dialog);
+        avpriv_ass_free_dialog(&dialog);
     }
 
     if (!av_bprint_is_complete(&s->buffer))
@@ -148,7 +147,7 @@ static av_cold int ttml_encode_close(AVCodecContext *avctx)
 {
     TTMLContext *s = avctx->priv_data;
 
-    ff_ass_split_free(s->ass_ctx);
+    avpriv_ass_split_free(s->ass_ctx);
 
     av_bprint_finalize(&s->buffer, NULL);
 
@@ -372,7 +371,7 @@ static av_cold int ttml_encode_init(AVCodecContext *avctx)
 
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
 
-    if (!(s->ass_ctx = ff_ass_split(avctx->subtitle_header))) {
+    if (!(s->ass_ctx = avpriv_ass_split(avctx->subtitle_header))) {
         return AVERROR_INVALIDDATA;
     }
 
diff --git a/libavcodec/webvttdec.c b/libavcodec/webvttdec.c
index fcf1062d86..549e60fe62 100644
--- a/libavcodec/webvttdec.c
+++ b/libavcodec/webvttdec.c
@@ -90,7 +90,7 @@ static int webvtt_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
 
     av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
     if (ptr && avpkt->size > 0 && !webvtt_event_to_ass(&buf, ptr))
-        ret = ff_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
+        ret = avpriv_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
     av_bprint_finalize(&buf, NULL);
     if (ret < 0)
         return ret;
diff --git a/libavcodec/webvttenc.c b/libavcodec/webvttenc.c
index e433bb4579..24d60c5dc1 100644
--- a/libavcodec/webvttenc.c
+++ b/libavcodec/webvttenc.c
@@ -24,9 +24,9 @@
 #include "avcodec.h"
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
-#include "ass_split.h"
-#include "ass.h"
 #include "codec_internal.h"
+#include "libavutil/ass_split_internal.h"
+#include "libavutil/ass_internal.h"
 
 #define WEBVTT_STACK_SIZE 64
 typedef struct {
@@ -93,7 +93,7 @@ static void webvtt_stack_push_pop(WebVTTContext *s, const char c, int close)
 
 static void webvtt_style_apply(WebVTTContext *s, const char *style)
 {
-    ASSStyle *st = ff_ass_style_get(s->ass_ctx, style);
+    ASSStyle *st = avpriv_ass_style_get(s->ass_ctx, style);
     if (st) {
         if (st->bold != ASS_DEFAULT_BOLD) {
             webvtt_print(s, "<b>");
@@ -172,12 +172,12 @@ static int webvtt_encode_frame(AVCodecContext *avctx,
             return AVERROR(EINVAL);
         }
 
-        dialog = ff_ass_split_dialog(s->ass_ctx, ass);
+        dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
         if (!dialog)
             return AVERROR(ENOMEM);
         webvtt_style_apply(s, dialog->style);
-        ff_ass_split_override_codes(&webvtt_callbacks, s, dialog->text);
-        ff_ass_free_dialog(&dialog);
+        avpriv_ass_split_override_codes(&webvtt_callbacks, s, dialog->text);
+        avpriv_ass_free_dialog(&dialog);
     }
 
     if (!av_bprint_is_complete(&s->buffer))
@@ -197,7 +197,7 @@ static int webvtt_encode_frame(AVCodecContext *avctx,
 static int webvtt_encode_close(AVCodecContext *avctx)
 {
     WebVTTContext *s = avctx->priv_data;
-    ff_ass_split_free(s->ass_ctx);
+    avpriv_ass_split_free(s->ass_ctx);
     av_bprint_finalize(&s->buffer, NULL);
     return 0;
 }
@@ -206,7 +206,7 @@ static av_cold int webvtt_encode_init(AVCodecContext *avctx)
 {
     WebVTTContext *s = avctx->priv_data;
     s->avctx = avctx;
-    s->ass_ctx = ff_ass_split(avctx->subtitle_header);
+    s->ass_ctx = avpriv_ass_split(avctx->subtitle_header);
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
     return s->ass_ctx ? 0 : AVERROR_INVALIDDATA;
 }
diff --git a/libavutil/Makefile b/libavutil/Makefile
index 48f78c81e5..9da830c0b5 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -103,6 +103,8 @@ BUILT_HEADERS = avconfig.h                                              \
 OBJS = adler32.o                                                        \
        aes.o                                                            \
        aes_ctr.o                                                        \
+       ass.o                                                            \
+       ass_split.o                                                      \
        audio_fifo.o                                                     \
        avstring.o                                                       \
        avsscanf.o                                                       \
diff --git a/libavcodec/ass.c b/libavutil/ass.c
similarity index 59%
rename from libavcodec/ass.c
rename to libavutil/ass.c
index a1e560d7ff..6739132acf 100644
--- a/libavcodec/ass.c
+++ b/libavutil/ass.c
@@ -19,21 +19,22 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-#include "avcodec.h"
-#include "ass.h"
+#include "ass_internal.h"
+
+#include "subfmt.h"
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
 #include "libavutil/common.h"
 
-int ff_ass_subtitle_header_full(AVCodecContext *avctx,
-                                int play_res_x, int play_res_y,
-                                const char *font, int font_size,
-                                int primary_color, int secondary_color,
-                                int outline_color, int back_color,
-                                int bold, int italic, int underline,
-                                int border_style, int alignment)
+char* avpriv_ass_get_subtitle_header_full(int play_res_x, int play_res_y,
+                                    const char *font, int font_size,
+                                    int primary_color, int secondary_color,
+                                    int outline_color, int back_color,
+                                    int bold, int italic, int underline,
+                                    int border_style, int alignment,
+                                    int print_av_version)
 {
-    avctx->subtitle_header = av_asprintf(
+    char* header = av_asprintf(
              "[Script Info]\r\n"
              "; Script generated by FFmpeg/Lavc%s\r\n"
              "ScriptType: v4.00+\r\n"
@@ -68,34 +69,31 @@ int ff_ass_subtitle_header_full(AVCodecContext *avctx,
              "\r\n"
              "[Events]\r\n"
              "Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\r\n",
-             !(avctx->flags & AV_CODEC_FLAG_BITEXACT) ? AV_STRINGIFY(LIBAVCODEC_VERSION) : "",
+             print_av_version ? AV_STRINGIFY(LIBAVCODEC_VERSION) : "",
              play_res_x, play_res_y, font, font_size,
              primary_color, secondary_color, outline_color, back_color,
              -bold, -italic, -underline, border_style, alignment);
 
-    if (!avctx->subtitle_header)
-        return AVERROR(ENOMEM);
-    avctx->subtitle_header_size = strlen(avctx->subtitle_header);
-    return 0;
+    return header;
 }
 
-int ff_ass_subtitle_header(AVCodecContext *avctx,
-                           const char *font, int font_size,
+char* avpriv_ass_get_subtitle_header(const char *font, int font_size,
                            int color, int back_color,
                            int bold, int italic, int underline,
-                           int border_style, int alignment)
+                           int border_style, int alignment,
+                           int print_av_version)
 {
-    return ff_ass_subtitle_header_full(avctx,
-                               ASS_DEFAULT_PLAYRESX, ASS_DEFAULT_PLAYRESY,
-                               font, font_size, color, color,
-                               back_color, back_color,
-                               bold, italic, underline,
-                               border_style, alignment);
+    return avpriv_ass_get_subtitle_header_full(ASS_DEFAULT_PLAYRESX, ASS_DEFAULT_PLAYRESY,
+                                       font, font_size, color, color,
+                                       back_color, back_color,
+                                       bold, italic, underline,
+                                       border_style, alignment,
+                                       print_av_version);
 }
 
-int ff_ass_subtitle_header_default(AVCodecContext *avctx)
+char* avpriv_ass_get_subtitle_header_default(int print_av_version)
 {
-    return ff_ass_subtitle_header(avctx, ASS_DEFAULT_FONT,
+    return avpriv_ass_get_subtitle_header(ASS_DEFAULT_FONT,
                                ASS_DEFAULT_FONT_SIZE,
                                ASS_DEFAULT_COLOR,
                                ASS_DEFAULT_BACK_COLOR,
@@ -103,10 +101,11 @@ int ff_ass_subtitle_header_default(AVCodecContext *avctx)
                                ASS_DEFAULT_ITALIC,
                                ASS_DEFAULT_UNDERLINE,
                                ASS_DEFAULT_BORDERSTYLE,
-                               ASS_DEFAULT_ALIGNMENT);
+                               ASS_DEFAULT_ALIGNMENT,
+                               print_av_version);
 }
 
-char *ff_ass_get_dialog(int readorder, int layer, const char *style,
+char *avpriv_ass_get_dialog(int readorder, int layer, const char *style,
                         const char *speaker, const char *text)
 {
     return av_asprintf("%d,%d,%s,%s,0,0,0,,%s",
@@ -114,61 +113,17 @@ char *ff_ass_get_dialog(int readorder, int layer, const char *style,
                        speaker ? speaker : "", text);
 }
 
-int ff_ass_add_rect2(AVSubtitle *sub, const char *dialog,
-                    int readorder, int layer, const char *style,
-                    const char *speaker, unsigned *nb_rect_allocated)
-{
-    AVSubtitleRect **rects = sub->rects, *rect;
-    char *ass_str;
-    uint64_t new_nb = 0;
-
-    if (sub->num_rects >= UINT_MAX)
-        return AVERROR(ENOMEM);
-
-    if (nb_rect_allocated && *nb_rect_allocated <= sub->num_rects) {
-        if (sub->num_rects < UINT_MAX / 17 * 16) {
-            new_nb = sub->num_rects + sub->num_rects/16 + 1;
-        } else
-            new_nb = UINT_MAX;
-    } else if (!nb_rect_allocated)
-        new_nb = sub->num_rects + 1;
-
-    if (new_nb) {
-        rects = av_realloc_array(rects, new_nb, sizeof(*sub->rects));
-        if (!rects)
-            return AVERROR(ENOMEM);
-        if (nb_rect_allocated)
-            *nb_rect_allocated = new_nb;
-        sub->rects = rects;
-    }
-
-    rect       = av_mallocz(sizeof(*rect));
-    if (!rect)
-        return AVERROR(ENOMEM);
-    rects[sub->num_rects++] = rect;
-    rect->type = SUBTITLE_ASS;
-    ass_str = ff_ass_get_dialog(readorder, layer, style, speaker, dialog);
-    if (!ass_str)
-        return AVERROR(ENOMEM);
-    rect->ass = ass_str;
-    return 0;
-}
-
-int ff_ass_add_rect(AVSubtitle *sub, const char *dialog,
-                    int readorder, int layer, const char *style,
-                    const char *speaker)
+char *avpriv_ass_get_dialog_ex(int readorder, int layer, const char *style,
+                        const char *speaker, int margin_l, int margin_r,
+                        int margin_v, const char *effect, const char *text)
 {
-    return ff_ass_add_rect2(sub, dialog, readorder, layer, style, speaker, NULL);
-}
-
-void ff_ass_decoder_flush(AVCodecContext *avctx)
-{
-    FFASSDecoderContext *s = avctx->priv_data;
-    if (!(avctx->flags2 & AV_CODEC_FLAG2_RO_FLUSH_NOOP))
-        s->readorder = 0;
+    return av_asprintf("%d,%d,%s,%s,%d,%d,%d,%s,%s",
+                       readorder, layer, style ? style : "Default",
+                       speaker ? speaker : "", margin_l, margin_r,
+                       margin_v, effect ? effect : "", text);
 }
 
-void ff_ass_bprint_text_event(AVBPrint *buf, const char *p, int size,
+void avpriv_ass_bprint_text_event(AVBPrint *buf, const char *p, int size,
                              const char *linebreaks, int keep_ass_markup)
 {
     const char *p_end = p + size;
diff --git a/libavutil/ass_internal.h b/libavutil/ass_internal.h
new file mode 100644
index 0000000000..fc4e6232fb
--- /dev/null
+++ b/libavutil/ass_internal.h
@@ -0,0 +1,135 @@
+/*
+ * SSA/ASS common functions
+ * Copyright (c) 2010  Aurelien Jacobs <aurel@gnuage.org>
+ *
+ * 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
+ */
+
+#ifndef AVUTIL_ASS_INTERNAL_H
+#define AVUTIL_ASS_INTERNAL_H
+
+#include "subfmt.h"
+#include "libavutil/bprint.h"
+
+#define ASS_DEFAULT_PLAYRESX 384
+#define ASS_DEFAULT_PLAYRESY 288
+
+/**
+ * @name Default values for ASS style
+ * @{
+ */
+#define ASS_DEFAULT_FONT        "Arial"
+#define ASS_DEFAULT_FONT_SIZE   16
+#define ASS_DEFAULT_COLOR       0xffffff
+#define ASS_DEFAULT_BACK_COLOR  0
+#define ASS_DEFAULT_BOLD        0
+#define ASS_DEFAULT_ITALIC      0
+#define ASS_DEFAULT_UNDERLINE   0
+#define ASS_DEFAULT_ALIGNMENT   2
+#define ASS_DEFAULT_BORDERSTYLE 1
+/** @} */
+
+/**
+ * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS.
+ * Can specify all fields explicitly
+ *
+ * @param play_res_x subtitle frame width
+ * @param play_res_y subtitle frame height
+ * @param font name of the default font face to use
+ * @param font_size default font size to use
+ * @param primary_color default text color to use (ABGR)
+ * @param secondary_color default secondary text color to use (ABGR)
+ * @param outline_color default outline color to use (ABGR)
+ * @param back_color default background color to use (ABGR)
+ * @param bold 1 for bold text, 0 for normal text
+ * @param italic 1 for italic text, 0 for normal text
+ * @param underline 1 for underline text, 0 for normal text
+ * @param border_style 1 for outline, 3 for opaque box
+ * @param alignment position of the text (left, center, top...), defined after
+ *                  the layout of the numpad (1-3 sub, 4-6 mid, 7-9 top)
+ * @param print_av_version include library version in header
+ * @return a string containing the subtitle header that needs
+ *         to be released via av_free()
+ */
+char* avpriv_ass_get_subtitle_header_full(int play_res_x, int play_res_y,
+                                  const char *font, int font_size,
+                                  int primary_color, int secondary_color,
+                                  int outline_color, int back_color,
+                                  int bold, int italic, int underline,
+                                  int border_style, int alignment,
+                                  int print_av_version);
+
+/**
+ * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS.
+ *
+ * @param font name of the default font face to use
+ * @param font_size default font size to use
+ * @param color default text color to use (ABGR)
+ * @param back_color default background color to use (ABGR)
+ * @param bold 1 for bold text, 0 for normal text
+ * @param italic 1 for italic text, 0 for normal text
+ * @param underline 1 for underline text, 0 for normal text
+ * @param border_style 1 for outline, 3 for opaque box
+ * @param alignment position of the text (left, center, top...), defined after
+ *                  the layout of the numpad (1-3 sub, 4-6 mid, 7-9 top)
+ * @param print_av_version include library version in header
+ * @return a string containing the subtitle header that needs
+ *         to be released via av_free()
+ */
+char* avpriv_ass_get_subtitle_header(const char *font, int font_size,
+                                int color, int back_color,
+                                int bold, int italic, int underline,
+                                int border_style, int alignment,
+                                int print_av_version);
+
+/**
+ * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS
+ * with default style.
+ *
+ * @param print_av_version include library version in header
+ * @return a string containing the subtitle header that needs
+ *         to be released via av_free()
+ */
+char* avpriv_ass_get_subtitle_header_default(int print_av_version);
+
+/**
+ * Craft an ASS dialog string.
+ */
+char *avpriv_ass_get_dialog(int readorder, int layer, const char *style,
+                        const char *speaker, const char *text);
+
+/**
+ * Craft an ASS dialog string.
+ */
+char *avpriv_ass_get_dialog_ex(int readorder, int layer, const char *style,
+                        const char *speaker, int margin_l, int margin_r,
+                        int margin_v, const char *effect, const char *text);
+
+/**
+ * Escape a text subtitle using ASS syntax into an AVBPrint buffer.
+ * Newline characters will be escaped to \N.
+ *
+ * @param buf pointer to an initialized AVBPrint buffer
+ * @param p source text
+ * @param size size of the source text
+ * @param linebreaks additional newline chars, which will be escaped to \N
+ * @param keep_ass_markup braces and backslash will not be escaped if set
+ */
+void avpriv_ass_bprint_text_event(AVBPrint *buf, const char *p, int size,
+                             const char *linebreaks, int keep_ass_markup);
+
+#endif /* AVUTIL_ASS_INTERNAL_H */
diff --git a/libavcodec/ass_split.c b/libavutil/ass_split.c
similarity index 71%
rename from libavcodec/ass_split.c
rename to libavutil/ass_split.c
index 73ef6196c5..3e5e24e2fa 100644
--- a/libavcodec/ass_split.c
+++ b/libavutil/ass_split.c
@@ -28,7 +28,7 @@
 #include "libavutil/error.h"
 #include "libavutil/macros.h"
 #include "libavutil/mem.h"
-#include "ass_split.h"
+#include "ass_split_internal.h"
 
 typedef enum {
     ASS_STR,
@@ -379,7 +379,7 @@ static int ass_split(ASSSplitContext *ctx, const char *buf)
     return buf ? 0 : AVERROR_INVALIDDATA;
 }
 
-ASSSplitContext *ff_ass_split(const char *buf)
+ASSSplitContext *avpriv_ass_split(const char *buf)
 {
     ASSSplitContext *ctx = av_mallocz(sizeof(*ctx));
     if (!ctx)
@@ -388,7 +388,7 @@ ASSSplitContext *ff_ass_split(const char *buf)
         buf += 3;
     ctx->current_section = -1;
     if (ass_split(ctx, buf) < 0) {
-        ff_ass_split_free(ctx);
+        avpriv_ass_split_free(ctx);
         return NULL;
     }
     return ctx;
@@ -418,7 +418,7 @@ static void free_section(ASSSplitContext *ctx, const ASSSection *section)
         av_freep((uint8_t *)&ctx->ass + section->offset);
 }
 
-void ff_ass_free_dialog(ASSDialog **dialogp)
+void avpriv_ass_free_dialog(ASSDialog **dialogp)
 {
     ASSDialog *dialog = *dialogp;
     if (!dialog)
@@ -430,7 +430,7 @@ void ff_ass_free_dialog(ASSDialog **dialogp)
     av_freep(dialogp);
 }
 
-ASSDialog *ff_ass_split_dialog(ASSSplitContext *ctx, const char *buf)
+ASSDialog *avpriv_ass_split_dialog(ASSSplitContext *ctx, const char *buf)
 {
     int i;
     static const ASSFields fields[] = {
@@ -457,7 +457,7 @@ ASSDialog *ff_ass_split_dialog(ASSSplitContext *ctx, const char *buf)
         buf = skip_space(buf);
         len = last ? strlen(buf) : strcspn(buf, ",");
         if (len >= INT_MAX) {
-            ff_ass_free_dialog(&dialog);
+            avpriv_ass_free_dialog(&dialog);
             return NULL;
         }
         convert_func[type](ptr, buf, len);
@@ -467,7 +467,7 @@ ASSDialog *ff_ass_split_dialog(ASSSplitContext *ctx, const char *buf)
     return dialog;
 }
 
-void ff_ass_split_free(ASSSplitContext *ctx)
+void avpriv_ass_split_free(ASSSplitContext *ctx)
 {
     if (ctx) {
         int i;
@@ -479,87 +479,209 @@ void ff_ass_split_free(ASSSplitContext *ctx)
     }
 }
 
+static int ass_remove_empty_braces(AVBPrint* buffer)
+{
+    char* tmp;
+    int ret = 0, n = 0;
 
-int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
-                                const char *buf)
+    if (buffer == NULL || buffer->len == 0 || !av_bprint_is_complete(buffer))
+        return 0;
+
+    ret = av_bprint_finalize(buffer, &tmp);
+    if (ret)
+        return ret;
+
+    for (unsigned i = 0; i < buffer->len; i++) {
+        if (tmp[i] == '{' && tmp[i+1] == '}')
+            i++;
+        else
+            tmp[n++] = tmp[i];
+    }
+
+    tmp[n++] = '\0';
+
+    av_bprint_init(buffer, n, n);
+    av_bprint_append_data(buffer, tmp, n - 1);
+    av_free(tmp);
+
+    return ret;
+}
+
+static void ass_write_filtered_line(AVBPrint* buffer, const char *buf, int len, enum ASSSplitComponents keep_flags, enum ASSSplitComponents split_component)
+{
+    if (buffer == NULL || buf == NULL || len == 0)
+        return;
+
+    if (split_component != ASS_SPLIT_ANY && !(keep_flags & split_component))
+        return;
+
+
+    av_bprint_append_data(buffer, buf, len - 1);
+}
+
+int avpriv_ass_filter_override_codes(const ASSCodesCallbacks *callbacks, void *priv, const char *buf, AVBPrint* outbuffer, enum ASSSplitComponents keep_flags)
 {
     const char *text = NULL;
     char new_line[2];
-    int text_len = 0;
+    int text_len = 0, ret = 0;
 
     while (buf && *buf) {
-        if (text && callbacks->text &&
-            (sscanf(buf, "\\%1[nN]", new_line) == 1 ||
-             !strncmp(buf, "{\\", 2))) {
-            callbacks->text(priv, text, text_len);
+
+        if (text && (sscanf(buf, "\\%1[nN]", new_line) == 1 || !strncmp(buf, "{\\", 2))) {
+            ass_write_filtered_line(outbuffer, text, text_len + 1, keep_flags, ASS_SPLIT_TEXT | ASS_SPLIT_TEXT2);
+
+            if (callbacks->text)
+                callbacks->text(priv, text, text_len);
             text = NULL;
         }
+
         if (sscanf(buf, "\\%1[nN]", new_line) == 1) {
             if (callbacks->new_line)
                 callbacks->new_line(priv, new_line[0] == 'N');
+            ass_write_filtered_line(outbuffer, buf, 3, keep_flags, ASS_SPLIT_ANY);
             buf += 2;
         } else if (!strncmp(buf, "{\\", 2)) {
+            ass_write_filtered_line(outbuffer, buf, 2, keep_flags, ASS_SPLIT_ANY);
             buf++;
             while (*buf == '\\') {
-                char style[2], c[2], sep[2], c_num[2] = "0", tmp[128] = {0};
+                char style[4], c[2], axis[3], sep[3], c_num[2] = "0", tmp[128] = {0};
                 unsigned int color = 0xFFFFFFFF;
-                int len, size = -1, an = -1, alpha = -1;
-                int x1, y1, x2, y2, t1 = -1, t2 = -1;
+                int len, size = -1, an = -1, alpha = -1, scale = 0;
+                float f1 = 1;
+                int x1, y1, x2, y2, x3, t1 = -1, t2 = -1, t3 = -1, t4 = -1, accel = 1;
                 if (sscanf(buf, "\\%1[bisu]%1[01\\}]%n", style, c, &len) > 1) {
                     int close = c[0] == '0' ? 1 : c[0] == '1' ? 0 : -1;
                     len += close != -1;
+                    switch (c[0]) {
+                    case 'b':
+                        ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FONT_BOLD);
+                        break;
+                    case 'u':
+                        ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FONT_UNDERLINE);
+                        break;
+                    case 'i':
+                        ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FONT_ITALIC);
+                        break;
+                    case 'a':
+                        ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FONT_STRIKEOUT);
+                        break;
+                    }
                     if (callbacks->style)
                         callbacks->style(priv, style[0], close);
                 } else if (sscanf(buf, "\\c%1[\\}]%n", sep, &len) > 0 ||
                            sscanf(buf, "\\c&H%X&%1[\\}]%n", &color, sep, &len) > 1 ||
                            sscanf(buf, "\\%1[1234]c%1[\\}]%n", c_num, sep, &len) > 1 ||
                            sscanf(buf, "\\%1[1234]c&H%X&%1[\\}]%n", c_num, &color, sep, &len) > 2) {
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_COLOR);
                     if (callbacks->color)
                         callbacks->color(priv, color, c_num[0] - '0');
                 } else if (sscanf(buf, "\\alpha%1[\\}]%n", sep, &len) > 0 ||
                            sscanf(buf, "\\alpha&H%2X&%1[\\}]%n", &alpha, sep, &len) > 1 ||
                            sscanf(buf, "\\%1[1234]a%1[\\}]%n", c_num, sep, &len) > 1 ||
                            sscanf(buf, "\\%1[1234]a&H%2X&%1[\\}]%n", c_num, &alpha, sep, &len) > 2) {
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_ALPHA);
                     if (callbacks->alpha)
                         callbacks->alpha(priv, alpha, c_num[0] - '0');
                 } else if (sscanf(buf, "\\fn%1[\\}]%n", sep, &len) > 0 ||
                            sscanf(buf, "\\fn%127[^\\}]%1[\\}]%n", tmp, sep, &len) > 1) {
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FONT_NAME);
                     if (callbacks->font_name)
                         callbacks->font_name(priv, tmp[0] ? tmp : NULL);
                 } else if (sscanf(buf, "\\fs%1[\\}]%n", sep, &len) > 0 ||
                            sscanf(buf, "\\fs%u%1[\\}]%n", &size, sep, &len) > 1) {
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FONT_SIZE);
                     if (callbacks->font_size)
                         callbacks->font_size(priv, size);
+                } else if (sscanf(buf, "\\fscx%1[\\}]%n", sep, &len) > 0 ||
+                           sscanf(buf, "\\fscx%f%1[\\}]%n", &f1, sep, &len) > 1) {
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FONT_SCALE);
+                } else if (sscanf(buf, "\\fscy%1[\\}]%n", sep, &len) > 0 ||
+                           sscanf(buf, "\\fscy%f%1[\\}]%n", &f1, sep, &len) > 1) {
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FONT_SCALE);
+                } else if (sscanf(buf, "\\fsp%1[\\}]%n", sep, &len) > 0 ||
+                           sscanf(buf, "\\fsp%u%1[\\}]%n", &size, sep, &len) > 1) {
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FONT_SPACING);
+                } else if (sscanf(buf, "\\fe%1[\\}]%n", sep, &len) > 0 ||
+                           sscanf(buf, "\\fe%u%1[\\}]%n", &size, sep, &len) > 1) {
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FONT_CHARSET);
+                } else if (sscanf(buf, "\\bord%1[\\}]%n", sep, &len) > 0 ||
+                           sscanf(buf, "\\bord%u%1[\\}]%n", &size, sep, &len) > 1) {
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_TEXT_BORDER);
+                } else if (sscanf(buf, "\\shad%1[\\}]%n", sep, &len) > 0 ||
+                           sscanf(buf, "\\shad%u%1[\\}]%n", &size, sep, &len) > 1) {
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_TEXT_SHADOW);
+                } else if (sscanf(buf, "\\fr%1[\\}]%n", sep, &len) > 0 ||
+                           sscanf(buf, "\\fr%u%1[\\}]%n", &x1, sep, &len) > 1 ||
+                           sscanf(buf, "\\fr%1[xyz]%1[\\}]%n", axis, sep, &len) > 1 ||
+                           sscanf(buf, "\\fr%1[xyz]%u%1[\\}]%n", axis, &size, sep, &len) > 2) {
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_TEXT_ROTATE);
+                } else if (sscanf(buf, "\\blur%1[\\}]%n", sep, &len) > 0 ||
+                           sscanf(buf, "\\blur%u%1[\\}]%n", &size, sep, &len) > 1) {
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_TEXT_BLUR);
+                } else if (sscanf(buf, "\\be%1[\\}]%n", sep, &len) > 0 ||
+                           sscanf(buf, "\\be%u%1[\\}]%n", &size, sep, &len) > 1) {
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_TEXT_BLUR);
+                } else if (sscanf(buf, "\\q%1[\\}]%n", sep, &len) > 0 ||
+                           sscanf(buf, "\\q%u%1[\\}]%n", &size, sep, &len) > 1) {
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_TEXT_WRAP);
                 } else if (sscanf(buf, "\\a%1[\\}]%n", sep, &len) > 0 ||
                            sscanf(buf, "\\a%2u%1[\\}]%n", &an, sep, &len) > 1 ||
                            sscanf(buf, "\\an%1[\\}]%n", sep, &len) > 0 ||
                            sscanf(buf, "\\an%1u%1[\\}]%n", &an, sep, &len) > 1) {
                     if (an != -1 && buf[2] != 'n')
                         an = (an&3) + (an&4 ? 6 : an&8 ? 3 : 0);
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_TEXT_ALIGNMENT);
                     if (callbacks->alignment)
                         callbacks->alignment(priv, an);
                 } else if (sscanf(buf, "\\r%1[\\}]%n", sep, &len) > 0 ||
                            sscanf(buf, "\\r%127[^\\}]%1[\\}]%n", tmp, sep, &len) > 1) {
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_CANCELLING);
                     if (callbacks->cancel_overrides)
                         callbacks->cancel_overrides(priv, tmp);
                 } else if (sscanf(buf, "\\move(%d,%d,%d,%d)%1[\\}]%n", &x1, &y1, &x2, &y2, sep, &len) > 4 ||
                            sscanf(buf, "\\move(%d,%d,%d,%d,%d,%d)%1[\\}]%n", &x1, &y1, &x2, &y2, &t1, &t2, sep, &len) > 6) {
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_MOVE);
                     if (callbacks->move)
                         callbacks->move(priv, x1, y1, x2, y2, t1, t2);
                 } else if (sscanf(buf, "\\pos(%d,%d)%1[\\}]%n", &x1, &y1, sep, &len) > 2) {
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_POS);
                     if (callbacks->move)
                         callbacks->move(priv, x1, y1, x1, y1, -1, -1);
                 } else if (sscanf(buf, "\\org(%d,%d)%1[\\}]%n", &x1, &y1, sep, &len) > 2) {
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_ORIGIN);
                     if (callbacks->origin)
                         callbacks->origin(priv, x1, y1);
+                } else if (sscanf(buf, "\\t(%1[\\}]%n", sep, &len) > 0 ||
+                           sscanf(buf, "\\t(%d,%d,%1[\\}]%n", &t1, &t2, sep, &len) > 2 ||
+                           sscanf(buf, "\\t(%d,%d,%d,%1[\\}]%n", &t1, &t2, &accel, sep, &len) > 3) {
+
+                    len = strcspn(buf, ")") + 2;
+
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_ANIMATE);
+                    if (callbacks->animate)
+                        callbacks->animate(priv, t1, t2, accel, tmp);
+                } else if (sscanf(buf, "\\fade(%d,%d,%d,%d,%d,%d,%d)%1[\\}]%n", &x1, &x2, &x3, &t1, &t2, &t3, &t4, sep, &len) > 7) {
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FADE);
+                } else if (sscanf(buf, "\\fad(%d,%d)%1[\\}]%n", &t1, &t2, sep, &len) > 2) {
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FADE);
+                } else if (sscanf(buf, "\\clip(%d,%d,%d,%d)%1[\\}]%n", &x1, &y1, &x2, &y2, sep, &len) > 4) {
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_CLIP);
+                } else if (sscanf(buf, "\\p%1[\\}]%n", sep, &len) > 0 ||
+                           sscanf(buf, "\\p%u%1[\\}]%n", &scale, sep, &len) > 1) {
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_DRAW);
+                    if (callbacks->drawing_mode)
+                        callbacks->drawing_mode(priv, scale);
                 } else {
-                    len = strcspn(buf+1, "\\}") + 2;  /* skip unknown code */
-                }
+                    len = strcspn(buf+1, "\\}") + 2;  /* unknown code */
+                    ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_UNKNOWN);
+             }
                 buf += len - 1;
             }
             if (*buf++ != '}')
                 return AVERROR_INVALIDDATA;
-        } else {
+
+            ass_write_filtered_line(outbuffer, "}", 2, keep_flags, ASS_SPLIT_ANY);
+     } else {
             if (!text) {
                 text = buf;
                 text_len = 1;
@@ -568,14 +690,27 @@ int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
             buf++;
         }
     }
+    if (text)
+        ass_write_filtered_line(outbuffer, text, text_len + 1, keep_flags, ASS_SPLIT_TEXT | ASS_SPLIT_TEXT2);
     if (text && callbacks->text)
         callbacks->text(priv, text, text_len);
     if (callbacks->end)
         callbacks->end(priv);
-    return 0;
+
+    if (outbuffer)
+        ret = ass_remove_empty_braces(outbuffer);
+
+    return ret;
+}
+
+
+int avpriv_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
+                                const char *buf)
+{
+    return avpriv_ass_filter_override_codes(callbacks, priv, buf, NULL, 0);
 }
 
-ASSStyle *ff_ass_style_get(ASSSplitContext *ctx, const char *style)
+ASSStyle *avpriv_ass_style_get(ASSSplitContext *ctx, const char *style)
 {
     ASS *ass = &ctx->ass;
     int i;
diff --git a/libavutil/ass_split_internal.h b/libavutil/ass_split_internal.h
new file mode 100644
index 0000000000..5d82aa8886
--- /dev/null
+++ b/libavutil/ass_split_internal.h
@@ -0,0 +1,254 @@
+/*
+ * SSA/ASS spliting functions
+ * Copyright (c) 2010  Aurelien Jacobs <aurel@gnuage.org>
+ *
+ * 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
+ */
+
+#ifndef AVUTIL_ASS_SPLIT_INTERNAL_H
+#define AVUTIL_ASS_SPLIT_INTERNAL_H
+
+#include "bprint.h"
+
+enum ASSSplitComponents
+{
+    ASS_SPLIT_ANY = 0,
+    ASS_SPLIT_TEXT           = (1 << 0),
+    ASS_SPLIT_TEXT2          = (1 << 1), // Same semantics as ASS_SPLIT_TEXT. To work around help output default display.
+    ASS_SPLIT_COLOR          = (1 << 2),
+    ASS_SPLIT_ALPHA          = (1 << 3),
+    ASS_SPLIT_FONT_NAME      = (1 << 4),
+    ASS_SPLIT_FONT_SIZE      = (1 << 5),
+    ASS_SPLIT_FONT_SCALE     = (1 << 6),
+    ASS_SPLIT_FONT_SPACING   = (1 << 7),
+    ASS_SPLIT_FONT_CHARSET   = (1 << 8),
+    ASS_SPLIT_FONT_BOLD      = (1 << 9),
+    ASS_SPLIT_FONT_ITALIC    = (1 << 10),
+    ASS_SPLIT_FONT_UNDERLINE = (1 << 11),
+    ASS_SPLIT_FONT_STRIKEOUT = (1 << 12),
+    ASS_SPLIT_TEXT_BORDER    = (1 << 13),
+    ASS_SPLIT_TEXT_SHADOW    = (1 << 14),
+    ASS_SPLIT_TEXT_ROTATE    = (1 << 15),
+    ASS_SPLIT_TEXT_BLUR      = (1 << 16),
+    ASS_SPLIT_TEXT_WRAP      = (1 << 17),
+    ASS_SPLIT_TEXT_ALIGNMENT = (1 << 18),
+    ASS_SPLIT_CANCELLING     = (1 << 19),
+    ASS_SPLIT_MOVE           = (1 << 20),
+    ASS_SPLIT_POS            = (1 << 21),
+    ASS_SPLIT_ORIGIN         = (1 << 22),
+    ASS_SPLIT_DRAW           = (1 << 23),
+    ASS_SPLIT_ANIMATE        = (1 << 24),
+    ASS_SPLIT_FADE           = (1 << 25),
+    ASS_SPLIT_CLIP           = (1 << 26),
+    ASS_SPLIT_UNKNOWN        = (1 << 27),
+
+    ASS_SPLIT_BASIC =  ASS_SPLIT_TEXT2 | ASS_SPLIT_COLOR | ASS_SPLIT_ALPHA | ASS_SPLIT_FONT_NAME | ASS_SPLIT_FONT_SIZE | ASS_SPLIT_FONT_SCALE | ASS_SPLIT_FONT_SPACING | ASS_SPLIT_FONT_CHARSET | ASS_SPLIT_FONT_BOLD | ASS_SPLIT_FONT_ITALIC | ASS_SPLIT_FONT_UNDERLINE | ASS_SPLIT_FONT_STRIKEOUT | ASS_SPLIT_TEXT_BORDER | ASS_SPLIT_TEXT_SHADOW | ASS_SPLIT_TEXT_WRAP | ASS_SPLIT_TEXT_ALIGNMENT | ASS_SPLIT_POS | ASS_SPLIT_CANCELLING,
+    ASS_SPLIT_ALL_KNOWN =  ASS_SPLIT_TEXT2 | ASS_SPLIT_COLOR | ASS_SPLIT_ALPHA | ASS_SPLIT_FONT_NAME | ASS_SPLIT_FONT_SIZE | ASS_SPLIT_FONT_SCALE | ASS_SPLIT_FONT_SPACING | ASS_SPLIT_FONT_CHARSET | ASS_SPLIT_FONT_BOLD | ASS_SPLIT_FONT_ITALIC | ASS_SPLIT_FONT_UNDERLINE | ASS_SPLIT_FONT_STRIKEOUT | ASS_SPLIT_TEXT_BORDER | ASS_SPLIT_TEXT_SHADOW | ASS_SPLIT_TEXT_ROTATE | ASS_SPLIT_TEXT_BLUR | ASS_SPLIT_TEXT_WRAP | ASS_SPLIT_TEXT_ALIGNMENT | ASS_SPLIT_CANCELLING | ASS_SPLIT_POS | ASS_SPLIT_MOVE | ASS_SPLIT_ORIGIN | ASS_SPLIT_DRAW | ASS_SPLIT_ANIMATE | ASS_SPLIT_FADE | ASS_SPLIT_CLIP,
+};
+
+    /**
+     * fields extracted from the [Script Info] section
+     */
+    typedef struct {
+      char *script_type; /**< SSA script format version (eg. v4.00) */
+  char *collisions;  /**< how subtitles are moved to prevent collisions */
+  int play_res_x;    /**< video width that ASS coords are referring to */
+  int play_res_y;    /**< video height that ASS coords are referring to */
+  float timer;       /**< time multiplier to apply to SSA clock (in %) */
+} ASSScriptInfo;
+
+/**
+ * fields extracted from the [V4(+) Styles] section
+ */
+typedef struct {
+  char *name;        /**< name of the tyle (case sensitive) */
+  char *font_name;   /**< font face (case sensitive) */
+  int font_size;     /**< font height */
+  int primary_color; /**< color that a subtitle will normally appear in */
+  int secondary_color;
+  int outline_color; /**< color for outline in ASS, called tertiary in SSA */
+  int back_color;    /**< color of the subtitle outline or shadow */
+  int bold;          /**< whether text is bold (1) or not (0) */
+  int italic;        /**< whether text is italic (1) or not (0) */
+  int underline;     /**< whether text is underlined (1) or not (0) */
+  int strikeout;
+  float scalex;
+  float scaley;
+  float spacing;
+  float angle;
+  int border_style;
+  float outline;
+  float shadow;
+  int alignment; /**< position of the text (left, center, top...),
+                      defined after the layout of the numpad
+                      (1-3 sub, 4-6 mid, 7-9 top) */
+  int margin_l;
+  int margin_r;
+  int margin_v;
+  int alpha_level;
+  int encoding;
+} ASSStyle;
+
+/**
+ * fields extracted from the [Events] section
+ */
+typedef struct {
+  int readorder;
+  int layer;   /**< higher numbered layers are drawn over lower numbered */
+  int start;   /**< start time of the dialog in centiseconds */
+  int end;     /**< end time of the dialog in centiseconds */
+  char *style; /**< name of the ASSStyle to use with this dialog */
+  char *name;
+  int margin_l;
+  int margin_r;
+  int margin_v;
+  char *effect;
+  char *text; /**< actual text which will be displayed as a subtitle,
+                   can include style override control codes (see
+                   avpriv_ass_split_override_codes()) */
+} ASSDialog;
+
+/**
+ * structure containing the whole split ASS data
+ */
+typedef struct {
+  ASSScriptInfo script_info; /**< general information about the SSA script*/
+  ASSStyle *styles;          /**< array of split out styles */
+  int styles_count;          /**< number of ASSStyle in the styles array */
+  ASSDialog *dialogs;        /**< array of split out dialogs */
+  int dialogs_count;         /**< number of ASSDialog in the dialogs array*/
+} ASS;
+
+/**
+ * This struct can be casted to ASS to access to the split data.
+ */
+typedef struct ASSSplitContext ASSSplitContext;
+
+/**
+ * Split a full ASS file or a ASS header from a string buffer and store
+ * the split structure in a newly allocated context.
+ *
+ * @param buf String containing the ASS formatted data.
+ * @return Newly allocated struct containing split data.
+ */
+ASSSplitContext *avpriv_ass_split(const char *buf);
+
+/**
+ * Free a dialogue obtained from avpriv_ass_split_dialog().
+ */
+void avpriv_ass_free_dialog(ASSDialog **dialogp);
+
+/**
+ * Split one ASS Dialogue line from a string buffer.
+ *
+ * @param ctx Context previously initialized by ff_ass_split().
+ * @param buf String containing the ASS "Dialogue" line.
+ * @return Pointer to the split ASSDialog. Must be freed with
+ * ff_ass_free_dialog()
+ */
+ASSDialog *avpriv_ass_split_dialog(ASSSplitContext *ctx, const char *buf);
+
+/**
+ * Free all the memory allocated for an ASSSplitContext.
+ *
+ * @param ctx Context previously initialized by ff_ass_split().
+ */
+void avpriv_ass_split_free(ASSSplitContext *ctx);
+
+/**
+ * Set of callback functions corresponding to each override codes that can
+ * be encountered in a "Dialogue" Text field.
+ */
+typedef struct {
+  /**
+   * @defgroup ass_styles    ASS styles
+   * @{
+   */
+  void (*text)(void *priv, const char *text, int len);
+  void (*hard_space)(void *priv);
+  void (*new_line)(void *priv, int forced);
+  void (*style)(void *priv, char style, int close);
+  void (*color)(void *priv, unsigned int /* color */, unsigned int color_id);
+  void (*alpha)(void *priv, int alpha, int alpha_id);
+  void (*font_name)(void *priv, const char *name);
+  void (*font_size)(void *priv, int size);
+  void (*alignment)(void *priv, int alignment);
+  void (*cancel_overrides)(void *priv, const char *style);
+  /** @} */
+
+  /**
+   * @defgroup ass_functions    ASS functions
+   * @{
+   */
+  void (*move)(void *priv, int x1, int y1, int x2, int y2, int t1, int t2);
+  void (*animate)(void *priv, int t1, int t2, int accel, char *style);
+  void (*origin)(void *priv, int x, int y);
+  void (*drawing_mode)(void *priv, int scale);
+  /** @} */
+
+  /**
+   * @defgroup ass_ext    ASS extensible parsing callback
+   * @{
+   */
+  void (*ext)(void *priv, int ext_id, const char *text, int p1, int p2);
+  /** @} */
+
+  /**
+   * @defgroup ass_end    end of Dialogue Event
+   * @{
+   */
+  void (*end)(void *priv);
+  /** @} */
+} ASSCodesCallbacks;
+
+/**
+ * Split override codes out of a ASS "Dialogue" Text field.
+ *
+ * @param callbacks Set of callback functions called for each override code
+ *                  encountered.
+ * @param priv Opaque pointer passed to the callback functions.
+ * @param buf The ASS "Dialogue" Text field to split.
+ * @param outbuffer The output buffer.
+ * @param keep_flags Flags for filtering ass codes.
+ * @return >= 0 on success otherwise an error code <0
+ */
+int avpriv_ass_filter_override_codes(const ASSCodesCallbacks *callbacks,
+                                     void *priv, const char *buf,
+                                     AVBPrint *outbuffer, enum ASSSplitComponents keep_flags);
+
+/**
+ * Split override codes out of a ASS "Dialogue" Text field.
+ *
+ * @param callbacks Set of callback functions called for each override code
+ *                  encountered.
+ * @param priv Opaque pointer passed to the callback functions.
+ * @param buf The ASS "Dialogue" Text field to split.
+ * @return >= 0 on success otherwise an error code <0
+ */
+int avpriv_ass_split_override_codes(const ASSCodesCallbacks *callbacks,
+                                    void *priv, const char *buf);
+
+/**
+ * Find an ASSStyle structure by its name.
+ *
+ * @param ctx Context previously initialized by ff_ass_split().
+ * @param style name of the style to search for.
+ * @return the ASSStyle corresponding to style, or NULL if style can't be found
+ */
+ASSStyle *avpriv_ass_style_get(ASSSplitContext *ctx, const char *style);
+
+#endif /* AVUTIL_ASS_SPLIT_INTERNAL_H */
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v6 07/25] avcodec/subtitles: Replace deprecated enum values
  2022-06-26 16:34         ` [FFmpeg-devel] [PATCH v6 00/25] Subtitle Filtering 2022 ffmpegagent
                             ` (5 preceding siblings ...)
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 06/25] avcodec, avutil: Move ass helper functions to avutil as avpriv_ and extend ass dialog parsing softworkz
@ 2022-06-26 16:35           ` softworkz
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 08/25] fftools/play, probe: Adjust for subtitle changes softworkz
                             ` (17 subsequent siblings)
  24 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-26 16:35 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/ass.h       | 2 +-
 libavcodec/assdec.c    | 2 +-
 libavcodec/dvbsubdec.c | 2 +-
 libavcodec/dvdsubdec.c | 2 +-
 libavcodec/dvdsubenc.c | 2 +-
 libavcodec/pgssubdec.c | 2 +-
 libavcodec/xsubdec.c   | 2 +-
 7 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/libavcodec/ass.h b/libavcodec/ass.h
index 8bc13d7ab8..43c5ad651a 100644
--- a/libavcodec/ass.h
+++ b/libavcodec/ass.h
@@ -83,7 +83,7 @@ static inline int avpriv_ass_add_rect(AVSubtitle *sub, const char *dialog,
     rects[sub->num_rects]       = av_mallocz(sizeof(*rects[0]));
     if (!rects[sub->num_rects])
         return AVERROR(ENOMEM);
-    rects[sub->num_rects]->type = SUBTITLE_ASS;
+    rects[sub->num_rects]->type = AV_SUBTITLE_FMT_ASS;
     ass_str = avpriv_ass_get_dialog(readorder, layer, style, speaker, dialog);
     if (!ass_str)
         return AVERROR(ENOMEM);
diff --git a/libavcodec/assdec.c b/libavcodec/assdec.c
index 1a1363471d..7bb60c9b26 100644
--- a/libavcodec/assdec.c
+++ b/libavcodec/assdec.c
@@ -53,7 +53,7 @@ static int ass_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
     if (!sub->rects[0])
         return AVERROR(ENOMEM);
     sub->num_rects = 1;
-    sub->rects[0]->type = SUBTITLE_ASS;
+    sub->rects[0]->type = AV_SUBTITLE_FMT_ASS;
     sub->rects[0]->ass  = av_strdup(avpkt->data);
     if (!sub->rects[0]->ass)
         return AVERROR(ENOMEM);
diff --git a/libavcodec/dvbsubdec.c b/libavcodec/dvbsubdec.c
index 4d4007ffd9..8d44529b4a 100644
--- a/libavcodec/dvbsubdec.c
+++ b/libavcodec/dvbsubdec.c
@@ -796,7 +796,7 @@ static int save_subtitle_set(AVCodecContext *avctx, AVSubtitle *sub, int *got_ou
             rect->w = region->width;
             rect->h = region->height;
             rect->nb_colors = (1 << region->depth);
-            rect->type      = SUBTITLE_BITMAP;
+            rect->type      = AV_SUBTITLE_FMT_BITMAP;
             rect->linesize[0] = region->width;
 
             clut = get_clut(ctx, region->clut);
diff --git a/libavcodec/dvdsubdec.c b/libavcodec/dvdsubdec.c
index 97f366cc74..cb6dbbbbbc 100644
--- a/libavcodec/dvdsubdec.c
+++ b/libavcodec/dvdsubdec.c
@@ -404,7 +404,7 @@ static int decode_dvd_subtitles(DVDSubContext *ctx, AVSubtitle *sub_header,
                 sub_header->rects[0]->y = y1;
                 sub_header->rects[0]->w = w;
                 sub_header->rects[0]->h = h;
-                sub_header->rects[0]->type = SUBTITLE_BITMAP;
+                sub_header->rects[0]->type = AV_SUBTITLE_FMT_BITMAP;
                 sub_header->rects[0]->linesize[0] = w;
                 sub_header->rects[0]->flags = is_menu ? AV_SUBTITLE_FLAG_FORCED : 0;
             }
diff --git a/libavcodec/dvdsubenc.c b/libavcodec/dvdsubenc.c
index d29db7d49c..24da94faee 100644
--- a/libavcodec/dvdsubenc.c
+++ b/libavcodec/dvdsubenc.c
@@ -269,7 +269,7 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
     if (rects == 0 || !h->rects)
         return AVERROR(EINVAL);
     for (i = 0; i < rects; i++)
-        if (h->rects[i]->type != SUBTITLE_BITMAP) {
+        if (h->rects[i]->type != AV_SUBTITLE_FMT_BITMAP) {
             av_log(avctx, AV_LOG_ERROR, "Bitmap subtitle required\n");
             return AVERROR(EINVAL);
         }
diff --git a/libavcodec/pgssubdec.c b/libavcodec/pgssubdec.c
index e50c6766c5..05399863b6 100644
--- a/libavcodec/pgssubdec.c
+++ b/libavcodec/pgssubdec.c
@@ -535,7 +535,7 @@ static int display_end_segment(AVCodecContext *avctx, AVSubtitle *sub,
         if (!rect)
             return AVERROR(ENOMEM);
         sub->rects[sub->num_rects++] = rect;
-        rect->type = SUBTITLE_BITMAP;
+        rect->type = AV_SUBTITLE_FMT_BITMAP;
 
         /* Process bitmap */
         object = find_object(ctx->presentation.objects[i].id, &ctx->objects);
diff --git a/libavcodec/xsubdec.c b/libavcodec/xsubdec.c
index d62fa164a5..30c3595c97 100644
--- a/libavcodec/xsubdec.c
+++ b/libavcodec/xsubdec.c
@@ -107,7 +107,7 @@ static int decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
     sub->num_rects = 1;
     rect->x = x; rect->y = y;
     rect->w = w; rect->h = h;
-    rect->type = SUBTITLE_BITMAP;
+    rect->type = AV_SUBTITLE_FMT_BITMAP;
     rect->linesize[0] = w;
     rect->data[0] = av_malloc(w * h);
     rect->nb_colors = 4;
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v6 08/25] fftools/play, probe: Adjust for subtitle changes
  2022-06-26 16:34         ` [FFmpeg-devel] [PATCH v6 00/25] Subtitle Filtering 2022 ffmpegagent
                             ` (6 preceding siblings ...)
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 07/25] avcodec/subtitles: Replace deprecated enum values softworkz
@ 2022-06-26 16:35           ` softworkz
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 09/25] avfilter/subtitles: Add subtitles.c for subtitle frame allocation softworkz
                             ` (16 subsequent siblings)
  24 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-26 16:35 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 fftools/ffplay.c  | 102 +++++++++++++++++++++-------------------------
 fftools/ffprobe.c |  47 +++++++++++++--------
 2 files changed, 77 insertions(+), 72 deletions(-)

diff --git a/fftools/ffplay.c b/fftools/ffplay.c
index 040afa0189..111e157979 100644
--- a/fftools/ffplay.c
+++ b/fftools/ffplay.c
@@ -153,7 +153,6 @@ typedef struct Clock {
 /* Common struct for handling all types of decoded data and allocated render buffers. */
 typedef struct Frame {
     AVFrame *frame;
-    AVSubtitle sub;
     int serial;
     double pts;           /* presentation timestamp for the frame */
     double duration;      /* estimated duration of the frame */
@@ -574,7 +573,7 @@ static int decoder_init(Decoder *d, AVCodecContext *avctx, PacketQueue *queue, S
     return 0;
 }
 
-static int decoder_decode_frame(Decoder *d, AVFrame *frame, AVSubtitle *sub) {
+static int decoder_decode_frame(Decoder *d, AVFrame *frame) {
     int ret = AVERROR(EAGAIN);
 
     for (;;) {
@@ -608,6 +607,9 @@ static int decoder_decode_frame(Decoder *d, AVFrame *frame, AVSubtitle *sub) {
                             }
                         }
                         break;
+                    case AVMEDIA_TYPE_SUBTITLE:
+                        ret = avcodec_receive_frame(d->avctx, frame);
+                        break;
                 }
                 if (ret == AVERROR_EOF) {
                     d->finished = d->pkt_serial;
@@ -640,25 +642,11 @@ static int decoder_decode_frame(Decoder *d, AVFrame *frame, AVSubtitle *sub) {
             av_packet_unref(d->pkt);
         } while (1);
 
-        if (d->avctx->codec_type == AVMEDIA_TYPE_SUBTITLE) {
-            int got_frame = 0;
-            ret = avcodec_decode_subtitle2(d->avctx, sub, &got_frame, d->pkt);
-            if (ret < 0) {
-                ret = AVERROR(EAGAIN);
-            } else {
-                if (got_frame && !d->pkt->data) {
-                    d->packet_pending = 1;
-                }
-                ret = got_frame ? 0 : (d->pkt->data ? AVERROR(EAGAIN) : AVERROR_EOF);
-            }
-            av_packet_unref(d->pkt);
+        if (avcodec_send_packet(d->avctx, d->pkt) == AVERROR(EAGAIN)) {
+            av_log(d->avctx, AV_LOG_ERROR, "Receive_frame and send_packet both returned EAGAIN, which is an API violation.\n");
+            d->packet_pending = 1;
         } else {
-            if (avcodec_send_packet(d->avctx, d->pkt) == AVERROR(EAGAIN)) {
-                av_log(d->avctx, AV_LOG_ERROR, "Receive_frame and send_packet both returned EAGAIN, which is an API violation.\n");
-                d->packet_pending = 1;
-            } else {
-                av_packet_unref(d->pkt);
-            }
+            av_packet_unref(d->pkt);
         }
     }
 }
@@ -671,7 +659,6 @@ static void decoder_destroy(Decoder *d) {
 static void frame_queue_unref_item(Frame *vp)
 {
     av_frame_unref(vp->frame);
-    avsubtitle_free(&vp->sub);
 }
 
 static int frame_queue_init(FrameQueue *f, PacketQueue *pktq, int max_size, int keep_last)
@@ -969,7 +956,7 @@ static void video_image_display(VideoState *is)
         if (frame_queue_nb_remaining(&is->subpq) > 0) {
             sp = frame_queue_peek(&is->subpq);
 
-            if (vp->pts >= sp->pts + ((float) sp->sub.start_display_time / 1000)) {
+            if (vp->pts >= sp->pts) {
                 if (!sp->uploaded) {
                     uint8_t* pixels[4];
                     int pitch[4];
@@ -981,25 +968,27 @@ static void video_image_display(VideoState *is)
                     if (realloc_texture(&is->sub_texture, SDL_PIXELFORMAT_ARGB8888, sp->width, sp->height, SDL_BLENDMODE_BLEND, 1) < 0)
                         return;
 
-                    for (i = 0; i < sp->sub.num_rects; i++) {
-                        AVSubtitleRect *sub_rect = sp->sub.rects[i];
+                    for (i = 0; i < sp->frame->num_subtitle_areas; i++) {
+                        AVSubtitleArea *area = sp->frame->subtitle_areas[i];
+                        SDL_Rect sdl_rect = { .x = area->x, .y = area->y, .w = area->w, .h = area->h };
 
-                        sub_rect->x = av_clip(sub_rect->x, 0, sp->width );
-                        sub_rect->y = av_clip(sub_rect->y, 0, sp->height);
-                        sub_rect->w = av_clip(sub_rect->w, 0, sp->width  - sub_rect->x);
-                        sub_rect->h = av_clip(sub_rect->h, 0, sp->height - sub_rect->y);
+                        area->x = av_clip(area->x, 0, sp->width );
+                        area->y = av_clip(area->y, 0, sp->height);
+                        area->w = av_clip(area->w, 0, sp->width  - area->x);
+                        area->h = av_clip(area->h, 0, sp->height - area->y);
 
                         is->sub_convert_ctx = sws_getCachedContext(is->sub_convert_ctx,
-                            sub_rect->w, sub_rect->h, AV_PIX_FMT_PAL8,
-                            sub_rect->w, sub_rect->h, AV_PIX_FMT_BGRA,
+                            area->w, area->h, AV_PIX_FMT_PAL8,
+                            area->w, area->h, AV_PIX_FMT_BGRA,
                             0, NULL, NULL, NULL);
                         if (!is->sub_convert_ctx) {
                             av_log(NULL, AV_LOG_FATAL, "Cannot initialize the conversion context\n");
                             return;
                         }
-                        if (!SDL_LockTexture(is->sub_texture, (SDL_Rect *)sub_rect, (void **)pixels, pitch)) {
-                            sws_scale(is->sub_convert_ctx, (const uint8_t * const *)sub_rect->data, sub_rect->linesize,
-                                      0, sub_rect->h, pixels, pitch);
+                        if (!SDL_LockTexture(is->sub_texture, &sdl_rect, (void **)pixels, pitch)) {
+                            const uint8_t* data[2] = { area->buf[0]->data, (uint8_t *)&area->pal };
+                            sws_scale(is->sub_convert_ctx, data, area->linesize,
+                                      0, area->h, pixels, pitch);
                             SDL_UnlockTexture(is->sub_texture);
                         }
                     }
@@ -1026,16 +1015,18 @@ static void video_image_display(VideoState *is)
 #if USE_ONEPASS_SUBTITLE_RENDER
         SDL_RenderCopy(renderer, is->sub_texture, NULL, &rect);
 #else
-        int i;
+        unsigned i;
         double xratio = (double)rect.w / (double)sp->width;
         double yratio = (double)rect.h / (double)sp->height;
-        for (i = 0; i < sp->sub.num_rects; i++) {
-            SDL_Rect *sub_rect = (SDL_Rect*)sp->sub.rects[i];
-            SDL_Rect target = {.x = rect.x + sub_rect->x * xratio,
-                               .y = rect.y + sub_rect->y * yratio,
-                               .w = sub_rect->w * xratio,
-                               .h = sub_rect->h * yratio};
-            SDL_RenderCopy(renderer, is->sub_texture, sub_rect, &target);
+        for (i = 0; i < sp->frame->num_subtitle_areas; i++) {
+            AVSubtitleArea *area = sp->frame->subtitle_areas[i];
+            SDL_Rect sub_rect = { .x = area->x, .y = area->y,
+                                  .w = area->w, .h = area->h};
+            SDL_Rect target = {.x = rect.x + sub_rect.x * xratio,
+                               .y = rect.y + sub_rect.y * yratio,
+                               .w = sub_rect.w * xratio,
+                               .h = sub_rect.h * yratio};
+            SDL_RenderCopy(renderer, is->sub_texture, &sub_rect, &target);
         }
 #endif
     }
@@ -1639,19 +1630,20 @@ retry:
                         sp2 = NULL;
 
                     if (sp->serial != is->subtitleq.serial
-                            || (is->vidclk.pts > (sp->pts + ((float) sp->sub.end_display_time / 1000)))
-                            || (sp2 && is->vidclk.pts > (sp2->pts + ((float) sp2->sub.start_display_time / 1000))))
+                            || (is->vidclk.pts > (sp->pts + ((float) sp->frame->subtitle_timing.duration / AV_TIME_BASE)))
+                            || (sp2 && is->vidclk.pts > (sp2->pts)))
                     {
                         if (sp->uploaded) {
                             int i;
-                            for (i = 0; i < sp->sub.num_rects; i++) {
-                                AVSubtitleRect *sub_rect = sp->sub.rects[i];
+                            for (i = 0; i < sp->frame->num_subtitle_areas; i++) {
+                                AVSubtitleArea *area = sp->frame->subtitle_areas[i];
+                                SDL_Rect sdl_rect = { .x = area->x, .y = area->y, .w = area->w, .h = area->h };
                                 uint8_t *pixels;
                                 int pitch, j;
 
-                                if (!SDL_LockTexture(is->sub_texture, (SDL_Rect *)sub_rect, (void **)&pixels, &pitch)) {
-                                    for (j = 0; j < sub_rect->h; j++, pixels += pitch)
-                                        memset(pixels, 0, sub_rect->w << 2);
+                                if (!SDL_LockTexture(is->sub_texture, &sdl_rect, (void **)&pixels, &pitch)) {
+                                    for (j = 0; j < area->h; j++, pixels += pitch)
+                                        memset(pixels, 0, area->w << 2);
                                     SDL_UnlockTexture(is->sub_texture);
                                 }
                             }
@@ -1762,7 +1754,7 @@ static int get_video_frame(VideoState *is, AVFrame *frame)
 {
     int got_picture;
 
-    if ((got_picture = decoder_decode_frame(&is->viddec, frame, NULL)) < 0)
+    if ((got_picture = decoder_decode_frame(&is->viddec, frame)) < 0)
         return -1;
 
     if (got_picture) {
@@ -2032,7 +2024,7 @@ static int audio_thread(void *arg)
         return AVERROR(ENOMEM);
 
     do {
-        if ((got_frame = decoder_decode_frame(&is->auddec, frame, NULL)) < 0)
+        if ((got_frame = decoder_decode_frame(&is->auddec, frame)) < 0)
             goto the_end;
 
         if (got_frame) {
@@ -2229,14 +2221,14 @@ static int subtitle_thread(void *arg)
         if (!(sp = frame_queue_peek_writable(&is->subpq)))
             return 0;
 
-        if ((got_subtitle = decoder_decode_frame(&is->subdec, NULL, &sp->sub)) < 0)
+        if ((got_subtitle = decoder_decode_frame(&is->subdec, sp->frame)) < 0)
             break;
 
         pts = 0;
 
-        if (got_subtitle && sp->sub.format == 0) {
-            if (sp->sub.pts != AV_NOPTS_VALUE)
-                pts = sp->sub.pts / (double)AV_TIME_BASE;
+        if (got_subtitle && sp->frame->format == AV_SUBTITLE_FMT_BITMAP) {
+            if (sp->frame->subtitle_timing.start_pts != AV_NOPTS_VALUE)
+                pts = (double)sp->frame->subtitle_timing.start_pts / (double)AV_TIME_BASE;
             sp->pts = pts;
             sp->serial = is->subdec.pkt_serial;
             sp->width = is->subdec.avctx->width;
@@ -2246,7 +2238,7 @@ static int subtitle_thread(void *arg)
             /* now we can update the picture count */
             frame_queue_push(&is->subpq);
         } else if (got_subtitle) {
-            avsubtitle_free(&sp->sub);
+            av_frame_free(&sp->frame);
         }
     }
     return 0;
diff --git a/fftools/ffprobe.c b/fftools/ffprobe.c
index 5020ba484c..4668cf5dc6 100644
--- a/fftools/ffprobe.c
+++ b/fftools/ffprobe.c
@@ -2522,22 +2522,42 @@ static void show_packet(WriterContext *w, InputFile *ifile, AVPacket *pkt, int p
     fflush(stdout);
 }
 
-static void show_subtitle(WriterContext *w, AVSubtitle *sub, AVStream *stream,
+static void show_subtitle(WriterContext *w, AVFrame *sub, AVStream *stream,
                           AVFormatContext *fmt_ctx)
 {
     AVBPrint pbuf;
+    const char *s;
 
     av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
 
     writer_print_section_header(w, SECTION_ID_SUBTITLE);
 
     print_str ("media_type",         "subtitle");
-    print_ts  ("pts",                 sub->pts);
-    print_time("pts_time",            sub->pts, &AV_TIME_BASE_Q);
-    print_int ("format",              sub->format);
-    print_int ("start_display_time",  sub->start_display_time);
-    print_int ("end_display_time",    sub->end_display_time);
-    print_int ("num_rects",           sub->num_rects);
+    print_ts  ("pts",                 sub->subtitle_timing.start_pts);
+    print_time("pts_time",            sub->subtitle_timing.start_pts, &AV_TIME_BASE_Q);
+    print_time("duration",            sub->subtitle_timing.duration, &AV_TIME_BASE_Q);
+
+    // Remain compatible with previous outputs
+    switch (sub->format) {
+    case AV_SUBTITLE_FMT_BITMAP:
+        print_int ("format",         0);
+        break;
+    case AV_SUBTITLE_FMT_TEXT:
+        print_int ("format",         1);
+        break;
+    case AV_SUBTITLE_FMT_ASS:
+        print_int ("format",         1);
+        break;
+    default:
+        print_int ("format",         -1);
+        break;
+    }
+
+    s = av_get_subtitle_fmt_name(sub->format);
+    if (s) print_str    ("format_str", s);
+    else   print_str_opt("format_str", "unknown");
+
+    print_int ("num_subtitle_rects",           sub->num_subtitle_areas);
 
     writer_print_section_footer(w);
 
@@ -2705,7 +2725,6 @@ static av_always_inline int process_frame(WriterContext *w,
     AVFormatContext *fmt_ctx = ifile->fmt_ctx;
     AVCodecContext *dec_ctx = ifile->streams[pkt->stream_index].dec_ctx;
     AVCodecParameters *par = ifile->streams[pkt->stream_index].st->codecpar;
-    AVSubtitle sub;
     int ret = 0, got_frame = 0;
 
     clear_log(1);
@@ -2713,6 +2732,7 @@ static av_always_inline int process_frame(WriterContext *w,
         switch (par->codec_type) {
         case AVMEDIA_TYPE_VIDEO:
         case AVMEDIA_TYPE_AUDIO:
+        case AVMEDIA_TYPE_SUBTITLE:
             if (*packet_new) {
                 ret = avcodec_send_packet(dec_ctx, pkt);
                 if (ret == AVERROR(EAGAIN)) {
@@ -2731,12 +2751,6 @@ static av_always_inline int process_frame(WriterContext *w,
                 }
             }
             break;
-
-        case AVMEDIA_TYPE_SUBTITLE:
-            if (*packet_new)
-                ret = avcodec_decode_subtitle2(dec_ctx, &sub, &got_frame, pkt);
-            *packet_new = 0;
-            break;
         default:
             *packet_new = 0;
         }
@@ -2751,12 +2765,11 @@ static av_always_inline int process_frame(WriterContext *w,
         nb_streams_frames[pkt->stream_index]++;
         if (do_show_frames)
             if (is_sub)
-                show_subtitle(w, &sub, ifile->streams[pkt->stream_index].st, fmt_ctx);
+                show_subtitle(w, frame, ifile->streams[pkt->stream_index].st, fmt_ctx);
             else
                 show_frame(w, frame, ifile->streams[pkt->stream_index].st, fmt_ctx);
-        if (is_sub)
-            avsubtitle_free(&sub);
     }
+
     return got_frame || *packet_new;
 }
 
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v6 09/25] avfilter/subtitles: Add subtitles.c for subtitle frame allocation
  2022-06-26 16:34         ` [FFmpeg-devel] [PATCH v6 00/25] Subtitle Filtering 2022 ffmpegagent
                             ` (7 preceding siblings ...)
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 08/25] fftools/play, probe: Adjust for subtitle changes softworkz
@ 2022-06-26 16:35           ` softworkz
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 10/25] avfilter/avfilter: Handle subtitle frames softworkz
                             ` (15 subsequent siblings)
  24 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-26 16:35 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Analog to avfilter/video.c and avfilter/audio.c

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavfilter/Makefile    |  1 +
 libavfilter/avfilter.c  |  4 +++
 libavfilter/internal.h  |  1 +
 libavfilter/subtitles.c | 63 +++++++++++++++++++++++++++++++++++++++++
 libavfilter/subtitles.h | 44 ++++++++++++++++++++++++++++
 5 files changed, 113 insertions(+)
 create mode 100644 libavfilter/subtitles.c
 create mode 100644 libavfilter/subtitles.h

diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 7ba1c8a861..af52a77ebc 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -20,6 +20,7 @@ OBJS = allfilters.o                                                     \
        framequeue.o                                                     \
        graphdump.o                                                      \
        graphparser.o                                                    \
+       subtitles.o                                                      \
        version.o                                                        \
        video.o                                                          \
 
diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c
index 965f5d0f63..28c5430c3e 100644
--- a/libavfilter/avfilter.c
+++ b/libavfilter/avfilter.c
@@ -42,6 +42,7 @@
 #include "formats.h"
 #include "framepool.h"
 #include "internal.h"
+#include "subtitles.h"
 
 static void tlog_ref(void *ctx, AVFrame *ref, int end)
 {
@@ -1452,6 +1453,9 @@ int ff_inlink_make_frame_writable(AVFilterLink *link, AVFrame **rframe)
     case AVMEDIA_TYPE_AUDIO:
         out = ff_get_audio_buffer(link, frame->nb_samples);
         break;
+    case AVMEDIA_TYPE_SUBTITLE:
+        out = ff_get_subtitles_buffer(link, link->format);
+        break;
     default:
         return AVERROR(EINVAL);
     }
diff --git a/libavfilter/internal.h b/libavfilter/internal.h
index 0f8da367d0..6c8496879a 100644
--- a/libavfilter/internal.h
+++ b/libavfilter/internal.h
@@ -89,6 +89,7 @@ struct AVFilterPad {
     union {
         AVFrame *(*video)(AVFilterLink *link, int w, int h);
         AVFrame *(*audio)(AVFilterLink *link, int nb_samples);
+        AVFrame *(*subtitle)(AVFilterLink *link, int format);
     } get_buffer;
 
     /**
diff --git a/libavfilter/subtitles.c b/libavfilter/subtitles.c
new file mode 100644
index 0000000000..951bfd612c
--- /dev/null
+++ b/libavfilter/subtitles.c
@@ -0,0 +1,63 @@
+/*
+ * 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
+ */
+
+#include "libavutil/common.h"
+
+#include "subtitles.h"
+#include "avfilter.h"
+#include "internal.h"
+
+
+AVFrame *ff_null_get_subtitles_buffer(AVFilterLink *link, int format)
+{
+    return ff_get_subtitles_buffer(link->dst->outputs[0], format);
+}
+
+AVFrame *ff_default_get_subtitles_buffer(AVFilterLink *link, int format)
+{
+    AVFrame *frame;
+
+    frame = av_frame_alloc();
+    if (!frame)
+        return NULL;
+
+    frame->format = format;
+    frame->type = AVMEDIA_TYPE_SUBTITLE;
+
+    if (av_frame_get_buffer2(frame, 0) < 0) {
+        av_frame_free(&frame);
+        return NULL;
+    }
+
+    return frame;
+}
+
+AVFrame *ff_get_subtitles_buffer(AVFilterLink *link, int format)
+{
+    AVFrame *ret = NULL;
+
+    if (link->dstpad->get_buffer.subtitle)
+        ret = link->dstpad->get_buffer.subtitle(link, format);
+
+    if (!ret)
+        ret = ff_default_get_subtitles_buffer(link, format);
+
+    return ret;
+}
diff --git a/libavfilter/subtitles.h b/libavfilter/subtitles.h
new file mode 100644
index 0000000000..4a9115126e
--- /dev/null
+++ b/libavfilter/subtitles.h
@@ -0,0 +1,44 @@
+/*
+ * 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
+ */
+
+#ifndef AVFILTER_SUBTITLES_H
+#define AVFILTER_SUBTITLES_H
+
+#include "avfilter.h"
+#include "internal.h"
+
+/** default handler for get_subtitles_buffer() for subtitle inputs */
+AVFrame *ff_default_get_subtitles_buffer(AVFilterLink *link, int format);
+
+/** get_subtitles_buffer() handler for filters which simply pass subtitles along */
+AVFrame *ff_null_get_subtitles_buffer(AVFilterLink *link, int format);
+
+/**
+ * Request a subtitles frame with a specific set of permissions.
+ *
+ * @param link           the output link to the filter from which the buffer will
+ *                       be requested
+ * @param format         The subtitles format.
+ * @return               A reference to the frame. This must be unreferenced with
+ *                       av_frame_free when you are finished with it.
+*/
+AVFrame *ff_get_subtitles_buffer(AVFilterLink *link, int format);
+
+#endif /* AVFILTER_SUBTITLES_H */
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v6 10/25] avfilter/avfilter: Handle subtitle frames
  2022-06-26 16:34         ` [FFmpeg-devel] [PATCH v6 00/25] Subtitle Filtering 2022 ffmpegagent
                             ` (8 preceding siblings ...)
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 09/25] avfilter/subtitles: Add subtitles.c for subtitle frame allocation softworkz
@ 2022-06-26 16:35           ` softworkz
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 11/25] avfilter/avfilter: Fix hardcoded input index softworkz
                             ` (14 subsequent siblings)
  24 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-26 16:35 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavfilter/avfilter.c      | 20 +++++++++++++++++---
 libavfilter/avfilter.h      | 11 +++++++++++
 libavfilter/avfiltergraph.c |  5 +++++
 libavfilter/formats.c       | 16 ++++++++++++++++
 libavfilter/formats.h       |  3 +++
 libavfilter/internal.h      | 18 +++++++++++++++---
 6 files changed, 67 insertions(+), 6 deletions(-)

diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c
index 28c5430c3e..853e2b967f 100644
--- a/libavfilter/avfilter.c
+++ b/libavfilter/avfilter.c
@@ -52,7 +52,8 @@ static void tlog_ref(void *ctx, AVFrame *ref, int end)
             ref->linesize[0], ref->linesize[1], ref->linesize[2], ref->linesize[3],
             ref->pts, ref->pkt_pos);
 
-    if (ref->width) {
+    switch(ref->type) {
+    case AVMEDIA_TYPE_VIDEO:
         ff_tlog(ctx, " a:%d/%d s:%dx%d i:%c iskey:%d type:%c",
                 ref->sample_aspect_ratio.num, ref->sample_aspect_ratio.den,
                 ref->width, ref->height,
@@ -60,12 +61,13 @@ static void tlog_ref(void *ctx, AVFrame *ref, int end)
                 ref->top_field_first ? 'T' : 'B',    /* Top / Bottom */
                 ref->key_frame,
                 av_get_picture_type_char(ref->pict_type));
-    }
-    if (ref->nb_samples) {
+        break;
+    case AVMEDIA_TYPE_AUDIO:
         ff_tlog(ctx, " cl:%"PRId64"d n:%d r:%d",
                 ref->channel_layout,
                 ref->nb_samples,
                 ref->sample_rate);
+        break;
     }
 
     ff_tlog(ctx, "]%s", end ? "\n" : "");
@@ -348,6 +350,14 @@ int avfilter_config_links(AVFilterContext *filter)
 
                 if (!link->time_base.num && !link->time_base.den)
                     link->time_base = (AVRational) {1, link->sample_rate};
+
+                break;
+
+            case AVMEDIA_TYPE_SUBTITLE:
+                if (!link->time_base.num && !link->time_base.den)
+                    link->time_base = inlink ? inlink->time_base : AV_TIME_BASE_Q;
+
+                break;
             }
 
             if (link->src->nb_inputs && link->src->inputs[0]->hw_frames_ctx &&
@@ -1013,6 +1023,10 @@ int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
             av_assert1(frame->width               == link->w);
             av_assert1(frame->height               == link->h);
         }
+    } else if (link->type == AVMEDIA_TYPE_SUBTITLE) {
+        if (frame->format != link->format) {
+            av_log(link->dst, AV_LOG_WARNING, "Subtitle format change from %d to %d\n", link->format, frame->format);
+        }
     } else {
         if (frame->format != link->format) {
             av_log(link->dst, AV_LOG_ERROR, "Format change is not supported\n");
diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h
index 2e8197c9a6..c436f304bc 100644
--- a/libavfilter/avfilter.h
+++ b/libavfilter/avfilter.h
@@ -45,6 +45,7 @@
 #include "libavutil/log.h"
 #include "libavutil/samplefmt.h"
 #include "libavutil/pixfmt.h"
+#include "libavutil/subfmt.h"
 #include "libavutil/rational.h"
 
 #include "libavfilter/version_major.h"
@@ -349,6 +350,12 @@ typedef struct AVFilter {
          * and outputs use the same sample rate and channel count/layout.
          */
         const enum AVSampleFormat *samples_list;
+        /**
+         * Analogous to pixels, but delimited by AV_SUBTITLE_FMT_NONE
+         * and restricted to filters that only have AVMEDIA_TYPE_SUBTITLE
+         * inputs and outputs.
+         */
+        const enum AVSubtitleType *subs_list;
         /**
          * Equivalent to { pix_fmt, AV_PIX_FMT_NONE } as pixels_list.
          */
@@ -357,6 +364,10 @@ typedef struct AVFilter {
          * Equivalent to { sample_fmt, AV_SAMPLE_FMT_NONE } as samples_list.
          */
         enum AVSampleFormat sample_fmt;
+        /**
+         * Equivalent to { sub_fmt, AV_SUBTITLE_FMT_NONE } as subs_list.
+         */
+        enum AVSubtitleType sub_fmt;
     } formats;
 
     int priv_size;      ///< size of private data to allocate for the filter
diff --git a/libavfilter/avfiltergraph.c b/libavfilter/avfiltergraph.c
index b7dbfc063b..f37be0203b 100644
--- a/libavfilter/avfiltergraph.c
+++ b/libavfilter/avfiltergraph.c
@@ -309,6 +309,8 @@ static int filter_link_check_formats(void *log, AVFilterLink *link, AVFilterForm
             return ret;
         break;
 
+    case AVMEDIA_TYPE_SUBTITLE:
+        return 0;
     default:
         av_assert0(!"reached");
     }
@@ -439,6 +441,9 @@ static int query_formats(AVFilterGraph *graph, void *log_ctx)
             if (!link)
                 continue;
 
+            if (link->type == AVMEDIA_TYPE_SUBTITLE)
+                continue;
+
             neg = ff_filter_get_negotiation(link);
             av_assert0(neg);
             for (neg_step = 1; neg_step < neg->nb_mergers; neg_step++) {
diff --git a/libavfilter/formats.c b/libavfilter/formats.c
index e8c2888c0c..12585ed428 100644
--- a/libavfilter/formats.c
+++ b/libavfilter/formats.c
@@ -19,6 +19,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
+#include "libavutil/subfmt.h"
 #include "libavutil/avassert.h"
 #include "libavutil/channel_layout.h"
 #include "libavutil/common.h"
@@ -491,6 +492,13 @@ AVFilterFormats *ff_all_formats(enum AVMediaType type)
                 return NULL;
             fmt++;
         }
+    } else if (type == AVMEDIA_TYPE_SUBTITLE) {
+        if (ff_add_format(&ret, AV_SUBTITLE_FMT_BITMAP) < 0)
+            return NULL;
+        if (ff_add_format(&ret, AV_SUBTITLE_FMT_ASS) < 0)
+            return NULL;
+        if (ff_add_format(&ret, AV_SUBTITLE_FMT_TEXT) < 0)
+            return NULL;
     }
 
     return ret;
@@ -774,6 +782,10 @@ int ff_default_query_formats(AVFilterContext *ctx)
         type    = AVMEDIA_TYPE_AUDIO;
         formats = ff_make_format_list(f->formats.samples_list);
         break;
+    case FF_FILTER_FORMATS_SUBFMTS_LIST:
+        type    = AVMEDIA_TYPE_SUBTITLE;
+        formats = ff_make_format_list(f->formats.subs_list);
+        break;
     case FF_FILTER_FORMATS_SINGLE_PIXFMT:
         type    = AVMEDIA_TYPE_VIDEO;
         formats = ff_make_formats_list_singleton(f->formats.pix_fmt);
@@ -782,6 +794,10 @@ int ff_default_query_formats(AVFilterContext *ctx)
         type    = AVMEDIA_TYPE_AUDIO;
         formats = ff_make_formats_list_singleton(f->formats.sample_fmt);
         break;
+    case FF_FILTER_FORMATS_SINGLE_SUBFMT:
+        type    = AVMEDIA_TYPE_SUBTITLE;
+        formats = ff_make_formats_list_singleton(f->formats.sub_fmt);
+        break;
     default:
         av_assert2(!"Unreachable");
     /* Intended fallthrough */
diff --git a/libavfilter/formats.h b/libavfilter/formats.h
index 22224dce2d..6cf952a059 100644
--- a/libavfilter/formats.h
+++ b/libavfilter/formats.h
@@ -183,6 +183,9 @@ av_warn_unused_result
 int ff_add_channel_layout(AVFilterChannelLayouts **l,
                           const AVChannelLayout *channel_layout);
 
+av_warn_unused_result
+int ff_add_subtitle_type(AVFilterFormats **avff, int64_t fmt);
+
 /**
  * Add *ref as a new reference to f.
  */
diff --git a/libavfilter/internal.h b/libavfilter/internal.h
index 6c8496879a..7972c5c6de 100644
--- a/libavfilter/internal.h
+++ b/libavfilter/internal.h
@@ -148,9 +148,11 @@ static av_always_inline int ff_filter_execute(AVFilterContext *ctx, avfilter_act
 
 enum FilterFormatsState {
     /**
-     * The default value meaning that this filter supports all formats
-     * and (for audio) sample rates and channel layouts/counts as long
-     * as these properties agree for all inputs and outputs.
+     * The default value meaning that this filter supports
+     * - For video:     all formats
+     * - For audio:     all sample rates and channel layouts/counts
+     * - For subtitles: all subtitle formats
+     * as long as these properties agree for all inputs and outputs.
      * This state is only allowed in case all inputs and outputs actually
      * have the same type.
      * The union is unused in this state.
@@ -161,8 +163,10 @@ enum FilterFormatsState {
     FF_FILTER_FORMATS_QUERY_FUNC,       ///< formats.query active.
     FF_FILTER_FORMATS_PIXFMT_LIST,      ///< formats.pixels_list active.
     FF_FILTER_FORMATS_SAMPLEFMTS_LIST,  ///< formats.samples_list active.
+    FF_FILTER_FORMATS_SUBFMTS_LIST,     ///< formats.subs_list active.
     FF_FILTER_FORMATS_SINGLE_PIXFMT,    ///< formats.pix_fmt active
     FF_FILTER_FORMATS_SINGLE_SAMPLEFMT, ///< formats.sample_fmt active.
+    FF_FILTER_FORMATS_SINGLE_SUBFMT,    ///< formats.sub_fmt active.
 };
 
 #define FILTER_QUERY_FUNC(func)        \
@@ -174,16 +178,24 @@ enum FilterFormatsState {
 #define FILTER_SAMPLEFMTS_ARRAY(array) \
         .formats.samples_list = array, \
         .formats_state        = FF_FILTER_FORMATS_SAMPLEFMTS_LIST
+#define FILTER_SUBFMTS_ARRAY(array) \
+        .formats.subs_list = array, \
+        .formats_state        = FF_FILTER_FORMATS_SUBFMTS_LIST
 #define FILTER_PIXFMTS(...)            \
     FILTER_PIXFMTS_ARRAY(((const enum AVPixelFormat []) { __VA_ARGS__, AV_PIX_FMT_NONE }))
 #define FILTER_SAMPLEFMTS(...)         \
     FILTER_SAMPLEFMTS_ARRAY(((const enum AVSampleFormat[]) { __VA_ARGS__, AV_SAMPLE_FMT_NONE }))
+#define FILTER_SUBFMTS(...)         \
+    FILTER_SUBFMTS_ARRAY(((const enum AVSubtitleType[]) { __VA_ARGS__, AV_SUBTITLE_FMT_NONE }))
 #define FILTER_SINGLE_PIXFMT(pix_fmt_)  \
         .formats.pix_fmt = pix_fmt_,    \
         .formats_state   = FF_FILTER_FORMATS_SINGLE_PIXFMT
 #define FILTER_SINGLE_SAMPLEFMT(sample_fmt_) \
         .formats.sample_fmt = sample_fmt_,   \
         .formats_state      = FF_FILTER_FORMATS_SINGLE_SAMPLEFMT
+#define FILTER_SINGLE_SUBFMT(sub_fmt_) \
+        .formats.sub_fmt = sub_fmt_,   \
+        .formats_state      = FF_FILTER_FORMATS_SINGLE_SUBFMT
 
 #define FILTER_INOUTPADS(inout, array) \
        .inout        = array, \
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v6 11/25] avfilter/avfilter: Fix hardcoded input index
  2022-06-26 16:34         ` [FFmpeg-devel] [PATCH v6 00/25] Subtitle Filtering 2022 ffmpegagent
                             ` (9 preceding siblings ...)
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 10/25] avfilter/avfilter: Handle subtitle frames softworkz
@ 2022-06-26 16:35           ` softworkz
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 12/25] avfilter/sbuffer: Add sbuffersrc and sbuffersink filters softworkz
                             ` (13 subsequent siblings)
  24 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-26 16:35 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

This fix targets (rare) cases where multiple input pads have a
.filter_frame function. ff_request_frame_to_filter needs
to call ff_request_frame with the correct input pad
instead of the hardcoded first one.

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavfilter/avfilter.c | 18 +++++++++++++-----
 1 file changed, 13 insertions(+), 5 deletions(-)

diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c
index 853e2b967f..e6217b60d6 100644
--- a/libavfilter/avfilter.c
+++ b/libavfilter/avfilter.c
@@ -451,7 +451,7 @@ static int64_t guess_status_pts(AVFilterContext *ctx, int status, AVRational lin
     return AV_NOPTS_VALUE;
 }
 
-static int ff_request_frame_to_filter(AVFilterLink *link)
+static int ff_request_frame_to_filter(AVFilterLink *link, int input_index)
 {
     int ret = -1;
 
@@ -460,8 +460,8 @@ static int ff_request_frame_to_filter(AVFilterLink *link)
     link->frame_blocked_in = 1;
     if (link->srcpad->request_frame)
         ret = link->srcpad->request_frame(link);
-    else if (link->src->inputs[0])
-        ret = ff_request_frame(link->src->inputs[0]);
+    else if (link->src->inputs[input_index])
+        ret = ff_request_frame(link->src->inputs[input_index]);
     if (ret < 0) {
         if (ret != AVERROR(EAGAIN) && ret != link->status_in)
             ff_avfilter_link_set_in_status(link, ret, guess_status_pts(link->src, ret, link->time_base));
@@ -1161,6 +1161,14 @@ static int forward_status_change(AVFilterContext *filter, AVFilterLink *in)
 {
     unsigned out = 0, progress = 0;
     int ret;
+    int input_index = 0;
+
+    for (int i = 0; i < in->dst->nb_inputs; i++) {
+        if (&in->dst->input_pads[i] == in->dstpad) {
+            input_index = i;
+            break;
+        }
+    }
 
     av_assert0(!in->status_out);
     if (!filter->nb_outputs) {
@@ -1170,7 +1178,7 @@ static int forward_status_change(AVFilterContext *filter, AVFilterLink *in)
     while (!in->status_out) {
         if (!filter->outputs[out]->status_in) {
             progress++;
-            ret = ff_request_frame_to_filter(filter->outputs[out]);
+            ret = ff_request_frame_to_filter(filter->outputs[out], input_index);
             if (ret < 0)
                 return ret;
         }
@@ -1207,7 +1215,7 @@ static int ff_filter_activate_default(AVFilterContext *filter)
     for (i = 0; i < filter->nb_outputs; i++) {
         if (filter->outputs[i]->frame_wanted_out &&
             !filter->outputs[i]->frame_blocked_in) {
-            return ff_request_frame_to_filter(filter->outputs[i]);
+            return ff_request_frame_to_filter(filter->outputs[i], 0);
         }
     }
     return FFERROR_NOT_READY;
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v6 12/25] avfilter/sbuffer: Add sbuffersrc and sbuffersink filters
  2022-06-26 16:34         ` [FFmpeg-devel] [PATCH v6 00/25] Subtitle Filtering 2022 ffmpegagent
                             ` (10 preceding siblings ...)
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 11/25] avfilter/avfilter: Fix hardcoded input index softworkz
@ 2022-06-26 16:35           ` softworkz
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 13/25] avfilter/overlaygraphicsubs: Add overlaygraphicsubs and graphicsub2video filters softworkz
                             ` (12 subsequent siblings)
  24 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-26 16:35 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 configure                |  2 +-
 libavfilter/allfilters.c |  2 ++
 libavfilter/buffersink.c | 54 ++++++++++++++++++++++++++++++
 libavfilter/buffersink.h |  7 ++++
 libavfilter/buffersrc.c  | 72 ++++++++++++++++++++++++++++++++++++++++
 libavfilter/buffersrc.h  |  1 +
 6 files changed, 137 insertions(+), 1 deletion(-)

diff --git a/configure b/configure
index 3a97610209..0580e4a536 100755
--- a/configure
+++ b/configure
@@ -7880,7 +7880,7 @@ print_enabled_components(){
         fi
     done
     if [ "$name" = "filter_list" ]; then
-        for c in asrc_abuffer vsrc_buffer asink_abuffer vsink_buffer; do
+        for c in asrc_abuffer vsrc_buffer ssrc_sbuffer asink_abuffer vsink_buffer ssink_sbuffer; do
             printf "    &ff_%s,\n" $c >> $TMPH
         done
     fi
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index ec70feef11..5288480a55 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -571,8 +571,10 @@ extern const AVFilter ff_avsrc_movie;
  * being the same while having different 'types'). */
 extern  const AVFilter ff_asrc_abuffer;
 extern  const AVFilter ff_vsrc_buffer;
+extern  const AVFilter ff_ssrc_sbuffer;
 extern  const AVFilter ff_asink_abuffer;
 extern  const AVFilter ff_vsink_buffer;
+extern  const AVFilter ff_ssink_sbuffer;
 extern const AVFilter ff_af_afifo;
 extern const AVFilter ff_vf_fifo;
 
diff --git a/libavfilter/buffersink.c b/libavfilter/buffersink.c
index e269cf72d1..204e9bad6c 100644
--- a/libavfilter/buffersink.c
+++ b/libavfilter/buffersink.c
@@ -30,6 +30,8 @@
 #include "libavutil/internal.h"
 #include "libavutil/opt.h"
 
+#include "libavcodec/avcodec.h"
+
 #define FF_INTERNAL_FIELDS 1
 #include "framequeue.h"
 
@@ -61,6 +63,10 @@ typedef struct BufferSinkContext {
     int *sample_rates;                  ///< list of accepted sample rates
     int sample_rates_size;
 
+    /* only used for subtitles */
+    enum AVSubtitleType *subtitle_types;     ///< list of accepted subtitle types, must be terminated with -1
+    int subtitle_types_size;
+
     AVFrame *peeked_frame;
 } BufferSinkContext;
 
@@ -372,6 +378,28 @@ static int asink_query_formats(AVFilterContext *ctx)
     return 0;
 }
 
+static int ssink_query_formats(AVFilterContext *ctx)
+{
+    BufferSinkContext *buf = ctx->priv;
+    AVFilterFormats *formats = NULL;
+    unsigned i;
+    int ret;
+
+    CHECK_LIST_SIZE(subtitle_types)
+    if (buf->subtitle_types_size) {
+        for (i = 0; i < NB_ITEMS(buf->subtitle_types); i++)
+            if ((ret = ff_add_format(&formats, buf->subtitle_types[i])) < 0)
+                return ret;
+        if ((ret = ff_set_common_formats(ctx, formats)) < 0)
+            return ret;
+    } else {
+        if ((ret = ff_default_query_formats(ctx)) < 0)
+            return ret;
+    }
+
+    return 0;
+}
+
 #define OFFSET(x) offsetof(BufferSinkContext, x)
 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
 static const AVOption buffersink_options[] = {
@@ -395,9 +423,16 @@ static const AVOption abuffersink_options[] = {
     { NULL },
 };
 #undef FLAGS
+#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_SUBTITLE_PARAM
+static const AVOption sbuffersink_options[] = {
+    { "subtitle_types", "set the supported subtitle formats", OFFSET(subtitle_types), AV_OPT_TYPE_BINARY, .flags = FLAGS },
+    { NULL },
+};
+#undef FLAGS
 
 AVFILTER_DEFINE_CLASS(buffersink);
 AVFILTER_DEFINE_CLASS(abuffersink);
+AVFILTER_DEFINE_CLASS(sbuffersink);
 
 static const AVFilterPad avfilter_vsink_buffer_inputs[] = {
     {
@@ -436,3 +471,22 @@ const AVFilter ff_asink_abuffer = {
     .outputs       = NULL,
     FILTER_QUERY_FUNC(asink_query_formats),
 };
+
+static const AVFilterPad avfilter_ssink_sbuffer_inputs[] = {
+    {
+        .name = "default",
+        .type = AVMEDIA_TYPE_SUBTITLE,
+    },
+};
+
+const AVFilter ff_ssink_sbuffer = {
+    .name          = "sbuffersink",
+    .description   = NULL_IF_CONFIG_SMALL("Buffer subtitle frames, and make them available to the end of the filter graph."),
+    .priv_class    = &sbuffersink_class,
+    .priv_size     = sizeof(BufferSinkContext),
+    .init          = common_init,
+    .activate      = activate,
+    FILTER_INPUTS(avfilter_ssink_sbuffer_inputs),
+    .outputs       = NULL,
+    FILTER_QUERY_FUNC(ssink_query_formats),
+};
diff --git a/libavfilter/buffersink.h b/libavfilter/buffersink.h
index 01e7c747d8..42c164e670 100644
--- a/libavfilter/buffersink.h
+++ b/libavfilter/buffersink.h
@@ -128,6 +128,13 @@ typedef struct AVABufferSinkParams {
  */
 attribute_deprecated
 AVABufferSinkParams *av_abuffersink_params_alloc(void);
+
+/**
+ * Deprecated and unused struct to use for initializing an sbuffersink context.
+ */
+typedef struct AVSBufferSinkParams {
+    const int *subtitle_type;
+} AVSBufferSinkParams;
 #endif
 
 /**
diff --git a/libavfilter/buffersrc.c b/libavfilter/buffersrc.c
index a3190468bb..1679c54f17 100644
--- a/libavfilter/buffersrc.c
+++ b/libavfilter/buffersrc.c
@@ -39,6 +39,7 @@
 #include "formats.h"
 #include "internal.h"
 #include "video.h"
+#include "libavcodec/avcodec.h"
 
 typedef struct BufferSourceContext {
     const AVClass    *class;
@@ -63,6 +64,9 @@ typedef struct BufferSourceContext {
     char    *channel_layout_str;
     AVChannelLayout ch_layout;
 
+    /* subtitle only */
+    enum AVSubtitleType subtitle_type;
+
     int eof;
 } BufferSourceContext;
 
@@ -143,6 +147,13 @@ FF_ENABLE_DEPRECATION_WARNINGS
                 return ret;
         }
         break;
+    case AVMEDIA_TYPE_SUBTITLE:
+        s->subtitle_type = param->format;
+        if (param->width > 0)
+            s->w = param->width;
+        if (param->height > 0)
+            s->h = param->height;
+        break;
     default:
         return AVERROR_BUG;
     }
@@ -224,6 +235,8 @@ FF_ENABLE_DEPRECATION_WARNINGS
             CHECK_AUDIO_PARAM_CHANGE(ctx, s, frame->sample_rate, frame->ch_layout,
                                      frame->format, frame->pts);
             break;
+        case AVMEDIA_TYPE_SUBTITLE:
+            break;
         default:
             return AVERROR(EINVAL);
         }
@@ -296,6 +309,7 @@ unsigned av_buffersrc_get_nb_failed_requests(AVFilterContext *buffer_src)
 #define OFFSET(x) offsetof(BufferSourceContext, x)
 #define A AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_AUDIO_PARAM
 #define V AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
+#define S AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_SUBTITLE_PARAM
 
 static const AVOption buffer_options[] = {
     { "width",         NULL,                     OFFSET(w),                AV_OPT_TYPE_INT,      { .i64 = 0 }, 0, INT_MAX, V },
@@ -325,6 +339,16 @@ static const AVOption abuffer_options[] = {
 
 AVFILTER_DEFINE_CLASS(abuffer);
 
+static const AVOption sbuffer_options[] = {
+    { "time_base",     NULL, OFFSET(time_base),            AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, 0, INT_MAX, S },
+    { "subtitle_type", NULL, OFFSET(subtitle_type),        AV_OPT_TYPE_INT,      { .i64 = 0 }, 0, INT_MAX, S },
+    { "width",         NULL, OFFSET(w),                    AV_OPT_TYPE_INT,      { .i64 = 0 }, 0, INT_MAX, V },
+    { "height",        NULL, OFFSET(h),                    AV_OPT_TYPE_INT,      { .i64 = 0 }, 0, INT_MAX, V },
+    { NULL },
+};
+
+AVFILTER_DEFINE_CLASS(sbuffer);
+
 static av_cold int init_audio(AVFilterContext *ctx)
 {
     BufferSourceContext *s = ctx->priv;
@@ -393,6 +417,21 @@ FF_ENABLE_DEPRECATION_WARNINGS
     return ret;
 }
 
+static av_cold int init_subtitle(AVFilterContext *ctx)
+{
+    BufferSourceContext *c = ctx->priv;
+
+    if (c->subtitle_type == AV_SUBTITLE_FMT_BITMAP)
+        av_log(ctx, AV_LOG_VERBOSE, "graphical subtitles - w:%d h:%d tb:%d/%d\n",
+               c->w, c->h, c->time_base.num, c->time_base.den);
+    else
+        av_log(ctx, AV_LOG_VERBOSE, "text subtitles -  w:%d h:%d tb:%d/%d\n",
+               c->w, c->h, c->time_base.num, c->time_base.den);
+
+    return 0;
+}
+
+
 static av_cold void uninit(AVFilterContext *ctx)
 {
     BufferSourceContext *s = ctx->priv;
@@ -426,6 +465,11 @@ static int query_formats(AVFilterContext *ctx)
         if ((ret = ff_set_common_channel_layouts(ctx, channel_layouts)) < 0)
             return ret;
         break;
+    case AVMEDIA_TYPE_SUBTITLE:
+        if ((ret = ff_add_format         (&formats, c->subtitle_type)) < 0 ||
+            (ret = ff_set_common_formats (ctx     , formats   )) < 0)
+            return ret;
+        break;
     default:
         return AVERROR(EINVAL);
     }
@@ -456,6 +500,11 @@ static int config_props(AVFilterLink *link)
                 return ret;
         }
         break;
+    case AVMEDIA_TYPE_SUBTITLE:
+        link->format = c->subtitle_type;
+        link->w = c->w;
+        link->h = c->h;
+        break;
     default:
         return AVERROR(EINVAL);
     }
@@ -520,3 +569,26 @@ const AVFilter ff_asrc_abuffer = {
     FILTER_QUERY_FUNC(query_formats),
     .priv_class = &abuffer_class,
 };
+
+static const AVFilterPad avfilter_ssrc_sbuffer_outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_SUBTITLE,
+        .request_frame = request_frame,
+        .config_props  = config_props,
+    },
+};
+
+const AVFilter ff_ssrc_sbuffer = {
+    .name          = "sbuffer",
+    .description   = NULL_IF_CONFIG_SMALL("Buffer subtitle frames, and make them accessible to the filterchain."),
+    .priv_size     = sizeof(BufferSourceContext),
+
+    .init      = init_subtitle,
+    .uninit    = uninit,
+
+    .inputs    = NULL,
+    FILTER_OUTPUTS(avfilter_ssrc_sbuffer_outputs),
+    FILTER_QUERY_FUNC(query_formats),
+    .priv_class = &sbuffer_class,
+};
diff --git a/libavfilter/buffersrc.h b/libavfilter/buffersrc.h
index 3b248b37cd..45a657bbb6 100644
--- a/libavfilter/buffersrc.h
+++ b/libavfilter/buffersrc.h
@@ -74,6 +74,7 @@ typedef struct AVBufferSrcParameters {
     /**
      * video: the pixel format, value corresponds to enum AVPixelFormat
      * audio: the sample format, value corresponds to enum AVSampleFormat
+     * subtitles: the subtitle format, value corresponds to enum AVSubtitleType
      */
     int format;
     /**
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v6 13/25] avfilter/overlaygraphicsubs: Add overlaygraphicsubs and graphicsub2video filters
  2022-06-26 16:34         ` [FFmpeg-devel] [PATCH v6 00/25] Subtitle Filtering 2022 ffmpegagent
                             ` (11 preceding siblings ...)
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 12/25] avfilter/sbuffer: Add sbuffersrc and sbuffersink filters softworkz
@ 2022-06-26 16:35           ` softworkz
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 14/25] avfilter/overlaytextsubs: Add overlaytextsubs and textsubs2video filters softworkz
                             ` (11 subsequent siblings)
  24 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-26 16:35 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

- overlaygraphicsubs (VS -> V)
  Overlay graphic subtitles onto a video stream

- graphicsub2video {S -> V)
  Converts graphic subtitles to video frames (with alpha)
  Gets auto-inserted for retaining compatibility with
  sub2video command lines

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 doc/filters.texi                    | 118 +++++
 libavfilter/Makefile                |   2 +
 libavfilter/allfilters.c            |   2 +
 libavfilter/vf_overlaygraphicsubs.c | 765 ++++++++++++++++++++++++++++
 4 files changed, 887 insertions(+)
 create mode 100644 libavfilter/vf_overlaygraphicsubs.c

diff --git a/doc/filters.texi b/doc/filters.texi
index e525e87b3c..a77546de3d 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -26990,6 +26990,124 @@ tools.
 
 @c man end VIDEO SINKS
 
+@chapter Subtitle Filters
+@c man begin SUBTITLE FILTERS
+
+When you configure your FFmpeg build, you can disable any of the
+existing filters using @code{--disable-filters}.
+
+Below is a description of the currently available subtitle filters.
+
+@section graphicsub2video
+
+Renders graphic subtitles as video frames.
+
+This filter replaces the previous "sub2video" hack which did the conversion implicitly and up-front as subtitle filtering wasn't possible at that time.
+To retain compatibility with earlier sub2video command lines, this filter is being auto-inserted in those cases.
+
+For overlaying graphicsal subtitles it is recommended to use the 'overlaygraphicsubs' filter which is more efficient and takes less processing resources.
+
+This filter is still useful in cases where the overlay is done with hardware acceleration (e.g. overlay_qsv, overlay_vaapi, overlay_cuda) for preparing the overlay frames.
+
+Inputs:
+@itemize
+@item 0: Subtitles [BITMAP]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Video [RGB32]
+@end itemize
+
+
+It accepts the following parameters:
+
+@table @option
+@item size, s
+Set the size of the output video frame.
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Overlay PGS subtitles
+(not recommended - better use overlaygraphicsubs)
+@example
+ffmpeg -i "https://streams.videolan.org/samples/sub/PGS/Girl_With_The_Dragon_Tattoo_2%3A23%3A56.mkv" -filter_complex "[0:1]graphicsub2video[subs];[0:0][subs]overlay" output.mp4
+@end example
+
+@item
+Overlay PGS subtitles implicitly
+The graphicsub2video is inserted automatically for compatibility with legacy command lines.
+@example
+ffmpeg -i "https://streams.videolan.org/samples/sub/PGS/Girl_With_The_Dragon_Tattoo_2%3A23%3A56.mkv" -filter_complex "[0:0][0:1]overlay" output.mp4
+@end example
+@end itemize
+
+@section overlaygraphicsubs
+
+Overlay graphic subtitles onto a video stream.
+
+This filter can blend graphical subtitles on a video stream directly, i.e. without creating full-size alpha images first.
+The blending operation is limited to the area of the subtitle rectangles, which also means that no processing is done at times where no subtitles are to be displayed.
+
+Inputs:
+@itemize
+@item 0: Video [YUV420P, YUV422P, YUV444P, ARGB, RGBA, ABGR, BGRA, RGB24, BGR24]
+@item 1: Subtitles [BITMAP]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Video (same as input)
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+@item x
+@item y
+Set the expression for the x and y coordinates of the overlaid video
+on the main video. Default value is "0" for both expressions. In case
+the expression is invalid, it is set to a huge value (meaning that the
+overlay will not be displayed within the output visible area).
+
+@item eof_action
+See @ref{framesync}.
+
+@item eval
+Set when the expressions for @option{x}, and @option{y} are evaluated.
+
+It accepts the following values:
+@table @samp
+@item init
+only evaluate expressions once during the filter initialization or
+when a command is processed
+
+@item frame
+evaluate expressions for each incoming frame
+@end table
+
+Default value is @samp{frame}.
+
+@item shortest
+See @ref{framesync}.
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Overlay PGS subtitles
+@example
+ffmpeg -i "https://streams.videolan.org/samples/sub/PGS/Girl_With_The_Dragon_Tattoo_2%3A23%3A56.mkv" -filter_complex "[0:0][0:1]overlaygraphicsubs" output.mp4
+@end example
+@end itemize
+@c man end SUBTITLE FILTERS
+
 @chapter Multimedia Filters
 @c man begin MULTIMEDIA FILTERS
 
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index af52a77ebc..97ec9fc3b3 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -307,6 +307,7 @@ OBJS-$(CONFIG_GBLUR_FILTER)                  += vf_gblur.o
 OBJS-$(CONFIG_GBLUR_VULKAN_FILTER)           += vf_gblur_vulkan.o vulkan.o vulkan_filter.o
 OBJS-$(CONFIG_GEQ_FILTER)                    += vf_geq.o
 OBJS-$(CONFIG_GRADFUN_FILTER)                += vf_gradfun.o
+OBJS-$(CONFIG_GRAPHICSUB2VIDEO_FILTER)       += vf_overlaygraphicsubs.o framesync.o
 OBJS-$(CONFIG_GRAPHMONITOR_FILTER)           += f_graphmonitor.o
 OBJS-$(CONFIG_GRAYWORLD_FILTER)              += vf_grayworld.o
 OBJS-$(CONFIG_GREYEDGE_FILTER)               += vf_colorconstancy.o
@@ -391,6 +392,7 @@ OBJS-$(CONFIG_OVERLAY_OPENCL_FILTER)         += vf_overlay_opencl.o opencl.o \
 OBJS-$(CONFIG_OVERLAY_QSV_FILTER)            += vf_overlay_qsv.o framesync.o
 OBJS-$(CONFIG_OVERLAY_VAAPI_FILTER)          += vf_overlay_vaapi.o framesync.o vaapi_vpp.o
 OBJS-$(CONFIG_OVERLAY_VULKAN_FILTER)         += vf_overlay_vulkan.o vulkan.o vulkan_filter.o
+OBJS-$(CONFIG_OVERLAYGRAPHICSUBS_FILTER)     += vf_overlaygraphicsubs.o framesync.o
 OBJS-$(CONFIG_OWDENOISE_FILTER)              += vf_owdenoise.o
 OBJS-$(CONFIG_PAD_FILTER)                    += vf_pad.o
 OBJS-$(CONFIG_PAD_OPENCL_FILTER)             += vf_pad_opencl.o opencl.o opencl/pad.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 5288480a55..06ac93457d 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -371,6 +371,7 @@ extern const AVFilter ff_vf_overlay_qsv;
 extern const AVFilter ff_vf_overlay_vaapi;
 extern const AVFilter ff_vf_overlay_vulkan;
 extern const AVFilter ff_vf_overlay_cuda;
+extern const AVFilter ff_vf_overlaygraphicsubs;
 extern const AVFilter ff_vf_owdenoise;
 extern const AVFilter ff_vf_pad;
 extern const AVFilter ff_vf_pad_opencl;
@@ -558,6 +559,7 @@ extern const AVFilter ff_avf_showspectrumpic;
 extern const AVFilter ff_avf_showvolume;
 extern const AVFilter ff_avf_showwaves;
 extern const AVFilter ff_avf_showwavespic;
+extern const AVFilter ff_svf_graphicsub2video;
 extern const AVFilter ff_vaf_spectrumsynth;
 
 /* multimedia sources */
diff --git a/libavfilter/vf_overlaygraphicsubs.c b/libavfilter/vf_overlaygraphicsubs.c
new file mode 100644
index 0000000000..7b26d8ef37
--- /dev/null
+++ b/libavfilter/vf_overlaygraphicsubs.c
@@ -0,0 +1,765 @@
+/*
+ * Copyright (c) 2021 softworkz (derived from vf_overlay)
+ * Copyright (c) 2010 Stefano Sabatini
+ * Copyright (c) 2010 Baptiste Coudurier
+ * Copyright (c) 2007 Bobby Bingham
+ *
+ * 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
+ * overlay graphical subtitles on top of a video frame
+ */
+
+#include "libavutil/eval.h"
+#include "libavutil/imgutils.h"
+#include "libavutil/opt.h"
+#include "internal.h"
+#include "drawutils.h"
+#include "framesync.h"
+
+enum var_name {
+    VAR_MAIN_W,    VAR_MW,
+    VAR_MAIN_H,    VAR_MH,
+    VAR_OVERLAY_W, VAR_OW,
+    VAR_OVERLAY_H, VAR_OH,
+    VAR_HSUB,
+    VAR_VSUB,
+    VAR_X,
+    VAR_Y,
+    VAR_N,
+    VAR_POS,
+    VAR_T,
+    VAR_VARS_NB
+};
+
+typedef struct OverlaySubsContext {
+    const AVClass *class;
+    int x, y;                   ///< position of overlaid picture
+    int w, h;
+    AVFrame *outpicref;
+
+    int main_is_packed_rgb;
+    uint8_t main_rgba_map[4];
+    int main_has_alpha;
+    uint8_t overlay_rgba_map[4];
+    int eval_mode;              ///< EvalMode
+    int use_caching;
+    AVFrame *cache_frame;
+
+    FFFrameSync fs;
+
+    int main_pix_step[4];       ///< steps per pixel for each plane of the main output
+    int hsub, vsub;             ///< chroma subsampling values
+    const AVPixFmtDescriptor *main_desc; ///< format descriptor for main input
+
+    double var_values[VAR_VARS_NB];
+    char *x_expr, *y_expr;
+
+    AVExpr *x_pexpr, *y_pexpr;
+
+    int pic_counter;
+} OverlaySubsContext;
+
+static const char *const var_names[] = {
+    "main_w",    "W", ///< width  of the main    video
+    "main_h",    "H", ///< height of the main    video
+    "overlay_w", "w", ///< width  of the overlay video
+    "overlay_h", "h", ///< height of the overlay video
+    "hsub",
+    "vsub",
+    "x",
+    "y",
+    "n",            ///< number of frame
+    "pos",          ///< position in the file
+    "t",            ///< timestamp expressed in seconds
+    NULL
+};
+
+#define MAIN    0
+#define OVERLAY 1
+
+#define R 0
+#define G 1
+#define B 2
+#define A 3
+
+#define Y 0
+#define U 1
+#define V 2
+
+enum EvalMode {
+    EVAL_MODE_INIT,
+    EVAL_MODE_FRAME,
+    EVAL_MODE_NB
+};
+
+static av_cold void overlay_graphicsubs_uninit(AVFilterContext *ctx)
+{
+    OverlaySubsContext *s = ctx->priv;
+
+    av_frame_free(&s->cache_frame);
+    ff_framesync_uninit(&s->fs);
+    av_expr_free(s->x_pexpr); s->x_pexpr = NULL;
+    av_expr_free(s->y_pexpr); s->y_pexpr = NULL;
+}
+
+static inline int normalize_xy(double d, int chroma_sub)
+{
+    if (isnan(d))
+        return INT_MAX;
+    return (int)d & ~((1 << chroma_sub) - 1);
+}
+
+static void eval_expr(AVFilterContext *ctx)
+{
+    OverlaySubsContext *s = ctx->priv;
+
+    s->var_values[VAR_X] = av_expr_eval(s->x_pexpr, s->var_values, NULL);
+    s->var_values[VAR_Y] = av_expr_eval(s->y_pexpr, s->var_values, NULL);
+    /* It is necessary if x is expressed from y  */
+    s->var_values[VAR_X] = av_expr_eval(s->x_pexpr, s->var_values, NULL);
+    s->x = normalize_xy(s->var_values[VAR_X], s->hsub);
+    s->y = normalize_xy(s->var_values[VAR_Y], s->vsub);
+}
+
+static int set_expr(AVExpr **pexpr, const char *expr, const char *option, void *log_ctx)
+{
+    int ret;
+    AVExpr *old = NULL;
+
+    if (*pexpr)
+        old = *pexpr;
+    ret = av_expr_parse(pexpr, expr, var_names,
+                        NULL, NULL, NULL, NULL, 0, log_ctx);
+    if (ret < 0) {
+        av_log(log_ctx, AV_LOG_ERROR,
+               "Error when evaluating the expression '%s' for %s\n",
+               expr, option);
+        *pexpr = old;
+        return ret;
+    }
+
+    av_expr_free(old);
+    return 0;
+}
+
+static int overlay_graphicsubs_query_formats(AVFilterContext *ctx)
+{
+    AVFilterFormats *formats;
+    AVFilterLink *inlink0 = ctx->inputs[0];
+    AVFilterLink *inlink1 = ctx->inputs[1];
+    AVFilterLink *outlink = ctx->outputs[0];
+    int ret;
+    static const enum AVSubtitleType subtitle_fmts[] = { AV_SUBTITLE_FMT_BITMAP, AV_SUBTITLE_FMT_NONE };
+    static const enum AVPixelFormat supported_pix_fmts[] = {
+        AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV444P,
+        AV_PIX_FMT_ARGB,  AV_PIX_FMT_RGBA,
+        AV_PIX_FMT_ABGR,  AV_PIX_FMT_BGRA,
+        AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24,
+        AV_PIX_FMT_NONE
+    };
+
+    /* set input0 video formats */
+    formats = ff_make_format_list(supported_pix_fmts);
+    if ((ret = ff_formats_ref(formats, &inlink0->outcfg.formats)) < 0)
+        return ret;
+
+    /* set output0 video formats */
+    if ((ret = ff_formats_ref(formats, &outlink->incfg.formats)) < 0)
+        return ret;
+
+    /* set input1 subtitle formats */
+    formats = ff_make_format_list(subtitle_fmts);
+    if ((ret = ff_formats_ref(formats, &inlink1->outcfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    OverlaySubsContext *s = ctx->priv;
+    int ret;
+
+    if ((ret = ff_framesync_init_dualinput(&s->fs, ctx)) < 0)
+        return ret;
+
+    outlink->w = ctx->inputs[MAIN]->w;
+    outlink->h = ctx->inputs[MAIN]->h;
+    outlink->time_base = ctx->inputs[MAIN]->time_base;
+    outlink->frame_rate = ctx->inputs[MAIN]->frame_rate;
+
+    return ff_framesync_configure(&s->fs);
+}
+
+// divide by 255 and round to nearest
+// apply a fast variant: (X+127)/255 = ((X+127)*257+257)>>16 = ((X+128)*257)>>16
+#define FAST_DIV255(x) ((((x) + 128) * 257) >> 16)
+
+// calculate the non-pre-multiplied alpha, applying the general equation:
+// alpha = alpha_overlay / ( (alpha_main + alpha_overlay) - (alpha_main * alpha_overlay) )
+// (((x) << 16) - ((x) << 9) + (x)) is a faster version of: 255 * 255 * x
+// ((((x) + (y)) << 8) - ((x) + (y)) - (y) * (x)) is a faster version of: 255 * (x + y)
+#define UNPREMULTIPLY_ALPHA(x, y) ((((x) << 16) - ((x) << 9) + (x)) / ((((x) + (y)) << 8) - ((x) + (y)) - (y) * (x)))
+
+/**
+ * Blend image in src to destination buffer dst at position (x, y).
+ */
+static av_always_inline void blend_packed_rgb(const AVFilterContext *ctx,
+    const AVFrame *dst, const AVSubtitleArea *src,
+    int x, int y,
+    int is_straight)
+{
+    OverlaySubsContext *s = ctx->priv;
+    int i, imax, j, jmax;
+    const int src_w = src->w;
+    const int src_h = src->h;
+    const int dst_w = dst->width;
+    const int dst_h = dst->height;
+    uint8_t alpha;          ///< the amount of overlay to blend on to main
+    const int dr = s->main_rgba_map[R];
+    const int dg = s->main_rgba_map[G];
+    const int db = s->main_rgba_map[B];
+    const int da = s->main_rgba_map[A];
+    const int dstep = s->main_pix_step[0];
+    const int sr = s->overlay_rgba_map[R];
+    const int sg = s->overlay_rgba_map[G];
+    const int sb = s->overlay_rgba_map[B];
+    const int sa = s->overlay_rgba_map[A];
+    int slice_start, slice_end;
+    uint8_t *S, *sp, *d, *dp;
+
+    i = FFMAX(-y, 0);
+    imax = FFMIN3(-y + dst_h, FFMIN(src_h, dst_h), y + src_h);
+
+    slice_start = i;
+    slice_end = i + imax;
+
+    sp = src->buf[0]->data + slice_start       * src->linesize[0];
+    dp = dst->data[0] + (slice_start + y) * dst->linesize[0];
+
+    for (i = slice_start; i < slice_end; i++) {
+        j = FFMAX(-x, 0);
+        S = sp + j;
+        d = dp + ((x + j) * dstep);
+
+        for (jmax = FFMIN(-x + dst_w, src_w); j < jmax; j++) {
+            uint32_t val = src->pal[*S];
+            const uint8_t *sval = (uint8_t *)&val;
+            alpha = sval[sa];
+
+            // if the main channel has an alpha channel, alpha has to be calculated
+            // to create an un-premultiplied (straight) alpha value
+            if (s->main_has_alpha && alpha != 0 && alpha != 255) {
+                const uint8_t alpha_d = d[da];
+                alpha = UNPREMULTIPLY_ALPHA(alpha, alpha_d);
+            }
+
+            switch (alpha) {
+            case 0:
+                break;
+            case 255:
+                d[dr] = sval[sr];
+                d[dg] = sval[sg];
+                d[db] = sval[sb];
+                break;
+            default:
+                // main_value = main_value * (1 - alpha) + overlay_value * alpha
+                // since alpha is in the range 0-255, the result must divided by 255
+                d[dr] = is_straight ? FAST_DIV255(d[dr] * (255 - alpha) + sval[sr] * alpha) :
+                        FFMIN(FAST_DIV255(d[dr] * (255 - alpha)) + sval[sr], 255);
+                d[dg] = is_straight ? FAST_DIV255(d[dg] * (255 - alpha) + sval[sg] * alpha) :
+                        FFMIN(FAST_DIV255(d[dg] * (255 - alpha)) + sval[sg], 255);
+                d[db] = is_straight ? FAST_DIV255(d[db] * (255 - alpha) + sval[sb] * alpha) :
+                        FFMIN(FAST_DIV255(d[db] * (255 - alpha)) + sval[sb], 255);
+            }
+
+            if (s->main_has_alpha) {
+                switch (alpha) {
+                case 0:
+                    break;
+                case 255:
+                    d[da] = sval[sa];
+                    break;
+                default:
+                    // apply alpha compositing: main_alpha += (1-main_alpha) * overlay_alpha
+                    d[da] += FAST_DIV255((255 - d[da]) * S[sa]);
+                }
+            }
+            d += dstep;
+            S += 1;
+        }
+        dp += dst->linesize[0];
+        sp += src->linesize[0];
+    }
+}
+
+static av_always_inline void blend_plane_8_8bits(const AVFilterContext *ctx, const AVFrame *dst, const AVSubtitleArea *area,
+    const uint32_t *yuv_pal, int src_w, int src_h, int dst_w, int dst_h, int plane, int hsub, int vsub,
+    int x, int y, int dst_plane, int dst_offset, int dst_step)
+{
+    const int src_wp = AV_CEIL_RSHIFT(src_w, hsub);
+    const int src_hp = AV_CEIL_RSHIFT(src_h, vsub);
+    const int dst_wp = AV_CEIL_RSHIFT(dst_w, hsub);
+    const int dst_hp = AV_CEIL_RSHIFT(dst_h, vsub);
+    const int yp = y >> vsub;
+    const int xp = x >> hsub;
+    uint8_t *s, *sp, *d, *dp, *dap;
+    int imax, i, j, jmax;
+    int slice_start, slice_end;
+
+    i = FFMAX(-yp, 0);                                                                                     \
+    imax = FFMIN3(-yp + dst_hp, FFMIN(src_hp, dst_hp), yp + src_hp);                                       \
+
+    slice_start = i;
+    slice_end = i + imax;
+
+    sp = area->buf[0]->data + (slice_start << vsub) * area->linesize[0];
+    dp = dst->data[dst_plane] + (yp + slice_start) * dst->linesize[dst_plane] + dst_offset;
+
+    dap = dst->data[3] + ((yp + slice_start) << vsub) * dst->linesize[3];
+
+    for (i = slice_start; i < slice_end; i++) {
+        j = FFMAX(-xp, 0);
+        d = dp + (xp + j) * dst_step;
+        s = sp + (j << hsub);
+        jmax = FFMIN(-xp + dst_wp, src_wp);
+
+        for (; j < jmax; j++) {
+            uint32_t val = yuv_pal[*s];
+            const uint8_t *sval = (uint8_t *)&val;
+            const int alpha = sval[3];
+            const int max = 255, mid = 128;
+            const int d_int = *d;
+            const int sval_int = sval[plane];
+
+            switch (alpha) {
+            case 0:
+                break;
+            case 255:
+                *d = sval[plane];
+                break;
+            default:
+                if (plane > 0)
+                    *d = av_clip(FAST_DIV255((d_int - mid) * (max - alpha) + (sval_int - mid) * alpha) , -mid, mid) + mid;
+                else
+                    *d = FAST_DIV255(d_int * (max - alpha) + sval_int * alpha);
+                break;
+            }
+
+            d += dst_step;
+            s += 1 << hsub;
+        }
+        dp += dst->linesize[dst_plane];
+        sp +=  (1 << vsub) * area->linesize[0];
+        dap += (1 << vsub) * dst->linesize[3];
+    }
+}
+
+#define RGB2Y(r, g, b) (uint8_t)(((66 * (r) + 129 * (g) +  25 * (b) + 128) >> 8) +  16)
+#define RGB2U(r, g, b) (uint8_t)(((-38 * (r) - 74 * (g) + 112 * (b) + 128) >> 8) + 128)
+#define RGB2V(r, g, b) (uint8_t)(((112 * (r) - 94 * (g) -  18 * (b) + 128) >> 8) + 128)
+/* Converts R8 G8 B8 color to YUV. */
+static av_always_inline void rgb_2_yuv(uint8_t r, uint8_t g, uint8_t b, uint8_t* y, uint8_t* u, uint8_t* v)
+{
+    *y = RGB2Y((int)r, (int)g, (int)b);
+    *u = RGB2U((int)r, (int)g, (int)b);
+    *v = RGB2V((int)r, (int)g, (int)b);
+}
+
+
+static av_always_inline void blend_yuv_8_8bits(AVFilterContext *ctx, AVFrame *dst, const AVSubtitleArea *area, int hsub, int vsub, int x, int y)
+{
+    OverlaySubsContext *s = ctx->priv;
+    const int src_w = area->w;
+    const int src_h = area->h;
+    const int dst_w = dst->width;
+    const int dst_h = dst->height;
+    const int sr = s->overlay_rgba_map[R];
+    const int sg = s->overlay_rgba_map[G];
+    const int sb = s->overlay_rgba_map[B];
+    const int sa = s->overlay_rgba_map[A];
+    uint32_t yuvpal[256];
+
+    for (int i = 0; i < 256; ++i) {
+        const uint8_t *rgba = (const uint8_t *)&area->pal[i];
+        uint8_t *yuva = (uint8_t *)&yuvpal[i];
+        rgb_2_yuv(rgba[sr], rgba[sg], rgba[sb], &yuva[Y], &yuva[U], &yuva[V]);
+        yuva[3] = rgba[sa];
+    }
+
+    blend_plane_8_8bits(ctx, dst, area, yuvpal, src_w, src_h, dst_w, dst_h, Y, 0,    0,    x, y, s->main_desc->comp[Y].plane, s->main_desc->comp[Y].offset, s->main_desc->comp[Y].step);
+    blend_plane_8_8bits(ctx, dst, area, yuvpal, src_w, src_h, dst_w, dst_h, U, hsub, vsub, x, y, s->main_desc->comp[U].plane, s->main_desc->comp[U].offset, s->main_desc->comp[U].step);
+    blend_plane_8_8bits(ctx, dst, area, yuvpal, src_w, src_h, dst_w, dst_h, V, hsub, vsub, x, y, s->main_desc->comp[V].plane, s->main_desc->comp[V].offset, s->main_desc->comp[V].step);
+}
+
+static int config_input_main(AVFilterLink *inlink)
+{
+    int ret;
+    AVFilterContext *ctx  = inlink->dst;
+    OverlaySubsContext *s = inlink->dst->priv;
+    const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(inlink->format);
+
+    av_image_fill_max_pixsteps(s->main_pix_step,    NULL, pix_desc);
+    ff_fill_rgba_map(s->overlay_rgba_map, AV_PIX_FMT_RGB32); // it's actually AV_PIX_FMT_PAL8);
+
+    s->hsub = pix_desc->log2_chroma_w;
+    s->vsub = pix_desc->log2_chroma_h;
+
+    s->main_desc = pix_desc;
+
+    s->main_is_packed_rgb = ff_fill_rgba_map(s->main_rgba_map, inlink->format) >= 0;
+    s->main_has_alpha = !!(pix_desc->flags & AV_PIX_FMT_FLAG_ALPHA);
+
+    /* Finish the configuration by evaluating the expressions
+       now when both inputs are configured. */
+    s->var_values[VAR_MAIN_W   ] = s->var_values[VAR_MW] = ctx->inputs[MAIN   ]->w;
+    s->var_values[VAR_MAIN_H   ] = s->var_values[VAR_MH] = ctx->inputs[MAIN   ]->h;
+    s->var_values[VAR_OVERLAY_W] = s->var_values[VAR_OW] = ctx->inputs[OVERLAY]->w;
+    s->var_values[VAR_OVERLAY_H] = s->var_values[VAR_OH] = ctx->inputs[OVERLAY]->h;
+    s->var_values[VAR_HSUB]  = 1<<pix_desc->log2_chroma_w;
+    s->var_values[VAR_VSUB]  = 1<<pix_desc->log2_chroma_h;
+    s->var_values[VAR_X]     = NAN;
+    s->var_values[VAR_Y]     = NAN;
+    s->var_values[VAR_N]     = 0;
+    s->var_values[VAR_T]     = NAN;
+    s->var_values[VAR_POS]   = NAN;
+
+    if ((ret = set_expr(&s->x_pexpr,      s->x_expr,      "x",      ctx)) < 0 ||
+        (ret = set_expr(&s->y_pexpr,      s->y_expr,      "y",      ctx)) < 0)
+        return ret;
+
+    if (s->eval_mode == EVAL_MODE_INIT) {
+        eval_expr(ctx);
+        av_log(ctx, AV_LOG_VERBOSE, "x:%f xi:%d y:%f yi:%d\n",
+               s->var_values[VAR_X], s->x,
+               s->var_values[VAR_Y], s->y);
+    }
+
+    av_log(ctx, AV_LOG_VERBOSE,
+           "main w:%d h:%d fmt:%s overlay w:%d h:%d fmt:%s\n",
+           ctx->inputs[MAIN]->w, ctx->inputs[MAIN]->h,
+           av_get_pix_fmt_name(ctx->inputs[MAIN]->format),
+           ctx->inputs[OVERLAY]->w, ctx->inputs[OVERLAY]->h,
+           av_get_pix_fmt_name(ctx->inputs[OVERLAY]->format));
+    return 0;
+}
+
+static int do_blend(FFFrameSync *fs)
+{
+    AVFilterContext *ctx = fs->parent;
+    AVFrame *mainpic, *second;
+    OverlaySubsContext *s = ctx->priv;
+    AVFilterLink *inlink = ctx->inputs[0];
+    unsigned i;
+    int ret;
+
+    ret = ff_framesync_dualinput_get_writable(fs, &mainpic, &second);
+    if (ret < 0)
+        return ret;
+    if (!second)
+        return ff_filter_frame(ctx->outputs[0], mainpic);
+
+    if (s->eval_mode == EVAL_MODE_FRAME) {
+        int64_t pos = mainpic->pkt_pos;
+
+        s->var_values[VAR_N] = (double)inlink->frame_count_out;
+        s->var_values[VAR_T] = mainpic->pts == AV_NOPTS_VALUE ?
+            NAN :(double)mainpic->pts * av_q2d(inlink->time_base);
+        s->var_values[VAR_POS] = pos == -1 ? NAN : (double)pos;
+
+        s->var_values[VAR_OVERLAY_W] = s->var_values[VAR_OW] = second->width;
+        s->var_values[VAR_OVERLAY_H] = s->var_values[VAR_OH] = second->height;
+        s->var_values[VAR_MAIN_W   ] = s->var_values[VAR_MW] = mainpic->width;
+        s->var_values[VAR_MAIN_H   ] = s->var_values[VAR_MH] = mainpic->height;
+
+        eval_expr(ctx);
+        av_log(ctx, AV_LOG_DEBUG, "n:%f t:%f pos:%f x:%f xi:%d y:%f yi:%d\n",
+               s->var_values[VAR_N], s->var_values[VAR_T], s->var_values[VAR_POS],
+               s->var_values[VAR_X], s->x,
+               s->var_values[VAR_Y], s->y);
+    }
+
+    for (i = 0; i < second->num_subtitle_areas; i++) {
+        const AVSubtitleArea *sub_area = second->subtitle_areas[i];
+
+        if (sub_area->type != AV_SUBTITLE_FMT_BITMAP) {
+            av_log(NULL, AV_LOG_WARNING, "overlay_graphicsubs: non-bitmap subtitle\n");
+            return AVERROR_INVALIDDATA;
+        }
+
+        switch (inlink->format) {
+        case AV_PIX_FMT_YUV420P:
+            blend_yuv_8_8bits(ctx, mainpic, sub_area, 1, 1, sub_area->x + s->x, sub_area->y + s->y);
+            break;
+        case AV_PIX_FMT_YUV422P:
+            blend_yuv_8_8bits(ctx, mainpic, sub_area, 1, 0, sub_area->x + s->x, sub_area->y + s->y);
+            break;
+        case AV_PIX_FMT_YUV444P:
+            blend_yuv_8_8bits(ctx, mainpic, sub_area, 0, 0, sub_area->x + s->x, sub_area->y + s->y);
+            break;
+        case AV_PIX_FMT_RGB24:
+        case AV_PIX_FMT_BGR24:
+        case AV_PIX_FMT_ARGB:
+        case AV_PIX_FMT_RGBA:
+        case AV_PIX_FMT_BGRA:
+        case AV_PIX_FMT_ABGR:
+            blend_packed_rgb(ctx, mainpic, sub_area, sub_area->x + s->x, sub_area->y + s->y, 1);
+            break;
+        default:
+            av_log(NULL, AV_LOG_ERROR, "Unsupported input pix fmt: %d\n", inlink->format);
+            return AVERROR(EINVAL);
+        }
+    }
+
+    return ff_filter_frame(ctx->outputs[0], mainpic);
+}
+
+static av_cold int overlay_graphicsubs_init(AVFilterContext *ctx)
+{
+    OverlaySubsContext *s = ctx->priv;
+
+    s->fs.on_event = do_blend;
+    return 0;
+}
+
+static int overlay_graphicsubs_activate(AVFilterContext *ctx)
+{
+    OverlaySubsContext *s = ctx->priv;
+    return ff_framesync_activate(&s->fs);
+}
+
+static int graphicsub2video_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_BITMAP, AV_SUBTITLE_FMT_NONE };
+    static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_RGB32, AV_PIX_FMT_NONE };
+    int ret;
+
+    /* set input subtitle formats */
+    formats = ff_make_format_list(subtitle_fmts);
+    if ((ret = ff_formats_ref(formats, &inlink->outcfg.formats)) < 0)
+        return ret;
+
+    /* set output video formats */
+    formats = ff_make_format_list(pix_fmts);
+    if ((ret = ff_formats_ref(formats, &outlink->incfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int graphicsub2video_config_input(AVFilterLink *inlink)
+{
+    AVFilterContext *ctx = inlink->dst;
+    OverlaySubsContext *s = ctx->priv;
+
+    if (s->w <= 0 || s->h <= 0) {
+        s->w = inlink->w;
+        s->h = inlink->h;
+    }
+    return 0;
+}
+
+static int graphicsub2video_config_output(AVFilterLink *outlink)
+{
+    const AVFilterContext *ctx  = outlink->src;
+    OverlaySubsContext *s = ctx->priv;
+    const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(outlink->format);
+
+    outlink->w = s->w;
+    outlink->h = s->h;
+
+    if (outlink->w == 0 && outlink->h == 0) {
+        outlink->w = 1;
+        outlink->h = 1;
+    }
+    outlink->sample_aspect_ratio = (AVRational){1,1};
+    outlink->time_base = ctx->inputs[0]->time_base;
+    outlink->frame_rate = ctx->inputs[0]->frame_rate;
+
+    av_image_fill_max_pixsteps(s->main_pix_step, NULL, pix_desc);
+    ff_fill_rgba_map(s->overlay_rgba_map, AV_PIX_FMT_RGB32);
+
+    s->hsub = pix_desc->log2_chroma_w;
+    s->vsub = pix_desc->log2_chroma_h;
+
+    s->main_desc = pix_desc;
+
+    s->main_is_packed_rgb = ff_fill_rgba_map(s->main_rgba_map, outlink->format) >= 0;
+    s->main_has_alpha = !!(pix_desc->flags & AV_PIX_FMT_FLAG_ALPHA);
+
+    return 0;
+}
+
+static int graphicsub2video_filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFilterLink *outlink = inlink->dst->outputs[0];
+    const AVFilterContext *ctx  = outlink->src;
+    OverlaySubsContext *s = ctx->priv;
+    AVFrame *out;
+    const unsigned num_rects = frame->num_subtitle_areas;
+    unsigned int i;
+    int ret;
+
+    if (s->use_caching && s->cache_frame && frame->repeat_sub
+        && s->cache_frame->subtitle_timing.start_pts == frame->subtitle_timing.start_pts) {
+        out = av_frame_clone(s->cache_frame);
+        if (!out)
+            return AVERROR(ENOMEM);
+
+        ret = av_frame_copy_props(out, frame);
+        if (ret < 0)
+            return ret;
+
+        av_log(inlink->dst, AV_LOG_DEBUG, "graphicsub2video CACHED - size %dx%d  pts: %"PRId64"  areas: %d\n", frame->width, frame->height, frame->subtitle_timing.start_pts, frame->num_subtitle_areas);
+        av_frame_free(&frame);
+        return ff_filter_frame(outlink, out);
+    }
+
+
+    out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
+    if (!out) {
+        av_frame_free(&frame);
+        return AVERROR(ENOMEM);
+    }
+
+    memset(out->data[0], 0, (size_t)out->linesize[0] * out->height);
+
+    out->pts = out->pkt_dts = out->best_effort_timestamp = frame->pts;
+    out->coded_picture_number = out->display_picture_number = s->pic_counter++;
+
+    for (i = 0; i < num_rects; i++) {
+        const AVSubtitleArea  *sub_rect = frame->subtitle_areas[i];
+
+        if (sub_rect->type != AV_SUBTITLE_FMT_BITMAP) {
+            av_log(NULL, AV_LOG_WARNING, "graphicsub2video: non-bitmap subtitle\n");
+            av_frame_free(&frame);
+            return AVERROR_INVALIDDATA;
+        }
+
+        blend_packed_rgb(inlink->dst, out, sub_rect, sub_rect->x, sub_rect->y, 1);
+    }
+
+    av_log(inlink->dst, AV_LOG_DEBUG, "graphicsub2video output - size %dx%d  pts: %"PRId64"  areas: %d\n", out->width, out->height, out->pts, frame->num_subtitle_areas);
+
+    if (s->use_caching) {
+        av_frame_free(&s->cache_frame);
+        s->cache_frame = av_frame_clone(out);
+    }
+
+    av_frame_free(&frame);
+    return ff_filter_frame(outlink, out);
+}
+
+#define OFFSET(x) offsetof(OverlaySubsContext, x)
+#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption overlaygraphicsubs_options[] = {
+    { "x", "set the x expression", OFFSET(x_expr), AV_OPT_TYPE_STRING, {.str = "0"}, 0, 0, .flags = FLAGS },
+    { "y", "set the y expression", OFFSET(y_expr), AV_OPT_TYPE_STRING, {.str = "0"}, 0, 0, .flags = FLAGS },
+    { "eof_action", "Action to take when encountering EOF from secondary input ",
+        OFFSET(fs.opt_eof_action), AV_OPT_TYPE_INT, { .i64 = EOF_ACTION_REPEAT },
+        EOF_ACTION_REPEAT, EOF_ACTION_PASS, .flags = FLAGS, "eof_action" },
+        { "repeat", "Repeat the previous frame.",   0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_REPEAT }, .flags = FLAGS, "eof_action" },
+        { "endall", "End both streams.",            0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_ENDALL }, .flags = FLAGS, "eof_action" },
+        { "pass",   "Pass through the main input.", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_PASS },   .flags = FLAGS, "eof_action" },
+    { "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, {.i64 = EVAL_MODE_FRAME}, 0, EVAL_MODE_NB-1, FLAGS, "eval" },
+         { "init",  "eval expressions once during initialization", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_INIT},  .flags = FLAGS, .unit = "eval" },
+         { "frame", "eval expressions per-frame",                  0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_FRAME}, .flags = FLAGS, .unit = "eval" },
+    { "shortest", "force termination when the shortest input terminates", OFFSET(fs.opt_shortest), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, .flags = FLAGS },
+    { "repeatlast", "repeat overlay of the last overlay frame", OFFSET(fs.opt_repeatlast), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, .flags = FLAGS },
+    { NULL }
+};
+
+static const AVOption graphicsub2video_options[] = {
+    { "size",        "set output frame size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, .flags = FLAGS },
+    { "s",           "set output frame size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, .flags = FLAGS },
+    { "use_caching", "Cache output frames",   OFFSET(use_caching), AV_OPT_TYPE_BOOL, { .i64 = 1}, 0, 1, .flags = FLAGS },
+    { NULL }
+};
+
+FRAMESYNC_DEFINE_CLASS(overlaygraphicsubs, OverlaySubsContext, fs);
+
+static const AVFilterPad overlaygraphicsubs_inputs[] = {
+    {
+        .name         = "main",
+        .type         = AVMEDIA_TYPE_VIDEO,
+        .config_props = config_input_main,
+        .flags        = AVFILTERPAD_FLAG_NEEDS_WRITABLE,
+    },
+    {
+        .name         = "overlay",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+    },
+};
+
+static const AVFilterPad overlaygraphicsubs_outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_VIDEO,
+        .config_props  = config_output,
+    },
+};
+
+const AVFilter ff_vf_overlaygraphicsubs = {
+    .name          = "overlaygraphicsubs",
+    .description   = NULL_IF_CONFIG_SMALL("Overlay graphical subtitles on top of the input."),
+    .preinit       = overlaygraphicsubs_framesync_preinit,
+    .init          = overlay_graphicsubs_init,
+    .uninit        = overlay_graphicsubs_uninit,
+    .priv_size     = sizeof(OverlaySubsContext),
+    .priv_class    = &overlaygraphicsubs_class,
+    .activate      = overlay_graphicsubs_activate,
+    FILTER_INPUTS(overlaygraphicsubs_inputs),
+    FILTER_OUTPUTS(overlaygraphicsubs_outputs),
+    FILTER_QUERY_FUNC(overlay_graphicsubs_query_formats),
+};
+
+AVFILTER_DEFINE_CLASS(graphicsub2video);
+
+static const AVFilterPad graphicsub2video_inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .filter_frame = graphicsub2video_filter_frame,
+        .config_props = graphicsub2video_config_input,
+    },
+};
+
+static const AVFilterPad graphicsub2video_outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_VIDEO,
+        .config_props  = graphicsub2video_config_output,
+    },
+};
+
+const AVFilter ff_svf_graphicsub2video = {
+    .name          = "graphicsub2video",
+    .description   = NULL_IF_CONFIG_SMALL("Convert graphical subtitles to video"),
+    .priv_size     = sizeof(OverlaySubsContext),
+    .priv_class    = &graphicsub2video_class,
+    FILTER_INPUTS(graphicsub2video_inputs),
+    FILTER_OUTPUTS(graphicsub2video_outputs),
+    FILTER_QUERY_FUNC(graphicsub2video_query_formats),
+};
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v6 14/25] avfilter/overlaytextsubs: Add overlaytextsubs and textsubs2video filters
  2022-06-26 16:34         ` [FFmpeg-devel] [PATCH v6 00/25] Subtitle Filtering 2022 ffmpegagent
                             ` (12 preceding siblings ...)
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 13/25] avfilter/overlaygraphicsubs: Add overlaygraphicsubs and graphicsub2video filters softworkz
@ 2022-06-26 16:35           ` softworkz
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 15/25] avfilter/textmod: Add textmod, censor and show_speaker filters softworkz
                             ` (10 subsequent siblings)
  24 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-26 16:35 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

- overlaytextsubs {VS -> V)
  Overlay text subtitles onto a video stream.

- textsubs2video {S -> V)
  Converts text subtitles to video frames

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 configure                        |   2 +
 doc/filters.texi                 | 113 +++++
 libavfilter/Makefile             |   2 +
 libavfilter/allfilters.c         |   2 +
 libavfilter/vf_overlaytextsubs.c | 680 +++++++++++++++++++++++++++++++
 5 files changed, 799 insertions(+)
 create mode 100644 libavfilter/vf_overlaytextsubs.c

diff --git a/configure b/configure
index 0580e4a536..99b4d7c8d7 100755
--- a/configure
+++ b/configure
@@ -3692,6 +3692,7 @@ overlay_qsv_filter_deps="libmfx"
 overlay_qsv_filter_select="qsvvpp"
 overlay_vaapi_filter_deps="vaapi VAProcPipelineCaps_blend_flags"
 overlay_vulkan_filter_deps="vulkan spirv_compiler"
+overlaytextsubs_filter_deps="avcodec libass"
 owdenoise_filter_deps="gpl"
 pad_opencl_filter_deps="opencl"
 pan_filter_deps="swresample"
@@ -3730,6 +3731,7 @@ stereo3d_filter_deps="gpl"
 subtitles_filter_deps="avformat avcodec libass"
 super2xsai_filter_deps="gpl"
 pixfmts_super2xsai_test_deps="super2xsai_filter"
+textsub2video_filter_deps="avcodec libass"
 tinterlace_filter_deps="gpl"
 tinterlace_merge_test_deps="tinterlace_filter"
 tinterlace_pad_test_deps="tinterlace_filter"
diff --git a/doc/filters.texi b/doc/filters.texi
index a77546de3d..b73a380515 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -27106,6 +27106,119 @@ Overlay PGS subtitles
 ffmpeg -i "https://streams.videolan.org/samples/sub/PGS/Girl_With_The_Dragon_Tattoo_2%3A23%3A56.mkv" -filter_complex "[0:0][0:1]overlaygraphicsubs" output.mp4
 @end example
 @end itemize
+
+@section overlaytextsubs
+
+Overlay text subtitles onto a video stream.
+
+This filter supersedes the classic @ref{subtitles} filter opposed to which it does no longer require to open and access the source stream separately, which is often causing problems or doesn't even work for non-local or slow sources.
+
+Inputs:
+@itemize
+@item 0: Video [YUV420P, YUV422P, YUV444P, ARGB, RGBA, ABGR, BGRA, RGB24, BGR24]
+@item 1: Subtitles [TEXT]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Video (same as input)
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+
+@item alpha
+Process alpha channel, by default alpha channel is untouched.
+
+@item fonts_dir
+Set a directory path containing fonts that can be used by the filter.
+These fonts will be used in addition to whatever the font provider uses.
+
+@item default_font_path
+Path to a font file to be used as the default font.
+
+@item font_size
+Set the default font size.
+
+@item fontconfig_file
+Path to ASS fontconfig configuration file.
+
+@item force_style
+Override default style or script info parameters of the subtitles. It accepts a
+string containing ASS style format @code{KEY=VALUE} couples separated by ",".
+
+@item margin
+Set the rendering margin in pixels.
+
+@item render_latest_only
+For rendering, alway use the latest event only, which is covering the given point in time
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Overlay ASS subtitles with animations:
+@example
+ffmpeg -i "http://streams.videolan.org/samples/sub/SSA/subtitle_testing_complex.mkv" -filter_complex "[0:v]overlaytextsubs" -map 0 -y out.mkv
+@end example
+@end itemize
+
+@section textsub2video
+
+Converts text subtitles to video frames.
+
+For overlaying text subtitles onto video frames it is recommended to use the overlaytextsubs filter.
+The textsub2video is useful for for creating transparent text-frames when overlay is done via hw acceleration
+
+Inputs:
+@itemize
+@item 0: Subtitles [TEXT]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Video [RGB32]
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+
+@item rate, r
+Set the framerate for updating overlay frames.
+Normally, overlay frames will only be updated each time when the subtitles to display are changing.
+In cases where subtitles include advanced features (like animation), this parameter determines the frequency by which the overlay frames should be updated.
+
+@item size, s
+Set the output frame size.
+Allows to override the size of output video frames.
+
+@item fonts_dir
+Set a directory path containing fonts that can be used by the filter.
+These fonts will be used in addition to whatever the font provider uses.
+
+@item default_font_path
+Path to a font file to be used as the default font.
+
+@item font_size
+Set the default font size.
+
+@item fontconfig_file
+Path to ASS fontconfig configuration file.
+
+@item force_style
+Override default style or script info parameters of the subtitles. It accepts a
+string containing ASS style format @code{KEY=VALUE} couples separated by ",".
+
+@item margin
+Set the rendering margin in pixels.
+
+@item render_latest_only
+For rendering, alway use the latest event only, which is covering the given point in time.
+@end table
+
 @c man end SUBTITLE FILTERS
 
 @chapter Multimedia Filters
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 97ec9fc3b3..7482df7a2a 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -393,6 +393,7 @@ OBJS-$(CONFIG_OVERLAY_QSV_FILTER)            += vf_overlay_qsv.o framesync.o
 OBJS-$(CONFIG_OVERLAY_VAAPI_FILTER)          += vf_overlay_vaapi.o framesync.o vaapi_vpp.o
 OBJS-$(CONFIG_OVERLAY_VULKAN_FILTER)         += vf_overlay_vulkan.o vulkan.o vulkan_filter.o
 OBJS-$(CONFIG_OVERLAYGRAPHICSUBS_FILTER)     += vf_overlaygraphicsubs.o framesync.o
+OBJS-$(CONFIG_OVERLAYTEXTSUBS_FILTER)        += vf_overlaytextsubs.o
 OBJS-$(CONFIG_OWDENOISE_FILTER)              += vf_owdenoise.o
 OBJS-$(CONFIG_PAD_FILTER)                    += vf_pad.o
 OBJS-$(CONFIG_PAD_OPENCL_FILTER)             += vf_pad_opencl.o opencl.o opencl/pad.o
@@ -484,6 +485,7 @@ OBJS-$(CONFIG_SWAPRECT_FILTER)               += vf_swaprect.o
 OBJS-$(CONFIG_SWAPUV_FILTER)                 += vf_swapuv.o
 OBJS-$(CONFIG_TBLEND_FILTER)                 += vf_blend.o framesync.o
 OBJS-$(CONFIG_TELECINE_FILTER)               += vf_telecine.o
+OBJS-$(CONFIG_TEXTSUB2VIDEO_FILTER)          += vf_overlaytextsubs.o
 OBJS-$(CONFIG_THISTOGRAM_FILTER)             += vf_histogram.o
 OBJS-$(CONFIG_THRESHOLD_FILTER)              += vf_threshold.o framesync.o
 OBJS-$(CONFIG_THUMBNAIL_FILTER)              += vf_thumbnail.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 06ac93457d..e540ec349f 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -372,6 +372,7 @@ extern const AVFilter ff_vf_overlay_vaapi;
 extern const AVFilter ff_vf_overlay_vulkan;
 extern const AVFilter ff_vf_overlay_cuda;
 extern const AVFilter ff_vf_overlaygraphicsubs;
+extern const AVFilter ff_vf_overlaytextsubs;
 extern const AVFilter ff_vf_owdenoise;
 extern const AVFilter ff_vf_pad;
 extern const AVFilter ff_vf_pad_opencl;
@@ -560,6 +561,7 @@ extern const AVFilter ff_avf_showvolume;
 extern const AVFilter ff_avf_showwaves;
 extern const AVFilter ff_avf_showwavespic;
 extern const AVFilter ff_svf_graphicsub2video;
+extern const AVFilter ff_svf_textsub2video;
 extern const AVFilter ff_vaf_spectrumsynth;
 
 /* multimedia sources */
diff --git a/libavfilter/vf_overlaytextsubs.c b/libavfilter/vf_overlaytextsubs.c
new file mode 100644
index 0000000000..1e43289bec
--- /dev/null
+++ b/libavfilter/vf_overlaytextsubs.c
@@ -0,0 +1,680 @@
+/*
+ * 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
+ * overlay text subtitles on top of a video frame
+ */
+
+#include "config_components.h"
+
+#include <ass/ass.h>
+#include "libavutil/ass_internal.h"
+#include "libavutil/thread.h"
+
+#include "drawutils.h"
+#include "filters.h"
+
+#include "libavutil/opt.h"
+#include "libavutil/pixdesc.h"
+
+typedef struct TextSubsContext {
+    const AVClass *class;
+    AVMutex mutex;
+    int is_mutex_initialized;
+
+    ASS_Library   *library;
+    ASS_Renderer  *renderer;
+    ASS_Track     *track;
+
+    char *default_font_path;
+    char *fonts_dir;
+    char *fc_file;
+    double font_size;
+    char *force_style;
+    char *language;
+    int margin;
+    int render_latest_only;
+
+    int alpha;
+    FFDrawContext draw;
+
+    int got_header;
+    int out_w, out_h;
+    AVRational frame_rate;
+    AVFrame *last_frame;
+    int need_frame;
+    int eof;
+} TextSubsContext;
+
+/* libass supports a log level ranging from 0 to 7 */
+static const int ass_libavfilter_log_level_map[] = {
+    AV_LOG_QUIET,               /* 0 */
+    AV_LOG_PANIC,               /* 1 */
+    AV_LOG_FATAL,               /* 2 */
+    AV_LOG_ERROR,               /* 3 */
+    AV_LOG_WARNING,             /* 4 */
+    AV_LOG_INFO,                /* 5 */
+    AV_LOG_VERBOSE,             /* 6 */
+    AV_LOG_DEBUG,               /* 7 */
+};
+
+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 void uninit(AVFilterContext *ctx)
+{
+    TextSubsContext *s = ctx->priv;
+
+    if (s->track)
+        ass_free_track(s->track);
+    if (s->renderer)
+        ass_renderer_done(s->renderer);
+    if (s->library)
+        ass_library_done(s->library);
+
+    s->track = NULL;
+    s->renderer = NULL;
+    s->library = NULL;
+
+    if (s->is_mutex_initialized) {
+        ff_mutex_destroy(&s->mutex);
+        s->is_mutex_initialized = 0;
+    }
+
+    av_frame_free(&s->last_frame);
+}
+
+static int overlay_textsubs_query_formats(AVFilterContext *ctx)
+{
+    AVFilterFormats *formats;
+    AVFilterLink *inlink0 = ctx->inputs[0];
+    AVFilterLink *inlink1 = ctx->inputs[1];
+    AVFilterLink *outlink = ctx->outputs[0];
+    static const enum AVSubtitleType subtitle_fmts[] = { AV_SUBTITLE_FMT_ASS, AV_SUBTITLE_FMT_NONE };
+    int ret;
+
+    /* set input0 and output0 video formats */
+    formats = ff_draw_supported_pixel_formats(0);
+    if ((ret = ff_formats_ref(formats, &inlink0->outcfg.formats)) < 0)
+        return ret;
+    if ((ret = ff_formats_ref(formats, &outlink->incfg.formats)) < 0)
+        return ret;
+
+    /* set input1 subtitle formats */
+    formats = ff_make_format_list(subtitle_fmts);
+    if ((ret = ff_formats_ref(formats, &inlink1->outcfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+
+    outlink->w = ctx->inputs[0]->w;
+    outlink->h = ctx->inputs[0]->h;
+    outlink->time_base = ctx->inputs[0]->time_base;
+    outlink->frame_rate = ctx->inputs[0]->frame_rate;
+
+    return 0;
+}
+
+static int config_input_main(AVFilterLink *inlink)
+{
+    AVFilterContext *ctx  = inlink->dst;
+    TextSubsContext *s = inlink->dst->priv;
+    int ret;
+
+    ret = ff_draw_init(&s->draw, inlink->format, s->alpha ? FF_DRAW_PROCESS_ALPHA : 0);
+    if (ret < 0) {
+        av_log(ctx, AV_LOG_ERROR, "Could not initialize ff_draw.\n");
+        return ret;
+    }
+
+    ass_set_frame_size  (s->renderer, inlink->w, inlink->h);
+    ass_set_pixel_aspect(s->renderer, av_q2d(inlink->sample_aspect_ratio));
+
+    av_log(ctx, AV_LOG_VERBOSE, "Subtitle screen: %dx%d\n\n\n\n", inlink->w, inlink->h);
+
+    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(TextSubsContext *s, 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(&s->draw, &color, rgba_color);
+        ff_blend_mask(&s->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 void process_header(AVFilterContext *link, AVFrame *frame)
+{
+    TextSubsContext *s = link->priv;
+    ASS_Track *track = s->track;
+    ASS_Style *style;
+    int sid = 0;
+
+    if (!track)
+        return;
+
+    if (frame && frame->subtitle_header) {
+        char *subtitle_header = (char *)frame->subtitle_header->data;
+        ass_process_codec_private(s->track, subtitle_header, strlen(subtitle_header));
+    }
+    else {
+        char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+        if (!subtitle_header)
+            return;
+
+        ass_process_codec_private(s->track, subtitle_header, strlen(subtitle_header));
+        av_free(subtitle_header);
+    }
+
+    if (s->language)
+        s->track->Language = av_strdup(s->language);
+
+    if (!s->track->event_format) {
+        s->track->event_format = av_strdup("ReadOrder, Layer, Style, Name, MarginL, MarginR, MarginV, Effect, Text");
+    }
+
+    if (s->track->n_styles == 0) {
+        sid = ass_alloc_style(track);
+        style = &s->track->styles[sid];
+        style->Name             = av_strdup("Default");
+        style->PrimaryColour    = 0xffffff00;
+        style->SecondaryColour  = 0x00ffff00;
+        style->OutlineColour    = 0x00000000;
+        style->BackColour       = 0x00000080;
+        style->Bold             = 200;
+        style->ScaleX           = 1.0;
+        style->ScaleY           = 1.0;
+        style->Spacing          = 0;
+        style->BorderStyle      = 1;
+        style->Outline          = 2;
+        style->Shadow           = 3;
+        style->Alignment        = 2;
+    }
+    else
+        style = &s->track->styles[sid];
+
+    style->FontSize         = s->font_size;
+    style->MarginL = style->MarginR = style->MarginV = s->margin;
+
+    track->default_style = sid;
+
+    s->got_header = 1;
+}
+
+static int filter_video_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFilterContext *ctx = inlink->dst;
+    TextSubsContext *s = ctx->priv;
+    int detect_change = 0;
+    ASS_Image *image;
+
+
+    int64_t time_ms  = (int64_t)((double)frame->pts * av_q2d(inlink->time_base) * 1000);
+    int64_t time_ms1 = (int64_t)((double)ctx->inputs[1]->current_pts * av_q2d(ctx->inputs[1]->time_base) * 1000);
+
+    if (time_ms1 < time_ms + 1000)
+        ff_request_frame(ctx->inputs[1]);
+
+    av_log(ctx, AV_LOG_DEBUG, "filter_video_frame - video: %"PRId64"ms  sub: %"PRId64"ms  rel %d\n", time_ms, time_ms1, (time_ms1 < time_ms));
+
+    ff_mutex_lock(&s->mutex);
+    image = ass_render_frame(s->renderer, s->track, time_ms, &detect_change);
+    ff_mutex_unlock(&s->mutex);
+
+    if (detect_change)
+        av_log(ctx, AV_LOG_DEBUG, "Change happened at time ms:%"PRId64"\n", time_ms);
+
+    overlay_ass_image(s, frame, image);
+
+    return ff_filter_frame(ctx->outputs[0], frame);
+}
+
+static int filter_subtitle_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFilterContext *ctx = inlink->dst;
+    TextSubsContext *s = ctx->priv;
+    const int64_t start_time = av_rescale_q(frame->subtitle_timing.start_pts, AV_TIME_BASE_Q, av_make_q(1, 1000));
+    const int64_t duration   = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, av_make_q(1, 1000));
+    const int64_t frame_time = (int64_t)((double)frame->pts * av_q2d(inlink->time_base) * 1000);
+
+    // Postpone header processing until we receive a frame with content
+    if (!s->got_header && frame->num_subtitle_areas > 0)
+        process_header(ctx, frame);
+
+    av_log(ctx, AV_LOG_DEBUG, "filter_subtitle_frame dur: %"PRId64"ms frame: %"PRId64"ms  sub: %"PRId64"ms  repeat_sub %d\n", duration, frame_time, start_time, frame->repeat_sub);
+
+    if (frame->repeat_sub)
+        goto exit;
+
+    ff_mutex_lock(&s->mutex);
+
+    if (s->render_latest_only && s->track->n_events > 0) {
+        const int64_t previous_start_time = s->track->events[s->track->n_events - 1].Start;
+        const int64_t diff = start_time - previous_start_time;
+        for (int i = s->track->n_events - 1; i >= 0; i--) {
+            if (previous_start_time != s->track->events[i].Start)
+                break;
+
+            if (s->track->events[i].Duration > diff)
+                s->track->events[i].Duration = diff;
+        }
+    }
+
+    for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+        char *ass_line = frame->subtitle_areas[i]->ass;
+        if (!ass_line)
+            continue;
+
+        ass_process_chunk(s->track, ass_line, strlen(ass_line), start_time, duration);
+    }
+
+    ff_mutex_unlock(&s->mutex);
+
+exit:
+    av_frame_free(&frame);
+    return 0;
+}
+
+static av_cold int init(AVFilterContext *ctx)
+{
+    int ret;
+    TextSubsContext *s = ctx->priv;
+
+    s->library = ass_library_init();
+
+    if (!s->library) {
+        av_log(ctx, AV_LOG_ERROR, "Could not initialize libass.\n");
+        return AVERROR(EINVAL);
+    }
+
+    ass_set_message_cb(s->library, ass_log, ctx);
+
+    /* Initialize fonts */
+    if (s->fonts_dir)
+        ass_set_fonts_dir(s->library, s->fonts_dir);
+
+    ass_set_extract_fonts(s->library, 1);
+
+    s->renderer = ass_renderer_init(s->library);
+    if (!s->renderer) {
+        av_log(ctx, AV_LOG_ERROR, "Could not initialize libass renderer.\n");
+        return AVERROR(EINVAL);
+    }
+
+    s->track = ass_new_track(s->library);
+    if (!s->track) {
+        av_log(ctx, AV_LOG_ERROR, "ass_new_track() failed!\n");
+        return AVERROR(EINVAL);
+    }
+
+    ass_set_check_readorder(s->track, 0);
+
+    ass_set_fonts(s->renderer, s->default_font_path, NULL, 1, s->fc_file, 1);
+
+    if (s->force_style) {
+        char **list = NULL;
+        char *temp = NULL;
+        char *ptr = av_strtok(s->force_style, ",", &temp);
+        int i = 0;
+        while (ptr) {
+            av_dynarray_add(&list, &i, ptr);
+            if (!list) {
+                return AVERROR(ENOMEM);
+            }
+            ptr = av_strtok(NULL, ",", &temp);
+        }
+        av_dynarray_add(&list, &i, NULL);
+        if (!list) {
+            return AVERROR(ENOMEM);
+        }
+        ass_set_style_overrides(s->library, list);
+        av_free(list);
+    }
+
+    ret = ff_mutex_init(&s->mutex, NULL);
+    if (ret) {
+        av_log(ctx, AV_LOG_ERROR, "mutex initialiuzation failed! Error code: %d\n", ret);
+        return AVERROR(EINVAL);
+    }
+
+    s->is_mutex_initialized = 1;
+
+    return ret;
+}
+
+static int textsub2video_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 input0 subtitle format */
+    formats = ff_make_format_list(subtitle_fmts);
+    if ((ret = ff_formats_ref(formats, &inlink->outcfg.formats)) < 0)
+        return ret;
+
+    /* set output0 video format */
+    formats = ff_draw_supported_pixel_formats(AV_PIX_FMT_FLAG_ALPHA);
+    if ((ret = ff_formats_ref(formats, &outlink->incfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int textsub2video_config_input(AVFilterLink *inlink)
+{
+    AVFilterContext *ctx = inlink->dst;
+    TextSubsContext *s = ctx->priv;
+
+    if (s->out_w <= 0 || s->out_h <= 0) {
+        s->out_w = inlink->w;
+        s->out_h = inlink->h;
+    }
+
+    return 0;
+}
+
+static int textsub2video_config_output(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    TextSubsContext *s = ctx->priv;
+    int ret;
+
+    ret = ff_draw_init(&s->draw, outlink->format, FF_DRAW_PROCESS_ALPHA);
+    if (ret < 0) {
+        av_log(ctx, AV_LOG_ERROR, "Could not initialize ff_draw.\n");
+        return ret;
+    }
+
+    if (s->out_w <= 0 || s->out_h <= 0) {
+        av_log(ctx, AV_LOG_ERROR, "No output image size set.\n");
+        return AVERROR(EINVAL);
+    }
+
+    ass_set_frame_size  (s->renderer, s->out_w, s->out_h);
+
+    outlink->w = s->out_w;
+    outlink->h = s->out_h;
+    outlink->sample_aspect_ratio = (AVRational){1,1};
+    outlink->frame_rate = s->frame_rate;
+
+    return 0;
+}
+
+static int textsub2video_request_frame(AVFilterLink *outlink)
+{
+    TextSubsContext *s = outlink->src->priv;
+    AVFilterLink *inlink = outlink->src->inputs[0];
+    int64_t last_pts = outlink->current_pts;
+    int64_t next_pts, time_ms;
+    int i, detect_change = 0, status;
+    AVFrame *out;
+    ASS_Image *image;
+
+    status = ff_outlink_get_status(inlink);
+    if (status == AVERROR_EOF)
+        return AVERROR_EOF;
+
+    if (s->eof)
+        return AVERROR_EOF;
+
+    if (inlink->current_pts == AV_NOPTS_VALUE) { // || outlink->current_pts > inlink->current_pts) {
+        int ret = ff_request_frame(inlink);
+        if (ret == AVERROR_EOF) {
+            s->eof = 1;
+        }
+
+        if (ret != 0)
+            av_log(outlink->src, AV_LOG_DEBUG, "ff_request_frame returned: %d\n", ret);
+
+        s->need_frame = 1;
+        return 0;
+    }
+
+    if (last_pts == AV_NOPTS_VALUE)
+        next_pts = last_pts = inlink->current_pts * av_q2d(inlink->time_base) / av_q2d(outlink->time_base);
+    else
+        next_pts = last_pts + (int64_t)(1.0 / av_q2d(outlink->frame_rate) / av_q2d(outlink->time_base));
+
+    time_ms = (int64_t)((double)next_pts * av_q2d(outlink->time_base) * 1000);
+
+    ff_mutex_lock(&s->mutex);
+    image = ass_render_frame(s->renderer, s->track, time_ms, &detect_change);
+    ff_mutex_unlock(&s->mutex);
+
+    if (detect_change)
+        av_log(outlink->src, AV_LOG_VERBOSE, "Change happened at time ms:%"PRId64" pts:%"PRId64"\n", time_ms, next_pts);
+    else if (s->last_frame) {
+        out = av_frame_clone(s->last_frame);
+        if (!out)
+            return AVERROR(ENOMEM);
+
+        out->pts = out->pkt_dts = out->best_effort_timestamp = next_pts;
+        return ff_filter_frame(outlink, out);
+    }
+
+    out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
+    if (!out)
+        return AVERROR(ENOMEM);
+
+    for (i = 0; i < AV_NUM_DATA_POINTERS; i++) {
+        if (out->buf[i] && i != 1)
+            memset(out->buf[i]->data, 0, out->buf[i]->size);
+    }
+
+    out->pts = out->pkt_dts = out->best_effort_timestamp = next_pts;
+
+    if (image)
+        overlay_ass_image(s, out, image);
+
+    av_frame_free(&s->last_frame);
+
+    s->last_frame = av_frame_clone(out);
+
+    return ff_filter_frame(outlink, out);
+}
+
+static int textsub2video_filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFilterContext *ctx = inlink->dst;
+    TextSubsContext *s = ctx->priv;
+    const int64_t start_time = av_rescale_q(frame->subtitle_timing.start_pts, AV_TIME_BASE_Q, av_make_q(1, 1000));
+    const int64_t duration   = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, av_make_q(1, 1000));
+
+    av_log(ctx, AV_LOG_VERBOSE, "textsub2video_filter_frame num_subtitle_rects: %d, start_time_ms: %"PRId64"\n", frame->num_subtitle_areas, start_time);
+
+    if (!s->got_header && frame->num_subtitle_areas > 0)
+        process_header(ctx, frame);
+
+    if (frame->repeat_sub)
+        goto exit;
+
+    ff_mutex_lock(&s->mutex);
+
+    if (s->render_latest_only && s->track->n_events > 0) {
+        const int64_t previous_start_time = s->track->events[s->track->n_events - 1].Start;
+        const int64_t diff = start_time - previous_start_time;
+        for (int i = s->track->n_events - 1; i >= 0; i--) {
+            if (previous_start_time != s->track->events[i].Start)
+                break;
+
+            if (s->track->events[i].Duration > diff)
+                s->track->events[i].Duration = diff;
+
+        }
+    }
+
+    for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+        char *ass_line = frame->subtitle_areas[i]->ass;
+        if (!ass_line)
+            continue;
+
+        ass_process_chunk(s->track, ass_line, strlen(ass_line), start_time, duration);
+    }
+
+
+    ff_mutex_unlock(&s->mutex);
+
+exit:
+    av_frame_free(&frame);
+
+    if (s->need_frame) {
+        s->need_frame = 0;
+        return textsub2video_request_frame(ctx->outputs[0]);
+    }
+
+    return 0;
+}
+
+#define OFFSET(x) offsetof(TextSubsContext, x)
+#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption overlaytextsubs_options[] = {
+    {"alpha",              "enable processing of alpha channel", OFFSET(alpha),              AV_OPT_TYPE_BOOL,   {.i64 = 0   }, 0,         1,        .flags =  FLAGS},
+    {"font_size",          "default font size",                  OFFSET(font_size),          AV_OPT_TYPE_DOUBLE, {.dbl = 18.0}, 0.0,       100.0,    .flags =  FLAGS},
+    {"force_style",        "force subtitle style",               OFFSET(force_style),        AV_OPT_TYPE_STRING, {.str = NULL}, 0,         0,        .flags =  FLAGS},
+    {"margin",             "default margin",                     OFFSET(margin),             AV_OPT_TYPE_INT,    {.i64 = 20  }, 0,         INT_MAX,  .flags =  FLAGS},
+    {"default_font_path",  "path to default font",               OFFSET(default_font_path),  AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"fonts_dir",          "directory to scan for fonts",        OFFSET(fonts_dir),          AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"fontsdir",           "directory to scan for fonts",        OFFSET(fonts_dir),          AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"fontconfig_file",    "fontconfig file to load",            OFFSET(fc_file),            AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"language",           "default language",                   OFFSET(language),           AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"render_latest_only", "newest sub event for each time",     OFFSET(render_latest_only), AV_OPT_TYPE_BOOL,   {.i64 = 0   }, 0,         1,        .flags =  FLAGS},
+    { .name = NULL }
+};
+
+static const AVOption textsub2video_options[] = {
+    {"rate",               "set frame rate",                   OFFSET(frame_rate),         AV_OPT_TYPE_VIDEO_RATE, {.str="8"},   0,         INT_MAX,   .flags =  FLAGS},
+    {"r",                  "set frame rate",                   OFFSET(frame_rate),         AV_OPT_TYPE_VIDEO_RATE, {.str="8"},   0,         INT_MAX,   .flags =  FLAGS},
+    {"size",               "set video size",                   OFFSET(out_w),              AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0,         0,        .flags =  FLAGS},
+    {"s",                  "set video size",                   OFFSET(out_w),              AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0,         0,        .flags =  FLAGS},
+    {"font_size",          "default font size",                OFFSET(font_size),          AV_OPT_TYPE_DOUBLE,     {.dbl = 18.0}, 0.0,       100.0,    .flags =  FLAGS},
+    {"force_style",        "force subtitle style",             OFFSET(force_style),        AV_OPT_TYPE_STRING,     {.str = NULL}, 0,         0,        .flags =  FLAGS},
+    {"margin",             "default margin",                   OFFSET(margin),             AV_OPT_TYPE_INT,        {.i64 = 20  }, 0,         INT_MAX,  .flags =  FLAGS},
+    {"default_font_path",  "path to default font",             OFFSET(default_font_path),  AV_OPT_TYPE_STRING,     {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"fonts_dir",          "directory to scan for fonts",      OFFSET(fonts_dir),          AV_OPT_TYPE_STRING,     {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"fontsdir",           "directory to scan for fonts",      OFFSET(fonts_dir),          AV_OPT_TYPE_STRING,     {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"fontconfig_file",    "fontconfig file to load",          OFFSET(fc_file),            AV_OPT_TYPE_STRING,     {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"language",           "default language",                 OFFSET(language),           AV_OPT_TYPE_STRING,     {.str = NULL}, CHAR_MIN,  CHAR_MAX, .flags =  FLAGS},
+    {"render_latest_only", "newest sub event for each time",   OFFSET(render_latest_only), AV_OPT_TYPE_BOOL,       {.i64 = 0   }, 0,         1,        .flags =  FLAGS},
+    { .name = NULL }
+};
+
+#if CONFIG_OVERLAYTEXTSUBS_FILTER
+
+AVFILTER_DEFINE_CLASS(overlaytextsubs);
+
+static const AVFilterPad overlaytextsubs_inputs[] = {
+    {
+        .name         = "main",
+        .type         = AVMEDIA_TYPE_VIDEO,
+        .config_props = config_input_main,
+        .flags        = AVFILTERPAD_FLAG_NEEDS_WRITABLE,
+        .filter_frame = filter_video_frame,
+    },
+    {
+        .name         = "overlay",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .filter_frame = filter_subtitle_frame,
+    },
+};
+
+static const AVFilterPad overlaytextsubs_outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_VIDEO,
+        .config_props  = config_output,
+    },
+};
+
+const AVFilter ff_vf_overlaytextsubs = {
+    .name          = "overlaytextsubs",
+    .description   = NULL_IF_CONFIG_SMALL("Overlay textual subtitles on top of the input."),
+    .init          = init,
+    .uninit        = uninit,
+    .priv_size     = sizeof(TextSubsContext),
+    .priv_class    = &overlaytextsubs_class,
+    FILTER_INPUTS(overlaytextsubs_inputs),
+    FILTER_OUTPUTS(overlaytextsubs_outputs),
+    FILTER_QUERY_FUNC(overlay_textsubs_query_formats),
+};
+#endif
+
+#if CONFIG_TEXTSUB2VIDEO_FILTER
+
+AVFILTER_DEFINE_CLASS(textsub2video);
+
+static const AVFilterPad textsub2video_inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .config_props = textsub2video_config_input,
+        .filter_frame = textsub2video_filter_frame,
+    },
+};
+
+static const AVFilterPad textsub2video_outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_VIDEO,
+        .config_props  = textsub2video_config_output,
+        .request_frame = textsub2video_request_frame,
+    },
+};
+
+const AVFilter ff_svf_textsub2video = {
+    .name          = "textsub2video",
+    .description   = NULL_IF_CONFIG_SMALL("Convert textual subtitles to video frames"),
+    .init          = init,
+    .uninit        = uninit,
+    .priv_size     = sizeof(TextSubsContext),
+    .priv_class    = &textsub2video_class,
+    FILTER_INPUTS(textsub2video_inputs),
+    FILTER_OUTPUTS(textsub2video_outputs),
+    FILTER_QUERY_FUNC(textsub2video_query_formats),
+};
+#endif
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v6 15/25] avfilter/textmod: Add textmod, censor and show_speaker filters
  2022-06-26 16:34         ` [FFmpeg-devel] [PATCH v6 00/25] Subtitle Filtering 2022 ffmpegagent
                             ` (13 preceding siblings ...)
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 14/25] avfilter/overlaytextsubs: Add overlaytextsubs and textsubs2video filters softworkz
@ 2022-06-26 16:35           ` softworkz
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 16/25] avfilter/stripstyles: Add stripstyles filter softworkz
                             ` (9 subsequent siblings)
  24 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-26 16:35 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

- textmod {S -> S)
  Modify subtitle text in a number of ways

- censor {S -> S)
  Censor subtitles using a word list

- show_speaker {S -> S)
  Prepend speaker names from ASS subtitles to the visible text lines

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 doc/filters.texi         | 206 ++++++++++++
 libavfilter/Makefile     |   5 +
 libavfilter/allfilters.c |   5 +
 libavfilter/sf_textmod.c | 710 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 926 insertions(+)
 create mode 100644 libavfilter/sf_textmod.c

diff --git a/doc/filters.texi b/doc/filters.texi
index b73a380515..0d078e2573 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -26998,6 +26998,145 @@ existing filters using @code{--disable-filters}.
 
 Below is a description of the currently available subtitle filters.
 
+
+@section censor
+
+Censor selected words in text subtitles.
+
+Inputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+@item mode
+The censoring mode to apply.
+
+Supported censoring modes are:
+
+@table @var
+@item 0, keep_first_last
+Replace all characters with the 'censor_char' except the first and the last character of a word.
+For words with less than 4 characters, the last character will be replaced as well.
+For words with less than 3 characters, the first character will be replaced as well.
+@item 1, keep_first
+Replace all characters with the 'censor_char' except the first character of a word.
+For words with less than 3 characters, the first character will be replaced as well.
+@item 2, all
+Replace all characters with the 'censor_char'.
+@end table
+
+@item words
+A list of words to censor, separated by 'separator'.
+
+@item words_file
+Specify a file from which to load the contents for the 'words' parameter.
+
+@item censor_char
+Single character used as replacement for censoring.
+
+@item separator
+Delimiter character for words. Used with replace_words and remove_words- Must be a single character.
+The default is '.'.
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Censor a few given words with a pound character.
+@example
+ffmpeg -i "http://streams.videolan.org/samples/sub/SSA/subtitle_testing_complex.mkv" -filter_complex "[0:1]censor=words='diss,louder,hope,beam,word':censor_char='#'" -map 0 -y output.mkv
+@end example
+@end itemize
+
+
+@section textmod
+
+Modify subtitle text in a number of ways.
+
+Inputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+@item mode
+The kind of text modification to apply
+
+Supported operation modes are:
+
+@table @var
+@item 0, leet
+Convert subtitle text to 'leet speak'. It's primarily useful for testing as the modification will be visible with almost all text lines.
+@item 1, to_upper
+Change all text to upper case. Might improve readability.
+@item 2, to_lower
+Change all text to lower case.
+@item 3, replace_chars
+Replace one or more characters. Requires the find and replace parameters to be specified.
+Both need to be equal in length.
+The first char in find is replaced by the first char in replace, same for all subsequent chars.
+@item 4, remove_chars
+Remove certain characters. Requires the find parameter to be specified.
+All chars in the find parameter string will be removed from all subtitle text.
+@item 5, replace_words
+Replace one or more words. Requires the find and replace parameters to be specified. Multiple words must be separated by the delimiter char specified vie the separator parameter (default: ',').
+The number of words in the find and replace parameters needs to be equal.
+The first word in find is replaced by the first word in replace, same for all subsequent words
+@item 6, remove_words
+Remove certain words. Requires the find parameter to be specified. Multiple words must be separated by the delimiter char specified vie the separator parameter (default: ',').
+All words in the find parameter string will be removed from all subtitle text.
+@end table
+
+@item find
+Required for replace_chars, remove_chars, replace_words and remove_words.
+
+@item find_file
+Specify a file from which to load the contents for the 'find' parameter.
+
+@item replace
+Required for replace_chars and replace_words.
+
+@item replace_file
+Specify a file from which to load the contents for the 'replace' parameter.
+
+@item separator
+Delimiter character for words. Used with replace_words and remove_words- Must be a single character.
+The default is '.'.
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Change all characters to upper case while keeping all styles and animations:
+@example
+ffmpeg -i "https://streams.videolan.org/ffmpeg/mkv_subtitles.mkv" -filter_complex "[0:s]textmod=mode=to_upper" -map 0 -y out.mkv
+@end example
+@item
+Remove a set of symbol characters for am improved and smoother visual apperance:
+@example
+ffmpeg -i "https://streams.videolan.org/ffmpeg/mkv_subtitles.mkv" -filter_complex "[0:s]textmod=mode=remove_chars:find='$&#@*§'" -map 0 -y out.mkv
+@end example
+@end itemize
+
 @section graphicsub2video
 
 Renders graphic subtitles as video frames.
@@ -27165,6 +27304,73 @@ ffmpeg -i "http://streams.videolan.org/samples/sub/SSA/subtitle_testing_complex.
 @end example
 @end itemize
 
+@section showspeaker
+
+Prepend speaker names to subtitle lines (when available).
+
+Subtitles in ASS/SSA format are often including the names of the persons
+or character for each subtitle line. The showspeaker filter adds those names
+to the actual subtitle text to make it visible on playback.
+
+Inputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+@item format
+The format for prepending speaker names. Default is 'square_brackets'.
+
+Supported operation modes are:
+
+@table @var
+@item 0, square_brackets
+Enclose the speaker name in square brackets, followed by space ('[speaker] text').
+@item 1, round_brackets
+Enclose the speaker name in round brackets, followed by space ('(speaker) text').
+@item 2, colon
+Separate the speaker name with a colon and space ('speaker: text').
+@item 3, plain
+Separate the speaker name with a space only ('speaker text').
+@end table
+
+@item line_break
+Set thís parameter to insert a line break between speaker name and text instead of the space character.
+
+@item style
+Allows to set a specific style for the speaker name text.
+
+This can be either a named style that exists in the ass subtitle header script (e.g. 'Default') or an ass style override code.
+Example:  @{\\c&HDD0000&\\be1\\i1\\bord10@}
+This sets the color to blue, enables edge blurring, italic font and a border of size 10.
+
+The behavior is as follows:
+
+- When the style parameter is not provided, the filter will find the first position in the event string that is actual text.
+  The speaker name will be inserted at this position. This allows to have the speaker name shown in the same style like the
+  regular text, in case the string would start with a sequence of style codes.
+- When the style parameter is provided, everything will be prepended to the original text:
+  Style Code or Style name >> Speaker Name >> Style Reset Code >> Original Text
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Prepend speaker names with blue text, smooth edges and blend/overlay that onto the video.
+@example
+ffmpeg -i INPUT -filter_complex "showspeaker=format=colon:style='@{\\c&HDD0000&\\be1@}',[0:v]overlaytextsubs"
+@end example
+@end itemize
+
 @section textsub2video
 
 Converts text subtitles to video frames.
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 7482df7a2a..6a68c44e1c 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -577,6 +577,11 @@ OBJS-$(CONFIG_YUVTESTSRC_FILTER)             += vsrc_testsrc.o
 
 OBJS-$(CONFIG_NULLSINK_FILTER)               += vsink_nullsink.o
 
+# subtitle filters
+OBJS-$(CONFIG_CENSOR_FILTER)                 += sf_textmod.o
+OBJS-$(CONFIG_SHOW_SPEAKER_FILTER)           += sf_textmod.o
+OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
+
 # multimedia filters
 OBJS-$(CONFIG_ABITSCOPE_FILTER)              += avf_abitscope.o
 OBJS-$(CONFIG_ADRAWGRAPH_FILTER)             += f_drawgraph.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index e540ec349f..2228e414db 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -569,6 +569,11 @@ extern const AVFilter ff_avsrc_avsynctest;
 extern const AVFilter ff_avsrc_amovie;
 extern const AVFilter ff_avsrc_movie;
 
+/* subtitle filters */
+extern const AVFilter ff_sf_censor;
+extern const AVFilter ff_sf_showspeaker;
+extern const AVFilter ff_sf_textmod;
+
 /* those filters are part of public or internal API,
  * they are formatted to not be found by the grep
  * as they are manually added again (due to their 'names'
diff --git a/libavfilter/sf_textmod.c b/libavfilter/sf_textmod.c
new file mode 100644
index 0000000000..9651e07c65
--- /dev/null
+++ b/libavfilter/sf_textmod.c
@@ -0,0 +1,710 @@
+/*
+ * 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 allows to modify subtitle text in several ways
+ */
+
+#include <libavutil/ass_internal.h>
+
+#include "libavutil/opt.h"
+#include "internal.h"
+#include "libavutil/ass_split_internal.h"
+#include "libavutil/bprint.h"
+#include "libavutil/file.h"
+
+static const char* leet_src = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+static const char* leet_dst = "abcd3f6#1jklmn0pq257uvwxyzAB(D3F6#1JKLMN0PQ257UVWXYZ";
+
+enum TextModFilterType {
+    TM_TEXTMOD,
+    TM_CENSOR,
+    TM_SHOW_SPEAKER,
+};
+
+enum TextModOperation {
+    OP_LEET,
+    OP_TO_UPPER,
+    OP_TO_LOWER,
+    OP_REPLACE_CHARS,
+    OP_REMOVE_CHARS,
+    OP_REPLACE_WORDS,
+    OP_REMOVE_WORDS,
+    NB_OPS,
+};
+
+enum CensorMode {
+    CM_KEEP_FIRST_LAST,
+    CM_KEEP_FIRST,
+    CM_ALL,
+};
+
+enum ShowSpeakerMode {
+    SM_SQUARE_BRACKETS,
+    SM_ROUND_BRACKETS,
+    SM_COLON,
+    SM_PLAIN,
+};
+
+typedef struct TextModContext {
+    const AVClass *class;
+    enum AVSubtitleType format;
+    enum TextModFilterType filter_type;
+    enum TextModOperation operation;
+    enum CensorMode censor_mode;
+    enum ShowSpeakerMode speaker_mode;
+    char *find;
+    char *find_file;
+    char *style;
+    char *replace;
+    char *replace_file;
+    char *separator;
+    char *censor_char;
+    char **find_list;
+    int  line_break;
+    int  nb_find_list;
+    char **replace_list;
+    int  nb_replace_list;
+} TextModContext;
+
+static char **split_string(char *source, int *nb_elems, const char *delim)
+{
+    char **list = NULL;
+    char *temp = NULL;
+    char *ptr = av_strtok(source, delim, &temp);
+
+    while (ptr) {
+        if (strlen(ptr)) {
+            av_dynarray_add(&list, nb_elems, ptr);
+            if (!list)
+                return NULL;
+        }
+
+        ptr = av_strtok(NULL, delim, &temp);
+    }
+
+    if (!list)
+        return NULL;
+
+    for (int i = 0; i < *nb_elems; i++) {
+        list[i] = av_strdup(list[i]);
+        if (!list[i]) {
+            for (int n = 0; n < i; n++)
+                av_free(list[n]);
+            av_free(list);
+            return NULL;
+        }
+    }
+
+    return list;
+}
+
+static int load_text_from_file(AVFilterContext *ctx, const char *file_name, char **text, char separator)
+{
+    int err;
+    uint8_t *textbuf;
+    char *tmp;
+    size_t textbuf_size;
+    int offset = 0;
+
+    if ((err = av_file_map(file_name, &textbuf, &textbuf_size, 0, ctx)) < 0) {
+        av_log(ctx, AV_LOG_ERROR, "The text file '%s' could not be read or is empty\n", file_name);
+        return err;
+    }
+
+    if (textbuf_size > 1 &&
+        (textbuf[0] == 0xFF && textbuf[1] == 0xFE
+        || textbuf[0] == 0xFE && textbuf[1] == 0xFF)) {
+        av_log(ctx, AV_LOG_ERROR, "UTF text files are not supported. File: %s\n", file_name);
+        return AVERROR(EINVAL);
+    }
+
+    if (textbuf_size > 2 && textbuf[0] == 0xEF && textbuf[1] == 0xBB && textbuf[2] == 0xBF)
+        offset = 3; // UTF-8
+
+    if (textbuf_size > SIZE_MAX - 1 || !((tmp = av_strndup((char *)textbuf + offset, textbuf_size - offset)))) {
+        av_file_unmap(textbuf, textbuf_size);
+        return AVERROR(ENOMEM);
+    }
+
+    av_file_unmap(textbuf, textbuf_size);
+
+    for (size_t i = 0; i < strlen(tmp); i++) {
+        switch (tmp[i]) {
+        case '\n':
+        case '\r':
+        case '\f':
+        case '\v':
+            tmp[i] = separator;
+        }
+    }
+
+    *text = tmp;
+
+    return 0;
+}
+
+static int load_files(AVFilterContext *ctx)
+{
+    TextModContext *s = ctx->priv;
+    int ret;
+
+    if (!s->separator || strlen(s->separator) != 1) {
+        av_log(ctx, AV_LOG_ERROR, "A single character needs to be specified for the separator parameter.\n");
+        return AVERROR(EINVAL);
+    }
+
+    if (s->find_file && strlen(s->find_file)) {
+        ret = load_text_from_file(ctx, s->find_file, &s->find, s->separator[0]);
+        if (ret < 0 )
+            return ret;
+    }
+
+    if (s->replace_file && strlen(s->replace_file)) {
+        ret = load_text_from_file(ctx, s->replace_file, &s->replace, s->separator[0]);
+        if (ret < 0 )
+            return ret;
+    }
+
+    return 0;
+}
+
+static int init_censor(AVFilterContext *ctx)
+{
+    TextModContext *s = ctx->priv;
+    int ret;
+
+    s->filter_type = TM_CENSOR;
+    s->operation = OP_REPLACE_WORDS;
+
+    ret = load_files(ctx);
+    if (ret < 0 )
+        return ret;
+
+    if (!s->find || !strlen(s->find)) {
+        av_log(ctx, AV_LOG_ERROR, "Either the 'words' or the 'words_file' parameter needs to be specified\n");
+        return AVERROR(EINVAL);
+    }
+
+    if (!s->censor_char || strlen(s->censor_char) != 1) {
+        av_log(ctx, AV_LOG_ERROR, "A single character needs to be specified for the censor_char parameter\n");
+        return AVERROR(EINVAL);
+    }
+
+    s->find_list = split_string(s->find, &s->nb_find_list, s->separator);
+    if (!s->find_list)
+        return AVERROR(ENOMEM);
+
+    s->replace_list = av_calloc(s->nb_find_list, sizeof(char *));
+    if (!s->replace_list)
+        return AVERROR(ENOMEM);
+
+    for (int i = 0; i < s->nb_find_list; i++) {
+        size_t len, start = 0, end;
+        char *item = av_strdup(s->find_list[i]);
+        if (!item)
+            return AVERROR(ENOMEM);
+
+        len = end = strlen(item);
+
+        switch (s->censor_mode) {
+        case CM_KEEP_FIRST_LAST:
+
+            if (len > 2)
+                start = 1;
+            if (len > 3)
+                end--;
+
+            break;
+        case CM_KEEP_FIRST:
+
+            if (len > 2)
+                start = 1;
+
+            break;
+        }
+
+        for (size_t n = start; n < end; n++)
+            item[n] = s->censor_char[0];
+
+        s->replace_list[i] = item;
+    }
+
+    return 0;
+}
+
+static int init_showspeaker(AVFilterContext *ctx)
+{
+    TextModContext *s = ctx->priv;
+    s->filter_type = TM_SHOW_SPEAKER;
+
+    return 0;
+}
+
+static int init(AVFilterContext *ctx)
+{
+    TextModContext *s = ctx->priv;
+    int ret;
+
+    ret = load_files(ctx);
+    if (ret < 0 )
+        return ret;
+
+    switch (s->operation) {
+    case OP_REPLACE_CHARS:
+    case OP_REMOVE_CHARS:
+    case OP_REPLACE_WORDS:
+    case OP_REMOVE_WORDS:
+        if (!s->find || !strlen(s->find)) {
+            av_log(ctx, AV_LOG_ERROR, "Selected mode requires the 'find' parameter to be specified\n");
+            return AVERROR(EINVAL);
+        }
+        break;
+    }
+
+    switch (s->operation) {
+    case OP_REPLACE_CHARS:
+    case OP_REPLACE_WORDS:
+        if (!s->replace || !strlen(s->replace)) {
+            av_log(ctx, AV_LOG_ERROR, "Selected mode requires the 'replace' parameter to be specified\n");
+            return AVERROR(EINVAL);
+        }
+        break;
+    }
+
+    if (s->operation == OP_REPLACE_CHARS && strlen(s->find) != strlen(s->replace)) {
+        av_log(ctx, AV_LOG_ERROR, "Selected mode requires the 'find' and 'replace' parameters to have the same length\n");
+        return AVERROR(EINVAL);
+    }
+
+    if (s->operation == OP_REPLACE_WORDS || s->operation == OP_REMOVE_WORDS) {
+        if (!s->separator || strlen(s->separator) != 1) {
+            av_log(ctx, AV_LOG_ERROR, "Selected mode requires a single separator char to be specified\n");
+            return AVERROR(EINVAL);
+        }
+
+        s->find_list = split_string(s->find, &s->nb_find_list, s->separator);
+        if (!s->find_list)
+            return AVERROR(ENOMEM);
+
+        if (s->operation == OP_REPLACE_WORDS) {
+
+            s->replace_list = split_string(s->replace, &s->nb_replace_list, s->separator);
+            if (!s->replace_list)
+                return AVERROR(ENOMEM);
+
+            if (s->nb_find_list != s->nb_replace_list) {
+                av_log(ctx, AV_LOG_ERROR, "The number of words in 'find' and 'replace' needs to be equal\n");
+                return AVERROR(EINVAL);
+            }
+        }
+    }
+
+    return 0;
+}
+
+static void uninit(AVFilterContext *ctx)
+{
+    TextModContext *s = ctx->priv;
+
+    for (int i = 0; i < s->nb_find_list; i++)
+        av_freep(&s->find_list[i]);
+
+    s->nb_find_list = 0;
+    av_freep(&s->find_list);
+
+    for (int i = 0; i < s->nb_replace_list; i++)
+        av_freep(&s->replace_list[i]);
+
+    s->nb_replace_list = 0;
+    av_freep(&s->replace_list);
+}
+
+static char *process_text(TextModContext *s, char *text)
+{
+    const char *char_src = s->find;
+    const char *char_dst = s->replace;
+    char *result         = NULL;
+    int escape_level     = 0, k = 0;
+
+    switch (s->operation) {
+    case OP_LEET:
+    case OP_REPLACE_CHARS:
+
+        if (s->operation == OP_LEET) {
+            char_src = leet_src;
+            char_dst = leet_dst;
+        }
+
+        result = av_strdup(text);
+        if (!result)
+            return NULL;
+
+        for (size_t n = 0; n < strlen(result); n++) {
+            if (result[n] == '{')
+                escape_level++;
+
+            if (!escape_level) {
+                size_t len = strlen(char_src);
+                for (size_t t = 0; t < len; t++) {
+                    if (result[n] == char_src[t]) {
+                        result[n] = char_dst[t];
+                        break;
+                    }
+                }
+            }
+
+            if (result[n] == '}')
+                escape_level--;
+        }
+
+        break;
+    case OP_TO_UPPER:
+    case OP_TO_LOWER:
+
+        result = av_strdup(text);
+        if (!result)
+            return NULL;
+
+        for (size_t n = 0; n < strlen(result); n++) {
+            if (result[n] == '{')
+                escape_level++;
+            if (!escape_level)
+                result[n] = s->operation == OP_TO_LOWER ? av_tolower(result[n]) : av_toupper(result[n]);
+            if (result[n] == '}')
+                escape_level--;
+        }
+
+        break;
+    case OP_REMOVE_CHARS:
+
+        result = av_strdup(text);
+        if (!result)
+            return NULL;
+
+        for (size_t n = 0; n < strlen(result); n++) {
+            int skip_char = 0;
+
+            if (result[n] == '{')
+                escape_level++;
+
+            if (!escape_level) {
+                size_t len = strlen(char_src);
+                for (size_t t = 0; t < len; t++) {
+                    if (result[n] == char_src[t]) {
+                        skip_char = 1;
+                        break;
+                    }
+                }
+            }
+
+            if (!skip_char)
+                result[k++] = result[n];
+
+            if (result[n] == '}')
+                escape_level--;
+        }
+
+        result[k] = 0;
+
+        break;
+    case OP_REPLACE_WORDS:
+    case OP_REMOVE_WORDS:
+
+        result = av_strdup(text);
+        if (!result)
+            return NULL;
+
+        for (int n = 0; n < s->nb_find_list; n++) {
+            char *tmp           = result;
+            const char *replace = (s->operation == OP_REPLACE_WORDS) ? s->replace_list[n] : "";
+
+            result = av_strireplace(result, s->find_list[n], replace);
+            if (!result)
+                return NULL;
+
+            av_free(tmp);
+        }
+
+        break;
+    }
+
+    return result;
+}
+
+static char *process_dialog_show_speaker(TextModContext *s, char *ass_line)
+{
+    ASSDialog *dialog = avpriv_ass_split_dialog(NULL, ass_line);
+    int escape_level = 0;
+    unsigned pos = 0, len;
+    char *result, *text;
+    AVBPrint pbuf;
+
+    if (!dialog)
+        return NULL;
+
+    text = process_text(s, dialog->text);
+    if (!text)
+        return NULL;
+
+    if (!dialog->name || !strlen(dialog->name) || !dialog->text || !strlen(dialog->text))
+        return av_strdup(ass_line);
+
+    // Find insertion point in case the line starts with style codes
+    len = (unsigned)strlen(dialog->text);
+    for (unsigned i = 0; i < len; i++) {
+
+        if (dialog->text[i] == '{')
+            escape_level++;
+
+        if (dialog->text[i] == '}')
+            escape_level--;
+
+        if (escape_level == 0) {
+            pos = i;
+            break;
+        }
+    }
+
+    if (s->style && strlen(s->style))
+        // When a style is specified reset the insertion point
+        // (always add speaker plus style at the start in that case)
+        pos = 0;
+
+    if (pos >= len - 1)
+        return av_strdup(ass_line);
+
+    av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
+
+    if (pos > 0) {
+        av_bprint_append_data(&pbuf, dialog->text, pos);
+    }
+
+    if (s->style && strlen(s->style)) {
+        if (s->style[0] == '{')
+            // Assume complete and valid style code, e.g. {\c&HFF0000&}
+            av_bprintf(&pbuf, "%s", s->style);
+        else
+            // Otherwise it must be a style name
+            av_bprintf(&pbuf, "{\\r%s}", s->style);
+    }
+
+    switch (s->speaker_mode) {
+    case SM_SQUARE_BRACKETS:
+        av_bprintf(&pbuf, "[%s]", dialog->name);
+        break;
+    case SM_ROUND_BRACKETS:
+        av_bprintf(&pbuf, "(%s)", dialog->name);
+        break;
+    case SM_COLON:
+        av_bprintf(&pbuf, "%s:", dialog->name);
+        break;
+    case SM_PLAIN:
+        av_bprintf(&pbuf, "%s", dialog->name);
+        break;
+    }
+
+    if (s->style && strlen(s->style)) {
+        // Reset line style
+        if (dialog->style && strlen(dialog->style) && !av_strcasecmp(dialog->style, "default"))
+            av_bprintf(&pbuf, "{\\r%s}", dialog->style);
+        else
+            av_bprintf(&pbuf, "{\\r}");
+    }
+
+    if (s->line_break)
+        av_bprintf(&pbuf, "\\N");
+    else
+        av_bprintf(&pbuf, " ");
+
+    av_bprint_append_data(&pbuf, dialog->text + pos, len - pos);
+
+    av_bprint_finalize(&pbuf, &text);
+
+    result = avpriv_ass_get_dialog_ex(dialog->readorder, dialog->layer, dialog->style, dialog->name, dialog->margin_l, dialog->margin_r, dialog->margin_v, dialog->effect, text);
+
+    av_free(text);
+    avpriv_ass_free_dialog(&dialog);
+    return result;
+}
+
+static char *process_dialog(TextModContext *s, char *ass_line)
+{
+    ASSDialog *dialog;
+    char *result, *text;
+
+    if (s->filter_type == TM_SHOW_SPEAKER)
+        return process_dialog_show_speaker(s, ass_line);
+
+    dialog = avpriv_ass_split_dialog(NULL, ass_line);
+    if (!dialog)
+        return NULL;
+
+    text = process_text(s, dialog->text);
+    if (!text)
+        return NULL;
+
+    result = avpriv_ass_get_dialog_ex(dialog->readorder, dialog->layer, dialog->style, dialog->name, dialog->margin_l, dialog->margin_r, dialog->margin_v, dialog->effect, text);
+
+    av_free(text);
+    avpriv_ass_free_dialog(&dialog);
+    return result;
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    AVFilterLink *inlink = outlink->src->inputs[0];
+
+    outlink->w = inlink->w;
+    outlink->h = inlink->h;
+    outlink->time_base = inlink->time_base;
+    outlink->frame_rate = inlink->frame_rate;
+
+    return 0;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    TextModContext *s = inlink->dst->priv;
+    AVFilterLink *outlink = inlink->dst->outputs[0];
+    int ret;
+
+    outlink->format = inlink->format;
+
+    ret = av_frame_make_writable(frame);
+    if (ret < 0)
+        return ret;
+
+    for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+
+        AVSubtitleArea *area = frame->subtitle_areas[i];
+
+        if (area->ass) {
+            char *tmp = area->ass;
+            area->ass = process_dialog(s, area->ass);
+            av_free(tmp);
+            if (!area->ass)
+                return AVERROR(ENOMEM);
+        }
+    }
+
+    return ff_filter_frame(outlink, frame);
+}
+
+#define OFFSET(x) offsetof(TextModContext, x)
+#define FLAGS (AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption textmod_options[] = {
+    { "mode",             "set operation mode",              OFFSET(operation),    AV_OPT_TYPE_INT,    {.i64=OP_LEET},          OP_LEET, NB_OPS-1, FLAGS, "mode" },
+    {   "leet",           "convert text to 'leet speak'",    0,                    AV_OPT_TYPE_CONST,  {.i64=OP_LEET},          0,       0,        FLAGS, "mode" },
+    {   "to_upper",       "change to upper case",            0,                    AV_OPT_TYPE_CONST,  {.i64=OP_TO_UPPER},      0,       0,        FLAGS, "mode" },
+    {   "to_lower",       "change to lower case",            0,                    AV_OPT_TYPE_CONST,  {.i64=OP_TO_LOWER},      0,       0,        FLAGS, "mode" },
+    {   "replace_chars",  "replace characters",              0,                    AV_OPT_TYPE_CONST,  {.i64=OP_REPLACE_CHARS}, 0,       0,        FLAGS, "mode" },
+    {   "remove_chars",   "remove characters",               0,                    AV_OPT_TYPE_CONST,  {.i64=OP_REMOVE_CHARS},  0,       0,        FLAGS, "mode" },
+    {   "replace_words",  "replace words",                   0,                    AV_OPT_TYPE_CONST,  {.i64=OP_REPLACE_WORDS}, 0,       0,        FLAGS, "mode" },
+    {   "remove_words",   "remove words",                    0,                    AV_OPT_TYPE_CONST,  {.i64=OP_REMOVE_WORDS},  0,       0,        FLAGS, "mode" },
+    { "find",             "chars/words to find or remove",   OFFSET(find),         AV_OPT_TYPE_STRING, {.str = NULL},           0,       0,        FLAGS, NULL   },
+    { "find_file",        "load find param from file",       OFFSET(find_file),    AV_OPT_TYPE_STRING, {.str = NULL},           0,       0,        FLAGS, NULL   },
+    { "replace",          "chars/words to replace",          OFFSET(replace),      AV_OPT_TYPE_STRING, {.str = NULL},           0,       0,        FLAGS, NULL   },
+    { "replace_file",     "load replace param from file",    OFFSET(replace_file), AV_OPT_TYPE_STRING, {.str = NULL},           0,       0,        FLAGS, NULL   },
+    { "separator",        "word separator",                  OFFSET(separator),    AV_OPT_TYPE_STRING, {.str = ","},            0,       0,        FLAGS, NULL   },
+    { NULL },
+};
+
+
+static const AVOption censor_options[] = {
+    { "mode",               "set censoring mode",        OFFSET(censor_mode), AV_OPT_TYPE_INT,    {.i64=CM_KEEP_FIRST_LAST}, 0, 2, FLAGS, "mode" },
+    {   "keep_first_last",  "censor inner chars",        0,                   AV_OPT_TYPE_CONST,  {.i64=CM_KEEP_FIRST_LAST}, 0, 0, FLAGS, "mode" },
+    {   "keep_first",       "censor all but first char", 0,                   AV_OPT_TYPE_CONST,  {.i64=CM_KEEP_FIRST},      0, 0, FLAGS, "mode" },
+    {   "all",              "censor all chars",          0,                   AV_OPT_TYPE_CONST,  {.i64=CM_ALL},             0, 0, FLAGS, "mode" },
+    { "words",              "list of words to censor",   OFFSET(find),        AV_OPT_TYPE_STRING, {.str = NULL},             0, 0, FLAGS, NULL   },
+    { "words_file",         "path to word list file",    OFFSET(find_file),   AV_OPT_TYPE_STRING, {.str = NULL},             0, 0, FLAGS, NULL   },
+    { "separator",          "word separator",            OFFSET(separator),   AV_OPT_TYPE_STRING, {.str = ","},              0, 0, FLAGS, NULL   },
+    { "censor_char",        "replacement character",     OFFSET(censor_char), AV_OPT_TYPE_STRING, {.str = "*"},              0, 0, FLAGS, NULL   },
+    { NULL },
+};
+
+static const AVOption showspeaker_options[] = {
+    { "format",             "speaker name formatting",        OFFSET(speaker_mode), AV_OPT_TYPE_INT,    {.i64=SM_SQUARE_BRACKETS}, 0, 2, FLAGS, "format" },
+    {   "square_brackets",  "[speaker] text",                 0,                    AV_OPT_TYPE_CONST,  {.i64=SM_SQUARE_BRACKETS}, 0, 0, FLAGS, "format" },
+    {   "round_brackets",   "(speaker) text",                 0,                    AV_OPT_TYPE_CONST,  {.i64=SM_ROUND_BRACKETS},  0, 0, FLAGS, "format" },
+    {   "colon",            "speaker: text",                  0,                    AV_OPT_TYPE_CONST,  {.i64=SM_COLON},           0, 0, FLAGS, "format" },
+    {   "plain",            "speaker text",                   0,                    AV_OPT_TYPE_CONST,  {.i64=SM_PLAIN},           0, 0, FLAGS, "format" },
+    { "line_break",         "insert line break",              OFFSET(line_break),   AV_OPT_TYPE_BOOL,   {.i64=0},                  0, 1, FLAGS, NULL     },
+    { "style",              "ass type name or style code",    OFFSET(style),        AV_OPT_TYPE_STRING, {.str = NULL},             0, 0, FLAGS, NULL     },
+    { NULL },
+};
+
+AVFILTER_DEFINE_CLASS(textmod);
+AVFILTER_DEFINE_CLASS(censor);
+AVFILTER_DEFINE_CLASS(showspeaker);
+
+static const AVFilterPad inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .filter_frame = filter_frame,
+    },
+};
+
+static const AVFilterPad outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_SUBTITLE,
+        .config_props  = config_output,
+    },
+};
+
+const AVFilter ff_sf_textmod = {
+    .name          = "textmod",
+    .description   = NULL_IF_CONFIG_SMALL("Modify subtitle text in several ways"),
+    .init          = init,
+    .uninit        = uninit,
+    .priv_size     = sizeof(TextModContext),
+    .priv_class    = &textmod_class,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_SINGLE_SUBFMT(AV_SUBTITLE_FMT_ASS),
+};
+
+const AVFilter ff_sf_censor = {
+    .name          = "censor",
+    .description   = NULL_IF_CONFIG_SMALL("Censor words in subtitle text"),
+    .init          = init_censor,
+    .uninit        = uninit,
+    .priv_size     = sizeof(TextModContext),
+    .priv_class    = &censor_class,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_SINGLE_SUBFMT(AV_SUBTITLE_FMT_ASS),
+};
+
+const AVFilter ff_sf_showspeaker = {
+    .name          = "showspeaker",
+    .description   = NULL_IF_CONFIG_SMALL("Prepend speaker names to text subtitles (when available)"),
+    .init          = init_showspeaker,
+    .uninit        = uninit,
+    .priv_size     = sizeof(TextModContext),
+    .priv_class    = &showspeaker_class,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_SINGLE_SUBFMT(AV_SUBTITLE_FMT_ASS),
+};
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v6 16/25] avfilter/stripstyles: Add stripstyles filter
  2022-06-26 16:34         ` [FFmpeg-devel] [PATCH v6 00/25] Subtitle Filtering 2022 ffmpegagent
                             ` (14 preceding siblings ...)
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 15/25] avfilter/textmod: Add textmod, censor and show_speaker filters softworkz
@ 2022-06-26 16:35           ` softworkz
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 17/25] avfilter/splitcc: Add splitcc filter for closed caption handling softworkz
                             ` (8 subsequent siblings)
  24 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-26 16:35 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

- stripstyles {S -> S)
  Remove all inline styles from subtitle events

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 doc/filters.texi             |  37 ++++++
 libavfilter/Makefile         |   1 +
 libavfilter/allfilters.c     |   1 +
 libavfilter/sf_stripstyles.c | 237 +++++++++++++++++++++++++++++++++++
 4 files changed, 276 insertions(+)
 create mode 100644 libavfilter/sf_stripstyles.c

diff --git a/doc/filters.texi b/doc/filters.texi
index 0d078e2573..1158df64d2 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -27058,6 +27058,43 @@ ffmpeg -i "http://streams.videolan.org/samples/sub/SSA/subtitle_testing_complex.
 @end example
 @end itemize
 
+@section stripstyles
+
+Remove all inline styles from subtitle events.
+
+Inputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Subtitles[TEXT]
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+@item remove_animated
+Also remove text which is subject to animation (default: true)
+Usually, animated text elements are used used in addition to static subtitle lines for creating effects, so in most cases it is safe to remove the animation content.
+If subtitle text is missing, try setting this to false.
+
+@item select_layer
+Process only ASS subtitle events from a specific layer. This allows to filter out certain effects where an ASS author duplicates the text onto multiple layers.
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Remove styles and animations from ASS subtitles and output events from ass layer 0 only. Then convert asn save as SRT stream:
+@example
+ffmpeg -i "https://streams.videolan.org/samples/sub/SSA/subtitle_testing_complex.mkv" -filter_complex "[0:1]stripstyles=select_layer=0" -map 0 -c:s srt output.mkv
+@end example
+@end itemize
+
 
 @section textmod
 
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 6a68c44e1c..a99a0f6583 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -580,6 +580,7 @@ OBJS-$(CONFIG_NULLSINK_FILTER)               += vsink_nullsink.o
 # subtitle filters
 OBJS-$(CONFIG_CENSOR_FILTER)                 += sf_textmod.o
 OBJS-$(CONFIG_SHOW_SPEAKER_FILTER)           += sf_textmod.o
+OBJS-$(CONFIG_STRIPSTYLES_FILTER)            += sf_stripstyles.o
 OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
 
 # multimedia filters
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 2228e414db..7a958b51d4 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -572,6 +572,7 @@ extern const AVFilter ff_avsrc_movie;
 /* subtitle filters */
 extern const AVFilter ff_sf_censor;
 extern const AVFilter ff_sf_showspeaker;
+extern const AVFilter ff_sf_stripstyles;
 extern const AVFilter ff_sf_textmod;
 
 /* those filters are part of public or internal API,
diff --git a/libavfilter/sf_stripstyles.c b/libavfilter/sf_stripstyles.c
new file mode 100644
index 0000000000..78dc6f3ef4
--- /dev/null
+++ b/libavfilter/sf_stripstyles.c
@@ -0,0 +1,237 @@
+/*
+ * 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 "libavutil/opt.h"
+#include "internal.h"
+#include "libavutil/ass_internal.h"
+#include "libavutil/ass_split_internal.h"
+#include "libavutil/bprint.h"
+
+typedef struct StripStylesContext {
+    const AVClass *class;
+    enum AVSubtitleType format;
+    int remove_animated;
+    enum ASSSplitComponents keep_flags;
+    int select_layer;
+} StripStylesContext;
+
+typedef struct DialogContext {
+    StripStylesContext* ss_ctx;
+    AVBPrint buffer;
+    int drawing_scale;
+    int is_animated;
+    int plain_text_length;
+} DialogContext;
+
+static void dialog_text_cb(void *priv, const char *text, int len)
+{
+    DialogContext *s = priv;
+
+    av_log(s->ss_ctx, AV_LOG_DEBUG, "dialog_text_cb: %s\n", text);
+
+    if (!s->drawing_scale && (!s->is_animated || !s->ss_ctx->remove_animated))
+        s->plain_text_length += len;
+        ////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)
+        s->plain_text_length += 2;
+        ////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 char *process_dialog(StripStylesContext *s, const char *ass_line)
+{
+    DialogContext dlg_ctx = { .ss_ctx = s };
+    ASSDialog *dialog = avpriv_ass_split_dialog(NULL, ass_line);
+    char *result = NULL;
+
+    if (!dialog)
+        return NULL;
+
+    if (s->select_layer >= 0 && dialog->layer != s->select_layer) {
+        avpriv_ass_free_dialog(&dialog);
+        return NULL;
+    }
+
+    dlg_ctx.ss_ctx = s;
+
+    av_bprint_init(&dlg_ctx.buffer, 512, AV_BPRINT_SIZE_UNLIMITED);
+
+    avpriv_ass_filter_override_codes(&dialog_callbacks, &dlg_ctx, dialog->text, &dlg_ctx.buffer, s->keep_flags);
+
+    if (av_bprint_is_complete(&dlg_ctx.buffer) && dlg_ctx.buffer.len > 0 && dlg_ctx.plain_text_length > 0)
+        result = avpriv_ass_get_dialog_ex(dialog->readorder, dialog->layer, dialog->style, dialog->name, dialog->margin_l, dialog->margin_r, dialog->margin_v, dialog->effect, dlg_ctx.buffer.str);
+
+    av_bprint_finalize(&dlg_ctx.buffer, NULL);
+    avpriv_ass_free_dialog(&dialog);
+
+    return result;
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    AVFilterLink *inlink = outlink->src->inputs[0];
+
+    outlink->w = inlink->w;
+    outlink->h = inlink->h;
+    outlink->time_base = inlink->time_base;
+    outlink->frame_rate = inlink->frame_rate;
+
+    return 0;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    StripStylesContext *s = inlink->dst->priv;
+    AVFilterLink *outlink = inlink->dst->outputs[0];
+    int ret;
+
+    outlink->format = inlink->format;
+
+    ret = av_frame_make_writable(frame);
+    if (ret <0 ) {
+        av_frame_free(&frame);
+        return AVERROR(ENOMEM);
+    }
+
+    for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+
+        AVSubtitleArea *area = frame->subtitle_areas[i];
+
+        if (area->ass) {
+            char *tmp = area->ass;
+            area->ass = process_dialog(s, area->ass);
+
+            if (area->ass) {
+                av_log(inlink->dst, AV_LOG_DEBUG, "original: %d %s\n", i, tmp);
+                av_log(inlink->dst, AV_LOG_DEBUG, "stripped: %d %s\n", i, area->ass);
+            }
+            else
+                area->ass = NULL;
+
+            av_free(tmp);
+        }
+    }
+
+    return ff_filter_frame(outlink, frame);
+}
+
+#define OFFSET(x) offsetof(StripStylesContext, x)
+#define FLAGS (AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption stripstyles_options[] = {
+    { "keep_flags", "flags to control which override codes to keep", OFFSET(keep_flags), AV_OPT_TYPE_FLAGS, { .i64 = ASS_SPLIT_TEXT }, .flags = FLAGS, .unit = "keepflags" },
+        { "basic",          "keep static style tags only",             .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_BASIC          },  .flags=FLAGS, .unit = "keepflags" },
+        { "all_known",      "keep all known tags",                     .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_ALL_KNOWN      },  .flags=FLAGS, .unit = "keepflags" },
+        { "text",           "keep text content",                       .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_TEXT           },  .flags=FLAGS, .unit = "keepflags" },
+        { "color",          "keep color tags (\\c, \\<n>c)",           .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_COLOR          },  .flags=FLAGS, .unit = "keepflags" },
+        { "alpha",          "keep color alpha tags (\\alpha, \\<n>a)", .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_ALPHA          },  .flags=FLAGS, .unit = "keepflags" },
+        { "font_name",      "keep font name tags (\\fn)",              .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_NAME      },  .flags=FLAGS, .unit = "keepflags" },
+        { "font_size",      "keep font size tags (\\fs)",              .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_SIZE      },  .flags=FLAGS, .unit = "keepflags" },
+        { "font_scale",     "keep font scale tags (\\fscx, \\fscy)",   .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_SCALE     },  .flags=FLAGS, .unit = "keepflags" },
+        { "font_spacing",   "keep font spacing tags (\\fsp)",          .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_SPACING   },  .flags=FLAGS, .unit = "keepflags" },
+        { "font_charset",   "keep font charset tags (\\fe)",           .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_CHARSET   },  .flags=FLAGS, .unit = "keepflags" },
+        { "font_bold",      "keep font bold tags (\\b)",               .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_BOLD      },  .flags=FLAGS, .unit = "keepflags" },
+        { "font_italic",    "keep font italic tags (\\i)",             .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_ITALIC    },  .flags=FLAGS, .unit = "keepflags" },
+        { "font_underline", "keep font underline tags (\\u)",          .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_UNDERLINE },  .flags=FLAGS, .unit = "keepflags" },
+        { "font_strikeout", "keep font strikeout tags (\\s)",          .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_STRIKEOUT },  .flags=FLAGS, .unit = "keepflags" },
+        { "text_border",    "keep text border tags (\\bord)",          .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_TEXT_BORDER    },  .flags=FLAGS, .unit = "keepflags" },
+        { "text_shadow",    "keep text shadow tags (\\shad)",          .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_TEXT_SHADOW    },  .flags=FLAGS, .unit = "keepflags" },
+        { "text_rotate",    "keep text rotate tags (\\fr)",            .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_TEXT_ROTATE    },  .flags=FLAGS, .unit = "keepflags" },
+        { "text_blur",      "keep text blur tags (\\blur, \\be)",      .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_TEXT_BLUR      },  .flags=FLAGS, .unit = "keepflags" },
+        { "text_wrap",      "keep text wrap tags (\\q)",               .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_TEXT_WRAP      },  .flags=FLAGS, .unit = "keepflags" },
+        { "text_align",     "keep text align tags (\\a, \\an)",        .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_TEXT_ALIGNMENT },  .flags=FLAGS, .unit = "keepflags" },
+        { "reset_override", "keep override reset tags (\\r)",          .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_CANCELLING     },  .flags=FLAGS, .unit = "keepflags" },
+        { "move",           "keep move tags (\\move)",                 .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_MOVE           },  .flags=FLAGS, .unit = "keepflags" },
+        { "pos",            "keep position tags (\\pos)",              .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_POS            },  .flags=FLAGS, .unit = "keepflags" },
+        { "origin",         "keep origin tags (\\org)",                .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_ORIGIN         },  .flags=FLAGS, .unit = "keepflags" },
+        { "draw",           "keep drawing tags (\\p)",                 .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_DRAW           },  .flags=FLAGS, .unit = "keepflags" },
+        { "animate",        "keep animation tags (\\t)",               .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_ANIMATE        },  .flags=FLAGS, .unit = "keepflags" },
+        { "fade",           "keep fade tags (\\fad, \\fade)",          .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FADE           },  .flags=FLAGS, .unit = "keepflags" },
+        { "clip",           "keep clip tags (\\clip)",                 .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_CLIP           },  .flags=FLAGS, .unit = "keepflags" },
+        { "unknown",        "keep unknown tags",                       .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_UNKNOWN        },  .flags=FLAGS, .unit = "keepflags" },
+    { "remove_animated", "remove animated text (default: yes)",   OFFSET(remove_animated),  AV_OPT_TYPE_BOOL, {.i64 = 1 },  0, 1, FLAGS, 0 },
+    { "select_layer", "process a specific ass layer only",   OFFSET(select_layer),  AV_OPT_TYPE_INT, {.i64 = -1 }, -1, INT_MAX, 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,
+        .config_props  = config_output,
+    },
+};
+
+const AVFilter ff_sf_stripstyles = {
+    .name          = "stripstyles",
+    .description   = NULL_IF_CONFIG_SMALL("Strip subtitle inline styles"),
+    .priv_size     = sizeof(StripStylesContext),
+    .priv_class    = &stripstyles_class,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_SINGLE_SUBFMT(AV_SUBTITLE_FMT_ASS),
+};
+
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v6 17/25] avfilter/splitcc: Add splitcc filter for closed caption handling
  2022-06-26 16:34         ` [FFmpeg-devel] [PATCH v6 00/25] Subtitle Filtering 2022 ffmpegagent
                             ` (15 preceding siblings ...)
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 16/25] avfilter/stripstyles: Add stripstyles filter softworkz
@ 2022-06-26 16:35           ` softworkz
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 18/25] avfilter/graphicsub2text: Add new graphicsub2text filter (OCR) softworkz
                             ` (7 subsequent siblings)
  24 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-26 16:35 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

- splitcc {V -> VS)
  Extract closed-caption (A53) data from video
  frames as subtitle Frames

ffmpeg -y -loglevel verbose -i "https://streams.videolan.org/streams
/ts/CC/NewsStream-608-ac3.ts" -filter_complex "[0:v]splitcc[vid1],
textmod=mode=remove_chars:find='@',[vid1]overlay_textsubs" output.mkv

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 configure                |   1 +
 doc/filters.texi         |  63 +++++++
 libavfilter/Makefile     |   1 +
 libavfilter/allfilters.c |   1 +
 libavfilter/sf_splitcc.c | 395 +++++++++++++++++++++++++++++++++++++++
 5 files changed, 461 insertions(+)
 create mode 100644 libavfilter/sf_splitcc.c

diff --git a/configure b/configure
index 99b4d7c8d7..bde13f3e09 100755
--- a/configure
+++ b/configure
@@ -3728,6 +3728,7 @@ spp_filter_select="fft idctdsp fdctdsp me_cmp pixblockdsp"
 sr_filter_deps="avformat swscale"
 sr_filter_select="dnn"
 stereo3d_filter_deps="gpl"
+splitcc_filter_deps="avcodec"
 subtitles_filter_deps="avformat avcodec libass"
 super2xsai_filter_deps="gpl"
 pixfmts_super2xsai_test_deps="super2xsai_filter"
diff --git a/doc/filters.texi b/doc/filters.texi
index 1158df64d2..b4bbbace7f 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -27408,6 +27408,69 @@ ffmpeg -i INPUT -filter_complex "showspeaker=format=colon:style='@{\\c&HDD0000&\
 @end example
 @end itemize
 
+
+@section splitcc
+
+Split-out closed-caption/A53 subtitles from video frame side data.
+
+This filter provides an input and an output for video frames, which are just passed through without modification.
+The second out provides subtitle frames which are extracted from video frame side data.
+
+Inputs:
+@itemize
+@item 0: Video [ALL]
+@end itemize
+
+Outputs:
+@itemize
+@item 0: Video (same as input)
+@item 1: Subtitles [TEXT]
+@end itemize
+
+It accepts the following parameters:
+
+@table @option
+
+@item use_cc_styles
+Emit closed caption style header.
+This will make closed captions appear in white font with a black rectangle background.
+
+@item real_time
+Emit subtitle events as they are decoded for real-time display.
+
+@item real_time_latency_msec
+Minimum elapsed time between emitting real-time subtitle events.
+Only applies to real_time mode.
+
+@item data_field
+Select data field. Possible values:
+
+@table @samp
+@item auto
+Pick first one that appears.
+@item first
+@item second
+@end table
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Extract closed captions as text subtitle stream and overlay it onto the video in cc style (black bar background):
+@example
+ffmpeg -i "https://streams.videolan.org/streams/ts/CC/NewsStream-608-ac3.ts" -filter_complex  "[0:v:0]splitcc=use_cc_styles=1[vid1][sub1];[vid1][sub1]overlaytextsubs" output.mkv
+@end example
+
+@item
+A nicer variant, using realtime output from cc_dec and rendering it with the render_latest_only parameter from overlaytextsubs to avoid ghosting by timely overlap.
+@example
+ffmpeg -i "https://streams.videolan.org/streams/ts/CC/NewsStream-608-ac3.ts" -filter_complex  "[0:v:0]splitcc=real_time=1:real_time_latency_msec=200[vid1][sub1];[vid1][sub1]overlaytextsubs=render_latest_only=1" output.mkv
+@end example
+@end itemize
+
+
 @section textsub2video
 
 Converts text subtitles to video frames.
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index a99a0f6583..958da451ea 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -580,6 +580,7 @@ OBJS-$(CONFIG_NULLSINK_FILTER)               += vsink_nullsink.o
 # subtitle filters
 OBJS-$(CONFIG_CENSOR_FILTER)                 += sf_textmod.o
 OBJS-$(CONFIG_SHOW_SPEAKER_FILTER)           += sf_textmod.o
+OBJS-$(CONFIG_SPLITCC_FILTER)                += sf_splitcc.o
 OBJS-$(CONFIG_STRIPSTYLES_FILTER)            += sf_stripstyles.o
 OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
 
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 7a958b51d4..3aa1e5ebc0 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -572,6 +572,7 @@ extern const AVFilter ff_avsrc_movie;
 /* subtitle filters */
 extern const AVFilter ff_sf_censor;
 extern const AVFilter ff_sf_showspeaker;
+extern const AVFilter ff_sf_splitcc;
 extern const AVFilter ff_sf_stripstyles;
 extern const AVFilter ff_sf_textmod;
 
diff --git a/libavfilter/sf_splitcc.c b/libavfilter/sf_splitcc.c
new file mode 100644
index 0000000000..14235e822c
--- /dev/null
+++ b/libavfilter/sf_splitcc.c
@@ -0,0 +1,395 @@
+/*
+ * 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
+ * subtitle filter for splitting out closed-caption/A53 subtitles from video frame side data
+ */
+
+#include "filters.h"
+#include "libavutil/opt.h"
+#include "subtitles.h"
+#include "libavcodec/avcodec.h"
+
+static const AVRational ms_tb = {1, 1000};
+
+typedef struct SplitCaptionsContext {
+    const AVClass *class;
+    enum AVSubtitleType format;
+    AVCodecContext *cc_dec;
+    int eof;
+    AVFrame *next_sub_frame;
+    AVFrame *empty_sub_frame;
+    int new_frame;
+    int64_t next_repetition_pts;
+    int had_keyframe;
+    AVBufferRef *subtitle_header;
+    int use_cc_styles;
+    int real_time;
+    int real_time_latency_msec;
+    int data_field;
+    int scatter_realtime_output;
+} SplitCaptionsContext;
+
+static int64_t ms_to_avtb(int64_t ms)
+{
+    return av_rescale_q(ms, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q);
+}
+
+static int init(AVFilterContext *ctx)
+{
+    SplitCaptionsContext *s = ctx->priv;
+    AVDictionary *options = NULL;
+
+    int ret;
+    const AVCodec *codec = avcodec_find_decoder(AV_CODEC_ID_EIA_608);
+    if (!codec) {
+        av_log(ctx, AV_LOG_ERROR, "failed to find EIA-608/708 decoder\n");
+        return AVERROR_DECODER_NOT_FOUND;
+    }
+
+    if (!((s->cc_dec = avcodec_alloc_context3(codec)))) {
+        av_log(ctx, AV_LOG_ERROR, "failed to allocate EIA-608/708 decoder\n");
+        return AVERROR(ENOMEM);
+    }
+
+    av_dict_set_int(&options, "real_time", s->real_time, 0);
+    av_dict_set_int(&options, "real_time_latency_msec", s->real_time_latency_msec, 0);
+    av_dict_set_int(&options, "data_field", s->data_field, 0);
+
+    if ((ret = avcodec_open2(s->cc_dec, codec, &options)) < 0) {
+        av_log(ctx, AV_LOG_ERROR, "failed to open EIA-608/708 decoder: %i\n", ret);
+        return ret;
+    }
+
+    if (s->use_cc_styles && s->cc_dec->subtitle_header && s->cc_dec->subtitle_header[0] != 0) {
+        char* subtitle_header =  av_strdup((char *)s->cc_dec->subtitle_header);
+        if (!subtitle_header)
+            return AVERROR(ENOMEM);
+        s->subtitle_header = av_buffer_create((uint8_t *)subtitle_header, strlen(subtitle_header) + 1, NULL, NULL, 0);
+        if (!s->subtitle_header) {
+            av_free(subtitle_header);
+            return AVERROR(ENOMEM);
+        }
+    }
+
+    return 0;
+}
+
+static void uninit(AVFilterContext *ctx)
+{
+    SplitCaptionsContext *s = ctx->priv;
+    av_frame_free(&s->next_sub_frame);
+    av_frame_free(&s->empty_sub_frame);
+    av_buffer_unref(&s->subtitle_header);
+}
+
+static int config_input(AVFilterLink *link)
+{
+    const SplitCaptionsContext *context = link->dst->priv;
+
+    if (context->cc_dec)
+        context->cc_dec->pkt_timebase = link->time_base;
+
+    return 0;
+}
+
+static int query_formats(AVFilterContext *ctx)
+{
+    AVFilterFormats *formats;
+    AVFilterLink *inlink0 = ctx->inputs[0];
+    AVFilterLink *outlink0 = ctx->outputs[0];
+    AVFilterLink *outlink1 = ctx->outputs[1];
+    static const enum AVSubtitleType subtitle_fmts[] = { AV_SUBTITLE_FMT_ASS, AV_SUBTITLE_FMT_NONE };
+    int ret;
+
+    /* set input0 video formats */
+    formats = ff_all_formats(AVMEDIA_TYPE_VIDEO);
+    if ((ret = ff_formats_ref(formats, &inlink0->outcfg.formats)) < 0)
+        return ret;
+
+    /* set output0 video formats */
+    if ((ret = ff_formats_ref(formats, &outlink0->incfg.formats)) < 0)
+        return ret;
+
+    /* set output1 subtitle formats */
+    formats = ff_make_format_list(subtitle_fmts);
+    if ((ret = ff_formats_ref(formats, &outlink1->incfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int config_video_output(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    const AVFilterLink *inlink = outlink->src->inputs[0];
+
+    outlink->w = ctx->inputs[0]->w;
+    outlink->h = ctx->inputs[0]->h;
+    outlink->frame_rate = ctx->inputs[0]->frame_rate;
+    outlink->time_base = ctx->inputs[0]->time_base;
+    outlink->sample_aspect_ratio = ctx->inputs[0]->sample_aspect_ratio;
+
+    if (inlink->hw_frames_ctx)
+        outlink->hw_frames_ctx = av_buffer_ref(inlink->hw_frames_ctx);
+
+    return 0;
+}
+
+static int config_sub_output(AVFilterLink *outlink)
+{
+    SplitCaptionsContext *s = outlink->src->priv;
+    const AVFilterLink *inlink = outlink->src->inputs[0];
+
+    outlink->time_base = inlink->time_base;
+    outlink->format = AV_SUBTITLE_FMT_ASS;
+    outlink->frame_rate = (AVRational){1000, s->real_time_latency_msec};
+
+    return 0;
+}
+
+static int request_sub_frame(AVFilterLink *outlink)
+{
+    SplitCaptionsContext *s = outlink->src->priv;
+    int status;
+    int64_t pts;
+
+    if (!s->empty_sub_frame) {
+        s->empty_sub_frame = ff_get_subtitles_buffer(outlink, outlink->format);
+        if (!s->empty_sub_frame)
+            return AVERROR(ENOMEM);
+    }
+
+    if (!s->eof && ff_inlink_acknowledge_status(outlink->src->inputs[0], &status, &pts)) {
+        if (status == AVERROR_EOF)
+            s->eof = 1;
+    }
+
+    if (s->eof)
+        return AVERROR_EOF;
+
+    if (s->next_sub_frame) {
+
+        AVFrame *out = NULL;
+        s->next_sub_frame->pts++;
+
+        if (s->new_frame)
+            out = av_frame_clone(s->next_sub_frame);
+        else if (s->empty_sub_frame) {
+            s->empty_sub_frame->pts = s->next_sub_frame->pts;
+            out = av_frame_clone(s->empty_sub_frame);
+            av_frame_copy_props(out, s->next_sub_frame);
+            out->repeat_sub = 1;
+        }
+
+        if (!out)
+            return AVERROR(ENOMEM);
+
+        out->subtitle_timing.start_pts = av_rescale_q(s->next_sub_frame->pts, outlink->time_base, AV_TIME_BASE_Q);
+        s->new_frame = 0;
+
+        return ff_filter_frame(outlink, out);
+    }
+
+    return 0;
+}
+
+static int decode(AVCodecContext *avctx, AVFrame *frame, int *got_frame, AVPacket *pkt)
+{
+    int ret;
+
+    *got_frame = 0;
+
+    if (pkt) {
+        ret = avcodec_send_packet(avctx, pkt);
+        // In particular, we don't expect AVERROR(EAGAIN), because we read all
+        // decoded frames with avcodec_receive_frame() until done.
+        if (ret < 0 && ret != AVERROR_EOF)
+            return ret;
+    }
+
+    ret = avcodec_receive_frame(avctx, frame);
+    if (ret < 0 && ret != AVERROR(EAGAIN))
+        return ret;
+    if (ret >= 0)
+        *got_frame = 1;
+
+    return 0;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFrameSideData *sd;
+    SplitCaptionsContext *s = inlink->dst->priv;
+    AVFilterLink *outlink0 = inlink->dst->outputs[0];
+    AVFilterLink *outlink1 = inlink->dst->outputs[1];
+    AVPacket *pkt = NULL;
+    AVFrame *sub_out = NULL;
+
+    int ret;
+
+    outlink0->format = inlink->format;
+
+    sd = av_frame_get_side_data(frame, AV_FRAME_DATA_A53_CC);
+
+    if (sd && (s->had_keyframe || frame->key_frame)) {
+        int got_output = 0;
+
+        s->had_keyframe = 1;
+        pkt = av_packet_alloc();
+        pkt->buf = av_buffer_ref(sd->buf);
+        if (!pkt->buf) {
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
+
+        pkt->data = pkt->buf->data;
+        pkt->size = pkt->buf->size;
+        pkt->pts  = av_rescale_q(frame->pts, inlink->time_base, AV_TIME_BASE_Q);
+
+        sub_out = ff_get_subtitles_buffer(outlink1, AV_SUBTITLE_FMT_ASS);
+        if (!sub_out) {
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
+
+        if ((ret = av_buffer_replace(&sub_out->subtitle_header, s->subtitle_header)) < 0)
+            goto fail;
+
+        ret = decode(s->cc_dec, sub_out, &got_output, pkt);
+
+        ////if (got_output) {
+        ////    av_log(inlink->dst, AV_LOG_INFO, "CC Packet PTS: %"PRId64" got_output: %d  out_frame_pts: %"PRId64"  out_sub_pts: %"PRId64"\n", pkt->pts, got_output, sub_out->pts, sub_out->subtitle_pts);
+        ////}
+
+        av_packet_free(&pkt);
+
+        if (ret < 0) {
+            av_log(inlink->dst, AV_LOG_ERROR, "Decode error: %d \n", ret);
+            goto fail;
+        }
+
+        if (got_output) {
+            sub_out->pts = frame->pts;
+            av_frame_free(&s->next_sub_frame);
+            s->next_sub_frame = sub_out;
+            sub_out = NULL;
+            s->new_frame = 1;
+            s->next_sub_frame->pts = frame->pts;
+
+            if ((ret = av_buffer_replace(&s->next_sub_frame->subtitle_header, s->subtitle_header)) < 0)
+                goto fail;
+
+            if (s->real_time && s->scatter_realtime_output) {
+                if (s->next_repetition_pts)
+                    s->next_sub_frame->pts = s->next_repetition_pts;
+
+                s->next_sub_frame->subtitle_timing.duration = ms_to_avtb(s->real_time_latency_msec);
+                s->next_repetition_pts = s->next_sub_frame->pts + av_rescale_q(s->real_time_latency_msec, ms_tb, inlink->time_base);
+            }
+
+            ret = request_sub_frame(outlink1);
+            if (ret < 0)
+                goto fail;
+        }
+    }
+
+    if (s->real_time && s->scatter_realtime_output && !s->new_frame && s->next_repetition_pts > 0 && frame->pts > s->next_repetition_pts) {
+        s->new_frame = 1;
+        s->next_sub_frame->pts = s->next_repetition_pts;
+        s->next_repetition_pts = s->next_sub_frame->pts + av_rescale_q(s->real_time_latency_msec, ms_tb, inlink->time_base);
+    }
+
+    if (!s->next_sub_frame) {
+        s->next_sub_frame = ff_get_subtitles_buffer(outlink1, outlink1->format);
+        if (!s->next_sub_frame) {
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
+
+        s->next_sub_frame->subtitle_timing.duration = ms_to_avtb(s->real_time_latency_msec);
+        s->next_sub_frame->pts = frame->pts;
+        s->new_frame = 1;
+
+        if ((ret = av_buffer_replace(&s->next_sub_frame->subtitle_header, s->subtitle_header)) < 0)
+            goto fail;
+    }
+
+    ret = ff_filter_frame(outlink0, frame);
+
+fail:
+    av_packet_free(&pkt);
+    av_frame_free(&sub_out);
+    return ret;
+}
+
+#define OFFSET(x) offsetof(SplitCaptionsContext, x)
+#define FLAGS (AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption split_cc_options[] = {
+    { "use_cc_styles",    "Emit closed caption style header", OFFSET(use_cc_styles),  AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, NULL },
+    { "real_time", "emit subtitle events as they are decoded for real-time display", OFFSET(real_time), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS },
+    { "real_time_latency_msec", "minimum elapsed time between emitting real-time subtitle events", OFFSET(real_time_latency_msec), AV_OPT_TYPE_INT, { .i64 = 200 }, 0, 500, FLAGS },
+    { "scatter_realtime_output", "scatter output events to a duration of real_time_latency_msec", OFFSET(scatter_realtime_output), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS },
+    { "data_field", "select data field", OFFSET(data_field), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1, FLAGS, "data_field" },
+    { "auto",   "pick first one that appears", 0, AV_OPT_TYPE_CONST, { .i64 =-1 }, 0, 0, FLAGS, "data_field" },
+    { "first",  NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, FLAGS, "data_field" },
+    { "second", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, FLAGS, "data_field" },
+    { NULL },
+};
+
+AVFILTER_DEFINE_CLASS(split_cc);
+
+static const AVFilterPad inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_VIDEO,
+        .filter_frame = filter_frame,
+        .config_props = config_input,
+    },
+};
+
+static const AVFilterPad outputs[] = {
+    {
+        .name          = "video_passthrough",
+        .type          = AVMEDIA_TYPE_VIDEO,
+        .config_props  = config_video_output,
+    },
+    {
+        .name          = "subtitles",
+        .type          = AVMEDIA_TYPE_SUBTITLE,
+        .request_frame = request_sub_frame,
+        .config_props  = config_sub_output,
+    },
+};
+
+const AVFilter ff_sf_splitcc = {
+    .name           = "splitcc",
+    .description    = NULL_IF_CONFIG_SMALL("Extract closed-caption (A53) data from video as subtitle stream."),
+    .init           = init,
+    .uninit         = uninit,
+    .priv_size      = sizeof(SplitCaptionsContext),
+    .priv_class     = &split_cc_class,
+    .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_QUERY_FUNC(query_formats),
+};
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v6 18/25] avfilter/graphicsub2text: Add new graphicsub2text filter (OCR)
  2022-06-26 16:34         ` [FFmpeg-devel] [PATCH v6 00/25] Subtitle Filtering 2022 ffmpegagent
                             ` (16 preceding siblings ...)
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 17/25] avfilter/splitcc: Add splitcc filter for closed caption handling softworkz
@ 2022-06-26 16:35           ` softworkz
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 19/25] avfilter/subscale: Add filter for scaling and/or re-arranging graphical subtitles softworkz
                             ` (6 subsequent siblings)
  24 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-26 16:35 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 configure                        |    1 +
 doc/filters.texi                 |   55 ++
 libavfilter/Makefile             |    1 +
 libavfilter/allfilters.c         |    1 +
 libavfilter/sf_graphicsub2text.c | 1137 ++++++++++++++++++++++++++++++
 5 files changed, 1195 insertions(+)
 create mode 100644 libavfilter/sf_graphicsub2text.c

diff --git a/configure b/configure
index bde13f3e09..462d473c5f 100755
--- a/configure
+++ b/configure
@@ -3663,6 +3663,7 @@ frei0r_filter_deps="frei0r"
 frei0r_src_filter_deps="frei0r"
 fspp_filter_deps="gpl"
 gblur_vulkan_filter_deps="vulkan spirv_compiler"
+graphicsub2text_filter_deps="libtesseract"
 hflip_vulkan_filter_deps="vulkan spirv_compiler"
 histeq_filter_deps="gpl"
 hqdn3d_filter_deps="gpl"
diff --git a/doc/filters.texi b/doc/filters.texi
index b4bbbace7f..4bbec62c02 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -27174,6 +27174,61 @@ ffmpeg -i "https://streams.videolan.org/ffmpeg/mkv_subtitles.mkv" -filter_comple
 @end example
 @end itemize
 
+@section graphicsub2text
+
+Converts graphic subtitles to text subtitles by performing OCR.
+
+For this filter to be available, ffmpeg needs to be compiled with libtesseract (see https://github.com/tesseract-ocr/tesseract).
+Language models need to be downloaded from https://github.com/tesseract-ocr/tessdata and put into as subfolder named 'tessdata' or into a folder specified via the environment variable 'TESSDATA_PREFIX'.
+The path can also be specified via filter option (see below).
+
+Note: These models are including the data for both OCR modes.
+
+Inputs:
+- 0: Subtitles [bitmap]
+
+Outputs:
+- 0: Subtitles [text]
+
+It accepts the following parameters:
+
+@table @option
+@item ocr_mode
+The character recognition mode to use.
+
+Supported OCR modes are:
+
+@table @var
+@item 0, tesseract
+This is the classic libtesseract operation mode. It is fast but less accurate than LSTM.
+@item 1, lstm
+Newer OCR implementation based on ML models. Provides usually better results, requires more processing resources.
+@item 2, both
+Use a combination of both modes.
+@end table
+
+@item tessdata_path
+The path to a folder containing the language models to be used.
+
+@item language
+The recognition language. It needs to match the first three characters of a  language model file in the tessdata path.
+
+@end table
+
+
+@subsection Examples
+
+@itemize
+@item
+Convert DVB graphic subtitles to ASS (text) subtitles
+
+Note: For this to work, you need to have the data file 'eng.traineddata' in a 'tessdata' subfolder (see above).
+@example
+ffmpeg -loglevel verbose -i "https://streams.videolan.org/streams/ts/video_subs_ttxt%2Bdvbsub.ts" -filter_complex "[0:13]graphicsub2text=delay_when_no_duration=1" -c:s ass -y output.mkv
+@end example
+@end itemize
+
+
 @section graphicsub2video
 
 Renders graphic subtitles as video frames.
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 958da451ea..6e6485c99a 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -307,6 +307,7 @@ OBJS-$(CONFIG_GBLUR_FILTER)                  += vf_gblur.o
 OBJS-$(CONFIG_GBLUR_VULKAN_FILTER)           += vf_gblur_vulkan.o vulkan.o vulkan_filter.o
 OBJS-$(CONFIG_GEQ_FILTER)                    += vf_geq.o
 OBJS-$(CONFIG_GRADFUN_FILTER)                += vf_gradfun.o
+OBJS-$(CONFIG_GRAPHICSUB2TEXT_FILTER)        += sf_graphicsub2text.o
 OBJS-$(CONFIG_GRAPHICSUB2VIDEO_FILTER)       += vf_overlaygraphicsubs.o framesync.o
 OBJS-$(CONFIG_GRAPHMONITOR_FILTER)           += f_graphmonitor.o
 OBJS-$(CONFIG_GRAYWORLD_FILTER)              += vf_grayworld.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 3aa1e5ebc0..cbd93c4236 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -571,6 +571,7 @@ extern const AVFilter ff_avsrc_movie;
 
 /* subtitle filters */
 extern const AVFilter ff_sf_censor;
+extern const AVFilter ff_sf_graphicsub2text;
 extern const AVFilter ff_sf_showspeaker;
 extern const AVFilter ff_sf_splitcc;
 extern const AVFilter ff_sf_stripstyles;
diff --git a/libavfilter/sf_graphicsub2text.c b/libavfilter/sf_graphicsub2text.c
new file mode 100644
index 0000000000..47c7030939
--- /dev/null
+++ b/libavfilter/sf_graphicsub2text.c
@@ -0,0 +1,1137 @@
+/*
+ * 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
+ * subtitle filter to convert graphical subs to text subs via OCR
+ */
+
+#include <tesseract/capi.h>
+#include <libavutil/ass_internal.h>
+
+#include "drawutils.h"
+#include "libavutil/opt.h"
+#include "subtitles.h"
+
+#include "libavcodec/elbg.h"
+
+enum {
+    RFLAGS_NONE         = 0,
+    RFLAGS_HALIGN       = 1 << 0,
+    RFLAGS_VALIGN       = 1 << 1,
+    RFLAGS_FBOLD        = 1 << 2,
+    RFLAGS_FITALIC      = 1 << 3,
+    RFLAGS_FUNDERLINE   = 1 << 4,
+    RFLAGS_FONT         = 1 << 5,
+    RFLAGS_FONTSIZE     = 1 << 6,
+    RFLAGS_COLOR        = 1 << 7,
+    RFLAGS_OUTLINECOLOR = 1 << 8,
+    RFLAGS_ALL = RFLAGS_HALIGN | RFLAGS_VALIGN | RFLAGS_FBOLD | RFLAGS_FITALIC | RFLAGS_FUNDERLINE |
+                RFLAGS_FONT | RFLAGS_FONTSIZE | RFLAGS_COLOR | RFLAGS_OUTLINECOLOR,
+};
+
+typedef struct SubOcrContext {
+    const AVClass *class;
+    int w, h;
+
+    TessBaseAPI *tapi;
+    TessOcrEngineMode ocr_mode;
+    char *tessdata_path;
+    char *language;
+    int preprocess_images;
+    int dump_bitmaps;
+    int delay_when_no_duration;
+    int recognize;
+    double font_size_factor;
+
+    int readorder_counter;
+
+    AVFrame *pending_frame;
+    AVBufferRef *subtitle_header;
+    AVBPrint buffer;
+
+    // Color Quantization Fields
+    struct ELBGContext *ctx;
+    AVLFG lfg;
+    int *codeword;
+    int *codeword_closest_codebook_idxs;
+    int *codebook;
+    int r_idx, g_idx, b_idx, a_idx;
+    int64_t last_subtitle_pts;
+} SubOcrContext;
+
+typedef struct OcrImageProps {
+    int background_color_index;
+    int fill_color_index;
+
+} OcrImageProps;
+
+static int64_t ms_to_avtb(int64_t ms)
+{
+    return av_rescale_q(ms, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q);
+}
+
+static int create_ass_header(AVFilterContext* ctx)
+{
+    SubOcrContext* s = ctx->priv;
+
+    if (!(s->w && s->h)) {
+        av_log(ctx, AV_LOG_WARNING, "create_ass_header: no width and height specified!\n");
+        s->w = ASS_DEFAULT_PLAYRESX;
+        s->h = ASS_DEFAULT_PLAYRESY;
+    }
+
+    char* subtitle_header_text = avpriv_ass_get_subtitle_header_full(s->w, s->h, ASS_DEFAULT_FONT, ASS_DEFAULT_FONT_SIZE,
+        ASS_DEFAULT_COLOR, ASS_DEFAULT_COLOR, ASS_DEFAULT_BACK_COLOR, ASS_DEFAULT_BACK_COLOR, ASS_DEFAULT_BOLD,
+        ASS_DEFAULT_ITALIC, ASS_DEFAULT_UNDERLINE, ASS_DEFAULT_BORDERSTYLE, ASS_DEFAULT_ALIGNMENT, 0);
+
+    if (!subtitle_header_text)
+        return AVERROR(ENOMEM);
+
+    s->subtitle_header = av_buffer_create((uint8_t*)subtitle_header_text, strlen(subtitle_header_text) + 1, NULL, NULL, 0);
+
+    if (!s->subtitle_header)
+        return AVERROR(ENOMEM);
+
+    return 0;
+}
+
+static int init(AVFilterContext *ctx)
+{
+    SubOcrContext *s = ctx->priv;
+    const char* tver = TessVersion();
+    uint8_t rgba_map[4];
+    int ret;
+
+    s->tapi = TessBaseAPICreate();
+
+    if (!s->tapi || !tver || !strlen(tver)) {
+        av_log(ctx, AV_LOG_ERROR, "Failed to access libtesseract\n");
+        return AVERROR(ENOSYS);
+    }
+
+    av_log(ctx, AV_LOG_VERBOSE, "Initializing libtesseract, version: %s\n", tver);
+
+    ret = TessBaseAPIInit4(s->tapi, s->tessdata_path, s->language, s->ocr_mode, NULL, 0, NULL, NULL, 0, 1);
+    if (ret < 0 ) {
+        av_log(ctx, AV_LOG_ERROR, "Failed to initialize libtesseract. Error: %d\n", ret);
+        return AVERROR(ENOSYS);
+    }
+
+    ret = TessBaseAPISetVariable(s->tapi, "tessedit_char_blacklist", "|");
+    if (ret < 0 ) {
+        av_log(ctx, AV_LOG_ERROR, "Failed to set 'tessedit_char_blacklist'. Error: %d\n", ret);
+        return AVERROR(EINVAL);
+    }
+
+    av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
+
+    ff_fill_rgba_map(&rgba_map[0], AV_PIX_FMT_RGB32);
+
+    s->r_idx = rgba_map[0]; // R
+    s->g_idx = rgba_map[1]; // G
+    s->b_idx = rgba_map[2]; // B
+    s->a_idx = rgba_map[3]; // A
+
+    av_lfg_init(&s->lfg, 123456789);
+
+    return 0;
+}
+
+static void uninit(AVFilterContext *ctx)
+{
+    SubOcrContext *s = ctx->priv;
+
+    av_buffer_unref(&s->subtitle_header);
+    av_bprint_finalize(&s->buffer, NULL);
+
+    if (s->tapi) {
+        TessBaseAPIEnd(s->tapi);
+        TessBaseAPIDelete(s->tapi);
+    }
+
+    avpriv_elbg_free(&s->ctx);
+}
+
+static int query_formats(AVFilterContext *ctx)
+{
+    AVFilterFormats *formats, *formats2;
+    AVFilterLink *inlink = ctx->inputs[0];
+    AVFilterLink *outlink = ctx->outputs[0];
+    static const enum AVSubtitleType in_fmts[] = { AV_SUBTITLE_FMT_BITMAP, AV_SUBTITLE_FMT_NONE };
+    static const enum AVSubtitleType out_fmts[] = { AV_SUBTITLE_FMT_ASS, AV_SUBTITLE_FMT_NONE };
+    int ret;
+
+    /* set input format */
+    formats = ff_make_format_list(in_fmts);
+    if ((ret = ff_formats_ref(formats, &inlink->outcfg.formats)) < 0)
+        return ret;
+
+    /* set output format */
+    formats2 = ff_make_format_list(out_fmts);
+    if ((ret = ff_formats_ref(formats2, &outlink->incfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int config_input(AVFilterLink *inlink)
+{
+    AVFilterContext *ctx = inlink->dst;
+    SubOcrContext *s = ctx->priv;
+
+    if (s->w <= 0 || s->h <= 0) {
+        s->w = inlink->w;
+        s->h = inlink->h;
+    }
+
+    return create_ass_header(ctx);
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    AVFilterLink *inlink = outlink->src->inputs[0];
+    const AVFilterContext *ctx  = outlink->src;
+    SubOcrContext *s = ctx->priv;
+
+    outlink->format = AV_SUBTITLE_FMT_ASS;
+    outlink->w = s->w;
+    outlink->h = s->h;
+    outlink->time_base = inlink->time_base;
+    outlink->frame_rate = inlink->frame_rate;
+
+    return 0;
+}
+
+static void free_subtitle_area(AVSubtitleArea *area)
+{
+    for (unsigned n = 0; n < FF_ARRAY_ELEMS(area->buf); n++)
+        av_buffer_unref(&area->buf[n]);
+
+    av_freep(&area->text);
+    av_freep(&area->ass);
+    av_free(area);
+
+}
+
+static AVSubtitleArea *copy_subtitle_area(const AVSubtitleArea *src)
+{
+    AVSubtitleArea *dst = av_mallocz(sizeof(AVSubtitleArea));
+
+    if (!dst)
+        return NULL;
+
+    dst->x         =  src->x;
+    dst->y         =  src->y;
+    dst->w         =  src->w;
+    dst->h         =  src->h;
+    dst->nb_colors =  src->nb_colors;
+    dst->type      =  src->type;
+    dst->flags     =  src->flags;
+
+    for (unsigned i = 0; i < AV_NUM_BUFFER_POINTERS; i++) {
+        if (src->h > 0 && src->w > 0 && src->buf[i]) {
+            dst->buf[0] = av_buffer_ref(src->buf[i]);
+            if (!dst->buf[i])
+                return NULL;
+
+            const int ret = av_buffer_make_writable(&dst->buf[i]);
+            if (ret < 0)
+                return NULL;
+
+            dst->linesize[i] = src->linesize[i];
+        }
+    }
+
+    memcpy(&dst->pal[0], &src->pal[0], sizeof(src->pal[0]) * 256);
+
+
+    return dst;
+}
+
+static int quantize_image_colors(SubOcrContext *const s, AVSubtitleArea *subtitle_area)
+{
+    const int num_quantized_colors = 3;
+    int k, ret;
+    const int codeword_length = subtitle_area->w * subtitle_area->h;
+    uint8_t *src_data = subtitle_area->buf[0]->data;
+
+    if (subtitle_area->nb_colors <= num_quantized_colors) {
+        av_log(s, AV_LOG_DEBUG, "No need to quantize colors. Color count: %d\n", subtitle_area->nb_colors);
+        return 0;
+    }
+
+    // Convert palette to grayscale
+    for (int i = 0; i < subtitle_area->nb_colors; i++) {
+        uint8_t *color        = (uint8_t *)&subtitle_area->pal[i];
+        const uint8_t average = (uint8_t)(((int)color[s->r_idx] + color[s->g_idx] + color[s->b_idx]) / 3);
+        color[s->b_idx]       = average;
+        color[s->g_idx]       = average;
+        color[s->r_idx]       = average;
+    }
+
+    /* Re-Initialize */
+    s->codeword = av_realloc_f(s->codeword, codeword_length, 4 * sizeof(*s->codeword));
+    if (!s->codeword)
+        return AVERROR(ENOMEM);
+
+    s->codeword_closest_codebook_idxs = av_realloc_f(s->codeword_closest_codebook_idxs,
+        codeword_length, sizeof(*s->codeword_closest_codebook_idxs));
+    if (!s->codeword_closest_codebook_idxs)
+        return AVERROR(ENOMEM);
+
+    s->codebook = av_realloc_f(s->codebook, num_quantized_colors, 4 * sizeof(*s->codebook));
+    if (!s->codebook)
+        return AVERROR(ENOMEM);
+
+    /* build the codeword */
+    k = 0;
+    for (int i = 0; i < subtitle_area->h; i++) {
+        uint8_t *p = src_data;
+        for (int j = 0; j < subtitle_area->w; j++) {
+            const uint8_t *color = (uint8_t *)&subtitle_area->pal[*p];
+            s->codeword[k++] = color[s->b_idx];
+            s->codeword[k++] = color[s->g_idx];
+            s->codeword[k++] = color[s->r_idx];
+            s->codeword[k++] = color[s->a_idx];
+            p++;
+        }
+        src_data += subtitle_area->linesize[0];
+    }
+
+    /* compute the codebook */
+    ret = avpriv_elbg_do(&s->ctx, s->codeword, 4, codeword_length, s->codebook,
+        num_quantized_colors, 1, s->codeword_closest_codebook_idxs, &s->lfg, 0);
+    if (ret < 0)
+        return ret;
+
+    /* Write Palette */
+    for (int i = 0; i < num_quantized_colors; i++) {
+        subtitle_area->pal[i] = s->codebook[i*4+3] << 24  |
+                    (s->codebook[i*4+2] << 16) |
+                    (s->codebook[i*4+1] <<  8) |
+                    (s->codebook[i*4  ] <<  0);
+    }
+
+
+    av_log(s, AV_LOG_DEBUG, "Quantized colors from %d to %d\n", subtitle_area->nb_colors, num_quantized_colors);
+
+    subtitle_area->nb_colors = num_quantized_colors;
+    src_data = subtitle_area->buf[0]->data;
+
+    /* Write Image */
+    k = 0;
+    for (int i = 0; i < subtitle_area->h; i++) {
+        uint8_t *p = src_data;
+        for (int j = 0; j < subtitle_area->w; j++, p++) {
+            p[0] = (uint8_t)s->codeword_closest_codebook_idxs[k++];
+        }
+
+        src_data += subtitle_area->linesize[0];
+    }
+
+    return ret;
+}
+
+#define MEASURE_LINE_COUNT 6
+
+static uint8_t get_background_color_index(SubOcrContext *const s, const AVSubtitleArea *subtitle_area)
+{
+    const int linesize = subtitle_area->linesize[0];
+    int index_counts[256] = {0};
+    const unsigned int line_offsets[MEASURE_LINE_COUNT] = {
+        0,
+        linesize,
+        2 * linesize,
+        (subtitle_area->h - 3) * linesize,
+        (subtitle_area->h - 2) * linesize,
+        (subtitle_area->h - 1) * linesize
+    };
+
+    const uint8_t *src_data = subtitle_area->buf[0]->data;
+    const uint8_t tl = src_data[0];
+    const uint8_t tr = src_data[subtitle_area->w - 1];
+    const uint8_t bl = src_data[(subtitle_area->h - 1) * linesize + 0];
+    const uint8_t br = src_data[(subtitle_area->h - 1) * linesize + subtitle_area->w - 1];
+    uint8_t max_index = 0;
+    int max_count;
+
+    // When all corner pixels are equal, assume that as background color
+    if (tl == tr == bl == br || subtitle_area->h < 6)
+        return tl;
+
+    for (unsigned int i = 0; i < MEASURE_LINE_COUNT; i++) {
+        uint8_t *p = subtitle_area->buf[0]->data + line_offsets[i];
+        for (int k = 0; k < subtitle_area->w; k++)
+            index_counts[p[k]]++;
+    }
+
+    max_count = index_counts[0];
+
+    for (uint8_t i = 1; i < subtitle_area->nb_colors; i++) {
+        if (index_counts[i] > max_count) {
+            max_count = index_counts[i];
+            max_index = i;
+        }
+    }
+
+    return max_index;
+}
+
+static uint8_t get_text_color_index(SubOcrContext *const s, const AVSubtitleArea *subtitle_area, const uint8_t bg_color_index, uint8_t *outline_color_index)
+{
+    const int linesize = subtitle_area->linesize[0];
+    int index_counts[256] = {0};
+    uint8_t last_index = bg_color_index;
+    int max_count, min_req_count;
+    uint8_t max_index = 0;
+
+    for (int i = 3; i < subtitle_area->h - 3; i += 5) {
+        const uint8_t *p = subtitle_area->buf[0]->data + ((ptrdiff_t)linesize * i);
+        for (int k = 0; k < subtitle_area->w; k++) {
+            const uint8_t cur_index = p[k];
+
+            // When color hasn't changed, continue
+            if (cur_index == last_index)
+                continue;
+
+            if (cur_index != bg_color_index)
+                index_counts[cur_index]++;
+
+            last_index = cur_index;
+        }
+    }
+
+    max_count = index_counts[0];
+
+    for (uint8_t i = 1; i < subtitle_area->nb_colors; i++) {
+        if (index_counts[i] > max_count) {
+            max_count = index_counts[i];
+            max_index = i;
+        }
+    }
+
+    min_req_count = max_count / 3;
+
+    for (uint8_t i = 1; i < subtitle_area->nb_colors; i++) {
+        if (index_counts[i] < min_req_count)
+            index_counts[i] = 0;
+    }
+
+    *outline_color_index = max_index;
+
+    index_counts[max_index] = 0;
+    max_count = 0;
+
+    for (uint8_t i = 0; i < subtitle_area->nb_colors; i++) {
+        if (index_counts[i] > max_count) {
+            max_count = index_counts[i];
+            max_index = i;
+        }
+    }
+
+    if (*outline_color_index == max_index)
+        *outline_color_index = 255;
+
+    return max_index;
+}
+
+static void make_image_binary(SubOcrContext *const s, AVSubtitleArea *subtitle_area, const uint8_t text_color_index)
+{
+    for (int i = 0; i < subtitle_area->nb_colors; i++) {
+
+        if (i != text_color_index)
+            subtitle_area->pal[i] = 0xffffffff;
+        else
+            subtitle_area->pal[i] = 0xff000000;
+    }
+}
+
+static int get_crop_region(SubOcrContext *const s, const AVSubtitleArea *subtitle_area, uint8_t text_color_index, int *x, int *y, int *w, int *h)
+{
+    const int linesize = subtitle_area->linesize[0];
+    int max_y = 0, max_x = 0;
+    int min_y = subtitle_area->h - 1, min_x = subtitle_area->w - 1;
+
+    for (int i = 0; i < subtitle_area->h; i += 3) {
+        const uint8_t *p = subtitle_area->buf[0]->data + ((ptrdiff_t)linesize * i);
+        for (int k = 0; k < subtitle_area->w; k += 2) {
+            if (p[k] == text_color_index) {
+                min_y = FFMIN(min_y, i);
+                min_x = FFMIN(min_x, k);
+                max_y = FFMAX(max_y, i);
+                max_x = FFMAX(max_x, k);
+            }
+        }
+    }
+
+    if (max_y <= min_y || max_x <= min_x) {
+        av_log(s, AV_LOG_WARNING, "Unable to detect crop region\n");
+        *x = 0;
+        *y = 0;
+        *w = subtitle_area->w;
+        *h = subtitle_area->h;
+    }    else {
+        *x = FFMAX(min_x - 10, 0);
+        *y = FFMAX(min_y - 10, 0);
+        *w = FFMIN(max_x + 10 - *x, (subtitle_area->w - *x));
+        *h = FFMIN(max_y + 10 - *y, (subtitle_area->h - *y));
+    }
+
+    return 0;
+}
+
+static int crop_area_bitmap(SubOcrContext *const s, AVSubtitleArea *subtitle_area, int x, int y, int w, int h)
+{
+    const int linesize = subtitle_area->linesize[0];
+    AVBufferRef *dst = av_buffer_allocz(h * w);
+    uint8_t *d;
+
+    if (!dst)
+        return AVERROR(ENOMEM);
+
+    d = dst->data;
+
+    for (int i = y; i < y + h; i++) {
+        const uint8_t *p = subtitle_area->buf[0]->data + ((ptrdiff_t)linesize * i);
+        for (int k = x; k < x + w; k++) {
+            *d = p[k];
+            d++;
+        }
+    }
+
+    subtitle_area->w = w;
+    subtitle_area->h = h;
+    subtitle_area->x += x;
+    subtitle_area->y += y;
+    subtitle_area->linesize[0] = w;
+    av_buffer_replace(&subtitle_area->buf[0], dst);
+
+    av_buffer_unref(&dst);
+    return 0;
+}
+
+#define R 0
+#define G 1
+#define B 2
+#define A 3
+
+static int print_code(AVBPrint *buf, int in_code, const char *fmt, ...)
+{
+    va_list vl;
+
+    if (!in_code)
+        av_bprint_chars(buf, '{', 1);
+
+    va_start(vl, fmt);
+    av_vbprintf(buf, fmt, vl);
+    va_end(vl);
+
+    return 1;
+}
+
+static int end_code(AVBPrint *buf, int in_code)
+{
+    if (in_code)
+        av_bprint_chars(buf, '}', 1);
+    return 0;
+}
+
+static uint8_t* create_grayscale_image(AVFilterContext *ctx, AVSubtitleArea *area, int invert)
+{
+    uint8_t gray_pal[256];
+    const size_t img_size = area->buf[0]->size;
+    const uint8_t* img    = area->buf[0]->data;
+    uint8_t* gs_img       = av_malloc(img_size);
+
+    if (!gs_img)
+        return NULL;
+
+    for (unsigned i = 0; i < 256; i++) {
+        const uint8_t *col = (uint8_t*)&area->pal[i];
+        const int val      = (int)col[3] * FFMAX3(col[0], col[1], col[2]);
+        gray_pal[i]        = (uint8_t)(val >> 8);
+    }
+
+    if (invert)
+        for (unsigned i = 0; i < img_size; i++)
+            gs_img[i]   = 255 - gray_pal[img[i]];
+    else
+        for (unsigned i = 0; i < img_size; i++)
+            gs_img[i]   = gray_pal[img[i]];
+
+    return gs_img;
+}
+
+static uint8_t* create_bitmap_image(AVFilterContext *ctx, AVSubtitleArea *area, const uint8_t text_color_index)
+{
+    const size_t img_size = area->buf[0]->size;
+    const uint8_t* img    = area->buf[0]->data;
+    uint8_t* gs_img       = av_malloc(img_size);
+
+    if (!gs_img)
+        return NULL;
+
+    for (unsigned i = 0; i < img_size; i++) {
+        if (img[i] == text_color_index)
+            gs_img[i]   = 0;
+        else
+            gs_img[i]   = 255;
+    }
+
+    return gs_img;
+}
+
+static void png_save(AVFilterContext *ctx, const char *filename, AVSubtitleArea *area)
+{
+    int x, y;
+    int v;
+    FILE *f;
+    char fname[40];
+    const uint8_t *data = area->buf[0]->data;
+
+    snprintf(fname, sizeof(fname), "%s.ppm", filename);
+
+    f = fopen(fname, "wb");
+    if (!f) {
+        perror(fname);
+        return;
+    }
+    fprintf(f, "P6\n"
+            "%d %d\n"
+            "%d\n",
+            area->w, area->h, 255);
+    for(y = 0; y < area->h; y++) {
+        for(x = 0; x < area->w; x++) {
+            const uint8_t index = data[y * area->linesize[0] + x];
+            v = (int)area->pal[index];
+            putc(v >> 16 & 0xff, f);
+            putc(v >> 8 & 0xff, f);
+            putc(v >> 0 & 0xff, f);
+        }
+    }
+
+    fclose(f);
+}
+
+static int get_max_index(int score[256])
+{
+    int max_val = 0, max_index = 0;
+
+    for (int i = 0; i < 256; i++) {
+        if (score[i] > max_val) {
+            max_val = score[i];
+            max_index = i;
+        }
+    }
+
+    return max_index;
+}
+
+static int get_word_colors(AVFilterContext *ctx, TessResultIterator* ri, const AVSubtitleArea* area, const AVSubtitleArea* original_area,
+                           uint8_t bg_color_index, uint8_t text_color_index, uint8_t outline_color_index,
+                           uint32_t* bg_color, uint32_t* text_color, uint32_t* outline_color)
+{
+    int left = 0, top = 0, right = 0, bottom = 0, ret;
+    int bg_score[256] = {0}, text_score[256] = {0}, outline_score[256] = {0};
+    int max_index;
+
+    ret = TessPageIteratorBoundingBox((TessPageIterator*)ri, RIL_WORD, &left, &top, &right, &bottom);
+    if (ret < 0) {
+        av_log(ctx, AV_LOG_WARNING, "get_word_colors: IteratorBoundingBox failed: %d\n", ret);
+        return  ret;
+    }
+
+    if (left >= area->w || right >= area->w || top >= area->h || bottom >= area->h) {
+        av_log(ctx, AV_LOG_WARNING, "get_word_colors: word bounding box (l: %d, t: %d r: %d, b: %d) out of image bounds (%dx%d)\n", left,top, right, bottom, area->w, area->h);
+        return  AVERROR(EINVAL);
+    }
+
+    for (int y = top; y < bottom; y += 3) {
+        uint8_t *p = area->buf[0]->data + (y * area->linesize[0]) + left;
+        uint8_t *porig = original_area->buf[0]->data + (y * original_area->linesize[0]) + left;
+        uint8_t current_index = 255;
+
+        for (int x = left; x < right; x++, p++, porig++) {
+
+            if (*p == current_index) {
+                if (*p == bg_color_index)
+                    bg_score[*porig]++;
+                if (*p == text_color_index)
+                    text_score[*porig]++;
+                if (*p == outline_color_index)
+                    outline_score[*porig]++;
+            }
+
+            current_index = *p;
+        }
+    }
+
+    max_index = get_max_index(bg_score);
+    if (bg_score[max_index] > 0)
+        *bg_color = original_area->pal[max_index];
+
+    max_index = get_max_index(text_score);
+    if (text_score[max_index] > 0)
+        *text_color = original_area->pal[max_index];
+
+    max_index = get_max_index(outline_score);
+    if (outline_score[max_index] > 0)
+        *outline_color = original_area->pal[max_index];
+
+    return 0;
+}
+
+static int convert_area(AVFilterContext *ctx, AVSubtitleArea *area, const AVFrame *frame, const unsigned area_index, int *margin_v)
+{
+    SubOcrContext *s = ctx->priv;
+    char *ocr_text = NULL;
+    int ret = 0;
+    uint8_t *gs_img;
+    uint8_t bg_color_index;
+    uint8_t text_color_index = 255;
+    uint8_t outline_color_index = 255;
+    char filename[32];
+    AVSubtitleArea *original_area = copy_subtitle_area(area);
+
+    if (!original_area)
+        return AVERROR(ENOMEM);
+
+    if (area->w < 6 || area->h < 6) {
+        area->ass = NULL;
+        goto exit;
+    }
+
+    if (s->dump_bitmaps) {
+        snprintf(filename, sizeof(filename), "graphicsub2text_%"PRId64"_%d_original", frame->subtitle_timing.start_pts, area_index);
+        png_save(ctx, filename, area);
+    }
+
+    if (s->preprocess_images) {
+        ret = quantize_image_colors(s, area);
+        if (ret < 0)
+            goto exit;
+        if (s->dump_bitmaps && original_area->nb_colors != area->nb_colors) {
+            snprintf(filename, sizeof(filename), "graphicsub2text_%"PRId64"_%d_quantized", frame->subtitle_timing.start_pts, area_index);
+            png_save(ctx, filename, area);
+        }
+    }
+
+    bg_color_index = get_background_color_index(s, area);
+
+    if (s->preprocess_images) {
+        int x, y, w, h;
+
+        for (int i = 0; i < area->nb_colors; ++i) {
+            av_log(s, AV_LOG_DEBUG, "Color #%d: %0.8X\n", i, area->pal[i]);
+        }
+
+        text_color_index = get_text_color_index(s, area, bg_color_index, &outline_color_index);
+
+        get_crop_region(s, area, text_color_index, &x, &y, &w, &h);
+
+        if ((ret = crop_area_bitmap(s, area, x, y, w, h) < 0))
+            goto exit;
+
+        if ((ret = crop_area_bitmap(s, original_area, x, y, w, h) < 0))
+            goto exit;
+
+        make_image_binary(s, area, text_color_index);
+
+        if (s->dump_bitmaps) {
+            snprintf(filename, sizeof(filename), "graphicsub2text_%"PRId64"_%d_preprocessed", frame->subtitle_timing.start_pts, area_index);
+            png_save(ctx, filename, area);
+        }
+
+        gs_img = create_bitmap_image(ctx, area, text_color_index);
+    } else
+        gs_img = create_grayscale_image(ctx, area, 1);
+
+    if (!gs_img) {
+        ret = AVERROR(ENOMEM);
+        goto exit;
+    }
+
+    area->type = AV_SUBTITLE_FMT_ASS;
+    TessBaseAPISetImage(s->tapi, gs_img, area->w, area->h, 1, area->linesize[0]);
+
+    TessBaseAPISetSourceResolution(s->tapi, 72);
+
+    ret = TessBaseAPIRecognize(s->tapi, NULL);
+    if (ret == 0)
+        ocr_text = TessBaseAPIGetUTF8Text(s->tapi);
+
+    if (!ocr_text || !strlen(ocr_text)) {
+        av_log(ctx, AV_LOG_WARNING, "OCR didn't return a text. ret=%d\n", ret);
+        area->ass = NULL;
+
+        goto exit;
+    }
+
+    const size_t len = strlen(ocr_text);
+    if (len > 0 && ocr_text[len - 1] == '\n')
+        ocr_text[len - 1] = 0;
+
+    av_log(ctx, AV_LOG_VERBOSE, "OCR Result: %s\n", ocr_text);
+
+    area->ass = av_strdup(ocr_text);
+    TessDeleteText(ocr_text);
+
+    // End of simple recognition
+
+    if (s->recognize != RFLAGS_NONE) {
+        TessResultIterator* ri = 0;
+        const TessPageIteratorLevel level = RIL_WORD;
+        int cur_is_bold = 0, cur_is_italic = 0, cur_is_underlined = 0, cur_pointsize = 0;
+        uint32_t cur_text_color = 0, cur_bg_color = 0, cur_outline_color = 0;
+
+        char *cur_font_name = NULL;
+        int valign = 0; // 0: bottom, 4: top, 8 middle
+        int halign = 2; // 1: left, 2: center, 3: right
+        int in_code = 0;
+        double font_factor = (0.000666 * (s->h - 480) + 1) * s->font_size_factor;
+
+        av_freep(&area->ass);
+        av_bprint_clear(&s->buffer);
+
+        ri = TessBaseAPIGetIterator(s->tapi);
+
+        // Horizontal Alignment
+        if (s->w && s->recognize & RFLAGS_HALIGN) {
+            int left_margin = area->x;
+            int right_margin = s->w - area->x - area->w;
+            double relative_diff = ((double)left_margin - right_margin) / s->w;
+
+            if (FFABS(relative_diff) < 0.1)
+                halign = 2; // center
+            else if (relative_diff > 0)
+                halign = 3; // right
+            else
+                halign = 1; // left
+        }
+
+        // Vertical Alignment
+        if (s->h && frame->height && s->recognize & RFLAGS_VALIGN) {
+            int left = 0, top = 0, right = 0, bottom = 0;
+
+            TessPageIteratorBoundingBox((TessPageIterator*)ri, RIL_TEXTLINE, &left, &top, &right, &bottom);
+            av_log(s, AV_LOG_DEBUG, "RIL_TEXTLINE - TOP: %d  BOTTOM: %d HEIGHT: %d\n", top, bottom, bottom - top);
+
+            TessPageIteratorBoundingBox((TessPageIterator*)ri, RIL_BLOCK, &left, &top, &right, &bottom);
+
+            const int vertical_pos = area->y + area->h / 2;
+            if (vertical_pos < s->h / 3) {
+                *margin_v = area->y + top;
+                valign = 4;
+            }
+            else if (vertical_pos < s->h / 3 * 2) {
+                *margin_v = 0;
+                valign = 8;
+            } else {
+                *margin_v = frame->height - area->y - area->h;
+                valign = 0;
+            }
+        }
+
+        if (*margin_v < 0)
+            *margin_v = 0;
+
+        // Set alignment when not default (2)
+        if ((valign | halign) != 2)
+            in_code = print_code(&s->buffer, in_code, "\\a%d", valign | halign);
+
+        do {
+            int is_bold, is_italic, is_underlined, is_monospace, is_serif, is_smallcaps, pointsize, font_id;
+            char* word;
+            const char *font_name = TessResultIteratorWordFontAttributes(ri, &is_bold, &is_italic, &is_underlined, &is_monospace, &is_serif, &is_smallcaps, &pointsize, &font_id);
+            uint32_t text_color = 0, bg_color = 0, outline_color = 0;
+
+            if (cur_is_underlined && !is_underlined && s->recognize & RFLAGS_FUNDERLINE)
+                in_code = print_code(&s->buffer, in_code, "\\u0");
+
+            if (cur_is_bold && !is_bold && s->recognize & RFLAGS_FBOLD)
+                in_code = print_code(&s->buffer, in_code, "\\b0");
+
+            if (cur_is_italic && !is_italic && s->recognize & RFLAGS_FITALIC)
+                in_code = print_code(&s->buffer, in_code, "\\i0");
+
+
+            if (TessPageIteratorIsAtBeginningOf((TessPageIterator*)ri, RIL_TEXTLINE) && !TessPageIteratorIsAtBeginningOf((TessPageIterator*)ri, RIL_BLOCK)) {
+                in_code = end_code(&s->buffer, in_code);
+                av_bprintf(&s->buffer, "\\N");
+            }
+
+            if (get_word_colors(ctx, ri, area, original_area, bg_color_index, text_color_index, outline_color_index, &bg_color, &text_color, &outline_color) == 0) {
+
+                if (text_color > 0 && cur_text_color != text_color && s->recognize & RFLAGS_COLOR) {
+                    const uint8_t* tval = (uint8_t*)&text_color;
+                    const int color = (int)tval[R] << 16 | (int)tval[G] << 8 | tval[B];
+
+                    in_code = print_code(&s->buffer, in_code, "\\1c&H%0.6X&", color);
+                    if (tval[A] != 255)
+                        in_code = print_code(&s->buffer, in_code, "\\1a&H%0.2X&", 255 - tval[A]);
+                }
+
+                if (outline_color > 0 && cur_outline_color != outline_color && s->recognize & RFLAGS_OUTLINECOLOR) {
+                    const uint8_t* tval = (uint8_t*)&outline_color;
+                    const int color = (int)tval[R] << 16 | (int)tval[G] << 8 | tval[B];
+
+                    in_code = print_code(&s->buffer, in_code, "\\3c&H%0.6X&\\bord2", color);
+                    in_code = print_code(&s->buffer, in_code, "\\3a&H%0.2X&", FFMIN(255 - tval[A], 30));
+                }
+
+                cur_text_color = text_color;
+                cur_outline_color = outline_color;
+            }
+
+            if (font_name && strlen(font_name) && s->recognize & RFLAGS_FONT) {
+                if (!cur_font_name || !strlen(cur_font_name) || strcmp(cur_font_name, font_name) != 0) {
+                    char *sanitized_font_name = av_strireplace(font_name, "_", " ");
+                    if (!sanitized_font_name) {
+                        ret = AVERROR(ENOMEM);
+                        goto exit;
+                    }
+
+                    in_code = print_code(&s->buffer, in_code, "\\fn%s", sanitized_font_name);
+                    av_freep(&sanitized_font_name);
+
+                    if (cur_font_name)
+                        av_freep(&cur_font_name);
+                    cur_font_name = av_strdup(font_name);
+                    if (!cur_font_name) {
+                        ret = AVERROR(ENOMEM);
+                        goto exit;
+                    }
+                }
+            }
+
+            if (pointsize > 0 && pointsize != cur_pointsize && s->recognize & RFLAGS_FONTSIZE) {
+                float change_factor = (float)(FFABS(pointsize - cur_pointsize)) / FFMAX(pointsize, cur_pointsize);
+
+                // Avoid small changes due to recognition variance
+                if (change_factor > 0.12f) {
+                    av_log(s, AV_LOG_DEBUG, "pointsize - pointsize: %d\n", pointsize);
+                    in_code = print_code(&s->buffer, in_code, "\\fs%d", (int)(pointsize * font_factor));
+                    cur_pointsize = pointsize;
+                }
+            }
+
+            if (is_italic && !cur_is_italic && s->recognize & RFLAGS_FITALIC)
+                in_code = print_code(&s->buffer, in_code, "\\i1");
+
+            if (is_bold && !cur_is_bold && s->recognize & RFLAGS_FBOLD)
+                in_code = print_code(&s->buffer, in_code, "\\b1");
+
+            if (is_underlined && !cur_is_underlined && s->recognize & RFLAGS_FUNDERLINE)
+                in_code = print_code(&s->buffer, in_code, "\\u1");
+
+            in_code = end_code(&s->buffer, in_code);
+
+            cur_is_underlined = is_underlined;
+            cur_is_bold = is_bold;
+            cur_is_italic = is_italic;
+
+            if (!TessPageIteratorIsAtBeginningOf((TessPageIterator*)ri, RIL_TEXTLINE))
+                av_bprint_chars(&s->buffer, ' ', 1);
+
+            word = TessResultIteratorGetUTF8Text(ri, level);
+            av_bprint_append_data(&s->buffer, word, strlen(word));
+            TessDeleteText(word);
+
+        } while (TessResultIteratorNext(ri, level));
+
+        if (!av_bprint_is_complete(&s->buffer))
+            ret = AVERROR(ENOMEM);
+        else {
+            av_log(ctx, AV_LOG_VERBOSE, "ASS Result: %s\n", s->buffer.str);
+            area->ass = av_strdup(s->buffer.str);
+        }
+
+        TessResultIteratorDelete(ri);
+        av_freep(&cur_font_name);
+    }
+
+exit:
+    free_subtitle_area(original_area);
+    av_freep(&gs_img);
+    av_buffer_unref(&area->buf[0]);
+    area->type = AV_SUBTITLE_FMT_ASS;
+
+    return ret;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFilterContext *ctx = inlink->dst;
+    SubOcrContext *s = ctx->priv;
+    AVFilterLink *outlink = inlink->dst->outputs[0];
+    int ret, frame_sent = 0;
+
+    if (s->pending_frame && !frame->repeat_sub) {
+        const int64_t pts_diff = frame->subtitle_timing.start_pts - s->pending_frame->subtitle_timing.start_pts;
+
+        if (pts_diff == 0) {
+            // This is just a repetition of the previous frame, ignore it
+            av_frame_free(&frame);
+            return 0;
+        }
+
+        s->pending_frame->subtitle_timing.duration = pts_diff;
+
+        if ((ret = av_buffer_replace(&s->pending_frame->subtitle_header, s->subtitle_header)) < 0)
+            return ret;
+
+        ret = ff_filter_frame(outlink, s->pending_frame);
+        s->pending_frame = NULL;
+        if (ret < 0)
+            return  ret;
+
+        frame_sent = 1;
+        s->last_subtitle_pts = frame->subtitle_timing.start_pts;
+    }
+
+    if (frame->repeat_sub) {
+        // Ignore repeated frame
+        av_frame_free(&frame);
+        return 0;
+    }
+
+    s->last_subtitle_pts = frame->subtitle_timing.start_pts;
+
+    ret = av_frame_make_writable(frame);
+
+    if (ret < 0) {
+        av_frame_free(&frame);
+        return ret;
+    }
+
+    frame->format = AV_SUBTITLE_FMT_ASS;
+
+    av_log(ctx, AV_LOG_VERBOSE, "filter_frame sub_pts: %"PRIu64", duration: %"PRIu64", num_areas: %d\n",
+        frame->subtitle_timing.start_pts, frame->subtitle_timing.duration, frame->num_subtitle_areas);
+
+    if (frame->num_subtitle_areas > 1 &&
+        frame->subtitle_areas[0]->y > frame->subtitle_areas[frame->num_subtitle_areas - 1]->y) {
+
+        for (unsigned i = 0; i < frame->num_subtitle_areas / 2; i++)
+            FFSWAP(AVSubtitleArea*, frame->subtitle_areas[i], frame->subtitle_areas[frame->num_subtitle_areas - i - 1]);
+    }
+
+    for (int i = 0; i < frame->num_subtitle_areas; i++) {
+        AVSubtitleArea *area = frame->subtitle_areas[i];
+        int margin_v = 0;
+
+        ret = convert_area(ctx, area, frame, i, &margin_v);
+        if (ret < 0)
+            return ret;
+
+        if (area->ass && area->ass[0] != '\0') {
+
+            const int layer = s->recognize ? i : 0;
+            char *tmp = area->ass;
+            area->ass = avpriv_ass_get_dialog_ex(s->readorder_counter++, layer, "Default", NULL, 0, 0, margin_v, NULL, tmp);
+            av_free(tmp);
+        }
+    }
+
+    // When decoders can't determine the end time, they are setting it either to UINT32_NAX
+    // or 30s (dvbsub).
+    if (s->delay_when_no_duration && frame->subtitle_timing.duration >= ms_to_avtb(29000)) {
+        // Can't send it without end time, wait for the next frame to determine the end_display time
+        s->pending_frame = frame;
+
+        if (frame_sent)
+            return 0;
+
+        // To keep all going, send an empty frame instead
+        frame = ff_get_subtitles_buffer(outlink, AV_SUBTITLE_FMT_ASS);
+        if (!frame)
+            return AVERROR(ENOMEM);
+
+        av_frame_copy_props(frame, s->pending_frame);
+        frame->subtitle_timing.start_pts = 0;
+        frame->subtitle_timing.duration = 1;
+        frame->repeat_sub = 1;
+    }
+
+    if ((ret = av_buffer_replace(&frame->subtitle_header, s->subtitle_header)) < 0)
+        return ret;
+
+    return ff_filter_frame(outlink, frame);
+}
+
+#define OFFSET(x) offsetof(SubOcrContext, x)
+#define FLAGS (AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption graphicsub2text_options[] = {
+    { "delay_when_no_duration", "delay output when duration is unknown", OFFSET(delay_when_no_duration), AV_OPT_TYPE_BOOL,   { .i64 = 0 },                         0,                  1,       FLAGS, NULL },
+    { "dump_bitmaps",           "save processed bitmaps as .ppm",        OFFSET(dump_bitmaps),           AV_OPT_TYPE_BOOL,   { .i64 = 0 },                         0,                  1,       FLAGS, NULL },
+    { "font_size_factor",       "font size adjustment factor",           OFFSET(font_size_factor),       AV_OPT_TYPE_DOUBLE, { .dbl = 1.0 },                       0.2,                5,       FLAGS, NULL },
+    { "language",               "ocr language",                          OFFSET(language),               AV_OPT_TYPE_STRING, { .str = "eng" },                     0,                  0,       FLAGS, NULL },
+    { "ocr_mode",               "set ocr mode",                          OFFSET(ocr_mode),               AV_OPT_TYPE_INT,    { .i64=OEM_TESSERACT_ONLY },          OEM_TESSERACT_ONLY, 2,       FLAGS, "ocr_mode" },
+    {   "tesseract",            "classic tesseract ocr",                 0,                              AV_OPT_TYPE_CONST,  { .i64=OEM_TESSERACT_ONLY },          0,                  0,       FLAGS, "ocr_mode" },
+    {   "lstm",                 "lstm (ML based)",                       0,                              AV_OPT_TYPE_CONST,  { .i64=OEM_LSTM_ONLY},                0,                  0,       FLAGS, "ocr_mode" },
+    {   "both",                 "use both models combined",              0,                              AV_OPT_TYPE_CONST,  { .i64=OEM_TESSERACT_LSTM_COMBINED }, 0,                  0,       FLAGS, "ocr_mode" },
+    { "preprocess_images",      "reduce colors, remove outlines",        OFFSET(preprocess_images),      AV_OPT_TYPE_BOOL,   { .i64 = 1 },                         0,                  1,       FLAGS, NULL },
+    { "recognize",              "detect fonts, styles and colors",       OFFSET(recognize),              AV_OPT_TYPE_FLAGS,  { .i64 = RFLAGS_ALL},                  0,                  INT_MAX, FLAGS, "reco_flags" },
+        { "none",         "no format detection",  0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_NONE         }, 0, 0, FLAGS, "reco_flags" },
+        { "halign",       "horizontal alignment", 0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_HALIGN       }, 0, 0, FLAGS, "reco_flags" },
+        { "valign",       "vertical alignment",   0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_VALIGN       }, 0, 0, FLAGS, "reco_flags" },
+        { "bold",         "font bold",            0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_FBOLD        }, 0, 0, FLAGS, "reco_flags" },
+        { "italic",       "font italic",          0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_FITALIC      }, 0, 0, FLAGS, "reco_flags" },
+        { "underline",    "font underline",       0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_FUNDERLINE   }, 0, 0, FLAGS, "reco_flags" },
+        { "font",         "font name",            0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_FONT         }, 0, 0, FLAGS, "reco_flags" },
+        { "fontsize",     "font size",            0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_FONTSIZE     }, 0, 0, FLAGS, "reco_flags" },
+        { "color",        "font color",           0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_COLOR        }, 0, 0, FLAGS, "reco_flags" },
+        { "outlinecolor", "outline color",        0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_OUTLINECOLOR }, 0, 0, FLAGS, "reco_flags" },
+    { "tessdata_path",          "path to tesseract data",                OFFSET(tessdata_path),          AV_OPT_TYPE_STRING, { .str = NULL },                      0,                  0,       FLAGS, NULL },
+    { NULL },
+};
+
+AVFILTER_DEFINE_CLASS(graphicsub2text);
+
+static const AVFilterPad inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .filter_frame = filter_frame,
+        .config_props = config_input,
+    },
+};
+
+static const AVFilterPad outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_SUBTITLE,
+        .config_props  = config_output,
+    },
+};
+
+const AVFilter ff_sf_graphicsub2text = {
+    .name          = "graphicsub2text",
+    .description   = NULL_IF_CONFIG_SMALL("Convert graphical subtitles to text subtitles via OCR"),
+    .init          = init,
+    .uninit        = uninit,
+    .priv_size     = sizeof(SubOcrContext),
+    .priv_class    = &graphicsub2text_class,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_QUERY_FUNC(query_formats),
+};
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v6 19/25] avfilter/subscale: Add filter for scaling and/or re-arranging graphical subtitles
  2022-06-26 16:34         ` [FFmpeg-devel] [PATCH v6 00/25] Subtitle Filtering 2022 ffmpegagent
                             ` (17 preceding siblings ...)
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 18/25] avfilter/graphicsub2text: Add new graphicsub2text filter (OCR) softworkz
@ 2022-06-26 16:35           ` softworkz
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 20/25] avfilter/subfeed: add subtitle feed filter softworkz
                             ` (5 subsequent siblings)
  24 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-26 16:35 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 configure                 |   1 +
 doc/filters.texi          | 164 +++++++
 libavfilter/Makefile      |   1 +
 libavfilter/allfilters.c  |   1 +
 libavfilter/sf_subscale.c | 884 ++++++++++++++++++++++++++++++++++++++
 5 files changed, 1051 insertions(+)
 create mode 100644 libavfilter/sf_subscale.c

diff --git a/configure b/configure
index 462d473c5f..89db322fb8 100755
--- a/configure
+++ b/configure
@@ -3730,6 +3730,7 @@ sr_filter_deps="avformat swscale"
 sr_filter_select="dnn"
 stereo3d_filter_deps="gpl"
 splitcc_filter_deps="avcodec"
+subscale_filter_deps="swscale avcodec"
 subtitles_filter_deps="avformat avcodec libass"
 super2xsai_filter_deps="gpl"
 pixfmts_super2xsai_test_deps="super2xsai_filter"
diff --git a/doc/filters.texi b/doc/filters.texi
index 4bbec62c02..00fa9bbac5 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -27580,6 +27580,170 @@ Set the rendering margin in pixels.
 For rendering, alway use the latest event only, which is covering the given point in time.
 @end table
 
+@section subscale
+
+Provides high-quality scaling and rearranging functionality for graphical subtitles.
+
+The subscale filter provides multiple approaches for manipulating
+the size and position of graphical subtitle rectangles wich can
+be combined or used separately.
+Scaling is performed by converting the palettized subtitle bitmaps
+to RGBA and re-quantization to palette colors afterwards via elbg algorithm.
+
+The two major operations are 'scale' and 're-arrange' with the
+latter being separated as 'arrange_h' and 'arrange_v'.
+
+
+Inputs:
+- 0: Subtitles [bitmap]
+
+Outputs:
+- 0: Subtitles [bitmap]
+
+It accepts the following parameters:
+
+@table @option
+
+@item w, width
+Set the width of the output.
+Width and height in case of graphical subtitles are just indicating
+a virtual size for which the output (consisting of 0-n bitmap rectangles)
+is intended to be displayed on.
+
+@item h, height
+Set the height of the output.
+
+@item margin_h
+Sets a horizontal margin to be preserverved when using any
+of the arrange modes.
+
+@item margin_v
+Sets a vertical margin to be preserverved when using any
+of the arrange modes.
+
+@item force_original_aspect_ratio
+Enable decreasing or increasing output video width or height if necessary to
+keep the original aspect ratio. Possible values:
+
+@table @samp
+@item disable
+Scale the video as specified and disable this feature.
+
+@item decrease
+The output video dimensions will automatically be decreased if needed.
+
+@item increase
+The output video dimensions will automatically be increased if needed.
+
+@end table
+
+
+@item scale_mode
+Specifies how subtitle bitmaps should be scaled.
+The scale factor is determined by the the factor between input
+and output size.
+
+@table @samp
+@item none
+Do not apply any common scaling.
+
+@item uniform
+Uniformly scale all subtitle bitmaps including their positions.
+
+@item uniform_no_reposition
+Uniformly scale all subtitle bitmaps without changing positions.
+
+@end table
+
+
+@item arrange_h
+Specifies how subtitle bitmaps should be arranged horizontally.
+
+@item arrange_v
+Specifies how subtitle bitmaps should be arranged vertically.
+
+
+@table @samp
+@item none
+Do not rearrange subtitle bitmaps.
+
+@item margin_no_scale
+Move subtitle bitmaps to be positioned inside the specified
+margin (margin_h or margin_v) when possible and without scaling.
+
+@item margin_and_scale
+Move subtitle bitmaps to be positioned inside the specified
+margin (margin_h or margin_v) and scale in case it doesn't fit.
+
+@item snapalign_no_scale
+Categorize subtitle bitmap positions as one of left/center/right
+or top/bottom/middle based on original positioning and apply
+these alignments for the target positioning.
+No scaling will be applied.
+
+@item snapalign_and_scale
+Categorize subtitle bitmap positions as one of left/center/right
+or top/bottom/middle based on original positioning and apply
+these alignments for the target positioning.
+Bitmaps that do not fit inside the margins borders are
+scaled to fit.
+@end table
+
+@item eval
+Set evaluation mode for the expressions (@option{width}, @option{height}).
+
+It accepts the following values:
+@table @samp
+@item init
+Evaluate expressions only once during the filter initialization.
+
+@item frame
+Evaluate expressions for each incoming frame. This is way slower than the
+@samp{init} mode since it requires all the scalers to be re-computed, but it
+allows advanced dynamic expressions.
+@end table
+
+Default value is @samp{init}.
+
+
+@item num_colors
+Set the number of palette colors for output images.
+Choose the maximum (256) when further processing is done (e.g.
+overlaying on a video).
+When subtitles will be encoded as bitmap subtitles (e.g. dvbsub),
+a smaller number of palette colors (e.g. 4-16) might need to be used, depending
+on the target format and codec.
+
+@item bitmap_width_align
+@item bitmap_height_align
+Make sure that subtitle bitmap sizes are a multiple of this
+value. Default is 2.
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Uniformly scale down video and bitmap subtitles and encode
+subtitles as dvbsub.
+@example
+ffmpeg -y -loglevel verbose -ss 32 -i "https://streams.videolan.org/samples/sub/largeres_vobsub.mkv" -filter_complex "[0:0]scale=480x270[vid1];[0:10]subscale=w=480:h=270:num_colors=16[sub1]" -map [vid1] -map [sub1] -c:s dvbsub -c:v libx265 output.ts
+@end example
+@item
+Squeeze video vertically and arrange subtitle bitmaps
+inside the video area without scaling, then overlay
+subtitles onto the video.
+@example
+ffmpeg -y -loglevel verbose -ss 32 -i "https://streams.videolan.org/samples/sub/largeres_vobsub.mkv" -filter_complex "[0:0]scale=1920x400,setsar=1[vid1];[0:10]subscale=w=1920:h=400:scale_mode=none:margin_h=50:arrange_h=margin_no_scale:arrange_v=margin_no_scale:margin_v=50:num_colors=256[sub1];[vid1][sub1]overlaygraphicsubs" -c:v libx265 output.ts
+@end example
+@item
+Scale both, a video and its embedded VOB subs, then encode them as separate streams into an MKV.
+@example
+ffmpeg -y -y -loglevel verbose -i "https://streams.videolan.org/samples/sub/largeres_vobsub.mkv" -filter_complex "[0:0]scale=720x576[vid1];[0:7]subscale=w=720:h=576:num_colors=16[sub1]" -map [vid1] -map [sub1] -c:s dvdsub out.mkv
+@end example
+@end itemize
+
 @c man end SUBTITLE FILTERS
 
 @chapter Multimedia Filters
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 6e6485c99a..0f43937205 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -583,6 +583,7 @@ OBJS-$(CONFIG_CENSOR_FILTER)                 += sf_textmod.o
 OBJS-$(CONFIG_SHOW_SPEAKER_FILTER)           += sf_textmod.o
 OBJS-$(CONFIG_SPLITCC_FILTER)                += sf_splitcc.o
 OBJS-$(CONFIG_STRIPSTYLES_FILTER)            += sf_stripstyles.o
+OBJS-$(CONFIG_SUBSCALE_FILTER)               += sf_subscale.o
 OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
 
 # multimedia filters
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index cbd93c4236..6792665730 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -575,6 +575,7 @@ extern const AVFilter ff_sf_graphicsub2text;
 extern const AVFilter ff_sf_showspeaker;
 extern const AVFilter ff_sf_splitcc;
 extern const AVFilter ff_sf_stripstyles;
+extern const AVFilter ff_sf_subscale;
 extern const AVFilter ff_sf_textmod;
 
 /* those filters are part of public or internal API,
diff --git a/libavfilter/sf_subscale.c b/libavfilter/sf_subscale.c
new file mode 100644
index 0000000000..04f0f16c27
--- /dev/null
+++ b/libavfilter/sf_subscale.c
@@ -0,0 +1,884 @@
+/*
+ * 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
+ * scale graphical subtitles filter
+ */
+
+#include <string.h>
+
+#include "drawutils.h"
+#include "internal.h"
+#include "scale_eval.h"
+#include "libavutil/eval.h"
+#include "libavutil/opt.h"
+#include "libswscale/swscale.h"
+
+#include "libavcodec/elbg.h"
+
+static const char *const var_names[] = {
+    "in_w",   "iw",
+    "in_h",   "ih",
+    "out_w",  "ow",
+    "out_h",  "oh",
+    "a",
+    "sar",
+    "dar",
+    "margin_h",
+    "margin_v",
+    NULL
+};
+
+enum var_name {
+    VAR_IN_W,   VAR_IW,
+    VAR_IN_H,   VAR_IH,
+    VAR_OUT_W,  VAR_OW,
+    VAR_OUT_H,  VAR_OH,
+    VAR_A,
+    VAR_SAR,
+    VAR_DAR,
+    VARS_B_H,
+    VARS_B_V,
+    VARS_NB
+};
+
+enum EvalMode {
+    EVAL_MODE_INIT,
+    EVAL_MODE_FRAME,
+    EVAL_MODE_NB
+};
+
+enum SubScaleMode {
+    SSM_NONE,
+    SSM_UNIFORM,
+    SSM_UNIFORM_NO_REPOSITION,
+};
+
+enum SubArrangeMode {
+    SAM_NONE,
+    SAM_ENSUREMARGIN_NO_SCALE,
+    SAM_ENSUREMARGIN_AND_SCALE,
+    SAM_SNAPALIGNMENT_NO_SCALE,
+    SAM_SNAPALIGNMENT_AND_SCALE,
+};
+
+typedef struct SubScaleContext {
+    const AVClass *class;
+    struct SwsContext *sws;
+    AVDictionary *opts;
+
+    int w, h;
+
+    char *w_expr;               ///< width  expression string
+    char *h_expr;               ///< height expression string
+    AVExpr *w_pexpr;
+    AVExpr *h_pexpr;
+    double var_values[VARS_NB];
+
+    int force_original_aspect_ratio;
+    int eval_mode;               ///< expression evaluation mode
+
+    int use_caching;
+
+    // Scale Options
+    enum SubScaleMode scale_mode;
+
+    // Arrange Options
+    enum SubArrangeMode arrange_mode_h;
+    enum SubArrangeMode arrange_mode_v;
+    int margin_h;
+    int margin_v;
+    char *margin_h_expr;
+    char *margin_v_expr;
+    AVExpr *margin_h_pexpr;
+    AVExpr *margin_v_pexpr;
+
+    // Bitmap Options
+    int num_output_colors;
+    int bitmap_width_align;
+    int bitmap_height_align;
+
+    // Color Quantization Fields
+    struct ELBGContext *ctx;
+    AVLFG lfg;
+    int *codeword;
+    int *codeword_closest_codebook_idxs;
+    int *codebook;
+    int r_idx, g_idx, b_idx, a_idx;
+    AVFrame *cache_frame;
+} SubScaleContext;
+
+
+static int config_output(AVFilterLink *outlink);
+
+static int check_exprs(AVFilterContext *ctx)
+{
+    const SubScaleContext *s = ctx->priv;
+    unsigned vars_w[VARS_NB] = { 0 }, vars_h[VARS_NB] = { 0 };
+
+    if (!s->w_pexpr && !s->h_pexpr)
+        return AVERROR(EINVAL);
+
+    if (s->w_pexpr)
+        av_expr_count_vars(s->w_pexpr, vars_w, VARS_NB);
+    if (s->h_pexpr)
+        av_expr_count_vars(s->h_pexpr, vars_h, VARS_NB);
+
+    if (vars_w[VAR_OUT_W] || vars_w[VAR_OW]) {
+        av_log(ctx, AV_LOG_ERROR, "Width expression cannot be self-referencing: '%s'.\n", s->w_expr);
+        return AVERROR(EINVAL);
+    }
+
+    if (vars_h[VAR_OUT_H] || vars_h[VAR_OH]) {
+        av_log(ctx, AV_LOG_ERROR, "Height expression cannot be self-referencing: '%s'.\n", s->h_expr);
+        return AVERROR(EINVAL);
+    }
+
+    if ((vars_w[VAR_OUT_H] || vars_w[VAR_OH]) &&
+        (vars_h[VAR_OUT_W] || vars_h[VAR_OW])) {
+        av_log(ctx, AV_LOG_WARNING, "Circular references detected for width '%s' and height '%s' - possibly invalid.\n", s->w_expr, s->h_expr);
+    }
+
+    if (s->margin_h_pexpr)
+        av_expr_count_vars(s->margin_h_pexpr, vars_w, VARS_NB);
+    if (s->margin_v_pexpr)
+        av_expr_count_vars(s->margin_v_pexpr, vars_h, VARS_NB);
+
+    return 0;
+}
+
+static int scale_parse_expr(AVFilterContext *ctx, char *str_expr, AVExpr **pexpr_ptr, const char *var, const char *args)
+{
+    SubScaleContext *s = ctx->priv;
+    int ret, is_inited = 0;
+    char *old_str_expr = NULL;
+    AVExpr *old_pexpr = NULL;
+
+    if (str_expr) {
+        old_str_expr = av_strdup(str_expr);
+        if (!old_str_expr)
+            return AVERROR(ENOMEM);
+        av_opt_set(s, var, args, 0);
+    }
+
+    if (*pexpr_ptr) {
+        old_pexpr = *pexpr_ptr;
+        *pexpr_ptr = NULL;
+        is_inited = 1;
+    }
+
+    ret = av_expr_parse(pexpr_ptr, args, var_names,
+                        NULL, NULL, NULL, NULL, 0, ctx);
+    if (ret < 0) {
+        av_log(ctx, AV_LOG_ERROR, "Cannot parse expression for %s: '%s'\n", var, args);
+        goto revert;
+    }
+
+    ret = check_exprs(ctx);
+    if (ret < 0)
+        goto revert;
+
+    if (is_inited && (ret = config_output(ctx->outputs[0])) < 0)
+        goto revert;
+
+    av_expr_free(old_pexpr);
+    av_freep(&old_str_expr);
+
+    return 0;
+
+revert:
+    av_expr_free(*pexpr_ptr);
+    *pexpr_ptr = NULL;
+    if (old_str_expr) {
+        av_opt_set(s, var, old_str_expr, 0);
+        av_free(old_str_expr);
+    }
+    if (old_pexpr)
+        *pexpr_ptr = old_pexpr;
+
+    return ret;
+}
+
+static av_cold int init_dict(AVFilterContext *ctx, AVDictionary **opts)
+{
+    SubScaleContext *s = ctx->priv;
+    uint8_t rgba_map[4];
+    int ret;
+
+    if (!s->w_expr)
+        av_opt_set(s, "w", "iw", 0);
+    if (!s->h_expr)
+        av_opt_set(s, "h", "ih", 0);
+
+    ret = scale_parse_expr(ctx, NULL, &s->w_pexpr, "width", s->w_expr);
+    if (ret < 0)
+        return ret;
+
+    ret = scale_parse_expr(ctx, NULL, &s->h_pexpr, "height", s->h_expr);
+    if (ret < 0)
+        return ret;
+
+    av_log(ctx, AV_LOG_VERBOSE, "w:%s h:%s\n",
+           s->w_expr, s->h_expr);
+
+    if (!s->margin_h_expr)
+        av_opt_set(s, "margin_h", "0", 0);
+    if (!s->margin_v_expr)
+        av_opt_set(s, "margin_v", "0", 0);
+
+    ret = scale_parse_expr(ctx, NULL, &s->margin_h_pexpr, "margin_h", s->margin_h_expr);
+    if (ret < 0)
+        return ret;
+
+    ret = scale_parse_expr(ctx, NULL, &s->margin_v_pexpr, "margin_v", s->margin_v_expr);
+    if (ret < 0)
+        return ret;
+
+    s->opts = *opts;
+    *opts = NULL;
+
+    ff_fill_rgba_map(&rgba_map[0], AV_PIX_FMT_RGB32);
+
+    s->r_idx = rgba_map[0]; // R
+    s->g_idx = rgba_map[1]; // G
+    s->b_idx = rgba_map[2]; // B
+    s->a_idx = rgba_map[3]; // A
+
+    av_lfg_init(&s->lfg, 123456789);
+
+
+    return 0;
+}
+
+static av_cold void uninit(AVFilterContext *ctx)
+{
+    SubScaleContext *s = ctx->priv;
+
+    av_frame_free(&s->cache_frame);
+
+    av_expr_free(s->w_pexpr);
+    av_expr_free(s->h_pexpr);
+    s->w_pexpr = s->h_pexpr = NULL;
+
+    av_expr_free(s->margin_h_pexpr);
+    av_expr_free(s->margin_v_pexpr);
+    s->margin_h_pexpr = s->margin_v_pexpr = NULL;
+
+    sws_freeContext(s->sws);
+    s->sws = NULL;
+    av_dict_free(&s->opts);
+
+    avpriv_elbg_free(&s->ctx);
+
+    av_freep(&s->codebook);
+    av_freep(&s->codeword);
+    av_freep(&s->codeword_closest_codebook_idxs);
+}
+
+static int config_input(AVFilterLink *inlink)
+{
+    ////const AVFilterContext *ctx = inlink->dst;
+    ////SubScaleContext *s = ctx->priv;
+
+    ////if (s->w <= 0 || s->h <= 0) {
+    ////    s->w = inlink->w;
+    ////    s->h = inlink->h;
+    ////}
+    return 0;
+}
+
+static int scale_eval_dimensions(AVFilterContext *ctx)
+{
+    SubScaleContext *s = ctx->priv;
+    const AVFilterLink *inlink = ctx->inputs[0];
+    char *expr;
+    int eval_w, eval_h, margin_h, margin_v;
+    int ret;
+    double res;
+
+    s->var_values[VAR_IN_W]  = s->var_values[VAR_IW] = inlink->w;
+    s->var_values[VAR_IN_H]  = s->var_values[VAR_IH] = inlink->h;
+    s->var_values[VAR_OUT_W] = s->var_values[VAR_OW] = NAN;
+    s->var_values[VAR_OUT_H] = s->var_values[VAR_OH] = NAN;
+    s->var_values[VARS_B_H]  = s->var_values[VARS_B_V] = 0;
+    s->var_values[VAR_A]     = (double) inlink->w / inlink->h;
+    s->var_values[VAR_SAR]   = inlink->sample_aspect_ratio.num ?
+        (double) inlink->sample_aspect_ratio.num / inlink->sample_aspect_ratio.den : 1;
+    s->var_values[VAR_DAR]   = s->var_values[VAR_A] * s->var_values[VAR_SAR];
+
+    res = av_expr_eval(s->w_pexpr, s->var_values, NULL);
+    s->var_values[VAR_OUT_W] = s->var_values[VAR_OW] = (int) res == 0 ? inlink->w : (int) res;
+
+    res = av_expr_eval(s->h_pexpr, s->var_values, NULL);
+    if (isnan(res)) {
+        expr = s->h_expr;
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+    eval_h = s->var_values[VAR_OUT_H] = s->var_values[VAR_OH] = (int) res == 0 ? inlink->h : (int) res;
+
+    res = av_expr_eval(s->w_pexpr, s->var_values, NULL);
+    if (isnan(res)) {
+        expr = s->w_expr;
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+    eval_w = s->var_values[VAR_OUT_W] = s->var_values[VAR_OW] = (int) res == 0 ? inlink->w : (int) res;
+
+    s->w = eval_w;
+    s->h = eval_h;
+
+    res = av_expr_eval(s->margin_h_pexpr, s->var_values, NULL);
+    s->var_values[VARS_B_H] = (int)res;
+
+    res = av_expr_eval(s->margin_v_pexpr, s->var_values, NULL);
+    if (isnan(res)) {
+        expr = s->margin_v_expr;
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+    margin_v = s->var_values[VARS_B_V] = (int)res;
+
+    res = av_expr_eval(s->margin_h_pexpr, s->var_values, NULL);
+    if (isnan(res)) {
+        expr = s->margin_h_expr;
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+    margin_h = s->var_values[VARS_B_H] = (int)res;
+
+    s->margin_h = margin_h;
+    s->margin_v = margin_v;
+
+    return 0;
+
+fail:
+    av_log(ctx, AV_LOG_ERROR,
+           "Error when evaluating the expression '%s'.\n", expr);
+    return ret;
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    AVFilterLink *inlink  = outlink->src->inputs[0];
+    SubScaleContext *s = ctx->priv;
+    int ret;
+
+    outlink->format = AV_SUBTITLE_FMT_BITMAP;
+    outlink->time_base = ctx->inputs[0]->time_base;
+    outlink->frame_rate = ctx->inputs[0]->frame_rate;
+
+    if ((ret = scale_eval_dimensions(ctx)) < 0)
+        goto fail;
+
+    ff_scale_adjust_dimensions(inlink, &s->w, &s->h,
+                               s->force_original_aspect_ratio, 2);
+
+    if (s->w > INT_MAX ||
+        s->h > INT_MAX ||
+        (s->h * inlink->w) > INT_MAX ||
+        (s->w * inlink->h) > INT_MAX)
+        av_log(ctx, AV_LOG_ERROR, "Rescaled value for width or height is too big.\n");
+
+    outlink->w = s->w;
+    outlink->h = s->h;
+
+    if (s->sws)
+        sws_freeContext(s->sws);
+
+    s->sws = sws_alloc_context();
+    if (!s->sws)
+        return AVERROR(ENOMEM);
+
+    av_opt_set_pixel_fmt(s->sws, "src_format", AV_PIX_FMT_PAL8, 0);
+    av_opt_set_int(s->sws, "dst_format", AV_PIX_FMT_RGB32, 0);
+    av_opt_set_int(s->sws, "threads", ff_filter_get_nb_threads(ctx), 0);
+
+    if (s->opts) {
+        const AVDictionaryEntry *e = NULL;
+        while ((e = av_dict_get(s->opts, "", e, AV_DICT_IGNORE_SUFFIX))) {
+            if ((ret = av_opt_set(s->sws, e->key, e->value, 0)) < 0)
+                return ret;
+        }
+    }
+
+    if ((ret = sws_init_context(s->sws, NULL, NULL)) < 0)
+        return ret;
+
+    if (inlink->sample_aspect_ratio.num){
+        outlink->sample_aspect_ratio = av_mul_q((AVRational){outlink->h * inlink->w, outlink->w * inlink->h}, inlink->sample_aspect_ratio);
+    } else
+        outlink->sample_aspect_ratio = inlink->sample_aspect_ratio;
+
+    av_log(ctx, AV_LOG_VERBOSE, "Output size set to %dx%d.\n", outlink->w, outlink->h);
+
+    return 0;
+fail:
+    return ret;
+}
+
+static int palettize_image(SubScaleContext *const s, const int w, const int h, const int src_linesize, uint8_t *src_data,
+                          int dst_linesize, uint8_t *dst_data, uint32_t *dst_pal)
+{
+    int k, ret;
+    const int codeword_length = w * h;
+
+    /* Re-Initialize */
+    s->codeword = av_realloc_f(s->codeword, codeword_length, 4 * sizeof(*s->codeword));
+    if (!s->codeword)
+        return AVERROR(ENOMEM);
+
+    s->codeword_closest_codebook_idxs =
+        av_realloc_f(s->codeword_closest_codebook_idxs, codeword_length,
+                     sizeof(*s->codeword_closest_codebook_idxs));
+    if (!s->codeword_closest_codebook_idxs)
+        return AVERROR(ENOMEM);
+
+    s->codebook = av_realloc_f(s->codebook, s->num_output_colors, 4 * sizeof(*s->codebook));
+    if (!s->codebook)
+        return AVERROR(ENOMEM);
+
+    /* build the codeword */
+    k = 0;
+    for (int i = 0; i < h; i++) {
+        uint8_t *p = src_data;
+        for (int j = 0; j < w; j++) {
+            s->codeword[k++] = p[s->b_idx];
+            s->codeword[k++] = p[s->g_idx];
+            s->codeword[k++] = p[s->r_idx];
+            s->codeword[k++] = p[s->a_idx];
+            p += 4;
+        }
+        src_data += src_linesize;
+    }
+
+    /* compute the codebook */
+    ret = avpriv_elbg_do(&s->ctx, s->codeword, 4,
+        codeword_length, s->codebook,
+        s->num_output_colors, 1,
+        s->codeword_closest_codebook_idxs, &s->lfg, 0);
+
+    if (ret < 0)
+        return ret;
+
+    /* Write Palette */
+    for (int i = 0; i < s->num_output_colors; i++) {
+        dst_pal[i] = s->codebook[i*4+3] << 24  |
+                    (s->codebook[i*4+2] << 16) |
+                    (s->codebook[i*4+1] <<  8) |
+                     s->codebook[i*4  ];
+    }
+
+    /* Write Image */
+    k = 0;
+    for (int i = 0; i < h; i++) {
+        uint8_t *p = dst_data;
+        for (int j = 0; j < w; j++, p++) {
+            p[0] = s->codeword_closest_codebook_idxs[k++];
+        }
+
+        dst_data += dst_linesize;
+    }
+
+    return ret;
+}
+
+static int rescale_size(int64_t a, AVRational factor)
+{
+    const int64_t res = av_rescale_rnd(a, factor.num, factor.den, AV_ROUND_NEAR_INF);
+    if (res > INT32_MAX || res < 0)
+        return 0;
+
+    return (int)res;
+}
+
+
+static int scale_area(AVFilterLink *link, AVSubtitleArea *area, const int target_width, const int target_height)
+{
+    const AVFilterContext *ctx = link->dst;
+    SubScaleContext *s = ctx->priv;
+    int ret;
+
+    AVBufferRef *dst_buffer;
+    const uint8_t* data[2]    = { area->buf[0]->data, (uint8_t *)&area->pal };
+    const int dstW            = FFALIGN(target_width, s->bitmap_width_align);
+    const int dstH            = FFALIGN(target_height, s->bitmap_height_align);
+    const int tmp_linesize[2] = { FFALIGN(dstW * 4, 32), 0 };
+    const int dst_linesize[2] = { dstW, 0 };
+    uint8_t* tmp[2] = { 0, 0 };
+
+    AVBufferRef *tmp_buffer = av_buffer_allocz(tmp_linesize[0] * dstH);
+    if (!tmp_buffer)
+        return AVERROR(ENOMEM);
+
+    if (!s->sws)
+        return 0;
+
+    tmp[0] = tmp_buffer->data;
+
+    s->sws = sws_getCachedContext(s->sws, area->w, area->h, AV_PIX_FMT_PAL8,
+        dstW, dstH, AV_PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL);
+    if (!s->sws) {
+        av_log(NULL, AV_LOG_FATAL, "Cannot initialize the conversion context. dstW=%d dstH=%d\n", dstW, dstH);
+        return AVERROR(EINVAL);
+    }
+
+    // Rescale to ARGB
+    ret = sws_scale(s->sws, data, area->linesize, 0, area->h, tmp, tmp_linesize);
+    if (ret < 0) {
+        av_buffer_unref(&tmp_buffer);
+        return ret;
+    }
+
+    // Alloc output buffer
+    dst_buffer = av_buffer_allocz(dst_linesize[0] * dstH);
+    if (!dst_buffer) {
+        av_buffer_unref(&tmp_buffer);
+        return AVERROR(ENOMEM);
+    }
+
+    // Quantize to palettized image
+    ret = palettize_image(s, dstW, dstH, tmp_linesize[0], tmp[0], dst_linesize[0], dst_buffer->data, area->pal);
+    av_buffer_unref(&tmp_buffer);
+
+    if (ret < 0) {
+        av_buffer_unref(&dst_buffer);
+        return ret;
+    }
+
+    av_buffer_unref(&area->buf[0]);
+    ret = av_buffer_replace(&area->buf[0], dst_buffer);
+    if (ret < 0) {
+        av_buffer_unref(&dst_buffer);
+        return ret;
+    }
+
+    area->w = dstW;
+    area->h = dstH;
+    area->linesize[0] = dst_linesize[0];
+    area->nb_colors = s->num_output_colors;
+
+    return ret;
+}
+
+static int process_area(AVFilterLink *inlink, AVSubtitleArea *area, AVRational x_factor, AVRational y_factor)
+{
+    AVFilterContext *ctx     = inlink->dst;
+    const SubScaleContext *s = ctx->priv;
+    int target_w, target_h, target_x, target_y;
+    const int border_l = s->margin_h;
+    const int border_r = s->w - s->margin_h;
+    const int border_t = s->margin_v;
+    const int border_b = s->h - s->margin_v;
+
+    av_log(ctx, AV_LOG_DEBUG, "process_area -  start: x/y: (%d:%d) size: %dx%d scale_mode: %d x-factor: %d:%d y-factor: %d:%d\n",
+        area->x, area->y, area->w, area->h, s->scale_mode, x_factor.num, x_factor.den, y_factor.num, y_factor.den);
+
+    switch (s->scale_mode) {
+    case SSM_NONE:
+        target_w = area->w;
+        target_h = area->h;
+        target_x = area->x;
+        target_y = area->y;
+        break;
+    case SSM_UNIFORM:
+        target_w = rescale_size(area->w, x_factor);
+        target_h = rescale_size(area->h, y_factor);
+        target_x = rescale_size(area->x, x_factor);
+        target_y = rescale_size(area->y, y_factor);
+        break;
+    case SSM_UNIFORM_NO_REPOSITION:
+        target_w = rescale_size(area->w, x_factor);
+        target_h = rescale_size(area->h, y_factor);
+        target_x = area->x;
+        target_y = area->y;
+        break;
+    default:
+        av_log(ctx, AV_LOG_ERROR, "Invalid scale_mode: %d\n", s->scale_mode);
+        return AVERROR(EINVAL);
+    }
+
+    av_log(ctx, AV_LOG_DEBUG, "process_area - scaled: x/y: (%d:%d) size: %dx%d.\n", target_x, target_y, target_w, target_h);
+
+
+    switch (s->arrange_mode_h) {
+    case SAM_ENSUREMARGIN_AND_SCALE:
+    case SAM_SNAPALIGNMENT_AND_SCALE:
+        {
+            // IF it doesn't fit - scale it
+            int max_width = s->w - 2 * s->margin_h;
+
+            if (max_width < 2)
+                max_width = 2;
+
+            if (target_w > max_width) {
+                target_h = (int)av_rescale(target_h, max_width, target_w);
+                target_w = max_width;
+                target_x = s->margin_h;
+            }
+        }
+        break;
+    }
+
+    switch (s->arrange_mode_h) {
+    case SAM_NONE:
+        break;
+    case SAM_ENSUREMARGIN_NO_SCALE:
+    case SAM_ENSUREMARGIN_AND_SCALE:
+        // Left border
+        if (target_x < border_l)
+            target_x = border_l;
+
+        // Right border
+        if (target_x + target_w > border_r)
+            target_x = border_r - target_w;
+
+        break;
+    case SAM_SNAPALIGNMENT_NO_SCALE:
+    case SAM_SNAPALIGNMENT_AND_SCALE:
+        {
+            // Use original values to detect alignment
+            const int left_margin          = area->x;
+            const int right_margin         = inlink->w - area->x - area->w;
+            const AVRational diff_factor_r = { left_margin - right_margin, area->w };
+            const float diff_factor        = (float)av_q2d(diff_factor_r);
+
+            if (diff_factor > 0.2f) {
+                // Right aligned
+                target_x = border_r - target_w;
+            } else if (diff_factor < -0.2f) {
+                // Left aligned
+                target_x = border_l;
+            } else {
+                // Centered
+                target_x = (inlink->w - area->w) / 2;
+            }
+        }
+
+        break;
+    default:
+        av_log(ctx, AV_LOG_ERROR, "Invalid arrange_mode_h: %d\n", s->arrange_mode_h);
+        return AVERROR(EINVAL);
+    }
+
+    av_log(ctx, AV_LOG_DEBUG, "process_area -  arr_h: x/y: (%d:%d) size: %dx%d.\n", target_x, target_y, target_w, target_h);
+
+
+    switch (s->arrange_mode_v) {
+    case SAM_ENSUREMARGIN_AND_SCALE:
+    case SAM_SNAPALIGNMENT_AND_SCALE:
+        {
+            // IF it doesn't fit - scale it
+            int max_height = s->h - 2 * s->margin_v;
+
+            if (max_height < 2)
+                max_height = 2;
+
+            if (target_h > max_height) {
+                target_w = (int)av_rescale(target_w, max_height, target_h);
+                target_h = max_height;
+                target_y = s->margin_v;
+            }
+        }
+        break;
+    }
+
+    switch (s->arrange_mode_v) {
+    case SAM_NONE:
+        break;
+    case SAM_ENSUREMARGIN_NO_SCALE:
+    case SAM_ENSUREMARGIN_AND_SCALE:
+        // Top border
+        if (target_y < border_t)
+            target_y = border_t;
+
+        // Bottom border
+        if (target_y + target_h > border_b)
+            target_y = border_b - target_h;
+
+        break;
+    case SAM_SNAPALIGNMENT_NO_SCALE:
+    case SAM_SNAPALIGNMENT_AND_SCALE:
+        {
+            // Use original values to detect alignment
+            const int top_margin           = area->y;
+            const int bottom_margin        = inlink->h - area->y - area->h;
+            const AVRational diff_factor_r = { top_margin - bottom_margin, area->h };
+            const float diff_factor        = (float)av_q2d(diff_factor_r);
+
+            if (diff_factor > 0.2f) {
+                // Bottom aligned
+                target_y = border_b - target_h;
+            } else if (diff_factor < -0.2f) {
+                // Top aligned
+                target_y = border_t;
+            } else {
+                // Centered
+                target_y = (inlink->h - area->h) / 2;
+            }
+        }
+
+        break;
+    default:
+        av_log(ctx, AV_LOG_ERROR, "Invalid arrange_mode_v: %d\n", s->arrange_mode_v);
+        return AVERROR(EINVAL);
+    }
+
+    av_log(ctx, AV_LOG_VERBOSE, "process_area -  arr_v: x/y: (%d:%d) size: %dx%d.\n", target_x, target_y, target_w, target_h);
+
+    area->x = target_x;
+    area->y = target_y;
+
+    if (area->w != target_w || area->h != target_h)
+        return scale_area(inlink, area, target_w, target_h);
+
+    return 0;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    const AVFilterContext *ctx = inlink->dst;
+    SubScaleContext *s = ctx->priv;
+    AVFilterLink *outlink = ctx->outputs[0];
+    int ret;
+
+    // just forward empty frames
+    if (frame->num_subtitle_areas == 0) {
+        av_frame_free(&s->cache_frame);
+        return ff_filter_frame(outlink, frame);
+    }
+
+    if (s->use_caching && s->cache_frame && frame->repeat_sub
+        && s->cache_frame->subtitle_timing.start_pts == frame->subtitle_timing.start_pts) {
+        AVFrame *out = av_frame_clone(s->cache_frame);
+        if (!out)
+            return AVERROR(ENOMEM);
+
+        ret = av_frame_copy_props(out, frame);
+        if (ret < 0)
+            return ret;
+
+        av_log(inlink->dst, AV_LOG_DEBUG, "subscale CACHED - size %dx%d  pts: %"PRId64"  areas: %d\n", frame->width, frame->height, frame->subtitle_timing.start_pts, frame->num_subtitle_areas);
+        av_frame_free(&frame);
+        return ff_filter_frame(outlink, out);
+    }
+
+    ret = av_frame_make_writable(frame);
+    if (ret >= 0) {
+        const AVRational x_factor = { .num = outlink->w, .den = inlink->w} ;
+        const AVRational y_factor = { .num = outlink->h, .den = inlink->h} ;
+
+        for (unsigned i = 0; i < frame->num_subtitle_areas; ++i) {
+            AVSubtitleArea *area = frame->subtitle_areas[i];
+
+            ret = process_area(inlink, area, x_factor, y_factor);
+            if (ret < 0)
+                return ret;
+        }
+
+        av_log(inlink->dst, AV_LOG_DEBUG, "subscale output - size %dx%d  pts: %"PRId64"  areas: %d\n", frame->width, frame->height, frame->subtitle_timing.start_pts, frame->num_subtitle_areas);
+
+        if (s->use_caching) {
+            av_frame_free(&s->cache_frame);
+            s->cache_frame = av_frame_clone(frame);
+        }
+
+        return ff_filter_frame(outlink, frame);
+    }
+
+    return ret;
+}
+
+#define OFFSET(x) offsetof(SubScaleContext, x)
+#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption subscale_options[] = {
+    { "margin_h", "horizontal border",        OFFSET(margin_h_expr),    AV_OPT_TYPE_STRING,        .flags = FLAGS },
+    { "margin_v", "vertical border",          OFFSET(margin_v_expr),    AV_OPT_TYPE_STRING,        .flags = FLAGS },
+    { "w",     "Output video width",          OFFSET(w_expr),    AV_OPT_TYPE_STRING,        .flags = FLAGS },
+    { "width", "Output video width",          OFFSET(w_expr),    AV_OPT_TYPE_STRING,        .flags = FLAGS },
+    { "h",     "Output video height",         OFFSET(h_expr),    AV_OPT_TYPE_STRING,        .flags = FLAGS },
+    { "height","Output video height",         OFFSET(h_expr),    AV_OPT_TYPE_STRING,        .flags = FLAGS },
+    { "force_original_aspect_ratio", "decrease or increase w/h if necessary to keep the original AR", OFFSET(force_original_aspect_ratio), AV_OPT_TYPE_INT, { .i64 = 0}, 0, 2, FLAGS, "force_oar" },
+    { "disable",  NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, 0, 0, FLAGS, "force_oar" },
+    { "decrease", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 1 }, 0, 0, FLAGS, "force_oar" },
+    { "increase", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 2 }, 0, 0, FLAGS, "force_oar" },
+    { "scale_mode", "specify how to scale subtitles", OFFSET(scale_mode), AV_OPT_TYPE_INT, {.i64 = SSM_UNIFORM}, 0, SSM_UNIFORM_NO_REPOSITION, FLAGS, "scale_mode" },
+         { "none",  "no common scaling", 0, AV_OPT_TYPE_CONST, {.i64=SSM_NONE},  .flags = FLAGS, .unit = "scale_mode" },
+         { "uniform",  "uniformly scale and reposition", 0, AV_OPT_TYPE_CONST, {.i64=SSM_UNIFORM},  .flags = FLAGS, .unit = "scale_mode" },
+         { "uniform_no_reposition",  "uniformly scale but keep positions", 0, AV_OPT_TYPE_CONST, {.i64=SSM_UNIFORM_NO_REPOSITION},  .flags = FLAGS, .unit = "scale_mode" },
+    { "use_caching", "Cache output frames",   OFFSET(use_caching), AV_OPT_TYPE_BOOL, { .i64 = 1}, 0, 1, .flags = FLAGS },
+
+    { "arrange_h", "specify how to arrange subtitles horizontally", OFFSET(arrange_mode_h), AV_OPT_TYPE_INT, {.i64 = SAM_NONE}, 0, SAM_SNAPALIGNMENT_AND_SCALE, FLAGS, "arrange" },
+    { "arrange_v", "specify how to arrange subtitles vertically", OFFSET(arrange_mode_v), AV_OPT_TYPE_INT, {.i64 = SAM_NONE}, 0, SAM_SNAPALIGNMENT_AND_SCALE, FLAGS, "arrange" },
+         { "none",  "no repositioning", 0, AV_OPT_TYPE_CONST, {.i64=SAM_NONE},  .flags = FLAGS, .unit = "arrange" },
+         { "margin_no_scale",  "move subs inside border when possible", 0, AV_OPT_TYPE_CONST, {.i64=SAM_ENSUREMARGIN_NO_SCALE,},  .flags = FLAGS, .unit = "arrange" },
+         { "margin_and_scale",  "move subs inside border and scale as needed", 0, AV_OPT_TYPE_CONST, {.i64=SAM_ENSUREMARGIN_AND_SCALE,},  .flags = FLAGS, .unit = "arrange" },
+         { "snapalign_no_scale",  "snap subs to near/far/center when possible", 0, AV_OPT_TYPE_CONST, {.i64=SAM_SNAPALIGNMENT_NO_SCALE,},  .flags = FLAGS, .unit = "arrange" },
+         { "snapalign_and_scale", "snap subs to near/far/center and scale as needed", 0, AV_OPT_TYPE_CONST, {.i64=SAM_SNAPALIGNMENT_AND_SCALE,}, .flags = FLAGS, .unit = "arrange" },
+
+    { "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, {.i64 = EVAL_MODE_INIT}, 0, EVAL_MODE_NB-1, FLAGS, "eval" },
+         { "init",  "eval expressions once during initialization", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_INIT},  .flags = FLAGS, .unit = "eval" },
+         { "frame", "eval expressions during initialization and per-frame", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_FRAME}, .flags = FLAGS, .unit = "eval" },
+    { "num_colors",     "number of palette colors in output", OFFSET(num_output_colors),  AV_OPT_TYPE_INT, {.i64 = 256 }, 2, 256, .flags = FLAGS },
+    { "bitmap_width_align",     "Bitmap width alignment", OFFSET(bitmap_width_align),  AV_OPT_TYPE_INT, {.i64 = 2 }, 1, 256, .flags = FLAGS },
+    { "bitmap_height_align",     "Bitmap height alignment", OFFSET(bitmap_height_align),  AV_OPT_TYPE_INT, {.i64 = 2 }, 1, 256, .flags = FLAGS },
+    { .name =  NULL }
+};
+
+static const AVClass subscale_class = {
+    .class_name       = "subscale",
+    .item_name        = av_default_item_name,
+    .option           = subscale_options,
+    .version          = LIBAVUTIL_VERSION_INT,
+    .category         = AV_CLASS_CATEGORY_FILTER,
+};
+
+static const AVFilterPad inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .filter_frame = filter_frame,
+        .config_props = config_input,
+    },
+};
+
+static const AVFilterPad outputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .config_props = config_output,
+    },
+};
+
+const AVFilter ff_sf_subscale = {
+    .name            = "subscale",
+    .description     = NULL_IF_CONFIG_SMALL("Scale graphical subtitles."),
+    .init_dict       = init_dict,
+    .uninit          = uninit,
+    .priv_size       = sizeof(SubScaleContext),
+    .priv_class      = &subscale_class,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_SINGLE_SUBFMT(AV_SUBTITLE_FMT_BITMAP),
+};
+
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v6 20/25] avfilter/subfeed: add subtitle feed filter
  2022-06-26 16:34         ` [FFmpeg-devel] [PATCH v6 00/25] Subtitle Filtering 2022 ffmpegagent
                             ` (18 preceding siblings ...)
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 19/25] avfilter/subscale: Add filter for scaling and/or re-arranging graphical subtitles softworkz
@ 2022-06-26 16:35           ` softworkz
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 21/25] avfilter/text2graphicsub: Added text2graphicsub subtitle filter softworkz
                             ` (4 subsequent siblings)
  24 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-26 16:35 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavfilter/Makefile     |   1 +
 libavfilter/allfilters.c |   1 +
 libavfilter/sf_subfeed.c | 412 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 414 insertions(+)
 create mode 100644 libavfilter/sf_subfeed.c

diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 0f43937205..780e5333a0 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -583,6 +583,7 @@ OBJS-$(CONFIG_CENSOR_FILTER)                 += sf_textmod.o
 OBJS-$(CONFIG_SHOW_SPEAKER_FILTER)           += sf_textmod.o
 OBJS-$(CONFIG_SPLITCC_FILTER)                += sf_splitcc.o
 OBJS-$(CONFIG_STRIPSTYLES_FILTER)            += sf_stripstyles.o
+OBJS-$(CONFIG_SUBFEED_FILTER)                += sf_subfeed.o
 OBJS-$(CONFIG_SUBSCALE_FILTER)               += sf_subscale.o
 OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
 
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 6792665730..ff56661b67 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -575,6 +575,7 @@ extern const AVFilter ff_sf_graphicsub2text;
 extern const AVFilter ff_sf_showspeaker;
 extern const AVFilter ff_sf_splitcc;
 extern const AVFilter ff_sf_stripstyles;
+extern const AVFilter ff_sf_subfeed;
 extern const AVFilter ff_sf_subscale;
 extern const AVFilter ff_sf_textmod;
 
diff --git a/libavfilter/sf_subfeed.c b/libavfilter/sf_subfeed.c
new file mode 100644
index 0000000000..3227861ac0
--- /dev/null
+++ b/libavfilter/sf_subfeed.c
@@ -0,0 +1,412 @@
+/*
+ * 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
+ * subtitle filter for feeding subtitle frames into a filtergraph in a contiguous way
+ *
+ *
+ * also supports
+ *   - duration fixup
+ *     delaying a subtitle event with unknown duration and infer duration from the
+ *     start time of the subsequent subtitle
+ *   - scattering
+ *     splitting a subtitle event with unknown duration into multiple ones with
+ *     a short and fixed duration
+ *
+ */
+
+#include "filters.h"
+#include "libavutil/opt.h"
+#include "subtitles.h"
+#include "libavutil/avassert.h"
+
+enum SubFeedMode {
+    FM_REPEAT,
+    FM_SCATTER,
+    FM_FORWARD,
+};
+
+typedef struct SubFeedContext {
+    const AVClass *class;
+    enum AVSubtitleType format;
+    enum SubFeedMode mode;
+
+    AVRational frame_rate;
+    int fix_durations;
+    int fix_overlap;
+
+    int current_frame_isnew;
+    int eof;
+    int got_first_input;
+    int need_frame;
+    int64_t next_pts_offset;
+    int64_t recent_subtitle_pts;
+
+    int64_t counter;
+
+    /**
+     * Queue of frames waiting to be filtered.
+     */
+    FFFrameQueue fifo;
+
+} SubFeedContext;
+
+static int64_t ms_to_avtb(int64_t ms)
+{
+    return av_rescale_q(ms, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q);
+}
+
+static int64_t avtb_to_ms(int64_t avtb)
+{
+    return av_rescale_q(avtb, AV_TIME_BASE_Q, (AVRational){ 1, 1000 });
+}
+
+static int init(AVFilterContext *ctx)
+{
+    SubFeedContext *s = ctx->priv;
+
+    ff_framequeue_init(&s->fifo, NULL);
+
+    return 0;
+}
+
+static void uninit(AVFilterContext *ctx)
+{
+    SubFeedContext *s = ctx->priv;
+    ff_framequeue_free(&s->fifo);
+}
+
+static int config_input(AVFilterLink *link)
+{
+    ////const subfeedContext *context = link->dst->priv;
+
+    return 0;
+}
+
+static int query_formats(AVFilterContext *ctx)
+{
+    AVFilterFormats *formats;
+    AVFilterLink *inlink0 = ctx->inputs[0];
+    AVFilterLink *outlink0 = ctx->outputs[0];
+    static const enum AVSubtitleType subtitle_fmts[] = { AV_SUBTITLE_FMT_BITMAP, AV_SUBTITLE_FMT_ASS, AV_SUBTITLE_FMT_NB };
+    int ret;
+
+    formats = ff_make_format_list(subtitle_fmts);
+
+    if ((ret = ff_formats_ref(formats, &inlink0->outcfg.formats)) < 0)
+        return ret;
+
+    if ((ret = ff_formats_ref(formats, &outlink0->incfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    SubFeedContext *s = outlink->src->priv;
+    const AVFilterLink *inlink = outlink->src->inputs[0];
+
+    outlink->time_base = AV_TIME_BASE_Q;
+    outlink->format = inlink->format;
+    outlink->w = inlink->w;
+    outlink->h = inlink->h;
+
+    if (s->mode == FM_FORWARD)
+        outlink->frame_rate = (AVRational) { 1, 0 };
+    else
+        outlink->frame_rate = s->frame_rate;
+
+    return 0;
+}
+
+static int request_frame(AVFilterLink *outlink)
+{
+    SubFeedContext *s = outlink->src->priv;
+    AVFilterLink *inlink = outlink->src->inputs[0];
+    int64_t last_pts = outlink->current_pts;
+    int64_t next_pts;
+    int64_t interval = ms_to_avtb((int64_t)(av_q2d(av_inv_q(outlink->frame_rate)) * 1000));
+    AVFrame *out;
+    int status;
+
+    if (s->mode == FM_FORWARD)
+        return ff_request_frame(inlink);
+
+    s->counter++;
+    if (interval == 0)
+        interval =  ms_to_avtb(200);
+
+    status = ff_outlink_get_status(inlink);
+    if (status == AVERROR_EOF)
+        s->eof = 1;
+
+    if (s->eof)
+        return AVERROR_EOF;
+
+    if (!s->got_first_input && inlink->current_pts != AV_NOPTS_VALUE) {
+
+        s->got_first_input = 1;
+        next_pts = av_rescale_q(inlink->current_pts, inlink->time_base, AV_TIME_BASE_Q);
+        if (next_pts < last_pts)
+            next_pts = last_pts + interval;
+
+    } else if (last_pts == AV_NOPTS_VALUE)
+        next_pts = av_rescale_q(inlink->current_pts, inlink->time_base, AV_TIME_BASE_Q);
+    else
+        next_pts = last_pts + interval;
+
+    if (next_pts == AV_NOPTS_VALUE)
+        next_pts = 0;
+
+    if (s->next_pts_offset) {
+        av_log(outlink->src, AV_LOG_VERBOSE, "Subtracting next_pts_offset: %"PRId64" \n", s->next_pts_offset);
+        next_pts -= s->next_pts_offset;
+        s->next_pts_offset = 0;
+    }
+
+retry:
+    if (ff_framequeue_queued_frames(&s->fifo) && !s->current_frame_isnew) {
+
+        const AVFrame *current_frame = ff_framequeue_peek(&s->fifo, 0);
+        const int64_t sub_end_time   = current_frame->subtitle_timing.start_pts + current_frame->subtitle_timing.duration;
+
+        if (ff_framequeue_queued_frames(&s->fifo) > 1) {
+            const AVFrame *next_frame = ff_framequeue_peek(&s->fifo, 1);
+            if (next_pts + interval > next_frame->subtitle_timing.start_pts) {
+                AVFrame *remove_frame = ff_framequeue_take(&s->fifo);
+                av_frame_free(&remove_frame);
+                s->current_frame_isnew = 1;
+                goto retry;
+            }
+        }
+
+        if (next_pts > sub_end_time) {
+            AVFrame *remove_frame = ff_framequeue_take(&s->fifo);
+            av_frame_free(&remove_frame);
+            s->current_frame_isnew = 1;
+            goto retry;
+        }
+    }
+
+    if (ff_framequeue_queued_frames(&s->fifo)) {
+        AVFrame *current_frame = ff_framequeue_peek(&s->fifo, 0);
+
+        if (current_frame && current_frame->subtitle_timing.start_pts <= next_pts + interval) {
+            if (!s->current_frame_isnew)
+                current_frame->repeat_sub++;
+
+            out = av_frame_clone(current_frame);
+
+            if (!out)
+                return AVERROR(ENOMEM);
+
+            if (!s->current_frame_isnew) {
+                out->pts = next_pts;
+            } else {
+                out->pts = out->subtitle_timing.start_pts;
+
+                if (out->pts < next_pts)
+                    out->pts = next_pts;
+
+                s->next_pts_offset = (out->pts - next_pts) % interval;
+            }
+
+            if (s->mode == FM_SCATTER) {
+                const int64_t sub_end_time  = current_frame->subtitle_timing.start_pts + current_frame->subtitle_timing.duration;
+
+                if (s->current_frame_isnew == 1 && current_frame->subtitle_timing.start_pts < out->pts) {
+                    const int64_t diff = out->pts - current_frame->subtitle_timing.start_pts;
+                    current_frame->subtitle_timing.duration -= diff;
+                }
+
+                out->repeat_sub = 0;
+                out->subtitle_timing.start_pts = out->pts;
+                out->subtitle_timing.duration = interval;
+                av_assert1(out->pts >= next_pts);
+                av_assert1(out->pts < next_pts + interval);
+                av_assert1(out->pts < sub_end_time);
+
+                if (out->pts > next_pts)
+                    out->subtitle_timing.duration -= out->pts - next_pts;
+
+                if (sub_end_time < next_pts + interval) {
+                    const int64_t diff = next_pts + interval - sub_end_time;
+                    av_assert1(diff <= out->subtitle_timing.duration);
+                    out->subtitle_timing.duration -= diff;
+                }
+            }
+
+            s->current_frame_isnew = 0;
+            s->recent_subtitle_pts = out->subtitle_timing.start_pts;
+
+            av_log(outlink->src, AV_LOG_DEBUG, "Output1 frame pts: %"PRId64"  subtitle_pts: %"PRId64"  repeat_frame: %d\n",
+                out->pts, out->subtitle_timing.start_pts, out->repeat_sub);
+
+            return ff_filter_frame(outlink, out);
+        }
+    }
+
+    if (ff_framequeue_queued_frames(&s->fifo) == 0) {
+        status = ff_request_frame(inlink);
+        if (status == AVERROR_EOF) {
+            s->eof = 1;
+            return status;
+        }
+
+        if (s->counter > 1 && s->counter % 2)
+            return 0;
+    }
+
+    out = ff_get_subtitles_buffer(outlink, outlink->format);
+    out->pts = next_pts;
+    out->repeat_sub = 1;
+    out->subtitle_timing.start_pts = s->recent_subtitle_pts;
+
+    av_log(outlink->src, AV_LOG_DEBUG, "Output2 frame pts: %"PRId64"  subtitle_pts: %"PRId64"  repeat_frame: %d\n",
+        out->pts, out->subtitle_timing.start_pts, out->repeat_sub);
+
+    return ff_filter_frame(outlink, out);
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFilterContext *ctx        = inlink->dst;
+    SubFeedContext *s           = inlink->dst->priv;
+    AVFilterLink *outlink       = inlink->dst->outputs[0];
+    const int64_t index         = (int64_t)ff_framequeue_queued_frames(&s->fifo) - 1;
+    size_t nb_queued_frames;
+    int ret = 0;
+
+    av_log(ctx, AV_LOG_VERBOSE, "frame.pts: %"PRId64" (AVTB: %"PRId64") -  subtitle_timing.start_pts: %"PRId64" subtitle_timing.duration: %"PRId64" - format: %d\n",
+        frame->pts, av_rescale_q(frame->pts, inlink->time_base, AV_TIME_BASE_Q), frame->subtitle_timing.start_pts, frame->subtitle_timing.duration, frame->format);
+
+    frame->pts = av_rescale_q(frame->pts, inlink->time_base, AV_TIME_BASE_Q);
+
+    if (index < 0) {
+        s->current_frame_isnew = 1;
+    } else if (s->fix_durations || s->fix_overlap) {
+        AVFrame *previous_frame = ff_framequeue_peek(&s->fifo, index);
+        const int64_t pts_diff = frame->subtitle_timing.start_pts - previous_frame->subtitle_timing.start_pts;
+        nb_queued_frames = ff_framequeue_queued_frames(&s->fifo);
+
+        if (s->fix_durations && pts_diff > 0 && previous_frame->subtitle_timing.duration > ms_to_avtb(29000)) {
+            av_log(ctx, AV_LOG_VERBOSE, "Previous frame (index #%"PRId64") has a duration of %"PRId64" ms, setting to  %"PRId64" ms\n",
+                index, avtb_to_ms(previous_frame->subtitle_timing.duration), avtb_to_ms(frame->subtitle_timing.start_pts - previous_frame->subtitle_timing.start_pts));
+            previous_frame->subtitle_timing.duration = frame->subtitle_timing.start_pts - previous_frame->subtitle_timing.start_pts;
+        }
+
+        if (s->fix_overlap && pts_diff > 0 && previous_frame->subtitle_timing.duration > pts_diff) {
+            av_log(ctx, AV_LOG_VERBOSE, "Detected overlap from previous frame (index #%"PRId64") which had a duration of %"PRId64" ms, setting to the pts_diff which is %"PRId64" ms\n",
+                index, avtb_to_ms(previous_frame->subtitle_timing.duration), avtb_to_ms(pts_diff));
+            previous_frame->subtitle_timing.duration = pts_diff;
+        }
+
+        if (pts_diff <= 0) {
+            av_log(ctx, AV_LOG_WARNING, "The pts_diff to the previous frame (index #%"PRId64")  is <= 0: %"PRId64" ms. The previous frame duration is %"PRId64" ms.\n",
+                index, avtb_to_ms(pts_diff),  avtb_to_ms(previous_frame->subtitle_timing.duration));
+
+            if (s->fix_overlap) {
+                av_log(ctx, AV_LOG_VERBOSE, "Removing previous frame\n");
+                previous_frame = ff_framequeue_take(&s->fifo);
+                while (nb_queued_frames > 1) {
+                    ff_framequeue_add(&s->fifo, previous_frame);
+                    previous_frame = ff_framequeue_take(&s->fifo);
+                    nb_queued_frames--;
+                }
+            }
+        }
+    }
+
+    ff_framequeue_add(&s->fifo, frame);
+
+    nb_queued_frames = ff_framequeue_queued_frames(&s->fifo);
+
+    if (nb_queued_frames > 3)
+        av_log(ctx, AV_LOG_WARNING, "frame queue count: %zu\n", nb_queued_frames);
+
+    if (s->mode == FM_FORWARD && nb_queued_frames) {
+
+        AVFrame *first_frame = ff_framequeue_peek(&s->fifo, 0);
+
+        if (s->fix_overlap && nb_queued_frames < 2) {
+          av_log(ctx, AV_LOG_VERBOSE, "Return no frame since we have less than 2\n");
+          return 0;
+        }
+
+        if (s->fix_durations && first_frame->subtitle_timing.duration > ms_to_avtb(29000)) {
+            av_log(ctx, AV_LOG_VERBOSE, "Return no frame because first frame duration is %"PRId64" ms\n", avtb_to_ms(first_frame->subtitle_timing.duration));
+            return 0;
+        }
+
+        first_frame = ff_framequeue_take(&s->fifo);
+        return ff_filter_frame(outlink, first_frame);
+    }
+
+    return ret;
+}
+
+#define OFFSET(x) offsetof(SubFeedContext, x)
+#define FLAGS (AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption subfeed_options[] = {
+    { "fix_durations", "delay output and determine duration from next frame", OFFSET(fix_durations),   AV_OPT_TYPE_BOOL,   { .i64=1 },                  0, 1, FLAGS, NULL     },
+    { "fix_overlap", "delay output and adjust durations to prevent overlap", OFFSET(fix_overlap),   AV_OPT_TYPE_BOOL,   { .i64=0 },                  0, 1, FLAGS, NULL     },
+    { "mode",       "set feed mode",         OFFSET(mode),      AV_OPT_TYPE_INT,                 { .i64=FM_REPEAT },  FM_REPEAT, FM_FORWARD,  FLAGS, "mode" },
+    {   "repeat",     "repeat recent while valid, send empty otherwise",   0, AV_OPT_TYPE_CONST, { .i64=FM_REPEAT },  0,                  0,  FLAGS, "mode" },
+    {   "scatter",    "subdivide subtitles into 1/framerate segments",     0, AV_OPT_TYPE_CONST, { .i64=FM_SCATTER }, 0,                  0,  FLAGS, "mode" },
+    {   "forward",    "forward only (clears output framerate)",            0, AV_OPT_TYPE_CONST, { .i64=FM_FORWARD }, 0,                  0,  FLAGS, "mode" },
+    { "rate",       "output frame rate",     OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE,         { .str = "5"},       0,         INT_MAX,     FLAGS, NULL },\
+    { "r",          "output frame rate",     OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE,         { .str = "5"},       0,         INT_MAX,     FLAGS, NULL },\
+    { NULL },
+};
+
+AVFILTER_DEFINE_CLASS(subfeed);
+
+static const AVFilterPad inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .filter_frame = filter_frame,
+        .config_props = config_input,
+    },
+};
+
+static const AVFilterPad outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_SUBTITLE,
+        .request_frame = request_frame,
+        .config_props  = config_output,
+    },
+};
+
+const AVFilter ff_sf_subfeed = {
+    .name           = "subfeed",
+    .description    = NULL_IF_CONFIG_SMALL("Control subtitle frame timing and flow in a filtergraph"),
+    .init           = init,
+    .uninit         = uninit,
+    .priv_size      = sizeof(SubFeedContext),
+    .priv_class     = &subfeed_class,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    FILTER_QUERY_FUNC(query_formats),
+};
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v6 21/25] avfilter/text2graphicsub: Added text2graphicsub subtitle filter
  2022-06-26 16:34         ` [FFmpeg-devel] [PATCH v6 00/25] Subtitle Filtering 2022 ffmpegagent
                             ` (19 preceding siblings ...)
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 20/25] avfilter/subfeed: add subtitle feed filter softworkz
@ 2022-06-26 16:35           ` softworkz
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 22/25] avfilter/snull, strim: Add snull and strim filters softworkz
                             ` (3 subsequent siblings)
  24 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-26 16:35 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Added a text2graphicsub subtitle filter which converts text-based
subtitle tracks to bitmap-based subtitle tracks. The filter uses libass
to render the subtitles.
It takes as parameters an output height and width, as well as a number
of colors in the output palette as well as sources of fonts. All its
arguments are optional.

Reviewed-by: softworkz <softworkz@hotmail.com>
Signed-off-by: softworkz <softworkz@hotmail.com>
Signed-off-by: tcoza <traian.coza@gmail.com>
---
 configure                        |   1 +
 doc/filters.texi                 |  51 +++
 libavfilter/Makefile             |   1 +
 libavfilter/allfilters.c         |   1 +
 libavfilter/sf_text2graphicsub.c | 634 +++++++++++++++++++++++++++++++
 5 files changed, 688 insertions(+)
 create mode 100644 libavfilter/sf_text2graphicsub.c

diff --git a/configure b/configure
index 89db322fb8..f74077b439 100755
--- a/configure
+++ b/configure
@@ -3734,6 +3734,7 @@ subscale_filter_deps="swscale avcodec"
 subtitles_filter_deps="avformat avcodec libass"
 super2xsai_filter_deps="gpl"
 pixfmts_super2xsai_test_deps="super2xsai_filter"
+text2graphicsub_filter_deps="avformat avcodec libass"
 textsub2video_filter_deps="avcodec libass"
 tinterlace_filter_deps="gpl"
 tinterlace_merge_test_deps="tinterlace_filter"
diff --git a/doc/filters.texi b/doc/filters.texi
index 00fa9bbac5..56da160634 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -27744,6 +27744,57 @@ ffmpeg -y -y -loglevel verbose -i "https://streams.videolan.org/samples/sub/larg
 @end example
 @end itemize
 
+@section text2graphicsub
+
+Converts a text-based subtitle track to a bitmap-based subtitle track.
+
+The text2graphicsub filter uses libass to render all the subtitle
+frames in a text-based subtitle track (such as srt or ass) to allow
+its encoding with a bitmap-based subtitle encoder (such as dvd_subtitle).
+
+Inputs:
+- 0: Subtitles [text]
+
+Outputs:
+- 0: Subtitles [bitmap]
+
+It accepts the following parameters:
+
+@table @option
+
+@item s, size
+Set the size of the output. Default from input track.
+
+@item n, num_colors
+Set the number of palette colors for output images,
+Range [2,256]. Default 16.
+
+@item f, filename
+Set the media container from which to extract fonts required
+for rendering the subtitles, usually the same as the input
+track's media container. Can be omitted.
+
+@item fd, fontsdir
+Set the directory from which to load fonts required for
+rendering the subtitles. Can be used with 'fonts'. Can be omitted.
+
+@item ss, stripstyles
+Remove certain styles from an ass track which do not render well.
+Stripped styles include blurs, fades, and other animations.
+Default yes.
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Convert a video's text-based subtitle track to a dvd_subtitle subtitle track.
+@example
+ffmpeg -i video.mkv -map 0:v -map 0:a -filter_complex [0:s]text2graphicsub=f=video.mkv[dvd_sub] -map [dvd_sub] -c copy -c:s dvd_subtitle output.mkv
+@end example
+@end itemize
+
 @c man end SUBTITLE FILTERS
 
 @chapter Multimedia Filters
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 780e5333a0..adbc16a5a9 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -585,6 +585,7 @@ OBJS-$(CONFIG_SPLITCC_FILTER)                += sf_splitcc.o
 OBJS-$(CONFIG_STRIPSTYLES_FILTER)            += sf_stripstyles.o
 OBJS-$(CONFIG_SUBFEED_FILTER)                += sf_subfeed.o
 OBJS-$(CONFIG_SUBSCALE_FILTER)               += sf_subscale.o
+OBJS-$(CONFIG_TEXT2GRAPHICSUB_FILTER)        += sf_text2graphicsub.o
 OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
 
 # multimedia filters
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index ff56661b67..82297e9657 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -577,6 +577,7 @@ extern const AVFilter ff_sf_splitcc;
 extern const AVFilter ff_sf_stripstyles;
 extern const AVFilter ff_sf_subfeed;
 extern const AVFilter ff_sf_subscale;
+extern const AVFilter ff_sf_text2graphicsub;
 extern const AVFilter ff_sf_textmod;
 
 /* those filters are part of public or internal API,
diff --git a/libavfilter/sf_text2graphicsub.c b/libavfilter/sf_text2graphicsub.c
new file mode 100644
index 0000000000..1fd7a76c53
--- /dev/null
+++ b/libavfilter/sf_text2graphicsub.c
@@ -0,0 +1,634 @@
+/*
+ * Copyright (c) 2021 tcoza
+ *
+ * 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
+ * convert text subtitles to bitmap subtitles filter
+ */
+
+#include <ass/ass.h>
+#include "libavutil/avstring.h"
+
+#include "internal.h"
+#include "avfilter.h"
+#include "drawutils.h"
+#include "libavutil/opt.h"
+#include "libavutil/buffer.h"
+#include "libavutil/internal.h"
+#include "libavformat/avformat.h"
+#include "libavcodec/elbg.h"
+#include "libavutil/ass_split_internal.h"
+#include "libavutil/ass_internal.h"
+#include "libavutil/lfg.h"
+
+typedef struct PalettizeContext {
+    int *codeword, *codebook;
+    int *codeword_closest_codebook_idxs;
+    int r_idx, g_idx, b_idx, a_idx;
+    struct ELBGContext *elbg;
+    AVLFG lfg;
+} PalettizeContext;
+
+typedef struct Text2GraphicSubContext {
+    const AVClass *class;
+    ASS_Library *library;
+    ASS_Renderer *renderer;
+    ASS_Track *track;
+    PalettizeContext *palettize_context;
+    FFDrawContext draw_context;
+    struct { int width; int height; } size;
+    int num_colors, stripstyles;
+    char *filename, *fontsdir, *force_style;
+    int got_header;
+} Text2GraphicSubContext;
+
+typedef struct DialogContext {
+    int is_animated;
+    int has_text;
+} DialogContext;
+
+static void dialog_text_cb(void *priv, const char *text, int len)
+{
+    DialogContext *s = priv;
+    if (!s->is_animated )
+        s->has_text = 1;
+}
+
+static void dialog_drawing_mode_cb(void *priv, int scale)
+{
+    DialogContext *s = priv;
+    s->is_animated = 1;
+}
+
+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,
+    .drawing_mode     = dialog_drawing_mode_cb,
+    .animate          = dialog_animate_cb,
+    .move             = dialog_move_cb,
+};
+
+static char *process_dialog(const char *ass_line)
+{
+    DialogContext dlg_ctx = { 0 };
+    ASSDialog *dialog = avpriv_ass_split_dialog(NULL, ass_line);
+    AVBPrint buffer;
+    char *result = NULL;
+
+    if (!dialog)
+        return NULL;
+
+    av_bprint_init(&buffer, 512, AV_BPRINT_SIZE_UNLIMITED);
+
+    avpriv_ass_filter_override_codes(&dialog_callbacks, &dlg_ctx, dialog->text, &buffer, ASS_SPLIT_BASIC);
+
+    if (av_bprint_is_complete(&buffer) && buffer.len > 0 && dlg_ctx.has_text > 0)
+        result = avpriv_ass_get_dialog_ex(dialog->readorder, dialog->layer, dialog->style, dialog->name, dialog->margin_l, dialog->margin_r, dialog->margin_v, dialog->effect, buffer.str);
+
+    av_bprint_finalize(&buffer, NULL);
+    avpriv_ass_free_dialog(&dialog);
+
+    return result;
+}
+
+static void palettize_image(PalettizeContext *const s,
+                           const int w, const int h,
+                           uint8_t *src_data, const int src_linesize,
+                           uint8_t *dst_data, const int dst_linesize,
+                           uint32_t *dst_pal, const int num_colors)
+{
+    const int codeword_length = w * h;
+
+    /* Re-Initialize */
+    s->codeword = av_realloc_f(s->codeword, codeword_length, 4 * sizeof(*s->codeword));
+    s->codeword_closest_codebook_idxs = av_realloc_f(
+        s->codeword_closest_codebook_idxs, codeword_length,
+        sizeof(*s->codeword_closest_codebook_idxs));
+    s->codebook = av_realloc_f(s->codebook, num_colors, 4 * sizeof(*s->codebook));
+
+    /* build the codeword */
+    for (int k = 0, i = 0; i < h; i++)
+        for (int j = 0; j < w; j++) {
+            s->codeword[k++] = src_data[i * src_linesize + j * 4 + s->b_idx];
+            s->codeword[k++] = src_data[i * src_linesize + j * 4 + s->g_idx];
+            s->codeword[k++] = src_data[i * src_linesize + j * 4 + s->r_idx];
+            s->codeword[k++] = src_data[i * src_linesize + j * 4 + s->a_idx];
+        }
+
+    /* compute the codebook */
+    avpriv_elbg_do(&s->elbg, s->codeword, 4,
+        codeword_length, s->codebook, num_colors, 1,
+        s->codeword_closest_codebook_idxs, &s->lfg, 0);
+
+    /* Write Palette */
+    for (int i = 0; i < num_colors; i++)
+        dst_pal[i] = (s->codebook[i*4+3] << 24) |
+                     (s->codebook[i*4+2] << 16) |
+                     (s->codebook[i*4+1] <<  8) |
+                     (s->codebook[i*4+0] <<  0);
+
+    /* Write Image */
+    for (int k = 0, i = 0; i < h; i++)
+        for (int j = 0; j < w; j++)
+            dst_data[i * dst_linesize + j] =
+                s->codeword_closest_codebook_idxs[k++];
+}
+
+static void init_palettizecontext(PalettizeContext **palettizecontext)
+{
+    uint8_t rgba_map[4];
+    PalettizeContext *context = (PalettizeContext *)av_malloc(sizeof(PalettizeContext));
+    context->codebook = NULL;
+    context->codeword = NULL;
+    context->codeword_closest_codebook_idxs = NULL;
+    context->elbg = NULL;
+    av_lfg_init(&context->lfg, 0xACBADF);
+    ff_fill_rgba_map(&rgba_map[0], AV_PIX_FMT_RGB32);
+    context->r_idx = rgba_map[0]; // R
+    context->g_idx = rgba_map[1]; // G
+    context->b_idx = rgba_map[2]; // B
+    context->a_idx = rgba_map[3]; // A
+    *palettizecontext = context;
+}
+
+static void free_palettizecontext(PalettizeContext **palettizecontext)
+{
+    PalettizeContext *context = *palettizecontext;
+    if (context) {
+        av_freep(&context->codebook);
+        av_freep(&context->codeword);
+        av_freep(&context->codeword_closest_codebook_idxs);
+        avpriv_elbg_free(&context->elbg);
+        av_free(context);
+        *palettizecontext = NULL;
+    }
+}
+
+/* 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 const char * const font_mimetypes[] = {
+    "font/ttf",
+    "font/otf",
+    "font/sfnt",
+    "font/woff",
+    "font/woff2",
+    "application/font-sfnt",
+    "application/font-woff",
+    "application/x-truetype-font",
+    "application/vnd.ms-opentype",
+    "application/x-font-ttf",
+    NULL
+};
+
+static int stream_is_font(AVStream* st)
+{
+    const AVDictionaryEntry *tag = NULL;
+
+    if (st->codecpar->codec_type != AVMEDIA_TYPE_ATTACHMENT)
+        return 0;
+
+    tag = av_dict_get(st->metadata, "mimetype", NULL, AV_DICT_MATCH_CASE);
+    if (!tag) return 0;
+    for (int i = 0; font_mimetypes[i]; i++)
+        if (av_strcasecmp(font_mimetypes[i], tag->value) == 0)
+            return 1;
+    return 0;
+}
+
+#define AR(c)  (((c)>>24)&0xFF)
+#define AG(c)  (((c)>>16)&0xFF)
+#define AB(c)  (((c)>> 8)&0xFF)
+#define AA(c)  ((0xFF-(c))&0xFF)
+
+static void set_area_bounds(ASS_Image *image, AVSubtitleArea *area)
+{
+    int x_min = INT_MAX, y_min = INT_MAX;
+    int x_max = 0, y_max = 0;
+    int stride_max = 0;
+
+    for (ASS_Image *img = image; img != NULL; img = img->next)
+    {
+        x_min = FFMIN(x_min, img->dst_x);
+        y_min = FFMIN(y_min, img->dst_y);
+        x_max = FFMAX(x_max, img->dst_x + img->w);
+        y_max = FFMAX(y_max, img->dst_y + img->h);
+        stride_max = FFMAX(stride_max, img->dst_x + img->stride);
+    }
+
+    area->x = x_min;
+    area->y = y_min;
+    area->w = FFALIGN(x_max - x_min, 2);
+    area->h = FFALIGN(y_max - y_min, 2);
+    area->linesize[0] = area->w;    //stride_max - x_min;
+}
+
+static void ass_image_to_area_palletization(Text2GraphicSubContext *context, ASS_Image *image, AVSubtitleArea *area)
+{
+    size_t image_rgba_size;
+    uint8_t *image_rgba;
+
+    set_area_bounds(image, area);
+    av_log(context, AV_LOG_VERBOSE, "set_area_bounds %d,%d %dx%d\n", area->x, area->y, area->w, area->h);
+
+    // Create rgba image
+    image_rgba_size = (area->linesize[0] * area->h) * 4 * sizeof(uint8_t);
+    image_rgba = (uint8_t *)av_mallocz(image_rgba_size);
+    for (; image != NULL; image = image->next)
+    {
+        uint8_t rgba_color[] = {AR(image->color), AG(image->color), AB(image->color), AA(image->color)};
+        int linesize = area->linesize[0] * 4;
+        FFDrawColor color;
+        ff_draw_color(&context->draw_context, &color, rgba_color);
+        ff_blend_mask(&context->draw_context, &color,
+                      &image_rgba, &linesize, area->w, area->h,
+                      image->bitmap, image->stride, image->w, image->h,
+                      3, 0, image->dst_x - area->x, image->dst_y - area->y);
+    }
+
+    area->nb_colors = context->num_colors;
+    area->buf[0] = av_buffer_alloc(image_rgba_size / 4);
+
+    palettize_image(context->palettize_context,
+                    area->w, area->h,
+                    image_rgba, area->linesize[0] * 4,
+                    area->buf[0]->data, area->linesize[0],
+                    &area->pal[0], context->num_colors);
+
+    // Clean up
+    av_free(image_rgba);
+}
+
+static void process_header(const AVFilterContext *link, const AVFrame *frame)
+{
+    Text2GraphicSubContext *s = link->priv;
+    ASS_Track *track = s->track;
+    ASS_Style *style;
+    int sid = 0;
+
+    if (!track)
+        return;
+
+    if (frame && frame->subtitle_header) {
+        char *subtitle_header = (char *)frame->subtitle_header->data;
+        ass_process_codec_private(s->track, subtitle_header, strlen(subtitle_header));
+    }
+    else {
+        char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+        if (!subtitle_header)
+            return;
+
+        ass_process_codec_private(s->track, subtitle_header, strlen(subtitle_header));
+        av_free(subtitle_header);
+    }
+
+    if (!s->track->event_format) {
+        s->track->event_format = av_strdup("ReadOrder, Layer, Style, Name, MarginL, MarginR, MarginV, Effect, Text");
+    }
+
+    if (s->track->n_styles == 0) {
+        sid = ass_alloc_style(track);
+        style = &s->track->styles[sid];
+        style->Name             = av_strdup("Default");
+        style->PrimaryColour    = 0xffffff00;
+        style->SecondaryColour  = 0x00ffff00;
+        style->OutlineColour    = 0x00000000;
+        style->BackColour       = 0x00000080;
+        style->Bold             = 200;
+        style->ScaleX           = 1.0;
+        style->ScaleY           = 1.0;
+        style->Spacing          = 0;
+        style->BorderStyle      = 1;
+        style->Outline          = 2;
+        style->Shadow           = 3;
+        style->Alignment        = 2;
+        track->default_style = sid;
+    }
+
+    s->got_header = 1;
+}
+
+// Main interface functions
+
+static av_cold int init(AVFilterContext *ctx)
+{
+    Text2GraphicSubContext *context = ctx->priv;
+
+    context->library = ass_library_init();
+    ass_set_message_cb(context->library, ass_log, context);
+    ass_set_fonts_dir(context->library, context->fontsdir);
+    ass_set_extract_fonts(context->library, 1);
+
+    if (context->filename) {
+        AVFormatContext *video = NULL;
+        int ret = avformat_open_input(&video, context->filename, NULL, NULL);
+        if (ret < 0) {
+            av_log(ctx, AV_LOG_ERROR, "Unable to open %s\n", context->filename);
+            return ret;
+        }
+        for (int i = 0; i < video->nb_streams; i++) {
+            const AVDictionaryEntry *tag;
+            AVStream *st = video->streams[i];
+            if (!stream_is_font(st)) continue;
+            tag = av_dict_get(st->metadata, "filename", NULL, AV_DICT_MATCH_CASE);
+            if (!tag) continue;
+            av_log(NULL, AV_LOG_DEBUG, "Loading attached font: %s\n", tag->value);
+            ass_add_font(context->library, tag->value,
+                 (char *)st->codecpar->extradata,
+                 st->codecpar->extradata_size);
+        }
+        avformat_close_input(&video);
+    }
+
+    context->renderer = ass_renderer_init(context->library);
+    ass_set_pixel_aspect(context->renderer, 1);
+    ass_set_shaper(context->renderer, 0);
+    ass_set_fonts(context->renderer, NULL, NULL, 1, NULL, 1);
+
+    context->track = ass_new_track(context->library);
+    if (!context->track) {
+        av_log(ctx, AV_LOG_ERROR, "ass_new_track() failed!\n");
+        return AVERROR(EINVAL);
+    }
+
+    ass_set_check_readorder(context->track, 0);
+
+    if (context->force_style) {
+        char **list = NULL;
+        char *temp = NULL;
+        char *ptr = av_strtok(context->force_style, ",", &temp);
+        int i = 0;
+        while (ptr) {
+            av_dynarray_add(&list, &i, ptr);
+            if (!list) {
+                return AVERROR(ENOMEM);
+            }
+            ptr = av_strtok(NULL, ",", &temp);
+        }
+        av_dynarray_add(&list, &i, NULL);
+        if (!list) {
+            return AVERROR(ENOMEM);
+        }
+        ass_set_style_overrides(context->library, list);
+        av_free(list);
+    }
+
+    init_palettizecontext(&context->palettize_context);
+
+    ff_draw_init(&context->draw_context, AV_PIX_FMT_BGRA, FF_DRAW_PROCESS_ALPHA);
+
+    return 0;
+}
+
+static int config_input(AVFilterLink *inlink)
+{
+    const AVFilterContext *ctx = inlink->dst;
+    Text2GraphicSubContext *context = ctx->priv;
+    if (!context->size.width)  context->size.width  = inlink->w;
+    if (!context->size.height) context->size.height = inlink->h;
+    if (!context->size.height || !context->size.width) {
+        av_log(NULL, AV_LOG_ERROR, "A positive height and width are required to render subtitles\n");
+        return AVERROR_EXIT;
+    }
+    ass_set_frame_size(context->renderer, context->size.width, context->size.height);
+    ass_set_storage_size(context->renderer, inlink->w, inlink->h);
+
+    return 0;
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    Text2GraphicSubContext *context = ctx->priv;
+
+    outlink->time_base = AV_TIME_BASE_Q;
+    outlink->format = AV_SUBTITLE_FMT_BITMAP;
+    outlink->w = context->size.width;
+    outlink->h = context->size.height;
+
+    return 0;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    const AVFilterContext *ctx = inlink->dst;
+    Text2GraphicSubContext *context = ctx->priv;
+    AVFilterLink *outlink = ctx->outputs[0];
+    AVSubtitleArea *area;
+    int ret;
+    unsigned processed_area_cnt = 0;
+    const int64_t start_time = av_rescale_q(frame->subtitle_timing.start_pts, AV_TIME_BASE_Q, av_make_q(1, 1000));
+    const int64_t duration   = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, av_make_q(1, 1000));
+    ASS_Image *image;
+
+    // Postpone header processing until we receive a frame with content
+    if (!context->got_header && frame->num_subtitle_areas > 0)
+        process_header(ctx, frame);
+
+    if (frame->repeat_sub || frame->num_subtitle_areas == 0) {
+        av_frame_free(&frame);
+        return 0;
+    }
+
+    ret = av_frame_make_writable(frame);
+    if (ret < 0)
+        return ret;
+
+    if (context->stripstyles) {
+        for (unsigned r = 0; r < frame->num_subtitle_areas; r++) {
+
+            area = frame->subtitle_areas[r];
+
+            if (area->ass) {
+                char *tmp = area->ass;
+                area->ass = process_dialog(area->ass);
+
+                if (area->ass) {
+                    av_log(inlink->dst, AV_LOG_DEBUG, "original: %d %s\n", r, tmp);
+                    av_log(inlink->dst, AV_LOG_DEBUG, "stripped: %d %s\n", r, area->ass);
+                }
+
+                av_free(tmp);
+            }
+        }
+    }
+
+    for (unsigned r = 0; r < frame->num_subtitle_areas; r++)
+    {
+        area = frame->subtitle_areas[r];
+        if (area->type != AV_SUBTITLE_FMT_ASS || area->ass == NULL)
+            continue;
+
+        ass_process_chunk(context->track, area->ass, strlen(area->ass), start_time, duration);
+        processed_area_cnt++;
+
+    }
+
+    if (processed_area_cnt == 0) {
+        av_frame_free(&frame);
+        return 0;
+    }
+
+    for (unsigned r = 1; r < frame->num_subtitle_areas; r++)
+        av_free(frame->subtitle_areas[r]);
+
+    frame->num_subtitle_areas = 1;
+    area = frame->subtitle_areas[0];
+
+    image = ass_render_frame(context->renderer, context->track, start_time + duration / 2, NULL);
+    if (image == NULL) {
+        av_log(NULL, AV_LOG_WARNING, "failed to render ass: %s\n", area->ass);
+        return 0;
+    }
+
+    // TODO: Split into multiple bitmaps
+
+    ass_image_to_area_palletization(context, image, area);
+    area->type = AV_SUBTITLE_FMT_BITMAP;
+
+    av_log(NULL, AV_LOG_DEBUG, "successfully rendered ass: %s\n", area->ass);
+
+    frame->width = context->size.width;
+    frame->height = context->size.height;
+    frame->format = AV_SUBTITLE_FMT_BITMAP;
+    return ff_filter_frame(outlink, frame);
+}
+
+static av_cold void uninit(AVFilterContext *ctx)
+{
+    Text2GraphicSubContext *context = ctx->priv;
+    free_palettizecontext(&context->palettize_context);
+    if (context->track) ass_free_track(context->track);
+    if (context->renderer) ass_renderer_done(context->renderer);
+    if (context->library) ass_library_done(context->library);
+    context->track = NULL;
+    context->renderer = NULL;
+    context->library = NULL;
+}
+
+// Copied from sf_graphicsub2text, with formats swapped
+static int query_formats(AVFilterContext *ctx)
+{
+    AVFilterFormats *formats, *formats2;
+    AVFilterLink *inlink = ctx->inputs[0];
+    AVFilterLink *outlink = ctx->outputs[0];
+    static const enum AVSubtitleType in_fmts[] = { AV_SUBTITLE_FMT_ASS, AV_SUBTITLE_FMT_NONE };
+    static const enum AVSubtitleType out_fmts[] = { AV_SUBTITLE_FMT_BITMAP, AV_SUBTITLE_FMT_NONE };
+    int ret;
+
+    /* set input format */
+    formats = ff_make_format_list(in_fmts);
+    if ((ret = ff_formats_ref(formats, &inlink->outcfg.formats)) < 0)
+        return ret;
+
+    /* set output format */
+    formats2 = ff_make_format_list(out_fmts);
+    if ((ret = ff_formats_ref(formats2, &outlink->incfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+// Filter structures
+
+#define OFFSET(x) offsetof(Text2GraphicSubContext, x)
+#define FLAGS (AV_OPT_FLAG_SUBTITLE_PARAM|AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption text2graphicsub_options[] = {
+        { "s",           "output size",                          OFFSET(size),        AV_OPT_TYPE_IMAGE_SIZE,                         .default_val.str = NULL, .flags = FLAGS },
+        { "size",        "output size",                          OFFSET(size),        AV_OPT_TYPE_IMAGE_SIZE,                         .default_val.str = NULL, .flags = FLAGS },
+        { "n",           "number of output colors",              OFFSET(num_colors),  AV_OPT_TYPE_INT,        .min = 2, .max = 256,   .default_val.i64 = 16,   .flags = FLAGS },
+        { "num_colors",  "number of output colors",              OFFSET(num_colors),  AV_OPT_TYPE_INT,        .min = 2, .max = 256,   .default_val.i64 = 16,   .flags = FLAGS },
+        { "ss",          "strip animations and blur styles",     OFFSET(stripstyles), AV_OPT_TYPE_BOOL,       .min = 0, .max = 1,     .default_val.i64 = 1,    .flags = FLAGS },
+        { "stripstyles", "strip animations and blur styles",     OFFSET(stripstyles), AV_OPT_TYPE_BOOL,       .min = 0, .max = 1,     .default_val.i64 = 1,    .flags = FLAGS },
+        { "force_style", "enforce subtitle styles",              OFFSET(force_style), AV_OPT_TYPE_STRING,                             .default_val.str = NULL, .flags = FLAGS },
+        { "f",           "media file from which to load fonts",  OFFSET(filename),    AV_OPT_TYPE_STRING,                             .default_val.str = NULL, .flags = FLAGS },
+        { "filename",    "media file from which to load fonts",  OFFSET(filename),    AV_OPT_TYPE_STRING,                             .default_val.str = NULL, .flags = FLAGS },
+        { "fd",          "fonts directory",                      OFFSET(fontsdir),    AV_OPT_TYPE_STRING,                             .default_val.str = NULL, .flags = FLAGS },
+        { "fontsdir",    "fonts directory",                      OFFSET(fontsdir),    AV_OPT_TYPE_STRING,                             .default_val.str = NULL, .flags = FLAGS },
+        { .name =  NULL }
+};
+
+static const AVClass text2graphicsub_class = {
+        .class_name       = "text2graphicsub",
+        .item_name        = av_default_item_name,
+        .option           = text2graphicsub_options,
+        .version          = LIBAVUTIL_VERSION_INT,
+        .category         = AV_CLASS_CATEGORY_FILTER,
+};
+
+static const AVFilterPad inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .filter_frame = filter_frame,
+        .config_props = config_input,
+    },
+};
+
+static const AVFilterPad outputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .config_props = config_output,
+    },
+};
+
+const AVFilter ff_sf_text2graphicsub = {
+        .name            = "text2graphicsub",
+        .description     = NULL_IF_CONFIG_SMALL("Convert text subtitles to bitmap subtitles."),
+        .init            = init,
+        .uninit          = uninit,
+        .priv_size       = sizeof(Text2GraphicSubContext),
+        .priv_class      = &text2graphicsub_class,
+        FILTER_INPUTS(inputs),
+        FILTER_OUTPUTS(outputs),
+        FILTER_QUERY_FUNC(query_formats)
+};
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v6 22/25] avfilter/snull, strim: Add snull and strim filters
  2022-06-26 16:34         ` [FFmpeg-devel] [PATCH v6 00/25] Subtitle Filtering 2022 ffmpegagent
                             ` (20 preceding siblings ...)
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 21/25] avfilter/text2graphicsub: Added text2graphicsub subtitle filter softworkz
@ 2022-06-26 16:35           ` softworkz
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 23/25] avcodec/subtitles: Migrate subtitle encoders to frame-based API softworkz
                             ` (2 subsequent siblings)
  24 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-26 16:35 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 configure                |  2 +-
 libavfilter/Makefile     |  2 ++
 libavfilter/allfilters.c |  2 ++
 libavfilter/sf_snull.c   | 50 +++++++++++++++++++++++++++++++++
 libavfilter/trim.c       | 60 +++++++++++++++++++++++++++++++++++++++-
 5 files changed, 114 insertions(+), 2 deletions(-)
 create mode 100644 libavfilter/sf_snull.c

diff --git a/configure b/configure
index f74077b439..285bdf958e 100755
--- a/configure
+++ b/configure
@@ -3817,7 +3817,7 @@ avutil_extralibs="d3d11va_extralibs nanosleep_extralibs pthreads_extralibs vaapi
 # programs
 ffmpeg_deps="avcodec avfilter avformat"
 ffmpeg_select="aformat_filter anull_filter atrim_filter format_filter
-               hflip_filter null_filter
+               snull_filter strim_filter hflip_filter null_filter
                transpose_filter trim_filter vflip_filter"
 ffmpeg_suggest="ole32 psapi shell32"
 ffplay_deps="avcodec avformat swscale swresample sdl2"
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index adbc16a5a9..697eee57d2 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -581,7 +581,9 @@ OBJS-$(CONFIG_NULLSINK_FILTER)               += vsink_nullsink.o
 # subtitle filters
 OBJS-$(CONFIG_CENSOR_FILTER)                 += sf_textmod.o
 OBJS-$(CONFIG_SHOW_SPEAKER_FILTER)           += sf_textmod.o
+OBJS-$(CONFIG_SNULL_FILTER)                  += sf_snull.o
 OBJS-$(CONFIG_SPLITCC_FILTER)                += sf_splitcc.o
+OBJS-$(CONFIG_STRIM_FILTER)                  += trim.o
 OBJS-$(CONFIG_STRIPSTYLES_FILTER)            += sf_stripstyles.o
 OBJS-$(CONFIG_SUBFEED_FILTER)                += sf_subfeed.o
 OBJS-$(CONFIG_SUBSCALE_FILTER)               += sf_subscale.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 82297e9657..fa7bee3bd8 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -573,7 +573,9 @@ extern const AVFilter ff_avsrc_movie;
 extern const AVFilter ff_sf_censor;
 extern const AVFilter ff_sf_graphicsub2text;
 extern const AVFilter ff_sf_showspeaker;
+extern const AVFilter ff_sf_snull;
 extern const AVFilter ff_sf_splitcc;
+extern const AVFilter ff_sf_strim;
 extern const AVFilter ff_sf_stripstyles;
 extern const AVFilter ff_sf_subfeed;
 extern const AVFilter ff_sf_subscale;
diff --git a/libavfilter/sf_snull.c b/libavfilter/sf_snull.c
new file mode 100644
index 0000000000..064c8d5c3a
--- /dev/null
+++ b/libavfilter/sf_snull.c
@@ -0,0 +1,50 @@
+/*
+ * 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
+ * null subtitle filter
+ */
+
+#include "avfilter.h"
+#include "internal.h"
+#include "libavutil/internal.h"
+
+static const AVFilterPad avfilter_sf_snull_inputs[] = {
+    {
+        .name = "default",
+        .type = AVMEDIA_TYPE_SUBTITLE,
+    },
+};
+
+static const AVFilterPad avfilter_sf_snull_outputs[] = {
+    {
+        .name = "default",
+        .type = AVMEDIA_TYPE_SUBTITLE,
+    },
+};
+
+const AVFilter ff_sf_snull = {
+    .name          = "snull",
+    .description   = NULL_IF_CONFIG_SMALL("Pass the source unchanged to the output."),
+    .flags         = AVFILTER_FLAG_METADATA_ONLY,
+    FILTER_INPUTS(avfilter_sf_snull_inputs),
+    FILTER_OUTPUTS(avfilter_sf_snull_outputs),
+};
diff --git a/libavfilter/trim.c b/libavfilter/trim.c
index ee6e821cd2..a25366497b 100644
--- a/libavfilter/trim.c
+++ b/libavfilter/trim.c
@@ -83,7 +83,7 @@ static int config_input(AVFilterLink *inlink)
 {
     AVFilterContext *ctx = inlink->dst;
     TrimContext       *s = ctx->priv;
-    AVRational tb = (inlink->type == AVMEDIA_TYPE_VIDEO) ?
+    AVRational tb = (inlink->type != AVMEDIA_TYPE_AUDIO) ?
                      inlink->time_base : (AVRational){ 1, inlink->sample_rate };
 
     if (s->start_time != INT64_MAX) {
@@ -369,3 +369,61 @@ const AVFilter ff_af_atrim = {
     FILTER_OUTPUTS(atrim_outputs),
 };
 #endif // CONFIG_ATRIM_FILTER
+
+#if CONFIG_STRIM_FILTER
+
+static int sconfig_output(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    AVFilterLink *inlink = ctx->inputs[0];
+
+    outlink->format = inlink->format;
+    outlink->w = inlink->w;
+    outlink->h = inlink->h;
+
+    return 0;
+}
+
+
+#define FLAGS (AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
+static const AVOption strim_options[] = {
+    COMMON_OPTS
+    { "start_frame", "Number of the first frame that should be passed "
+        "to the output",                                                 OFFSET(start_frame), AV_OPT_TYPE_INT64,  { .i64 = -1 },       -1, INT64_MAX, FLAGS },
+    { "end_frame",   "Number of the first frame that should be dropped "
+        "again",                                                         OFFSET(end_frame),   AV_OPT_TYPE_INT64,  { .i64 = INT64_MAX }, 0, INT64_MAX, FLAGS },
+    { NULL }
+};
+#undef FLAGS
+
+AVFILTER_DEFINE_CLASS(strim);
+
+static const AVFilterPad strim_inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .filter_frame = trim_filter_frame,
+        .config_props = config_input,
+    },
+};
+
+static const AVFilterPad strim_outputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .config_props = sconfig_output,
+    },
+};
+
+const AVFilter ff_sf_strim = {
+    .name        = "strim",
+    .description = NULL_IF_CONFIG_SMALL("Pick one continuous section from the input, drop the rest."),
+    .init        = init,
+    .priv_size   = sizeof(TrimContext),
+    .priv_class  = &strim_class,
+    .flags       = AVFILTER_FLAG_METADATA_ONLY,
+    FILTER_INPUTS(strim_inputs),
+    FILTER_OUTPUTS(strim_outputs),
+};
+#endif // CONFIG_STRIM_FILTER
+
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v6 23/25] avcodec/subtitles: Migrate subtitle encoders to frame-based API
  2022-06-26 16:34         ` [FFmpeg-devel] [PATCH v6 00/25] Subtitle Filtering 2022 ffmpegagent
                             ` (21 preceding siblings ...)
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 22/25] avfilter/snull, strim: Add snull and strim filters softworkz
@ 2022-06-26 16:35           ` softworkz
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 24/25] fftools/ffmpeg: Introduce subtitle filtering and new frame-based subtitle encoding softworkz
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 25/25] avcodec/dvbsubdec: Fix conditions for fallback to default resolution softworkz
  24 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-26 16:35 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

and provide a compatibility shim for the legacy api

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/assenc.c         | 189 ++++++++++++++++++++++++++++++------
 libavcodec/avcodec.h        |   5 +-
 libavcodec/codec_internal.h |  12 ---
 libavcodec/dvbsubenc.c      |  96 ++++++++++--------
 libavcodec/dvdsubenc.c      | 102 +++++++++++--------
 libavcodec/encode.c         |  61 +++++++++++-
 libavcodec/movtextenc.c     | 114 ++++++++++++++++------
 libavcodec/srtenc.c         | 108 ++++++++++++++-------
 libavcodec/tests/avcodec.c  |   5 +-
 libavcodec/ttmlenc.c        | 101 ++++++++++++++-----
 libavcodec/utils.c          |   1 -
 libavcodec/webvttenc.c      |  86 +++++++++++-----
 libavcodec/xsubenc.c        |  88 ++++++++++-------
 13 files changed, 688 insertions(+), 280 deletions(-)

diff --git a/libavcodec/assenc.c b/libavcodec/assenc.c
index 391d690133..5067244e77 100644
--- a/libavcodec/assenc.c
+++ b/libavcodec/assenc.c
@@ -25,69 +25,194 @@
 
 #include "avcodec.h"
 #include "codec_internal.h"
+#include "encode.h"
 #include "libavutil/ass_internal.h"
 #include "libavutil/avstring.h"
 #include "libavutil/internal.h"
 #include "libavutil/mem.h"
 
+typedef struct {
+    AVCodecContext *avctx;
+    AVFrame* current_frame;
+    int have_frame;
+    int current_area;
+} AssEncContext;
+
+static void check_write_header(AVCodecContext* avctx, const AVFrame* frame)
+{
+    if (avctx->extradata_size)
+        return;
+
+    if (frame->subtitle_header && frame->subtitle_header->size > 0) {
+        const char* subtitle_header = (char*)frame->subtitle_header->data;
+        avctx->extradata_size = strlen(subtitle_header);
+        avctx->extradata = av_mallocz(frame->subtitle_header->size + 1);
+        memcpy(avctx->extradata, subtitle_header, avctx->extradata_size);
+        avctx->extradata[avctx->extradata_size] = 0;
+    }
+
+    if (!avctx->extradata_size) {
+        const char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+        if (!subtitle_header)
+            return;
+
+        avctx->extradata_size = strlen(subtitle_header);
+        avctx->extradata = av_mallocz(avctx->extradata_size + 1);
+        memcpy(avctx->extradata, subtitle_header, avctx->extradata_size);
+        avctx->extradata[avctx->extradata_size] = 0;
+        av_freep(&subtitle_header);
+    }
+}
+
 static av_cold int ass_encode_init(AVCodecContext *avctx)
 {
-    avctx->extradata = av_malloc(avctx->subtitle_header_size + 1);
-    if (!avctx->extradata)
-        return AVERROR(ENOMEM);
-    memcpy(avctx->extradata, avctx->subtitle_header, avctx->subtitle_header_size);
-    avctx->extradata_size = avctx->subtitle_header_size;
-    avctx->extradata[avctx->extradata_size] = 0;
+    AssEncContext *s = avctx->priv_data;
+
+    if (avctx->subtitle_header_size) {
+        avctx->extradata = av_malloc(avctx->subtitle_header_size + 1);
+        if (!avctx->extradata)
+            return AVERROR(ENOMEM);
+        memcpy(avctx->extradata, avctx->subtitle_header, avctx->subtitle_header_size);
+        avctx->extradata_size                   = avctx->subtitle_header_size;
+        avctx->extradata[avctx->extradata_size] = 0;
+    }
+
+    s->current_frame = av_frame_alloc();
+    return 0;
+}
+
+static av_cold int ass_encode_close(AVCodecContext *avctx)
+{
+    AssEncContext *s = avctx->priv_data;
+    av_frame_free(&s->current_frame);
     return 0;
 }
 
-static int ass_encode_frame(AVCodecContext *avctx,
-                            unsigned char *buf, int bufsize,
-                            const AVSubtitle *sub)
+////static int ass_encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+////                            const AVFrame* frame, int* got_packet)
+////{
+////    int ret;
+////    size_t req_len = 0, total_len = 0;
+////
+////    check_write_header(avctx, frame);
+////
+////    for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+////        const char *ass = frame->subtitle_areas[i]->ass;
+////
+////        if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) {
+////            av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
+////            return AVERROR(EINVAL);
+////        }
+////
+////        if (ass)
+////            req_len += strlen(ass);
+////    }
+////
+////    ret = ff_get_encode_buffer(avctx, avpkt, req_len + 1, 0);
+////    if (ret < 0) {
+////        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+////        return ret;
+////    }
+////
+////    for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+////        const char *ass = frame->subtitle_areas[i]->ass;
+////
+////        if (ass) {
+////            size_t len = av_strlcpy((char *)avpkt->data + total_len, ass, avpkt->size - total_len);
+////            total_len += len;
+////        }
+////    }
+////
+////    avpkt->size = total_len;
+////    *got_packet = total_len > 0;
+////
+////    return 0;
+////}
+
+static int ass_receive_packet(AVCodecContext *avctx, AVPacket *avpkt)
 {
-    int i, len, total_len = 0;
+    AssEncContext *s = avctx->priv_data;
+    int ret;
+
+    if (!s->have_frame) {
+        s->current_area = 0;
+        ret = ff_encode_get_frame(avctx, s->current_frame);
+
+        if (ret < 0) {
+            av_frame_unref(s->current_frame);
+            return ret;
+        }
+
+        s->have_frame = 1;
+    }
 
-    for (i=0; i<sub->num_rects; i++) {
-        const char *ass = sub->rects[i]->ass;
+    check_write_header(avctx, s->current_frame);
 
-        if (sub->rects[i]->type != SUBTITLE_ASS) {
-            av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
+    if (s->current_frame->repeat_sub) {
+        av_frame_unref(s->current_frame);
+        s->have_frame = 0;
+        return AVERROR(EAGAIN);
+    }
+
+    if (s->current_area < s->current_frame->num_subtitle_areas) {
+        const AVSubtitleArea *area = s->current_frame->subtitle_areas[s->current_area];
+        const char *ass = area->ass;
+
+        if (area->type != AV_SUBTITLE_FMT_ASS) {
+            av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
             return AVERROR(EINVAL);
         }
 
-        len = av_strlcpy(buf+total_len, ass, bufsize-total_len);
+        if (ass) {
+            size_t len = strlen(ass);
+
+            ret = ff_get_encode_buffer(avctx, avpkt, len + 1, 0);
+            if (ret < 0) {
+                av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+                return ret;
+            }
+
+            len = av_strlcpy((char *)avpkt->data, ass, avpkt->size);
 
-        if (len > bufsize-total_len-1) {
-            av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n");
-            return AVERROR_BUFFER_TOO_SMALL;
+            avpkt->size = len;
         }
 
-        total_len += len;
+        s->current_area++;
     }
 
-    return total_len;
+    if (s->current_area < s->current_frame->num_subtitle_areas)
+        return 0;
+
+    av_frame_unref(s->current_frame);
+    s->have_frame = 0;
+
+    return 0;
 }
 
 #if CONFIG_SSA_ENCODER
 const FFCodec ff_ssa_encoder = {
-    .p.name       = "ssa",
-    .p.long_name  = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"),
-    .p.type       = AVMEDIA_TYPE_SUBTITLE,
-    .p.id         = AV_CODEC_ID_ASS,
-    .init         = ass_encode_init,
-    FF_CODEC_ENCODE_SUB_CB(ass_encode_frame),
+    .p.name           = "ssa",
+    .p.long_name      = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"),
+    .p.type           = AVMEDIA_TYPE_SUBTITLE,
+    .p.id             = AV_CODEC_ID_ASS,
+    .priv_data_size = sizeof(AssEncContext),
+    .init           = ass_encode_init,
+    .close          = ass_encode_close,
+    FF_CODEC_RECEIVE_PACKET_CB(ass_receive_packet),
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
 };
 #endif
 
 #if CONFIG_ASS_ENCODER
 const FFCodec ff_ass_encoder = {
-    .p.name       = "ass",
-    .p.long_name  = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"),
-    .p.type       = AVMEDIA_TYPE_SUBTITLE,
-    .p.id         = AV_CODEC_ID_ASS,
-    .init         = ass_encode_init,
-    FF_CODEC_ENCODE_SUB_CB(ass_encode_frame),
+    .p.name           = "ass",
+    .p.long_name      = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"),
+    .p.type           = AVMEDIA_TYPE_SUBTITLE,
+    .priv_data_size = sizeof(AssEncContext),
+    .p.id             = AV_CODEC_ID_ASS,
+    .init           = ass_encode_init,
+    .close          = ass_encode_close,
+    FF_CODEC_RECEIVE_PACKET_CB(ass_receive_packet),
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
 };
 #endif
diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index de87b0406b..8a8efe6e4b 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -3014,10 +3014,13 @@ void av_parser_close(AVCodecParserContext *s);
  * @{
  */
 
+ /**
+  * @deprecated Use @ref avcodec_encode_subtitle2() instead.
+  */
+attribute_deprecated
 int avcodec_encode_subtitle(AVCodecContext *avctx, uint8_t *buf, int buf_size,
                             const AVSubtitle *sub);
 
-
 /**
  * @}
  */
diff --git a/libavcodec/codec_internal.h b/libavcodec/codec_internal.h
index 5df286ce52..7223f41757 100644
--- a/libavcodec/codec_internal.h
+++ b/libavcodec/codec_internal.h
@@ -101,9 +101,6 @@ enum FFCodecType {
     /* The codec is an encoder using the encode callback;
      * audio and video codecs only. */
     FF_CODEC_CB_TYPE_ENCODE,
-    /* The codec is an encoder using the encode_sub callback;
-     * subtitle codecs only. */
-    FF_CODEC_CB_TYPE_ENCODE_SUB,
     /* The codec is an encoder using the receive_packet callback;
      * audio and video codecs only. */
     FF_CODEC_CB_TYPE_RECEIVE_PACKET,
@@ -206,12 +203,6 @@ typedef struct FFCodec {
          */
         int (*encode)(struct AVCodecContext *avctx, struct AVPacket *avpkt,
                       const struct AVFrame *frame, int *got_packet_ptr);
-        /**
-         * Encode subtitles to a raw buffer.
-         * cb is in this state if cb_type is FF_CODEC_CB_TYPE_ENCODE_SUB.
-         */
-        int (*encode_sub)(struct AVCodecContext *avctx, uint8_t *buf,
-                          int buf_size, const struct AVSubtitle *sub);
         /**
          * Encode API with decoupled frame/packet dataflow.
          * cb is in this state if cb_type is FF_CODEC_CB_TYPE_RECEIVE_PACKET.
@@ -263,9 +254,6 @@ typedef struct FFCodec {
 #define FF_CODEC_ENCODE_CB(func)                          \
     .cb_type           = FF_CODEC_CB_TYPE_ENCODE,         \
     .cb.encode         = (func)
-#define FF_CODEC_ENCODE_SUB_CB(func)                      \
-    .cb_type           = FF_CODEC_CB_TYPE_ENCODE_SUB,     \
-    .cb.encode_sub     = (func)
 #define FF_CODEC_RECEIVE_PACKET_CB(func)                  \
     .cb_type           = FF_CODEC_CB_TYPE_RECEIVE_PACKET, \
     .cb.receive_packet = (func)
diff --git a/libavcodec/dvbsubenc.c b/libavcodec/dvbsubenc.c
index 06087b058d..3f4655bebe 100644
--- a/libavcodec/dvbsubenc.c
+++ b/libavcodec/dvbsubenc.c
@@ -21,6 +21,7 @@
 #include "avcodec.h"
 #include "bytestream.h"
 #include "codec_internal.h"
+#include "encode.h"
 #include "libavutil/colorspace.h"
 
 typedef struct DVBSubtitleContext {
@@ -269,21 +270,29 @@ static int dvb_encode_rle8(uint8_t **pq, int buf_size,
     return len;
 }
 
-static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
-                         const AVSubtitle *h)
+static int dvbsub_encode(AVCodecContext* avctx, AVPacket* avpkt,
+                         const AVFrame* frame, int* got_packet)
 {
     DVBSubtitleContext *s = avctx->priv_data;
     uint8_t *q, *pseg_len;
     int page_id, region_id, clut_id, object_id, i, bpp_index, page_state;
-
-
-    q = outbuf;
+    size_t buf_size;
+    int ret;
 
     page_id = 1;
 
-    if (h->num_rects && !h->rects)
+    if (frame->num_subtitle_areas && !frame->subtitle_areas)
         return AVERROR(EINVAL);
 
+    ret = ff_get_encode_buffer(avctx, avpkt, 1024 * 1024, 0);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+        return ret;
+    }
+
+    buf_size = avpkt->size;
+    q = avpkt->data;
+
     if (avctx->width > 0 && avctx->height > 0) {
         if (buf_size < 11)
             return AVERROR_BUFFER_TOO_SMALL;
@@ -302,7 +311,7 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
 
     /* page composition segment */
 
-    if (buf_size < 8 + h->num_rects * 6)
+    if (buf_size < 8 + frame->num_subtitle_areas * 6)
         return AVERROR_BUFFER_TOO_SMALL;
     *q++ = 0x0f; /* sync_byte */
     *q++ = 0x10; /* segment_type */
@@ -314,30 +323,30 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
     /* page_version = 0 + page_state */
     *q++ = (s->object_version << 4) | (page_state << 2) | 3;
 
-    for (region_id = 0; region_id < h->num_rects; region_id++) {
+    for (region_id = 0; region_id < frame->num_subtitle_areas; region_id++) {
         *q++ = region_id;
         *q++ = 0xff; /* reserved */
-        bytestream_put_be16(&q, h->rects[region_id]->x); /* left pos */
-        bytestream_put_be16(&q, h->rects[region_id]->y); /* top pos */
+        bytestream_put_be16(&q, frame->subtitle_areas[region_id]->x); /* left pos */
+        bytestream_put_be16(&q, frame->subtitle_areas[region_id]->y); /* top pos */
     }
 
     bytestream_put_be16(&pseg_len, q - pseg_len - 2);
-    buf_size -= 8 + h->num_rects * 6;
+    buf_size -= 8 + frame->num_subtitle_areas * 6;
 
-    if (h->num_rects) {
-        for (clut_id = 0; clut_id < h->num_rects; clut_id++) {
-            if (buf_size < 6 + h->rects[clut_id]->nb_colors * 6)
+    if (frame->num_subtitle_areas) {
+        for (clut_id = 0; clut_id < frame->num_subtitle_areas; clut_id++) {
+            if (buf_size < 6 + frame->subtitle_areas[clut_id]->nb_colors * 6)
                 return AVERROR_BUFFER_TOO_SMALL;
 
             /* CLUT segment */
 
-            if (h->rects[clut_id]->nb_colors <= 4) {
+            if (frame->subtitle_areas[clut_id]->nb_colors <= 4) {
                 /* 2 bpp, some decoders do not support it correctly */
                 bpp_index = 0;
-            } else if (h->rects[clut_id]->nb_colors <= 16) {
+            } else if (frame->subtitle_areas[clut_id]->nb_colors <= 16) {
                 /* 4 bpp, standard encoding */
                 bpp_index = 1;
-            } else if (h->rects[clut_id]->nb_colors <= 256) {
+            } else if (frame->subtitle_areas[clut_id]->nb_colors <= 256) {
                 /* 8 bpp, standard encoding */
                 bpp_index = 2;
             } else {
@@ -354,12 +363,12 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
             *q++ = clut_id;
             *q++ = (0 << 4) | 0xf; /* version = 0 */
 
-            for(i = 0; i < h->rects[clut_id]->nb_colors; i++) {
+            for(i = 0; i < frame->subtitle_areas[clut_id]->nb_colors; i++) {
                 *q++ = i; /* clut_entry_id */
                 *q++ = (1 << (7 - bpp_index)) | (0xf << 1) | 1; /* 2 bits/pixel full range */
                 {
                     int a, r, g, b;
-                    uint32_t x= ((uint32_t*)h->rects[clut_id]->data[1])[i];
+                    uint32_t x= ((uint32_t*)frame->subtitle_areas[clut_id]->pal)[i];
                     a = (x >> 24) & 0xff;
                     r = (x >> 16) & 0xff;
                     g = (x >>  8) & 0xff;
@@ -373,22 +382,22 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
             }
 
             bytestream_put_be16(&pseg_len, q - pseg_len - 2);
-            buf_size -= 6 + h->rects[clut_id]->nb_colors * 6;
+            buf_size -= 6 + frame->subtitle_areas[clut_id]->nb_colors * 6;
         }
 
-        if (buf_size < h->num_rects * 22)
+        if (buf_size < frame->num_subtitle_areas * 22)
             return AVERROR_BUFFER_TOO_SMALL;
-        for (region_id = 0; region_id < h->num_rects; region_id++) {
+        for (region_id = 0; region_id < frame->num_subtitle_areas; region_id++) {
 
             /* region composition segment */
 
-            if (h->rects[region_id]->nb_colors <= 4) {
+            if (frame->subtitle_areas[region_id]->nb_colors <= 4) {
                 /* 2 bpp, some decoders do not support it correctly */
                 bpp_index = 0;
-            } else if (h->rects[region_id]->nb_colors <= 16) {
+            } else if (frame->subtitle_areas[region_id]->nb_colors <= 16) {
                 /* 4 bpp, standard encoding */
                 bpp_index = 1;
-            } else if (h->rects[region_id]->nb_colors <= 256) {
+            } else if (frame->subtitle_areas[region_id]->nb_colors <= 256) {
                 /* 8 bpp, standard encoding */
                 bpp_index = 2;
             } else {
@@ -402,8 +411,8 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
             q += 2; /* segment length */
             *q++ = region_id;
             *q++ = (s->object_version << 4) | (0 << 3) | 0x07; /* version , no fill */
-            bytestream_put_be16(&q, h->rects[region_id]->w); /* region width */
-            bytestream_put_be16(&q, h->rects[region_id]->h); /* region height */
+            bytestream_put_be16(&q, frame->subtitle_areas[region_id]->w); /* region width */
+            bytestream_put_be16(&q, frame->subtitle_areas[region_id]->h); /* region height */
             *q++ = ((1 + bpp_index) << 5) | ((1 + bpp_index) << 2) | 0x03;
             *q++ = region_id; /* clut_id == region_id */
             *q++ = 0; /* 8 bit fill colors */
@@ -417,9 +426,9 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
 
             bytestream_put_be16(&pseg_len, q - pseg_len - 2);
         }
-        buf_size -= h->num_rects * 22;
+        buf_size -= frame->num_subtitle_areas * 22;
 
-        for (object_id = 0; object_id < h->num_rects; object_id++) {
+        for (object_id = 0; object_id < frame->num_subtitle_areas; object_id++) {
             int (*dvb_encode_rle)(uint8_t **pq, int buf_size,
                                   const uint8_t *bitmap, int linesize,
                                   int w, int h);
@@ -428,13 +437,13 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
                 return AVERROR_BUFFER_TOO_SMALL;
 
             /* bpp_index maths */
-            if (h->rects[object_id]->nb_colors <= 4) {
+            if (frame->subtitle_areas[object_id]->nb_colors <= 4) {
                 /* 2 bpp, some decoders do not support it correctly */
                 dvb_encode_rle = dvb_encode_rle2;
-            } else if (h->rects[object_id]->nb_colors <= 16) {
+            } else if (frame->subtitle_areas[object_id]->nb_colors <= 16) {
                 /* 4 bpp, standard encoding */
                 dvb_encode_rle = dvb_encode_rle4;
-            } else if (h->rects[object_id]->nb_colors <= 256) {
+            } else if (frame->subtitle_areas[object_id]->nb_colors <= 256) {
                 /* 8 bpp, standard encoding */
                 dvb_encode_rle = dvb_encode_rle8;
             } else {
@@ -464,19 +473,19 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
 
                 top_ptr = q;
                 ret = dvb_encode_rle(&q, buf_size,
-                                     h->rects[object_id]->data[0],
-                                     h->rects[object_id]->w * 2,
-                                     h->rects[object_id]->w,
-                                     h->rects[object_id]->h >> 1);
+                                     frame->subtitle_areas[object_id]->buf[0]->data,
+                                     frame->subtitle_areas[object_id]->w * 2,
+                                     frame->subtitle_areas[object_id]->w,
+                                     frame->subtitle_areas[object_id]->h >> 1);
                 if (ret < 0)
                     return ret;
                 buf_size -= ret;
                 bottom_ptr = q;
                 ret = dvb_encode_rle(&q, buf_size,
-                                     h->rects[object_id]->data[0] + h->rects[object_id]->w,
-                                     h->rects[object_id]->w * 2,
-                                     h->rects[object_id]->w,
-                                     h->rects[object_id]->h >> 1);
+                                     frame->subtitle_areas[object_id]->buf[0]->data + frame->subtitle_areas[object_id]->w,
+                                     frame->subtitle_areas[object_id]->w * 2,
+                                     frame->subtitle_areas[object_id]->w,
+                                     frame->subtitle_areas[object_id]->h >> 1);
                 if (ret < 0)
                     return ret;
                 buf_size -= ret;
@@ -503,7 +512,10 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
     buf_size -= 6;
 
     s->object_version = (s->object_version + 1) & 0xf;
-    return q - outbuf;
+    avpkt->size = q - avpkt->data;
+    *got_packet = 1;
+
+    return 0;
 }
 
 const FFCodec ff_dvbsub_encoder = {
@@ -512,5 +524,5 @@ const FFCodec ff_dvbsub_encoder = {
     .p.type         = AVMEDIA_TYPE_SUBTITLE,
     .p.id           = AV_CODEC_ID_DVB_SUBTITLE,
     .priv_data_size = sizeof(DVBSubtitleContext),
-    FF_CODEC_ENCODE_SUB_CB(dvbsub_encode),
+    FF_CODEC_ENCODE_CB(dvbsub_encode),
 };
diff --git a/libavcodec/dvdsubenc.c b/libavcodec/dvdsubenc.c
index 24da94faee..d8a86ee098 100644
--- a/libavcodec/dvdsubenc.c
+++ b/libavcodec/dvdsubenc.c
@@ -21,6 +21,7 @@
 #include "avcodec.h"
 #include "bytestream.h"
 #include "codec_internal.h"
+#include "encode.h"
 #include "internal.h"
 #include "libavutil/avassert.h"
 #include "libavutil/bprint.h"
@@ -115,15 +116,14 @@ static int color_distance(uint32_t a, uint32_t b)
  * Count colors used in a rectangle, quantizing alpha and grouping by
  * nearest global palette entry.
  */
-static void count_colors(AVCodecContext *avctx, unsigned hits[33],
-                         const AVSubtitleRect *r)
+static void count_colors(const AVCodecContext *avctx, unsigned hits[33],
+                         const AVSubtitleArea *r)
 {
     DVDSubtitleContext *dvdc = avctx->priv_data;
     unsigned count[256] = { 0 };
-    uint32_t *palette = (uint32_t *)r->data[1];
     uint32_t color;
     int x, y, i, j, match, d, best_d, av_uninit(best_j);
-    uint8_t *p = r->data[0];
+    uint8_t *p = r->buf[0]->data;
 
     for (y = 0; y < r->h; y++) {
         for (x = 0; x < r->w; x++)
@@ -133,7 +133,7 @@ static void count_colors(AVCodecContext *avctx, unsigned hits[33],
     for (i = 0; i < 256; i++) {
         if (!count[i]) /* avoid useless search */
             continue;
-        color = palette[i];
+        color = r->pal[i];
         /* 0: transparent, 1-16: semi-transparent, 17-33 opaque */
         match = color < 0x33000000 ? 0 : color < 0xCC000000 ? 1 : 17;
         if (match) {
@@ -233,13 +233,13 @@ static void build_color_map(AVCodecContext *avctx, int cmap[],
     }
 }
 
-static void copy_rectangle(AVSubtitleRect *dst, AVSubtitleRect *src, int cmap[])
+static void copy_rectangle(AVSubtitleArea*dst, AVSubtitleArea *src, int cmap[])
 {
     int x, y;
     uint8_t *p, *q;
 
-    p = src->data[0];
-    q = dst->data[0] + (src->x - dst->x) +
+    p = src->buf[0]->data;
+    q = dst->buf[0]->data + (src->x - dst->x) +
                             (src->y - dst->y) * dst->linesize[0];
     for (y = 0; y < src->h; y++) {
         for (x = 0; x < src->w; x++)
@@ -249,51 +249,57 @@ static void copy_rectangle(AVSubtitleRect *dst, AVSubtitleRect *src, int cmap[])
     }
 }
 
-static int encode_dvd_subtitles(AVCodecContext *avctx,
-                                uint8_t *outbuf, int outbuf_size,
-                                const AVSubtitle *h)
+static int encode_dvd_subtitles(AVCodecContext* avctx, AVPacket* avpkt,
+                                const AVFrame* frame, int* got_packet)
 {
     DVDSubtitleContext *dvdc = avctx->priv_data;
     uint8_t *q, *qq;
     int offset1, offset2;
-    int i, rects = h->num_rects, ret;
+    int ret = 0;
+    unsigned i, rects = frame->num_subtitle_areas;
     unsigned global_palette_hits[33] = { 0 };
     int cmap[256];
     int out_palette[4];
     int out_alpha[4];
-    AVSubtitleRect vrect;
-    uint8_t *vrect_data = NULL;
+    AVSubtitleArea vrect;
+    uint8_t* vrect_data = NULL, *outbuf;
     int x2, y2;
     int forced = 0;
+    int outbuf_size;
+    int64_t duration_ms;
 
-    if (rects == 0 || !h->rects)
+    if (rects == 0)
+        return 0;
+
+    if (!frame->subtitle_areas)
         return AVERROR(EINVAL);
+
     for (i = 0; i < rects; i++)
-        if (h->rects[i]->type != AV_SUBTITLE_FMT_BITMAP) {
+        if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_BITMAP) {
             av_log(avctx, AV_LOG_ERROR, "Bitmap subtitle required\n");
             return AVERROR(EINVAL);
         }
     /* Mark this subtitle forced if any of the rectangles is forced. */
     for (i = 0; i < rects; i++)
-        if ((h->rects[i]->flags & AV_SUBTITLE_FLAG_FORCED) != 0) {
+        if ((frame->subtitle_areas[i]->flags & AV_SUBTITLE_FLAG_FORCED) != 0) {
             forced = 1;
             break;
         }
 
-    vrect = *h->rects[0];
+    vrect = *frame->subtitle_areas[0];
 
     if (rects > 1) {
         /* DVD subtitles can have only one rectangle: build a virtual
            rectangle containing all actual rectangles.
            The data of the rectangles will be copied later, when the palette
            is decided, because the rectangles may have different palettes. */
-        int xmin = h->rects[0]->x, xmax = xmin + h->rects[0]->w;
-        int ymin = h->rects[0]->y, ymax = ymin + h->rects[0]->h;
+        int xmin = frame->subtitle_areas[0]->x, xmax = xmin + frame->subtitle_areas[0]->w;
+        int ymin = frame->subtitle_areas[0]->y, ymax = ymin + frame->subtitle_areas[0]->h;
         for (i = 1; i < rects; i++) {
-            xmin = FFMIN(xmin, h->rects[i]->x);
-            ymin = FFMIN(ymin, h->rects[i]->y);
-            xmax = FFMAX(xmax, h->rects[i]->x + h->rects[i]->w);
-            ymax = FFMAX(ymax, h->rects[i]->y + h->rects[i]->h);
+            xmin = FFMIN(xmin, frame->subtitle_areas[i]->x);
+            ymin = FFMIN(ymin, frame->subtitle_areas[i]->y);
+            xmax = FFMAX(xmax, frame->subtitle_areas[i]->x + frame->subtitle_areas[i]->w);
+            ymax = FFMAX(ymax, frame->subtitle_areas[i]->y + frame->subtitle_areas[i]->h);
         }
         vrect.x = xmin;
         vrect.y = ymin;
@@ -305,27 +311,29 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
         /* Count pixels outside the virtual rectangle as transparent */
         global_palette_hits[0] = vrect.w * vrect.h;
         for (i = 0; i < rects; i++)
-            global_palette_hits[0] -= h->rects[i]->w * h->rects[i]->h;
+            global_palette_hits[0] -= frame->subtitle_areas[i]->w * frame->subtitle_areas[i]->h;
     }
 
     for (i = 0; i < rects; i++)
-        count_colors(avctx, global_palette_hits, h->rects[i]);
+        count_colors(avctx, global_palette_hits, frame->subtitle_areas[i]);
     select_palette(avctx, out_palette, out_alpha, global_palette_hits);
 
     if (rects > 1) {
-        if (!(vrect_data = av_calloc(vrect.w, vrect.h)))
+
+        vrect.buf[0] = av_buffer_allocz((size_t)vrect.w * vrect.h);
+        if (!vrect.buf[0])
             return AVERROR(ENOMEM);
-        vrect.data    [0] = vrect_data;
+
         vrect.linesize[0] = vrect.w;
         for (i = 0; i < rects; i++) {
-            build_color_map(avctx, cmap, (uint32_t *)h->rects[i]->data[1],
+            build_color_map(avctx, cmap, frame->subtitle_areas[i]->pal,
                             out_palette, out_alpha);
-            copy_rectangle(&vrect, h->rects[i], cmap);
+            copy_rectangle(&vrect, frame->subtitle_areas[i], cmap);
         }
         for (i = 0; i < 4; i++)
             cmap[i] = i;
     } else {
-        build_color_map(avctx, cmap, (uint32_t *)h->rects[0]->data[1],
+        build_color_map(avctx, cmap, frame->subtitle_areas[0]->pal,
                         out_palette, out_alpha);
     }
 
@@ -336,6 +344,16 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
                out_palette[i], out_alpha[i] >> 4);
     av_log(avctx, AV_LOG_DEBUG, "\n");
 
+
+    ret = ff_get_encode_buffer(avctx, avpkt, 4 + vrect.w * vrect.h / 2 + 17 + 21, 0);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+        return ret;
+    }
+
+    outbuf_size = avpkt->size;
+    outbuf = avpkt->data;
+
     // encode data block
     q = outbuf + 4;
     offset1 = q - outbuf;
@@ -345,10 +363,10 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
         ret = AVERROR_BUFFER_TOO_SMALL;
         goto fail;
     }
-    dvd_encode_rle(&q, vrect.data[0], vrect.w * 2,
+    dvd_encode_rle(&q, vrect.buf[0]->data, vrect.w * 2,
                    vrect.w, (vrect.h + 1) >> 1, cmap);
     offset2 = q - outbuf;
-    dvd_encode_rle(&q, vrect.data[0] + vrect.w, vrect.w * 2,
+    dvd_encode_rle(&q, vrect.buf[0]->data + vrect.w, vrect.w * 2,
                    vrect.w, vrect.h >> 1, cmap);
 
     if (dvdc->even_rows_fix && (vrect.h & 1)) {
@@ -363,7 +381,7 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
     bytestream_put_be16(&qq, q - outbuf);
 
     // send start display command
-    bytestream_put_be16(&q, (h->start_display_time*90) >> 10);
+    bytestream_put_be16(&q, 0);
     bytestream_put_be16(&q, (q - outbuf) /*- 2 */ + 8 + 12 + 2);
     *q++ = 0x03; // palette - 4 nibbles
     *q++ = (out_palette[3] << 4) | out_palette[2];
@@ -401,7 +419,8 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
     *q++ = 0xff; // terminating command
 
     // send stop display command last
-    bytestream_put_be16(&q, (h->end_display_time*90) >> 10);
+    duration_ms = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, (AVRational){ 1, 1000 });
+    bytestream_put_be16(&q, (duration_ms*90) >> 10);
     bytestream_put_be16(&q, (q - outbuf) - 2 /*+ 4*/);
     *q++ = 0x02; // set end
     *q++ = 0xff; // terminating command
@@ -410,7 +429,9 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
     bytestream_put_be16(&qq, q - outbuf);
 
     av_log(NULL, AV_LOG_DEBUG, "subtitle_packet size=%"PTRDIFF_SPECIFIER"\n", q - outbuf);
-    ret = q - outbuf;
+    avpkt->size = q - outbuf;
+    ret = 0;
+    *got_packet = 1;
 
 fail:
     av_free(vrect_data);
@@ -474,14 +495,13 @@ static int dvdsub_init(AVCodecContext *avctx)
     return 0;
 }
 
-static int dvdsub_encode(AVCodecContext *avctx,
-                         unsigned char *buf, int buf_size,
-                         const AVSubtitle *sub)
+static int dvdsub_encode(struct AVCodecContext* avctx, struct AVPacket* avpkt,
+                         const struct AVFrame* frame, int* got_packet)
 {
     //DVDSubtitleContext *s = avctx->priv_data;
     int ret;
 
-    ret = encode_dvd_subtitles(avctx, buf, buf_size, sub);
+    ret = encode_dvd_subtitles(avctx, avpkt, frame, got_packet);
     return ret;
 }
 
@@ -506,7 +526,7 @@ const FFCodec ff_dvdsub_encoder = {
     .p.type         = AVMEDIA_TYPE_SUBTITLE,
     .p.id           = AV_CODEC_ID_DVD_SUBTITLE,
     .init           = dvdsub_init,
-    FF_CODEC_ENCODE_SUB_CB(dvdsub_encode),
+    FF_CODEC_ENCODE_CB(dvdsub_encode),
     .p.priv_class   = &dvdsubenc_class,
     .priv_data_size = sizeof(DVDSubtitleContext),
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
diff --git a/libavcodec/encode.c b/libavcodec/encode.c
index b68bf1e184..c96f5feb61 100644
--- a/libavcodec/encode.c
+++ b/libavcodec/encode.c
@@ -143,17 +143,70 @@ fail:
     return ret;
 }
 
-int avcodec_encode_subtitle(AVCodecContext *avctx, uint8_t *buf, int buf_size,
-                            const AVSubtitle *sub)
+int avcodec_encode_subtitle(AVCodecContext *avctx, uint8_t *buf, int buf_size, const AVSubtitle *sub)
 {
-    int ret;
+    int ret = 0;
+    AVFrame *frame = NULL;
+    AVPacket* avpkt = NULL;
+
     if (sub->start_display_time) {
         av_log(avctx, AV_LOG_ERROR, "start_display_time must be 0.\n");
         return -1;
     }
 
-    ret = ffcodec(avctx->codec)->cb.encode_sub(avctx, buf, buf_size, sub);
+    memset(buf, 0, buf_size);
+    // Create a temporary frame for calling the regular api:
+    frame = av_frame_alloc();
+    if (!frame) {
+        ret = AVERROR(ENOMEM);
+        goto exit;
+    }
+
+    frame->format = sub->format;
+    frame->type = AVMEDIA_TYPE_SUBTITLE;
+    ret = av_frame_get_buffer2(frame, 0);
+    if (ret < 0)
+        goto exit;
+
+    // Create a temporary packet
+    avpkt = av_packet_alloc();
+    if (!avpkt) {
+        ret = AVERROR(ENOMEM);
+        goto exit;
+    }
+
+    // Copy legacy subtitle data to temp frame
+    ret = ff_frame_put_subtitle(frame, sub);
+    if (ret < 0)
+        goto exit;
+
+    ret = avcodec_send_frame(avctx, frame);
+    if (ret < 0)
+        goto exit;
+
+    ret = avcodec_receive_packet(avctx, avpkt);
+
+    if (ret < 0 && ret != AVERROR(EAGAIN))
+        goto exit;
+
+    //ret = avctx->codec->encode2(avctx, avpkt, frame, &got_packet);
+
     avctx->frame_number++;
+
+    if (avpkt->size) {
+        if (avpkt->size > buf_size) {
+            ret = AVERROR_BUFFER_TOO_SMALL;
+            goto exit;
+        }
+
+        memcpy(buf, avpkt->data, avpkt->size);
+        ret = avpkt->size;
+    }
+
+exit:
+
+    av_packet_free(&avpkt);
+    av_frame_free(&frame);
     return ret;
 }
 
diff --git a/libavcodec/movtextenc.c b/libavcodec/movtextenc.c
index 6f0b7a8a5c..b9e0288403 100644
--- a/libavcodec/movtextenc.c
+++ b/libavcodec/movtextenc.c
@@ -30,6 +30,7 @@
 #include "libavutil/ass_internal.h"
 #include "bytestream.h"
 #include "codec_internal.h"
+#include "encode.h"
 
 #define STYLE_FLAG_BOLD         (1<<0)
 #define STYLE_FLAG_ITALIC       (1<<1)
@@ -73,6 +74,7 @@ typedef struct {
     AVCodecContext *avctx;
 
     ASSSplitContext *ass_ctx;
+    int is_default_ass_context;
     ASSStyle *ass_dialog_style;
     StyleBox *style_attributes;
     unsigned  count;
@@ -329,7 +331,7 @@ static av_cold int mov_text_encode_init(AVCodecContext *avctx)
 
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
 
-    s->ass_ctx = avpriv_ass_split(avctx->subtitle_header);
+    s->ass_ctx = avpriv_ass_split((char*)avctx->subtitle_header);
     if (!s->ass_ctx)
         return AVERROR_INVALIDDATA;
     ret = encode_sample_description(avctx);
@@ -633,56 +635,112 @@ static const ASSCodesCallbacks mov_text_callbacks = {
     .end              = mov_text_end_cb,
 };
 
-static int mov_text_encode_frame(AVCodecContext *avctx, unsigned char *buf,
-                                 int bufsize, const AVSubtitle *sub)
+static void ensure_ass_context(AVCodecContext* avctx, const AVFrame* frame)
+{
+    MovTextContext* s = avctx->priv_data;
+    int ret;
+
+    if (s->ass_ctx && !s->is_default_ass_context)
+        // We already have a (non-default context)
+        return;
+
+    if (!frame->num_subtitle_areas)
+        // Don't need ass context for processing empty subtitle frames
+        return;
+
+    // The frame has content, so we need to set up a context
+    if (frame->subtitle_header && frame->subtitle_header->size > 0) {
+        const char* subtitle_header = (char*)frame->subtitle_header->data;
+        avpriv_ass_split_free(s->ass_ctx);
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 0;
+    }
+    else if (!s->ass_ctx) {
+        char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+        if (!subtitle_header)
+            return;
+
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 1;
+        av_free(subtitle_header);
+    }
+
+    if (s->ass_ctx && !avctx->extradata_size) {
+        ret = encode_sample_description(avctx);
+        if (ret < 0)
+            av_log(avctx, AV_LOG_ERROR, "Error during encode_sample_description().\n");
+    }
+}
+
+static int mov_text_encode_frame(AVCodecContext *avctx, AVPacket *avpkt,
+                                 const AVFrame *frame, int *got_packet)
 {
     MovTextContext *s = avctx->priv_data;
     ASSDialog *dialog;
-    int i, length;
+    int i, ret = 0;
+    size_t j;
+    uint8_t* buf;
+
+    ensure_ass_context(avctx, frame);
 
     s->text_pos = 0;
     s->count = 0;
     s->box_flags = 0;
     av_bprint_clear(&s->buffer);
-    for (i = 0; i < sub->num_rects; i++) {
-        const char *ass = sub->rects[i]->ass;
+    for (i = 0; i < frame->num_subtitle_areas; i++) {
+        const char *ass = frame->subtitle_areas[i]->ass;
 
-        if (sub->rects[i]->type != SUBTITLE_ASS) {
-            av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
+        if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) {
+            av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
             return AVERROR(EINVAL);
         }
 
-        dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
-        if (!dialog)
-            return AVERROR(ENOMEM);
-        mov_text_dialog(s, dialog);
-        avpriv_ass_split_override_codes(&mov_text_callbacks, s, dialog->text);
-        avpriv_ass_free_dialog(&dialog);
+        if (ass) {
+
+            if (i > 0)
+                mov_text_new_line_cb(s, 0);
+
+            dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
+            if (!dialog)
+                return AVERROR(ENOMEM);
+            mov_text_dialog(s, dialog);
+            avpriv_ass_split_override_codes(&mov_text_callbacks, s, dialog->text);
+            avpriv_ass_free_dialog(&dialog);
+
+        }
     }
 
-    if (s->buffer.len > UINT16_MAX)
-        return AVERROR(ERANGE);
-    AV_WB16(buf, s->buffer.len);
-    buf += 2;
+    if (!av_bprint_is_complete(&s->buffer)) {
+        return AVERROR(ENOMEM);
+    }
 
-    for (size_t j = 0; j < box_count; j++)
+    for (j = 0; j < box_count; j++) {
         box_types[j].encode(s);
+    }
 
-    if (!av_bprint_is_complete(&s->buffer))
-        return AVERROR(ENOMEM);
+    ret = ff_get_encode_buffer(avctx, avpkt, s->buffer.len + 3, 0);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+        return ret;
+    }
 
-    if (!s->buffer.len)
-        return 0;
+    buf = avpkt->data;
 
-    if (s->buffer.len > bufsize - 3) {
+    AV_WB16(buf, s->buffer.len);
+    buf += 2;
+
+    if (s->buffer.len > avpkt->size - 3) {
         av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n");
-        return AVERROR_BUFFER_TOO_SMALL;
+        ret = AVERROR_BUFFER_TOO_SMALL;
+        goto exit;
     }
 
     memcpy(buf, s->buffer.str, s->buffer.len);
-    length = s->buffer.len + 2;
+    avpkt->size = s->buffer.len + 2;
+    *got_packet = 1;
 
-    return length;
+exit:
+    return ret;
 }
 
 #define OFFSET(x) offsetof(MovTextContext, x)
@@ -707,7 +765,7 @@ const FFCodec ff_movtext_encoder = {
     .priv_data_size = sizeof(MovTextContext),
     .p.priv_class   = &mov_text_encoder_class,
     .init           = mov_text_encode_init,
-    FF_CODEC_ENCODE_SUB_CB(mov_text_encode_frame),
+    FF_CODEC_ENCODE_CB(mov_text_encode_frame),
     .close          = mov_text_encode_close,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP,
 };
diff --git a/libavcodec/srtenc.c b/libavcodec/srtenc.c
index 2baa6e70ad..bc336ea91e 100644
--- a/libavcodec/srtenc.c
+++ b/libavcodec/srtenc.c
@@ -23,6 +23,7 @@
 
 #include <stdarg.h>
 #include "avcodec.h"
+#include "encode.h"
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
 #include "codec_internal.h"
@@ -35,6 +36,7 @@
 typedef struct {
     AVCodecContext *avctx;
     ASSSplitContext *ass_ctx;
+    int is_default_ass_context;
     AVBPrint buffer;
     char stack[SRT_STACK_SIZE];
     int stack_ptr;
@@ -132,14 +134,13 @@ static void srt_style_apply(SRTContext *s, const char *style)
     }
 }
 
-
 static av_cold int srt_encode_init(AVCodecContext *avctx)
 {
     SRTContext *s = avctx->priv_data;
     s->avctx = avctx;
-    s->ass_ctx = avpriv_ass_split(avctx->subtitle_header);
+    s->ass_ctx = avpriv_ass_split((char *)avctx->subtitle_header);
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
-    return s->ass_ctx ? 0 : AVERROR_INVALIDDATA;
+    return 0;
 }
 
 static void srt_text_cb(void *priv, const char *text, int len)
@@ -229,58 +230,95 @@ static const ASSCodesCallbacks text_callbacks = {
     .new_line         = srt_new_line_cb,
 };
 
-static int encode_frame(AVCodecContext *avctx,
-                        unsigned char *buf, int bufsize, const AVSubtitle *sub,
-                        const ASSCodesCallbacks *cb)
+static void ensure_ass_context(SRTContext* s, const AVFrame* frame)
+{
+    if (s->ass_ctx && !s->is_default_ass_context)
+        // We already have a (non-default context)
+        return;
+
+    if (!frame->num_subtitle_areas)
+        // Don't need ass context for processing empty subtitle frames
+        return;
+
+    // The frame has content, so we need to set up a context
+    if (frame->subtitle_header && frame->subtitle_header->size > 0) {
+        const char* subtitle_header = (char*)frame->subtitle_header->data;
+        avpriv_ass_split_free(s->ass_ctx);
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 0;
+    }
+    else if (!s->ass_ctx) {
+        char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+        if (!subtitle_header)
+            return;
+
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 1;
+        av_free(subtitle_header);
+    }
+}
+
+static int encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+                        const AVFrame* frame, int* got_packet, const ASSCodesCallbacks* cb)
 {
     SRTContext *s = avctx->priv_data;
     ASSDialog *dialog;
-    int i;
+    int i, ret;
+
+    ensure_ass_context(s, frame);
 
     av_bprint_clear(&s->buffer);
 
-    for (i=0; i<sub->num_rects; i++) {
-        const char *ass = sub->rects[i]->ass;
+    for (i=0; i< frame->num_subtitle_areas; i++) {
+        const char *ass = frame->subtitle_areas[i]->ass;
 
-        if (sub->rects[i]->type != SUBTITLE_ASS) {
-            av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
+        if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) {
+            av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
             return AVERROR(EINVAL);
         }
 
-        dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
-        if (!dialog)
-            return AVERROR(ENOMEM);
-        s->alignment_applied = 0;
-        if (avctx->codec_id == AV_CODEC_ID_SUBRIP)
-            srt_style_apply(s, dialog->style);
-        avpriv_ass_split_override_codes(cb, s, dialog->text);
-        avpriv_ass_free_dialog(&dialog);
+        if (ass) {
+
+            if (i > 0)
+                srt_new_line_cb(s, 0);
+
+            dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
+            if (!dialog)
+                return AVERROR(ENOMEM);
+            s->alignment_applied = 0;
+            if (avctx->codec_id == AV_CODEC_ID_SUBRIP)
+                srt_style_apply(s, dialog->style);
+            avpriv_ass_split_override_codes(cb, s, dialog->text);
+            avpriv_ass_free_dialog(&dialog);
+        }
     }
 
     if (!av_bprint_is_complete(&s->buffer))
         return AVERROR(ENOMEM);
-    if (!s->buffer.len)
-        return 0;
 
-    if (s->buffer.len > bufsize) {
-        av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n");
-        return AVERROR_BUFFER_TOO_SMALL;
+    ret = ff_get_encode_buffer(avctx, avpkt, s->buffer.len + 1, 0);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+        return ret;
     }
-    memcpy(buf, s->buffer.str, s->buffer.len);
 
-    return s->buffer.len;
+    memcpy(avpkt->data, s->buffer.str, s->buffer.len);
+    avpkt->size = s->buffer.len;
+    *got_packet = 1;
+
+    return 0;
 }
 
-static int srt_encode_frame(AVCodecContext *avctx,
-                               unsigned char *buf, int bufsize, const AVSubtitle *sub)
+static int srt_encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+                            const AVFrame* frame, int* got_packet)
 {
-    return encode_frame(avctx, buf, bufsize, sub, &srt_callbacks);
+    return encode_frame(avctx, avpkt, frame, got_packet, &srt_callbacks);
 }
 
-static int text_encode_frame(AVCodecContext *avctx,
-                             unsigned char *buf, int bufsize, const AVSubtitle *sub)
+static int text_encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+                             const AVFrame* frame, int* got_packet)
 {
-    return encode_frame(avctx, buf, bufsize, sub, &text_callbacks);
+    return encode_frame(avctx, avpkt, frame, got_packet, &text_callbacks);
 }
 
 static int srt_encode_close(AVCodecContext *avctx)
@@ -300,7 +338,7 @@ const FFCodec ff_srt_encoder = {
     .p.id           = AV_CODEC_ID_SUBRIP,
     .priv_data_size = sizeof(SRTContext),
     .init           = srt_encode_init,
-    FF_CODEC_ENCODE_SUB_CB(srt_encode_frame),
+    FF_CODEC_ENCODE_CB(srt_encode_frame),
     .close          = srt_encode_close,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
 };
@@ -314,7 +352,7 @@ const FFCodec ff_subrip_encoder = {
     .p.id           = AV_CODEC_ID_SUBRIP,
     .priv_data_size = sizeof(SRTContext),
     .init           = srt_encode_init,
-    FF_CODEC_ENCODE_SUB_CB(srt_encode_frame),
+    FF_CODEC_ENCODE_CB(srt_encode_frame),
     .close          = srt_encode_close,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
 };
@@ -328,7 +366,7 @@ const FFCodec ff_text_encoder = {
     .p.id           = AV_CODEC_ID_TEXT,
     .priv_data_size = sizeof(SRTContext),
     .init           = srt_encode_init,
-    FF_CODEC_ENCODE_SUB_CB(text_encode_frame),
+    FF_CODEC_ENCODE_CB(text_encode_frame),
     .close          = srt_encode_close,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
 };
diff --git a/libavcodec/tests/avcodec.c b/libavcodec/tests/avcodec.c
index 08b5fbede1..1469c23f99 100644
--- a/libavcodec/tests/avcodec.c
+++ b/libavcodec/tests/avcodec.c
@@ -109,7 +109,6 @@ int main(void){
             is_decoder = 1;
             break;
         case FF_CODEC_CB_TYPE_ENCODE:
-        case FF_CODEC_CB_TYPE_ENCODE_SUB:
         case FF_CODEC_CB_TYPE_RECEIVE_PACKET:
             is_encoder = 1;
             break;
@@ -125,15 +124,13 @@ int main(void){
 #define CHECK(TYPE, type) (codec2->cb_type == FF_CODEC_CB_TYPE_ ## TYPE && !codec2->cb.type)
         if (CHECK(DECODE, decode) || CHECK(DECODE_SUB, decode_sub) ||
             CHECK(RECEIVE_PACKET, receive_packet) ||
-            CHECK(ENCODE, encode) || CHECK(ENCODE_SUB, encode_sub) ||
+            CHECK(ENCODE, encode) ||
             CHECK(RECEIVE_FRAME, receive_frame)) {
             ERR_EXT("Codec %s does not implement its %s callback.\n",
                     is_decoder ? "decoding" : "encoding");
         }
 #undef CHECK
         if (is_encoder) {
-            if ((codec->type == AVMEDIA_TYPE_SUBTITLE) != (codec2->cb_type == FF_CODEC_CB_TYPE_ENCODE_SUB))
-                ERR("Encoder %s is both subtitle encoder and not subtitle encoder.");
             if (codec2->update_thread_context || codec2->update_thread_context_for_user || codec2->bsfs)
                 ERR("Encoder %s has decoder-only thread functions or bsf.\n");
             if (codec->type == AVMEDIA_TYPE_AUDIO) {
diff --git a/libavcodec/ttmlenc.c b/libavcodec/ttmlenc.c
index d4f11a87d2..035fe33f24 100644
--- a/libavcodec/ttmlenc.c
+++ b/libavcodec/ttmlenc.c
@@ -33,11 +33,17 @@
 #include "libavutil/bprint.h"
 #include "libavutil/internal.h"
 #include "libavutil/ass_split_internal.h"
+#include "libavutil/ass_internal.h"
 #include "ttmlenc.h"
 
+#include "encode.h"
+
+
 typedef struct {
     AVCodecContext *avctx;
     ASSSplitContext *ass_ctx;
+    int is_default_ass_context;
+    int extradata_written;
     AVBPrint buffer;
 } TTMLContext;
 
@@ -76,28 +82,75 @@ static const ASSCodesCallbacks ttml_callbacks = {
     .new_line         = ttml_new_line_cb,
 };
 
-static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf,
-                             int bufsize, const AVSubtitle *sub)
+static int ttml_write_header_content(AVCodecContext* avctx);
+
+static void ensure_ass_context(AVCodecContext* avctx, const AVFrame* frame)
+{
+    TTMLContext* s = avctx->priv_data;
+    int ret;
+
+    if (s->ass_ctx && !s->is_default_ass_context)
+        // We already have a (non-default context)
+        return;
+
+    if (!frame->num_subtitle_areas)
+        // Don't need ass context for processing empty subtitle frames
+        return;
+
+    // The frame has content, so we need to set up a context
+    if (frame->subtitle_header && frame->subtitle_header->size > 0) {
+        const char* subtitle_header = (char*)frame->subtitle_header->data;
+        avpriv_ass_split_free(s->ass_ctx);
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 0;
+    }
+    else if (!s->ass_ctx) {
+        char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+        if (!subtitle_header)
+            return;
+
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 1;
+        av_free(subtitle_header);
+    }
+
+    if (s->ass_ctx && !s->extradata_written) {
+        s->extradata_written = 1;
+        if ((ret = ttml_write_header_content(avctx)) < 0) {
+            av_log(avctx, AV_LOG_ERROR, "Error writing header content.\n");
+        }
+    }
+}
+
+static int ttml_encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+                             const AVFrame* frame, int* got_packet)
 {
     TTMLContext *s = avctx->priv_data;
     ASSDialog *dialog;
-    int i;
+    int i, ret;
+
+    ensure_ass_context(avctx, frame);
 
     av_bprint_clear(&s->buffer);
 
-    for (i=0; i<sub->num_rects; i++) {
-        const char *ass = sub->rects[i]->ass;
-        int ret;
+    for (i=0; i< frame->num_subtitle_areas; i++) {
+        const char *ass = frame->subtitle_areas[i]->ass;
 
-        if (sub->rects[i]->type != SUBTITLE_ASS) {
-            av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
+        if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) {
+            av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
             return AVERROR(EINVAL);
         }
 
+        if (!ass)
+            continue;
+
         dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
         if (!dialog)
             return AVERROR(ENOMEM);
 
+        if (i > 0)
+            ttml_new_line_cb(s, 0);
+
         if (dialog->style) {
             av_bprintf(&s->buffer, "<span region=\"");
             av_bprint_escape(&s->buffer, dialog->style, NULL,
@@ -130,17 +183,19 @@ static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf,
 
     if (!av_bprint_is_complete(&s->buffer))
         return AVERROR(ENOMEM);
-    if (!s->buffer.len)
-        return 0;
-
-    // force null-termination, so in case our destination buffer is
-    // too small, the return value is larger than bufsize minus null.
-    if (av_strlcpy(buf, s->buffer.str, bufsize) > bufsize - 1) {
-        av_log(avctx, AV_LOG_ERROR, "Buffer too small for TTML event.\n");
-        return AVERROR_BUFFER_TOO_SMALL;
+
+    ret = ff_get_encode_buffer(avctx, avpkt, s->buffer.len + 3, 0);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+        return ret;
     }
 
-    return s->buffer.len;
+    av_strlcpy(avpkt->data, s->buffer.str, avpkt->size);
+
+    avpkt->size = s->buffer.len;
+    *got_packet = 1;
+
+    return 0;
 }
 
 static av_cold int ttml_encode_close(AVCodecContext *avctx)
@@ -370,13 +425,13 @@ static av_cold int ttml_encode_init(AVCodecContext *avctx)
     s->avctx   = avctx;
 
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
+    s->ass_ctx = avpriv_ass_split((char*)avctx->subtitle_header);
 
-    if (!(s->ass_ctx = avpriv_ass_split(avctx->subtitle_header))) {
-        return AVERROR_INVALIDDATA;
-    }
+    if (s->ass_ctx) {
+        if (ret = ttml_write_header_content(avctx) < 0)
+            return ret;
 
-    if ((ret = ttml_write_header_content(avctx)) < 0) {
-        return ret;
+        s->extradata_written = 1;
     }
 
     return 0;
@@ -389,7 +444,7 @@ const FFCodec ff_ttml_encoder = {
     .p.id           = AV_CODEC_ID_TTML,
     .priv_data_size = sizeof(TTMLContext),
     .init           = ttml_encode_init,
-    FF_CODEC_ENCODE_SUB_CB(ttml_encode_frame),
+    FF_CODEC_ENCODE_CB(ttml_encode_frame),
     .close          = ttml_encode_close,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP,
 };
diff --git a/libavcodec/utils.c b/libavcodec/utils.c
index b67b6b6122..b9ebe18e32 100644
--- a/libavcodec/utils.c
+++ b/libavcodec/utils.c
@@ -75,7 +75,6 @@ int av_codec_is_encoder(const AVCodec *avcodec)
 {
     const FFCodec *const codec = ffcodec(avcodec);
     return codec && (codec->cb_type == FF_CODEC_CB_TYPE_ENCODE     ||
-                     codec->cb_type == FF_CODEC_CB_TYPE_ENCODE_SUB ||
                      codec->cb_type == FF_CODEC_CB_TYPE_RECEIVE_PACKET);
 }
 
diff --git a/libavcodec/webvttenc.c b/libavcodec/webvttenc.c
index 24d60c5dc1..128607a669 100644
--- a/libavcodec/webvttenc.c
+++ b/libavcodec/webvttenc.c
@@ -22,6 +22,7 @@
 
 #include <stdarg.h>
 #include "avcodec.h"
+#include "encode.h"
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
 #include "codec_internal.h"
@@ -32,6 +33,7 @@
 typedef struct {
     AVCodecContext *avctx;
     ASSSplitContext *ass_ctx;
+    int is_default_ass_context;
     AVBPrint buffer;
     unsigned timestamp_end;
     int count;
@@ -155,43 +157,81 @@ static const ASSCodesCallbacks webvtt_callbacks = {
     .end              = webvtt_end_cb,
 };
 
-static int webvtt_encode_frame(AVCodecContext *avctx,
-                               unsigned char *buf, int bufsize, const AVSubtitle *sub)
+static void ensure_ass_context(WebVTTContext* s, const AVFrame* frame)
+{
+    if (s->ass_ctx && !s->is_default_ass_context)
+        // We already have a (non-default context)
+        return;
+
+    if (!frame->num_subtitle_areas)
+        // Don't need ass context for processing empty subtitle frames
+        return;
+
+    // The frame has content, so we need to set up a context
+    if (frame->subtitle_header && frame->subtitle_header->size > 0) {
+        const char* subtitle_header = (char*)frame->subtitle_header->data;
+        avpriv_ass_split_free(s->ass_ctx);
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 0;
+    }
+    else if (!s->ass_ctx) {
+        char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+        if (!subtitle_header)
+            return;
+
+        s->ass_ctx = avpriv_ass_split(subtitle_header);
+        s->is_default_ass_context = 1;
+        av_free(subtitle_header);
+    }
+}
+
+static int webvtt_encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+                               const AVFrame* frame, int* got_packet)
 {
     WebVTTContext *s = avctx->priv_data;
     ASSDialog *dialog;
-    int i;
+    int ret, i;
+
+    ensure_ass_context(s, frame);
 
     av_bprint_clear(&s->buffer);
 
-    for (i=0; i<sub->num_rects; i++) {
-        const char *ass = sub->rects[i]->ass;
+    for (i=0; i< frame->num_subtitle_areas; i++) {
+        const char *ass = frame->subtitle_areas[i]->ass;
 
-        if (sub->rects[i]->type != SUBTITLE_ASS) {
-            av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
+        if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) {
+            av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
             return AVERROR(EINVAL);
         }
 
-        dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
-        if (!dialog)
-            return AVERROR(ENOMEM);
-        webvtt_style_apply(s, dialog->style);
-        avpriv_ass_split_override_codes(&webvtt_callbacks, s, dialog->text);
-        avpriv_ass_free_dialog(&dialog);
+        if (ass) {
+
+            if (i > 0)
+                webvtt_new_line_cb(s, 0);
+
+            dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
+            if (!dialog)
+                return AVERROR(ENOMEM);
+            webvtt_style_apply(s, dialog->style);
+            avpriv_ass_split_override_codes(&webvtt_callbacks, s, dialog->text);
+            avpriv_ass_free_dialog(&dialog);
+        }
     }
 
     if (!av_bprint_is_complete(&s->buffer))
         return AVERROR(ENOMEM);
-    if (!s->buffer.len)
-        return 0;
 
-    if (s->buffer.len > bufsize) {
-        av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n");
-        return AVERROR_BUFFER_TOO_SMALL;
+    ret = ff_get_encode_buffer(avctx, avpkt, s->buffer.len + 1, 0);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+        return ret;
     }
-    memcpy(buf, s->buffer.str, s->buffer.len);
 
-    return s->buffer.len;
+    memcpy(avpkt->data, s->buffer.str, s->buffer.len);
+    avpkt->size = s->buffer.len;
+    *got_packet = s->buffer.len > 0;
+
+    return 0;
 }
 
 static int webvtt_encode_close(AVCodecContext *avctx)
@@ -206,9 +246,9 @@ static av_cold int webvtt_encode_init(AVCodecContext *avctx)
 {
     WebVTTContext *s = avctx->priv_data;
     s->avctx = avctx;
-    s->ass_ctx = avpriv_ass_split(avctx->subtitle_header);
+    s->ass_ctx = avpriv_ass_split((char*)avctx->subtitle_header);
     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
-    return s->ass_ctx ? 0 : AVERROR_INVALIDDATA;
+    return 0;
 }
 
 const FFCodec ff_webvtt_encoder = {
@@ -218,7 +258,7 @@ const FFCodec ff_webvtt_encoder = {
     .p.id           = AV_CODEC_ID_WEBVTT,
     .priv_data_size = sizeof(WebVTTContext),
     .init           = webvtt_encode_init,
-    FF_CODEC_ENCODE_SUB_CB(webvtt_encode_frame),
+    FF_CODEC_ENCODE_CB(webvtt_encode_frame),
     .close          = webvtt_encode_close,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
 };
diff --git a/libavcodec/xsubenc.c b/libavcodec/xsubenc.c
index 8ca411e5af..19570aee2f 100644
--- a/libavcodec/xsubenc.c
+++ b/libavcodec/xsubenc.c
@@ -23,6 +23,7 @@
 #include "avcodec.h"
 #include "bytestream.h"
 #include "codec_internal.h"
+#include "encode.h"
 #include "put_bits.h"
 
 /**
@@ -111,39 +112,56 @@ static int make_tc(uint64_t ms, int *tc)
     return ms > 99;
 }
 
-static int xsub_encode(AVCodecContext *avctx, unsigned char *buf,
-                       int bufsize, const AVSubtitle *h)
+static int xsub_encode(AVCodecContext* avctx, AVPacket* avpkt,
+                       const AVFrame* frame, int* got_packet)
 {
-    uint64_t startTime = h->pts / 1000; // FIXME: need better solution...
-    uint64_t endTime = startTime + h->end_display_time - h->start_display_time;
+    const int64_t duration_ms = (int64_t)((double)frame->subtitle_timing.duration * av_q2d(AV_TIME_BASE_Q) * 1000);
+    const uint64_t startTime = (int64_t)((double)frame->subtitle_timing.start_pts * av_q2d(AV_TIME_BASE_Q) * 1000);
+    const uint64_t endTime   = startTime + duration_ms;
     int start_tc[4], end_tc[4];
-    uint8_t *hdr = buf + 27; // Point behind the timestamp
+    uint8_t *hdr;
     uint8_t *rlelenptr;
     uint16_t width, height;
-    int i;
+    int i, ret;
     PutBitContext pb;
+    uint8_t* buf;
+    int64_t req_size;
 
-    if (bufsize < 27 + 7*2 + 4*3) {
-        av_log(avctx, AV_LOG_ERROR, "Buffer too small for XSUB header.\n");
-        return AVERROR_BUFFER_TOO_SMALL;
+    if (!frame->num_subtitle_areas) {
+        // Don't encode empty sub events
+        return 0;
     }
 
+    // Estimate size (timestamp 27, header 7*2 + 4*3, padding 10)
+    req_size = 27 + 7*2 + 4*3 + 10;
+    req_size += frame->subtitle_areas[0]->linesize[0] * frame->subtitle_areas[0]->h;
+    req_size += 256; // Palette
+
+    ret = ff_get_encode_buffer(avctx, avpkt, req_size, 0);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+        return ret;
+    }
+
+    buf = avpkt->data;
+    hdr = avpkt->data + 27; // Point behind the timestamp
+
     // TODO: support multiple rects
-    if (h->num_rects != 1)
-        av_log(avctx, AV_LOG_WARNING, "Only single rects supported (%d in subtitle.)\n", h->num_rects);
+    if (frame->num_subtitle_areas != 1)
+        av_log(avctx, AV_LOG_WARNING, "Only single rects supported (%d in subtitle.)\n", frame->num_subtitle_areas);
 
     // TODO: render text-based subtitles into bitmaps
-    if (!h->rects[0]->data[0] || !h->rects[0]->data[1]) {
+    if (!frame->subtitle_areas[0]->buf[0]->data || !frame->subtitle_areas[0]->pal) {
         av_log(avctx, AV_LOG_WARNING, "No subtitle bitmap available.\n");
         return AVERROR(EINVAL);
     }
 
     // TODO: color reduction, similar to dvdsub encoder
-    if (h->rects[0]->nb_colors > 4)
-        av_log(avctx, AV_LOG_WARNING, "No more than 4 subtitle colors supported (%d found.)\n", h->rects[0]->nb_colors);
+    if (frame->subtitle_areas[0]->nb_colors > 4)
+        av_log(avctx, AV_LOG_WARNING, "No more than 4 subtitle colors supported (%d found.)\n", frame->subtitle_areas[0]->nb_colors);
 
     // TODO: Palette swapping if color zero is not transparent
-    if (((uint32_t *)h->rects[0]->data[1])[0] & 0xff000000)
+    if (((uint32_t *)frame->subtitle_areas[0]->pal)[0] & 0xff000000)
         av_log(avctx, AV_LOG_WARNING, "Color index 0 is not transparent. Transparency will be messed up.\n");
 
     if (make_tc(startTime, start_tc) || make_tc(endTime, end_tc)) {
@@ -151,7 +169,7 @@ static int xsub_encode(AVCodecContext *avctx, unsigned char *buf,
         return AVERROR(EINVAL);
     }
 
-    snprintf(buf, 28,
+    snprintf((char *)avpkt->data, 28,
         "[%02d:%02d:%02d.%03d-%02d:%02d:%02d.%03d]",
         start_tc[3], start_tc[2], start_tc[1], start_tc[0],
         end_tc[3],   end_tc[2],   end_tc[1],   end_tc[0]);
@@ -160,45 +178,47 @@ static int xsub_encode(AVCodecContext *avctx, unsigned char *buf,
     // 2 pixels required on either side of subtitle.
     // Possibly due to limitations of hardware renderers.
     // TODO: check if the bitmap is already padded
-    width  = FFALIGN(h->rects[0]->w, 2) + PADDING * 2;
-    height = FFALIGN(h->rects[0]->h, 2);
+    width  = FFALIGN(frame->subtitle_areas[0]->w, 2) + PADDING * 2;
+    height = FFALIGN(frame->subtitle_areas[0]->h, 2);
 
     bytestream_put_le16(&hdr, width);
     bytestream_put_le16(&hdr, height);
-    bytestream_put_le16(&hdr, h->rects[0]->x);
-    bytestream_put_le16(&hdr, h->rects[0]->y);
-    bytestream_put_le16(&hdr, h->rects[0]->x + width -1);
-    bytestream_put_le16(&hdr, h->rects[0]->y + height -1);
+    bytestream_put_le16(&hdr, frame->subtitle_areas[0]->x);
+    bytestream_put_le16(&hdr, frame->subtitle_areas[0]->y);
+    bytestream_put_le16(&hdr, frame->subtitle_areas[0]->x + width -1);
+    bytestream_put_le16(&hdr, frame->subtitle_areas[0]->y + height -1);
 
     rlelenptr = hdr; // Will store length of first field here later.
     hdr+=2;
 
     // Palette
     for (i=0; i<4; i++)
-        bytestream_put_be24(&hdr, ((uint32_t *)h->rects[0]->data[1])[i]);
+        bytestream_put_be24(&hdr, ((uint32_t *)frame->subtitle_areas[0]->pal)[i]);
 
     // Bitmap
     // RLE buffer. Reserve 2 bytes for possible padding after the last row.
-    init_put_bits(&pb, hdr, bufsize - (hdr - buf) - 2);
-    if (xsub_encode_rle(&pb, h->rects[0]->data[0],
-                        h->rects[0]->linesize[0] * 2,
-                        h->rects[0]->w, (h->rects[0]->h + 1) >> 1))
+    init_put_bits(&pb, hdr, avpkt->size - (hdr - buf) - 2);
+    if (xsub_encode_rle(&pb, frame->subtitle_areas[0]->buf[0]->data,
+                        frame->subtitle_areas[0]->linesize[0] * 2,
+                        frame->subtitle_areas[0]->w, (frame->subtitle_areas[0]->h + 1) >> 1))
         return AVERROR_BUFFER_TOO_SMALL;
     bytestream_put_le16(&rlelenptr, put_bytes_count(&pb, 0)); // Length of first field
 
-    if (xsub_encode_rle(&pb, h->rects[0]->data[0] + h->rects[0]->linesize[0],
-                        h->rects[0]->linesize[0] * 2,
-                        h->rects[0]->w, h->rects[0]->h >> 1))
+    if (xsub_encode_rle(&pb, frame->subtitle_areas[0]->buf[0]->data + frame->subtitle_areas[0]->linesize[0],
+                        frame->subtitle_areas[0]->linesize[0] * 2,
+                        frame->subtitle_areas[0]->w, frame->subtitle_areas[0]->h >> 1))
         return AVERROR_BUFFER_TOO_SMALL;
 
     // Enforce total height to be a multiple of 2
-    if (h->rects[0]->h & 1) {
-        put_xsub_rle(&pb, h->rects[0]->w, PADDING_COLOR);
+    if (frame->subtitle_areas[0]->h & 1) {
+        put_xsub_rle(&pb, frame->subtitle_areas[0]->w, PADDING_COLOR);
     }
 
     flush_put_bits(&pb);
 
-    return hdr - buf + put_bytes_output(&pb);
+    avpkt->size = hdr - buf + put_bytes_output(&pb);
+    *got_packet = 1;
+    return 0;
 }
 
 static av_cold int xsub_encoder_init(AVCodecContext *avctx)
@@ -217,6 +237,6 @@ const FFCodec ff_xsub_encoder = {
     .p.type     = AVMEDIA_TYPE_SUBTITLE,
     .p.id       = AV_CODEC_ID_XSUB,
     .init       = xsub_encoder_init,
-    FF_CODEC_ENCODE_SUB_CB(xsub_encode),
+    FF_CODEC_ENCODE_CB(xsub_encode),
     .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE,
 };
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v6 24/25] fftools/ffmpeg: Introduce subtitle filtering and new frame-based subtitle encoding
  2022-06-26 16:34         ` [FFmpeg-devel] [PATCH v6 00/25] Subtitle Filtering 2022 ffmpegagent
                             ` (22 preceding siblings ...)
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 23/25] avcodec/subtitles: Migrate subtitle encoders to frame-based API softworkz
@ 2022-06-26 16:35           ` softworkz
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 25/25] avcodec/dvbsubdec: Fix conditions for fallback to default resolution softworkz
  24 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-26 16:35 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

This commit actually enables subtitle filtering in ffmpeg by
sending and receiving subtitle frames to and from a filtergraph.

The heartbeat functionality from the previous sub2video implementation
is removed and now provided by the 'subfeed' filter.
The other part of sub2video functionality is retained by
auto-insertion of the new graphicsub2video filter.

Justification for changed test refs:

- sub2video
  The previous results were incorrect. The command line for this test
  specifies -r 5 (5 frames per second), which is now fulfilled by the
  additional frames in the reference output.
  Example: The first subtitle time is 499000, the second is 15355000,
  which means 0.5s and 15.35s with a difference of 14.85s.
  15s * 5fps = 75 frames and that's now the exact number of video
  frames between these two subtitle events.

- sub2video_basic
  The previous results had some incorrect output because multiple
  frames had the same dts
  The non-empty content frames are visually identical, the different
  CRC is due to the different blending algorithm that is being used.

- sub2video_time_limited
  Subtitle frames are emitted to the filter graphs at a 5 fps rate
  by default. The time limit for this test is 15s * 5fps = 75 frames
  which matches the count in the new ref.

- sub-dvb
  Running ffprobe -show_frames on the source file shows that there
  are 7 subtitle frames with 0 rects in the source at the start
  and 2 at the end. This translates to the 14 and 4 additional
  entries in the new test results.

- filter-overlay-dvdsub-2397
  Overlay results have slightly different CRCs due to different
  blending implementation

- sub-scc
  The first entry is no longer in the output because it is before
  the actual start time and the strim filter removes such entries
  now (like for video and audio)

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 fftools/ffmpeg.c                          |  613 +++++-----
 fftools/ffmpeg.h                          |   17 +-
 fftools/ffmpeg_filter.c                   |  270 +++--
 fftools/ffmpeg_hw.c                       |    2 +-
 fftools/ffmpeg_opt.c                      |   28 +-
 tests/ref/fate/filter-overlay-dvdsub-2397 |  182 +--
 tests/ref/fate/sub-dvb                    |  162 +--
 tests/ref/fate/sub-scc                    |    1 -
 tests/ref/fate/sub2video                  | 1091 +++++++++++++++++-
 tests/ref/fate/sub2video_basic            | 1238 +++++++++++++++++++--
 tests/ref/fate/sub2video_time_limited     |   78 +-
 11 files changed, 3010 insertions(+), 672 deletions(-)

diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c
index 09caa3e3c4..40d7868368 100644
--- a/fftools/ffmpeg.c
+++ b/fftools/ffmpeg.c
@@ -142,8 +142,6 @@ int want_sdp = 1;
 static BenchmarkTimeStamps current_time;
 AVIOContext *progress_avio = NULL;
 
-static uint8_t *subtitle_out;
-
 InputStream **input_streams = NULL;
 int        nb_input_streams = 0;
 InputFile   **input_files   = NULL;
@@ -168,163 +166,6 @@ static int restore_tty;
 static void free_input_threads(void);
 #endif
 
-/* sub2video hack:
-   Convert subtitles to video with alpha to insert them in filter graphs.
-   This is a temporary solution until libavfilter gets real subtitles support.
- */
-
-static int sub2video_get_blank_frame(InputStream *ist)
-{
-    int ret;
-    AVFrame *frame = ist->sub2video.frame;
-
-    av_frame_unref(frame);
-    ist->sub2video.frame->width  = ist->dec_ctx->width  ? ist->dec_ctx->width  : ist->sub2video.w;
-    ist->sub2video.frame->height = ist->dec_ctx->height ? ist->dec_ctx->height : ist->sub2video.h;
-    ist->sub2video.frame->format = AV_PIX_FMT_RGB32;
-    if ((ret = av_frame_get_buffer(frame, 0)) < 0)
-        return ret;
-    memset(frame->data[0], 0, frame->height * frame->linesize[0]);
-    return 0;
-}
-
-static void sub2video_copy_rect(uint8_t *dst, int dst_linesize, int w, int h,
-                                AVSubtitleRect *r)
-{
-    uint32_t *pal, *dst2;
-    uint8_t *src, *src2;
-    int x, y;
-
-    if (r->type != SUBTITLE_BITMAP) {
-        av_log(NULL, AV_LOG_WARNING, "sub2video: non-bitmap subtitle\n");
-        return;
-    }
-    if (r->x < 0 || r->x + r->w > w || r->y < 0 || r->y + r->h > h) {
-        av_log(NULL, AV_LOG_WARNING, "sub2video: rectangle (%d %d %d %d) overflowing %d %d\n",
-            r->x, r->y, r->w, r->h, w, h
-        );
-        return;
-    }
-
-    dst += r->y * dst_linesize + r->x * 4;
-    src = r->data[0];
-    pal = (uint32_t *)r->data[1];
-    for (y = 0; y < r->h; y++) {
-        dst2 = (uint32_t *)dst;
-        src2 = src;
-        for (x = 0; x < r->w; x++)
-            *(dst2++) = pal[*(src2++)];
-        dst += dst_linesize;
-        src += r->linesize[0];
-    }
-}
-
-static void sub2video_push_ref(InputStream *ist, int64_t pts)
-{
-    AVFrame *frame = ist->sub2video.frame;
-    int i;
-    int ret;
-
-    av_assert1(frame->data[0]);
-    ist->sub2video.last_pts = frame->pts = pts;
-    for (i = 0; i < ist->nb_filters; i++) {
-        ret = av_buffersrc_add_frame_flags(ist->filters[i]->filter, frame,
-                                           AV_BUFFERSRC_FLAG_KEEP_REF |
-                                           AV_BUFFERSRC_FLAG_PUSH);
-        if (ret != AVERROR_EOF && ret < 0)
-            av_log(NULL, AV_LOG_WARNING, "Error while add the frame to buffer source(%s).\n",
-                   av_err2str(ret));
-    }
-}
-
-void sub2video_update(InputStream *ist, int64_t heartbeat_pts, AVSubtitle *sub)
-{
-    AVFrame *frame = ist->sub2video.frame;
-    int8_t *dst;
-    int     dst_linesize;
-    int num_rects, i;
-    int64_t pts, end_pts;
-
-    if (!frame)
-        return;
-    if (sub) {
-        pts       = av_rescale_q(sub->pts + sub->start_display_time * 1000LL,
-                                 AV_TIME_BASE_Q, ist->st->time_base);
-        end_pts   = av_rescale_q(sub->pts + sub->end_display_time   * 1000LL,
-                                 AV_TIME_BASE_Q, ist->st->time_base);
-        num_rects = sub->num_rects;
-    } else {
-        /* If we are initializing the system, utilize current heartbeat
-           PTS as the start time, and show until the following subpicture
-           is received. Otherwise, utilize the previous subpicture's end time
-           as the fall-back value. */
-        pts       = ist->sub2video.initialize ?
-                    heartbeat_pts : ist->sub2video.end_pts;
-        end_pts   = INT64_MAX;
-        num_rects = 0;
-    }
-    if (sub2video_get_blank_frame(ist) < 0) {
-        av_log(ist->dec_ctx, AV_LOG_ERROR,
-               "Impossible to get a blank canvas.\n");
-        return;
-    }
-    dst          = frame->data    [0];
-    dst_linesize = frame->linesize[0];
-    for (i = 0; i < num_rects; i++)
-        sub2video_copy_rect(dst, dst_linesize, frame->width, frame->height, sub->rects[i]);
-    sub2video_push_ref(ist, pts);
-    ist->sub2video.end_pts = end_pts;
-    ist->sub2video.initialize = 0;
-}
-
-static void sub2video_heartbeat(InputStream *ist, int64_t pts)
-{
-    InputFile *infile = input_files[ist->file_index];
-    int i, j, nb_reqs;
-    int64_t pts2;
-
-    /* When a frame is read from a file, examine all sub2video streams in
-       the same file and send the sub2video frame again. Otherwise, decoded
-       video frames could be accumulating in the filter graph while a filter
-       (possibly overlay) is desperately waiting for a subtitle frame. */
-    for (i = 0; i < infile->nb_streams; i++) {
-        InputStream *ist2 = input_streams[infile->ist_index + i];
-        if (!ist2->sub2video.frame)
-            continue;
-        /* subtitles seem to be usually muxed ahead of other streams;
-           if not, subtracting a larger time here is necessary */
-        pts2 = av_rescale_q(pts, ist->st->time_base, ist2->st->time_base) - 1;
-        /* do not send the heartbeat frame if the subtitle is already ahead */
-        if (pts2 <= ist2->sub2video.last_pts)
-            continue;
-        if (pts2 >= ist2->sub2video.end_pts || ist2->sub2video.initialize)
-            /* if we have hit the end of the current displayed subpicture,
-               or if we need to initialize the system, update the
-               overlayed subpicture and its start/end times */
-            sub2video_update(ist2, pts2 + 1, NULL);
-        for (j = 0, nb_reqs = 0; j < ist2->nb_filters; j++)
-            nb_reqs += av_buffersrc_get_nb_failed_requests(ist2->filters[j]->filter);
-        if (nb_reqs)
-            sub2video_push_ref(ist2, pts2);
-    }
-}
-
-static void sub2video_flush(InputStream *ist)
-{
-    int i;
-    int ret;
-
-    if (ist->sub2video.end_pts < INT64_MAX)
-        sub2video_update(ist, INT64_MAX, NULL);
-    for (i = 0; i < ist->nb_filters; i++) {
-        ret = av_buffersrc_add_frame(ist->filters[i]->filter, NULL);
-        if (ret != AVERROR_EOF && ret < 0)
-            av_log(NULL, AV_LOG_WARNING, "Flush the frame error.\n");
-    }
-}
-
-/* end of sub2video hack */
-
 static void term_exit_sigsafe(void)
 {
 #if HAVE_TERMIOS_H
@@ -525,7 +366,6 @@ static void ffmpeg_cleanup(int ret)
         avfilter_graph_free(&fg->graph);
         for (j = 0; j < fg->nb_inputs; j++) {
             InputFilter *ifilter = fg->inputs[j];
-            struct InputStream *ist = ifilter->ist;
 
             if (ifilter->frame_queue) {
                 AVFrame *frame;
@@ -534,12 +374,6 @@ static void ffmpeg_cleanup(int ret)
                 av_fifo_freep2(&ifilter->frame_queue);
             }
             av_freep(&ifilter->displaymatrix);
-            if (ist->sub2video.sub_queue) {
-                AVSubtitle sub;
-                while (av_fifo_read(ist->sub2video.sub_queue, &sub, 1) >= 0)
-                    avsubtitle_free(&sub);
-                av_fifo_freep2(&ist->sub2video.sub_queue);
-            }
             av_buffer_unref(&ifilter->hw_frames_ctx);
             av_freep(&ifilter->name);
             av_freep(&fg->inputs[j]);
@@ -560,7 +394,7 @@ static void ffmpeg_cleanup(int ret)
     }
     av_freep(&filtergraphs);
 
-    av_freep(&subtitle_out);
+    ////av_freep(&subtitle_out);
 
     /* close files */
     for (i = 0; i < nb_output_files; i++)
@@ -613,15 +447,19 @@ static void ffmpeg_cleanup(int ret)
     for (i = 0; i < nb_input_streams; i++) {
         InputStream *ist = input_streams[i];
 
+        if (ist->prev_sub.subtitle == ist->decoded_frame)
+            // Prevent double-free
+            ist->prev_sub.subtitle = NULL;
+
         av_frame_free(&ist->decoded_frame);
         av_packet_free(&ist->pkt);
         av_dict_free(&ist->decoder_opts);
-        avsubtitle_free(&ist->prev_sub.subtitle);
-        av_frame_free(&ist->sub2video.frame);
+        av_frame_free(&ist->prev_sub.subtitle);
         av_freep(&ist->filters);
         av_freep(&ist->hwaccel_device);
         av_freep(&ist->dts_buffer);
 
+        av_buffer_unref(&ist->subtitle_header);
         avcodec_free_context(&ist->dec_ctx);
 
         av_freep(&input_streams[i]);
@@ -991,33 +829,83 @@ static void do_audio_out(OutputFile *of, OutputStream *ost,
         exit_program(1);
 }
 
-static void do_subtitle_out(OutputFile *of,
-                            OutputStream *ost,
-                            AVSubtitle *sub)
+static void encode_subtitle_frame(OutputFile *of, OutputStream *ost, AVFrame *frame, AVPacket *pkt, int64_t pts_offset)
+{
+    AVCodecContext *enc = ost->enc_ctx;
+    int ret;
+
+        ost->frames_encoded++;
+
+        ret = avcodec_send_frame(enc, frame);
+        if (ret < 0)
+            goto error;
+
+        while (1) {
+            ret = avcodec_receive_packet(enc, pkt);
+            update_benchmark("encode_subtitles %d.%d", ost->file_index, ost->index);
+            if (ret == AVERROR(EAGAIN))
+                break;
+            if (ret < 0)
+                goto error;
+
+            if (debug_ts) {
+                av_log(NULL, AV_LOG_INFO, "encoder -> type:subtitles "
+                       "pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s\n",
+                       av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &enc->time_base),
+                       av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &enc->time_base));
+            }
+
+            pkt->pts  = av_rescale_q(frame->subtitle_timing.start_pts, AV_TIME_BASE_Q, ost->mux_timebase);
+            pkt->duration = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, ost->mux_timebase);
+            pkt->pts += pts_offset;
+
+            pkt->dts = pkt->pts;
+            output_packet(of, pkt, ost, 0);
+        }
+        ost->sync_opts++;
+        ost->frame_number++;
+
+    return;
+error:
+    av_log(NULL, AV_LOG_FATAL, "Subtitle encoding failed - Error code: %d\n", ret);
+    exit_program(1);
+}
+
+static void do_subtitle_out(OutputFile *of, OutputStream *ost, AVFrame *frame)
 {
-    int subtitle_out_max_size = 1024 * 1024;
-    int subtitle_out_size, nb, i;
+    int nb, i;
     AVCodecContext *enc;
     AVPacket *pkt = ost->pkt;
     int64_t pts;
 
-    if (sub->pts == AV_NOPTS_VALUE) {
+    if (!frame)
+        return;
+
+    av_log(NULL, AV_LOG_DEBUG, "do_subtitle_out: sub->pts: %"PRId64"  frame->pts: %"PRId64"\n", frame->subtitle_timing.start_pts, frame->pts);
+
+    if (frame->subtitle_timing.start_pts == AV_NOPTS_VALUE) {
         av_log(NULL, AV_LOG_ERROR, "Subtitle packets must have a pts\n");
         if (exit_on_error)
             exit_program(1);
         return;
     }
 
-    enc = ost->enc_ctx;
-
-    if (!subtitle_out) {
-        subtitle_out = av_malloc(subtitle_out_max_size);
-        if (!subtitle_out) {
-            av_log(NULL, AV_LOG_FATAL, "Failed to allocate subtitle_out\n");
-            exit_program(1);
-        }
+    if (frame->repeat_sub) {
+        av_log(NULL, AV_LOG_WARNING, "Ignoring repeated subtitle frame\n");
+        return;
     }
 
+    ////if (ost->last_subtitle_pts && ost->last_subtitle_pts == frame->subtitle_timing.start_pts) {
+    ////    av_log(NULL, AV_LOG_DEBUG, "Ignoring subtitle frame with duplicate subtitle_pts\n");
+    ////    return;
+    ////}
+
+    ost->last_subtitle_pts = frame->subtitle_timing.start_pts;
+
+    init_output_stream_wrapper(ost, frame, 1);
+
+    enc = ost->enc_ctx;
+
     /* Note: DVB subtitle need one packet to draw them and one other
        packet to clear them */
     /* XXX: signal it in the codec context ? */
@@ -1027,50 +915,38 @@ static void do_subtitle_out(OutputFile *of,
         nb = 1;
 
     /* shift timestamp to honor -ss and make check_recording_time() work with -t */
-    pts = sub->pts;
+    pts = frame->subtitle_timing.start_pts;
     if (output_files[ost->file_index]->start_time != AV_NOPTS_VALUE)
         pts -= output_files[ost->file_index]->start_time;
-    for (i = 0; i < nb; i++) {
-        unsigned save_num_rects = sub->num_rects;
 
-        ost->sync_opts = av_rescale_q(pts, AV_TIME_BASE_Q, enc->time_base);
-        if (!check_recording_time(ost))
-            return;
+    ost->sync_opts = av_rescale_q(pts, AV_TIME_BASE_Q, enc->time_base);
+    if (!check_recording_time(ost))
+        return;
 
-        sub->pts = pts;
-        // start_display_time is required to be 0
-        sub->pts               += av_rescale_q(sub->start_display_time, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q);
-        sub->end_display_time  -= sub->start_display_time;
-        sub->start_display_time = 0;
-        if (i == 1)
-            sub->num_rects = 0;
+    frame->subtitle_timing.start_pts = pts;
+
+    for (i = 0; i < nb; i++) {
+        const unsigned save_num_rects = frame->num_subtitle_areas;
+        int64_t pts_offset = 0;
 
         ost->frames_encoded++;
 
-        subtitle_out_size = avcodec_encode_subtitle(enc, subtitle_out,
-                                                    subtitle_out_max_size, sub);
         if (i == 1)
-            sub->num_rects = save_num_rects;
-        if (subtitle_out_size < 0) {
-            av_log(NULL, AV_LOG_FATAL, "Subtitle encoding failed\n");
-            exit_program(1);
-        }
+            frame->num_subtitle_areas = 0;
 
-        av_packet_unref(pkt);
-        pkt->data = subtitle_out;
-        pkt->size = subtitle_out_size;
-        pkt->pts  = av_rescale_q(sub->pts, AV_TIME_BASE_Q, ost->mux_timebase);
-        pkt->duration = av_rescale_q(sub->end_display_time, (AVRational){ 1, 1000 }, ost->mux_timebase);
         if (enc->codec_id == AV_CODEC_ID_DVB_SUBTITLE) {
             /* XXX: the pts correction is handled here. Maybe handling
                it in the codec would be better */
             if (i == 0)
-                pkt->pts += av_rescale_q(sub->start_display_time, (AVRational){ 1, 1000 }, ost->mux_timebase);
+                pts_offset = 0;
             else
-                pkt->pts += av_rescale_q(sub->end_display_time, (AVRational){ 1, 1000 }, ost->mux_timebase);
+                pts_offset = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, ost->mux_timebase);
         }
-        pkt->dts = pkt->pts;
-        output_packet(of, pkt, ost, 0);
+
+        encode_subtitle_frame(of, ost, frame, pkt, pts_offset);
+
+        if (i == 1)
+            frame->num_subtitle_areas = save_num_rects;
     }
 }
 
@@ -1375,8 +1251,26 @@ static int reap_filters(int flush)
                 }
                 do_audio_out(of, ost, filtered_frame);
                 break;
+            case AVMEDIA_TYPE_SUBTITLE:
+
+                if (filtered_frame->format == AV_SUBTITLE_FMT_ASS && !enc->subtitle_header
+                    && filtered_frame->subtitle_header) {
+                    const char *subtitle_header = (char *)filtered_frame->subtitle_header->data;
+                    enc->subtitle_header = (uint8_t *)av_strdup(subtitle_header);
+                    if (!enc->subtitle_header)
+                        return AVERROR(ENOMEM);
+                    enc->subtitle_header_size = strlen(subtitle_header);
+                }
+
+                if ((ost->enc_ctx->width == 0 || ost->enc_ctx->height == 0)
+                    && filter->inputs[0]->w > 0 && filter->inputs[0]->h > 0 ) {
+                    ost->enc_ctx->width = filter->inputs[0]->w;
+                    ost->enc_ctx->height = filter->inputs[0]->h;
+                }
+
+                do_subtitle_out(of, ost, filtered_frame);
+                break;
             default:
-                // TODO support subtitle filters
                 av_assert0(0);
             }
 
@@ -1928,7 +1822,8 @@ static int ifilter_has_all_input_formats(FilterGraph *fg)
     int i;
     for (i = 0; i < fg->nb_inputs; i++) {
         if (fg->inputs[i]->format < 0 && (fg->inputs[i]->type == AVMEDIA_TYPE_AUDIO ||
-                                          fg->inputs[i]->type == AVMEDIA_TYPE_VIDEO))
+                                          fg->inputs[i]->type == AVMEDIA_TYPE_VIDEO ||
+                                          fg->inputs[i]->type == AVMEDIA_TYPE_SUBTITLE))
             return 0;
     }
     return 1;
@@ -1955,6 +1850,18 @@ static int ifilter_send_frame(InputFilter *ifilter, AVFrame *frame, int keep_ref
     case AVMEDIA_TYPE_VIDEO:
         need_reinit |= ifilter->width  != frame->width ||
                        ifilter->height != frame->height;
+
+        if (need_reinit)
+            need_reinit = 1;
+        break;
+    case AVMEDIA_TYPE_SUBTITLE:
+        need_reinit |= ifilter->width  != frame->width ||
+                       ifilter->height != frame->height;
+
+        need_reinit &= (ifilter->width == 0 || ifilter->height == 0);
+
+        if (need_reinit)
+            need_reinit = 1;
         break;
     }
 
@@ -2026,12 +1933,9 @@ static int ifilter_send_eof(InputFilter *ifilter, int64_t pts)
             return ret;
     } else {
         // the filtergraph was never configured
-        if (ifilter->format < 0) {
-            ret = ifilter_parameters_from_codecpar(ifilter, ifilter->ist->st->codecpar);
-            if (ret < 0)
-                return ret;
-        }
-        if (ifilter->format < 0 && (ifilter->type == AVMEDIA_TYPE_AUDIO || ifilter->type == AVMEDIA_TYPE_VIDEO)) {
+        if (ifilter->format < 0)
+            ifilter_parameters_from_codecpar(ifilter, ifilter->ist->st->codecpar);
+        if (ifilter->format < 0 && (ifilter->type == AVMEDIA_TYPE_AUDIO || ifilter->type == AVMEDIA_TYPE_VIDEO || ifilter->type == AVMEDIA_TYPE_SUBTITLE)) {
             av_log(NULL, AV_LOG_ERROR, "Cannot determine format of input stream %d:%d after EOF\n", ifilter->ist->file_index, ifilter->ist->st->index);
             return AVERROR_INVALIDDATA;
         }
@@ -2069,7 +1973,7 @@ static int decode(AVCodecContext *avctx, AVFrame *frame, int *got_frame, AVPacke
 
 static int send_frame_to_filters(InputStream *ist, AVFrame *decoded_frame)
 {
-    int i, ret;
+    int i, ret = 0;
 
     av_assert1(ist->nb_filters > 0); /* ensure ret is initialized */
     for (i = 0; i < ist->nb_filters; i++) {
@@ -2270,79 +2174,200 @@ fail:
     return err < 0 ? err : ret;
 }
 
-static int transcode_subtitles(InputStream *ist, AVPacket *pkt, int *got_output,
+static void subtitle_send_kickoff(InputStream *ist, int64_t heartbeat_pts)
+{
+    AVFrame *frame;
+    int64_t pts;
+
+    frame = av_frame_alloc();
+    if (!frame) {
+        av_log(ist->dec_ctx, AV_LOG_ERROR, "Unable to alloc frame (out of memory).\n");
+        return;
+    }
+
+    frame->type = AVMEDIA_TYPE_SUBTITLE;
+    av_frame_get_buffer2(frame, 0);
+
+    frame->format = (uint16_t)ist->dec_ctx->subtitle_type;
+
+    pts = FFMAX(heartbeat_pts, ist->subtitle_kickoff.last_pts) + av_rescale_q(10, (AVRational){ 1, 1000 }, ist->st->time_base);
+
+    frame->width = ist->subtitle_kickoff.w;
+    frame->height = ist->subtitle_kickoff.h;
+    frame->subtitle_timing.start_pts = av_rescale_q(pts, ist->st->time_base, AV_TIME_BASE_Q);
+    frame->subtitle_timing.duration =  av_rescale_q(10, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q);
+    frame->pts = pts;
+
+    av_log(NULL, AV_LOG_WARNING, "subtitle_kickoff: call subtitle_resend_current %"PRId64" frame->format: %d\n", pts, frame->format);
+
+    ist->subtitle_kickoff.last_pts = pts;
+
+    send_frame_to_filters(ist, frame);
+
+    av_frame_free(&frame);
+}
+
+// Opposed to the earlier "subtitle hearbeat", this is primarily aimed at
+// sending an initial subtitle frame to the filters for propagating the initial
+// timing values and to avoid that too much time is spent on a single "HW" decoder
+// which could overflow its buffer pool otherwise
+static void subtitle_kickoff(InputStream *ist, int64_t pts)
+{
+    int i;
+    int64_t pts2;
+    int64_t diff;
+
+    if (ist->st->codecpar->codec_type != AVMEDIA_TYPE_VIDEO)
+        return;
+
+    for (i = 0; i < nb_input_streams; i++) {
+        InputStream *ist2 = input_streams[i];
+        unsigned j, nb_reqs;
+
+        if (!ist2->subtitle_kickoff.is_active)
+            continue;
+        /* It's primarily the initial send that matters and which propagates
+         * the right start times to subtitle filters depending on input from decoding */
+        diff = av_rescale_q(5000, (AVRational){ 1, 1000 }, ist2->st->time_base);
+        pts2 = av_rescale_q(pts, ist->st->time_base, ist2->st->time_base);
+
+        /* do not send a kickoff frame if the subtitle is already ahead */
+        if (pts2 - diff <= ist2->subtitle_kickoff.last_pts)
+            continue;
+
+        for (j = 0, nb_reqs = 0; j < ist2->nb_filters; j++) {
+            if (ist2->filters[j]->filter == NULL) {
+                subtitle_send_kickoff(ist2, pts2);
+                return;
+            }
+            nb_reqs += av_buffersrc_get_nb_failed_requests(ist2->filters[j]->filter);
+        }
+        if (nb_reqs) {
+            av_log(NULL, AV_LOG_WARNING, "subtitle_kickoff: resend - pts: %"PRIi64"\n", pts2);
+            subtitle_send_kickoff(ist2, pts2);
+        }
+    }
+}
+
+static InputStream *get_input_stream(OutputStream *ost)
+{
+    if (ost->source_index >= 0)
+        return input_streams[ost->source_index];
+    return NULL;
+}
+
+static int decode_subtitles(InputStream *ist, AVPacket *pkt, int *got_output,
                                int *decode_failed)
 {
-    AVSubtitle subtitle;
-    int free_sub = 1;
-    int i, ret = avcodec_decode_subtitle2(ist->dec_ctx,
-                                          &subtitle, got_output, pkt);
+    AVFrame *decoded_frame;
+    AVCodecContext *avctx = ist->dec_ctx;
+    int i = 0, ret = 0, err = 0;
+    int64_t pts;
+
+    if (!ist->decoded_frame && !((ist->decoded_frame = av_frame_alloc())))
+        return AVERROR(ENOMEM);
+    decoded_frame = ist->decoded_frame;
+    decoded_frame->type = AVMEDIA_TYPE_SUBTITLE;
+    decoded_frame->format = (uint16_t)avctx->subtitle_type;
+
+    if (!ist->subtitle_header && avctx->subtitle_header && avctx->subtitle_header_size > 0) {
+        ist->subtitle_header = av_buffer_allocz(avctx->subtitle_header_size + 1);
+        if (!ist->subtitle_header)
+            return AVERROR(ENOMEM);
+        memcpy(ist->subtitle_header->data, avctx->subtitle_header, avctx->subtitle_header_size);
+    }
+
+    ret = decode(avctx, decoded_frame, got_output, pkt);
 
-    check_decode_result(NULL, got_output, ret);
+    if (ret != AVERROR_EOF)
+        check_decode_result(NULL, got_output, ret);
 
     if (ret < 0 || !*got_output) {
         *decode_failed = 1;
-        if (!pkt->size)
-            sub2video_flush(ist);
+        if (!pkt->size) {
+            // Flush
+            for (i = 0; i < ist->nb_filters; i++) {
+                if (ist->filters[i]->filter != NULL) {
+                    ret = av_buffersrc_add_frame(ist->filters[i]->filter, NULL);
+                    if (ret != AVERROR_EOF && ret < 0)
+                        av_log(NULL, AV_LOG_WARNING, "Flush the frame error.\n");
+                }
+            }
+        }
         return ret;
     }
 
     if (ist->fix_sub_duration) {
-        int end = 1;
-        if (ist->prev_sub.got_output) {
-            end = av_rescale(subtitle.pts - ist->prev_sub.subtitle.pts,
-                             1000, AV_TIME_BASE);
-            if (end < ist->prev_sub.subtitle.end_display_time) {
-                av_log(ist->dec_ctx, AV_LOG_DEBUG,
-                       "Subtitle duration reduced from %"PRId32" to %d%s\n",
-                       ist->prev_sub.subtitle.end_display_time, end,
-                       end <= 0 ? ", dropping it" : "");
-                ist->prev_sub.subtitle.end_display_time = end;
+        int64_t end = 1;
+        if (ist->prev_sub.got_output && ist->prev_sub.subtitle) {
+
+            const int64_t duration = ist->prev_sub.subtitle->subtitle_timing.duration;
+            end = decoded_frame->subtitle_timing.start_pts - ist->prev_sub.subtitle->subtitle_timing.start_pts;
+
+            if (end < duration) {
+                av_log(avctx, AV_LOG_DEBUG, "Subtitle duration reduced from %"PRId64" to %"PRId64"%s\n",
+                    duration, end, end <= 0 ? ", dropping it" : "");
+                ist->prev_sub.subtitle->subtitle_timing.duration = end;
             }
         }
-        FFSWAP(int,        *got_output, ist->prev_sub.got_output);
-        FFSWAP(int,        ret,         ist->prev_sub.ret);
-        FFSWAP(AVSubtitle, subtitle,    ist->prev_sub.subtitle);
+        FFSWAP(int,        *got_output,        ist->prev_sub.got_output);
+        FFSWAP(int,        ret,                ist->prev_sub.ret);
+        FFSWAP(AVFrame*,   ist->decoded_frame, ist->prev_sub.subtitle);
+        decoded_frame = ist->decoded_frame;
         if (end <= 0)
-            goto out;
+            return (int)end;
     }
 
-    if (!*got_output)
+    if (!*got_output || !decoded_frame)
         return ret;
 
-    if (ist->sub2video.frame) {
-        sub2video_update(ist, INT64_MIN, &subtitle);
-    } else if (ist->nb_filters) {
-        if (!ist->sub2video.sub_queue)
-            ist->sub2video.sub_queue = av_fifo_alloc2(8, sizeof(AVSubtitle), AV_FIFO_FLAG_AUTO_GROW);
-        if (!ist->sub2video.sub_queue)
-            exit_program(1);
+    decoded_frame->type = AVMEDIA_TYPE_SUBTITLE;
 
-        ret = av_fifo_write(ist->sub2video.sub_queue, &subtitle, 1);
-        if (ret < 0)
-            exit_program(1);
-        free_sub = 0;
-    }
+    if (decoded_frame->format == AV_SUBTITLE_FMT_UNKNOWN)
+        decoded_frame->format = (uint16_t)avctx->subtitle_type;
+
+    if ((ret = av_buffer_replace(&decoded_frame->subtitle_header, ist->subtitle_header)) < 0)
+        return ret;
 
-    if (!subtitle.num_rects)
-        goto out;
+    decoded_frame->pts = av_rescale_q(decoded_frame->subtitle_timing.start_pts, AV_TIME_BASE_Q, ist->st->time_base);
 
-    ist->frames_decoded++;
+    pts     = av_rescale_q(decoded_frame->subtitle_timing.start_pts,
+                           AV_TIME_BASE_Q, ist->st->time_base);
 
-    for (i = 0; i < nb_output_streams; i++) {
-        OutputStream *ost = output_streams[i];
+    ist->subtitle_kickoff.last_pts = decoded_frame->pts = pts;
 
-        if (!check_output_constraints(ist, ost) || !ost->encoding_needed
-            || ost->enc->type != AVMEDIA_TYPE_SUBTITLE)
-            continue;
+    if (ist->nb_filters > 0) {
+        AVFrame *filter_frame = av_frame_clone(decoded_frame);
+        if (!filter_frame) {
+            err = AVERROR(ENOMEM);
+            goto end;
+        }
 
-        do_subtitle_out(output_files[ost->file_index], ost, &subtitle);
+        err = send_frame_to_filters(ist, filter_frame);
+        av_frame_free(&filter_frame);
     }
 
-out:
-    if (free_sub)
-        avsubtitle_free(&subtitle);
-    return ret;
+    if (err >= 0) {
+        for (i = 0; i < nb_output_streams; i++) {
+            OutputStream *ost = output_streams[i];
+            InputStream *ist_src = get_input_stream(ost);
+
+            if (!ist_src || !check_output_constraints(ist, ost)
+                || ist_src != ist
+                || !ost->encoding_needed
+                || ost->enc->type != AVMEDIA_TYPE_SUBTITLE)
+                continue;
+
+            if (ost->filter && ost->filter->filter->nb_inputs > 0)
+                continue;
+
+            do_subtitle_out(output_files[ost->file_index], ost, decoded_frame);
+        }
+    }
+
+end:
+    av_frame_unref(decoded_frame);
+    return err < 0 ? err : ret;
 }
 
 static int send_filter_eof(InputStream *ist)
@@ -2446,7 +2471,7 @@ static int process_input_packet(InputStream *ist, const AVPacket *pkt, int no_eo
         case AVMEDIA_TYPE_SUBTITLE:
             if (repeating)
                 break;
-            ret = transcode_subtitles(ist, avpkt, &got_output, &decode_failed);
+            ret = decode_subtitles(ist, avpkt, &got_output, &decode_failed);
             if (!pkt && ret >= 0)
                 ret = AVERROR_EOF;
             av_packet_unref(avpkt);
@@ -2663,13 +2688,6 @@ static int init_input_stream(int ist_index, char *error, int error_len)
     return 0;
 }
 
-static InputStream *get_input_stream(OutputStream *ost)
-{
-    if (ost->source_index >= 0)
-        return input_streams[ost->source_index];
-    return NULL;
-}
-
 static int compare_int64(const void *a, const void *b)
 {
     return FFDIFFSIGN(*(const int64_t *)a, *(const int64_t *)b);
@@ -3097,7 +3115,7 @@ static int init_output_stream_encode(OutputStream *ost, AVFrame *frame)
         break;
     case AVMEDIA_TYPE_SUBTITLE:
         enc_ctx->time_base = AV_TIME_BASE_Q;
-        if (!enc_ctx->width) {
+        if (!enc_ctx->width && ost->source_index >= 0) {
             enc_ctx->width     = input_streams[ost->source_index]->st->codecpar->width;
             enc_ctx->height    = input_streams[ost->source_index]->st->codecpar->height;
         }
@@ -3114,6 +3132,17 @@ static int init_output_stream_encode(OutputStream *ost, AVFrame *frame)
     return 0;
 }
 
+static enum AVSubtitleType get_subtitle_format(const AVCodecDescriptor *codec_descriptor)
+{
+    if(codec_descriptor->props & AV_CODEC_PROP_BITMAP_SUB)
+        return AV_SUBTITLE_FMT_BITMAP;
+
+    if(codec_descriptor->props & AV_CODEC_PROP_TEXT_SUB)
+        return AV_SUBTITLE_FMT_ASS;
+
+    return AV_SUBTITLE_FMT_UNKNOWN;
+}
+
 static int init_output_stream(OutputStream *ost, AVFrame *frame,
                               char *error, int error_len)
 {
@@ -3150,19 +3179,13 @@ static int init_output_stream(OutputStream *ost, AVFrame *frame,
         }
 
         if (ist && ist->dec->type == AVMEDIA_TYPE_SUBTITLE && ost->enc->type == AVMEDIA_TYPE_SUBTITLE) {
-            int input_props = 0, output_props = 0;
-            AVCodecDescriptor const *input_descriptor =
-                avcodec_descriptor_get(dec->codec_id);
-            AVCodecDescriptor const *output_descriptor =
-                avcodec_descriptor_get(ost->enc_ctx->codec_id);
-            if (input_descriptor)
-                input_props = input_descriptor->props & (AV_CODEC_PROP_TEXT_SUB | AV_CODEC_PROP_BITMAP_SUB);
-            if (output_descriptor)
-                output_props = output_descriptor->props & (AV_CODEC_PROP_TEXT_SUB | AV_CODEC_PROP_BITMAP_SUB);
-            if (input_props && output_props && input_props != output_props) {
-                snprintf(error, error_len,
-                         "Subtitle encoding currently only possible from text to text "
-                         "or bitmap to bitmap");
+            AVCodecDescriptor const *output_descriptor    = avcodec_descriptor_get(ost->enc_ctx->codec_id);
+            const enum AVSubtitleType in_subtitle_format  = (uint16_t)dec->subtitle_type;
+            const enum AVSubtitleType out_subtitle_format = output_descriptor ? get_subtitle_format(output_descriptor) : AV_SUBTITLE_FMT_UNKNOWN;
+
+            if (ist->nb_filters == 0 && in_subtitle_format != AV_SUBTITLE_FMT_UNKNOWN && out_subtitle_format != AV_SUBTITLE_FMT_UNKNOWN
+                && in_subtitle_format != out_subtitle_format) {
+                snprintf(error, error_len, "Subtitle encoding is only possible from text to text or bitmap to bitmap");
                 return AVERROR_INVALIDDATA;
             }
         }
@@ -3326,7 +3349,8 @@ static int transcode_init(void)
     for (i = 0; i < nb_output_streams; i++) {
         if (!output_streams[i]->stream_copy &&
             (output_streams[i]->enc_ctx->codec_type == AVMEDIA_TYPE_VIDEO ||
-             output_streams[i]->enc_ctx->codec_type == AVMEDIA_TYPE_AUDIO))
+             output_streams[i]->enc_ctx->codec_type == AVMEDIA_TYPE_AUDIO ||
+             output_streams[i]->enc_ctx->codec_type == AVMEDIA_TYPE_SUBTITLE))
             continue;
 
         ret = init_output_stream_wrapper(output_streams[i], NULL, 0);
@@ -3497,7 +3521,7 @@ static OutputStream *choose_output(void)
                        av_rescale_q(ost->last_mux_dts, ost->st->time_base,
                                     AV_TIME_BASE_Q);
         if (ost->last_mux_dts == AV_NOPTS_VALUE)
-            av_log(NULL, AV_LOG_DEBUG,
+            av_log(NULL, AV_LOG_INFO,
                 "cur_dts is invalid st:%d (%d) [init:%d i_done:%d finish:%d] (this is harmless if it occurs once at the start per stream)\n",
                 ost->st->index, ost->st->id, ost->initialized, ost->inputs_done, ost->finished);
 
@@ -4166,7 +4190,7 @@ static int process_input(int file_index)
                av_ts2timestr(input_files[ist->file_index]->ts_offset, &AV_TIME_BASE_Q));
     }
 
-    sub2video_heartbeat(ist, pkt->pts);
+    subtitle_kickoff(ist, pkt->pts);
 
     process_input_packet(ist, pkt, 0);
 
@@ -4378,6 +4402,7 @@ static int transcode(void)
     /* at the end of stream, we must flush the decoder buffers */
     for (i = 0; i < nb_input_streams; i++) {
         ist = input_streams[i];
+        ist->subtitle_kickoff.is_active = 0;
         if (!input_files[ist->file_index]->eof_reached) {
             process_input_packet(ist, NULL, 0);
         }
diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h
index 69a368b8d1..74feff734f 100644
--- a/fftools/ffmpeg.h
+++ b/fftools/ffmpeg.h
@@ -353,17 +353,16 @@ typedef struct InputStream {
     struct { /* previous decoded subtitle and related variables */
         int got_output;
         int ret;
-        AVSubtitle subtitle;
+        AVFrame *subtitle;
     } prev_sub;
 
-    struct sub2video {
+    struct subtitle_kickoff {
+        int is_active;
         int64_t last_pts;
-        int64_t end_pts;
-        AVFifo *sub_queue;    ///< queue of AVSubtitle* before filter init
-        AVFrame *frame;
         int w, h;
-        unsigned int initialize; ///< marks if sub2video_update should force an initialization
-    } sub2video;
+    } subtitle_kickoff;
+
+    AVBufferRef *subtitle_header;
 
     /* decoded data from this stream goes into all those filters
      * currently video and audio only */
@@ -468,6 +467,8 @@ typedef struct OutputStream {
     int64_t first_pts;
     /* dts of the last packet sent to the muxer */
     int64_t last_mux_dts;
+    /* subtitle_pts values of the last subtitle frame having arrived for encoding */
+    int64_t last_subtitle_pts;
     // the timebase of the packets sent to the muxer
     AVRational mux_timebase;
     AVRational enc_timebase;
@@ -675,8 +676,6 @@ int filtergraph_is_simple(FilterGraph *fg);
 int init_simple_filtergraph(InputStream *ist, OutputStream *ost);
 int init_complex_filtergraph(FilterGraph *fg);
 
-void sub2video_update(InputStream *ist, int64_t heartbeat_pts, AVSubtitle *sub);
-
 int ifilter_parameters_from_frame(InputFilter *ifilter, const AVFrame *frame);
 
 int ffmpeg_parse_options(int argc, char **argv);
diff --git a/fftools/ffmpeg_filter.c b/fftools/ffmpeg_filter.c
index 0845c631a5..fe03d06373 100644
--- a/fftools/ffmpeg_filter.c
+++ b/fftools/ffmpeg_filter.c
@@ -22,6 +22,8 @@
 
 #include "ffmpeg.h"
 
+#include "libavutil/ass_split_internal.h"
+
 #include "libavfilter/avfilter.h"
 #include "libavfilter/buffersink.h"
 #include "libavfilter/buffersrc.h"
@@ -30,11 +32,9 @@
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
 #include "libavutil/channel_layout.h"
-#include "libavutil/display.h"
 #include "libavutil/opt.h"
 #include "libavutil/pixdesc.h"
 #include "libavutil/pixfmt.h"
-#include "libavutil/imgutils.h"
 #include "libavutil/samplefmt.h"
 
 // FIXME: YUV420P etc. are actually supported with full color range,
@@ -232,9 +232,8 @@ static void init_input_filter(FilterGraph *fg, AVFilterInOut *in)
     InputFilter *ifilter;
     int i;
 
-    // TODO: support other filter types
-    if (type != AVMEDIA_TYPE_VIDEO && type != AVMEDIA_TYPE_AUDIO) {
-        av_log(NULL, AV_LOG_FATAL, "Only video and audio filters supported "
+    if (type != AVMEDIA_TYPE_VIDEO && type != AVMEDIA_TYPE_AUDIO && type != AVMEDIA_TYPE_SUBTITLE) {
+        av_log(NULL, AV_LOG_FATAL, "Only video, audio and subtitle filters supported "
                "currently.\n");
         exit_program(1);
     }
@@ -255,8 +254,9 @@ static void init_input_filter(FilterGraph *fg, AVFilterInOut *in)
         for (i = 0; i < s->nb_streams; i++) {
             enum AVMediaType stream_type = s->streams[i]->codecpar->codec_type;
             if (stream_type != type &&
-                !(stream_type == AVMEDIA_TYPE_SUBTITLE &&
-                  type == AVMEDIA_TYPE_VIDEO /* sub2video hack */))
+                // in the followng case we auto-insert the graphicsub2video conversion filter
+                // for retaining compatibility with the previous sub2video hack
+                !(stream_type == AVMEDIA_TYPE_SUBTITLE && type == AVMEDIA_TYPE_VIDEO))
                 continue;
             if (check_stream_specifier(s, s->streams[i], *p == ':' ? p + 1 : p) == 1) {
                 st = s->streams[i];
@@ -361,6 +361,21 @@ static int insert_trim(int64_t start_time, int64_t duration,
     const char *name = (type == AVMEDIA_TYPE_VIDEO) ? "trim" : "atrim";
     int ret = 0;
 
+    switch (type) {
+    case AVMEDIA_TYPE_VIDEO:
+        name = "trim";
+        break;
+    case AVMEDIA_TYPE_AUDIO:
+        name = "atrim";
+        break;
+    case AVMEDIA_TYPE_SUBTITLE:
+        name = "strim";
+        break;
+    default:
+        av_log(NULL, AV_LOG_ERROR, "insert_trim: Invalid media type: %d\n", type);
+        return AVERROR_INVALIDDATA;
+    }
+
     if (duration == INT64_MAX && start_time == AV_NOPTS_VALUE)
         return 0;
 
@@ -423,6 +438,40 @@ static int insert_filter(AVFilterContext **last_filter, int *pad_idx,
     return 0;
 }
 
+static int configure_output_subtitle_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out)
+{
+    OutputStream *ost = ofilter->ost;
+    OutputFile    *of = output_files[ost->file_index];
+    AVFilterContext *last_filter = out->filter_ctx;
+    int pad_idx = out->pad_idx;
+    int ret;
+    char name[255];
+
+    snprintf(name, sizeof(name), "out_%d_%d", ost->file_index, ost->index);
+    ret = avfilter_graph_create_filter(&ofilter->filter,
+                                       avfilter_get_by_name("sbuffersink"),
+                                       name, NULL, NULL, fg->graph);
+
+    if (ret < 0) {
+        av_log(NULL, AV_LOG_ERROR, "Unable to create filter sbuffersink\n");
+        return ret;
+    }
+
+    snprintf(name, sizeof(name), "trim_out_%d_%d",
+             ost->file_index, ost->index);
+    ret = insert_trim(of->start_time, of->recording_time,
+                      &last_filter, &pad_idx, name);
+    if (ret < 0)
+        return ret;
+
+    ////ost->st->codecpar->codec_tag = MKTAG('a', 's', 's', 's');
+
+    if ((ret = avfilter_link(last_filter, pad_idx, ofilter->filter, 0)) < 0)
+        return ret;
+
+    return 0;
+}
+
 static int configure_output_video_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out)
 {
     OutputStream *ost = ofilter->ost;
@@ -604,7 +653,8 @@ static int configure_output_audio_filter(FilterGraph *fg, OutputFilter *ofilter,
         int i;
 
         for (i=0; i<of->ctx->nb_streams; i++)
-            if (of->ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
+            if (of->ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ||
+                of->ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)
                 break;
 
         if (i<of->ctx->nb_streams) {
@@ -638,6 +688,7 @@ static int configure_output_filter(FilterGraph *fg, OutputFilter *ofilter,
     switch (avfilter_pad_get_type(out->filter_ctx->output_pads, out->pad_idx)) {
     case AVMEDIA_TYPE_VIDEO: return configure_output_video_filter(fg, ofilter, out);
     case AVMEDIA_TYPE_AUDIO: return configure_output_audio_filter(fg, ofilter, out);
+    case AVMEDIA_TYPE_SUBTITLE: return configure_output_subtitle_filter(fg, ofilter, out);
     default: av_assert0(0); return 0;
     }
 }
@@ -657,51 +708,154 @@ void check_filter_outputs(void)
     }
 }
 
-static int sub2video_prepare(InputStream *ist, InputFilter *ifilter)
+static int configure_input_subtitle_filter(FilterGraph *fg, InputFilter *ifilter,
+                                        AVFilterInOut *in)
 {
-    AVFormatContext *avf = input_files[ist->file_index]->ctx;
-    int i, w, h;
+    AVFilterContext *last_filter;
+    const AVFilter *buffer_filt = avfilter_get_by_name("sbuffer");
+    InputStream *ist = ifilter->ist;
+    InputFile     *f = input_files[ist->file_index];
+    AVBPrint args;
+    char name[255];
+    int ret, pad_idx = 0;
+    int w, h;
+    int64_t tsoffset = 0;
+    AVBufferSrcParameters *par = av_buffersrc_parameters_alloc();
+    enum AVMediaType media_type;
+
+    if (!par)
+        return AVERROR(ENOMEM);
+
+    par->format = AV_PIX_FMT_NONE;
+
+    if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) {
+        av_log(NULL, AV_LOG_ERROR, "Cannot connect subtitle filter to audio input\n");
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+
+    if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO) {
+        av_log(NULL, AV_LOG_ERROR, "Cannot connect subtitle filter to video input\n");
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+
+    if (ist->st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE && ist->dec_ctx) {
+        // For subtitles, we need to set the format here. Would we leave the format
+        // at -1, it would delay processing (ifilter_has_all_input_formats()) until
+        // the first subtile frame arrives, which could never happen in the worst case
+        ifilter->format = (uint16_t)ist->dec_ctx->subtitle_type;
+    }
+
+    ist->subtitle_kickoff.is_active = 1;
 
-    /* Compute the size of the canvas for the subtitles stream.
-       If the subtitles codecpar has set a size, use it. Otherwise use the
-       maximum dimensions of the video streams in the same file. */
     w = ifilter->width;
     h = ifilter->height;
+
     if (!(w && h)) {
-        for (i = 0; i < avf->nb_streams; i++) {
-            if (avf->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
-                w = FFMAX(w, avf->streams[i]->codecpar->width);
-                h = FFMAX(h, avf->streams[i]->codecpar->height);
+        w = ist->dec_ctx->width;
+        h = ist->dec_ctx->height;
+    }
+
+    if (!(w && h) && ist->dec_ctx->subtitle_header) {
+        ASSSplitContext *ass_ctx = avpriv_ass_split((char *)ist->dec_ctx->subtitle_header);
+        ASS *ass = (ASS *)ass_ctx;
+        w = ass->script_info.play_res_x;
+        h = ass->script_info.play_res_y;
+        avpriv_ass_split_free(ass_ctx);
+    }
+
+    ist->subtitle_kickoff.w = w;
+    ist->subtitle_kickoff.h = h;
+    av_log(ifilter, AV_LOG_INFO, "subtitle input filter: decoding size %dx%d\n", ist->subtitle_kickoff.w, ist->subtitle_kickoff.h);
+
+    ifilter->width = w;
+    ifilter->height = h;
+    ist->dec_ctx->width = w;
+    ist->dec_ctx->height = h;
+
+    ist->subtitle_kickoff.last_pts = INT64_MIN;
+
+    snprintf(name, sizeof(name), "graph %d subtitle input from stream %d:%d", fg->index,
+             ist->file_index, ist->st->index);
+
+
+    av_bprint_init(&args, 0, AV_BPRINT_SIZE_AUTOMATIC);
+    av_bprintf(&args,
+             "subtitle_type=%d:width=%d:height=%d:time_base=%d/%d:",
+             ifilter->format, ifilter->width, ifilter->height,
+             ist->st->time_base.num, ist->st->time_base.den);
+    if ((ret = avfilter_graph_create_filter(&ifilter->filter, buffer_filt, name,
+                                            args.str, NULL, fg->graph)) < 0)
+        goto fail;
+
+    par->hw_frames_ctx = ifilter->hw_frames_ctx;
+    par->format = ifilter->format;
+    par->width = ifilter->width;
+    par->height = ifilter->height;
+
+    ret = av_buffersrc_parameters_set(ifilter->filter, par);
+    if (ret < 0)
+        goto fail;
+    av_freep(&par);
+    last_filter = ifilter->filter;
+
+    // This is for sub2video compatibility
+    media_type = avfilter_pad_get_type(in->filter_ctx->input_pads, in->pad_idx);
+    if (media_type == AVMEDIA_TYPE_VIDEO) {
+        int subscale_w = w, subscale_h = h;
+
+        av_log(NULL, AV_LOG_INFO, "Auto-inserting subfeed filter\n");
+        ret = insert_filter(&last_filter, &pad_idx, "subfeed", NULL);
+        if (ret < 0)
+            return ret;
+
+        if (!(subscale_w && subscale_h)) {
+            // If the subtitle frame size is unknown, try to find a video input
+            // and use its size for adding a subscale filter
+            for (int i = 0; i < fg->nb_inputs; i++) {
+                InputFilter *input = fg->inputs[i];
+                if (input->type == AVMEDIA_TYPE_VIDEO && input->width && input->height) {
+                    subscale_w = input->width;
+                    subscale_h = input->height;
+                    break;
+                }
             }
         }
-        if (!(w && h)) {
-            w = FFMAX(w, 720);
-            h = FFMAX(h, 576);
-        }
-        av_log(avf, AV_LOG_INFO, "sub2video: using %dx%d canvas\n", w, h);
-    }
-    ist->sub2video.w = ifilter->width  = w;
-    ist->sub2video.h = ifilter->height = h;
 
-    ifilter->width  = ist->dec_ctx->width  ? ist->dec_ctx->width  : ist->sub2video.w;
-    ifilter->height = ist->dec_ctx->height ? ist->dec_ctx->height : ist->sub2video.h;
+        if (subscale_w && subscale_h) {
+            char subscale_params[64];
+            snprintf(subscale_params, sizeof(subscale_params), "w=%d:h=%d", subscale_w, subscale_h);
+            ret = insert_filter(&last_filter, &pad_idx, "subscale", subscale_params);
+            if (ret < 0)
+                return ret;
+        }
 
-    /* rectangles are AV_PIX_FMT_PAL8, but we have no guarantee that the
-       palettes for all rectangles are identical or compatible */
-    ifilter->format = AV_PIX_FMT_RGB32;
+        av_log(NULL, AV_LOG_INFO, "Auto-inserting graphicsub2video filter\n");
+        ret = insert_filter(&last_filter, &pad_idx, "graphicsub2video", NULL);
+        if (ret < 0)
+            return ret;
+    }
 
-    ist->sub2video.frame = av_frame_alloc();
-    if (!ist->sub2video.frame)
-        return AVERROR(ENOMEM);
-    ist->sub2video.last_pts = INT64_MIN;
-    ist->sub2video.end_pts  = INT64_MIN;
+    if (copy_ts) {
+        tsoffset = f->start_time == AV_NOPTS_VALUE ? 0 : f->start_time;
+        if (!start_at_zero && f->ctx->start_time != AV_NOPTS_VALUE)
+            tsoffset += f->ctx->start_time;
+    }
+    ret = insert_trim(((f->start_time == AV_NOPTS_VALUE) || !f->accurate_seek) ?
+                      AV_NOPTS_VALUE : tsoffset, f->recording_time,
+                      &last_filter, &pad_idx, name);
+    if (ret < 0)
+        return ret;
 
-    /* sub2video structure has been (re-)initialized.
-       Mark it as such so that the system will be
-       initialized with the first received heartbeat. */
-    ist->sub2video.initialize = 1;
+    if ((ret = avfilter_link(last_filter, 0, in->filter_ctx, in->pad_idx)) < 0)
+        return ret;
 
     return 0;
+fail:
+    av_freep(&par);
+
+    return ret;
 }
 
 static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter,
@@ -720,8 +874,15 @@ static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter,
     char name[255];
     int ret, pad_idx = 0;
     int64_t tsoffset = 0;
-    AVBufferSrcParameters *par = av_buffersrc_parameters_alloc();
+    AVBufferSrcParameters *par;
+
+    if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_SUBTITLE) {
+        // Automatically insert conversion filter to retain compatibility
+        // with sub2video command lines
+        return configure_input_subtitle_filter(fg, ifilter, in);
+    }
 
+    par = av_buffersrc_parameters_alloc();
     if (!par)
         return AVERROR(ENOMEM);
     memset(par, 0, sizeof(*par));
@@ -736,12 +897,6 @@ static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter,
     if (!fr.num)
         fr = av_guess_frame_rate(input_files[ist->file_index]->ctx, ist->st, NULL);
 
-    if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_SUBTITLE) {
-        ret = sub2video_prepare(ist, ifilter);
-        if (ret < 0)
-            goto fail;
-    }
-
     sar = ifilter->sample_aspect_ratio;
     if(!sar.den)
         sar = (AVRational){0,1};
@@ -753,7 +908,7 @@ static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter,
              tb.num, tb.den, sar.num, sar.den);
     if (fr.num && fr.den)
         av_bprintf(&args, ":frame_rate=%d/%d", fr.num, fr.den);
-    snprintf(name, sizeof(name), "graph %d input from stream %d:%d", fg->index,
+    snprintf(name, sizeof(name), "graph %d video input from stream %d:%d", fg->index,
              ist->file_index, ist->st->index);
 
 
@@ -952,6 +1107,7 @@ static int configure_input_filter(FilterGraph *fg, InputFilter *ifilter,
     switch (avfilter_pad_get_type(in->filter_ctx->input_pads, in->pad_idx)) {
     case AVMEDIA_TYPE_VIDEO: return configure_input_video_filter(fg, ifilter, in);
     case AVMEDIA_TYPE_AUDIO: return configure_input_audio_filter(fg, ifilter, in);
+    case AVMEDIA_TYPE_SUBTITLE: return configure_input_subtitle_filter(fg, ifilter, in);
     default: av_assert0(0); return 0;
     }
 }
@@ -969,8 +1125,9 @@ static void cleanup_filtergraph(FilterGraph *fg)
 static int filter_is_buffersrc(const AVFilterContext *f)
 {
     return f->nb_inputs == 0 &&
-           (!strcmp(f->filter->name, "buffer") ||
-            !strcmp(f->filter->name, "abuffer"));
+           (!strcmp(f->filter->name, "buffersrc") ||
+            !strcmp(f->filter->name, "abuffersrc") ||
+            !strcmp(f->filter->name, "sbuffersrc"));
 }
 
 static int graph_is_meta(AVFilterGraph *graph)
@@ -1147,18 +1304,6 @@ int configure_filtergraph(FilterGraph *fg)
         }
     }
 
-    /* process queued up subtitle packets */
-    for (i = 0; i < fg->nb_inputs; i++) {
-        InputStream *ist = fg->inputs[i]->ist;
-        if (ist->sub2video.sub_queue && ist->sub2video.frame) {
-            AVSubtitle tmp;
-            while (av_fifo_read(ist->sub2video.sub_queue, &tmp, 1) >= 0) {
-                sub2video_update(ist, INT64_MIN, &tmp);
-                avsubtitle_free(&tmp);
-            }
-        }
-    }
-
     return 0;
 
 fail:
@@ -1178,6 +1323,7 @@ int ifilter_parameters_from_frame(InputFilter *ifilter, const AVFrame *frame)
     ifilter->width               = frame->width;
     ifilter->height              = frame->height;
     ifilter->sample_aspect_ratio = frame->sample_aspect_ratio;
+    ifilter->type                = frame->type;
 
     ifilter->sample_rate         = frame->sample_rate;
     ret = av_channel_layout_copy(&ifilter->ch_layout, &frame->ch_layout);
diff --git a/fftools/ffmpeg_hw.c b/fftools/ffmpeg_hw.c
index 14e702bd92..be69d54aaf 100644
--- a/fftools/ffmpeg_hw.c
+++ b/fftools/ffmpeg_hw.c
@@ -449,7 +449,7 @@ int hw_device_setup_for_encode(OutputStream *ost)
     AVBufferRef *frames_ref = NULL;
     int i;
 
-    if (ost->filter) {
+    if (ost->filter && ost->filter->filter) {
         frames_ref = av_buffersink_get_hw_frames_ctx(ost->filter->filter);
         if (frames_ref &&
             ((AVHWFramesContext*)frames_ref->data)->format ==
diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c
index e08455478f..5a52f355a0 100644
--- a/fftools/ffmpeg_opt.c
+++ b/fftools/ffmpeg_opt.c
@@ -1000,6 +1000,7 @@ static void add_input_streams(OptionsContext *o, AVFormatContext *ic)
                 av_log(NULL, AV_LOG_FATAL, "Invalid canvas size: %s.\n", canvas_size);
                 exit_program(1);
             }
+            ist->subtitle_kickoff.is_active = 1;
             break;
         }
         case AVMEDIA_TYPE_ATTACHMENT:
@@ -1715,11 +1716,15 @@ static char *get_ost_filters(OptionsContext *o, AVFormatContext *oc,
 
     if (ost->filters_script)
         return read_file(ost->filters_script);
-    else if (ost->filters)
+    if (ost->filters)
         return av_strdup(ost->filters);
 
-    return av_strdup(st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ?
-                     "null" : "anull");
+    switch (st->codecpar->codec_type) {
+    case AVMEDIA_TYPE_VIDEO: return av_strdup("null");
+    case AVMEDIA_TYPE_AUDIO: return av_strdup("anull");
+    case AVMEDIA_TYPE_SUBTITLE: return av_strdup("snull");
+    default: av_assert0(0); return NULL;
+    }
 }
 
 static void check_streamcopy_filters(OptionsContext *o, AVFormatContext *oc,
@@ -2120,6 +2125,9 @@ static OutputStream *new_subtitle_stream(OptionsContext *o, AVFormatContext *oc,
 
     subtitle_enc->codec_type = AVMEDIA_TYPE_SUBTITLE;
 
+    MATCH_PER_STREAM_OPT(filter_scripts, str, ost->filters_script, oc, st);
+    MATCH_PER_STREAM_OPT(filters,        str, ost->filters,        oc, st);
+
     if (!ost->stream_copy) {
         char *frame_size = NULL;
 
@@ -2128,6 +2136,10 @@ static OutputStream *new_subtitle_stream(OptionsContext *o, AVFormatContext *oc,
             av_log(NULL, AV_LOG_FATAL, "Invalid frame size: %s.\n", frame_size);
             exit_program(1);
         }
+
+        ost->avfilter = get_ost_filters(o, oc, ost);
+        if (!ost->avfilter)
+            exit_program(1);
     }
 
     return ost;
@@ -2273,8 +2285,9 @@ static void init_output_filter(OutputFilter *ofilter, OptionsContext *o,
     switch (ofilter->type) {
     case AVMEDIA_TYPE_VIDEO: ost = new_video_stream(o, oc, -1); break;
     case AVMEDIA_TYPE_AUDIO: ost = new_audio_stream(o, oc, -1); break;
+    case AVMEDIA_TYPE_SUBTITLE: ost = new_subtitle_stream(o, oc, -1); break;
     default:
-        av_log(NULL, AV_LOG_FATAL, "Only video and audio filters are supported "
+        av_log(NULL, AV_LOG_FATAL, "Only video, audio and subtitle filters are supported "
                "currently.\n");
         exit_program(1);
     }
@@ -2679,7 +2692,8 @@ loop_end:
             ist->processing_needed = 1;
 
             if (ost->st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ||
-                ost->st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
+                ost->st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO ||
+                ost->st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) {
                 err = init_simple_filtergraph(ist, ost);
                 if (err < 0) {
                     av_log(NULL, AV_LOG_ERROR,
@@ -2724,6 +2738,10 @@ loop_end:
                 } else if (ost->enc->ch_layouts) {
                     f->ch_layouts = ost->enc->ch_layouts;
                 }
+                break;
+            case AVMEDIA_TYPE_SUBTITLE:
+                f->format     = ost->enc_ctx->subtitle_type;
+
                 break;
             }
         }
diff --git a/tests/ref/fate/filter-overlay-dvdsub-2397 b/tests/ref/fate/filter-overlay-dvdsub-2397
index 7df4f50776..7b827956f1 100644
--- a/tests/ref/fate/filter-overlay-dvdsub-2397
+++ b/tests/ref/fate/filter-overlay-dvdsub-2397
@@ -489,368 +489,368 @@
 1,       3877,       3877,       10,     2013, 0x95a39f9c
 1,       3887,       3887,       10,     2013, 0x4f7ea123
 1,       3897,       3897,       10,     2013, 0x9efb9ba1
-0,        117,        117,        1,   518400, 0xbf8523da
+0,        117,        117,        1,   518400, 0xc44e1d4c
 1,       3907,       3907,       10,     2013, 0xf395b2cd
 1,       3917,       3917,       10,     2013, 0x261a881e
 1,       3927,       3927,       10,     2013, 0x7f2d9f72
 1,       3937,       3937,       10,     2013, 0x0105b38d
-0,        118,        118,        1,   518400, 0x41890ed6
+0,        118,        118,        1,   518400, 0xad1a084c
 1,       3952,       3952,       10,     2013, 0x0e5db67e
 1,       3962,       3962,       10,     2013, 0xfc9baf97
-0,        119,        119,        1,   518400, 0x588534fc
+0,        119,        119,        1,   518400, 0x52d52e73
 1,       3972,       3972,       10,     2013, 0x8e02a1b1
 1,       3982,       3982,       10,     2013, 0x6eecaac8
 1,       3992,       3992,       10,     2013, 0xf5558f0c
 1,       4002,       4002,       10,     2013, 0x512ba99b
-0,        120,        120,        1,   518400, 0x2145ebc1
+0,        120,        120,        1,   518400, 0xc732e546
 1,       4012,       4012,       10,     2013, 0x932b9932
 1,       4022,       4022,       10,     2013, 0xc01ea987
-0,        121,        121,        1,   518400, 0x28bca595
+0,        121,        121,        1,   518400, 0xc36f9f14
 1,       4038,       4038,       10,     2013, 0x10879cf7
 1,       4048,       4048,       10,     2013, 0x90679338
 1,       4058,       4058,       10,     2013, 0x077d8a9e
 1,       4068,       4068,       10,     2013, 0x969fa57c
-0,        122,        122,        1,   518400, 0x77dc951e
+0,        122,        122,        1,   518400, 0x78428e8b
 1,       4078,       4078,       10,     2013, 0xe049ab07
 1,       4088,       4088,       10,     2013, 0xf535b3b3
 1,       4098,       4098,       10,     2013, 0xfe76bd37
-0,        123,        123,        1,   518400, 0xe8924c17
+0,        123,        123,        1,   518400, 0xf0f8458d
 1,       4108,       4108,       10,     2013, 0xde79ad8c
 1,       4123,       4123,       10,     2013, 0xe89b9c47
 1,       4133,       4133,       10,     2013, 0xc570b0f0
-0,        124,        124,        1,   518400, 0xadb4cccc
+0,        124,        124,        1,   518400, 0x7083c653
 1,       4143,       4143,       10,     2013, 0xee709cd9
 1,       4153,       4153,       10,     2013, 0xcfe5afab
 1,       4163,       4163,       10,     2013, 0x98ff8ce4
-0,        125,        125,        1,   518400, 0x1d7b56ac
+0,        125,        125,        1,   518400, 0xa105502c
 1,       4173,       4173,       10,     2013, 0x9d19b44c
 1,       4183,       4183,       10,     2013, 0x4349917a
 1,       4193,       4193,       10,     2013, 0xbf54a59a
-0,        126,        126,        1,   518400, 0xad5739a4
+0,        126,        126,        1,   518400, 0xd411331a
 1,       4208,       4208,       10,     2013, 0xc4a399e0
 1,       4218,       4218,       10,     2013, 0x1bf58ff0
 1,       4228,       4228,       10,     2013, 0x3518ac56
-0,        127,        127,        1,   518400, 0x2733d35a
+0,        127,        127,        1,   518400, 0x83b0ccdb
 1,       4238,       4238,       10,     2013, 0xcd38c1de
 1,       4248,       4248,       10,     2013, 0xbe7d9c4d
 1,       4258,       4258,       10,     2013, 0xe113a306
 1,       4268,       4268,       10,     2013, 0x083197ea
-0,        128,        128,        1,   518400, 0x78e76da2
+0,        128,        128,        1,   518400, 0xa9be671a
 1,       4278,       4278,       10,     2013, 0x1929b1eb
 1,       4294,       4294,       10,     2013, 0x5d6ea5af
 1,       4304,       4304,       10,     2013, 0x05519d53
-0,        129,        129,        1,   518400, 0x6c076013
+0,        129,        129,        1,   518400, 0xaeb75983
 1,       4314,       4314,       10,     2013, 0x5773b380
 1,       4324,       4324,       10,     2013, 0xaa70a8f5
 1,       4334,       4334,       10,     2013, 0x990db0ec
-0,        130,        130,        1,   518400, 0x7854f2b1
+0,        130,        130,        1,   518400, 0x81f8ec13
 1,       4344,       4344,       10,     2013, 0x91d3a623
 1,       4354,       4354,       10,     2013, 0xc91f9824
 1,       4364,       4364,       10,     2013, 0x1d058abf
-0,        131,        131,        1,   518400, 0xd2ae1ecd
+0,        131,        131,        1,   518400, 0x8aaa1839
 1,       4379,       4379,       10,     2013, 0x8de1b8d5
 1,       4389,       4389,       10,     2013, 0x7872b06b
 1,       4399,       4399,       10,     2013, 0xa084c203
-0,        132,        132,        1,   518400, 0xf5eab38d
+0,        132,        132,        1,   518400, 0xc98bacf5
 1,       4409,       4409,       10,     2013, 0xff90ae8d
 1,       4419,       4419,       10,     2013, 0x61dead8e
 1,       4429,       4429,       10,     2013, 0xee76b284
-0,        133,        133,        1,   518400, 0x994d3e9c
+0,        133,        133,        1,   518400, 0x31083804
 1,       4439,       4439,       10,     2013, 0xe888af7f
 1,       4449,       4449,       10,     2013, 0x5d57b115
 1,       4464,       4464,       10,     2013, 0xcdbfb1d0
-0,        134,        134,        1,   518400, 0x95ab705a
+0,        134,        134,        1,   518400, 0x540a69dc
 1,       4474,       4474,       10,     2013, 0x2e28a952
 1,       4484,       4484,       10,     2013, 0x4795a994
 1,       4494,       4494,       10,     2013, 0x7e7ea304
 1,       4504,       4504,       10,     2013, 0x9502c1e1
-0,        135,        135,        1,   518400, 0x3c83c5ce
+0,        135,        135,        1,   518400, 0x80d3bf46
 1,       4514,       4514,       10,     2013, 0xf7c78ab2
 1,       4524,       4524,       10,     2013, 0x24049816
 1,       4534,       4534,       10,     2013, 0x52089dcf
-0,        136,        136,        1,   518400, 0xfa22c508
+0,        136,        136,        1,   518400, 0x2967be7f
 1,       4550,       4550,       10,     2013, 0x2150a0b1
 1,       4560,       4560,       10,     2013, 0x3c2e9b93
 1,       4570,       4570,       10,     2013, 0x491f932b
-0,        137,        137,        1,   518400, 0xddda1712
+0,        137,        137,        1,   518400, 0x5a3b1092
 1,       4580,       4580,       10,     2013, 0x31359cf8
 1,       4590,       4590,       10,     2013, 0x1b00ac3f
 1,       4600,       4600,       10,     2013, 0x8d7ab3cb
-0,        138,        138,        1,   518400, 0x985a3b93
+0,        138,        138,        1,   518400, 0x8741350b
 1,       4610,       4610,       10,     2013, 0xb2c2a4de
 1,       4620,       4620,       10,     2013, 0x80a4abf2
 1,       4635,       4635,       10,     2013, 0x0701a4ee
-0,        139,        139,        1,   518400, 0xea63c5e7
+0,        139,        139,        1,   518400, 0xd5a9bf60
 1,       4645,       4645,       10,     2013, 0xdc1ba5bc
 1,       4655,       4655,       10,     2013, 0x6083a8a4
 1,       4665,       4665,       10,     2013, 0x6226ad45
-0,        140,        140,        1,   518400, 0xef64983d
+0,        140,        140,        1,   518400, 0xc05f91ba
 1,       4675,       4675,       10,     2013, 0x2732a205
 1,       4685,       4685,       10,     2013, 0x0f62a0d3
 1,       4695,       4695,       10,     2013, 0xc1799249
-0,        141,        141,        1,   518400, 0x747bb193
+0,        141,        141,        1,   518400, 0x3fdaab0b
 1,       4705,       4705,       10,     2013, 0xbccfa9c8
 1,       4720,       4720,       10,     2013, 0xded096e7
 1,       4730,       4730,       10,     2013, 0x7f0daf43
-0,        142,        142,        1,   518400, 0xb8748862
+0,        142,        142,        1,   518400, 0xab7281d9
 1,       4740,       4740,       10,     2013, 0xc47ea682
 1,       4750,       4750,       10,     2013, 0x5a72b07a
 1,       4760,       4760,       10,     2013, 0x386faa8c
 1,       4770,       4770,       10,     2013, 0xf9919a91
-0,        143,        143,        1,   518400, 0xaab55a5f
+0,        143,        143,        1,   518400, 0xc80053d6
 1,       4780,       4780,       10,     2013, 0x4908897e
 1,       4790,       4790,       10,     2013, 0x4882b594
-0,        144,        144,        1,   518400, 0x7b468add
+0,        144,        144,        1,   518400, 0x6526845c
 1,       4806,       4806,       10,     2013, 0x113e98d1
 1,       4816,       4816,       10,     2013, 0x5098b30d
 1,       4826,       4826,       10,     2013, 0x0ef7b857
 1,       4836,       4836,       10,     2013, 0x216ea176
-0,        145,        145,        1,   518400, 0xf2078707
+0,        145,        145,        1,   518400, 0x1b788089
 1,       4846,       4846,       10,     2013, 0xf906944a
 1,       4856,       4856,       10,     2013, 0xee9b92fb
 1,       4866,       4866,       10,     2013, 0xd6029209
-0,        146,        146,        1,   518400, 0x6a2d931e
+0,        146,        146,        1,   518400, 0xfa8e8ca9
 1,       4876,       4876,       10,     2013, 0x2256a12e
 1,       4891,       4891,       10,     2013, 0x89de8e4a
 1,       4901,       4901,       10,     2013, 0x0bf0a584
-0,        147,        147,        1,   518400, 0xbbe3c417
+0,        147,        147,        1,   518400, 0xb278bda1
 1,       4911,       4911,       10,     2013, 0x6a5ebd58
 1,       4921,       4921,       10,     2013, 0x3edd9aa4
 1,       4931,       4931,       10,     2013, 0xbd66ac26
-0,        148,        148,        1,   518400, 0x6294e449
+0,        148,        148,        1,   518400, 0xb0c3ddca
 1,       4941,       4941,       10,     2013, 0x313896ea
 1,       4951,       4951,       10,     2013, 0x6b83a6a0
 1,       4961,       4961,       10,     2013, 0x9aafb109
-0,        149,        149,        1,   518400, 0xa05721e7
+0,        149,        149,        1,   518400, 0x10351b53
 1,       4976,       4976,       10,     2013, 0x5192a85a
 1,       4986,       4986,       10,     2013, 0x1f919f79
 1,       4996,       4996,       10,     2013, 0xc0799c40
-0,        150,        150,        1,   518400, 0x37749183
+0,        150,        150,        1,   518400, 0xc1408aee
 1,       5006,       5006,       10,     2013, 0x2988bcd8
 1,       5016,       5016,       10,     2013, 0x1482913a
 1,       5026,       5026,       10,     2013, 0x74da9a94
 1,       5036,       5036,       10,     2013, 0x763eb709
-0,        151,        151,        1,   518400, 0xf9d9dca0
+0,        151,        151,        1,   518400, 0xf016d615
 1,       5046,       5046,       10,     2013, 0x1285b405
 1,       5062,       5062,       10,     2013, 0xb6ab9dfc
-0,        152,        152,        1,   518400, 0x5f8ccf08
+0,        152,        152,        1,   518400, 0xa768c892
 1,       5072,       5072,       10,     2013, 0xe4c8bf19
 1,       5082,       5082,       10,     2013, 0xabbbade8
 1,       5092,       5092,       10,     2013, 0xf8b69d89
 1,       5102,       5102,       10,     2013, 0xce04a866
-0,        153,        153,        1,   518400, 0x7303f77b
+0,        153,        153,        1,   518400, 0x11c3f11e
 1,       5112,       5112,       10,     2013, 0x07528abf
 1,       5122,       5122,       10,     2013, 0x74fb98bf
 1,       5132,       5132,       10,     2013, 0x579fb1c9
-0,        154,        154,        1,   518400, 0x22b0513f
+0,        154,        154,        1,   518400, 0xcd9a4ac4
 1,       5147,       5147,       10,     2013, 0x7ddea2ed
 1,       5157,       5157,       10,     2013, 0x296caa2c
 1,       5167,       5167,       10,     2013, 0x346d9c4f
-0,        155,        155,        1,   518400, 0x330485d2
+0,        155,        155,        1,   518400, 0x4ade7f5e
 1,       5177,       5177,       10,     2013, 0x3e1fba15
 1,       5187,       5187,       10,     2013, 0x48a2908f
 1,       5197,       5197,       10,     2013, 0xc1938d09
-0,        156,        156,        1,   518400, 0x7f83daea
+0,        156,        156,        1,   518400, 0x655dd46b
 1,       5207,       5207,       10,     2013, 0x0e96a060
 1,       5217,       5217,       10,     2013, 0x7b6a9e06
 1,       5232,       5232,       10,     2013, 0x5b779d28
-0,        157,        157,        1,   518400, 0xee19f2df
+0,        157,        157,        1,   518400, 0x5ab5ec61
 1,       5242,       5242,       10,     2013, 0xf600aca1
 1,       5252,       5252,       10,     2013, 0x3a6c9e68
 1,       5262,       5262,       10,     2013, 0x0c8dc1b0
-0,        158,        158,        1,   518400, 0xb71b1c77
+0,        158,        158,        1,   518400, 0x45dc15e6
 1,       5272,       5272,       10,     2013, 0x26beb245
 1,       5282,       5282,       10,     2013, 0x2bc09557
 1,       5292,       5292,       10,     2013, 0x27fc8845
 1,       5302,       5302,       10,     2013, 0x1025aa47
-0,        159,        159,        1,   518400, 0xbffc1856
+0,        159,        159,        1,   518400, 0x201911d3
 1,       5318,       5318,       10,     2013, 0xc2e69baa
 1,       5328,       5328,       10,     2013, 0xdb249b92
 1,       5338,       5338,       10,     2013, 0x6ccda29e
-0,        160,        160,        1,   518400, 0xabc125aa
+0,        160,        160,        1,   518400, 0x0fbc1f46
 1,       5348,       5348,       10,     2013, 0xeaf6a1cf
 1,       5358,       5358,       10,     2013, 0x509ba397
 1,       5368,       5368,       10,     2013, 0xfaf8a2df
-0,        161,        161,        1,   518400, 0x5ee467f8
+0,        161,        161,        1,   518400, 0x7e316179
 1,       5378,       5378,       10,     2013, 0x41388f28
 1,       5388,       5388,       10,     2013, 0xfe5eab39
 1,       5403,       5403,       10,     2013, 0xd5ffa066
-0,        162,        162,        1,   518400, 0x6c2cf168
+0,        162,        162,        1,   518400, 0x73bbeaed
 1,       5413,       5413,       10,     2013, 0x6813a30a
 1,       5423,       5423,       10,     2013, 0x9be89718
 1,       5433,       5433,       10,     2013, 0xaec3a27b
-0,        163,        163,        1,   518400, 0x63996b26
+0,        163,        163,        1,   518400, 0x3a7c648a
 1,       5446,       5446,       10,     2013, 0x579a983e
 1,       5456,       5456,       10,     2013, 0x98cea21f
 1,       5466,       5466,       10,     2013, 0xca77a58a
-0,        164,        164,        1,   518400, 0xb34d789a
+0,        164,        164,        1,   518400, 0x9f707209
 1,       5476,       5476,       10,     2013, 0xcbc3b1ee
 1,       5486,       5486,       10,     2013, 0xf3bb8f07
 1,       5496,       5496,       10,     2013, 0x6aeebd92
-0,        165,        165,        1,   518400, 0xf49c030f
+0,        165,        165,        1,   518400, 0x9f25fc5c
 1,       5506,       5506,       10,     2013, 0xe955a449
 1,       5516,       5516,       10,     2013, 0x9436aa5b
 1,       5531,       5531,       10,     2013, 0x4f0a8f9f
-0,        166,        166,        1,   518400, 0x092dc41a
+0,        166,        166,        1,   518400, 0x2ed8bd75
 1,       5541,       5541,       10,     2013, 0x3551b22d
 1,       5551,       5551,       10,     2013, 0x0959a3d4
 1,       5561,       5561,       10,     2013, 0x2ed5a11b
 1,       5571,       5571,       10,     2013, 0x8f52a5c3
-0,        167,        167,        1,   518400, 0x4134c577
+0,        167,        167,        1,   518400, 0xb493becb
 1,       5581,       5581,       10,     2013, 0x6552978d
 1,       5591,       5591,       10,     2013, 0x7dcca0c1
 1,       5601,       5601,       10,     2013, 0xbcd4a3c9
-0,        168,        168,        1,   518400, 0x261de1ed
+0,        168,        168,        1,   518400, 0x7df6db57
 1,       5616,       5616,       10,     2013, 0xfe41a8d8
 1,       5626,       5626,       10,     2013, 0xc85aae14
 1,       5636,       5636,       10,     2013, 0x1185b346
-0,        169,        169,        1,   518400, 0xcbc8566a
+0,        169,        169,        1,   518400, 0x1cb94fca
 1,       5646,       5646,       10,     2013, 0xf7429a0d
 1,       5656,       5656,       10,     2013, 0x48c2a160
 1,       5666,       5666,       10,     2013, 0x9d85a85d
-0,        170,        170,        1,   518400, 0x407a5c76
+0,        170,        170,        1,   518400, 0x70db55d8
 1,       5676,       5676,       10,     2013, 0xbbe89fe9
 1,       5686,       5686,       10,     2013, 0xea429fe2
 1,       5702,       5702,       10,     2013, 0x221ca1d4
-0,        171,        171,        1,   518400, 0x1ed73bb2
+0,        171,        171,        1,   518400, 0xc1d9351b
 1,       5712,       5712,       10,     2013, 0x394b925b
 1,       5722,       5722,       10,     2013, 0x556dc26f
 1,       5732,       5732,       10,     2013, 0xce21a5e1
-0,        172,        172,        1,   518400, 0x8467ddb5
+0,        172,        172,        1,   518400, 0xa4b0d717
 1,       5742,       5742,       10,     2013, 0xbc87c0a8
 1,       5752,       5752,       10,     2013, 0xbac4ac07
 1,       5762,       5762,       10,     2013, 0xdeefa4aa
 1,       5772,       5772,       10,     2013, 0x1f15b362
-0,        173,        173,        1,   518400, 0x0523dc73
+0,        173,        173,        1,   518400, 0x3730d5e9
 1,       5787,       5787,       10,     2013, 0x6406b7b2
 1,       5797,       5797,       10,     2013, 0x8030a03d
-0,        174,        174,        1,   518400, 0x81f5e895
+0,        174,        174,        1,   518400, 0x9673e1ec
 1,       5807,       5807,       10,     2013, 0x0373a5b1
 1,       5817,       5817,       10,     2013, 0x34ef93da
 1,       5827,       5827,       10,     2013, 0x94c198fe
 1,       5837,       5837,       10,     2013, 0xfefcabad
-0,        175,        175,        1,   518400, 0xfc74608d
+0,        175,        175,        1,   518400, 0x877959d5
 1,       5847,       5847,       10,     2013, 0x8755b3ec
 1,       5857,       5857,       10,     2013, 0xe436a6fd
 1,       5872,       5872,       10,     2013, 0x9cf5a11e
-0,        176,        176,        1,   518400, 0xc4e0dae0
+0,        176,        176,        1,   518400, 0x04f3d421
 1,       5882,       5882,       10,     2013, 0x03b8a98c
 1,       5892,       5892,       10,     2013, 0x6216a138
 1,       5902,       5902,       10,     2013, 0xd87b9f12
-0,        177,        177,        1,   518400, 0x98367f5b
+0,        177,        177,        1,   518400, 0x4f3078bc
 1,       5912,       5912,       10,     2013, 0x4ce99653
 1,       5922,       5922,       10,     2013, 0x6c2ea9e2
 1,       5932,       5932,       10,     2013, 0x918cae4c
-0,        178,        178,        1,   518400, 0x0f1a869d
+0,        178,        178,        1,   518400, 0x8a127ff8
 1,       5942,       5942,       10,     2013, 0xd19fa5f2
 1,       5958,       5958,       10,     2013, 0x0bdda7c6
 1,       5968,       5968,       10,     2013, 0x0f9ab0ca
-0,        179,        179,        1,   518400, 0x45b6ccf2
+0,        179,        179,        1,   518400, 0x5864c64f
 1,       5978,       5978,       10,     2013, 0x410a92b1
 1,       5988,       5988,       10,     2013, 0xcfbe9d1c
 1,       5998,       5998,       10,     2013, 0x59ed9d15
-0,        180,        180,        1,   518400, 0x5f9ccb77
+0,        180,        180,        1,   518400, 0xdaccc4c0
 1,       6008,       6008,       10,     2013, 0x4e129e27
 1,       6018,       6018,       10,     2013, 0x7bb9ac0a
 1,       6028,       6028,       10,     2013, 0x826ca82b
-0,        181,        181,        1,   518400, 0x5f15ea31
+0,        181,        181,        1,   518400, 0xd999e376
 1,       6043,       6043,       10,     2013, 0x9ad5a74b
 1,       6053,       6053,       10,     2013, 0x6c5f969a
 1,       6063,       6063,       10,     2013, 0x8479a0e5
-0,        182,        182,        1,   518400, 0x86369f27
+0,        182,        182,        1,   518400, 0x8af39876
 1,       6073,       6073,       10,     2013, 0x165298ef
 1,       6083,       6083,       10,     2013, 0xdcadb4a1
 1,       6093,       6093,       10,     2013, 0xa90e987c
 1,       6103,       6103,       10,     2013, 0x1ac5b510
-0,        183,        183,        1,   518400, 0x2e27f9fa
+0,        183,        183,        1,   518400, 0x5e72f33d
 1,       6113,       6113,       10,     2013, 0x66728d85
 1,       6128,       6128,       10,     2013, 0xe4859fc5
 1,       6138,       6138,       10,     2013, 0x9901786e
-0,        184,        184,        1,   518400, 0xc029a44d
+0,        184,        184,        1,   518400, 0x14af9d92
 1,       6148,       6148,       10,     2013, 0x6aebb406
 1,       6158,       6158,       10,     2013, 0x7d13a2cc
 1,       6168,       6168,       10,     2013, 0x99b7a8cc
-0,        185,        185,        1,   518400, 0xebee33b0
+0,        185,        185,        1,   518400, 0x50b82d10
 1,       6178,       6178,       10,     2013, 0x80b8a624
 1,       6188,       6188,       10,     2013, 0xbb6aa271
 1,       6198,       6198,       10,     2013, 0x17af9e4a
-0,        186,        186,        1,   518400, 0x19e5494f
+0,        186,        186,        1,   518400, 0xc068429c
 1,       6214,       6214,       10,     2013, 0xfaf0a8f1
 1,       6224,       6224,       10,     2013, 0xd6849b93
 1,       6234,       6234,       10,     2013, 0xe9829669
-0,        187,        187,        1,   518400, 0xf697bd7c
+0,        187,        187,        1,   518400, 0x8934b6d1
 1,       6244,       6244,       10,     2013, 0x7ec98944
 1,       6254,       6254,       10,     2013, 0x2b2099a4
 1,       6264,       6264,       10,     2013, 0x1033a82f
-0,        188,        188,        1,   518400, 0x82569002
+0,        188,        188,        1,   518400, 0x11d08947
 1,       6274,       6274,       10,     2013, 0x5ec88990
 1,       6284,       6284,       10,     2013, 0xd2a19b3d
 1,       6299,       6299,       10,     2013, 0xa377b268
-0,        189,        189,        1,   518400, 0xfcb6d707
+0,        189,        189,        1,   518400, 0x8a27d041
 1,       6309,       6309,       10,     2013, 0xfa859901
 1,       6319,       6319,       10,     2013, 0x1713955a
 1,       6329,       6329,       10,     2013, 0x70aab0da
 1,       6339,       6339,       10,     2013, 0xcdaea422
-0,        190,        190,        1,   518400, 0x82a9662b
+0,        190,        190,        1,   518400, 0xab265f7d
 1,       6349,       6349,       10,     2013, 0x65c3bf80
 1,       6359,       6359,       10,     2013, 0x1d75a55f
 1,       6369,       6369,       10,     2013, 0xa5bea4de
-0,        191,        191,        1,   518400, 0x212e16ee
+0,        191,        191,        1,   518400, 0xff491040
 1,       6384,       6384,       10,     2013, 0x184db71c
 1,       6394,       6394,       10,     2013, 0x99858ec8
 1,       6404,       6404,       10,     2013, 0xb8f2aee5
-0,        192,        192,        1,   518400, 0x2ca34dca
+0,        192,        192,        1,   518400, 0x822b4704
 1,       6414,       6414,       10,     2013, 0x4435b2ef
 1,       6424,       6424,       10,     2013, 0x8acfa6c7
 1,       6434,       6434,       10,     2013, 0x42b4c01f
-0,        193,        193,        1,   518400, 0xe9ebe0a5
+0,        193,        193,        1,   518400, 0x4523d9f4
 1,       6444,       6444,       10,     2013, 0x6e308c13
 1,       6454,       6454,       10,     2013, 0x8227a0f6
 1,       6470,       6470,       10,     2013, 0x6f12a7a2
-0,        194,        194,        1,   518400, 0x4e6b6917
+0,        194,        194,        1,   518400, 0xfc3c626e
 1,       6480,       6480,       10,     2013, 0x785392be
 1,       6490,       6490,       10,     2013, 0x81849c2b
 1,       6500,       6500,       10,     2013, 0x5cf2af65
-0,        195,        195,        1,   518400, 0x7dcf20ab
+0,        195,        195,        1,   518400, 0x237319e5
 1,       6510,       6510,       10,     2013, 0x0c6ca6b4
 1,       6520,       6520,       10,     2013, 0x412fab9f
 1,       6530,       6530,       10,     2013, 0x08e792b4
-0,        196,        196,        1,   518400, 0xf30fac97
+0,        196,        196,        1,   518400, 0x892ca5d8
 1,       6540,       6540,       10,     2013, 0x407aace3
 1,       6555,       6555,       10,     2013, 0xd26bac16
 1,       6565,       6565,       10,     2013, 0xac8bb295
-0,        197,        197,        1,   518400, 0xcb9fc692
+0,        197,        197,        1,   518400, 0xc4c0bfc7
 1,       6575,       6575,       10,     2013, 0xddd1949c
 1,       6585,       6585,       10,     2013, 0x6b26b868
 1,       6595,       6595,       10,     2013, 0x5eaba587
 1,       6605,       6605,       10,     2013, 0xef0793b9
-0,        198,        198,        1,   518400, 0x5d05601e
+0,        198,        198,        1,   518400, 0x57c85956
 1,       6615,       6615,       10,     2013, 0xdef19bd6
 1,       6625,       6625,       10,     2013, 0xca98a635
-0,        199,        199,        1,   518400, 0x456c1417
+0,        199,        199,        1,   518400, 0xd6300d46
 1,       6640,       6640,       10,     2013, 0x06269a5a
 1,       6650,       6650,       10,     2013, 0x32cb9952
 1,       6660,       6660,       10,     2013, 0xf01fa95a
 1,       6670,       6670,       10,     2013, 0xefab9e55
-0,        200,        200,        1,   518400, 0x9a0fd1ad
+0,        200,        200,        1,   518400, 0xd3dacaec
 1,       6680,       6680,       10,     2013, 0x55a3b63a
 1,       6690,       6690,       10,     2013, 0xcd36a553
 1,       6700,       6700,       10,     2013, 0x2ec19877
-0,        201,        201,        1,   518400, 0x55db9716
+0,        201,        201,        1,   518400, 0x65429052
 1,       6710,       6710,       10,     2013, 0xc18b924c
 1,       6726,       6726,       10,     2013, 0xf132b04c
 1,       6736,       6736,       10,     2013, 0x7975a44d
-0,        202,        202,        1,   518400, 0x1f0d40d6
+0,        202,        202,        1,   518400, 0xec803a15
 1,       6746,       6746,       10,     2013, 0x2aaf94cb
 1,       6756,       6756,       10,     2013, 0x58cfa60f
 1,       6766,       6766,       10,     2013, 0x9757a658
-0,        203,        203,        1,   518400, 0x73695c82
+0,        203,        203,        1,   518400, 0x7a9a55c9
 1,       6776,       6776,       10,     2013, 0x67ebc0d5
 1,       6786,       6786,       10,     2013, 0x3c50a70e
 1,       6796,       6796,       10,     2013, 0x9c5799c6
-0,        204,        204,        1,   518400, 0xb0f10812
+0,        204,        204,        1,   518400, 0xcac30160
 1,       6811,       6811,       10,     2013, 0x018d85b2
 1,       6821,       6821,       10,     2013, 0x5367a956
-0,        205,        205,        1,   518400, 0xdec18505
-0,        208,        208,        1,   518400, 0xb147b947
-0,        240,        240,        1,   518400, 0x9d2e3977
+0,        205,        205,        1,   518400, 0x7e187e4f
+0,        208,        208,        1,   518400, 0x0be0b2a2
+0,        213,        213,        1,   518400, 0x0be0b2a2
diff --git a/tests/ref/fate/sub-dvb b/tests/ref/fate/sub-dvb
index cbd1801d64..8f33c75d70 100644
--- a/tests/ref/fate/sub-dvb
+++ b/tests/ref/fate/sub-dvb
@@ -1,75 +1,93 @@
 #tb 0: 1/1000000
 #media_type 0: subtitle
 #codec_id 0: dvb_subtitle
-0,   15600000,   15600000,   159000,     1168, 0xd0f89d82
-0,   15759000,   15759000,   159000,       14, 0x064900eb
-0,   15760000,   15760000,   239000,     1544, 0xe60f1751
-0,   15999000,   15999000,   239000,       14, 0x0729010b
-0,   16000000,   16000000,   339000,     1658, 0xbe343093
-0,   16339000,   16339000,   339000,       14, 0x0809012b
-0,   16340000,   16340000,   599000,     2343, 0xc68f07ef
-0,   16939000,   16939000,   599000,       14, 0x08e9014b
-0,   16940000,   16940000,   459000,     2568, 0x0ee657b1
-0,   17399000,   17399000,   459000,       14, 0x09c9016b
-0,   17400000,   17400000,   359000,     3422, 0xba5b63ce
-0,   17759000,   17759000,   359000,       14, 0x0aa9018b
-0,   17760000,   17760000,   219000,     5078, 0x95b19902
-0,   17979000,   17979000,   219000,       14, 0x0b8901ab
-0,   17980000,   17980000,   959000,     5808, 0xc9717b89
-0,   18939000,   18939000,   959000,       14, 0x0c6901cb
-0,   18940000,   18940000,   219000,     6015, 0x0becbfac
-0,   19159000,   19159000,   219000,       14, 0x064900eb
-0,   19160000,   19160000,   259000,     6519, 0xfcd24d26
-0,   19419000,   19419000,   259000,       14, 0x0729010b
-0,   19420000,   19420000,    99000,     7061, 0xf0320408
-0,   19519000,   19519000,    99000,       14, 0x0809012b
-0,   19520000,   19520000,   219000,     4773, 0x66c93074
-0,   19739000,   19739000,   219000,       14, 0x08e9014b
-0,   19740000,   19740000,   219000,     5546, 0x06052c81
-0,   19959000,   19959000,   219000,       14, 0x09c9016b
-0,   19960000,   19960000,   239000,     5754, 0x904f7325
-0,   20199000,   20199000,   239000,       14, 0x0aa9018b
-0,   20200000,   20200000,   139000,     6099, 0xe30cde07
-0,   20339000,   20339000,   139000,       14, 0x0b8901ab
-0,   20340000,   20340000,   799000,     6839, 0x770fcb6c
-0,   21139000,   21139000,   799000,       14, 0x0c6901cb
-0,   21140000,   21140000,   239000,     4744, 0xa91e1b41
-0,   21379000,   21379000,   239000,       14, 0x064900eb
-0,   21380000,   21380000,   339000,     5824, 0xcf6d782b
-0,   21719000,   21719000,   339000,       14, 0x0729010b
-0,   21720000,   21720000,  1439000,     6212, 0xabf8f7cf
-0,   23159000,   23159000,  1439000,       14, 0x0809012b
-0,   23160000,   23160000,  1319000,     7082, 0xd7ca10f2
-0,   24479000,   24479000,  1319000,       14, 0x08e9014b
-0,   24480000,   24480000,   219000,     5345, 0x12b2cae0
-0,   24699000,   24699000,   219000,       14, 0x09c9016b
-0,   24700000,   24700000,   219000,     5765, 0xc7d46192
-0,   24919000,   24919000,   219000,       14, 0x0aa9018b
-0,   24920000,   24920000,   599000,     6557, 0xcb995d30
-0,   25519000,   25519000,   599000,       14, 0x0b8901ab
-0,   25520000,   25520000,   219000,     7091, 0xe6ea0559
-0,   25739000,   25739000,   219000,       14, 0x0c6901cb
-0,   25740000,   25740000,   239000,     7305, 0xb66c404e
-0,   25979000,   25979000,   239000,       14, 0x064900eb
-0,   25980000,   25980000,   359000,     7590, 0x0cc2a481
-0,   26339000,   26339000,   359000,       14, 0x0729010b
-0,   26340000,   26340000,   219000,     4629, 0xe18cfea8
-0,   26559000,   26559000,   219000,       14, 0x0809012b
-0,   26560000,   26560000,   719000,     4785, 0x82043fc0
-0,   27279000,   27279000,   719000,       14, 0x08e9014b
-0,   27280000,   27280000,   459000,     6061, 0xbde7d245
-0,   27739000,   27739000,   459000,       14, 0x09c9016b
-0,   27740000,   27740000,   239000,     6301, 0x92d01a51
-0,   27979000,   27979000,   239000,       14, 0x0aa9018b
-0,   27980000,   27980000,    99000,     6736, 0xbd25a134
-0,   28079000,   28079000,    99000,       14, 0x0b8901ab
-0,   28080000,   28080000,   219000,     7214, 0x7ef93c13
-0,   28299000,   28299000,   219000,       14, 0x0c6901cb
-0,   28300000,   28300000,   239000,     7366, 0x5bed7fcd
-0,   28539000,   28539000,   239000,       14, 0x064900eb
-0,   28540000,   28540000,   599000,     4564, 0x7f4c014b
-0,   29139000,   29139000,   599000,       14, 0x0729010b
-0,   29140000,   29140000,   219000,     4637, 0x682626b7
-0,   29359000,   29359000,   219000,       14, 0x0809012b
-0,   29360000,   29360000,  1679000,     5358, 0x29e30c48
-0,   31039000,   31039000,  1679000,       14, 0x08e9014b
+0,          0,          0,   279000,       14, 0x05d900db
+0,     279000,     279000,   279000,       14, 0x064900eb
+0,     280000,     280000,  4999000,       14, 0x06b900fb
+0,    5279000,    5279000,  4999000,       14, 0x0729010b
+0,    5280000,    5280000,  5019000,       14, 0x0799011b
+0,   10299000,   10299000,  5019000,       14, 0x0809012b
+0,   10300000,   10300000,  3599000,       14, 0x0879013b
+0,   13899000,   13899000,  3599000,       14, 0x08e9014b
+0,   13900000,   13900000,   219000,       14, 0x0959015b
+0,   14119000,   14119000,   219000,       14, 0x09c9016b
+0,   14120000,   14120000,  1439000,       14, 0x0a39017b
+0,   15559000,   15559000,  1439000,       14, 0x0aa9018b
+0,   15560000,   15560000,    39000,       14, 0x0b19019b
+0,   15599000,   15599000,    39000,       14, 0x0b8901ab
+0,   15600000,   15600000,   159000,     1168, 0xd69da022
+0,   15759000,   15759000,   159000,       14, 0x0c6901cb
+0,   15760000,   15760000,   239000,     1544, 0xc5f116f1
+0,   15999000,   15999000,   239000,       14, 0x064900eb
+0,   16000000,   16000000,   339000,     1658, 0x73563033
+0,   16339000,   16339000,   339000,       14, 0x0729010b
+0,   16340000,   16340000,   599000,     2343, 0x7ac2078f
+0,   16939000,   16939000,   599000,       14, 0x0809012b
+0,   16940000,   16940000,   459000,     2568, 0x6eaa5751
+0,   17399000,   17399000,   459000,       14, 0x08e9014b
+0,   17400000,   17400000,   359000,     3422, 0xd9d0636e
+0,   17759000,   17759000,   359000,       14, 0x09c9016b
+0,   17760000,   17760000,   219000,     5078, 0x722c9862
+0,   17979000,   17979000,   219000,       14, 0x0aa9018b
+0,   17980000,   17980000,   959000,     5808, 0x38dd7ae9
+0,   18939000,   18939000,   959000,       14, 0x0b8901ab
+0,   18940000,   18940000,   219000,     6015, 0xd4d2c40c
+0,   19159000,   19159000,   219000,       14, 0x0c6901cb
+0,   19160000,   19160000,   259000,     6519, 0x08af4c86
+0,   19419000,   19419000,   259000,       14, 0x064900eb
+0,   19420000,   19420000,    99000,     7061, 0xecf10368
+0,   19519000,   19519000,    99000,       14, 0x0729010b
+0,   19520000,   19520000,   219000,     4773, 0xbee42fd4
+0,   19739000,   19739000,   219000,       14, 0x0809012b
+0,   19740000,   19740000,   219000,     5546, 0xdb822be1
+0,   19959000,   19959000,   219000,       14, 0x08e9014b
+0,   19960000,   19960000,   239000,     5754, 0xfdcc7285
+0,   20199000,   20199000,   239000,       14, 0x09c9016b
+0,   20200000,   20200000,   139000,     6099, 0xa409dd67
+0,   20339000,   20339000,   139000,       14, 0x0aa9018b
+0,   20340000,   20340000,   799000,     6839, 0xc5eecacc
+0,   21139000,   21139000,   799000,       14, 0x0b8901ab
+0,   21140000,   21140000,   239000,     4744, 0x4e451fa1
+0,   21379000,   21379000,   239000,       14, 0x0c6901cb
+0,   21380000,   21380000,   339000,     5824, 0x5299778b
+0,   21719000,   21719000,   339000,       14, 0x064900eb
+0,   21720000,   21720000,  1439000,     6212, 0x6d15f72f
+0,   23159000,   23159000,  1439000,       14, 0x0729010b
+0,   23160000,   23160000,  1319000,     7082, 0xe5c91052
+0,   24479000,   24479000,  1319000,       14, 0x0809012b
+0,   24480000,   24480000,   219000,     5345, 0x2e5eca40
+0,   24699000,   24699000,   219000,       14, 0x08e9014b
+0,   24700000,   24700000,   219000,     5765, 0x118060f2
+0,   24919000,   24919000,   219000,       14, 0x09c9016b
+0,   24920000,   24920000,   599000,     6557, 0x89275c90
+0,   25519000,   25519000,   599000,       14, 0x0aa9018b
+0,   25520000,   25520000,   219000,     7091, 0x996904b9
+0,   25739000,   25739000,   219000,       14, 0x0b8901ab
+0,   25740000,   25740000,   239000,     7305, 0xc23e44ae
+0,   25979000,   25979000,   239000,       14, 0x0c6901cb
+0,   25980000,   25980000,   359000,     7590, 0xc5a3a3e1
+0,   26339000,   26339000,   359000,       14, 0x064900eb
+0,   26340000,   26340000,   219000,     4629, 0x7ad6fe08
+0,   26559000,   26559000,   219000,       14, 0x0729010b
+0,   26560000,   26560000,   719000,     4785, 0xcd3f3f20
+0,   27279000,   27279000,   719000,       14, 0x0809012b
+0,   27280000,   27280000,   459000,     6061, 0x8b04d1a5
+0,   27739000,   27739000,   459000,       14, 0x08e9014b
+0,   27740000,   27740000,   239000,     6301, 0xe7de19b1
+0,   27979000,   27979000,   239000,       14, 0x09c9016b
+0,   27980000,   27980000,    99000,     6736, 0x38b3a094
+0,   28079000,   28079000,    99000,       14, 0x0aa9018b
+0,   28080000,   28080000,   219000,     7214, 0x0b783b73
+0,   28299000,   28299000,   219000,       14, 0x0b8901ab
+0,   28300000,   28300000,   239000,     7366, 0x98bf842d
+0,   28539000,   28539000,   239000,       14, 0x0c6901cb
+0,   28540000,   28540000,   599000,     4564, 0x3d9600ab
+0,   29139000,   29139000,   599000,       14, 0x064900eb
+0,   29140000,   29140000,   219000,     4637, 0x01f02617
+0,   29359000,   29359000,   219000,       14, 0x0729010b
+0,   29360000,   29360000,  1679000,     5358, 0x5b0f0ba8
+0,   31039000,   31039000,  1679000,       14, 0x0809012b
+0,   31040000,   31040000,   359000,       14, 0x0879013b
+0,   31399000,   31399000,   359000,       14, 0x08e9014b
+0,   31400000,   31400000,   479000,       14, 0x0959015b
+0,   31879000,   31879000,   479000,       14, 0x09c9016b
diff --git a/tests/ref/fate/sub-scc b/tests/ref/fate/sub-scc
index 62cbf6fa4a..b02e6e95b5 100644
--- a/tests/ref/fate/sub-scc
+++ b/tests/ref/fate/sub-scc
@@ -11,7 +11,6 @@ Style: Default,Monospace,16,&Hffffff,&Hffffff,&H0,&H0,0,0,0,0,100,100,0,0,3,1,0,
 
 [Events]
 Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
-Dialogue: 0,0:00:-2.-47,0:00:00.70,Default,,0,0,0,,{\an7}{\pos(76,228)}WE HAVE FOUND A WITCH !\N{\an7}{\pos(76,243)}MAY WE BURN HER ?
 Dialogue: 0,0:00:00.69,0:00:03.29,Default,,0,0,0,,{\an7}{\pos(115,228)}[ Crowd ]\N{\an7}{\pos(115,243)}BURN HER !  BURN HER !
 Dialogue: 0,0:00:03.30,0:00:07.07,Default,,0,0,0,,{\an7}{\pos(38,197)}HOW DO YOU KNOW\N{\an7}{\pos(38,213)}SHE IS A WITCH ?\N{\an7}{\pos(153,243)}SHE LOOKS LIKE ONE !
 Dialogue: 0,0:00:07.07,0:00:09.27,Default,,0,0,0,,{\an7}{\pos(192,228)}[ Shouting\N{\an7}{\pos(192,243)}\h\hAffirmations ]
diff --git a/tests/ref/fate/sub2video b/tests/ref/fate/sub2video
index 80abe9c905..7cc6549966 100644
--- a/tests/ref/fate/sub2video
+++ b/tests/ref/fate/sub2video
@@ -58,129 +58,1136 @@
 0,         47,         47,        1,   518400, 0xde69683f
 0,         48,         48,        1,   518400, 0x7df08fba
 0,         49,         49,        1,   518400, 0xbab197ea
+0,         50,         50,        1,   518400, 0xbab197ea
+0,         51,         51,        1,   518400, 0xbab197ea
+0,         52,         52,        1,   518400, 0xbab197ea
+0,         53,         53,        1,   518400, 0xbab197ea
+0,         54,         54,        1,   518400, 0xbab197ea
+0,         55,         55,        1,   518400, 0xbab197ea
+0,         56,         56,        1,   518400, 0xbab197ea
+0,         57,         57,        1,   518400, 0xbab197ea
+0,         58,         58,        1,   518400, 0xbab197ea
+0,         59,         59,        1,   518400, 0xbab197ea
+0,         60,         60,        1,   518400, 0xbab197ea
+0,         61,         61,        1,   518400, 0xbab197ea
+0,         62,         62,        1,   518400, 0xbab197ea
+0,         63,         63,        1,   518400, 0xbab197ea
+0,         64,         64,        1,   518400, 0xbab197ea
+0,         65,         65,        1,   518400, 0xbab197ea
+0,         66,         66,        1,   518400, 0xbab197ea
+0,         67,         67,        1,   518400, 0xbab197ea
+0,         68,         68,        1,   518400, 0xbab197ea
+0,         69,         69,        1,   518400, 0xbab197ea
+0,         70,         70,        1,   518400, 0xbab197ea
+0,         71,         71,        1,   518400, 0xbab197ea
+0,         72,         72,        1,   518400, 0xbab197ea
+0,         73,         73,        1,   518400, 0xbab197ea
+0,         74,         74,        1,   518400, 0xbab197ea
+0,         75,         75,        1,   518400, 0xbab197ea
+0,         76,         76,        1,   518400, 0xbab197ea
 1,   15355000,   15355000,  4733000,     2094, 0x3c171425
 0,         77,         77,        1,   518400, 0x902285d9
-0,        100,        100,        1,   518400, 0xbab197ea
+0,         78,         78,        1,   518400, 0x902285d9
+0,         79,         79,        1,   518400, 0x902285d9
+0,         80,         80,        1,   518400, 0x902285d9
+0,         81,         81,        1,   518400, 0x902285d9
+0,         82,         82,        1,   518400, 0x902285d9
+0,         83,         83,        1,   518400, 0x902285d9
+0,         84,         84,        1,   518400, 0x902285d9
+0,         85,         85,        1,   518400, 0x902285d9
+0,         86,         86,        1,   518400, 0x902285d9
+0,         87,         87,        1,   518400, 0x902285d9
+0,         88,         88,        1,   518400, 0x902285d9
+0,         89,         89,        1,   518400, 0x902285d9
+0,         90,         90,        1,   518400, 0x902285d9
+0,         91,         91,        1,   518400, 0x902285d9
+0,         92,         92,        1,   518400, 0x902285d9
+0,         93,         93,        1,   518400, 0x902285d9
+0,         94,         94,        1,   518400, 0x902285d9
+0,         95,         95,        1,   518400, 0x902285d9
+0,         96,         96,        1,   518400, 0x902285d9
+0,         97,         97,        1,   518400, 0x902285d9
+0,         98,         98,        1,   518400, 0x902285d9
+0,         99,         99,        1,   518400, 0x902285d9
+0,        100,        100,        1,   518400, 0x902285d9
+0,        101,        101,        1,   518400, 0xbab197ea
+0,        102,        102,        1,   518400, 0xbab197ea
+0,        103,        103,        1,   518400, 0xbab197ea
+0,        104,        104,        1,   518400, 0xbab197ea
+0,        105,        105,        1,   518400, 0xbab197ea
+0,        106,        106,        1,   518400, 0xbab197ea
+0,        107,        107,        1,   518400, 0xbab197ea
+0,        108,        108,        1,   518400, 0xbab197ea
+0,        109,        109,        1,   518400, 0xbab197ea
+0,        110,        110,        1,   518400, 0xbab197ea
+0,        111,        111,        1,   518400, 0xbab197ea
+0,        112,        112,        1,   518400, 0xbab197ea
+0,        113,        113,        1,   518400, 0xbab197ea
+0,        114,        114,        1,   518400, 0xbab197ea
+0,        115,        115,        1,   518400, 0xbab197ea
+0,        116,        116,        1,   518400, 0xbab197ea
+0,        117,        117,        1,   518400, 0xbab197ea
+0,        118,        118,        1,   518400, 0xbab197ea
+0,        119,        119,        1,   518400, 0xbab197ea
+0,        120,        120,        1,   518400, 0xbab197ea
+0,        121,        121,        1,   518400, 0xbab197ea
+0,        122,        122,        1,   518400, 0xbab197ea
+0,        123,        123,        1,   518400, 0xbab197ea
+0,        124,        124,        1,   518400, 0xbab197ea
+0,        125,        125,        1,   518400, 0xbab197ea
+0,        126,        126,        1,   518400, 0xbab197ea
+0,        127,        127,        1,   518400, 0xbab197ea
+0,        128,        128,        1,   518400, 0xbab197ea
+0,        129,        129,        1,   518400, 0xbab197ea
+0,        130,        130,        1,   518400, 0xbab197ea
+0,        131,        131,        1,   518400, 0xbab197ea
+0,        132,        132,        1,   518400, 0xbab197ea
+0,        133,        133,        1,   518400, 0xbab197ea
+0,        134,        134,        1,   518400, 0xbab197ea
+0,        135,        135,        1,   518400, 0xbab197ea
+0,        136,        136,        1,   518400, 0xbab197ea
+0,        137,        137,        1,   518400, 0xbab197ea
+0,        138,        138,        1,   518400, 0xbab197ea
+0,        139,        139,        1,   518400, 0xbab197ea
+0,        140,        140,        1,   518400, 0xbab197ea
+0,        141,        141,        1,   518400, 0xbab197ea
+0,        142,        142,        1,   518400, 0xbab197ea
+0,        143,        143,        1,   518400, 0xbab197ea
+0,        144,        144,        1,   518400, 0xbab197ea
+0,        145,        145,        1,   518400, 0xbab197ea
+0,        146,        146,        1,   518400, 0xbab197ea
+0,        147,        147,        1,   518400, 0xbab197ea
+0,        148,        148,        1,   518400, 0xbab197ea
+0,        149,        149,        1,   518400, 0xbab197ea
+0,        150,        150,        1,   518400, 0xbab197ea
+0,        151,        151,        1,   518400, 0xbab197ea
+0,        152,        152,        1,   518400, 0xbab197ea
+0,        153,        153,        1,   518400, 0xbab197ea
+0,        154,        154,        1,   518400, 0xbab197ea
+0,        155,        155,        1,   518400, 0xbab197ea
+0,        156,        156,        1,   518400, 0xbab197ea
+0,        157,        157,        1,   518400, 0xbab197ea
+0,        158,        158,        1,   518400, 0xbab197ea
+0,        159,        159,        1,   518400, 0xbab197ea
+0,        160,        160,        1,   518400, 0xbab197ea
+0,        161,        161,        1,   518400, 0xbab197ea
+0,        162,        162,        1,   518400, 0xbab197ea
+0,        163,        163,        1,   518400, 0xbab197ea
+0,        164,        164,        1,   518400, 0xbab197ea
+0,        165,        165,        1,   518400, 0xbab197ea
+0,        166,        166,        1,   518400, 0xbab197ea
+0,        167,        167,        1,   518400, 0xbab197ea
+0,        168,        168,        1,   518400, 0xbab197ea
+0,        169,        169,        1,   518400, 0xbab197ea
+0,        170,        170,        1,   518400, 0xbab197ea
+0,        171,        171,        1,   518400, 0xbab197ea
+0,        172,        172,        1,   518400, 0xbab197ea
+0,        173,        173,        1,   518400, 0xbab197ea
+0,        174,        174,        1,   518400, 0xbab197ea
+0,        175,        175,        1,   518400, 0xbab197ea
+0,        176,        176,        1,   518400, 0xbab197ea
+0,        177,        177,        1,   518400, 0xbab197ea
+0,        178,        178,        1,   518400, 0xbab197ea
+0,        179,        179,        1,   518400, 0xbab197ea
+0,        180,        180,        1,   518400, 0xbab197ea
+0,        181,        181,        1,   518400, 0xbab197ea
+0,        182,        182,        1,   518400, 0xbab197ea
+0,        183,        183,        1,   518400, 0xbab197ea
+0,        184,        184,        1,   518400, 0xbab197ea
+0,        185,        185,        1,   518400, 0xbab197ea
+0,        186,        186,        1,   518400, 0xbab197ea
+0,        187,        187,        1,   518400, 0xbab197ea
+0,        188,        188,        1,   518400, 0xbab197ea
+0,        189,        189,        1,   518400, 0xbab197ea
+0,        190,        190,        1,   518400, 0xbab197ea
+0,        191,        191,        1,   518400, 0xbab197ea
+0,        192,        192,        1,   518400, 0xbab197ea
+0,        193,        193,        1,   518400, 0xbab197ea
+0,        194,        194,        1,   518400, 0xbab197ea
+0,        195,        195,        1,   518400, 0xbab197ea
+0,        196,        196,        1,   518400, 0xbab197ea
+0,        197,        197,        1,   518400, 0xbab197ea
+0,        198,        198,        1,   518400, 0xbab197ea
+0,        199,        199,        1,   518400, 0xbab197ea
+0,        200,        200,        1,   518400, 0xbab197ea
+0,        201,        201,        1,   518400, 0xbab197ea
+0,        202,        202,        1,   518400, 0xbab197ea
+0,        203,        203,        1,   518400, 0xbab197ea
+0,        204,        204,        1,   518400, 0xbab197ea
+0,        205,        205,        1,   518400, 0xbab197ea
+0,        206,        206,        1,   518400, 0xbab197ea
+0,        207,        207,        1,   518400, 0xbab197ea
+0,        208,        208,        1,   518400, 0xbab197ea
+0,        209,        209,        1,   518400, 0xbab197ea
+0,        210,        210,        1,   518400, 0xbab197ea
+0,        211,        211,        1,   518400, 0xbab197ea
+0,        212,        212,        1,   518400, 0xbab197ea
+0,        213,        213,        1,   518400, 0xbab197ea
+0,        214,        214,        1,   518400, 0xbab197ea
+0,        215,        215,        1,   518400, 0xbab197ea
+0,        216,        216,        1,   518400, 0xbab197ea
+0,        217,        217,        1,   518400, 0xbab197ea
+0,        218,        218,        1,   518400, 0xbab197ea
+0,        219,        219,        1,   518400, 0xbab197ea
+0,        220,        220,        1,   518400, 0xbab197ea
+0,        221,        221,        1,   518400, 0xbab197ea
+0,        222,        222,        1,   518400, 0xbab197ea
+0,        223,        223,        1,   518400, 0xbab197ea
+0,        224,        224,        1,   518400, 0xbab197ea
+0,        225,        225,        1,   518400, 0xbab197ea
+0,        226,        226,        1,   518400, 0xbab197ea
+0,        227,        227,        1,   518400, 0xbab197ea
+0,        228,        228,        1,   518400, 0xbab197ea
+0,        229,        229,        1,   518400, 0xbab197ea
+0,        230,        230,        1,   518400, 0xbab197ea
+0,        231,        231,        1,   518400, 0xbab197ea
+0,        232,        232,        1,   518400, 0xbab197ea
+0,        233,        233,        1,   518400, 0xbab197ea
+0,        234,        234,        1,   518400, 0xbab197ea
+0,        235,        235,        1,   518400, 0xbab197ea
+0,        236,        236,        1,   518400, 0xbab197ea
+0,        237,        237,        1,   518400, 0xbab197ea
+0,        238,        238,        1,   518400, 0xbab197ea
+0,        239,        239,        1,   518400, 0xbab197ea
+0,        240,        240,        1,   518400, 0xbab197ea
+0,        241,        241,        1,   518400, 0xbab197ea
+0,        242,        242,        1,   518400, 0xbab197ea
+0,        243,        243,        1,   518400, 0xbab197ea
 1,   48797000,   48797000,  2560000,     2480, 0x7c0edf21
 0,        244,        244,        1,   518400, 0x7a11c812
-0,        257,        257,        1,   518400, 0xbab197ea
+0,        245,        245,        1,   518400, 0x7a11c812
+0,        246,        246,        1,   518400, 0x7a11c812
+0,        247,        247,        1,   518400, 0x7a11c812
+0,        248,        248,        1,   518400, 0x7a11c812
+0,        249,        249,        1,   518400, 0x7a11c812
+0,        250,        250,        1,   518400, 0x7a11c812
+0,        251,        251,        1,   518400, 0x7a11c812
+0,        252,        252,        1,   518400, 0x7a11c812
+0,        253,        253,        1,   518400, 0x7a11c812
+0,        254,        254,        1,   518400, 0x7a11c812
+0,        255,        255,        1,   518400, 0x7a11c812
+0,        256,        256,        1,   518400, 0x7a11c812
+0,        257,        257,        1,   518400, 0x34cdddee
 1,   51433000,   51433000,  2366000,     3059, 0xc95b8a05
 0,        258,        258,        1,   518400, 0x34cdddee
-0,        269,        269,        1,   518400, 0xbab197ea
+0,        259,        259,        1,   518400, 0x34cdddee
+0,        260,        260,        1,   518400, 0x34cdddee
+0,        261,        261,        1,   518400, 0x34cdddee
+0,        262,        262,        1,   518400, 0x34cdddee
+0,        263,        263,        1,   518400, 0x34cdddee
+0,        264,        264,        1,   518400, 0x34cdddee
+0,        265,        265,        1,   518400, 0x34cdddee
+0,        266,        266,        1,   518400, 0x34cdddee
+0,        267,        267,        1,   518400, 0x34cdddee
+0,        268,        268,        1,   518400, 0x34cdddee
+0,        269,        269,        1,   518400, 0x34cdddee
 1,   53910000,   53910000,  2696000,     2095, 0x61bb15ed
 0,        270,        270,        1,   518400, 0x4db4ce51
-0,        283,        283,        1,   518400, 0xbab197ea
+0,        271,        271,        1,   518400, 0x4db4ce51
+0,        272,        272,        1,   518400, 0x4db4ce51
+0,        273,        273,        1,   518400, 0x4db4ce51
+0,        274,        274,        1,   518400, 0x4db4ce51
+0,        275,        275,        1,   518400, 0x4db4ce51
+0,        276,        276,        1,   518400, 0x4db4ce51
+0,        277,        277,        1,   518400, 0x4db4ce51
+0,        278,        278,        1,   518400, 0x4db4ce51
+0,        279,        279,        1,   518400, 0x4db4ce51
+0,        280,        280,        1,   518400, 0x4db4ce51
+0,        281,        281,        1,   518400, 0x4db4ce51
+0,        282,        282,        1,   518400, 0x4db4ce51
+0,        283,        283,        1,   518400, 0xe6bc0ea9
 1,   56663000,   56663000,  1262000,     1013, 0xc9ae89b7
 0,        284,        284,        1,   518400, 0xe6bc0ea9
-0,        290,        290,        1,   518400, 0xbab197ea
+0,        285,        285,        1,   518400, 0xe6bc0ea9
+0,        286,        286,        1,   518400, 0xe6bc0ea9
+0,        287,        287,        1,   518400, 0xe6bc0ea9
+0,        288,        288,        1,   518400, 0xe6bc0ea9
+0,        289,        289,        1,   518400, 0xe6bc0ea9
+0,        290,        290,        1,   518400, 0xa8643af7
 1,   58014000,   58014000,  1661000,      969, 0xe01878f0
 0,        291,        291,        1,   518400, 0xa8643af7
-0,        298,        298,        1,   518400, 0xbab197ea
+0,        292,        292,        1,   518400, 0xa8643af7
+0,        293,        293,        1,   518400, 0xa8643af7
+0,        294,        294,        1,   518400, 0xa8643af7
+0,        295,        295,        1,   518400, 0xa8643af7
+0,        296,        296,        1,   518400, 0xa8643af7
+0,        297,        297,        1,   518400, 0xa8643af7
+0,        298,        298,        1,   518400, 0xa8643af7
+0,        299,        299,        1,   518400, 0xbab197ea
+0,        300,        300,        1,   518400, 0xbab197ea
+0,        301,        301,        1,   518400, 0xbab197ea
+0,        302,        302,        1,   518400, 0xbab197ea
+0,        303,        303,        1,   518400, 0xbab197ea
+0,        304,        304,        1,   518400, 0xbab197ea
+0,        305,        305,        1,   518400, 0xbab197ea
+0,        306,        306,        1,   518400, 0xbab197ea
+0,        307,        307,        1,   518400, 0xbab197ea
+0,        308,        308,        1,   518400, 0xbab197ea
+0,        309,        309,        1,   518400, 0xbab197ea
+0,        310,        310,        1,   518400, 0xbab197ea
+0,        311,        311,        1,   518400, 0xbab197ea
+0,        312,        312,        1,   518400, 0xbab197ea
+0,        313,        313,        1,   518400, 0xbab197ea
+0,        314,        314,        1,   518400, 0xbab197ea
+0,        315,        315,        1,   518400, 0xbab197ea
+0,        316,        316,        1,   518400, 0xbab197ea
+0,        317,        317,        1,   518400, 0xbab197ea
+0,        318,        318,        1,   518400, 0xbab197ea
+0,        319,        319,        1,   518400, 0xbab197ea
+0,        320,        320,        1,   518400, 0xbab197ea
+0,        321,        321,        1,   518400, 0xbab197ea
+0,        322,        322,        1,   518400, 0xbab197ea
+0,        323,        323,        1,   518400, 0xbab197ea
+0,        324,        324,        1,   518400, 0xbab197ea
+0,        325,        325,        1,   518400, 0xbab197ea
+0,        326,        326,        1,   518400, 0xbab197ea
+0,        327,        327,        1,   518400, 0xbab197ea
+0,        328,        328,        1,   518400, 0xbab197ea
+0,        329,        329,        1,   518400, 0xbab197ea
+0,        330,        330,        1,   518400, 0xbab197ea
+0,        331,        331,        1,   518400, 0xbab197ea
+0,        332,        332,        1,   518400, 0xbab197ea
+0,        333,        333,        1,   518400, 0xbab197ea
+0,        334,        334,        1,   518400, 0xbab197ea
+0,        335,        335,        1,   518400, 0xbab197ea
+0,        336,        336,        1,   518400, 0xbab197ea
+0,        337,        337,        1,   518400, 0xbab197ea
+0,        338,        338,        1,   518400, 0xbab197ea
 1,   67724000,   67724000,  1365000,      844, 0xe7db4fc1
 0,        339,        339,        1,   518400, 0xb1885c67
-0,        345,        345,        1,   518400, 0xbab197ea
+0,        340,        340,        1,   518400, 0xb1885c67
+0,        341,        341,        1,   518400, 0xb1885c67
+0,        342,        342,        1,   518400, 0xb1885c67
+0,        343,        343,        1,   518400, 0xb1885c67
+0,        344,        344,        1,   518400, 0xb1885c67
+0,        345,        345,        1,   518400, 0xb1885c67
 1,   69175000,   69175000,  1558000,      802, 0xf48531ba
 0,        346,        346,        1,   518400, 0x378e3fd0
-0,        354,        354,        1,   518400, 0xbab197ea
+0,        347,        347,        1,   518400, 0x378e3fd0
+0,        348,        348,        1,   518400, 0x378e3fd0
+0,        349,        349,        1,   518400, 0x378e3fd0
+0,        350,        350,        1,   518400, 0x378e3fd0
+0,        351,        351,        1,   518400, 0x378e3fd0
+0,        352,        352,        1,   518400, 0x378e3fd0
+0,        353,        353,        1,   518400, 0x378e3fd0
+0,        354,        354,        1,   518400, 0xa3782469
 1,   70819000,   70819000,  1865000,     1709, 0xb4d5a1bd
 0,        355,        355,        1,   518400, 0xa3782469
-0,        363,        363,        1,   518400, 0xbab197ea
+0,        356,        356,        1,   518400, 0xa3782469
+0,        357,        357,        1,   518400, 0xa3782469
+0,        358,        358,        1,   518400, 0xa3782469
+0,        359,        359,        1,   518400, 0xa3782469
+0,        360,        360,        1,   518400, 0xa3782469
+0,        361,        361,        1,   518400, 0xa3782469
+0,        362,        362,        1,   518400, 0xa3782469
+0,        363,        363,        1,   518400, 0xa3782469
 1,   72762000,   72762000,  1968000,     2438, 0x99d7bc82
 0,        364,        364,        1,   518400, 0xba23a0d5
-0,        374,        374,        1,   518400, 0xbab197ea
+0,        365,        365,        1,   518400, 0xba23a0d5
+0,        366,        366,        1,   518400, 0xba23a0d5
+0,        367,        367,        1,   518400, 0xba23a0d5
+0,        368,        368,        1,   518400, 0xba23a0d5
+0,        369,        369,        1,   518400, 0xba23a0d5
+0,        370,        370,        1,   518400, 0xba23a0d5
+0,        371,        371,        1,   518400, 0xba23a0d5
+0,        372,        372,        1,   518400, 0xba23a0d5
+0,        373,        373,        1,   518400, 0xba23a0d5
+0,        374,        374,        1,   518400, 0x129de2f8
 1,   74806000,   74806000,  1831000,     2116, 0x96514097
 0,        375,        375,        1,   518400, 0x129de2f8
-0,        383,        383,        1,   518400, 0xbab197ea
+0,        376,        376,        1,   518400, 0x129de2f8
+0,        377,        377,        1,   518400, 0x129de2f8
+0,        378,        378,        1,   518400, 0x129de2f8
+0,        379,        379,        1,   518400, 0x129de2f8
+0,        380,        380,        1,   518400, 0x129de2f8
+0,        381,        381,        1,   518400, 0x129de2f8
+0,        382,        382,        1,   518400, 0x129de2f8
+0,        383,        383,        1,   518400, 0x129de2f8
 1,   76716000,   76716000,  1262000,     1822, 0xefccc72e
 0,        384,        384,        1,   518400, 0x19772f0f
-0,        390,        390,        1,   518400, 0xbab197ea
+0,        385,        385,        1,   518400, 0x19772f0f
+0,        386,        386,        1,   518400, 0x19772f0f
+0,        387,        387,        1,   518400, 0x19772f0f
+0,        388,        388,        1,   518400, 0x19772f0f
+0,        389,        389,        1,   518400, 0x19772f0f
+0,        390,        390,        1,   518400, 0x56f54e73
 1,   78051000,   78051000,  1524000,      987, 0x7b927a27
 0,        391,        391,        1,   518400, 0x56f54e73
-0,        398,        398,        1,   518400, 0xbab197ea
+0,        392,        392,        1,   518400, 0x56f54e73
+0,        393,        393,        1,   518400, 0x56f54e73
+0,        394,        394,        1,   518400, 0x56f54e73
+0,        395,        395,        1,   518400, 0x56f54e73
+0,        396,        396,        1,   518400, 0x56f54e73
+0,        397,        397,        1,   518400, 0x56f54e73
+0,        398,        398,        1,   518400, 0x300b5247
 1,   79644000,   79644000,  2662000,     2956, 0x190778f7
 0,        399,        399,        1,   518400, 0x300b5247
+0,        400,        400,        1,   518400, 0x300b5247
+0,        401,        401,        1,   518400, 0x300b5247
+0,        402,        402,        1,   518400, 0x300b5247
+0,        403,        403,        1,   518400, 0x300b5247
+0,        404,        404,        1,   518400, 0x300b5247
+0,        405,        405,        1,   518400, 0x300b5247
+0,        406,        406,        1,   518400, 0x300b5247
+0,        407,        407,        1,   518400, 0x300b5247
+0,        408,        408,        1,   518400, 0x300b5247
+0,        409,        409,        1,   518400, 0x300b5247
+0,        410,        410,        1,   518400, 0x300b5247
+0,        411,        411,        1,   518400, 0x300b5247
 1,   82380000,   82380000,  2764000,     3094, 0xc021b7d3
-0,        412,        412,        1,   518400, 0xbab197ea
+0,        412,        412,        1,   518400, 0x6fd028fa
 0,        413,        413,        1,   518400, 0x6fd028fa
-0,        426,        426,        1,   518400, 0xbab197ea
+0,        414,        414,        1,   518400, 0x6fd028fa
+0,        415,        415,        1,   518400, 0x6fd028fa
+0,        416,        416,        1,   518400, 0x6fd028fa
+0,        417,        417,        1,   518400, 0x6fd028fa
+0,        418,        418,        1,   518400, 0x6fd028fa
+0,        419,        419,        1,   518400, 0x6fd028fa
+0,        420,        420,        1,   518400, 0x6fd028fa
+0,        421,        421,        1,   518400, 0x6fd028fa
+0,        422,        422,        1,   518400, 0x6fd028fa
+0,        423,        423,        1,   518400, 0x6fd028fa
+0,        424,        424,        1,   518400, 0x6fd028fa
+0,        425,        425,        1,   518400, 0x6fd028fa
+0,        426,        426,        1,   518400, 0x01f80e9d
 1,   85225000,   85225000,  2366000,     2585, 0x74d0048f
 0,        427,        427,        1,   518400, 0x01f80e9d
-0,        438,        438,        1,   518400, 0xbab197ea
+0,        428,        428,        1,   518400, 0x01f80e9d
+0,        429,        429,        1,   518400, 0x01f80e9d
+0,        430,        430,        1,   518400, 0x01f80e9d
+0,        431,        431,        1,   518400, 0x01f80e9d
+0,        432,        432,        1,   518400, 0x01f80e9d
+0,        433,        433,        1,   518400, 0x01f80e9d
+0,        434,        434,        1,   518400, 0x01f80e9d
+0,        435,        435,        1,   518400, 0x01f80e9d
+0,        436,        436,        1,   518400, 0x01f80e9d
+0,        437,        437,        1,   518400, 0x01f80e9d
+0,        438,        438,        1,   518400, 0xb48d90c0
 1,   87652000,   87652000,  1831000,      634, 0x8832fda1
 0,        439,        439,        1,   518400, 0xb48d90c0
-0,        447,        447,        1,   518400, 0xbab197ea
+0,        440,        440,        1,   518400, 0xb48d90c0
+0,        441,        441,        1,   518400, 0xb48d90c0
+0,        442,        442,        1,   518400, 0xb48d90c0
+0,        443,        443,        1,   518400, 0xb48d90c0
+0,        444,        444,        1,   518400, 0xb48d90c0
+0,        445,        445,        1,   518400, 0xb48d90c0
+0,        446,        446,        1,   518400, 0xb48d90c0
+0,        447,        447,        1,   518400, 0xb48d90c0
+0,        448,        448,        1,   518400, 0xbab197ea
+0,        449,        449,        1,   518400, 0xbab197ea
+0,        450,        450,        1,   518400, 0xbab197ea
+0,        451,        451,        1,   518400, 0xbab197ea
+0,        452,        452,        1,   518400, 0xbab197ea
+0,        453,        453,        1,   518400, 0xbab197ea
+0,        454,        454,        1,   518400, 0xbab197ea
+0,        455,        455,        1,   518400, 0xbab197ea
+0,        456,        456,        1,   518400, 0xbab197ea
+0,        457,        457,        1,   518400, 0xbab197ea
 1,   91531000,   91531000,  2332000,     2080, 0x97a1146f
 0,        458,        458,        1,   518400, 0xcb5a0173
-0,        469,        469,        1,   518400, 0xbab197ea
+0,        459,        459,        1,   518400, 0xcb5a0173
+0,        460,        460,        1,   518400, 0xcb5a0173
+0,        461,        461,        1,   518400, 0xcb5a0173
+0,        462,        462,        1,   518400, 0xcb5a0173
+0,        463,        463,        1,   518400, 0xcb5a0173
+0,        464,        464,        1,   518400, 0xcb5a0173
+0,        465,        465,        1,   518400, 0xcb5a0173
+0,        466,        466,        1,   518400, 0xcb5a0173
+0,        467,        467,        1,   518400, 0xcb5a0173
+0,        468,        468,        1,   518400, 0xcb5a0173
+0,        469,        469,        1,   518400, 0xcb5a0173
+0,        470,        470,        1,   518400, 0xbab197ea
+0,        471,        471,        1,   518400, 0xbab197ea
+0,        472,        472,        1,   518400, 0xbab197ea
+0,        473,        473,        1,   518400, 0xbab197ea
+0,        474,        474,        1,   518400, 0xbab197ea
+0,        475,        475,        1,   518400, 0xbab197ea
+0,        476,        476,        1,   518400, 0xbab197ea
+0,        477,        477,        1,   518400, 0xbab197ea
 1,   95510000,   95510000,  3299000,     2964, 0x8b8f6684
 0,        478,        478,        1,   518400, 0xb8a323e4
-0,        494,        494,        1,   518400, 0xbab197ea
+0,        479,        479,        1,   518400, 0xb8a323e4
+0,        480,        480,        1,   518400, 0xb8a323e4
+0,        481,        481,        1,   518400, 0xb8a323e4
+0,        482,        482,        1,   518400, 0xb8a323e4
+0,        483,        483,        1,   518400, 0xb8a323e4
+0,        484,        484,        1,   518400, 0xb8a323e4
+0,        485,        485,        1,   518400, 0xb8a323e4
+0,        486,        486,        1,   518400, 0xb8a323e4
+0,        487,        487,        1,   518400, 0xb8a323e4
+0,        488,        488,        1,   518400, 0xb8a323e4
+0,        489,        489,        1,   518400, 0xb8a323e4
+0,        490,        490,        1,   518400, 0xb8a323e4
+0,        491,        491,        1,   518400, 0xb8a323e4
+0,        492,        492,        1,   518400, 0xb8a323e4
+0,        493,        493,        1,   518400, 0xb8a323e4
+0,        494,        494,        1,   518400, 0xc43518ba
 1,   98872000,   98872000,  2161000,     1875, 0x9002ef71
 0,        495,        495,        1,   518400, 0xc43518ba
-0,        505,        505,        1,   518400, 0xbab197ea
+0,        496,        496,        1,   518400, 0xc43518ba
+0,        497,        497,        1,   518400, 0xc43518ba
+0,        498,        498,        1,   518400, 0xc43518ba
+0,        499,        499,        1,   518400, 0xc43518ba
+0,        500,        500,        1,   518400, 0xc43518ba
+0,        501,        501,        1,   518400, 0xc43518ba
+0,        502,        502,        1,   518400, 0xc43518ba
+0,        503,        503,        1,   518400, 0xc43518ba
+0,        504,        504,        1,   518400, 0xc43518ba
+0,        505,        505,        1,   518400, 0xc43518ba
 1,  101124000,  101124000,  4096000,     3872, 0x20c6ed9c
 0,        506,        506,        1,   518400, 0x04e38692
-0,        526,        526,        1,   518400, 0xbab197ea
+0,        507,        507,        1,   518400, 0x04e38692
+0,        508,        508,        1,   518400, 0x04e38692
+0,        509,        509,        1,   518400, 0x04e38692
+0,        510,        510,        1,   518400, 0x04e38692
+0,        511,        511,        1,   518400, 0x04e38692
+0,        512,        512,        1,   518400, 0x04e38692
+0,        513,        513,        1,   518400, 0x04e38692
+0,        514,        514,        1,   518400, 0x04e38692
+0,        515,        515,        1,   518400, 0x04e38692
+0,        516,        516,        1,   518400, 0x04e38692
+0,        517,        517,        1,   518400, 0x04e38692
+0,        518,        518,        1,   518400, 0x04e38692
+0,        519,        519,        1,   518400, 0x04e38692
+0,        520,        520,        1,   518400, 0x04e38692
+0,        521,        521,        1,   518400, 0x04e38692
+0,        522,        522,        1,   518400, 0x04e38692
+0,        523,        523,        1,   518400, 0x04e38692
+0,        524,        524,        1,   518400, 0x04e38692
+0,        525,        525,        1,   518400, 0x04e38692
+0,        526,        526,        1,   518400, 0x04e38692
 1,  105303000,  105303000,  2730000,     3094, 0xf203a663
 0,        527,        527,        1,   518400, 0x856b0ee5
-0,        540,        540,        1,   518400, 0xbab197ea
+0,        528,        528,        1,   518400, 0x856b0ee5
+0,        529,        529,        1,   518400, 0x856b0ee5
+0,        530,        530,        1,   518400, 0x856b0ee5
+0,        531,        531,        1,   518400, 0x856b0ee5
+0,        532,        532,        1,   518400, 0x856b0ee5
+0,        533,        533,        1,   518400, 0x856b0ee5
+0,        534,        534,        1,   518400, 0x856b0ee5
+0,        535,        535,        1,   518400, 0x856b0ee5
+0,        536,        536,        1,   518400, 0x856b0ee5
+0,        537,        537,        1,   518400, 0x856b0ee5
+0,        538,        538,        1,   518400, 0x856b0ee5
+0,        539,        539,        1,   518400, 0x856b0ee5
+0,        540,        540,        1,   518400, 0x856b0ee5
 1,  108106000,  108106000,  2059000,     2404, 0x41a7b429
 0,        541,        541,        1,   518400, 0x3e5beee2
-0,        551,        551,        1,   518400, 0xbab197ea
+0,        542,        542,        1,   518400, 0x3e5beee2
+0,        543,        543,        1,   518400, 0x3e5beee2
+0,        544,        544,        1,   518400, 0x3e5beee2
+0,        545,        545,        1,   518400, 0x3e5beee2
+0,        546,        546,        1,   518400, 0x3e5beee2
+0,        547,        547,        1,   518400, 0x3e5beee2
+0,        548,        548,        1,   518400, 0x3e5beee2
+0,        549,        549,        1,   518400, 0x3e5beee2
+0,        550,        550,        1,   518400, 0x3e5beee2
+0,        551,        551,        1,   518400, 0x3e5beee2
+0,        552,        552,        1,   518400, 0xbab197ea
+0,        553,        553,        1,   518400, 0xbab197ea
+0,        554,        554,        1,   518400, 0xbab197ea
+0,        555,        555,        1,   518400, 0xbab197ea
+0,        556,        556,        1,   518400, 0xbab197ea
+0,        557,        557,        1,   518400, 0xbab197ea
+0,        558,        558,        1,   518400, 0xbab197ea
+0,        559,        559,        1,   518400, 0xbab197ea
+0,        560,        560,        1,   518400, 0xbab197ea
+0,        561,        561,        1,   518400, 0xbab197ea
+0,        562,        562,        1,   518400, 0xbab197ea
+0,        563,        563,        1,   518400, 0xbab197ea
+0,        564,        564,        1,   518400, 0xbab197ea
+0,        565,        565,        1,   518400, 0xbab197ea
+0,        566,        566,        1,   518400, 0xbab197ea
+0,        567,        567,        1,   518400, 0xbab197ea
+0,        568,        568,        1,   518400, 0xbab197ea
+0,        569,        569,        1,   518400, 0xbab197ea
+0,        570,        570,        1,   518400, 0xbab197ea
+0,        571,        571,        1,   518400, 0xbab197ea
+0,        572,        572,        1,   518400, 0xbab197ea
+0,        573,        573,        1,   518400, 0xbab197ea
+0,        574,        574,        1,   518400, 0xbab197ea
+0,        575,        575,        1,   518400, 0xbab197ea
+0,        576,        576,        1,   518400, 0xbab197ea
+0,        577,        577,        1,   518400, 0xbab197ea
+0,        578,        578,        1,   518400, 0xbab197ea
+0,        579,        579,        1,   518400, 0xbab197ea
+0,        580,        580,        1,   518400, 0xbab197ea
+0,        581,        581,        1,   518400, 0xbab197ea
+0,        582,        582,        1,   518400, 0xbab197ea
+0,        583,        583,        1,   518400, 0xbab197ea
+0,        584,        584,        1,   518400, 0xbab197ea
+0,        585,        585,        1,   518400, 0xbab197ea
+0,        586,        586,        1,   518400, 0xbab197ea
+0,        587,        587,        1,   518400, 0xbab197ea
+0,        588,        588,        1,   518400, 0xbab197ea
+0,        589,        589,        1,   518400, 0xbab197ea
+0,        590,        590,        1,   518400, 0xbab197ea
+0,        591,        591,        1,   518400, 0xbab197ea
+0,        592,        592,        1,   518400, 0xbab197ea
+0,        593,        593,        1,   518400, 0xbab197ea
+0,        594,        594,        1,   518400, 0xbab197ea
+0,        595,        595,        1,   518400, 0xbab197ea
+0,        596,        596,        1,   518400, 0xbab197ea
+0,        597,        597,        1,   518400, 0xbab197ea
+0,        598,        598,        1,   518400, 0xbab197ea
+0,        599,        599,        1,   518400, 0xbab197ea
+0,        600,        600,        1,   518400, 0xbab197ea
+0,        601,        601,        1,   518400, 0xbab197ea
+0,        602,        602,        1,   518400, 0xbab197ea
+0,        603,        603,        1,   518400, 0xbab197ea
+0,        604,        604,        1,   518400, 0xbab197ea
+0,        605,        605,        1,   518400, 0xbab197ea
+0,        606,        606,        1,   518400, 0xbab197ea
+0,        607,        607,        1,   518400, 0xbab197ea
+0,        608,        608,        1,   518400, 0xbab197ea
+0,        609,        609,        1,   518400, 0xbab197ea
+0,        610,        610,        1,   518400, 0xbab197ea
+0,        611,        611,        1,   518400, 0xbab197ea
+0,        612,        612,        1,   518400, 0xbab197ea
+0,        613,        613,        1,   518400, 0xbab197ea
+0,        614,        614,        1,   518400, 0xbab197ea
+0,        615,        615,        1,   518400, 0xbab197ea
+0,        616,        616,        1,   518400, 0xbab197ea
+0,        617,        617,        1,   518400, 0xbab197ea
+0,        618,        618,        1,   518400, 0xbab197ea
+0,        619,        619,        1,   518400, 0xbab197ea
+0,        620,        620,        1,   518400, 0xbab197ea
+0,        621,        621,        1,   518400, 0xbab197ea
+0,        622,        622,        1,   518400, 0xbab197ea
+0,        623,        623,        1,   518400, 0xbab197ea
+0,        624,        624,        1,   518400, 0xbab197ea
+0,        625,        625,        1,   518400, 0xbab197ea
+0,        626,        626,        1,   518400, 0xbab197ea
+0,        627,        627,        1,   518400, 0xbab197ea
+0,        628,        628,        1,   518400, 0xbab197ea
+0,        629,        629,        1,   518400, 0xbab197ea
+0,        630,        630,        1,   518400, 0xbab197ea
+0,        631,        631,        1,   518400, 0xbab197ea
+0,        632,        632,        1,   518400, 0xbab197ea
+0,        633,        633,        1,   518400, 0xbab197ea
+0,        634,        634,        1,   518400, 0xbab197ea
+0,        635,        635,        1,   518400, 0xbab197ea
+0,        636,        636,        1,   518400, 0xbab197ea
+0,        637,        637,        1,   518400, 0xbab197ea
+0,        638,        638,        1,   518400, 0xbab197ea
+0,        639,        639,        1,   518400, 0xbab197ea
+0,        640,        640,        1,   518400, 0xbab197ea
+0,        641,        641,        1,   518400, 0xbab197ea
+0,        642,        642,        1,   518400, 0xbab197ea
+0,        643,        643,        1,   518400, 0xbab197ea
+0,        644,        644,        1,   518400, 0xbab197ea
+0,        645,        645,        1,   518400, 0xbab197ea
+0,        646,        646,        1,   518400, 0xbab197ea
+0,        647,        647,        1,   518400, 0xbab197ea
+0,        648,        648,        1,   518400, 0xbab197ea
+0,        649,        649,        1,   518400, 0xbab197ea
+0,        650,        650,        1,   518400, 0xbab197ea
+0,        651,        651,        1,   518400, 0xbab197ea
+0,        652,        652,        1,   518400, 0xbab197ea
+0,        653,        653,        1,   518400, 0xbab197ea
+0,        654,        654,        1,   518400, 0xbab197ea
+0,        655,        655,        1,   518400, 0xbab197ea
+0,        656,        656,        1,   518400, 0xbab197ea
+0,        657,        657,        1,   518400, 0xbab197ea
+0,        658,        658,        1,   518400, 0xbab197ea
+0,        659,        659,        1,   518400, 0xbab197ea
+0,        660,        660,        1,   518400, 0xbab197ea
+0,        661,        661,        1,   518400, 0xbab197ea
+0,        662,        662,        1,   518400, 0xbab197ea
+0,        663,        663,        1,   518400, 0xbab197ea
+0,        664,        664,        1,   518400, 0xbab197ea
+0,        665,        665,        1,   518400, 0xbab197ea
+0,        666,        666,        1,   518400, 0xbab197ea
+0,        667,        667,        1,   518400, 0xbab197ea
+0,        668,        668,        1,   518400, 0xbab197ea
+0,        669,        669,        1,   518400, 0xbab197ea
+0,        670,        670,        1,   518400, 0xbab197ea
+0,        671,        671,        1,   518400, 0xbab197ea
+0,        672,        672,        1,   518400, 0xbab197ea
+0,        673,        673,        1,   518400, 0xbab197ea
+0,        674,        674,        1,   518400, 0xbab197ea
+0,        675,        675,        1,   518400, 0xbab197ea
+0,        676,        676,        1,   518400, 0xbab197ea
+0,        677,        677,        1,   518400, 0xbab197ea
+0,        678,        678,        1,   518400, 0xbab197ea
+0,        679,        679,        1,   518400, 0xbab197ea
+0,        680,        680,        1,   518400, 0xbab197ea
+0,        681,        681,        1,   518400, 0xbab197ea
+0,        682,        682,        1,   518400, 0xbab197ea
+0,        683,        683,        1,   518400, 0xbab197ea
+0,        684,        684,        1,   518400, 0xbab197ea
+0,        685,        685,        1,   518400, 0xbab197ea
+0,        686,        686,        1,   518400, 0xbab197ea
+0,        687,        687,        1,   518400, 0xbab197ea
+0,        688,        688,        1,   518400, 0xbab197ea
+0,        689,        689,        1,   518400, 0xbab197ea
+0,        690,        690,        1,   518400, 0xbab197ea
+0,        691,        691,        1,   518400, 0xbab197ea
+0,        692,        692,        1,   518400, 0xbab197ea
+0,        693,        693,        1,   518400, 0xbab197ea
+0,        694,        694,        1,   518400, 0xbab197ea
+0,        695,        695,        1,   518400, 0xbab197ea
+0,        696,        696,        1,   518400, 0xbab197ea
+0,        697,        697,        1,   518400, 0xbab197ea
+0,        698,        698,        1,   518400, 0xbab197ea
+0,        699,        699,        1,   518400, 0xbab197ea
+0,        700,        700,        1,   518400, 0xbab197ea
+0,        701,        701,        1,   518400, 0xbab197ea
+0,        702,        702,        1,   518400, 0xbab197ea
+0,        703,        703,        1,   518400, 0xbab197ea
+0,        704,        704,        1,   518400, 0xbab197ea
+0,        705,        705,        1,   518400, 0xbab197ea
+0,        706,        706,        1,   518400, 0xbab197ea
+0,        707,        707,        1,   518400, 0xbab197ea
 1,  141556000,  141556000,  1661000,     1088, 0xde20aa20
 0,        708,        708,        1,   518400, 0xb8bc1365
-0,        716,        716,        1,   518400, 0xbab197ea
+0,        709,        709,        1,   518400, 0xb8bc1365
+0,        710,        710,        1,   518400, 0xb8bc1365
+0,        711,        711,        1,   518400, 0xb8bc1365
+0,        712,        712,        1,   518400, 0xb8bc1365
+0,        713,        713,        1,   518400, 0xb8bc1365
+0,        714,        714,        1,   518400, 0xb8bc1365
+0,        715,        715,        1,   518400, 0xb8bc1365
+0,        716,        716,        1,   518400, 0xb8bc1365
+0,        717,        717,        1,   518400, 0xbab197ea
+0,        718,        718,        1,   518400, 0xbab197ea
+0,        719,        719,        1,   518400, 0xbab197ea
+0,        720,        720,        1,   518400, 0xbab197ea
+0,        721,        721,        1,   518400, 0xbab197ea
+0,        722,        722,        1,   518400, 0xbab197ea
+0,        723,        723,        1,   518400, 0xbab197ea
+0,        724,        724,        1,   518400, 0xbab197ea
+0,        725,        725,        1,   518400, 0xbab197ea
+0,        726,        726,        1,   518400, 0xbab197ea
+0,        727,        727,        1,   518400, 0xbab197ea
+0,        728,        728,        1,   518400, 0xbab197ea
+0,        729,        729,        1,   518400, 0xbab197ea
+0,        730,        730,        1,   518400, 0xbab197ea
+0,        731,        731,        1,   518400, 0xbab197ea
+0,        732,        732,        1,   518400, 0xbab197ea
+0,        733,        733,        1,   518400, 0xbab197ea
+0,        734,        734,        1,   518400, 0xbab197ea
+0,        735,        735,        1,   518400, 0xbab197ea
+0,        736,        736,        1,   518400, 0xbab197ea
+0,        737,        737,        1,   518400, 0xbab197ea
+0,        738,        738,        1,   518400, 0xbab197ea
+0,        739,        739,        1,   518400, 0xbab197ea
+0,        740,        740,        1,   518400, 0xbab197ea
+0,        741,        741,        1,   518400, 0xbab197ea
+0,        742,        742,        1,   518400, 0xbab197ea
+0,        743,        743,        1,   518400, 0xbab197ea
+0,        744,        744,        1,   518400, 0xbab197ea
+0,        745,        745,        1,   518400, 0xbab197ea
+0,        746,        746,        1,   518400, 0xbab197ea
+0,        747,        747,        1,   518400, 0xbab197ea
+0,        748,        748,        1,   518400, 0xbab197ea
+0,        749,        749,        1,   518400, 0xbab197ea
+0,        750,        750,        1,   518400, 0xbab197ea
+0,        751,        751,        1,   518400, 0xbab197ea
+0,        752,        752,        1,   518400, 0xbab197ea
+0,        753,        753,        1,   518400, 0xbab197ea
+0,        754,        754,        1,   518400, 0xbab197ea
+0,        755,        755,        1,   518400, 0xbab197ea
+0,        756,        756,        1,   518400, 0xbab197ea
+0,        757,        757,        1,   518400, 0xbab197ea
+0,        758,        758,        1,   518400, 0xbab197ea
+0,        759,        759,        1,   518400, 0xbab197ea
+0,        760,        760,        1,   518400, 0xbab197ea
+0,        761,        761,        1,   518400, 0xbab197ea
+0,        762,        762,        1,   518400, 0xbab197ea
+0,        763,        763,        1,   518400, 0xbab197ea
+0,        764,        764,        1,   518400, 0xbab197ea
+0,        765,        765,        1,   518400, 0xbab197ea
+0,        766,        766,        1,   518400, 0xbab197ea
+0,        767,        767,        1,   518400, 0xbab197ea
+0,        768,        768,        1,   518400, 0xbab197ea
+0,        769,        769,        1,   518400, 0xbab197ea
+0,        770,        770,        1,   518400, 0xbab197ea
+0,        771,        771,        1,   518400, 0xbab197ea
+0,        772,        772,        1,   518400, 0xbab197ea
+0,        773,        773,        1,   518400, 0xbab197ea
+0,        774,        774,        1,   518400, 0xbab197ea
+0,        775,        775,        1,   518400, 0xbab197ea
+0,        776,        776,        1,   518400, 0xbab197ea
+0,        777,        777,        1,   518400, 0xbab197ea
+0,        778,        778,        1,   518400, 0xbab197ea
+0,        779,        779,        1,   518400, 0xbab197ea
+0,        780,        780,        1,   518400, 0xbab197ea
+0,        781,        781,        1,   518400, 0xbab197ea
+0,        782,        782,        1,   518400, 0xbab197ea
+0,        783,        783,        1,   518400, 0xbab197ea
+0,        784,        784,        1,   518400, 0xbab197ea
+0,        785,        785,        1,   518400, 0xbab197ea
+0,        786,        786,        1,   518400, 0xbab197ea
+0,        787,        787,        1,   518400, 0xbab197ea
+0,        788,        788,        1,   518400, 0xbab197ea
+0,        789,        789,        1,   518400, 0xbab197ea
+0,        790,        790,        1,   518400, 0xbab197ea
+0,        791,        791,        1,   518400, 0xbab197ea
+0,        792,        792,        1,   518400, 0xbab197ea
+0,        793,        793,        1,   518400, 0xbab197ea
+0,        794,        794,        1,   518400, 0xbab197ea
+0,        795,        795,        1,   518400, 0xbab197ea
+0,        796,        796,        1,   518400, 0xbab197ea
+0,        797,        797,        1,   518400, 0xbab197ea
+0,        798,        798,        1,   518400, 0xbab197ea
+0,        799,        799,        1,   518400, 0xbab197ea
+0,        800,        800,        1,   518400, 0xbab197ea
+0,        801,        801,        1,   518400, 0xbab197ea
+0,        802,        802,        1,   518400, 0xbab197ea
+0,        803,        803,        1,   518400, 0xbab197ea
+0,        804,        804,        1,   518400, 0xbab197ea
+0,        805,        805,        1,   518400, 0xbab197ea
+0,        806,        806,        1,   518400, 0xbab197ea
+0,        807,        807,        1,   518400, 0xbab197ea
+0,        808,        808,        1,   518400, 0xbab197ea
+0,        809,        809,        1,   518400, 0xbab197ea
+0,        810,        810,        1,   518400, 0xbab197ea
+0,        811,        811,        1,   518400, 0xbab197ea
+0,        812,        812,        1,   518400, 0xbab197ea
+0,        813,        813,        1,   518400, 0xbab197ea
+0,        814,        814,        1,   518400, 0xbab197ea
+0,        815,        815,        1,   518400, 0xbab197ea
+0,        816,        816,        1,   518400, 0xbab197ea
 0,        817,        817,        1,   518400, 0x83efa32d
 1,  163445000,  163445000,  1331000,      339, 0x8bd186ef
-0,        824,        824,        1,   518400, 0xbab197ea
+0,        818,        818,        1,   518400, 0x83efa32d
+0,        819,        819,        1,   518400, 0x83efa32d
+0,        820,        820,        1,   518400, 0x83efa32d
+0,        821,        821,        1,   518400, 0x83efa32d
+0,        822,        822,        1,   518400, 0x83efa32d
+0,        823,        823,        1,   518400, 0x83efa32d
+0,        824,        824,        1,   518400, 0x83efa32d
+0,        825,        825,        1,   518400, 0xbab197ea
+0,        826,        826,        1,   518400, 0xbab197ea
+0,        827,        827,        1,   518400, 0xbab197ea
+0,        828,        828,        1,   518400, 0xbab197ea
+0,        829,        829,        1,   518400, 0xbab197ea
+0,        830,        830,        1,   518400, 0xbab197ea
+0,        831,        831,        1,   518400, 0xbab197ea
+0,        832,        832,        1,   518400, 0xbab197ea
+0,        833,        833,        1,   518400, 0xbab197ea
+0,        834,        834,        1,   518400, 0xbab197ea
+0,        835,        835,        1,   518400, 0xbab197ea
+0,        836,        836,        1,   518400, 0xbab197ea
+0,        837,        837,        1,   518400, 0xbab197ea
+0,        838,        838,        1,   518400, 0xbab197ea
+0,        839,        839,        1,   518400, 0xbab197ea
 0,        840,        840,        1,   518400, 0x03ea0e90
 1,  168049000,  168049000,  1900000,     1312, 0x0bf20e8d
-0,        850,        850,        1,   518400, 0xbab197ea
+0,        841,        841,        1,   518400, 0x03ea0e90
+0,        842,        842,        1,   518400, 0x03ea0e90
+0,        843,        843,        1,   518400, 0x03ea0e90
+0,        844,        844,        1,   518400, 0x03ea0e90
+0,        845,        845,        1,   518400, 0x03ea0e90
+0,        846,        846,        1,   518400, 0x03ea0e90
+0,        847,        847,        1,   518400, 0x03ea0e90
+0,        848,        848,        1,   518400, 0x03ea0e90
+0,        849,        849,        1,   518400, 0x03ea0e90
+0,        850,        850,        1,   518400, 0x8780239e
 1,  170035000,  170035000,  1524000,     1279, 0xb6c2dafe
 0,        851,        851,        1,   518400, 0x8780239e
-0,        858,        858,        1,   518400, 0xbab197ea
+0,        852,        852,        1,   518400, 0x8780239e
+0,        853,        853,        1,   518400, 0x8780239e
+0,        854,        854,        1,   518400, 0x8780239e
+0,        855,        855,        1,   518400, 0x8780239e
+0,        856,        856,        1,   518400, 0x8780239e
+0,        857,        857,        1,   518400, 0x8780239e
+0,        858,        858,        1,   518400, 0x8780239e
+0,        859,        859,        1,   518400, 0xbab197ea
+0,        860,        860,        1,   518400, 0xbab197ea
 0,        861,        861,        1,   518400, 0x6eb72347
 1,  172203000,  172203000,  1695000,     1826, 0x9a1ac769
-0,        869,        869,        1,   518400, 0xbab197ea
+0,        862,        862,        1,   518400, 0x6eb72347
+0,        863,        863,        1,   518400, 0x6eb72347
+0,        864,        864,        1,   518400, 0x6eb72347
+0,        865,        865,        1,   518400, 0x6eb72347
+0,        866,        866,        1,   518400, 0x6eb72347
+0,        867,        867,        1,   518400, 0x6eb72347
+0,        868,        868,        1,   518400, 0x6eb72347
+0,        869,        869,        1,   518400, 0x6eb72347
 1,  173947000,  173947000,  1934000,     1474, 0xa9b03cdc
 0,        870,        870,        1,   518400, 0x9c4a3a3d
-0,        879,        879,        1,   518400, 0xbab197ea
+0,        871,        871,        1,   518400, 0x9c4a3a3d
+0,        872,        872,        1,   518400, 0x9c4a3a3d
+0,        873,        873,        1,   518400, 0x9c4a3a3d
+0,        874,        874,        1,   518400, 0x9c4a3a3d
+0,        875,        875,        1,   518400, 0x9c4a3a3d
+0,        876,        876,        1,   518400, 0x9c4a3a3d
+0,        877,        877,        1,   518400, 0x9c4a3a3d
+0,        878,        878,        1,   518400, 0x9c4a3a3d
+0,        879,        879,        1,   518400, 0x9c4a3a3d
 1,  175957000,  175957000,  1763000,     1019, 0x20409355
 0,        880,        880,        1,   518400, 0xc9ebfa89
-0,        889,        889,        1,   518400, 0xbab197ea
+0,        881,        881,        1,   518400, 0xc9ebfa89
+0,        882,        882,        1,   518400, 0xc9ebfa89
+0,        883,        883,        1,   518400, 0xc9ebfa89
+0,        884,        884,        1,   518400, 0xc9ebfa89
+0,        885,        885,        1,   518400, 0xc9ebfa89
+0,        886,        886,        1,   518400, 0xc9ebfa89
+0,        887,        887,        1,   518400, 0xc9ebfa89
+0,        888,        888,        1,   518400, 0xc9ebfa89
+0,        889,        889,        1,   518400, 0xc9ebfa89
+0,        890,        890,        1,   518400, 0xbab197ea
+0,        891,        891,        1,   518400, 0xbab197ea
+0,        892,        892,        1,   518400, 0xbab197ea
+0,        893,        893,        1,   518400, 0xbab197ea
+0,        894,        894,        1,   518400, 0xbab197ea
+0,        895,        895,        1,   518400, 0xbab197ea
+0,        896,        896,        1,   518400, 0xbab197ea
+0,        897,        897,        1,   518400, 0xbab197ea
+0,        898,        898,        1,   518400, 0xbab197ea
+0,        899,        899,        1,   518400, 0xbab197ea
+0,        900,        900,        1,   518400, 0xbab197ea
+0,        901,        901,        1,   518400, 0xbab197ea
+0,        902,        902,        1,   518400, 0xbab197ea
+0,        903,        903,        1,   518400, 0xbab197ea
+0,        904,        904,        1,   518400, 0xbab197ea
+0,        905,        905,        1,   518400, 0xbab197ea
+0,        906,        906,        1,   518400, 0xbab197ea
+0,        907,        907,        1,   518400, 0xbab197ea
+0,        908,        908,        1,   518400, 0xbab197ea
+0,        909,        909,        1,   518400, 0xbab197ea
+0,        910,        910,        1,   518400, 0xbab197ea
+0,        911,        911,        1,   518400, 0xbab197ea
+0,        912,        912,        1,   518400, 0xbab197ea
+0,        913,        913,        1,   518400, 0xbab197ea
+0,        914,        914,        1,   518400, 0xbab197ea
+0,        915,        915,        1,   518400, 0xbab197ea
+0,        916,        916,        1,   518400, 0xbab197ea
+0,        917,        917,        1,   518400, 0xbab197ea
+0,        918,        918,        1,   518400, 0xbab197ea
+0,        919,        919,        1,   518400, 0xbab197ea
+0,        920,        920,        1,   518400, 0xbab197ea
+0,        921,        921,        1,   518400, 0xbab197ea
+0,        922,        922,        1,   518400, 0xbab197ea
+0,        923,        923,        1,   518400, 0xbab197ea
+0,        924,        924,        1,   518400, 0xbab197ea
+0,        925,        925,        1,   518400, 0xbab197ea
+0,        926,        926,        1,   518400, 0xbab197ea
+0,        927,        927,        1,   518400, 0xbab197ea
+0,        928,        928,        1,   518400, 0xbab197ea
+0,        929,        929,        1,   518400, 0xbab197ea
+0,        930,        930,        1,   518400, 0xbab197ea
+0,        931,        931,        1,   518400, 0xbab197ea
+0,        932,        932,        1,   518400, 0xbab197ea
+0,        933,        933,        1,   518400, 0xbab197ea
+0,        934,        934,        1,   518400, 0xbab197ea
+0,        935,        935,        1,   518400, 0xbab197ea
+0,        936,        936,        1,   518400, 0xbab197ea
+0,        937,        937,        1,   518400, 0xbab197ea
+0,        938,        938,        1,   518400, 0xbab197ea
+0,        939,        939,        1,   518400, 0xbab197ea
+0,        940,        940,        1,   518400, 0xbab197ea
+0,        941,        941,        1,   518400, 0xbab197ea
+0,        942,        942,        1,   518400, 0xbab197ea
+0,        943,        943,        1,   518400, 0xbab197ea
+0,        944,        944,        1,   518400, 0xbab197ea
+0,        945,        945,        1,   518400, 0xbab197ea
 0,        946,        946,        1,   518400, 0xbaf801ef
 1,  189295000,  189295000,  1968000,     1596, 0x408c726e
-0,        956,        956,        1,   518400, 0xbab197ea
+0,        947,        947,        1,   518400, 0xbaf801ef
+0,        948,        948,        1,   518400, 0xbaf801ef
+0,        949,        949,        1,   518400, 0xbaf801ef
+0,        950,        950,        1,   518400, 0xbaf801ef
+0,        951,        951,        1,   518400, 0xbaf801ef
+0,        952,        952,        1,   518400, 0xbaf801ef
+0,        953,        953,        1,   518400, 0xbaf801ef
+0,        954,        954,        1,   518400, 0xbaf801ef
+0,        955,        955,        1,   518400, 0xbaf801ef
+0,        956,        956,        1,   518400, 0xbaf801ef
 1,  191356000,  191356000,  1228000,     1517, 0xae8c5c2b
 0,        957,        957,        1,   518400, 0x59f4e72f
-0,        963,        963,        1,   518400, 0xbab197ea
+0,        958,        958,        1,   518400, 0x59f4e72f
+0,        959,        959,        1,   518400, 0x59f4e72f
+0,        960,        960,        1,   518400, 0x59f4e72f
+0,        961,        961,        1,   518400, 0x59f4e72f
+0,        962,        962,        1,   518400, 0x59f4e72f
+0,        963,        963,        1,   518400, 0x9d5b9d69
 1,  192640000,  192640000,  1763000,     2506, 0xa458d6d4
 0,        964,        964,        1,   518400, 0x9d5b9d69
-0,        972,        972,        1,   518400, 0xbab197ea
+0,        965,        965,        1,   518400, 0x9d5b9d69
+0,        966,        966,        1,   518400, 0x9d5b9d69
+0,        967,        967,        1,   518400, 0x9d5b9d69
+0,        968,        968,        1,   518400, 0x9d5b9d69
+0,        969,        969,        1,   518400, 0x9d5b9d69
+0,        970,        970,        1,   518400, 0x9d5b9d69
+0,        971,        971,        1,   518400, 0x9d5b9d69
+0,        972,        972,        1,   518400, 0x9d5b9d69
+0,        973,        973,        1,   518400, 0xbab197ea
+0,        974,        974,        1,   518400, 0xbab197ea
+0,        975,        975,        1,   518400, 0xbab197ea
 1,  195193000,  195193000,  1092000,     1074, 0x397ba9a8
 0,        976,        976,        1,   518400, 0x923d1ce7
-0,        981,        981,        1,   518400, 0xbab197ea
+0,        977,        977,        1,   518400, 0x923d1ce7
+0,        978,        978,        1,   518400, 0x923d1ce7
+0,        979,        979,        1,   518400, 0x923d1ce7
+0,        980,        980,        1,   518400, 0x923d1ce7
+0,        981,        981,        1,   518400, 0x923d1ce7
 1,  196361000,  196361000,  1524000,     1715, 0x695ca41e
 0,        982,        982,        1,   518400, 0x6e652cd2
-0,        989,        989,        1,   518400, 0xbab197ea
+0,        983,        983,        1,   518400, 0x6e652cd2
+0,        984,        984,        1,   518400, 0x6e652cd2
+0,        985,        985,        1,   518400, 0x6e652cd2
+0,        986,        986,        1,   518400, 0x6e652cd2
+0,        987,        987,        1,   518400, 0x6e652cd2
+0,        988,        988,        1,   518400, 0x6e652cd2
+0,        989,        989,        1,   518400, 0x6e652cd2
 1,  197946000,  197946000,  1160000,      789, 0xc63a189e
 0,        990,        990,        1,   518400, 0x25113966
-0,        996,        996,        1,   518400, 0xbab197ea
+0,        991,        991,        1,   518400, 0x25113966
+0,        992,        992,        1,   518400, 0x25113966
+0,        993,        993,        1,   518400, 0x25113966
+0,        994,        994,        1,   518400, 0x25113966
+0,        995,        995,        1,   518400, 0x25113966
+0,        996,        996,        1,   518400, 0x2dc83609
 1,  199230000,  199230000,  1627000,     1846, 0xeea8c599
 0,        997,        997,        1,   518400, 0x2dc83609
-0,       1004,       1004,        1,   518400, 0xbab197ea
+0,        998,        998,        1,   518400, 0x2dc83609
+0,        999,        999,        1,   518400, 0x2dc83609
+0,       1000,       1000,        1,   518400, 0x2dc83609
+0,       1001,       1001,        1,   518400, 0x2dc83609
+0,       1002,       1002,        1,   518400, 0x2dc83609
+0,       1003,       1003,        1,   518400, 0x2dc83609
+0,       1004,       1004,        1,   518400, 0x2dc83609
 1,  200924000,  200924000,  1763000,      922, 0xd4a87222
 0,       1005,       1005,        1,   518400, 0x90483bc6
-0,       1013,       1013,        1,   518400, 0xbab197ea
+0,       1006,       1006,        1,   518400, 0x90483bc6
+0,       1007,       1007,        1,   518400, 0x90483bc6
+0,       1008,       1008,        1,   518400, 0x90483bc6
+0,       1009,       1009,        1,   518400, 0x90483bc6
+0,       1010,       1010,        1,   518400, 0x90483bc6
+0,       1011,       1011,        1,   518400, 0x90483bc6
+0,       1012,       1012,        1,   518400, 0x90483bc6
+0,       1013,       1013,        1,   518400, 0x90483bc6
+0,       1014,       1014,        1,   518400, 0xbab197ea
+0,       1015,       1015,        1,   518400, 0xbab197ea
+0,       1016,       1016,        1,   518400, 0xbab197ea
+0,       1017,       1017,        1,   518400, 0xbab197ea
+0,       1018,       1018,        1,   518400, 0xbab197ea
+0,       1019,       1019,        1,   518400, 0xbab197ea
+0,       1020,       1020,        1,   518400, 0xbab197ea
+0,       1021,       1021,        1,   518400, 0xbab197ea
+0,       1022,       1022,        1,   518400, 0xbab197ea
+0,       1023,       1023,        1,   518400, 0xbab197ea
+0,       1024,       1024,        1,   518400, 0xbab197ea
+0,       1025,       1025,        1,   518400, 0xbab197ea
+0,       1026,       1026,        1,   518400, 0xbab197ea
+0,       1027,       1027,        1,   518400, 0xbab197ea
+0,       1028,       1028,        1,   518400, 0xbab197ea
+0,       1029,       1029,        1,   518400, 0xbab197ea
+0,       1030,       1030,        1,   518400, 0xbab197ea
+0,       1031,       1031,        1,   518400, 0xbab197ea
+0,       1032,       1032,        1,   518400, 0xbab197ea
+0,       1033,       1033,        1,   518400, 0xbab197ea
+0,       1034,       1034,        1,   518400, 0xbab197ea
+0,       1035,       1035,        1,   518400, 0xbab197ea
+0,       1036,       1036,        1,   518400, 0xbab197ea
+0,       1037,       1037,        1,   518400, 0xbab197ea
+0,       1038,       1038,        1,   518400, 0xbab197ea
+0,       1039,       1039,        1,   518400, 0xbab197ea
+0,       1040,       1040,        1,   518400, 0xbab197ea
+0,       1041,       1041,        1,   518400, 0xbab197ea
+0,       1042,       1042,        1,   518400, 0xbab197ea
+0,       1043,       1043,        1,   518400, 0xbab197ea
+0,       1044,       1044,        1,   518400, 0xbab197ea
+0,       1045,       1045,        1,   518400, 0xbab197ea
+0,       1046,       1046,        1,   518400, 0xbab197ea
+0,       1047,       1047,        1,   518400, 0xbab197ea
+0,       1048,       1048,        1,   518400, 0xbab197ea
+0,       1049,       1049,        1,   518400, 0xbab197ea
+0,       1050,       1050,        1,   518400, 0xbab197ea
+0,       1051,       1051,        1,   518400, 0xbab197ea
+0,       1052,       1052,        1,   518400, 0xbab197ea
 0,       1053,       1053,        1,   518400, 0x3de86ab7
 1,  210600000,  210600000,  1831000,      665, 0x55580135
-0,       1062,       1062,        1,   518400, 0xbab197ea
+0,       1054,       1054,        1,   518400, 0x3de86ab7
+0,       1055,       1055,        1,   518400, 0x3de86ab7
+0,       1056,       1056,        1,   518400, 0x3de86ab7
+0,       1057,       1057,        1,   518400, 0x3de86ab7
+0,       1058,       1058,        1,   518400, 0x3de86ab7
+0,       1059,       1059,        1,   518400, 0x3de86ab7
+0,       1060,       1060,        1,   518400, 0x3de86ab7
+0,       1061,       1061,        1,   518400, 0x3de86ab7
+0,       1062,       1062,        1,   518400, 0x3de86ab7
+0,       1063,       1063,        1,   518400, 0xbab197ea
+0,       1064,       1064,        1,   518400, 0xbab197ea
+0,       1065,       1065,        1,   518400, 0xbab197ea
+0,       1066,       1066,        1,   518400, 0xbab197ea
+0,       1067,       1067,        1,   518400, 0xbab197ea
+0,       1068,       1068,        1,   518400, 0xbab197ea
+0,       1069,       1069,        1,   518400, 0xbab197ea
+0,       1070,       1070,        1,   518400, 0xbab197ea
+0,       1071,       1071,        1,   518400, 0xbab197ea
+0,       1072,       1072,        1,   518400, 0xbab197ea
+0,       1073,       1073,        1,   518400, 0xbab197ea
 1,  214771000,  214771000,  1558000,     1216, 0x50d1f6c5
 0,       1074,       1074,        1,   518400, 0x8c320e68
-0,       1082,       1082,        1,   518400, 0xbab197ea
+0,       1075,       1075,        1,   518400, 0x8c320e68
+0,       1076,       1076,        1,   518400, 0x8c320e68
+0,       1077,       1077,        1,   518400, 0x8c320e68
+0,       1078,       1078,        1,   518400, 0x8c320e68
+0,       1079,       1079,        1,   518400, 0x8c320e68
+0,       1080,       1080,        1,   518400, 0x8c320e68
+0,       1081,       1081,        1,   518400, 0x8c320e68
+0,       1082,       1082,        1,   518400, 0x8c320e68
+0,       1083,       1083,        1,   518400, 0xbab197ea
+0,       1084,       1084,        1,   518400, 0xbab197ea
+0,       1085,       1085,        1,   518400, 0xbab197ea
+0,       1086,       1086,        1,   518400, 0xbab197ea
+0,       1087,       1087,        1,   518400, 0xbab197ea
+0,       1088,       1088,        1,   518400, 0xbab197ea
+0,       1089,       1089,        1,   518400, 0xbab197ea
+0,       1090,       1090,        1,   518400, 0xbab197ea
+0,       1091,       1091,        1,   518400, 0xbab197ea
+0,       1092,       1092,        1,   518400, 0xbab197ea
+0,       1093,       1093,        1,   518400, 0xbab197ea
+0,       1094,       1094,        1,   518400, 0xbab197ea
+0,       1095,       1095,        1,   518400, 0xbab197ea
+0,       1096,       1096,        1,   518400, 0xbab197ea
+0,       1097,       1097,        1,   518400, 0xbab197ea
+0,       1098,       1098,        1,   518400, 0xbab197ea
+0,       1099,       1099,        1,   518400, 0xbab197ea
+0,       1100,       1100,        1,   518400, 0xbab197ea
+0,       1101,       1101,        1,   518400, 0xbab197ea
+0,       1102,       1102,        1,   518400, 0xbab197ea
+0,       1103,       1103,        1,   518400, 0xbab197ea
+0,       1104,       1104,        1,   518400, 0xbab197ea
+0,       1105,       1105,        1,   518400, 0xbab197ea
+0,       1106,       1106,        1,   518400, 0xbab197ea
+0,       1107,       1107,        1,   518400, 0xbab197ea
+0,       1108,       1108,        1,   518400, 0xbab197ea
+0,       1109,       1109,        1,   518400, 0xbab197ea
+0,       1110,       1110,        1,   518400, 0xbab197ea
+0,       1111,       1111,        1,   518400, 0xbab197ea
+0,       1112,       1112,        1,   518400, 0xbab197ea
+0,       1113,       1113,        1,   518400, 0xbab197ea
+0,       1114,       1114,        1,   518400, 0xbab197ea
+0,       1115,       1115,        1,   518400, 0xbab197ea
+0,       1116,       1116,        1,   518400, 0xbab197ea
+0,       1117,       1117,        1,   518400, 0xbab197ea
+0,       1118,       1118,        1,   518400, 0xbab197ea
+0,       1119,       1119,        1,   518400, 0xbab197ea
+0,       1120,       1120,        1,   518400, 0xbab197ea
+0,       1121,       1121,        1,   518400, 0xbab197ea
+0,       1122,       1122,        1,   518400, 0xbab197ea
+0,       1123,       1123,        1,   518400, 0xbab197ea
+0,       1124,       1124,        1,   518400, 0xbab197ea
+0,       1125,       1125,        1,   518400, 0xbab197ea
+0,       1126,       1126,        1,   518400, 0xbab197ea
+0,       1127,       1127,        1,   518400, 0xbab197ea
 0,       1128,       1128,        1,   518400, 0x81e977b2
 1,  225640000,  225640000,  2127000,     2133, 0x670c11a5
-0,       1139,       1139,        1,   518400, 0xbab197ea
+0,       1129,       1129,        1,   518400, 0x81e977b2
+0,       1130,       1130,        1,   518400, 0x81e977b2
+0,       1131,       1131,        1,   518400, 0x81e977b2
+0,       1132,       1132,        1,   518400, 0x81e977b2
+0,       1133,       1133,        1,   518400, 0x81e977b2
+0,       1134,       1134,        1,   518400, 0x81e977b2
+0,       1135,       1135,        1,   518400, 0x81e977b2
+0,       1136,       1136,        1,   518400, 0x81e977b2
+0,       1137,       1137,        1,   518400, 0x81e977b2
+0,       1138,       1138,        1,   518400, 0x81e977b2
+0,       1139,       1139,        1,   518400, 0xb046dd30
 1,  227834000,  227834000,  1262000,     1264, 0xc1d9fc57
 0,       1140,       1140,        1,   518400, 0xb046dd30
-0,       1145,       1145,        1,   518400, 0xbab197ea
diff --git a/tests/ref/fate/sub2video_basic b/tests/ref/fate/sub2video_basic
index 5f72e292c9..9bd4e8b114 100644
--- a/tests/ref/fate/sub2video_basic
+++ b/tests/ref/fate/sub2video_basic
@@ -1,95 +1,1149 @@
-#tb 0: 1/25
+#tb 0: 1/5
 #media_type 0: video
 #codec_id 0: rawvideo
 #dimensions 0: 720x480
-#sar 0: 0/1
-0,       3312,       3312,        1,  1382400, 0x00000000
-0,       3312,       3312,        1,  1382400, 0x8c93c2ba
-0,       3436,       3436,        1,  1382400, 0x00000000
-0,       3684,       3684,        1,  1382400, 0xb02e32ca
-0,       3802,       3802,        1,  1382400, 0x00000000
-0,       4520,       4520,        1,  1382400, 0x83b71116
-0,       4584,       4584,        1,  1382400, 0x00000000
-0,       4586,       4586,        1,  1382400, 0x85547fd1
-0,       4645,       4645,        1,  1382400, 0x00000000
-0,       4648,       4648,        1,  1382400, 0x00000000
-0,       4648,       4648,        1,  1382400, 0xb6a8f181
-0,       4715,       4715,        1,  1382400, 0x00000000
-0,       4717,       4717,        1,  1382400, 0xb64d1a2c
-0,       4748,       4748,        1,  1382400, 0x00000000
-0,       4750,       4750,        1,  1382400, 0x7b37ecf3
-0,       4792,       4792,        1,  1382400, 0x00000000
-0,       4993,       4993,        1,  1382400, 0xdc025bd1
-0,       5027,       5027,        1,  1382400, 0x00000000
-0,       5029,       5029,        1,  1382400, 0x688b294d
-0,       5068,       5068,        1,  1382400, 0x00000000
-0,       5070,       5070,        1,  1382400, 0xa2b33d1b
-0,       5117,       5117,        1,  1382400, 0x00000000
-0,       5119,       5119,        1,  1382400, 0xb3e525e3
-0,       5168,       5168,        1,  1382400, 0x00000000
-0,       5170,       5170,        1,  1382400, 0xaa8fbdd7
-0,       5216,       5216,        1,  1382400, 0x00000000
-0,       5218,       5218,        1,  1382400, 0x7b7f26dd
-0,       5249,       5249,        1,  1382400, 0x00000000
-0,       5251,       5251,        1,  1382400, 0x15e2f836
-0,       5289,       5289,        1,  1382400, 0x00000000
-0,       5291,       5291,        1,  1382400, 0x0fee9b0c
-0,       5358,       5358,        1,  1382400, 0x00000000
-0,       5360,       5360,        1,  1382400, 0x89d62791
-0,       5429,       5429,        1,  1382400, 0x00000000
-0,       5431,       5431,        1,  1382400, 0xa6a9fd74
-0,       5490,       5490,        1,  1382400, 0x00000000
-0,       5491,       5491,        1,  1382400, 0x7896178d
-0,       5537,       5537,        1,  1382400, 0x00000000
-0,       5588,       5588,        1,  1382400, 0x01751a52
-0,       5647,       5647,        1,  1382400, 0x00000000
-0,       5688,       5688,        1,  1382400, 0xa3959c6f
-0,       5770,       5770,        1,  1382400, 0x00000000
-0,       5772,       5772,        1,  1382400, 0x3d3ea47b
-0,       5826,       5826,        1,  1382400, 0x00000000
-0,       5828,       5828,        1,  1382400, 0x593f8b24
-0,       5931,       5931,        1,  1382400, 0x00000000
-0,       5933,       5933,        1,  1382400, 0x171f05ba
-0,       6001,       6001,        1,  1382400, 0x00000000
-0,       6003,       6003,        1,  1382400, 0xb014cdf1
-0,       6054,       6054,        1,  1382400, 0x00000000
-0,       6839,       6839,        1,  1382400, 0xd918e667
-0,       6880,       6880,        1,  1382400, 0x00000000
-0,       7386,       7386,        1,  1382400, 0xc9406331
-0,       7419,       7419,        1,  1382400, 0x00000000
-0,       7501,       7501,        1,  1382400, 0xaf08b10d
-0,       7549,       7549,        1,  1382400, 0x00000000
-0,       7551,       7551,        1,  1382400, 0x00000000
-0,       7551,       7551,        1,  1382400, 0x853a9d93
-0,       7589,       7589,        1,  1382400, 0x00000000
-0,       7605,       7605,        1,  1382400, 0x7491a87d
-0,       7647,       7647,        1,  1382400, 0x00000000
-0,       7649,       7649,        1,  1382400, 0xf7383c58
-0,       7697,       7697,        1,  1382400, 0x00000000
-0,       7699,       7699,        1,  1382400, 0xe66be411
-0,       7743,       7743,        1,  1382400, 0x00000000
-0,       8032,       8032,        1,  1382400, 0xd6850362
-0,       8082,       8082,        1,  1382400, 0x00000000
-0,       8084,       8084,        1,  1382400, 0x3e1ed109
-0,       8115,       8115,        1,  1382400, 0x00000000
-0,       8116,       8116,        1,  1382400, 0x39c1b7bd
-0,       8160,       8160,        1,  1382400, 0x00000000
-0,       8180,       8180,        1,  1382400, 0x35b85f2e
-0,       8207,       8207,        1,  1382400, 0x00000000
-0,       8209,       8209,        1,  1382400, 0x00000000
-0,       8209,       8209,        1,  1382400, 0x83f103e5
-0,       8247,       8247,        1,  1382400, 0x00000000
-0,       8249,       8249,        1,  1382400, 0xbc1ca9b3
-0,       8278,       8278,        1,  1382400, 0x00000000
-0,       8281,       8281,        1,  1382400, 0x94d4a51e
-0,       8321,       8321,        1,  1382400, 0x00000000
-0,       8323,       8323,        1,  1382400, 0xf88cdfde
-0,       8367,       8367,        1,  1382400, 0x00000000
-0,       8565,       8565,        1,  1382400, 0xdd51423b
-0,       8611,       8611,        1,  1382400, 0x00000000
-0,       8669,       8669,        1,  1382400, 0x08259fa4
-0,       8708,       8708,        1,  1382400, 0x00000000
-0,       8941,       8941,        1,  1382400, 0x1663fa34
-0,       8994,       8994,        1,  1382400, 0x00000000
-0,       8996,       8996,        1,  1382400, 0xda2ceb55
-0,       9027,       9027,        1,  1382400, 0x00000000
+#sar 0: 1/1
+0,        662,        662,        1,  1382400, 0xc637b893
+0,        663,        663,        1,  1382400, 0xc637b893
+0,        664,        664,        1,  1382400, 0xc637b893
+0,        665,        665,        1,  1382400, 0xc637b893
+0,        666,        666,        1,  1382400, 0xc637b893
+0,        667,        667,        1,  1382400, 0xc637b893
+0,        668,        668,        1,  1382400, 0xc637b893
+0,        669,        669,        1,  1382400, 0xc637b893
+0,        670,        670,        1,  1382400, 0xc637b893
+0,        671,        671,        1,  1382400, 0xc637b893
+0,        672,        672,        1,  1382400, 0xc637b893
+0,        673,        673,        1,  1382400, 0xc637b893
+0,        674,        674,        1,  1382400, 0xc637b893
+0,        675,        675,        1,  1382400, 0xc637b893
+0,        676,        676,        1,  1382400, 0xc637b893
+0,        677,        677,        1,  1382400, 0xc637b893
+0,        678,        678,        1,  1382400, 0xc637b893
+0,        679,        679,        1,  1382400, 0xc637b893
+0,        680,        680,        1,  1382400, 0xc637b893
+0,        681,        681,        1,  1382400, 0xc637b893
+0,        682,        682,        1,  1382400, 0xc637b893
+0,        683,        683,        1,  1382400, 0xc637b893
+0,        684,        684,        1,  1382400, 0xc637b893
+0,        685,        685,        1,  1382400, 0xc637b893
+0,        686,        686,        1,  1382400, 0xc637b893
+0,        687,        687,        1,  1382400, 0x00000000
+0,        688,        688,        1,  1382400, 0x00000000
+0,        689,        689,        1,  1382400, 0x00000000
+0,        690,        690,        1,  1382400, 0x00000000
+0,        691,        691,        1,  1382400, 0x00000000
+0,        692,        692,        1,  1382400, 0x00000000
+0,        693,        693,        1,  1382400, 0x00000000
+0,        694,        694,        1,  1382400, 0x00000000
+0,        695,        695,        1,  1382400, 0x00000000
+0,        696,        696,        1,  1382400, 0x00000000
+0,        697,        697,        1,  1382400, 0x00000000
+0,        698,        698,        1,  1382400, 0x00000000
+0,        699,        699,        1,  1382400, 0x00000000
+0,        700,        700,        1,  1382400, 0x00000000
+0,        701,        701,        1,  1382400, 0x00000000
+0,        702,        702,        1,  1382400, 0x00000000
+0,        703,        703,        1,  1382400, 0x00000000
+0,        704,        704,        1,  1382400, 0x00000000
+0,        705,        705,        1,  1382400, 0x00000000
+0,        706,        706,        1,  1382400, 0x00000000
+0,        707,        707,        1,  1382400, 0x00000000
+0,        708,        708,        1,  1382400, 0x00000000
+0,        709,        709,        1,  1382400, 0x00000000
+0,        710,        710,        1,  1382400, 0x00000000
+0,        711,        711,        1,  1382400, 0x00000000
+0,        712,        712,        1,  1382400, 0x00000000
+0,        713,        713,        1,  1382400, 0x00000000
+0,        714,        714,        1,  1382400, 0x00000000
+0,        715,        715,        1,  1382400, 0x00000000
+0,        716,        716,        1,  1382400, 0x00000000
+0,        717,        717,        1,  1382400, 0x00000000
+0,        718,        718,        1,  1382400, 0x00000000
+0,        719,        719,        1,  1382400, 0x00000000
+0,        720,        720,        1,  1382400, 0x00000000
+0,        721,        721,        1,  1382400, 0x00000000
+0,        722,        722,        1,  1382400, 0x00000000
+0,        723,        723,        1,  1382400, 0x00000000
+0,        724,        724,        1,  1382400, 0x00000000
+0,        725,        725,        1,  1382400, 0x00000000
+0,        726,        726,        1,  1382400, 0x00000000
+0,        727,        727,        1,  1382400, 0x00000000
+0,        728,        728,        1,  1382400, 0x00000000
+0,        729,        729,        1,  1382400, 0x00000000
+0,        730,        730,        1,  1382400, 0x00000000
+0,        731,        731,        1,  1382400, 0x00000000
+0,        732,        732,        1,  1382400, 0x00000000
+0,        733,        733,        1,  1382400, 0x00000000
+0,        734,        734,        1,  1382400, 0x00000000
+0,        735,        735,        1,  1382400, 0x00000000
+0,        737,        737,        1,  1382400, 0x4c2960ca
+0,        737,        737,        1,  1382400, 0x4c2960ca
+0,        738,        738,        1,  1382400, 0x4c2960ca
+0,        739,        739,        1,  1382400, 0x4c2960ca
+0,        740,        740,        1,  1382400, 0x4c2960ca
+0,        741,        741,        1,  1382400, 0x4c2960ca
+0,        742,        742,        1,  1382400, 0x4c2960ca
+0,        743,        743,        1,  1382400, 0x4c2960ca
+0,        744,        744,        1,  1382400, 0x4c2960ca
+0,        745,        745,        1,  1382400, 0x4c2960ca
+0,        746,        746,        1,  1382400, 0x4c2960ca
+0,        747,        747,        1,  1382400, 0x4c2960ca
+0,        748,        748,        1,  1382400, 0x4c2960ca
+0,        749,        749,        1,  1382400, 0x4c2960ca
+0,        750,        750,        1,  1382400, 0x4c2960ca
+0,        751,        751,        1,  1382400, 0x4c2960ca
+0,        752,        752,        1,  1382400, 0x4c2960ca
+0,        753,        753,        1,  1382400, 0x4c2960ca
+0,        754,        754,        1,  1382400, 0x4c2960ca
+0,        755,        755,        1,  1382400, 0x4c2960ca
+0,        756,        756,        1,  1382400, 0x4c2960ca
+0,        757,        757,        1,  1382400, 0x4c2960ca
+0,        758,        758,        1,  1382400, 0x4c2960ca
+0,        759,        759,        1,  1382400, 0x4c2960ca
+0,        760,        760,        1,  1382400, 0x00000000
+0,        761,        761,        1,  1382400, 0x00000000
+0,        762,        762,        1,  1382400, 0x00000000
+0,        763,        763,        1,  1382400, 0x00000000
+0,        764,        764,        1,  1382400, 0x00000000
+0,        765,        765,        1,  1382400, 0x00000000
+0,        766,        766,        1,  1382400, 0x00000000
+0,        767,        767,        1,  1382400, 0x00000000
+0,        768,        768,        1,  1382400, 0x00000000
+0,        769,        769,        1,  1382400, 0x00000000
+0,        770,        770,        1,  1382400, 0x00000000
+0,        771,        771,        1,  1382400, 0x00000000
+0,        772,        772,        1,  1382400, 0x00000000
+0,        773,        773,        1,  1382400, 0x00000000
+0,        774,        774,        1,  1382400, 0x00000000
+0,        775,        775,        1,  1382400, 0x00000000
+0,        776,        776,        1,  1382400, 0x00000000
+0,        777,        777,        1,  1382400, 0x00000000
+0,        778,        778,        1,  1382400, 0x00000000
+0,        779,        779,        1,  1382400, 0x00000000
+0,        780,        780,        1,  1382400, 0x00000000
+0,        781,        781,        1,  1382400, 0x00000000
+0,        782,        782,        1,  1382400, 0x00000000
+0,        783,        783,        1,  1382400, 0x00000000
+0,        784,        784,        1,  1382400, 0x00000000
+0,        785,        785,        1,  1382400, 0x00000000
+0,        786,        786,        1,  1382400, 0x00000000
+0,        787,        787,        1,  1382400, 0x00000000
+0,        788,        788,        1,  1382400, 0x00000000
+0,        789,        789,        1,  1382400, 0x00000000
+0,        790,        790,        1,  1382400, 0x00000000
+0,        791,        791,        1,  1382400, 0x00000000
+0,        792,        792,        1,  1382400, 0x00000000
+0,        793,        793,        1,  1382400, 0x00000000
+0,        794,        794,        1,  1382400, 0x00000000
+0,        795,        795,        1,  1382400, 0x00000000
+0,        796,        796,        1,  1382400, 0x00000000
+0,        797,        797,        1,  1382400, 0x00000000
+0,        798,        798,        1,  1382400, 0x00000000
+0,        799,        799,        1,  1382400, 0x00000000
+0,        800,        800,        1,  1382400, 0x00000000
+0,        801,        801,        1,  1382400, 0x00000000
+0,        802,        802,        1,  1382400, 0x00000000
+0,        803,        803,        1,  1382400, 0x00000000
+0,        804,        804,        1,  1382400, 0x00000000
+0,        805,        805,        1,  1382400, 0x00000000
+0,        806,        806,        1,  1382400, 0x00000000
+0,        807,        807,        1,  1382400, 0x00000000
+0,        808,        808,        1,  1382400, 0x00000000
+0,        809,        809,        1,  1382400, 0x00000000
+0,        810,        810,        1,  1382400, 0x00000000
+0,        811,        811,        1,  1382400, 0x00000000
+0,        812,        812,        1,  1382400, 0x00000000
+0,        813,        813,        1,  1382400, 0x00000000
+0,        814,        814,        1,  1382400, 0x00000000
+0,        815,        815,        1,  1382400, 0x00000000
+0,        816,        816,        1,  1382400, 0x00000000
+0,        817,        817,        1,  1382400, 0x00000000
+0,        818,        818,        1,  1382400, 0x00000000
+0,        819,        819,        1,  1382400, 0x00000000
+0,        820,        820,        1,  1382400, 0x00000000
+0,        821,        821,        1,  1382400, 0x00000000
+0,        822,        822,        1,  1382400, 0x00000000
+0,        823,        823,        1,  1382400, 0x00000000
+0,        824,        824,        1,  1382400, 0x00000000
+0,        825,        825,        1,  1382400, 0x00000000
+0,        826,        826,        1,  1382400, 0x00000000
+0,        827,        827,        1,  1382400, 0x00000000
+0,        828,        828,        1,  1382400, 0x00000000
+0,        829,        829,        1,  1382400, 0x00000000
+0,        830,        830,        1,  1382400, 0x00000000
+0,        831,        831,        1,  1382400, 0x00000000
+0,        832,        832,        1,  1382400, 0x00000000
+0,        833,        833,        1,  1382400, 0x00000000
+0,        834,        834,        1,  1382400, 0x00000000
+0,        835,        835,        1,  1382400, 0x00000000
+0,        836,        836,        1,  1382400, 0x00000000
+0,        837,        837,        1,  1382400, 0x00000000
+0,        838,        838,        1,  1382400, 0x00000000
+0,        839,        839,        1,  1382400, 0x00000000
+0,        840,        840,        1,  1382400, 0x00000000
+0,        841,        841,        1,  1382400, 0x00000000
+0,        842,        842,        1,  1382400, 0x00000000
+0,        843,        843,        1,  1382400, 0x00000000
+0,        844,        844,        1,  1382400, 0x00000000
+0,        845,        845,        1,  1382400, 0x00000000
+0,        846,        846,        1,  1382400, 0x00000000
+0,        847,        847,        1,  1382400, 0x00000000
+0,        848,        848,        1,  1382400, 0x00000000
+0,        849,        849,        1,  1382400, 0x00000000
+0,        850,        850,        1,  1382400, 0x00000000
+0,        851,        851,        1,  1382400, 0x00000000
+0,        852,        852,        1,  1382400, 0x00000000
+0,        853,        853,        1,  1382400, 0x00000000
+0,        854,        854,        1,  1382400, 0x00000000
+0,        855,        855,        1,  1382400, 0x00000000
+0,        856,        856,        1,  1382400, 0x00000000
+0,        857,        857,        1,  1382400, 0x00000000
+0,        858,        858,        1,  1382400, 0x00000000
+0,        859,        859,        1,  1382400, 0x00000000
+0,        860,        860,        1,  1382400, 0x00000000
+0,        861,        861,        1,  1382400, 0x00000000
+0,        862,        862,        1,  1382400, 0x00000000
+0,        863,        863,        1,  1382400, 0x00000000
+0,        864,        864,        1,  1382400, 0x00000000
+0,        865,        865,        1,  1382400, 0x00000000
+0,        866,        866,        1,  1382400, 0x00000000
+0,        867,        867,        1,  1382400, 0x00000000
+0,        868,        868,        1,  1382400, 0x00000000
+0,        869,        869,        1,  1382400, 0x00000000
+0,        870,        870,        1,  1382400, 0x00000000
+0,        871,        871,        1,  1382400, 0x00000000
+0,        872,        872,        1,  1382400, 0x00000000
+0,        873,        873,        1,  1382400, 0x00000000
+0,        874,        874,        1,  1382400, 0x00000000
+0,        875,        875,        1,  1382400, 0x00000000
+0,        876,        876,        1,  1382400, 0x00000000
+0,        877,        877,        1,  1382400, 0x00000000
+0,        878,        878,        1,  1382400, 0x00000000
+0,        879,        879,        1,  1382400, 0x00000000
+0,        880,        880,        1,  1382400, 0x00000000
+0,        881,        881,        1,  1382400, 0x00000000
+0,        882,        882,        1,  1382400, 0x00000000
+0,        883,        883,        1,  1382400, 0x00000000
+0,        884,        884,        1,  1382400, 0x00000000
+0,        885,        885,        1,  1382400, 0x00000000
+0,        886,        886,        1,  1382400, 0x00000000
+0,        887,        887,        1,  1382400, 0x00000000
+0,        888,        888,        1,  1382400, 0x00000000
+0,        889,        889,        1,  1382400, 0x00000000
+0,        890,        890,        1,  1382400, 0x00000000
+0,        891,        891,        1,  1382400, 0x00000000
+0,        892,        892,        1,  1382400, 0x00000000
+0,        893,        893,        1,  1382400, 0x00000000
+0,        894,        894,        1,  1382400, 0x00000000
+0,        895,        895,        1,  1382400, 0x00000000
+0,        896,        896,        1,  1382400, 0x00000000
+0,        897,        897,        1,  1382400, 0x00000000
+0,        898,        898,        1,  1382400, 0x00000000
+0,        899,        899,        1,  1382400, 0x00000000
+0,        900,        900,        1,  1382400, 0x00000000
+0,        901,        901,        1,  1382400, 0x00000000
+0,        902,        902,        1,  1382400, 0x00000000
+0,        904,        904,        1,  1382400, 0x5fa18966
+0,        904,        904,        1,  1382400, 0x5fa18966
+0,        905,        905,        1,  1382400, 0x5fa18966
+0,        906,        906,        1,  1382400, 0x5fa18966
+0,        907,        907,        1,  1382400, 0x5fa18966
+0,        908,        908,        1,  1382400, 0x5fa18966
+0,        909,        909,        1,  1382400, 0x5fa18966
+0,        910,        910,        1,  1382400, 0x5fa18966
+0,        911,        911,        1,  1382400, 0x5fa18966
+0,        912,        912,        1,  1382400, 0x5fa18966
+0,        913,        913,        1,  1382400, 0x5fa18966
+0,        914,        914,        1,  1382400, 0x5fa18966
+0,        915,        915,        1,  1382400, 0x5fa18966
+0,        916,        916,        1,  1382400, 0x5fa18966
+0,        917,        917,        1,  1382400, 0x55f4b7b1
+0,        918,        918,        1,  1382400, 0x55f4b7b1
+0,        919,        919,        1,  1382400, 0x55f4b7b1
+0,        920,        920,        1,  1382400, 0x55f4b7b1
+0,        921,        921,        1,  1382400, 0x55f4b7b1
+0,        922,        922,        1,  1382400, 0x55f4b7b1
+0,        923,        923,        1,  1382400, 0x55f4b7b1
+0,        924,        924,        1,  1382400, 0x55f4b7b1
+0,        925,        925,        1,  1382400, 0x55f4b7b1
+0,        926,        926,        1,  1382400, 0x55f4b7b1
+0,        927,        927,        1,  1382400, 0x55f4b7b1
+0,        928,        928,        1,  1382400, 0x55f4b7b1
+0,        929,        929,        1,  1382400, 0x00000000
+0,        930,        930,        1,  1382400, 0xdfa4cf32
+0,        931,        931,        1,  1382400, 0xdfa4cf32
+0,        932,        932,        1,  1382400, 0xdfa4cf32
+0,        933,        933,        1,  1382400, 0xdfa4cf32
+0,        934,        934,        1,  1382400, 0xdfa4cf32
+0,        935,        935,        1,  1382400, 0xdfa4cf32
+0,        936,        936,        1,  1382400, 0xdfa4cf32
+0,        937,        937,        1,  1382400, 0xdfa4cf32
+0,        938,        938,        1,  1382400, 0xdfa4cf32
+0,        939,        939,        1,  1382400, 0xdfa4cf32
+0,        940,        940,        1,  1382400, 0xdfa4cf32
+0,        941,        941,        1,  1382400, 0xdfa4cf32
+0,        942,        942,        1,  1382400, 0xdfa4cf32
+0,        943,        943,        1,  1382400, 0x35023df8
+0,        944,        944,        1,  1382400, 0x35023df8
+0,        945,        945,        1,  1382400, 0x35023df8
+0,        946,        946,        1,  1382400, 0x35023df8
+0,        947,        947,        1,  1382400, 0x35023df8
+0,        948,        948,        1,  1382400, 0x35023df8
+0,        949,        949,        1,  1382400, 0x35023df8
+0,        950,        950,        1,  1382400, 0xed933219
+0,        951,        951,        1,  1382400, 0xed933219
+0,        952,        952,        1,  1382400, 0xed933219
+0,        953,        953,        1,  1382400, 0xed933219
+0,        954,        954,        1,  1382400, 0xed933219
+0,        955,        955,        1,  1382400, 0xed933219
+0,        956,        956,        1,  1382400, 0xed933219
+0,        957,        957,        1,  1382400, 0xed933219
+0,        958,        958,        1,  1382400, 0x00000000
+0,        959,        959,        1,  1382400, 0x00000000
+0,        960,        960,        1,  1382400, 0x00000000
+0,        961,        961,        1,  1382400, 0x00000000
+0,        962,        962,        1,  1382400, 0x00000000
+0,        963,        963,        1,  1382400, 0x00000000
+0,        964,        964,        1,  1382400, 0x00000000
+0,        965,        965,        1,  1382400, 0x00000000
+0,        966,        966,        1,  1382400, 0x00000000
+0,        967,        967,        1,  1382400, 0x00000000
+0,        968,        968,        1,  1382400, 0x00000000
+0,        969,        969,        1,  1382400, 0x00000000
+0,        970,        970,        1,  1382400, 0x00000000
+0,        971,        971,        1,  1382400, 0x00000000
+0,        972,        972,        1,  1382400, 0x00000000
+0,        973,        973,        1,  1382400, 0x00000000
+0,        974,        974,        1,  1382400, 0x00000000
+0,        975,        975,        1,  1382400, 0x00000000
+0,        976,        976,        1,  1382400, 0x00000000
+0,        977,        977,        1,  1382400, 0x00000000
+0,        978,        978,        1,  1382400, 0x00000000
+0,        979,        979,        1,  1382400, 0x00000000
+0,        980,        980,        1,  1382400, 0x00000000
+0,        981,        981,        1,  1382400, 0x00000000
+0,        982,        982,        1,  1382400, 0x00000000
+0,        983,        983,        1,  1382400, 0x00000000
+0,        984,        984,        1,  1382400, 0x00000000
+0,        985,        985,        1,  1382400, 0x00000000
+0,        986,        986,        1,  1382400, 0x00000000
+0,        987,        987,        1,  1382400, 0x00000000
+0,        988,        988,        1,  1382400, 0x00000000
+0,        989,        989,        1,  1382400, 0x00000000
+0,        990,        990,        1,  1382400, 0x00000000
+0,        991,        991,        1,  1382400, 0x00000000
+0,        992,        992,        1,  1382400, 0x00000000
+0,        993,        993,        1,  1382400, 0x00000000
+0,        994,        994,        1,  1382400, 0x00000000
+0,        995,        995,        1,  1382400, 0x00000000
+0,        996,        996,        1,  1382400, 0x00000000
+0,        997,        997,        1,  1382400, 0x00000000
+0,        999,        999,        1,  1382400, 0x1b26389a
+0,        999,        999,        1,  1382400, 0x1b26389a
+0,       1000,       1000,        1,  1382400, 0x1b26389a
+0,       1001,       1001,        1,  1382400, 0x1b26389a
+0,       1002,       1002,        1,  1382400, 0x1b26389a
+0,       1003,       1003,        1,  1382400, 0x1b26389a
+0,       1004,       1004,        1,  1382400, 0x1b26389a
+0,       1006,       1006,        1,  1382400, 0xf0c7028b
+0,       1006,       1006,        1,  1382400, 0xf0c7028b
+0,       1007,       1007,        1,  1382400, 0xf0c7028b
+0,       1008,       1008,        1,  1382400, 0xf0c7028b
+0,       1009,       1009,        1,  1382400, 0xf0c7028b
+0,       1010,       1010,        1,  1382400, 0xf0c7028b
+0,       1011,       1011,        1,  1382400, 0xf0c7028b
+0,       1012,       1012,        1,  1382400, 0xf0c7028b
+0,       1013,       1013,        1,  1382400, 0xf0c7028b
+0,       1014,       1014,        1,  1382400, 0x395f521d
+0,       1015,       1015,        1,  1382400, 0x395f521d
+0,       1016,       1016,        1,  1382400, 0x395f521d
+0,       1017,       1017,        1,  1382400, 0x395f521d
+0,       1018,       1018,        1,  1382400, 0x395f521d
+0,       1019,       1019,        1,  1382400, 0x395f521d
+0,       1020,       1020,        1,  1382400, 0x395f521d
+0,       1021,       1021,        1,  1382400, 0x395f521d
+0,       1022,       1022,        1,  1382400, 0x395f521d
+0,       1024,       1024,        1,  1382400, 0x1ea87415
+0,       1024,       1024,        1,  1382400, 0x1ea87415
+0,       1025,       1025,        1,  1382400, 0x1ea87415
+0,       1026,       1026,        1,  1382400, 0x1ea87415
+0,       1027,       1027,        1,  1382400, 0x1ea87415
+0,       1028,       1028,        1,  1382400, 0x1ea87415
+0,       1029,       1029,        1,  1382400, 0x1ea87415
+0,       1030,       1030,        1,  1382400, 0x1ea87415
+0,       1031,       1031,        1,  1382400, 0x1ea87415
+0,       1032,       1032,        1,  1382400, 0x1ea87415
+0,       1033,       1033,        1,  1382400, 0x1ea87415
+0,       1034,       1034,        1,  1382400, 0xc6effdc1
+0,       1035,       1035,        1,  1382400, 0xc6effdc1
+0,       1036,       1036,        1,  1382400, 0xc6effdc1
+0,       1037,       1037,        1,  1382400, 0xc6effdc1
+0,       1038,       1038,        1,  1382400, 0xc6effdc1
+0,       1039,       1039,        1,  1382400, 0xc6effdc1
+0,       1040,       1040,        1,  1382400, 0xc6effdc1
+0,       1041,       1041,        1,  1382400, 0xc6effdc1
+0,       1042,       1042,        1,  1382400, 0xc6effdc1
+0,       1044,       1044,        1,  1382400, 0xba6846f8
+0,       1044,       1044,        1,  1382400, 0xba6846f8
+0,       1045,       1045,        1,  1382400, 0xba6846f8
+0,       1046,       1046,        1,  1382400, 0xba6846f8
+0,       1047,       1047,        1,  1382400, 0xba6846f8
+0,       1048,       1048,        1,  1382400, 0xba6846f8
+0,       1049,       1049,        1,  1382400, 0xba6846f8
+0,       1050,       1050,        1,  1382400, 0x033c5d5b
+0,       1051,       1051,        1,  1382400, 0x033c5d5b
+0,       1052,       1052,        1,  1382400, 0x033c5d5b
+0,       1053,       1053,        1,  1382400, 0x033c5d5b
+0,       1054,       1054,        1,  1382400, 0x033c5d5b
+0,       1055,       1055,        1,  1382400, 0x033c5d5b
+0,       1056,       1056,        1,  1382400, 0x033c5d5b
+0,       1057,       1057,        1,  1382400, 0x033c5d5b
+0,       1058,       1058,        1,  1382400, 0x00000000
+0,       1059,       1059,        1,  1382400, 0xef5abf66
+0,       1060,       1060,        1,  1382400, 0xef5abf66
+0,       1061,       1061,        1,  1382400, 0xef5abf66
+0,       1062,       1062,        1,  1382400, 0xef5abf66
+0,       1063,       1063,        1,  1382400, 0xef5abf66
+0,       1064,       1064,        1,  1382400, 0xef5abf66
+0,       1065,       1065,        1,  1382400, 0xef5abf66
+0,       1066,       1066,        1,  1382400, 0xef5abf66
+0,       1067,       1067,        1,  1382400, 0xef5abf66
+0,       1068,       1068,        1,  1382400, 0xef5abf66
+0,       1069,       1069,        1,  1382400, 0xef5abf66
+0,       1070,       1070,        1,  1382400, 0xef5abf66
+0,       1071,       1071,        1,  1382400, 0xef5abf66
+0,       1072,       1072,        1,  1382400, 0x00000000
+0,       1073,       1073,        1,  1382400, 0xec747954
+0,       1074,       1074,        1,  1382400, 0xec747954
+0,       1075,       1075,        1,  1382400, 0xec747954
+0,       1076,       1076,        1,  1382400, 0xec747954
+0,       1077,       1077,        1,  1382400, 0xec747954
+0,       1078,       1078,        1,  1382400, 0xec747954
+0,       1079,       1079,        1,  1382400, 0xec747954
+0,       1080,       1080,        1,  1382400, 0xec747954
+0,       1081,       1081,        1,  1382400, 0xec747954
+0,       1082,       1082,        1,  1382400, 0xec747954
+0,       1083,       1083,        1,  1382400, 0xec747954
+0,       1084,       1084,        1,  1382400, 0xec747954
+0,       1085,       1085,        1,  1382400, 0xec747954
+0,       1086,       1086,        1,  1382400, 0x00000000
+0,       1087,       1087,        1,  1382400, 0xfa34bcaf
+0,       1088,       1088,        1,  1382400, 0xfa34bcaf
+0,       1089,       1089,        1,  1382400, 0xfa34bcaf
+0,       1090,       1090,        1,  1382400, 0xfa34bcaf
+0,       1091,       1091,        1,  1382400, 0xfa34bcaf
+0,       1092,       1092,        1,  1382400, 0xfa34bcaf
+0,       1093,       1093,        1,  1382400, 0xfa34bcaf
+0,       1094,       1094,        1,  1382400, 0xfa34bcaf
+0,       1095,       1095,        1,  1382400, 0xfa34bcaf
+0,       1096,       1096,        1,  1382400, 0xfa34bcaf
+0,       1097,       1097,        1,  1382400, 0xfa34bcaf
+0,       1098,       1098,        1,  1382400, 0x00000000
+0,       1099,       1099,        1,  1382400, 0x8b7a709b
+0,       1100,       1100,        1,  1382400, 0x8b7a709b
+0,       1101,       1101,        1,  1382400, 0x8b7a709b
+0,       1102,       1102,        1,  1382400, 0x8b7a709b
+0,       1103,       1103,        1,  1382400, 0x8b7a709b
+0,       1104,       1104,        1,  1382400, 0x8b7a709b
+0,       1105,       1105,        1,  1382400, 0x8b7a709b
+0,       1106,       1106,        1,  1382400, 0x8b7a709b
+0,       1107,       1107,        1,  1382400, 0x00000000
+0,       1108,       1108,        1,  1382400, 0x00000000
+0,       1109,       1109,        1,  1382400, 0x00000000
+0,       1110,       1110,        1,  1382400, 0x00000000
+0,       1111,       1111,        1,  1382400, 0x00000000
+0,       1112,       1112,        1,  1382400, 0x00000000
+0,       1113,       1113,        1,  1382400, 0x00000000
+0,       1114,       1114,        1,  1382400, 0x00000000
+0,       1115,       1115,        1,  1382400, 0x00000000
+0,       1116,       1116,        1,  1382400, 0x00000000
+0,       1118,       1118,        1,  1382400, 0xc333382f
+0,       1118,       1118,        1,  1382400, 0xc333382f
+0,       1119,       1119,        1,  1382400, 0xc333382f
+0,       1120,       1120,        1,  1382400, 0xc333382f
+0,       1121,       1121,        1,  1382400, 0xc333382f
+0,       1122,       1122,        1,  1382400, 0xc333382f
+0,       1123,       1123,        1,  1382400, 0xc333382f
+0,       1124,       1124,        1,  1382400, 0xc333382f
+0,       1125,       1125,        1,  1382400, 0xc333382f
+0,       1126,       1126,        1,  1382400, 0xc333382f
+0,       1127,       1127,        1,  1382400, 0xc333382f
+0,       1128,       1128,        1,  1382400, 0xc333382f
+0,       1129,       1129,        1,  1382400, 0x00000000
+0,       1130,       1130,        1,  1382400, 0x00000000
+0,       1131,       1131,        1,  1382400, 0x00000000
+0,       1132,       1132,        1,  1382400, 0x00000000
+0,       1133,       1133,        1,  1382400, 0x00000000
+0,       1134,       1134,        1,  1382400, 0x00000000
+0,       1135,       1135,        1,  1382400, 0x00000000
+0,       1136,       1136,        1,  1382400, 0x00000000
+0,       1138,       1138,        1,  1382400, 0xabe5dfcf
+0,       1138,       1138,        1,  1382400, 0xabe5dfcf
+0,       1139,       1139,        1,  1382400, 0xabe5dfcf
+0,       1140,       1140,        1,  1382400, 0xabe5dfcf
+0,       1141,       1141,        1,  1382400, 0xabe5dfcf
+0,       1142,       1142,        1,  1382400, 0xabe5dfcf
+0,       1143,       1143,        1,  1382400, 0xabe5dfcf
+0,       1144,       1144,        1,  1382400, 0xabe5dfcf
+0,       1145,       1145,        1,  1382400, 0xabe5dfcf
+0,       1146,       1146,        1,  1382400, 0xabe5dfcf
+0,       1147,       1147,        1,  1382400, 0xabe5dfcf
+0,       1148,       1148,        1,  1382400, 0xabe5dfcf
+0,       1149,       1149,        1,  1382400, 0xabe5dfcf
+0,       1150,       1150,        1,  1382400, 0xabe5dfcf
+0,       1151,       1151,        1,  1382400, 0xabe5dfcf
+0,       1152,       1152,        1,  1382400, 0xabe5dfcf
+0,       1153,       1153,        1,  1382400, 0xabe5dfcf
+0,       1154,       1154,        1,  1382400, 0x56948101
+0,       1155,       1155,        1,  1382400, 0x56948101
+0,       1156,       1156,        1,  1382400, 0x56948101
+0,       1157,       1157,        1,  1382400, 0x56948101
+0,       1158,       1158,        1,  1382400, 0x56948101
+0,       1159,       1159,        1,  1382400, 0x56948101
+0,       1160,       1160,        1,  1382400, 0x56948101
+0,       1161,       1161,        1,  1382400, 0x56948101
+0,       1162,       1162,        1,  1382400, 0x56948101
+0,       1163,       1163,        1,  1382400, 0x56948101
+0,       1164,       1164,        1,  1382400, 0x56948101
+0,       1166,       1166,        1,  1382400, 0xb747834a
+0,       1166,       1166,        1,  1382400, 0xb747834a
+0,       1167,       1167,        1,  1382400, 0xb747834a
+0,       1168,       1168,        1,  1382400, 0xb747834a
+0,       1169,       1169,        1,  1382400, 0xb747834a
+0,       1170,       1170,        1,  1382400, 0xb747834a
+0,       1171,       1171,        1,  1382400, 0xb747834a
+0,       1172,       1172,        1,  1382400, 0xb747834a
+0,       1173,       1173,        1,  1382400, 0xb747834a
+0,       1174,       1174,        1,  1382400, 0xb747834a
+0,       1175,       1175,        1,  1382400, 0xb747834a
+0,       1176,       1176,        1,  1382400, 0xb747834a
+0,       1177,       1177,        1,  1382400, 0xb747834a
+0,       1178,       1178,        1,  1382400, 0xb747834a
+0,       1179,       1179,        1,  1382400, 0xb747834a
+0,       1180,       1180,        1,  1382400, 0xb747834a
+0,       1181,       1181,        1,  1382400, 0xb747834a
+0,       1182,       1182,        1,  1382400, 0xb747834a
+0,       1183,       1183,        1,  1382400, 0xb747834a
+0,       1184,       1184,        1,  1382400, 0xb747834a
+0,       1185,       1185,        1,  1382400, 0xb747834a
+0,       1187,       1187,        1,  1382400, 0x3448baad
+0,       1187,       1187,        1,  1382400, 0x3448baad
+0,       1188,       1188,        1,  1382400, 0x3448baad
+0,       1189,       1189,        1,  1382400, 0x3448baad
+0,       1190,       1190,        1,  1382400, 0x3448baad
+0,       1191,       1191,        1,  1382400, 0x3448baad
+0,       1192,       1192,        1,  1382400, 0x3448baad
+0,       1193,       1193,        1,  1382400, 0x3448baad
+0,       1194,       1194,        1,  1382400, 0x3448baad
+0,       1195,       1195,        1,  1382400, 0x3448baad
+0,       1196,       1196,        1,  1382400, 0x3448baad
+0,       1197,       1197,        1,  1382400, 0x3448baad
+0,       1198,       1198,        1,  1382400, 0x3448baad
+0,       1199,       1199,        1,  1382400, 0x3448baad
+0,       1200,       1200,        1,  1382400, 0x00000000
+0,       1201,       1201,        1,  1382400, 0xaabe4f37
+0,       1202,       1202,        1,  1382400, 0xaabe4f37
+0,       1203,       1203,        1,  1382400, 0xaabe4f37
+0,       1204,       1204,        1,  1382400, 0xaabe4f37
+0,       1205,       1205,        1,  1382400, 0xaabe4f37
+0,       1206,       1206,        1,  1382400, 0xaabe4f37
+0,       1207,       1207,        1,  1382400, 0xaabe4f37
+0,       1208,       1208,        1,  1382400, 0xaabe4f37
+0,       1209,       1209,        1,  1382400, 0xaabe4f37
+0,       1210,       1210,        1,  1382400, 0xaabe4f37
+0,       1211,       1211,        1,  1382400, 0x00000000
+0,       1212,       1212,        1,  1382400, 0x00000000
+0,       1213,       1213,        1,  1382400, 0x00000000
+0,       1214,       1214,        1,  1382400, 0x00000000
+0,       1215,       1215,        1,  1382400, 0x00000000
+0,       1216,       1216,        1,  1382400, 0x00000000
+0,       1217,       1217,        1,  1382400, 0x00000000
+0,       1218,       1218,        1,  1382400, 0x00000000
+0,       1219,       1219,        1,  1382400, 0x00000000
+0,       1220,       1220,        1,  1382400, 0x00000000
+0,       1221,       1221,        1,  1382400, 0x00000000
+0,       1222,       1222,        1,  1382400, 0x00000000
+0,       1223,       1223,        1,  1382400, 0x00000000
+0,       1224,       1224,        1,  1382400, 0x00000000
+0,       1225,       1225,        1,  1382400, 0x00000000
+0,       1226,       1226,        1,  1382400, 0x00000000
+0,       1227,       1227,        1,  1382400, 0x00000000
+0,       1228,       1228,        1,  1382400, 0x00000000
+0,       1229,       1229,        1,  1382400, 0x00000000
+0,       1230,       1230,        1,  1382400, 0x00000000
+0,       1231,       1231,        1,  1382400, 0x00000000
+0,       1232,       1232,        1,  1382400, 0x00000000
+0,       1233,       1233,        1,  1382400, 0x00000000
+0,       1234,       1234,        1,  1382400, 0x00000000
+0,       1235,       1235,        1,  1382400, 0x00000000
+0,       1236,       1236,        1,  1382400, 0x00000000
+0,       1237,       1237,        1,  1382400, 0x00000000
+0,       1238,       1238,        1,  1382400, 0x00000000
+0,       1239,       1239,        1,  1382400, 0x00000000
+0,       1240,       1240,        1,  1382400, 0x00000000
+0,       1241,       1241,        1,  1382400, 0x00000000
+0,       1242,       1242,        1,  1382400, 0x00000000
+0,       1243,       1243,        1,  1382400, 0x00000000
+0,       1244,       1244,        1,  1382400, 0x00000000
+0,       1245,       1245,        1,  1382400, 0x00000000
+0,       1246,       1246,        1,  1382400, 0x00000000
+0,       1247,       1247,        1,  1382400, 0x00000000
+0,       1248,       1248,        1,  1382400, 0x00000000
+0,       1249,       1249,        1,  1382400, 0x00000000
+0,       1250,       1250,        1,  1382400, 0x00000000
+0,       1251,       1251,        1,  1382400, 0x00000000
+0,       1252,       1252,        1,  1382400, 0x00000000
+0,       1253,       1253,        1,  1382400, 0x00000000
+0,       1254,       1254,        1,  1382400, 0x00000000
+0,       1255,       1255,        1,  1382400, 0x00000000
+0,       1256,       1256,        1,  1382400, 0x00000000
+0,       1257,       1257,        1,  1382400, 0x00000000
+0,       1258,       1258,        1,  1382400, 0x00000000
+0,       1259,       1259,        1,  1382400, 0x00000000
+0,       1260,       1260,        1,  1382400, 0x00000000
+0,       1261,       1261,        1,  1382400, 0x00000000
+0,       1262,       1262,        1,  1382400, 0x00000000
+0,       1263,       1263,        1,  1382400, 0x00000000
+0,       1264,       1264,        1,  1382400, 0x00000000
+0,       1265,       1265,        1,  1382400, 0x00000000
+0,       1266,       1266,        1,  1382400, 0x00000000
+0,       1267,       1267,        1,  1382400, 0x00000000
+0,       1268,       1268,        1,  1382400, 0x00000000
+0,       1269,       1269,        1,  1382400, 0x00000000
+0,       1270,       1270,        1,  1382400, 0x00000000
+0,       1271,       1271,        1,  1382400, 0x00000000
+0,       1272,       1272,        1,  1382400, 0x00000000
+0,       1273,       1273,        1,  1382400, 0x00000000
+0,       1274,       1274,        1,  1382400, 0x00000000
+0,       1275,       1275,        1,  1382400, 0x00000000
+0,       1276,       1276,        1,  1382400, 0x00000000
+0,       1277,       1277,        1,  1382400, 0x00000000
+0,       1278,       1278,        1,  1382400, 0x00000000
+0,       1279,       1279,        1,  1382400, 0x00000000
+0,       1280,       1280,        1,  1382400, 0x00000000
+0,       1281,       1281,        1,  1382400, 0x00000000
+0,       1282,       1282,        1,  1382400, 0x00000000
+0,       1283,       1283,        1,  1382400, 0x00000000
+0,       1284,       1284,        1,  1382400, 0x00000000
+0,       1285,       1285,        1,  1382400, 0x00000000
+0,       1286,       1286,        1,  1382400, 0x00000000
+0,       1287,       1287,        1,  1382400, 0x00000000
+0,       1288,       1288,        1,  1382400, 0x00000000
+0,       1289,       1289,        1,  1382400, 0x00000000
+0,       1290,       1290,        1,  1382400, 0x00000000
+0,       1291,       1291,        1,  1382400, 0x00000000
+0,       1292,       1292,        1,  1382400, 0x00000000
+0,       1293,       1293,        1,  1382400, 0x00000000
+0,       1294,       1294,        1,  1382400, 0x00000000
+0,       1295,       1295,        1,  1382400, 0x00000000
+0,       1296,       1296,        1,  1382400, 0x00000000
+0,       1297,       1297,        1,  1382400, 0x00000000
+0,       1298,       1298,        1,  1382400, 0x00000000
+0,       1299,       1299,        1,  1382400, 0x00000000
+0,       1300,       1300,        1,  1382400, 0x00000000
+0,       1301,       1301,        1,  1382400, 0x00000000
+0,       1302,       1302,        1,  1382400, 0x00000000
+0,       1303,       1303,        1,  1382400, 0x00000000
+0,       1304,       1304,        1,  1382400, 0x00000000
+0,       1305,       1305,        1,  1382400, 0x00000000
+0,       1306,       1306,        1,  1382400, 0x00000000
+0,       1307,       1307,        1,  1382400, 0x00000000
+0,       1308,       1308,        1,  1382400, 0x00000000
+0,       1309,       1309,        1,  1382400, 0x00000000
+0,       1310,       1310,        1,  1382400, 0x00000000
+0,       1311,       1311,        1,  1382400, 0x00000000
+0,       1312,       1312,        1,  1382400, 0x00000000
+0,       1313,       1313,        1,  1382400, 0x00000000
+0,       1314,       1314,        1,  1382400, 0x00000000
+0,       1315,       1315,        1,  1382400, 0x00000000
+0,       1316,       1316,        1,  1382400, 0x00000000
+0,       1317,       1317,        1,  1382400, 0x00000000
+0,       1318,       1318,        1,  1382400, 0x00000000
+0,       1319,       1319,        1,  1382400, 0x00000000
+0,       1320,       1320,        1,  1382400, 0x00000000
+0,       1321,       1321,        1,  1382400, 0x00000000
+0,       1322,       1322,        1,  1382400, 0x00000000
+0,       1323,       1323,        1,  1382400, 0x00000000
+0,       1324,       1324,        1,  1382400, 0x00000000
+0,       1325,       1325,        1,  1382400, 0x00000000
+0,       1326,       1326,        1,  1382400, 0x00000000
+0,       1327,       1327,        1,  1382400, 0x00000000
+0,       1328,       1328,        1,  1382400, 0x00000000
+0,       1329,       1329,        1,  1382400, 0x00000000
+0,       1330,       1330,        1,  1382400, 0x00000000
+0,       1331,       1331,        1,  1382400, 0x00000000
+0,       1332,       1332,        1,  1382400, 0x00000000
+0,       1333,       1333,        1,  1382400, 0x00000000
+0,       1334,       1334,        1,  1382400, 0x00000000
+0,       1335,       1335,        1,  1382400, 0x00000000
+0,       1336,       1336,        1,  1382400, 0x00000000
+0,       1337,       1337,        1,  1382400, 0x00000000
+0,       1338,       1338,        1,  1382400, 0x00000000
+0,       1339,       1339,        1,  1382400, 0x00000000
+0,       1340,       1340,        1,  1382400, 0x00000000
+0,       1341,       1341,        1,  1382400, 0x00000000
+0,       1342,       1342,        1,  1382400, 0x00000000
+0,       1343,       1343,        1,  1382400, 0x00000000
+0,       1344,       1344,        1,  1382400, 0x00000000
+0,       1345,       1345,        1,  1382400, 0x00000000
+0,       1346,       1346,        1,  1382400, 0x00000000
+0,       1347,       1347,        1,  1382400, 0x00000000
+0,       1348,       1348,        1,  1382400, 0x00000000
+0,       1349,       1349,        1,  1382400, 0x00000000
+0,       1350,       1350,        1,  1382400, 0x00000000
+0,       1351,       1351,        1,  1382400, 0x00000000
+0,       1352,       1352,        1,  1382400, 0x00000000
+0,       1353,       1353,        1,  1382400, 0x00000000
+0,       1354,       1354,        1,  1382400, 0x00000000
+0,       1355,       1355,        1,  1382400, 0x00000000
+0,       1356,       1356,        1,  1382400, 0x00000000
+0,       1357,       1357,        1,  1382400, 0x00000000
+0,       1358,       1358,        1,  1382400, 0x00000000
+0,       1359,       1359,        1,  1382400, 0x00000000
+0,       1360,       1360,        1,  1382400, 0x00000000
+0,       1361,       1361,        1,  1382400, 0x00000000
+0,       1362,       1362,        1,  1382400, 0x00000000
+0,       1363,       1363,        1,  1382400, 0x00000000
+0,       1364,       1364,        1,  1382400, 0x00000000
+0,       1365,       1365,        1,  1382400, 0x00000000
+0,       1366,       1366,        1,  1382400, 0x00000000
+0,       1368,       1368,        1,  1382400, 0x8a48cd6f
+0,       1368,       1368,        1,  1382400, 0x8a48cd6f
+0,       1369,       1369,        1,  1382400, 0x8a48cd6f
+0,       1370,       1370,        1,  1382400, 0x8a48cd6f
+0,       1371,       1371,        1,  1382400, 0x8a48cd6f
+0,       1372,       1372,        1,  1382400, 0x8a48cd6f
+0,       1373,       1373,        1,  1382400, 0x8a48cd6f
+0,       1374,       1374,        1,  1382400, 0x8a48cd6f
+0,       1375,       1375,        1,  1382400, 0x8a48cd6f
+0,       1376,       1376,        1,  1382400, 0x00000000
+0,       1377,       1377,        1,  1382400, 0x00000000
+0,       1378,       1378,        1,  1382400, 0x00000000
+0,       1379,       1379,        1,  1382400, 0x00000000
+0,       1380,       1380,        1,  1382400, 0x00000000
+0,       1381,       1381,        1,  1382400, 0x00000000
+0,       1382,       1382,        1,  1382400, 0x00000000
+0,       1383,       1383,        1,  1382400, 0x00000000
+0,       1384,       1384,        1,  1382400, 0x00000000
+0,       1385,       1385,        1,  1382400, 0x00000000
+0,       1386,       1386,        1,  1382400, 0x00000000
+0,       1387,       1387,        1,  1382400, 0x00000000
+0,       1388,       1388,        1,  1382400, 0x00000000
+0,       1389,       1389,        1,  1382400, 0x00000000
+0,       1390,       1390,        1,  1382400, 0x00000000
+0,       1391,       1391,        1,  1382400, 0x00000000
+0,       1392,       1392,        1,  1382400, 0x00000000
+0,       1393,       1393,        1,  1382400, 0x00000000
+0,       1394,       1394,        1,  1382400, 0x00000000
+0,       1395,       1395,        1,  1382400, 0x00000000
+0,       1396,       1396,        1,  1382400, 0x00000000
+0,       1397,       1397,        1,  1382400, 0x00000000
+0,       1398,       1398,        1,  1382400, 0x00000000
+0,       1399,       1399,        1,  1382400, 0x00000000
+0,       1400,       1400,        1,  1382400, 0x00000000
+0,       1401,       1401,        1,  1382400, 0x00000000
+0,       1402,       1402,        1,  1382400, 0x00000000
+0,       1403,       1403,        1,  1382400, 0x00000000
+0,       1404,       1404,        1,  1382400, 0x00000000
+0,       1405,       1405,        1,  1382400, 0x00000000
+0,       1406,       1406,        1,  1382400, 0x00000000
+0,       1407,       1407,        1,  1382400, 0x00000000
+0,       1408,       1408,        1,  1382400, 0x00000000
+0,       1409,       1409,        1,  1382400, 0x00000000
+0,       1410,       1410,        1,  1382400, 0x00000000
+0,       1411,       1411,        1,  1382400, 0x00000000
+0,       1412,       1412,        1,  1382400, 0x00000000
+0,       1413,       1413,        1,  1382400, 0x00000000
+0,       1414,       1414,        1,  1382400, 0x00000000
+0,       1415,       1415,        1,  1382400, 0x00000000
+0,       1416,       1416,        1,  1382400, 0x00000000
+0,       1417,       1417,        1,  1382400, 0x00000000
+0,       1418,       1418,        1,  1382400, 0x00000000
+0,       1419,       1419,        1,  1382400, 0x00000000
+0,       1420,       1420,        1,  1382400, 0x00000000
+0,       1421,       1421,        1,  1382400, 0x00000000
+0,       1422,       1422,        1,  1382400, 0x00000000
+0,       1423,       1423,        1,  1382400, 0x00000000
+0,       1424,       1424,        1,  1382400, 0x00000000
+0,       1425,       1425,        1,  1382400, 0x00000000
+0,       1426,       1426,        1,  1382400, 0x00000000
+0,       1427,       1427,        1,  1382400, 0x00000000
+0,       1428,       1428,        1,  1382400, 0x00000000
+0,       1429,       1429,        1,  1382400, 0x00000000
+0,       1430,       1430,        1,  1382400, 0x00000000
+0,       1431,       1431,        1,  1382400, 0x00000000
+0,       1432,       1432,        1,  1382400, 0x00000000
+0,       1433,       1433,        1,  1382400, 0x00000000
+0,       1434,       1434,        1,  1382400, 0x00000000
+0,       1435,       1435,        1,  1382400, 0x00000000
+0,       1436,       1436,        1,  1382400, 0x00000000
+0,       1437,       1437,        1,  1382400, 0x00000000
+0,       1438,       1438,        1,  1382400, 0x00000000
+0,       1439,       1439,        1,  1382400, 0x00000000
+0,       1440,       1440,        1,  1382400, 0x00000000
+0,       1441,       1441,        1,  1382400, 0x00000000
+0,       1442,       1442,        1,  1382400, 0x00000000
+0,       1443,       1443,        1,  1382400, 0x00000000
+0,       1444,       1444,        1,  1382400, 0x00000000
+0,       1445,       1445,        1,  1382400, 0x00000000
+0,       1446,       1446,        1,  1382400, 0x00000000
+0,       1447,       1447,        1,  1382400, 0x00000000
+0,       1448,       1448,        1,  1382400, 0x00000000
+0,       1449,       1449,        1,  1382400, 0x00000000
+0,       1450,       1450,        1,  1382400, 0x00000000
+0,       1451,       1451,        1,  1382400, 0x00000000
+0,       1452,       1452,        1,  1382400, 0x00000000
+0,       1453,       1453,        1,  1382400, 0x00000000
+0,       1454,       1454,        1,  1382400, 0x00000000
+0,       1455,       1455,        1,  1382400, 0x00000000
+0,       1456,       1456,        1,  1382400, 0x00000000
+0,       1457,       1457,        1,  1382400, 0x00000000
+0,       1458,       1458,        1,  1382400, 0x00000000
+0,       1459,       1459,        1,  1382400, 0x00000000
+0,       1460,       1460,        1,  1382400, 0x00000000
+0,       1461,       1461,        1,  1382400, 0x00000000
+0,       1462,       1462,        1,  1382400, 0x00000000
+0,       1463,       1463,        1,  1382400, 0x00000000
+0,       1464,       1464,        1,  1382400, 0x00000000
+0,       1465,       1465,        1,  1382400, 0x00000000
+0,       1466,       1466,        1,  1382400, 0x00000000
+0,       1467,       1467,        1,  1382400, 0x00000000
+0,       1468,       1468,        1,  1382400, 0x00000000
+0,       1469,       1469,        1,  1382400, 0x00000000
+0,       1470,       1470,        1,  1382400, 0x00000000
+0,       1471,       1471,        1,  1382400, 0x00000000
+0,       1472,       1472,        1,  1382400, 0x00000000
+0,       1473,       1473,        1,  1382400, 0x00000000
+0,       1474,       1474,        1,  1382400, 0x00000000
+0,       1475,       1475,        1,  1382400, 0x00000000
+0,       1477,       1477,        1,  1382400, 0x49518c43
+0,       1477,       1477,        1,  1382400, 0x49518c43
+0,       1478,       1478,        1,  1382400, 0x49518c43
+0,       1479,       1479,        1,  1382400, 0x49518c43
+0,       1480,       1480,        1,  1382400, 0x49518c43
+0,       1481,       1481,        1,  1382400, 0x49518c43
+0,       1482,       1482,        1,  1382400, 0x49518c43
+0,       1483,       1483,        1,  1382400, 0x49518c43
+0,       1484,       1484,        1,  1382400, 0x00000000
+0,       1485,       1485,        1,  1382400, 0x00000000
+0,       1486,       1486,        1,  1382400, 0x00000000
+0,       1487,       1487,        1,  1382400, 0x00000000
+0,       1488,       1488,        1,  1382400, 0x00000000
+0,       1489,       1489,        1,  1382400, 0x00000000
+0,       1490,       1490,        1,  1382400, 0x00000000
+0,       1491,       1491,        1,  1382400, 0x00000000
+0,       1492,       1492,        1,  1382400, 0x00000000
+0,       1493,       1493,        1,  1382400, 0x00000000
+0,       1494,       1494,        1,  1382400, 0x00000000
+0,       1495,       1495,        1,  1382400, 0x00000000
+0,       1496,       1496,        1,  1382400, 0x00000000
+0,       1497,       1497,        1,  1382400, 0x00000000
+0,       1498,       1498,        1,  1382400, 0x00000000
+0,       1500,       1500,        1,  1382400, 0x4a72fa21
+0,       1500,       1500,        1,  1382400, 0x4a72fa21
+0,       1501,       1501,        1,  1382400, 0x4a72fa21
+0,       1502,       1502,        1,  1382400, 0x4a72fa21
+0,       1503,       1503,        1,  1382400, 0x4a72fa21
+0,       1504,       1504,        1,  1382400, 0x4a72fa21
+0,       1505,       1505,        1,  1382400, 0x4a72fa21
+0,       1506,       1506,        1,  1382400, 0x4a72fa21
+0,       1507,       1507,        1,  1382400, 0x4a72fa21
+0,       1508,       1508,        1,  1382400, 0x4a72fa21
+0,       1509,       1509,        1,  1382400, 0x4a72fa21
+0,       1510,       1510,        1,  1382400, 0x00000000
+0,       1511,       1511,        1,  1382400, 0xa82f7de8
+0,       1512,       1512,        1,  1382400, 0xa82f7de8
+0,       1513,       1513,        1,  1382400, 0xa82f7de8
+0,       1514,       1514,        1,  1382400, 0xa82f7de8
+0,       1515,       1515,        1,  1382400, 0xa82f7de8
+0,       1516,       1516,        1,  1382400, 0xa82f7de8
+0,       1517,       1517,        1,  1382400, 0xa82f7de8
+0,       1518,       1518,        1,  1382400, 0x00000000
+0,       1519,       1519,        1,  1382400, 0x00000000
+0,       1521,       1521,        1,  1382400, 0xeba0b5f3
+0,       1521,       1521,        1,  1382400, 0xeba0b5f3
+0,       1522,       1522,        1,  1382400, 0xeba0b5f3
+0,       1523,       1523,        1,  1382400, 0xeba0b5f3
+0,       1524,       1524,        1,  1382400, 0xeba0b5f3
+0,       1525,       1525,        1,  1382400, 0xeba0b5f3
+0,       1526,       1526,        1,  1382400, 0xeba0b5f3
+0,       1527,       1527,        1,  1382400, 0xeba0b5f3
+0,       1528,       1528,        1,  1382400, 0xeba0b5f3
+0,       1530,       1530,        1,  1382400, 0xd6a91770
+0,       1530,       1530,        1,  1382400, 0xd6a91770
+0,       1531,       1531,        1,  1382400, 0xd6a91770
+0,       1532,       1532,        1,  1382400, 0xd6a91770
+0,       1533,       1533,        1,  1382400, 0xd6a91770
+0,       1534,       1534,        1,  1382400, 0xd6a91770
+0,       1535,       1535,        1,  1382400, 0xd6a91770
+0,       1536,       1536,        1,  1382400, 0xd6a91770
+0,       1537,       1537,        1,  1382400, 0xd6a91770
+0,       1538,       1538,        1,  1382400, 0xd6a91770
+0,       1539,       1539,        1,  1382400, 0x00000000
+0,       1540,       1540,        1,  1382400, 0x222f827c
+0,       1541,       1541,        1,  1382400, 0x222f827c
+0,       1542,       1542,        1,  1382400, 0x222f827c
+0,       1543,       1543,        1,  1382400, 0x222f827c
+0,       1544,       1544,        1,  1382400, 0x222f827c
+0,       1545,       1545,        1,  1382400, 0x222f827c
+0,       1546,       1546,        1,  1382400, 0x222f827c
+0,       1547,       1547,        1,  1382400, 0x222f827c
+0,       1548,       1548,        1,  1382400, 0x222f827c
+0,       1549,       1549,        1,  1382400, 0x00000000
+0,       1550,       1550,        1,  1382400, 0x00000000
+0,       1551,       1551,        1,  1382400, 0x00000000
+0,       1552,       1552,        1,  1382400, 0x00000000
+0,       1553,       1553,        1,  1382400, 0x00000000
+0,       1554,       1554,        1,  1382400, 0x00000000
+0,       1555,       1555,        1,  1382400, 0x00000000
+0,       1556,       1556,        1,  1382400, 0x00000000
+0,       1557,       1557,        1,  1382400, 0x00000000
+0,       1558,       1558,        1,  1382400, 0x00000000
+0,       1559,       1559,        1,  1382400, 0x00000000
+0,       1560,       1560,        1,  1382400, 0x00000000
+0,       1561,       1561,        1,  1382400, 0x00000000
+0,       1562,       1562,        1,  1382400, 0x00000000
+0,       1563,       1563,        1,  1382400, 0x00000000
+0,       1564,       1564,        1,  1382400, 0x00000000
+0,       1565,       1565,        1,  1382400, 0x00000000
+0,       1566,       1566,        1,  1382400, 0x00000000
+0,       1567,       1567,        1,  1382400, 0x00000000
+0,       1568,       1568,        1,  1382400, 0x00000000
+0,       1569,       1569,        1,  1382400, 0x00000000
+0,       1570,       1570,        1,  1382400, 0x00000000
+0,       1571,       1571,        1,  1382400, 0x00000000
+0,       1572,       1572,        1,  1382400, 0x00000000
+0,       1573,       1573,        1,  1382400, 0x00000000
+0,       1574,       1574,        1,  1382400, 0x00000000
+0,       1575,       1575,        1,  1382400, 0x00000000
+0,       1576,       1576,        1,  1382400, 0x00000000
+0,       1577,       1577,        1,  1382400, 0x00000000
+0,       1578,       1578,        1,  1382400, 0x00000000
+0,       1579,       1579,        1,  1382400, 0x00000000
+0,       1580,       1580,        1,  1382400, 0x00000000
+0,       1581,       1581,        1,  1382400, 0x00000000
+0,       1582,       1582,        1,  1382400, 0x00000000
+0,       1583,       1583,        1,  1382400, 0x00000000
+0,       1584,       1584,        1,  1382400, 0x00000000
+0,       1585,       1585,        1,  1382400, 0x00000000
+0,       1586,       1586,        1,  1382400, 0x00000000
+0,       1587,       1587,        1,  1382400, 0x00000000
+0,       1588,       1588,        1,  1382400, 0x00000000
+0,       1589,       1589,        1,  1382400, 0x00000000
+0,       1590,       1590,        1,  1382400, 0x00000000
+0,       1591,       1591,        1,  1382400, 0x00000000
+0,       1592,       1592,        1,  1382400, 0x00000000
+0,       1593,       1593,        1,  1382400, 0x00000000
+0,       1594,       1594,        1,  1382400, 0x00000000
+0,       1595,       1595,        1,  1382400, 0x00000000
+0,       1596,       1596,        1,  1382400, 0x00000000
+0,       1597,       1597,        1,  1382400, 0x00000000
+0,       1598,       1598,        1,  1382400, 0x00000000
+0,       1599,       1599,        1,  1382400, 0x00000000
+0,       1600,       1600,        1,  1382400, 0x00000000
+0,       1601,       1601,        1,  1382400, 0x00000000
+0,       1602,       1602,        1,  1382400, 0x00000000
+0,       1603,       1603,        1,  1382400, 0x00000000
+0,       1604,       1604,        1,  1382400, 0x00000000
+0,       1606,       1606,        1,  1382400, 0x3270f4ff
+0,       1606,       1606,        1,  1382400, 0x3270f4ff
+0,       1607,       1607,        1,  1382400, 0x3270f4ff
+0,       1608,       1608,        1,  1382400, 0x3270f4ff
+0,       1609,       1609,        1,  1382400, 0x3270f4ff
+0,       1610,       1610,        1,  1382400, 0x3270f4ff
+0,       1611,       1611,        1,  1382400, 0x3270f4ff
+0,       1612,       1612,        1,  1382400, 0x3270f4ff
+0,       1613,       1613,        1,  1382400, 0x3270f4ff
+0,       1614,       1614,        1,  1382400, 0x3270f4ff
+0,       1615,       1615,        1,  1382400, 0x3270f4ff
+0,       1617,       1617,        1,  1382400, 0x40813cb3
+0,       1617,       1617,        1,  1382400, 0x40813cb3
+0,       1618,       1618,        1,  1382400, 0x40813cb3
+0,       1619,       1619,        1,  1382400, 0x40813cb3
+0,       1620,       1620,        1,  1382400, 0x40813cb3
+0,       1621,       1621,        1,  1382400, 0x40813cb3
+0,       1622,       1622,        1,  1382400, 0x40813cb3
+0,       1623,       1623,        1,  1382400, 0x9d8fde41
+0,       1624,       1624,        1,  1382400, 0x9d8fde41
+0,       1625,       1625,        1,  1382400, 0x9d8fde41
+0,       1626,       1626,        1,  1382400, 0x9d8fde41
+0,       1627,       1627,        1,  1382400, 0x9d8fde41
+0,       1628,       1628,        1,  1382400, 0x9d8fde41
+0,       1629,       1629,        1,  1382400, 0x9d8fde41
+0,       1630,       1630,        1,  1382400, 0x9d8fde41
+0,       1631,       1631,        1,  1382400, 0x9d8fde41
+0,       1632,       1632,        1,  1382400, 0x00000000
+0,       1633,       1633,        1,  1382400, 0x00000000
+0,       1634,       1634,        1,  1382400, 0x00000000
+0,       1636,       1636,        1,  1382400, 0xc6d7a701
+0,       1636,       1636,        1,  1382400, 0xc6d7a701
+0,       1637,       1637,        1,  1382400, 0xc6d7a701
+0,       1638,       1638,        1,  1382400, 0xc6d7a701
+0,       1639,       1639,        1,  1382400, 0xc6d7a701
+0,       1640,       1640,        1,  1382400, 0xc6d7a701
+0,       1642,       1642,        1,  1382400, 0x9d45f2dc
+0,       1642,       1642,        1,  1382400, 0x9d45f2dc
+0,       1643,       1643,        1,  1382400, 0x9d45f2dc
+0,       1644,       1644,        1,  1382400, 0x9d45f2dc
+0,       1645,       1645,        1,  1382400, 0x9d45f2dc
+0,       1646,       1646,        1,  1382400, 0x9d45f2dc
+0,       1647,       1647,        1,  1382400, 0x9d45f2dc
+0,       1648,       1648,        1,  1382400, 0x9d45f2dc
+0,       1649,       1649,        1,  1382400, 0x00000000
+0,       1650,       1650,        1,  1382400, 0x8525ee40
+0,       1651,       1651,        1,  1382400, 0x8525ee40
+0,       1652,       1652,        1,  1382400, 0x8525ee40
+0,       1653,       1653,        1,  1382400, 0x8525ee40
+0,       1654,       1654,        1,  1382400, 0x8525ee40
+0,       1655,       1655,        1,  1382400, 0x8525ee40
+0,       1656,       1656,        1,  1382400, 0x5b26b98b
+0,       1657,       1657,        1,  1382400, 0x5b26b98b
+0,       1658,       1658,        1,  1382400, 0x5b26b98b
+0,       1659,       1659,        1,  1382400, 0x5b26b98b
+0,       1660,       1660,        1,  1382400, 0x5b26b98b
+0,       1661,       1661,        1,  1382400, 0x5b26b98b
+0,       1662,       1662,        1,  1382400, 0x5b26b98b
+0,       1663,       1663,        1,  1382400, 0x5b26b98b
+0,       1664,       1664,        1,  1382400, 0x00000000
+0,       1665,       1665,        1,  1382400, 0x51be311f
+0,       1666,       1666,        1,  1382400, 0x51be311f
+0,       1667,       1667,        1,  1382400, 0x51be311f
+0,       1668,       1668,        1,  1382400, 0x51be311f
+0,       1669,       1669,        1,  1382400, 0x51be311f
+0,       1670,       1670,        1,  1382400, 0x51be311f
+0,       1671,       1671,        1,  1382400, 0x51be311f
+0,       1672,       1672,        1,  1382400, 0x51be311f
+0,       1673,       1673,        1,  1382400, 0x00000000
+0,       1674,       1674,        1,  1382400, 0x00000000
+0,       1675,       1675,        1,  1382400, 0x00000000
+0,       1676,       1676,        1,  1382400, 0x00000000
+0,       1677,       1677,        1,  1382400, 0x00000000
+0,       1678,       1678,        1,  1382400, 0x00000000
+0,       1679,       1679,        1,  1382400, 0x00000000
+0,       1680,       1680,        1,  1382400, 0x00000000
+0,       1681,       1681,        1,  1382400, 0x00000000
+0,       1682,       1682,        1,  1382400, 0x00000000
+0,       1683,       1683,        1,  1382400, 0x00000000
+0,       1684,       1684,        1,  1382400, 0x00000000
+0,       1685,       1685,        1,  1382400, 0x00000000
+0,       1686,       1686,        1,  1382400, 0x00000000
+0,       1687,       1687,        1,  1382400, 0x00000000
+0,       1688,       1688,        1,  1382400, 0x00000000
+0,       1689,       1689,        1,  1382400, 0x00000000
+0,       1690,       1690,        1,  1382400, 0x00000000
+0,       1691,       1691,        1,  1382400, 0x00000000
+0,       1692,       1692,        1,  1382400, 0x00000000
+0,       1693,       1693,        1,  1382400, 0x00000000
+0,       1694,       1694,        1,  1382400, 0x00000000
+0,       1695,       1695,        1,  1382400, 0x00000000
+0,       1696,       1696,        1,  1382400, 0x00000000
+0,       1697,       1697,        1,  1382400, 0x00000000
+0,       1698,       1698,        1,  1382400, 0x00000000
+0,       1699,       1699,        1,  1382400, 0x00000000
+0,       1700,       1700,        1,  1382400, 0x00000000
+0,       1701,       1701,        1,  1382400, 0x00000000
+0,       1702,       1702,        1,  1382400, 0x00000000
+0,       1703,       1703,        1,  1382400, 0x00000000
+0,       1704,       1704,        1,  1382400, 0x00000000
+0,       1705,       1705,        1,  1382400, 0x00000000
+0,       1706,       1706,        1,  1382400, 0x00000000
+0,       1707,       1707,        1,  1382400, 0x00000000
+0,       1708,       1708,        1,  1382400, 0x00000000
+0,       1709,       1709,        1,  1382400, 0x00000000
+0,       1710,       1710,        1,  1382400, 0x00000000
+0,       1711,       1711,        1,  1382400, 0x00000000
+0,       1713,       1713,        1,  1382400, 0x00a4f2a3
+0,       1713,       1713,        1,  1382400, 0x00a4f2a3
+0,       1714,       1714,        1,  1382400, 0x00a4f2a3
+0,       1715,       1715,        1,  1382400, 0x00a4f2a3
+0,       1716,       1716,        1,  1382400, 0x00a4f2a3
+0,       1717,       1717,        1,  1382400, 0x00a4f2a3
+0,       1718,       1718,        1,  1382400, 0x00a4f2a3
+0,       1719,       1719,        1,  1382400, 0x00a4f2a3
+0,       1720,       1720,        1,  1382400, 0x00a4f2a3
+0,       1721,       1721,        1,  1382400, 0x00a4f2a3
+0,       1722,       1722,        1,  1382400, 0x00000000
+0,       1723,       1723,        1,  1382400, 0x00000000
+0,       1724,       1724,        1,  1382400, 0x00000000
+0,       1725,       1725,        1,  1382400, 0x00000000
+0,       1726,       1726,        1,  1382400, 0x00000000
+0,       1727,       1727,        1,  1382400, 0x00000000
+0,       1728,       1728,        1,  1382400, 0x00000000
+0,       1729,       1729,        1,  1382400, 0x00000000
+0,       1730,       1730,        1,  1382400, 0x00000000
+0,       1731,       1731,        1,  1382400, 0x00000000
+0,       1732,       1732,        1,  1382400, 0x00000000
+0,       1734,       1734,        1,  1382400, 0x40a445e8
+0,       1734,       1734,        1,  1382400, 0x40a445e8
+0,       1735,       1735,        1,  1382400, 0x40a445e8
+0,       1736,       1736,        1,  1382400, 0x40a445e8
+0,       1737,       1737,        1,  1382400, 0x40a445e8
+0,       1738,       1738,        1,  1382400, 0x40a445e8
+0,       1739,       1739,        1,  1382400, 0x40a445e8
+0,       1740,       1740,        1,  1382400, 0x40a445e8
+0,       1741,       1741,        1,  1382400, 0x40a445e8
+0,       1742,       1742,        1,  1382400, 0x00000000
+0,       1743,       1743,        1,  1382400, 0x00000000
+0,       1744,       1744,        1,  1382400, 0x00000000
+0,       1745,       1745,        1,  1382400, 0x00000000
+0,       1746,       1746,        1,  1382400, 0x00000000
+0,       1747,       1747,        1,  1382400, 0x00000000
+0,       1748,       1748,        1,  1382400, 0x00000000
+0,       1749,       1749,        1,  1382400, 0x00000000
+0,       1750,       1750,        1,  1382400, 0x00000000
+0,       1751,       1751,        1,  1382400, 0x00000000
+0,       1752,       1752,        1,  1382400, 0x00000000
+0,       1753,       1753,        1,  1382400, 0x00000000
+0,       1754,       1754,        1,  1382400, 0x00000000
+0,       1755,       1755,        1,  1382400, 0x00000000
+0,       1756,       1756,        1,  1382400, 0x00000000
+0,       1757,       1757,        1,  1382400, 0x00000000
+0,       1758,       1758,        1,  1382400, 0x00000000
+0,       1759,       1759,        1,  1382400, 0x00000000
+0,       1760,       1760,        1,  1382400, 0x00000000
+0,       1761,       1761,        1,  1382400, 0x00000000
+0,       1762,       1762,        1,  1382400, 0x00000000
+0,       1763,       1763,        1,  1382400, 0x00000000
+0,       1764,       1764,        1,  1382400, 0x00000000
+0,       1765,       1765,        1,  1382400, 0x00000000
+0,       1766,       1766,        1,  1382400, 0x00000000
+0,       1767,       1767,        1,  1382400, 0x00000000
+0,       1768,       1768,        1,  1382400, 0x00000000
+0,       1769,       1769,        1,  1382400, 0x00000000
+0,       1770,       1770,        1,  1382400, 0x00000000
+0,       1771,       1771,        1,  1382400, 0x00000000
+0,       1772,       1772,        1,  1382400, 0x00000000
+0,       1773,       1773,        1,  1382400, 0x00000000
+0,       1774,       1774,        1,  1382400, 0x00000000
+0,       1775,       1775,        1,  1382400, 0x00000000
+0,       1776,       1776,        1,  1382400, 0x00000000
+0,       1777,       1777,        1,  1382400, 0x00000000
+0,       1778,       1778,        1,  1382400, 0x00000000
+0,       1779,       1779,        1,  1382400, 0x00000000
+0,       1780,       1780,        1,  1382400, 0x00000000
+0,       1781,       1781,        1,  1382400, 0x00000000
+0,       1782,       1782,        1,  1382400, 0x00000000
+0,       1783,       1783,        1,  1382400, 0x00000000
+0,       1784,       1784,        1,  1382400, 0x00000000
+0,       1785,       1785,        1,  1382400, 0x00000000
+0,       1786,       1786,        1,  1382400, 0x00000000
+0,       1788,       1788,        1,  1382400, 0x43ef5128
+0,       1788,       1788,        1,  1382400, 0x43ef5128
+0,       1789,       1789,        1,  1382400, 0x43ef5128
+0,       1790,       1790,        1,  1382400, 0x43ef5128
+0,       1791,       1791,        1,  1382400, 0x43ef5128
+0,       1792,       1792,        1,  1382400, 0x43ef5128
+0,       1793,       1793,        1,  1382400, 0x43ef5128
+0,       1794,       1794,        1,  1382400, 0x43ef5128
+0,       1795,       1795,        1,  1382400, 0x43ef5128
+0,       1796,       1796,        1,  1382400, 0x43ef5128
+0,       1797,       1797,        1,  1382400, 0x43ef5128
+0,       1798,       1798,        1,  1382400, 0x43ef5128
+0,       1799,       1799,        1,  1382400, 0x3c3e3819
+0,       1800,       1800,        1,  1382400, 0x3c3e3819
+0,       1801,       1801,        1,  1382400, 0x3c3e3819
+0,       1802,       1802,        1,  1382400, 0x3c3e3819
+0,       1803,       1803,        1,  1382400, 0x3c3e3819
+0,       1804,       1804,        1,  1382400, 0x3c3e3819
+0,       1805,       1805,        1,  1382400, 0x00000000
diff --git a/tests/ref/fate/sub2video_time_limited b/tests/ref/fate/sub2video_time_limited
index 9fb6fb06f9..2fbd91a7eb 100644
--- a/tests/ref/fate/sub2video_time_limited
+++ b/tests/ref/fate/sub2video_time_limited
@@ -1,8 +1,80 @@
-#tb 0: 1/25
+#tb 0: 1/5
 #media_type 0: video
 #codec_id 0: rawvideo
 #dimensions 0: 1920x1080
-#sar 0: 0/1
-0,          2,          2,        1,  8294400, 0x00000000
+#sar 0: 1/1
+0,          0,          0,        1,  8294400, 0xa87c518f
+0,          1,          1,        1,  8294400, 0xa87c518f
 0,          2,          2,        1,  8294400, 0xa87c518f
+0,          3,          3,        1,  8294400, 0xa87c518f
+0,          4,          4,        1,  8294400, 0xa87c518f
+0,          5,          5,        1,  8294400, 0xa87c518f
+0,          6,          6,        1,  8294400, 0xa87c518f
+0,          7,          7,        1,  8294400, 0xa87c518f
+0,          8,          8,        1,  8294400, 0xa87c518f
+0,          9,          9,        1,  8294400, 0xa87c518f
 0,         10,         10,        1,  8294400, 0xa87c518f
+0,         11,         11,        1,  8294400, 0xa87c518f
+0,         12,         12,        1,  8294400, 0xa87c518f
+0,         13,         13,        1,  8294400, 0xa87c518f
+0,         14,         14,        1,  8294400, 0xa87c518f
+0,         15,         15,        1,  8294400, 0xa87c518f
+0,         16,         16,        1,  8294400, 0xa87c518f
+0,         17,         17,        1,  8294400, 0xa87c518f
+0,         18,         18,        1,  8294400, 0xa87c518f
+0,         19,         19,        1,  8294400, 0xa87c518f
+0,         20,         20,        1,  8294400, 0xa87c518f
+0,         21,         21,        1,  8294400, 0xa87c518f
+0,         22,         22,        1,  8294400, 0xa87c518f
+0,         23,         23,        1,  8294400, 0xa87c518f
+0,         24,         24,        1,  8294400, 0xa87c518f
+0,         25,         25,        1,  8294400, 0xa87c518f
+0,         26,         26,        1,  8294400, 0xa87c518f
+0,         27,         27,        1,  8294400, 0xa87c518f
+0,         28,         28,        1,  8294400, 0xa87c518f
+0,         29,         29,        1,  8294400, 0xa87c518f
+0,         30,         30,        1,  8294400, 0xa87c518f
+0,         31,         31,        1,  8294400, 0xa87c518f
+0,         32,         32,        1,  8294400, 0xa87c518f
+0,         33,         33,        1,  8294400, 0xa87c518f
+0,         34,         34,        1,  8294400, 0xa87c518f
+0,         35,         35,        1,  8294400, 0xa87c518f
+0,         36,         36,        1,  8294400, 0xa87c518f
+0,         37,         37,        1,  8294400, 0xa87c518f
+0,         38,         38,        1,  8294400, 0xa87c518f
+0,         39,         39,        1,  8294400, 0xa87c518f
+0,         40,         40,        1,  8294400, 0xa87c518f
+0,         41,         41,        1,  8294400, 0xa87c518f
+0,         42,         42,        1,  8294400, 0xa87c518f
+0,         43,         43,        1,  8294400, 0xa87c518f
+0,         44,         44,        1,  8294400, 0xa87c518f
+0,         45,         45,        1,  8294400, 0xa87c518f
+0,         46,         46,        1,  8294400, 0xa87c518f
+0,         47,         47,        1,  8294400, 0xa87c518f
+0,         48,         48,        1,  8294400, 0xa87c518f
+0,         49,         49,        1,  8294400, 0xa87c518f
+0,         50,         50,        1,  8294400, 0xa87c518f
+0,         51,         51,        1,  8294400, 0xa87c518f
+0,         52,         52,        1,  8294400, 0xa87c518f
+0,         53,         53,        1,  8294400, 0xa87c518f
+0,         54,         54,        1,  8294400, 0xa87c518f
+0,         55,         55,        1,  8294400, 0xa87c518f
+0,         56,         56,        1,  8294400, 0xa87c518f
+0,         57,         57,        1,  8294400, 0xa87c518f
+0,         58,         58,        1,  8294400, 0xa87c518f
+0,         59,         59,        1,  8294400, 0xa87c518f
+0,         60,         60,        1,  8294400, 0xa87c518f
+0,         61,         61,        1,  8294400, 0xa87c518f
+0,         62,         62,        1,  8294400, 0xa87c518f
+0,         63,         63,        1,  8294400, 0xa87c518f
+0,         64,         64,        1,  8294400, 0xa87c518f
+0,         65,         65,        1,  8294400, 0xa87c518f
+0,         66,         66,        1,  8294400, 0xa87c518f
+0,         67,         67,        1,  8294400, 0xa87c518f
+0,         68,         68,        1,  8294400, 0xa87c518f
+0,         69,         69,        1,  8294400, 0xa87c518f
+0,         70,         70,        1,  8294400, 0xa87c518f
+0,         71,         71,        1,  8294400, 0xa87c518f
+0,         72,         72,        1,  8294400, 0xa87c518f
+0,         73,         73,        1,  8294400, 0xa87c518f
+0,         74,         74,        1,  8294400, 0xa87c518f
-- 
ffmpeg-codebot

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* [FFmpeg-devel] [PATCH v6 25/25] avcodec/dvbsubdec: Fix conditions for fallback to default resolution
  2022-06-26 16:34         ` [FFmpeg-devel] [PATCH v6 00/25] Subtitle Filtering 2022 ffmpegagent
                             ` (23 preceding siblings ...)
  2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 24/25] fftools/ffmpeg: Introduce subtitle filtering and new frame-based subtitle encoding softworkz
@ 2022-06-26 16:35           ` softworkz
  24 siblings, 0 replies; 217+ messages in thread
From: softworkz @ 2022-06-26 16:35 UTC (permalink / raw)
  To: ffmpeg-devel
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

From: softworkz <softworkz@hotmail.com>

The previous code expected a segment of type CLUT definition to exist
in order to accept a set of segments to be complete.
This was an incorrect assumption as the presence of a CLUT segment
is not mandatory.
(version 1.6.1 of the spec is probably a bit more clear about this
than earlier versions: https://www.etsi.org/deliver/etsi_en/
300700_300799/300743/01.06.01_20/en_300743v010601a.pdf)

The flawed condition prevented proper fallback to using the default
resolution for the decoding context.

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/dvbsubdec.c | 51 +++++++++++++++++++++++++-----------------
 1 file changed, 30 insertions(+), 21 deletions(-)

diff --git a/libavcodec/dvbsubdec.c b/libavcodec/dvbsubdec.c
index 8d44529b4a..06f39161a8 100644
--- a/libavcodec/dvbsubdec.c
+++ b/libavcodec/dvbsubdec.c
@@ -34,7 +34,7 @@
 #define DVBSUB_CLUT_SEGMENT     0x12
 #define DVBSUB_OBJECT_SEGMENT   0x13
 #define DVBSUB_DISPLAYDEFINITION_SEGMENT 0x14
-#define DVBSUB_DISPLAY_SEGMENT  0x80
+#define DVBSUB_END_DISPLAY_SEGMENT  0x80
 
 #define cm (ff_crop_tab + MAX_NEG_CROP)
 
@@ -1450,8 +1450,12 @@ static int dvbsub_decode(AVCodecContext *avctx, AVSubtitle *sub,
     int segment_length;
     int i;
     int ret = 0;
-    int got_segment = 0;
-    int got_dds = 0;
+    //int got_segment = 0;
+    int got_page = 0;
+    int got_region = 0;
+    int got_object = 0;
+    int got_end_display = 0;
+    int got_displaydef = 0;
 
     ff_dlog(avctx, "DVB sub packet:\n");
 
@@ -1496,34 +1500,28 @@ static int dvbsub_decode(AVCodecContext *avctx, AVSubtitle *sub,
             switch (segment_type) {
             case DVBSUB_PAGE_SEGMENT:
                 ret = dvbsub_parse_page_segment(avctx, p, segment_length, sub, got_sub_ptr);
-                got_segment |= 1;
+                got_page = 1;
                 break;
             case DVBSUB_REGION_SEGMENT:
                 ret = dvbsub_parse_region_segment(avctx, p, segment_length);
-                got_segment |= 2;
+                got_region = 1;
                 break;
             case DVBSUB_CLUT_SEGMENT:
                 ret = dvbsub_parse_clut_segment(avctx, p, segment_length);
                 if (ret < 0) goto end;
-                got_segment |= 4;
                 break;
             case DVBSUB_OBJECT_SEGMENT:
                 ret = dvbsub_parse_object_segment(avctx, p, segment_length);
-                got_segment |= 8;
+                got_object = 1;
                 break;
             case DVBSUB_DISPLAYDEFINITION_SEGMENT:
                 ret = dvbsub_parse_display_definition_segment(avctx, p,
                                                               segment_length);
-                got_dds = 1;
+                got_displaydef = 1;
                 break;
-            case DVBSUB_DISPLAY_SEGMENT:
+            case DVBSUB_END_DISPLAY_SEGMENT:
                 ret = dvbsub_display_end_segment(avctx, p, segment_length, sub, got_sub_ptr);
-                if (got_segment == 15 && !got_dds && !avctx->width && !avctx->height) {
-                    // Default from ETSI EN 300 743 V1.3.1 (7.2.1)
-                    avctx->width  = 720;
-                    avctx->height = 576;
-                }
-                got_segment |= 16;
+                got_end_display = 1;
                 break;
             default:
                 ff_dlog(avctx, "Subtitling segment type 0x%x, page id %d, length %d\n",
@@ -1536,13 +1534,24 @@ static int dvbsub_decode(AVCodecContext *avctx, AVSubtitle *sub,
 
         p += segment_length;
     }
-    // Some streams do not send a display segment but if we have all the other
-    // segments then we need no further data.
-    if (got_segment == 15) {
-        av_log(avctx, AV_LOG_DEBUG, "Missing display_end_segment, emulating\n");
-        dvbsub_display_end_segment(avctx, p, 0, sub, got_sub_ptr);
-    }
 
+    // Even though not mandated by the spec, we're imposing a minimum requirement
+    // for a useful packet to have at least one page, region and object segment.
+    if (got_page && got_region && got_object && got_end_display) {
+
+        if (!got_displaydef && !avctx->width && !avctx->height) {
+            // Default from ETSI EN 300 743 V1.3.1 (7.2.1)
+            avctx->width  = 720;
+            avctx->height = 576;
+        }
+
+        // Some streams do not send an end-of-display segment but if we have all the other
+        // segments then we need no further data.
+        if (!got_end_display) {
+            av_log(avctx, AV_LOG_DEBUG, "Missing display_end_segment, emulating\n");
+            dvbsub_display_end_segment(avctx, p, 0, sub, got_sub_ptr);
+        }
+    }
 end:
     if (ret < 0) {
         return ret;
-- 
ffmpeg-codebot
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-06-25  9:57       ` [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022 ffmpegagent
                           ` (25 preceding siblings ...)
  2022-06-26 16:34         ` [FFmpeg-devel] [PATCH v6 00/25] Subtitle Filtering 2022 ffmpegagent
@ 2022-07-02 16:39         ` Paul B Mahol
  2022-07-02 17:18           ` Nicolas George
  2022-07-02 19:03           ` Soft Works
  2022-08-11 22:50         ` Soft Works
  27 siblings, 2 replies; 217+ messages in thread
From: Paul B Mahol @ 2022-07-02 16:39 UTC (permalink / raw)
  To: FFmpeg development discussions and patches
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

On Sat, Jun 25, 2022 at 11:58 AM ffmpegagent <ffmpegagent@gmail.com> wrote:

>
> Subtitle Filtering 2022
> =======================
>
> This is a substantial update to the earlier subtitle filtering patch
> series.
> A primary goal has been to address others' concerns as much as possible on
> one side and to provide more clarity and control over the way things are
> working. Clarity is is specifically important to allow for a better
> understanding of the need for a subtitle start pts value that can be
> different from the frame's pts value. This is done by refactoring the
> subtitle timing fields in AVFrame, adding a frame field to indicate
> repeated
> subtitle frames, and finally the full removal of the heartbeat
> functionality, replaced by a new 'subfeed' filter that provides different
> modes for arbitrating subtitle frames in a filter graph. Finally, each
> subtitle filter's documentation has been amended by a section describing
> the
> filter's timeline behavior (in v3 update).
>
>
> Subtitle Filtering Demos
> ========================
>
> I published a demonstration of subtitle filtering capabilities with OCR,
> text and bitmap subtitle manipulation involved: Demo 1: Text-Manipulation
> with Bitmap Subtitles
> [https://github.com/softworkz/SubtitleFilteringDemos/tree/master/Demo1]
>
>
> v5 - Conversion to Graphic Subtitles, and other enhancements
> ============================================================
>
>  * I'm glad to announce that Traian (@tcoza) has joined the project and
>    contributed a new 'text2graphicsub' filter to convert text subtitles to
>    graphic subtitles, which can in turn be encoded as dvd, dvb or x-subs
>    (and any other encoder for graphic subs that might be added in the
>    future). This filter closes the last open "gap" in subtitle processing.
>  * stripstyles filter: now allows very fine-grained control over which ASS
>    style codes should be preserved or stripped
>  * stripstyles: do not drop dialog margin values
>  * subfeed filter: eliminates duplicate frames with duplicate start times
>    when 'fix_overlap' is specified
>  * textmod: do not drop effect values
>  * graphicsub2text: reduce font size jitter
>  * ass_split: add function to selectively preserve elements when splitting
>  * add strim, snull and ssink and further unify subtitle frame handling
> with
>    audio and video
>  * ffmpeg_filter: get simple filter notation working for subtitles
>
>
> v4 - Quality Improvements
> =========================
>
>  * finally an updated version
>  * includes many improvements from internal testing
>  * all FATE tests passed
>  * all example commands from the docs verified to work
>  * can't list all the detail changes..
>  * I have left out the extra commits which can be handled separately, just
>    in case somebody wonders why these are missing:
>    * avcodec/webvttenc: Don't encode drawing codes and empty lines
>    * avcodec/webvttenc: convert hard-space tags to
>    * avutil/ass_split: Add parsing of hard-space tags (\h)
>    * avutil/ass_split: Treat all content in curly braces as hidden
>    * avutil/ass_split: Fix ass parsing of style codes with comments
>
>
> v3 - Rebase
> ===========
>
> due to merge conflicts - apologies.
>
>
> Changes in v2
> =============
>
>  * added .gitattributes file to enforce binary diffs for the test refs that
>    cannot be applied when being sent via e-mail
>  * perform filter graph re-init due to subtitle "frame size" change only
>    when the size was unknown before and not set via -canvas_size
>  * overlaytextsubs: Make sure to request frames on the subtitle input
>  * avfilter/splitcc: Start parsing cc data on key frames only
>  * avcodec/webvttenc: Don't encode ass drawing codes and empty lines
>  * stripstyles: fix mem leak
>  * gs2t: improve color detection
>  * gs2t: empty frames must not be skipped
>  * subfeed: fix name
>  * textmod: preserve margins
>  * added .gitattributes file to enforce binary diffs for the test refs that
>    cannot be applied when being sent via e-mail
>  * perform filter graph re-init due to subtitle "frame size" change only
>    when the size was unknown before and not set via -canvas_size
>  * avcodec/dvbsubdec: Fix conditions for fallback to default resolution
>  * Made changes suggested by Andreas
>  * Fixed failing command line reported by Michael
>
> Changes from previous version v24:
>
>
> AVFrame
> =======
>
>  * Removed sub_start_time The start time is now added to the subtitle
>    start_pts during decoding The sub_end_time field is adjusted accordingly
>  * Renamed sub_end_time to duration which it is effectively after removing
>    the start_time
>  * Added a sub-struct 'subtitle_timing' to av frame Contains subtitle_pts
>    renamed to 'subtitle_timing.start_pts' and 'subtitle_timing.duration'
>  * Change both fields to (fixed) time_base AV_TIMEBASE
>  * add repeat_sub field provides a clear indication whether a subtitle
> frame
>    is an actual subtitle event or a repeated subtitle frame in a filter
>    graph
>
>
> Heartbeat Removal
> =================
>
>  * completely removed the earlier heartbeat implementation
>  * filtering arbitration is now implemented in a new filter: 'subfeed'
>  * subfeed will be auto-inserted for compatiblity with sub2video command
>    lines
>  * the new behavior is not exactly identical to the earlier behavior, but
> it
>    basically allows to achieve the same results
>  * there's a small remainder, now named subtitle kickoff which serves to
> get
>    things (in the filter graph) going right from the start
>
>
> New 'subfeed' Filter
> ====================
>
>  * a versatile filter for solving all kinds of problems with subtile frame
>    flow in filter graphs
>  * Can be inserted at any position in a graph
>  * Auto-inserted for sub2video command lines (in repeat-mode)
>  * Allows duration fixup delay input frames with unknown duration and infer
>    duration from start of subsequent frame
>  * Provides multiple modes of operation:
>    * repeat mode (default) Queues input frames Outputs frames at a fixed
>      (configurable) rate Either sends a matching input frame (repeatedly)
> or
>      empty frames otherwise
>    * scatter mode similar to repeat mode, but splits input frames by
>      duration into small segments with same content
>    * forward mode No fixed output rate Useful in combination with duration
>      fixup or overlap fixup
>
>
> ffmpeg Tool Changes
> ===================
>
>  * delay subtitle output stream initialization (like for audio and video)
>    This is needed for example when a format header depends on having
>    received an initial frame to derive certain header values from
>  * decoding: set subtitle frame size from decoding context
>  * re-init graph when subtitle size changes
>  * always insert subscale filter for sub2video command lines (to ensure
>    correct scaling)
>
>
> Subtitle Encoding
> =================
>
>  * ignore repeated frames for encoding based on repeat_sub field in AVFrame
>  * support multi-area encoding for text subtitles Subtitle OCR can create
>    multiple areas at different positions. Previously, the texts were always
>    squashed into a single area ('subtitle rect'), which was not ideal.
>    Multiple text areas are now generally supported:
>    * ASS Encoder Changed to use the 'receive_packet' encoding API A single
>      frame with multiple text areas will create multiple packets now
>    * All other text subtitle encoders A newline is inserted between the
> text
>      from multiple areas
>
>
> graphicsub2text (OCR)
> =====================
>
>  * enhanced preprocessing
>    * using elbg algorithm for color quantization
>    * detection and removal of text outlines
>    * map-based identification of colors per word (text, outline,
> background)
>  * add option for duration fixup
>  * add option to dump preprocessing bitmaps
>  * Recognize formatting and apply as ASS inline styles
>    * per word(!)
>    * paragraph alignment
>    * positioning
>    * font names
>    * font size
>    * font style (italic, underline, bold)
>    * text color, outline color
>
>
> Other Filter Changes
> ====================
>
>  * all: Make sure to forward all link properties (time base, frame rate, w,
>    h) where appropriate
>  * overlaytextsubs: request frames on the subtitle input
>  * overlaytextsubs: disable read-order checking
>  * overlaytextsubs: improve implementation of render_latest_only
>  * overlaytextsubs: ensure equal in/out video formats
>  * splitcc: derive framerate from realtime_latency
>  * graphicsub2video: implement caching of converted frames
>  * graphicsub2video: use 1x1 output frame size as long as subtitle size is
>    unknown (0x0)
>
> Plus a dozen of things I forgot..
>
> softworkz (24):
>   avcodec,avutil: Move enum AVSubtitleType to avutil, add new and
>     deprecate old values
>   avutil/frame: Prepare AVFrame for subtitle handling
>   avcodec/subtitles: Introduce new frame-based subtitle decoding API
>   avcodec/libzvbi: set subtitle type
>   avfilter/subtitles: Update vf_subtitles to use new decoding api
>   avcodec,avutil: Move ass helper functions to avutil as avpriv_ and
>     extend ass dialog parsing
>   avcodec/subtitles: Replace deprecated enum values
>   fftools/play,probe: Adjust for subtitle changes
>   avfilter/subtitles: Add subtitles.c for subtitle frame allocation
>   avfilter/avfilter: Handle subtitle frames
>   avfilter/avfilter: Fix hardcoded input index
>   avfilter/sbuffer: Add sbuffersrc and sbuffersink filters
>   avfilter/overlaygraphicsubs: Add overlaygraphicsubs and
>     graphicsub2video filters
>   avfilter/overlaytextsubs: Add overlaytextsubs and textsubs2video
>     filters
>   avfilter/textmod: Add textmod, censor and show_speaker filters
>   avfilter/stripstyles: Add stripstyles filter
>   avfilter/splitcc: Add splitcc filter for closed caption handling
>   avfilter/graphicsub2text: Add new graphicsub2text filter (OCR)
>   avfilter/subscale: Add filter for scaling and/or re-arranging
>     graphical subtitles
>   avfilter/subfeed: add subtitle feed filter
>   avfilter/snull,strim: Add snull and strim filters
>   avcodec/subtitles: Migrate subtitle encoders to frame-based API
>   fftools/ffmpeg: Introduce subtitle filtering and new frame-based
>     subtitle encoding
>   avcodec/dvbsubdec: Fix conditions for fallback to default resolution
>
>

Can this be properly finally be fully reviewed and accepted?

Otherwise if its kept mainly ignored than it should be regarded as spam.

Current status quo is bad.



> tcoza (1):
>   avfilter/text2graphicsub: Added text2graphicsub subtitle filter
>
>  configure                                 |   10 +-
>  doc/filters.texi                          |  807 ++++++++++++++
>  fftools/ffmpeg.c                          |  613 +++++-----
>  fftools/ffmpeg.h                          |   17 +-
>  fftools/ffmpeg_filter.c                   |  270 +++--
>  fftools/ffmpeg_hw.c                       |    2 +-
>  fftools/ffmpeg_opt.c                      |   28 +-
>  fftools/ffplay.c                          |  102 +-
>  fftools/ffprobe.c                         |   47 +-
>  libavcodec/Makefile                       |   56 +-
>  libavcodec/ass.h                          |  151 +--
>  libavcodec/ass_split.h                    |  191 ----
>  libavcodec/assdec.c                       |    4 +-
>  libavcodec/assenc.c                       |  191 +++-
>  libavcodec/avcodec.c                      |    8 +
>  libavcodec/avcodec.h                      |   34 +-
>  libavcodec/ccaption_dec.c                 |   20 +-
>  libavcodec/codec_internal.h               |   12 -
>  libavcodec/decode.c                       |   60 +-
>  libavcodec/dvbsubdec.c                    |   53 +-
>  libavcodec/dvbsubenc.c                    |   96 +-
>  libavcodec/dvdsubdec.c                    |    2 +-
>  libavcodec/dvdsubenc.c                    |  102 +-
>  libavcodec/encode.c                       |   61 +-
>  libavcodec/internal.h                     |   16 +
>  libavcodec/jacosubdec.c                   |    2 +-
>  libavcodec/libaribb24.c                   |    2 +-
>  libavcodec/libzvbi-teletextdec.c          |   17 +-
>  libavcodec/microdvddec.c                  |    7 +-
>  libavcodec/movtextdec.c                   |    3 +-
>  libavcodec/movtextenc.c                   |  126 ++-
>  libavcodec/mpl2dec.c                      |    2 +-
>  libavcodec/pgssubdec.c                    |    2 +-
>  libavcodec/realtextdec.c                  |    2 +-
>  libavcodec/samidec.c                      |    2 +-
>  libavcodec/srtdec.c                       |    2 +-
>  libavcodec/srtenc.c                       |  116 +-
>  libavcodec/subviewerdec.c                 |    2 +-
>  libavcodec/tests/avcodec.c                |    5 +-
>  libavcodec/textdec.c                      |    4 +-
>  libavcodec/ttmlenc.c                      |  114 +-
>  libavcodec/utils.c                        |  185 ++-
>  libavcodec/webvttdec.c                    |    2 +-
>  libavcodec/webvttenc.c                    |   94 +-
>  libavcodec/xsubdec.c                      |    2 +-
>  libavcodec/xsubenc.c                      |   88 +-
>  libavfilter/Makefile                      |   18 +
>  libavfilter/allfilters.c                  |   19 +
>  libavfilter/avfilter.c                    |   34 +-
>  libavfilter/avfilter.h                    |   11 +
>  libavfilter/avfiltergraph.c               |    5 +
>  libavfilter/buffersink.c                  |   54 +
>  libavfilter/buffersink.h                  |    7 +
>  libavfilter/buffersrc.c                   |   72 ++
>  libavfilter/buffersrc.h                   |    1 +
>  libavfilter/formats.c                     |   16 +
>  libavfilter/formats.h                     |    3 +
>  libavfilter/internal.h                    |   19 +-
>  libavfilter/sf_graphicsub2text.c          | 1137 +++++++++++++++++++
>  libavfilter/sf_snull.c                    |   50 +
>  libavfilter/sf_splitcc.c                  |  395 +++++++
>  libavfilter/sf_stripstyles.c              |  237 ++++
>  libavfilter/sf_subfeed.c                  |  412 +++++++
>  libavfilter/sf_subscale.c                 |  884 +++++++++++++++
>  libavfilter/sf_text2graphicsub.c          |  630 +++++++++++
>  libavfilter/sf_textmod.c                  |  710 ++++++++++++
>  libavfilter/subtitles.c                   |   63 ++
>  libavfilter/subtitles.h                   |   44 +
>  libavfilter/trim.c                        |   46 +-
>  libavfilter/vf_overlaygraphicsubs.c       |  765 +++++++++++++
>  libavfilter/vf_overlaytextsubs.c          |  680 +++++++++++
>  libavfilter/vf_subtitles.c                |   67 +-
>  libavutil/Makefile                        |    4 +
>  {libavcodec => libavutil}/ass.c           |  115 +-
>  libavutil/ass_internal.h                  |  135 +++
>  {libavcodec => libavutil}/ass_split.c     |  179 ++-
>  libavutil/ass_split_internal.h            |  254 +++++
>  libavutil/frame.c                         |  206 +++-
>  libavutil/frame.h                         |   85 +-
>  libavutil/subfmt.c                        |   45 +
>  libavutil/subfmt.h                        |  115 ++
>  libavutil/version.h                       |    1 +
>  tests/ref/fate/filter-overlay-dvdsub-2397 |  182 +--
>  tests/ref/fate/sub-dvb                    |  162 +--
>  tests/ref/fate/sub-scc                    |    1 -
>  tests/ref/fate/sub2video                  | 1091 +++++++++++++++++-
>  tests/ref/fate/sub2video_basic            | 1238 +++++++++++++++++++--
>  tests/ref/fate/sub2video_time_limited     |   78 +-
>  88 files changed, 12398 insertions(+), 1604 deletions(-)
>  delete mode 100644 libavcodec/ass_split.h
>  create mode 100644 libavfilter/sf_graphicsub2text.c
>  create mode 100644 libavfilter/sf_snull.c
>  create mode 100644 libavfilter/sf_splitcc.c
>  create mode 100644 libavfilter/sf_stripstyles.c
>  create mode 100644 libavfilter/sf_subfeed.c
>  create mode 100644 libavfilter/sf_subscale.c
>  create mode 100644 libavfilter/sf_text2graphicsub.c
>  create mode 100644 libavfilter/sf_textmod.c
>  create mode 100644 libavfilter/subtitles.c
>  create mode 100644 libavfilter/subtitles.h
>  create mode 100644 libavfilter/vf_overlaygraphicsubs.c
>  create mode 100644 libavfilter/vf_overlaytextsubs.c
>  rename {libavcodec => libavutil}/ass.c (59%)
>  create mode 100644 libavutil/ass_internal.h
>  rename {libavcodec => libavutil}/ass_split.c (71%)
>  create mode 100644 libavutil/ass_split_internal.h
>  create mode 100644 libavutil/subfmt.c
>  create mode 100644 libavutil/subfmt.h
>
>
> base-commit: 6a82412bf33108111eb3f63076fd5a51349ae114
> Published-As:
> https://github.com/ffstaging/FFmpeg/releases/tag/pr-ffstaging-18%2Fsoftworkz%2Fsubmit_subfiltering-v5
> Fetch-It-Via
> <https://github.com/ffstaging/FFmpeg/releases/tag/pr-ffstaging-18%2Fsoftworkz%2Fsubmit_subfiltering-v5Fetch-It-Via>:
> git fetch https://github.com/ffstaging/FFmpeg
> pr-ffstaging-18/softworkz/submit_subfiltering-v5
> Pull-Request: https://github.com/ffstaging/FFmpeg/pull/18
>
> Range-diff vs v4:
>
>   1:  2f3ba171f5 =  1:  aa32b9048f avcodec,avutil: Move enum
> AVSubtitleType to avutil, add new and deprecate old values
>   2:  ff101f8a76 !  2:  d5ab9d1919 avutil/frame: Prepare AVFrame for
> subtitle handling
>      @@ libavutil/frame.c: FF_ENABLE_DEPRECATION_WARNINGS
>       +    case AVMEDIA_TYPE_VIDEO:
>                return frame_copy_video(dst, src);
>       -    else if (dst->nb_samples > 0 &&
>      --             (av_channel_layout_check(&dst->ch_layout)
>      --#if FF_API_OLD_CHANNEL_LAYOUT
>      --              || dst->channel_layout || dst->channels
>      --#endif
>      --            ))
>       +    case AVMEDIA_TYPE_AUDIO:
>      -         return frame_copy_audio(dst, src);
>      ++        if (dst->nb_samples > 0 &&
>      +              (av_channel_layout_check(&dst->ch_layout)
>      + #if FF_API_OLD_CHANNEL_LAYOUT
>      +               || dst->channels > 0
>      + #endif
>      +             ))
>      +-        return frame_copy_audio(dst, src);
>       -FF_ENABLE_DEPRECATION_WARNINGS
>      --
>      --    return AVERROR(EINVAL);
>      ++            return frame_copy_audio(dst, src);
>      ++        break;
>       +    case AVMEDIA_TYPE_SUBTITLE:
>       +        return frame_copy_subtitles(dst, src, 1);
>      -+    default:
>      -+        return AVERROR(EINVAL);
>       +    }
>      - }
>
>      - void av_frame_remove_side_data(AVFrame *frame, enum
> AVFrameSideDataType type)
>      +     return AVERROR(EINVAL);
>      + }
>
>        ## libavutil/frame.h ##
>       @@
>   3:  b8935d5e68 =  3:  0a685a6b19 avcodec/subtitles: Introduce new
> frame-based subtitle decoding API
>   4:  4b44732e07 =  4:  0b69b1ce19 avcodec/libzvbi: set subtitle type
>   5:  8faa7a4043 =  5:  0c2091e57c avfilter/subtitles: Update vf_subtitles
> to use new decoding api
>   6:  1664026d7c !  6:  4903cdd1cd avcodec,avutil: Move ass helper
> functions to avutil as avpriv_ and extend ass dialog parsing
>      @@ Commit message
>
>           - hard_space callback (for upcoming fix)
>           - extensible callback (for future extension)
>      +    - new API which allows tag filtering
>
>           Signed-off-by: softworkz <softworkz@hotmail.com>
>
>      @@ libavcodec/ass.h
>       -                             const char *linebreaks, int
> keep_ass_markup);
>        #endif /* AVCODEC_ASS_H */
>
>      + ## libavcodec/ass_split.h (deleted) ##
>      +@@
>      +-/*
>      +- * SSA/ASS spliting functions
>      +- * Copyright (c) 2010  Aurelien Jacobs <aurel@gnuage.org>
>      +- *
>      +- * 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
>      +- */
>      +-
>      +-#ifndef AVCODEC_ASS_SPLIT_H
>      +-#define AVCODEC_ASS_SPLIT_H
>      +-
>      +-/**
>      +- * fields extracted from the [Script Info] section
>      +- */
>      +-typedef struct {
>      +-    char *script_type;    /**< SSA script format version (eg.
> v4.00) */
>      +-    char *collisions;     /**< how subtitles are moved to prevent
> collisions */
>      +-    int   play_res_x;     /**< video width that ASS coords are
> referring to */
>      +-    int   play_res_y;     /**< video height that ASS coords are
> referring to */
>      +-    float timer;          /**< time multiplier to apply to SSA
> clock (in %) */
>      +-} ASSScriptInfo;
>      +-
>      +-/**
>      +- * fields extracted from the [V4(+) Styles] section
>      +- */
>      +-typedef struct {
>      +-    char *name;           /**< name of the tyle (case sensitive) */
>      +-    char *font_name;      /**< font face (case sensitive) */
>      +-    int   font_size;      /**< font height */
>      +-    int   primary_color;  /**< color that a subtitle will normally
> appear in */
>      +-    int   secondary_color;
>      +-    int   outline_color;  /**< color for outline in ASS, called
> tertiary in SSA */
>      +-    int   back_color;     /**< color of the subtitle outline or
> shadow */
>      +-    int   bold;           /**< whether text is bold (1) or not (0)
> */
>      +-    int   italic;         /**< whether text is italic (1) or not
> (0) */
>      +-    int   underline;      /**< whether text is underlined (1) or
> not (0) */
>      +-    int   strikeout;
>      +-    float scalex;
>      +-    float scaley;
>      +-    float spacing;
>      +-    float angle;
>      +-    int   border_style;
>      +-    float outline;
>      +-    float shadow;
>      +-    int   alignment;      /**< position of the text (left, center,
> top...),
>      +-                               defined after the layout of the
> numpad
>      +-                               (1-3 sub, 4-6 mid, 7-9 top) */
>      +-    int   margin_l;
>      +-    int   margin_r;
>      +-    int   margin_v;
>      +-    int   alpha_level;
>      +-    int   encoding;
>      +-} ASSStyle;
>      +-
>      +-/**
>      +- * fields extracted from the [Events] section
>      +- */
>      +-typedef struct {
>      +-    int   readorder;
>      +-    int   layer;    /**< higher numbered layers are drawn over
> lower numbered */
>      +-    int   start;    /**< start time of the dialog in centiseconds */
>      +-    int   end;      /**< end time of the dialog in centiseconds */
>      +-    char *style;    /**< name of the ASSStyle to use with this
> dialog */
>      +-    char *name;
>      +-    int   margin_l;
>      +-    int   margin_r;
>      +-    int   margin_v;
>      +-    char *effect;
>      +-    char *text;     /**< actual text which will be displayed as a
> subtitle,
>      +-                         can include style override control codes
> (see
>      +-                         ff_ass_split_override_codes()) */
>      +-} ASSDialog;
>      +-
>      +-/**
>      +- * structure containing the whole split ASS data
>      +- */
>      +-typedef struct {
>      +-    ASSScriptInfo script_info;   /**< general information about the
> SSA script*/
>      +-    ASSStyle     *styles;        /**< array of split out styles */
>      +-    int           styles_count;  /**< number of ASSStyle in the
> styles array */
>      +-    ASSDialog    *dialogs;       /**< array of split out dialogs */
>      +-    int           dialogs_count; /**< number of ASSDialog in the
> dialogs array*/
>      +-} ASS;
>      +-
>      +-/**
>      +- * This struct can be casted to ASS to access to the split data.
>      +- */
>      +-typedef struct ASSSplitContext ASSSplitContext;
>      +-
>      +-/**
>      +- * Split a full ASS file or a ASS header from a string buffer and
> store
>      +- * the split structure in a newly allocated context.
>      +- *
>      +- * @param buf String containing the ASS formatted data.
>      +- * @return Newly allocated struct containing split data.
>      +- */
>      +-ASSSplitContext *ff_ass_split(const char *buf);
>      +-
>      +-/**
>      +- * Free a dialogue obtained from ff_ass_split_dialog().
>      +- */
>      +-void ff_ass_free_dialog(ASSDialog **dialogp);
>      +-
>      +-/**
>      +- * Split one ASS Dialogue line from a string buffer.
>      +- *
>      +- * @param ctx Context previously initialized by ff_ass_split().
>      +- * @param buf String containing the ASS "Dialogue" line.
>      +- * @return Pointer to the split ASSDialog. Must be freed with
> ff_ass_free_dialog()
>      +- */
>      +-ASSDialog *ff_ass_split_dialog(ASSSplitContext *ctx, const char
> *buf);
>      +-
>      +-/**
>      +- * Free all the memory allocated for an ASSSplitContext.
>      +- *
>      +- * @param ctx Context previously initialized by ff_ass_split().
>      +- */
>      +-void ff_ass_split_free(ASSSplitContext *ctx);
>      +-
>      +-
>      +-/**
>      +- * Set of callback functions corresponding to each override codes
> that can
>      +- * be encountered in a "Dialogue" Text field.
>      +- */
>      +-typedef struct {
>      +-    /**
>      +-     * @defgroup ass_styles    ASS styles
>      +-     * @{
>      +-     */
>      +-    void (*text)(void *priv, const char *text, int len);
>      +-    void (*new_line)(void *priv, int forced);
>      +-    void (*style)(void *priv, char style, int close);
>      +-    void (*color)(void *priv, unsigned int /* color */, unsigned
> int color_id);
>      +-    void (*alpha)(void *priv, int alpha, int alpha_id);
>      +-    void (*font_name)(void *priv, const char *name);
>      +-    void (*font_size)(void *priv, int size);
>      +-    void (*alignment)(void *priv, int alignment);
>      +-    void (*cancel_overrides)(void *priv, const char *style);
>      +-    /** @} */
>      +-
>      +-    /**
>      +-     * @defgroup ass_functions    ASS functions
>      +-     * @{
>      +-     */
>      +-    void (*move)(void *priv, int x1, int y1, int x2, int y2, int
> t1, int t2);
>      +-    void (*origin)(void *priv, int x, int y);
>      +-    /** @} */
>      +-
>      +-    /**
>      +-     * @defgroup ass_end    end of Dialogue Event
>      +-     * @{
>      +-     */
>      +-    void (*end)(void *priv);
>      +-    /** @} */
>      +-} ASSCodesCallbacks;
>      +-
>      +-/**
>      +- * Split override codes out of a ASS "Dialogue" Text field.
>      +- *
>      +- * @param callbacks Set of callback functions called for each
> override code
>      +- *                  encountered.
>      +- * @param priv Opaque pointer passed to the callback functions.
>      +- * @param buf The ASS "Dialogue" Text field to split.
>      +- * @return >= 0 on success otherwise an error code <0
>      +- */
>      +-int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks,
> void *priv,
>      +-                                const char *buf);
>      +-
>      +-/**
>      +- * Find an ASSStyle structure by its name.
>      +- *
>      +- * @param ctx Context previously initialized by ff_ass_split().
>      +- * @param style name of the style to search for.
>      +- * @return the ASSStyle corresponding to style, or NULL if style
> can't be found
>      +- */
>      +-ASSStyle *ff_ass_style_get(ASSSplitContext *ctx, const char *style);
>      +-
>      +-#endif /* AVCODEC_ASS_SPLIT_H */
>      +
>        ## libavcodec/assdec.c ##
>       @@
>        #include <string.h>
>      @@ libavutil/ass.c: char *ff_ass_get_dialog(int readorder, int layer,
> const char *s
>       -                    const char *speaker)
>       +char *avpriv_ass_get_dialog_ex(int readorder, int layer, const char
> *style,
>       +                        const char *speaker, int margin_l, int
> margin_r,
>      -+                        int margin_v, const char *text)
>      ++                        int margin_v, const char *effect, const
> char *text)
>        {
>       -    return ff_ass_add_rect2(sub, dialog, readorder, layer, style,
> speaker, NULL);
>       -}
>      @@ libavutil/ass.c: char *ff_ass_get_dialog(int readorder, int layer,
> const char *s
>       -    FFASSDecoderContext *s = avctx->priv_data;
>       -    if (!(avctx->flags2 & AV_CODEC_FLAG2_RO_FLUSH_NOOP))
>       -        s->readorder = 0;
>      -+    return av_asprintf("%d,%d,%s,%s,%d,%d,%d,,%s",
>      ++    return av_asprintf("%d,%d,%s,%s,%d,%d,%d,%s,%s",
>       +                       readorder, layer, style ? style : "Default",
>       +                       speaker ? speaker : "", margin_l, margin_r,
>      -+                       margin_v, text);
>      ++                       margin_v, effect ? effect : "", text);
>        }
>
>       -void ff_ass_bprint_text_event(AVBPrint *buf, const char *p, int
> size,
>      @@ libavutil/ass_internal.h (new)
>       + */
>       +char *avpriv_ass_get_dialog_ex(int readorder, int layer, const char
> *style,
>       +                        const char *speaker, int margin_l, int
> margin_r,
>      -+                        int margin_v, const char *text);
>      ++                        int margin_v, const char *effect, const
> char *text);
>       +
>       +/**
>       + * Escape a text subtitle using ASS syntax into an AVBPrint buffer.
>      @@ libavutil/ass_split.c: ASSDialog
> *ff_ass_split_dialog(ASSSplitContext *ctx, cons
>            if (ctx) {
>                int i;
>       @@ libavutil/ass_split.c: void ff_ass_split_free(ASSSplitContext
> *ctx)
>      +     }
>        }
>
>      ++static int ass_remove_empty_braces(AVBPrint* buffer)
>      ++{
>      ++    char* tmp;
>      ++    int ret = 0, n = 0;
>
>       -int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks,
> void *priv,
>      -+int avpriv_ass_split_override_codes(const ASSCodesCallbacks
> *callbacks, void *priv,
>      -                                 const char *buf)
>      +-                                const char *buf)
>      ++    if (buffer == NULL || buffer->len == 0 ||
> !av_bprint_is_complete(buffer))
>      ++        return 0;
>      ++
>      ++    ret = av_bprint_finalize(buffer, &tmp);
>      ++    if (ret)
>      ++        return ret;
>      ++
>      ++    for (unsigned i = 0; i < buffer->len; i++) {
>      ++        if (tmp[i] == '{' && tmp[i+1] == '}')
>      ++            i++;
>      ++        else
>      ++            tmp[n++] = tmp[i];
>      ++    }
>      ++
>      ++    tmp[n++] = '\0';
>      ++
>      ++    av_bprint_init(buffer, n, n);
>      ++    av_bprint_append_data(buffer, tmp, n - 1);
>      ++    av_free(tmp);
>      ++
>      ++    return ret;
>      ++}
>      ++
>      ++static void ass_write_filtered_line(AVBPrint* buffer, const char
> *buf, int len, enum ASSSplitComponents keep_flags, enum ASSSplitComponents
> split_component)
>      ++{
>      ++    if (buffer == NULL || buf == NULL || len == 0)
>      ++        return;
>      ++
>      ++    if (split_component != ASS_SPLIT_ANY && !(keep_flags &
> split_component))
>      ++        return;
>      ++
>      ++
>      ++    av_bprint_append_data(buffer, buf, len - 1);
>      ++}
>      ++
>      ++int avpriv_ass_filter_override_codes(const ASSCodesCallbacks
> *callbacks, void *priv, const char *buf, AVBPrint* outbuffer, enum
> ASSSplitComponents keep_flags)
>        {
>            const char *text = NULL;
>      -@@ libavutil/ass_split.c: int ff_ass_split_override_codes(const
> ASSCodesCallbacks *callbacks, void *priv,
>      +     char new_line[2];
>      +-    int text_len = 0;
>      ++    int text_len = 0, ret = 0;
>      +
>      +     while (buf && *buf) {
>      +-        if (text && callbacks->text &&
>      +-            (sscanf(buf, "\\%1[nN]", new_line) == 1 ||
>      +-             !strncmp(buf, "{\\", 2))) {
>      +-            callbacks->text(priv, text, text_len);
>      ++
>      ++        if (text && (sscanf(buf, "\\%1[nN]", new_line) == 1 ||
> !strncmp(buf, "{\\", 2))) {
>      ++            ass_write_filtered_line(outbuffer, text, text_len + 1,
> keep_flags, ASS_SPLIT_TEXT | ASS_SPLIT_TEXT2);
>      ++
>      ++            if (callbacks->text)
>      ++                callbacks->text(priv, text, text_len);
>      +             text = NULL;
>      +         }
>      ++
>      +         if (sscanf(buf, "\\%1[nN]", new_line) == 1) {
>      +             if (callbacks->new_line)
>      +                 callbacks->new_line(priv, new_line[0] == 'N');
>      ++            ass_write_filtered_line(outbuffer, buf, 3, keep_flags,
> ASS_SPLIT_ANY);
>      +             buf += 2;
>      +         } else if (!strncmp(buf, "{\\", 2)) {
>      ++            ass_write_filtered_line(outbuffer, buf, 2, keep_flags,
> ASS_SPLIT_ANY);
>      +             buf++;
>                    while (*buf == '\\') {
>      -                 char style[2], c[2], sep[2], c_num[2] = "0",
> tmp[128] = {0};
>      +-                char style[2], c[2], sep[2], c_num[2] = "0",
> tmp[128] = {0};
>      ++                char style[4], c[2], axis[3], sep[3], c_num[2] =
> "0", tmp[128] = {0};
>                        unsigned int color = 0xFFFFFFFF;
>       -                int len, size = -1, an = -1, alpha = -1;
>       -                int x1, y1, x2, y2, t1 = -1, t2 = -1;
>       +                int len, size = -1, an = -1, alpha = -1, scale = 0;
>      -+                int x1, y1, x2, y2, t1 = -1, t2 = -1, accel = 1;
>      ++                float f1 = 1;
>      ++                int x1, y1, x2, y2, x3, t1 = -1, t2 = -1, t3 = -1,
> t4 = -1, accel = 1;
>                        if (sscanf(buf, "\\%1[bisu]%1[01\\}]%n", style, c,
> &len) > 1) {
>                            int close = c[0] == '0' ? 1 : c[0] == '1' ? 0 :
> -1;
>                            len += close != -1;
>      -@@ libavutil/ass_split.c: int ff_ass_split_override_codes(const
> ASSCodesCallbacks *callbacks, void *priv,
>      ++                    switch (c[0]) {
>      ++                    case 'b':
>      ++                        ass_write_filtered_line(outbuffer, buf,
> len, keep_flags, ASS_SPLIT_FONT_BOLD);
>      ++                        break;
>      ++                    case 'u':
>      ++                        ass_write_filtered_line(outbuffer, buf,
> len, keep_flags, ASS_SPLIT_FONT_UNDERLINE);
>      ++                        break;
>      ++                    case 'i':
>      ++                        ass_write_filtered_line(outbuffer, buf,
> len, keep_flags, ASS_SPLIT_FONT_ITALIC);
>      ++                        break;
>      ++                    case 'a':
>      ++                        ass_write_filtered_line(outbuffer, buf,
> len, keep_flags, ASS_SPLIT_FONT_STRIKEOUT);
>      ++                        break;
>      ++                    }
>      +                     if (callbacks->style)
>      +                         callbacks->style(priv, style[0], close);
>      +                 } else if (sscanf(buf, "\\c%1[\\}]%n", sep, &len) >
> 0 ||
>      +                            sscanf(buf, "\\c&H%X&%1[\\}]%n", &color,
> sep, &len) > 1 ||
>      +                            sscanf(buf, "\\%1[1234]c%1[\\}]%n",
> c_num, sep, &len) > 1 ||
>      +                            sscanf(buf, "\\%1[1234]c&H%X&%1[\\}]%n",
> c_num, &color, sep, &len) > 2) {
>      ++                    ass_write_filtered_line(outbuffer, buf, len,
> keep_flags, ASS_SPLIT_COLOR);
>      +                     if (callbacks->color)
>      +                         callbacks->color(priv, color, c_num[0] -
> '0');
>      +                 } else if (sscanf(buf, "\\alpha%1[\\}]%n", sep,
> &len) > 0 ||
>      +                            sscanf(buf, "\\alpha&H%2X&%1[\\}]%n",
> &alpha, sep, &len) > 1 ||
>      +                            sscanf(buf, "\\%1[1234]a%1[\\}]%n",
> c_num, sep, &len) > 1 ||
>      +                            sscanf(buf,
> "\\%1[1234]a&H%2X&%1[\\}]%n", c_num, &alpha, sep, &len) > 2) {
>      ++                    ass_write_filtered_line(outbuffer, buf, len,
> keep_flags, ASS_SPLIT_ALPHA);
>      +                     if (callbacks->alpha)
>      +                         callbacks->alpha(priv, alpha, c_num[0] -
> '0');
>      +                 } else if (sscanf(buf, "\\fn%1[\\}]%n", sep, &len)
> > 0 ||
>      +                            sscanf(buf, "\\fn%127[^\\}]%1[\\}]%n",
> tmp, sep, &len) > 1) {
>      ++                    ass_write_filtered_line(outbuffer, buf, len,
> keep_flags, ASS_SPLIT_FONT_NAME);
>      +                     if (callbacks->font_name)
>      +                         callbacks->font_name(priv, tmp[0] ? tmp :
> NULL);
>      +                 } else if (sscanf(buf, "\\fs%1[\\}]%n", sep, &len)
> > 0 ||
>      +                            sscanf(buf, "\\fs%u%1[\\}]%n", &size,
> sep, &len) > 1) {
>      ++                    ass_write_filtered_line(outbuffer, buf, len,
> keep_flags, ASS_SPLIT_FONT_SIZE);
>      +                     if (callbacks->font_size)
>      +                         callbacks->font_size(priv, size);
>      ++                } else if (sscanf(buf, "\\fscx%1[\\}]%n", sep,
> &len) > 0 ||
>      ++                           sscanf(buf, "\\fscx%f%1[\\}]%n", &f1,
> sep, &len) > 1) {
>      ++                    ass_write_filtered_line(outbuffer, buf, len,
> keep_flags, ASS_SPLIT_FONT_SCALE);
>      ++                } else if (sscanf(buf, "\\fscy%1[\\}]%n", sep,
> &len) > 0 ||
>      ++                           sscanf(buf, "\\fscy%f%1[\\}]%n", &f1,
> sep, &len) > 1) {
>      ++                    ass_write_filtered_line(outbuffer, buf, len,
> keep_flags, ASS_SPLIT_FONT_SCALE);
>      ++                } else if (sscanf(buf, "\\fsp%1[\\}]%n", sep, &len)
> > 0 ||
>      ++                           sscanf(buf, "\\fsp%u%1[\\}]%n", &size,
> sep, &len) > 1) {
>      ++                    ass_write_filtered_line(outbuffer, buf, len,
> keep_flags, ASS_SPLIT_FONT_SPACING);
>      ++                } else if (sscanf(buf, "\\fe%1[\\}]%n", sep, &len)
> > 0 ||
>      ++                           sscanf(buf, "\\fe%u%1[\\}]%n", &size,
> sep, &len) > 1) {
>      ++                    ass_write_filtered_line(outbuffer, buf, len,
> keep_flags, ASS_SPLIT_FONT_CHARSET);
>      ++                } else if (sscanf(buf, "\\bord%1[\\}]%n", sep,
> &len) > 0 ||
>      ++                           sscanf(buf, "\\bord%u%1[\\}]%n", &size,
> sep, &len) > 1) {
>      ++                    ass_write_filtered_line(outbuffer, buf, len,
> keep_flags, ASS_SPLIT_TEXT_BORDER);
>      ++                } else if (sscanf(buf, "\\shad%1[\\}]%n", sep,
> &len) > 0 ||
>      ++                           sscanf(buf, "\\shad%u%1[\\}]%n", &size,
> sep, &len) > 1) {
>      ++                    ass_write_filtered_line(outbuffer, buf, len,
> keep_flags, ASS_SPLIT_TEXT_SHADOW);
>      ++                } else if (sscanf(buf, "\\fr%1[\\}]%n", sep, &len)
> > 0 ||
>      ++                           sscanf(buf, "\\fr%u%1[\\}]%n", &x1, sep,
> &len) > 1 ||
>      ++                           sscanf(buf, "\\fr%1[xyz]%1[\\}]%n",
> axis, sep, &len) > 1 ||
>      ++                           sscanf(buf, "\\fr%1[xyz]%u%1[\\}]%n",
> axis, &size, sep, &len) > 2) {
>      ++                    ass_write_filtered_line(outbuffer, buf, len,
> keep_flags, ASS_SPLIT_TEXT_ROTATE);
>      ++                } else if (sscanf(buf, "\\blur%1[\\}]%n", sep,
> &len) > 0 ||
>      ++                           sscanf(buf, "\\blur%u%1[\\}]%n", &size,
> sep, &len) > 1) {
>      ++                    ass_write_filtered_line(outbuffer, buf, len,
> keep_flags, ASS_SPLIT_TEXT_BLUR);
>      ++                } else if (sscanf(buf, "\\be%1[\\}]%n", sep, &len)
> > 0 ||
>      ++                           sscanf(buf, "\\be%u%1[\\}]%n", &size,
> sep, &len) > 1) {
>      ++                    ass_write_filtered_line(outbuffer, buf, len,
> keep_flags, ASS_SPLIT_TEXT_BLUR);
>      ++                } else if (sscanf(buf, "\\q%1[\\}]%n", sep, &len) >
> 0 ||
>      ++                           sscanf(buf, "\\q%u%1[\\}]%n", &size,
> sep, &len) > 1) {
>      ++                    ass_write_filtered_line(outbuffer, buf, len,
> keep_flags, ASS_SPLIT_TEXT_WRAP);
>      +                 } else if (sscanf(buf, "\\a%1[\\}]%n", sep, &len) >
> 0 ||
>      +                            sscanf(buf, "\\a%2u%1[\\}]%n", &an, sep,
> &len) > 1 ||
>      +                            sscanf(buf, "\\an%1[\\}]%n", sep, &len)
> > 0 ||
>      +                            sscanf(buf, "\\an%1u%1[\\}]%n", &an,
> sep, &len) > 1) {
>      +                     if (an != -1 && buf[2] != 'n')
>      +                         an = (an&3) + (an&4 ? 6 : an&8 ? 3 : 0);
>      ++                    ass_write_filtered_line(outbuffer, buf, len,
> keep_flags, ASS_SPLIT_TEXT_ALIGNMENT);
>      +                     if (callbacks->alignment)
>      +                         callbacks->alignment(priv, an);
>      +                 } else if (sscanf(buf, "\\r%1[\\}]%n", sep, &len) >
> 0 ||
>      +                            sscanf(buf, "\\r%127[^\\}]%1[\\}]%n",
> tmp, sep, &len) > 1) {
>      ++                    ass_write_filtered_line(outbuffer, buf, len,
> keep_flags, ASS_SPLIT_CANCELLING);
>      +                     if (callbacks->cancel_overrides)
>      +                         callbacks->cancel_overrides(priv, tmp);
>      +                 } else if (sscanf(buf,
> "\\move(%d,%d,%d,%d)%1[\\}]%n", &x1, &y1, &x2, &y2, sep, &len) > 4 ||
>      +                            sscanf(buf,
> "\\move(%d,%d,%d,%d,%d,%d)%1[\\}]%n", &x1, &y1, &x2, &y2, &t1, &t2, sep,
> &len) > 6) {
>      ++                    ass_write_filtered_line(outbuffer, buf, len,
> keep_flags, ASS_SPLIT_MOVE);
>      +                     if (callbacks->move)
>      +                         callbacks->move(priv, x1, y1, x2, y2, t1,
> t2);
>      +                 } else if (sscanf(buf, "\\pos(%d,%d)%1[\\}]%n",
> &x1, &y1, sep, &len) > 2) {
>      ++                    ass_write_filtered_line(outbuffer, buf, len,
> keep_flags, ASS_SPLIT_POS);
>      +                     if (callbacks->move)
>      +                         callbacks->move(priv, x1, y1, x1, y1, -1,
> -1);
>                        } else if (sscanf(buf, "\\org(%d,%d)%1[\\}]%n",
> &x1, &y1, sep, &len) > 2) {
>      ++                    ass_write_filtered_line(outbuffer, buf, len,
> keep_flags, ASS_SPLIT_ORIGIN);
>                            if (callbacks->origin)
>                                callbacks->origin(priv, x1, y1);
>      -+                } else if (sscanf(buf, "\\t(%d,%d,%1[\\}]%n", &t1,
> &t2, sep, &len) > 2 ||
>      ++                } else if (sscanf(buf, "\\t(%1[\\}]%n", sep, &len)
> > 0 ||
>      ++                           sscanf(buf, "\\t(%d,%d,%1[\\}]%n", &t1,
> &t2, sep, &len) > 2 ||
>       +                           sscanf(buf, "\\t(%d,%d,%d,%1[\\}]%n",
> &t1, &t2, &accel, sep, &len) > 3) {
>      ++
>      ++                    len = strcspn(buf, ")") + 2;
>      ++
>      ++                    ass_write_filtered_line(outbuffer, buf, len,
> keep_flags, ASS_SPLIT_ANIMATE);
>       +                    if (callbacks->animate)
>       +                        callbacks->animate(priv, t1, t2, accel,
> tmp);
>      ++                } else if (sscanf(buf,
> "\\fade(%d,%d,%d,%d,%d,%d,%d)%1[\\}]%n", &x1, &x2, &x3, &t1, &t2, &t3, &t4,
> sep, &len) > 7) {
>      ++                    ass_write_filtered_line(outbuffer, buf, len,
> keep_flags, ASS_SPLIT_FADE);
>      ++                } else if (sscanf(buf, "\\fad(%d,%d)%1[\\}]%n",
> &t1, &t2, sep, &len) > 2) {
>      ++                    ass_write_filtered_line(outbuffer, buf, len,
> keep_flags, ASS_SPLIT_FADE);
>      ++                } else if (sscanf(buf,
> "\\clip(%d,%d,%d,%d)%1[\\}]%n", &x1, &y1, &x2, &y2, sep, &len) > 4) {
>      ++                    ass_write_filtered_line(outbuffer, buf, len,
> keep_flags, ASS_SPLIT_CLIP);
>       +                } else if (sscanf(buf, "\\p%1[\\}]%n", sep, &len) >
> 0 ||
>       +                           sscanf(buf, "\\p%u%1[\\}]%n", &scale,
> sep, &len) > 1) {
>      ++                    ass_write_filtered_line(outbuffer, buf, len,
> keep_flags, ASS_SPLIT_DRAW);
>       +                    if (callbacks->drawing_mode)
>       +                        callbacks->drawing_mode(priv, scale);
>                        } else {
>      -                     len = strcspn(buf+1, "\\}") + 2;  /* skip
> unknown code */
>      -                 }
>      +-                    len = strcspn(buf+1, "\\}") + 2;  /* skip
> unknown code */
>      +-                }
>      ++                    len = strcspn(buf+1, "\\}") + 2;  /* unknown
> code */
>      ++                    ass_write_filtered_line(outbuffer, buf, len,
> keep_flags, ASS_SPLIT_UNKNOWN);
>      ++             }
>      +                 buf += len - 1;
>      +             }
>      +             if (*buf++ != '}')
>      +                 return AVERROR_INVALIDDATA;
>      +-        } else {
>      ++
>      ++            ass_write_filtered_line(outbuffer, "}", 2, keep_flags,
> ASS_SPLIT_ANY);
>      ++     } else {
>      +             if (!text) {
>      +                 text = buf;
>      +                 text_len = 1;
>       @@ libavutil/ass_split.c: int ff_ass_split_override_codes(const
> ASSCodesCallbacks *callbacks, void *priv,
>      -     return 0;
>      +             buf++;
>      +         }
>      +     }
>      ++    if (text)
>      ++        ass_write_filtered_line(outbuffer, text, text_len + 1,
> keep_flags, ASS_SPLIT_TEXT | ASS_SPLIT_TEXT2);
>      +     if (text && callbacks->text)
>      +         callbacks->text(priv, text, text_len);
>      +     if (callbacks->end)
>      +         callbacks->end(priv);
>      +-    return 0;
>      ++
>      ++    if (outbuffer)
>      ++        ret = ass_remove_empty_braces(outbuffer);
>      ++
>      ++    return ret;
>      ++}
>      ++
>      ++
>      ++int avpriv_ass_split_override_codes(const ASSCodesCallbacks
> *callbacks, void *priv,
>      ++                                const char *buf)
>      ++{
>      ++    return avpriv_ass_filter_override_codes(callbacks, priv, buf,
> NULL, 0);
>        }
>
>       -ASSStyle *ff_ass_style_get(ASSSplitContext *ctx, const char *style)
>      @@ libavutil/ass_split.c: int ff_ass_split_override_codes(const
> ASSCodesCallbacks *
>            ASS *ass = &ctx->ass;
>            int i;
>
>      - ## libavcodec/ass_split.h => libavutil/ass_split_internal.h ##
>      + ## libavutil/ass_split_internal.h (new) ##
>       @@
>      -  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> 02110-1301 USA
>      -  */
>      -
>      --#ifndef AVCODEC_ASS_SPLIT_H
>      --#define AVCODEC_ASS_SPLIT_H
>      ++/*
>      ++ * SSA/ASS spliting functions
>      ++ * Copyright (c) 2010  Aurelien Jacobs <aurel@gnuage.org>
>      ++ *
>      ++ * 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
>      ++ */
>      ++
>       +#ifndef AVUTIL_ASS_SPLIT_INTERNAL_H
>       +#define AVUTIL_ASS_SPLIT_INTERNAL_H
>      -
>      - /**
>      -  * fields extracted from the [Script Info] section
>      -@@ libavutil/ass_split_internal.h: typedef struct {
>      -     char *effect;
>      -     char *text;     /**< actual text which will be displayed as a
> subtitle,
>      -                          can include style override control codes
> (see
>      --                         ff_ass_split_override_codes()) */
>      -+                         avpriv_ass_split_override_codes()) */
>      - } ASSDialog;
>      -
>      - /**
>      -@@ libavutil/ass_split_internal.h: typedef struct ASSSplitContext
> ASSSplitContext;
>      -  * @param buf String containing the ASS formatted data.
>      -  * @return Newly allocated struct containing split data.
>      -  */
>      --ASSSplitContext *ff_ass_split(const char *buf);
>      ++
>      ++#include "bprint.h"
>      ++
>      ++enum ASSSplitComponents
>      ++{
>      ++    ASS_SPLIT_ANY = 0,
>      ++    ASS_SPLIT_TEXT           = (1 << 0),
>      ++    ASS_SPLIT_TEXT2          = (1 << 1), // Same semantics as
> ASS_SPLIT_TEXT. To work around help output default display.
>      ++    ASS_SPLIT_COLOR          = (1 << 2),
>      ++    ASS_SPLIT_ALPHA          = (1 << 3),
>      ++    ASS_SPLIT_FONT_NAME      = (1 << 4),
>      ++    ASS_SPLIT_FONT_SIZE      = (1 << 5),
>      ++    ASS_SPLIT_FONT_SCALE     = (1 << 6),
>      ++    ASS_SPLIT_FONT_SPACING   = (1 << 7),
>      ++    ASS_SPLIT_FONT_CHARSET   = (1 << 8),
>      ++    ASS_SPLIT_FONT_BOLD      = (1 << 9),
>      ++    ASS_SPLIT_FONT_ITALIC    = (1 << 10),
>      ++    ASS_SPLIT_FONT_UNDERLINE = (1 << 11),
>      ++    ASS_SPLIT_FONT_STRIKEOUT = (1 << 12),
>      ++    ASS_SPLIT_TEXT_BORDER    = (1 << 13),
>      ++    ASS_SPLIT_TEXT_SHADOW    = (1 << 14),
>      ++    ASS_SPLIT_TEXT_ROTATE    = (1 << 15),
>      ++    ASS_SPLIT_TEXT_BLUR      = (1 << 16),
>      ++    ASS_SPLIT_TEXT_WRAP      = (1 << 17),
>      ++    ASS_SPLIT_TEXT_ALIGNMENT = (1 << 18),
>      ++    ASS_SPLIT_CANCELLING     = (1 << 19),
>      ++    ASS_SPLIT_MOVE           = (1 << 20),
>      ++    ASS_SPLIT_POS            = (1 << 21),
>      ++    ASS_SPLIT_ORIGIN         = (1 << 22),
>      ++    ASS_SPLIT_DRAW           = (1 << 23),
>      ++    ASS_SPLIT_ANIMATE        = (1 << 24),
>      ++    ASS_SPLIT_FADE           = (1 << 25),
>      ++    ASS_SPLIT_CLIP           = (1 << 26),
>      ++    ASS_SPLIT_UNKNOWN        = (1 << 27),
>      ++
>      ++    ASS_SPLIT_BASIC =  ASS_SPLIT_TEXT2 | ASS_SPLIT_COLOR |
> ASS_SPLIT_ALPHA | ASS_SPLIT_FONT_NAME | ASS_SPLIT_FONT_SIZE |
> ASS_SPLIT_FONT_SCALE | ASS_SPLIT_FONT_SPACING | ASS_SPLIT_FONT_CHARSET |
> ASS_SPLIT_FONT_BOLD | ASS_SPLIT_FONT_ITALIC | ASS_SPLIT_FONT_UNDERLINE |
> ASS_SPLIT_FONT_STRIKEOUT | ASS_SPLIT_TEXT_BORDER | ASS_SPLIT_TEXT_SHADOW |
> ASS_SPLIT_TEXT_WRAP | ASS_SPLIT_TEXT_ALIGNMENT | ASS_SPLIT_POS |
> ASS_SPLIT_CANCELLING,
>      ++    ASS_SPLIT_ALL_KNOWN =  ASS_SPLIT_TEXT2 | ASS_SPLIT_COLOR |
> ASS_SPLIT_ALPHA | ASS_SPLIT_FONT_NAME | ASS_SPLIT_FONT_SIZE |
> ASS_SPLIT_FONT_SCALE | ASS_SPLIT_FONT_SPACING | ASS_SPLIT_FONT_CHARSET |
> ASS_SPLIT_FONT_BOLD | ASS_SPLIT_FONT_ITALIC | ASS_SPLIT_FONT_UNDERLINE |
> ASS_SPLIT_FONT_STRIKEOUT | ASS_SPLIT_TEXT_BORDER | ASS_SPLIT_TEXT_SHADOW |
> ASS_SPLIT_TEXT_ROTATE | ASS_SPLIT_TEXT_BLUR | ASS_SPLIT_TEXT_WRAP |
> ASS_SPLIT_TEXT_ALIGNMENT | ASS_SPLIT_CANCELLING | ASS_SPLIT_POS |
> ASS_SPLIT_MOVE | ASS_SPLIT_ORIGIN | ASS_SPLIT_DRAW | ASS_SPLIT_ANIMATE |
> ASS_SPLIT_FADE | ASS_SPLIT_CLIP,
>      ++};
>      ++
>      ++    /**
>      ++     * fields extracted from the [Script Info] section
>      ++     */
>      ++    typedef struct {
>      ++      char *script_type; /**< SSA script format version (eg. v4.00)
> */
>      ++  char *collisions;  /**< how subtitles are moved to prevent
> collisions */
>      ++  int play_res_x;    /**< video width that ASS coords are referring
> to */
>      ++  int play_res_y;    /**< video height that ASS coords are
> referring to */
>      ++  float timer;       /**< time multiplier to apply to SSA clock (in
> %) */
>      ++} ASSScriptInfo;
>      ++
>      ++/**
>      ++ * fields extracted from the [V4(+) Styles] section
>      ++ */
>      ++typedef struct {
>      ++  char *name;        /**< name of the tyle (case sensitive) */
>      ++  char *font_name;   /**< font face (case sensitive) */
>      ++  int font_size;     /**< font height */
>      ++  int primary_color; /**< color that a subtitle will normally
> appear in */
>      ++  int secondary_color;
>      ++  int outline_color; /**< color for outline in ASS, called tertiary
> in SSA */
>      ++  int back_color;    /**< color of the subtitle outline or shadow */
>      ++  int bold;          /**< whether text is bold (1) or not (0) */
>      ++  int italic;        /**< whether text is italic (1) or not (0) */
>      ++  int underline;     /**< whether text is underlined (1) or not (0)
> */
>      ++  int strikeout;
>      ++  float scalex;
>      ++  float scaley;
>      ++  float spacing;
>      ++  float angle;
>      ++  int border_style;
>      ++  float outline;
>      ++  float shadow;
>      ++  int alignment; /**< position of the text (left, center, top...),
>      ++                      defined after the layout of the numpad
>      ++                      (1-3 sub, 4-6 mid, 7-9 top) */
>      ++  int margin_l;
>      ++  int margin_r;
>      ++  int margin_v;
>      ++  int alpha_level;
>      ++  int encoding;
>      ++} ASSStyle;
>      ++
>      ++/**
>      ++ * fields extracted from the [Events] section
>      ++ */
>      ++typedef struct {
>      ++  int readorder;
>      ++  int layer;   /**< higher numbered layers are drawn over lower
> numbered */
>      ++  int start;   /**< start time of the dialog in centiseconds */
>      ++  int end;     /**< end time of the dialog in centiseconds */
>      ++  char *style; /**< name of the ASSStyle to use with this dialog */
>      ++  char *name;
>      ++  int margin_l;
>      ++  int margin_r;
>      ++  int margin_v;
>      ++  char *effect;
>      ++  char *text; /**< actual text which will be displayed as a
> subtitle,
>      ++                   can include style override control codes (see
>      ++                   avpriv_ass_split_override_codes()) */
>      ++} ASSDialog;
>      ++
>      ++/**
>      ++ * structure containing the whole split ASS data
>      ++ */
>      ++typedef struct {
>      ++  ASSScriptInfo script_info; /**< general information about the SSA
> script*/
>      ++  ASSStyle *styles;          /**< array of split out styles */
>      ++  int styles_count;          /**< number of ASSStyle in the styles
> array */
>      ++  ASSDialog *dialogs;        /**< array of split out dialogs */
>      ++  int dialogs_count;         /**< number of ASSDialog in the
> dialogs array*/
>      ++} ASS;
>      ++
>      ++/**
>      ++ * This struct can be casted to ASS to access to the split data.
>      ++ */
>      ++typedef struct ASSSplitContext ASSSplitContext;
>      ++
>      ++/**
>      ++ * Split a full ASS file or a ASS header from a string buffer and
> store
>      ++ * the split structure in a newly allocated context.
>      ++ *
>      ++ * @param buf String containing the ASS formatted data.
>      ++ * @return Newly allocated struct containing split data.
>      ++ */
>       +ASSSplitContext *avpriv_ass_split(const char *buf);
>      -
>      - /**
>      -- * Free a dialogue obtained from ff_ass_split_dialog().
>      ++
>      ++/**
>       + * Free a dialogue obtained from avpriv_ass_split_dialog().
>      -  */
>      --void ff_ass_free_dialog(ASSDialog **dialogp);
>      ++ */
>       +void avpriv_ass_free_dialog(ASSDialog **dialogp);
>      -
>      - /**
>      -  * Split one ASS Dialogue line from a string buffer.
>      -@@ libavutil/ass_split_internal.h: void ff_ass_free_dialog(ASSDialog
> **dialogp);
>      -  * @param buf String containing the ASS "Dialogue" line.
>      -  * @return Pointer to the split ASSDialog. Must be freed with
> ff_ass_free_dialog()
>      -  */
>      --ASSDialog *ff_ass_split_dialog(ASSSplitContext *ctx, const char
> *buf);
>      ++
>      ++/**
>      ++ * Split one ASS Dialogue line from a string buffer.
>      ++ *
>      ++ * @param ctx Context previously initialized by ff_ass_split().
>      ++ * @param buf String containing the ASS "Dialogue" line.
>      ++ * @return Pointer to the split ASSDialog. Must be freed with
>      ++ * ff_ass_free_dialog()
>      ++ */
>       +ASSDialog *avpriv_ass_split_dialog(ASSSplitContext *ctx, const char
> *buf);
>      -
>      - /**
>      -  * Free all the memory allocated for an ASSSplitContext.
>      -  *
>      -  * @param ctx Context previously initialized by ff_ass_split().
>      -  */
>      --void ff_ass_split_free(ASSSplitContext *ctx);
>      ++
>      ++/**
>      ++ * Free all the memory allocated for an ASSSplitContext.
>      ++ *
>      ++ * @param ctx Context previously initialized by ff_ass_split().
>      ++ */
>       +void avpriv_ass_split_free(ASSSplitContext *ctx);
>      -
>      -
>      - /**
>      -@@ libavutil/ass_split_internal.h: typedef struct {
>      -      * @{
>      -      */
>      -     void (*text)(void *priv, const char *text, int len);
>      -+    void (*hard_space)(void *priv);
>      -     void (*new_line)(void *priv, int forced);
>      -     void (*style)(void *priv, char style, int close);
>      -     void (*color)(void *priv, unsigned int /* color */, unsigned
> int color_id);
>      -@@ libavutil/ass_split_internal.h: typedef struct {
>      -      * @{
>      -      */
>      -     void (*move)(void *priv, int x1, int y1, int x2, int y2, int
> t1, int t2);
>      -+    void (*animate)(void *priv, int t1, int t2, int accel, char
> *style);
>      -     void (*origin)(void *priv, int x, int y);
>      -+    void (*drawing_mode)(void *priv, int scale);
>      -+    /** @} */
>       +
>      -+    /**
>      -+     * @defgroup ass_ext    ASS extensible parsing callback
>      -+     * @{
>      -+     */
>      -+    void (*ext)(void *priv, int ext_id, const char *text, int p1,
> int p2);
>      -     /** @} */
>      -
>      -     /**
>      -@@ libavutil/ass_split_internal.h: typedef struct {
>      -  * @param buf The ASS "Dialogue" Text field to split.
>      -  * @return >= 0 on success otherwise an error code <0
>      -  */
>      --int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks,
> void *priv,
>      -+int avpriv_ass_split_override_codes(const ASSCodesCallbacks
> *callbacks, void *priv,
>      -                                 const char *buf);
>      -
>      - /**
>      -@@ libavutil/ass_split_internal.h: int
> ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
>      -  * @param style name of the style to search for.
>      -  * @return the ASSStyle corresponding to style, or NULL if style
> can't be found
>      -  */
>      --ASSStyle *ff_ass_style_get(ASSSplitContext *ctx, const char *style);
>      ++/**
>      ++ * Set of callback functions corresponding to each override codes
> that can
>      ++ * be encountered in a "Dialogue" Text field.
>      ++ */
>      ++typedef struct {
>      ++  /**
>      ++   * @defgroup ass_styles    ASS styles
>      ++   * @{
>      ++   */
>      ++  void (*text)(void *priv, const char *text, int len);
>      ++  void (*hard_space)(void *priv);
>      ++  void (*new_line)(void *priv, int forced);
>      ++  void (*style)(void *priv, char style, int close);
>      ++  void (*color)(void *priv, unsigned int /* color */, unsigned int
> color_id);
>      ++  void (*alpha)(void *priv, int alpha, int alpha_id);
>      ++  void (*font_name)(void *priv, const char *name);
>      ++  void (*font_size)(void *priv, int size);
>      ++  void (*alignment)(void *priv, int alignment);
>      ++  void (*cancel_overrides)(void *priv, const char *style);
>      ++  /** @} */
>      ++
>      ++  /**
>      ++   * @defgroup ass_functions    ASS functions
>      ++   * @{
>      ++   */
>      ++  void (*move)(void *priv, int x1, int y1, int x2, int y2, int t1,
> int t2);
>      ++  void (*animate)(void *priv, int t1, int t2, int accel, char
> *style);
>      ++  void (*origin)(void *priv, int x, int y);
>      ++  void (*drawing_mode)(void *priv, int scale);
>      ++  /** @} */
>      ++
>      ++  /**
>      ++   * @defgroup ass_ext    ASS extensible parsing callback
>      ++   * @{
>      ++   */
>      ++  void (*ext)(void *priv, int ext_id, const char *text, int p1, int
> p2);
>      ++  /** @} */
>      ++
>      ++  /**
>      ++   * @defgroup ass_end    end of Dialogue Event
>      ++   * @{
>      ++   */
>      ++  void (*end)(void *priv);
>      ++  /** @} */
>      ++} ASSCodesCallbacks;
>      ++
>      ++/**
>      ++ * Split override codes out of a ASS "Dialogue" Text field.
>      ++ *
>      ++ * @param callbacks Set of callback functions called for each
> override code
>      ++ *                  encountered.
>      ++ * @param priv Opaque pointer passed to the callback functions.
>      ++ * @param buf The ASS "Dialogue" Text field to split.
>      ++ * @param outbuffer The output buffer.
>      ++ * @param keep_flags Flags for filtering ass codes.
>      ++ * @return >= 0 on success otherwise an error code <0
>      ++ */
>      ++int avpriv_ass_filter_override_codes(const ASSCodesCallbacks
> *callbacks,
>      ++                                     void *priv, const char *buf,
>      ++                                     AVBPrint *outbuffer, enum
> ASSSplitComponents keep_flags);
>      ++
>      ++/**
>      ++ * Split override codes out of a ASS "Dialogue" Text field.
>      ++ *
>      ++ * @param callbacks Set of callback functions called for each
> override code
>      ++ *                  encountered.
>      ++ * @param priv Opaque pointer passed to the callback functions.
>      ++ * @param buf The ASS "Dialogue" Text field to split.
>      ++ * @return >= 0 on success otherwise an error code <0
>      ++ */
>      ++int avpriv_ass_split_override_codes(const ASSCodesCallbacks
> *callbacks,
>      ++                                    void *priv, const char *buf);
>      ++
>      ++/**
>      ++ * Find an ASSStyle structure by its name.
>      ++ *
>      ++ * @param ctx Context previously initialized by ff_ass_split().
>      ++ * @param style name of the style to search for.
>      ++ * @return the ASSStyle corresponding to style, or NULL if style
> can't be found
>      ++ */
>       +ASSStyle *avpriv_ass_style_get(ASSSplitContext *ctx, const char
> *style);
>      -
>      --#endif /* AVCODEC_ASS_SPLIT_H */
>      ++
>       +#endif /* AVUTIL_ASS_SPLIT_INTERNAL_H */
>   7:  09d8cf7880 =  7:  98f12ad7e9 avcodec/subtitles: Replace deprecated
> enum values
>   8:  897299bf7f =  8:  12c8a308d3 fftools/play,probe: Adjust for subtitle
> changes
>   9:  ca580c6d21 =  9:  2e55dbe180 avfilter/subtitles: Add subtitles.c for
> subtitle frame allocation
>  10:  0781e974a2 = 10:  c931041103 avfilter/avfilter: Handle subtitle
> frames
>  11:  d9d9f42558 = 11:  36cab55ff2 avfilter/avfilter: Fix hardcoded input
> index
>  12:  af69a4b321 = 12:  f41070479c avfilter/sbuffer: Add sbuffersrc and
> sbuffersink filters
>  13:  f7e5b590a2 ! 13:  9bfaba4ace avfilter/overlaygraphicsubs: Add
> overlaygraphicsubs and graphicsub2video filters
>      @@ libavfilter/allfilters.c: extern const AVFilter ff_vf_overlay_qsv;
>        extern const AVFilter ff_vf_owdenoise;
>        extern const AVFilter ff_vf_pad;
>        extern const AVFilter ff_vf_pad_opencl;
>      -@@ libavfilter/allfilters.c: extern const AVFilter ff_avf_showvolume;
>      +@@ libavfilter/allfilters.c: extern const AVFilter
> ff_avf_showspectrumpic;
>      + 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_svf_graphicsub2video;
>      + extern const AVFilter ff_vaf_spectrumsynth;
>
>        /* multimedia sources */
>      - extern const AVFilter ff_avsrc_avsynctest;
>
>        ## libavfilter/vf_overlaygraphicsubs.c (new) ##
>       @@
>  14:  4c8092357f ! 14:  918fd9aaf5 avfilter/overlaytextsubs: Add
> overlaytextsubs and textsubs2video filters
>      @@ libavfilter/allfilters.c: extern const AVFilter
> ff_vf_overlay_vaapi;
>        extern const AVFilter ff_vf_owdenoise;
>        extern const AVFilter ff_vf_pad;
>        extern const AVFilter ff_vf_pad_opencl;
>      -@@ libavfilter/allfilters.c: extern const AVFilter ff_avf_showwaves;
>      +@@ libavfilter/allfilters.c: 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_svf_graphicsub2video;
>       +extern const AVFilter ff_svf_textsub2video;
>      + extern const AVFilter ff_vaf_spectrumsynth;
>
>        /* multimedia sources */
>      - extern const AVFilter ff_avsrc_avsynctest;
>
>        ## libavfilter/vf_overlaytextsubs.c (new) ##
>       @@
>  15:  8fdbdf7c5f ! 15:  a361ad35c5 avfilter/textmod: Add textmod, censor
> and show_speaker filters
>      @@ libavfilter/Makefile: OBJS-$(CONFIG_YUVTESTSRC_FILTER)
>  += vsrc_tests
>        OBJS-$(CONFIG_ADRAWGRAPH_FILTER)             += f_drawgraph.o
>
>        ## libavfilter/allfilters.c ##
>      -@@ libavfilter/allfilters.c: extern const AVFilter ff_avf_showvolume;
>      - extern const AVFilter ff_avf_showwaves;
>      - extern const AVFilter ff_avf_showwavespic;
>      - extern const AVFilter ff_vaf_spectrumsynth;
>      +@@ libavfilter/allfilters.c: extern const AVFilter
> ff_avsrc_avsynctest;
>      + extern const AVFilter ff_avsrc_amovie;
>      + extern const AVFilter ff_avsrc_movie;
>      +
>      ++/* subtitle filters */
>       +extern const AVFilter ff_sf_censor;
>       +extern const AVFilter ff_sf_showspeaker;
>       +extern const AVFilter ff_sf_textmod;
>      - extern const AVFilter ff_svf_graphicsub2video;
>      - extern const AVFilter ff_svf_textsub2video;
>      -
>      ++
>      + /* those filters are part of public or internal API,
>      +  * they are formatted to not be found by the grep
>      +  * as they are manually added again (due to their 'names'
>
>        ## libavfilter/sf_textmod.c (new) ##
>       @@
>      @@ libavfilter/sf_textmod.c (new)
>       +
>       +    av_bprint_finalize(&pbuf, &text);
>       +
>      -+    result = avpriv_ass_get_dialog_ex(dialog->readorder,
> dialog->layer, dialog->style, dialog->name, dialog->margin_l,
> dialog->margin_r, dialog->margin_v, text);
>      ++    result = avpriv_ass_get_dialog_ex(dialog->readorder,
> dialog->layer, dialog->style, dialog->name, dialog->margin_l,
> dialog->margin_r, dialog->margin_v, dialog->effect, text);
>       +
>       +    av_free(text);
>       +    avpriv_ass_free_dialog(&dialog);
>      @@ libavfilter/sf_textmod.c (new)
>       +    if (!text)
>       +        return NULL;
>       +
>      -+    result = avpriv_ass_get_dialog_ex(dialog->readorder,
> dialog->layer, dialog->style, dialog->name, dialog->margin_l,
> dialog->margin_r, dialog->margin_v, text);
>      ++    result = avpriv_ass_get_dialog_ex(dialog->readorder,
> dialog->layer, dialog->style, dialog->name, dialog->margin_l,
> dialog->margin_r, dialog->margin_v, dialog->effect, text);
>       +
>       +    av_free(text);
>       +    avpriv_ass_free_dialog(&dialog);
>  16:  d44b22f15b ! 16:  bca90ebc3e avfilter/stripstyles: Add stripstyles
> filter
>      @@ doc/filters.texi: ffmpeg -i "
> http://streams.videolan.org/samples/sub/SSA/subtitl
>
>        ## libavfilter/Makefile ##
>       @@ libavfilter/Makefile: OBJS-$(CONFIG_NULLSINK_FILTER)
>  += vsink_nullsink.o
>      + # subtitle filters
>        OBJS-$(CONFIG_CENSOR_FILTER)                 += sf_textmod.o
>        OBJS-$(CONFIG_SHOW_SPEAKER_FILTER)           += sf_textmod.o
>      - OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
>       +OBJS-$(CONFIG_STRIPSTYLES_FILTER)            += sf_stripstyles.o
>      + OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
>
>        # multimedia filters
>      - OBJS-$(CONFIG_ABITSCOPE_FILTER)              += avf_abitscope.o
>
>        ## libavfilter/allfilters.c ##
>      -@@ libavfilter/allfilters.c: extern const AVFilter
> ff_avf_showwavespic;
>      - extern const AVFilter ff_vaf_spectrumsynth;
>      +@@ libavfilter/allfilters.c: extern const AVFilter ff_avsrc_movie;
>      + /* subtitle filters */
>        extern const AVFilter ff_sf_censor;
>        extern const AVFilter ff_sf_showspeaker;
>       +extern const AVFilter ff_sf_stripstyles;
>        extern const AVFilter ff_sf_textmod;
>      - extern const AVFilter ff_svf_graphicsub2video;
>      - extern const AVFilter ff_svf_textsub2video;
>      +
>      + /* those filters are part of public or internal API,
>
>        ## libavfilter/sf_stripstyles.c (new) ##
>       @@
>      @@ libavfilter/sf_stripstyles.c (new)
>       +
>       +#include "libavutil/opt.h"
>       +#include "internal.h"
>      ++#include "libavutil/ass_internal.h"
>       +#include "libavutil/ass_split_internal.h"
>       +#include "libavutil/bprint.h"
>       +
>      @@ libavfilter/sf_stripstyles.c (new)
>       +    const AVClass *class;
>       +    enum AVSubtitleType format;
>       +    int remove_animated;
>      ++    enum ASSSplitComponents keep_flags;
>       +    int select_layer;
>       +} StripStylesContext;
>       +
>      @@ libavfilter/sf_stripstyles.c (new)
>       +    AVBPrint buffer;
>       +    int drawing_scale;
>       +    int is_animated;
>      ++    int plain_text_length;
>       +} DialogContext;
>       +
>       +static void dialog_text_cb(void *priv, const char *text, int len)
>      @@ libavfilter/sf_stripstyles.c (new)
>       +    av_log(s->ss_ctx, AV_LOG_DEBUG, "dialog_text_cb: %s\n", text);
>       +
>       +    if (!s->drawing_scale && (!s->is_animated ||
> !s->ss_ctx->remove_animated))
>      -+        av_bprint_append_data(&s->buffer, text, len);
>      ++        s->plain_text_length += len;
>      ++        ////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);
>      ++        s->plain_text_length += 2;
>      ++        ////av_bprint_append_data(&s->buffer, forced ? "\\N" :
> "\\n", 2);
>       +}
>       +
>       +static void dialog_drawing_mode_cb(void *priv, int scale)
>      @@ libavfilter/sf_stripstyles.c (new)
>       +    .move             = dialog_move_cb,
>       +};
>       +
>      -+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)
>       +{
>       +    DialogContext dlg_ctx = { .ss_ctx = s };
>      @@ libavfilter/sf_stripstyles.c (new)
>       +
>       +    dlg_ctx.ss_ctx = s;
>       +
>      -+    av_bprint_init(&dlg_ctx.buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
>      ++    av_bprint_init(&dlg_ctx.buffer, 512, AV_BPRINT_SIZE_UNLIMITED);
>       +
>      -+    avpriv_ass_split_override_codes(&dialog_callbacks, &dlg_ctx,
> dialog->text);
>      ++    avpriv_ass_filter_override_codes(&dialog_callbacks, &dlg_ctx,
> dialog->text, &dlg_ctx.buffer, s->keep_flags);
>       +
>      -+    if (av_bprint_is_complete(&dlg_ctx.buffer)
>      -+        && dlg_ctx.buffer.len > 0)
>      -+        result = ass_get_line(dialog->readorder, dialog->layer,
> dialog->style, dialog->name, dialog->effect, dlg_ctx.buffer.str);
>      ++    if (av_bprint_is_complete(&dlg_ctx.buffer) &&
> dlg_ctx.buffer.len > 0 && dlg_ctx.plain_text_length > 0)
>      ++        result = avpriv_ass_get_dialog_ex(dialog->readorder,
> dialog->layer, dialog->style, dialog->name, dialog->margin_l,
> dialog->margin_r, dialog->margin_v, dialog->effect, dlg_ctx.buffer.str);
>       +
>       +    av_bprint_finalize(&dlg_ctx.buffer, NULL);
>       +    avpriv_ass_free_dialog(&dialog);
>      @@ libavfilter/sf_stripstyles.c (new)
>       +            area->ass = process_dialog(s, area->ass);
>       +
>       +            if (area->ass) {
>      -+                av_log(inlink->dst, AV_LOG_INFO, "original: %d
> %s\n", i, tmp);
>      -+                av_log(inlink->dst, AV_LOG_INFO, "stripped: %d
> %s\n", i, area->ass);
>      ++                av_log(inlink->dst, AV_LOG_DEBUG, "original: %d
> %s\n", i, tmp);
>      ++                av_log(inlink->dst, AV_LOG_DEBUG, "stripped: %d
> %s\n", i, area->ass);
>       +            }
>       +            else
>       +                area->ass = NULL;
>      @@ libavfilter/sf_stripstyles.c (new)
>       +#define FLAGS (AV_OPT_FLAG_SUBTITLE_PARAM |
> AV_OPT_FLAG_FILTERING_PARAM)
>       +
>       +static const AVOption stripstyles_options[] = {
>      ++    { "keep_flags", "flags to control which override codes to
> keep", OFFSET(keep_flags), AV_OPT_TYPE_FLAGS, { .i64 = ASS_SPLIT_TEXT },
> .flags = FLAGS, .unit = "keepflags" },
>      ++        { "basic",          "keep static style tags only",
>    .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_BASIC          },
> .flags=FLAGS, .unit = "keepflags" },
>      ++        { "all_known",      "keep all known tags",
>    .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_ALL_KNOWN      },
> .flags=FLAGS, .unit = "keepflags" },
>      ++        { "text",           "keep text content",
>    .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_TEXT           },
> .flags=FLAGS, .unit = "keepflags" },
>      ++        { "color",          "keep color tags (\\c, \\<n>c)",
>    .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_COLOR          },
> .flags=FLAGS, .unit = "keepflags" },
>      ++        { "alpha",          "keep color alpha tags (\\alpha,
> \\<n>a)", .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_ALPHA          },
> .flags=FLAGS, .unit = "keepflags" },
>      ++        { "font_name",      "keep font name tags (\\fn)",
>     .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_NAME      },
> .flags=FLAGS, .unit = "keepflags" },
>      ++        { "font_size",      "keep font size tags (\\fs)",
>     .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_SIZE      },
> .flags=FLAGS, .unit = "keepflags" },
>      ++        { "font_scale",     "keep font scale tags (\\fscx,
> \\fscy)",   .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_SCALE
>  },  .flags=FLAGS, .unit = "keepflags" },
>      ++        { "font_spacing",   "keep font spacing tags (\\fsp)",
>     .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_SPACING   },
> .flags=FLAGS, .unit = "keepflags" },
>      ++        { "font_charset",   "keep font charset tags (\\fe)",
>    .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_CHARSET   },
> .flags=FLAGS, .unit = "keepflags" },
>      ++        { "font_bold",      "keep font bold tags (\\b)",
>    .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_BOLD      },
> .flags=FLAGS, .unit = "keepflags" },
>      ++        { "font_italic",    "keep font italic tags (\\i)",
>    .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_ITALIC    },
> .flags=FLAGS, .unit = "keepflags" },
>      ++        { "font_underline", "keep font underline tags (\\u)",
>     .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_UNDERLINE },
> .flags=FLAGS, .unit = "keepflags" },
>      ++        { "font_strikeout", "keep font strikeout tags (\\s)",
>     .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_STRIKEOUT },
> .flags=FLAGS, .unit = "keepflags" },
>      ++        { "text_border",    "keep text border tags (\\bord)",
>     .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_TEXT_BORDER    },
> .flags=FLAGS, .unit = "keepflags" },
>      ++        { "text_shadow",    "keep text shadow tags (\\shad)",
>     .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_TEXT_SHADOW    },
> .flags=FLAGS, .unit = "keepflags" },
>      ++        { "text_rotate",    "keep text rotate tags (\\fr)",
>     .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_TEXT_ROTATE    },
> .flags=FLAGS, .unit = "keepflags" },
>      ++        { "text_blur",      "keep text blur tags (\\blur, \\be)",
>     .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_TEXT_BLUR      },
> .flags=FLAGS, .unit = "keepflags" },
>      ++        { "text_wrap",      "keep text wrap tags (\\q)",
>    .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_TEXT_WRAP      },
> .flags=FLAGS, .unit = "keepflags" },
>      ++        { "text_align",     "keep text align tags (\\a, \\an)",
>     .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_TEXT_ALIGNMENT },
> .flags=FLAGS, .unit = "keepflags" },
>      ++        { "reset_override", "keep override reset tags (\\r)",
>     .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_CANCELLING     },
> .flags=FLAGS, .unit = "keepflags" },
>      ++        { "move",           "keep move tags (\\move)",
>    .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_MOVE           },
> .flags=FLAGS, .unit = "keepflags" },
>      ++        { "pos",            "keep position tags (\\pos)",
>     .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_POS            },
> .flags=FLAGS, .unit = "keepflags" },
>      ++        { "origin",         "keep origin tags (\\org)",
>     .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_ORIGIN         },
> .flags=FLAGS, .unit = "keepflags" },
>      ++        { "draw",           "keep drawing tags (\\p)",
>    .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_DRAW           },
> .flags=FLAGS, .unit = "keepflags" },
>      ++        { "animate",        "keep animation tags (\\t)",
>    .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_ANIMATE        },
> .flags=FLAGS, .unit = "keepflags" },
>      ++        { "fade",           "keep fade tags (\\fad, \\fade)",
>     .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FADE           },
> .flags=FLAGS, .unit = "keepflags" },
>      ++        { "clip",           "keep clip tags (\\clip)",
>    .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_CLIP           },
> .flags=FLAGS, .unit = "keepflags" },
>      ++        { "unknown",        "keep unknown tags",
>    .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_UNKNOWN        },
> .flags=FLAGS, .unit = "keepflags" },
>       +    { "remove_animated", "remove animated text (default: yes)",
>  OFFSET(remove_animated),  AV_OPT_TYPE_BOOL, {.i64 = 1 },  0, 1, FLAGS, 0 },
>      -+    { "select_layer", "process a specific ass layer only",
>  OFFSET(remove_animated),  AV_OPT_TYPE_INT, {.i64 = -1 }, -1, INT_MAX,
> FLAGS, 0 },
>      ++    { "select_layer", "process a specific ass layer only",
>  OFFSET(select_layer),  AV_OPT_TYPE_INT, {.i64 = -1 }, -1, INT_MAX, FLAGS,
> 0 },
>       +    { NULL },
>       +};
>       +
>  17:  28d75dc982 ! 17:  6e488e495f avfilter/splitcc: Add splitcc filter
> for closed caption handling
>      @@ doc/filters.texi: ffmpeg -i INPUT -filter_complex
> "showspeaker=format=colon:styl
>
>        ## libavfilter/Makefile ##
>       @@ libavfilter/Makefile: OBJS-$(CONFIG_NULLSINK_FILTER)
>  += vsink_nullsink.o
>      + # subtitle filters
>        OBJS-$(CONFIG_CENSOR_FILTER)                 += sf_textmod.o
>        OBJS-$(CONFIG_SHOW_SPEAKER_FILTER)           += sf_textmod.o
>      - OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
>       +OBJS-$(CONFIG_SPLITCC_FILTER)                += sf_splitcc.o
>        OBJS-$(CONFIG_STRIPSTYLES_FILTER)            += sf_stripstyles.o
>      + OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
>
>      - # multimedia filters
>
>        ## libavfilter/allfilters.c ##
>      -@@ libavfilter/allfilters.c: extern const AVFilter
> ff_avf_showwavespic;
>      - extern const AVFilter ff_vaf_spectrumsynth;
>      +@@ libavfilter/allfilters.c: extern const AVFilter ff_avsrc_movie;
>      + /* subtitle filters */
>        extern const AVFilter ff_sf_censor;
>        extern const AVFilter ff_sf_showspeaker;
>       +extern const AVFilter ff_sf_splitcc;
>        extern const AVFilter ff_sf_stripstyles;
>        extern const AVFilter ff_sf_textmod;
>      - extern const AVFilter ff_svf_graphicsub2video;
>      +
>
>        ## libavfilter/sf_splitcc.c (new) ##
>       @@
>  18:  42d1d1c819 ! 18:  1057dff7da avfilter/graphicsub2text: Add new
> graphicsub2text filter (OCR)
>      @@ libavfilter/Makefile: OBJS-$(CONFIG_GBLUR_FILTER)
> += vf_gblur.o
>        OBJS-$(CONFIG_GRAYWORLD_FILTER)              += vf_grayworld.o
>
>        ## libavfilter/allfilters.c ##
>      -@@ libavfilter/allfilters.c: extern const AVFilter ff_avf_showwaves;
>      - extern const AVFilter ff_avf_showwavespic;
>      - extern const AVFilter ff_vaf_spectrumsynth;
>      +@@ libavfilter/allfilters.c: extern const AVFilter ff_avsrc_movie;
>      +
>      + /* subtitle filters */
>        extern const AVFilter ff_sf_censor;
>       +extern const AVFilter ff_sf_graphicsub2text;
>        extern const AVFilter ff_sf_showspeaker;
>      @@ libavfilter/sf_graphicsub2text.c (new)
>       +                }
>       +            }
>       +
>      -+            if (pointsize != cur_pointsize && s->recognize &
> RFLAGS_FONTSIZE) {
>      -+                av_log(s, AV_LOG_DEBUG, "pointsize - pointsize:
> %d\n", pointsize);
>      -+                in_code = print_code(&s->buffer, in_code, "\\fs%d",
> (int)(pointsize * font_factor));
>      -+                cur_pointsize = pointsize;
>      ++            if (pointsize > 0 && pointsize != cur_pointsize &&
> s->recognize & RFLAGS_FONTSIZE) {
>      ++                float change_factor = (float)(FFABS(pointsize -
> cur_pointsize)) / FFMAX(pointsize, cur_pointsize);
>      ++
>      ++                // Avoid small changes due to recognition variance
>      ++                if (change_factor > 0.12f) {
>      ++                    av_log(s, AV_LOG_DEBUG, "pointsize - pointsize:
> %d\n", pointsize);
>      ++                    in_code = print_code(&s->buffer, in_code,
> "\\fs%d", (int)(pointsize * font_factor));
>      ++                    cur_pointsize = pointsize;
>      ++                }
>       +            }
>       +
>       +            if (is_italic && !cur_is_italic && s->recognize &
> RFLAGS_FITALIC)
>      @@ libavfilter/sf_graphicsub2text.c (new)
>       +
>       +            const int layer = s->recognize ? i : 0;
>       +            char *tmp = area->ass;
>      -+            area->ass =
> avpriv_ass_get_dialog_ex(s->readorder_counter++, layer, "Default", NULL, 0,
> 0, margin_v, tmp);
>      ++            area->ass =
> avpriv_ass_get_dialog_ex(s->readorder_counter++, layer, "Default", NULL, 0,
> 0, margin_v, NULL, tmp);
>       +            av_free(tmp);
>       +        }
>       +    }
>  19:  7095e8aa26 ! 19:  4e85fb5d2f avfilter/subscale: Add filter for
> scaling and/or re-arranging graphical subtitles
>      @@ doc/filters.texi: Set the rendering margin in pixels.
>        @chapter Multimedia Filters
>
>        ## libavfilter/Makefile ##
>      -@@ libavfilter/Makefile: OBJS-$(CONFIG_SHOW_SPEAKER_FILTER)
>  += sf_textmod.o
>      - OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
>      +@@ libavfilter/Makefile: OBJS-$(CONFIG_CENSOR_FILTER)
>  += sf_textmod.o
>      + OBJS-$(CONFIG_SHOW_SPEAKER_FILTER)           += sf_textmod.o
>        OBJS-$(CONFIG_SPLITCC_FILTER)                += sf_splitcc.o
>        OBJS-$(CONFIG_STRIPSTYLES_FILTER)            += sf_stripstyles.o
>       +OBJS-$(CONFIG_SUBSCALE_FILTER)               += sf_subscale.o
>      + OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
>
>        # multimedia filters
>      - OBJS-$(CONFIG_ABITSCOPE_FILTER)              += avf_abitscope.o
>
>        ## libavfilter/allfilters.c ##
>       @@ libavfilter/allfilters.c: extern const AVFilter
> ff_sf_graphicsub2text;
>      @@ libavfilter/allfilters.c: extern const AVFilter
> ff_sf_graphicsub2text;
>        extern const AVFilter ff_sf_stripstyles;
>       +extern const AVFilter ff_sf_subscale;
>        extern const AVFilter ff_sf_textmod;
>      - extern const AVFilter ff_svf_graphicsub2video;
>      - extern const AVFilter ff_svf_textsub2video;
>      +
>      + /* those filters are part of public or internal API,
>
>        ## libavfilter/sf_subscale.c (new) ##
>       @@
>  20:  697939451e ! 20:  88e8adb889 avfilter/subfeed: add subtitle feed
> filter
>      @@ Commit message
>           Signed-off-by: softworkz <softworkz@hotmail.com>
>
>        ## libavfilter/Makefile ##
>      -@@ libavfilter/Makefile: OBJS-$(CONFIG_TEXTMOD_FILTER)
>   += sf_textmod.o
>      +@@ libavfilter/Makefile: OBJS-$(CONFIG_CENSOR_FILTER)
>  += sf_textmod.o
>      + OBJS-$(CONFIG_SHOW_SPEAKER_FILTER)           += sf_textmod.o
>        OBJS-$(CONFIG_SPLITCC_FILTER)                += sf_splitcc.o
>        OBJS-$(CONFIG_STRIPSTYLES_FILTER)            += sf_stripstyles.o
>      - OBJS-$(CONFIG_SUBSCALE_FILTER)               += sf_subscale.o
>       +OBJS-$(CONFIG_SUBFEED_FILTER)                += sf_subfeed.o
>      + OBJS-$(CONFIG_SUBSCALE_FILTER)               += sf_subscale.o
>      + OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
>
>      - # multimedia filters
>      - OBJS-$(CONFIG_ABITSCOPE_FILTER)              += avf_abitscope.o
>
>        ## libavfilter/allfilters.c ##
>      -@@ libavfilter/allfilters.c: extern const AVFilter ff_sf_showspeaker;
>      +@@ libavfilter/allfilters.c: extern const AVFilter
> ff_sf_graphicsub2text;
>      + extern const AVFilter ff_sf_showspeaker;
>        extern const AVFilter ff_sf_splitcc;
>        extern const AVFilter ff_sf_stripstyles;
>      - extern const AVFilter ff_sf_subscale;
>       +extern const AVFilter ff_sf_subfeed;
>      + extern const AVFilter ff_sf_subscale;
>        extern const AVFilter ff_sf_textmod;
>      - extern const AVFilter ff_svf_graphicsub2video;
>      - extern const AVFilter ff_svf_textsub2video;
>      +
>
>        ## libavfilter/sf_subfeed.c (new) ##
>       @@
>      @@ libavfilter/sf_subfeed.c (new)
>       +        if (pts_diff <= 0) {
>       +            av_log(ctx, AV_LOG_WARNING, "The pts_diff to the
> previous frame (index #%"PRId64")  is <= 0: %"PRId64" ms. The previous
> frame duration is %"PRId64" ms.\n",
>       +                index, avtb_to_ms(pts_diff),
> avtb_to_ms(previous_frame->subtitle_timing.duration));
>      ++
>      ++            if (s->fix_overlap) {
>      ++                av_log(ctx, AV_LOG_VERBOSE, "Removing previous
> frame\n");
>      ++                previous_frame = ff_framequeue_take(&s->fifo);
>      ++                while (nb_queued_frames > 1) {
>      ++                    ff_framequeue_add(&s->fifo, previous_frame);
>      ++                    previous_frame = ff_framequeue_take(&s->fifo);
>      ++                    nb_queued_frames--;
>      ++                }
>      ++            }
>       +        }
>       +    }
>       +
>   -:  ---------- > 21:  a96bb5c788 avfilter/text2graphicsub: Added
> text2graphicsub subtitle filter
>   -:  ---------- > 22:  c4922f8466 avfilter/snull,strim: Add snull and
> strim filters
>  21:  32e9af0806 = 23:  848f84d5dc avcodec/subtitles: Migrate subtitle
> encoders to frame-based API
>  22:  fa0b5c2077 ! 24:  2645a1a842 fftools/ffmpeg: Introduce subtitle
> filtering and new frame-based subtitle encoding
>      @@ Commit message
>             Overlay results have slightly different CRCs due to different
>             blending implementation
>
>      +    - sub-scc
>      +      The first entry is no longer in the output because it is before
>      +      the actual start time and the strim filter removes such entries
>      +      now (like for video and audio)
>      +
>           Signed-off-by: softworkz <softworkz@hotmail.com>
>
>        ## fftools/ffmpeg.c ##
>      @@ fftools/ffmpeg.c: static int init_output_stream(OutputStream *ost,
> AVFrame *fram
>                        return AVERROR_INVALIDDATA;
>                    }
>                }
>      +@@ fftools/ffmpeg.c: static int transcode_init(void)
>      +     for (i = 0; i < nb_output_streams; i++) {
>      +         if (!output_streams[i]->stream_copy &&
>      +             (output_streams[i]->enc_ctx->codec_type ==
> AVMEDIA_TYPE_VIDEO ||
>      +-             output_streams[i]->enc_ctx->codec_type ==
> AVMEDIA_TYPE_AUDIO))
>      ++             output_streams[i]->enc_ctx->codec_type ==
> AVMEDIA_TYPE_AUDIO ||
>      ++             output_streams[i]->enc_ctx->codec_type ==
> AVMEDIA_TYPE_SUBTITLE))
>      +             continue;
>      +
>      +         ret = init_output_stream_wrapper(output_streams[i], NULL,
> 0);
>       @@ fftools/ffmpeg.c: static OutputStream *choose_output(void)
>                               av_rescale_q(ost->last_mux_dts,
> ost->st->time_base,
>                                            AV_TIME_BASE_Q);
>      @@ fftools/ffmpeg_filter.c: static void init_input_filter(FilterGraph
> *fg, AVFilter
>                        continue;
>                    if (check_stream_specifier(s, s->streams[i], *p == ':'
> ? p + 1 : p) == 1) {
>                        st = s->streams[i];
>      +@@ fftools/ffmpeg_filter.c: static int insert_trim(int64_t
> start_time, int64_t duration,
>      +     const char *name = (type == AVMEDIA_TYPE_VIDEO) ? "trim" :
> "atrim";
>      +     int ret = 0;
>      +
>      ++    switch (type) {
>      ++    case AVMEDIA_TYPE_VIDEO:
>      ++        name = "trim";
>      ++        break;
>      ++    case AVMEDIA_TYPE_AUDIO:
>      ++        name = "atrim";
>      ++        break;
>      ++    case AVMEDIA_TYPE_SUBTITLE:
>      ++        name = "strim";
>      ++        break;
>      ++    default:
>      ++        av_log(NULL, AV_LOG_ERROR, "insert_trim: Invalid media
> type: %d\n", type);
>      ++        return AVERROR_INVALIDDATA;
>      ++    }
>      ++
>      +     if (duration == INT64_MAX && start_time == AV_NOPTS_VALUE)
>      +         return 0;
>      +
>       @@ fftools/ffmpeg_filter.c: static int insert_filter(AVFilterContext
> **last_filter, int *pad_idx,
>            return 0;
>        }
>      @@ fftools/ffmpeg_filter.c: static int insert_filter(AVFilterContext
> **last_filter,
>       +static int configure_output_subtitle_filter(FilterGraph *fg,
> OutputFilter *ofilter, AVFilterInOut *out)
>       +{
>       +    OutputStream *ost = ofilter->ost;
>      ++    OutputFile    *of = output_files[ost->file_index];
>       +    AVFilterContext *last_filter = out->filter_ctx;
>       +    int pad_idx = out->pad_idx;
>       +    int ret;
>      @@ fftools/ffmpeg_filter.c: static int insert_filter(AVFilterContext
> **last_filter,
>       +        return ret;
>       +    }
>       +
>      -+    ////snprintf(name, sizeof(name), "trim_out_%d_%d",
>      -+    ////         ost->file_index, ost->index);
>      -+    ////ret = insert_trim(of->start_time, of->recording_time,
>      -+    ////                  &last_filter, &pad_idx, name);
>      -+    ////if (ret < 0)
>      -+    ////    return ret;
>      ++    snprintf(name, sizeof(name), "trim_out_%d_%d",
>      ++             ost->file_index, ost->index);
>      ++    ret = insert_trim(of->start_time, of->recording_time,
>      ++                      &last_filter, &pad_idx, name);
>      ++    if (ret < 0)
>      ++        return ret;
>       +
>       +    ////ost->st->codecpar->codec_tag = MKTAG('a', 's', 's', 's');
>       +
>      @@ fftools/ffmpeg_filter.c: void check_filter_outputs(void)
>       +    AVFilterContext *last_filter;
>       +    const AVFilter *buffer_filt = avfilter_get_by_name("sbuffer");
>       +    InputStream *ist = ifilter->ist;
>      ++    InputFile     *f = input_files[ist->file_index];
>       +    AVBPrint args;
>       +    char name[255];
>       +    int ret, pad_idx = 0;
>       +    int w, h;
>      ++    int64_t tsoffset = 0;
>       +    AVBufferSrcParameters *par = av_buffersrc_parameters_alloc();
>       +    enum AVMediaType media_type;
>       +
>      @@ fftools/ffmpeg_filter.c: void check_filter_outputs(void)
>       -            if (avf->streams[i]->codecpar->codec_type ==
> AVMEDIA_TYPE_VIDEO) {
>       -                w = FFMAX(w, avf->streams[i]->codecpar->width);
>       -                h = FFMAX(h, avf->streams[i]->codecpar->height);
>      --            }
>      --        }
>      --        if (!(w && h)) {
>      --            w = FFMAX(w, 720);
>      --            h = FFMAX(h, 576);
>      --        }
>      --        av_log(avf, AV_LOG_INFO, "sub2video: using %dx%d canvas\n",
> w, h);
>       +        w = ist->dec_ctx->width;
>       +        h = ist->dec_ctx->height;
>       +    }
>      @@ fftools/ffmpeg_filter.c: void check_filter_outputs(void)
>       +        w = ass->script_info.play_res_x;
>       +        h = ass->script_info.play_res_y;
>       +        avpriv_ass_split_free(ass_ctx);
>      -     }
>      --    ist->sub2video.w = ifilter->width  = w;
>      --    ist->sub2video.h = ifilter->height = h;
>      -
>      --    ifilter->width  = ist->dec_ctx->width  ? ist->dec_ctx->width  :
> ist->sub2video.w;
>      --    ifilter->height = ist->dec_ctx->height ? ist->dec_ctx->height :
> ist->sub2video.h;
>      ++    }
>      ++
>       +    ist->subtitle_kickoff.w = w;
>       +    ist->subtitle_kickoff.h = h;
>       +    av_log(ifilter, AV_LOG_INFO, "subtitle input filter: decoding
> size %dx%d\n", ist->subtitle_kickoff.w, ist->subtitle_kickoff.h);
>      -
>      --    /* rectangles are AV_PIX_FMT_PAL8, but we have no guarantee
> that the
>      --       palettes for all rectangles are identical or compatible */
>      --    ifilter->format = AV_PIX_FMT_RGB32;
>      ++
>       +    ifilter->width = w;
>       +    ifilter->height = h;
>       +    ist->dec_ctx->width = w;
>       +    ist->dec_ctx->height = h;
>      -
>      --    ist->sub2video.frame = av_frame_alloc();
>      --    if (!ist->sub2video.frame)
>      --        return AVERROR(ENOMEM);
>      --    ist->sub2video.last_pts = INT64_MIN;
>      --    ist->sub2video.end_pts  = INT64_MIN;
>      ++
>       +    ist->subtitle_kickoff.last_pts = INT64_MIN;
>       +
>       +    snprintf(name, sizeof(name), "graph %d subtitle input from
> stream %d:%d", fg->index,
>      @@ fftools/ffmpeg_filter.c: void check_filter_outputs(void)
>       +    if ((ret = avfilter_graph_create_filter(&ifilter->filter,
> buffer_filt, name,
>       +                                            args.str, NULL,
> fg->graph)) < 0)
>       +        goto fail;
>      -
>      --    /* sub2video structure has been (re-)initialized.
>      --       Mark it as such so that the system will be
>      --       initialized with the first received heartbeat. */
>      --    ist->sub2video.initialize = 1;
>      ++
>       +    par->hw_frames_ctx = ifilter->hw_frames_ctx;
>       +    par->format = ifilter->format;
>       +    par->width = ifilter->width;
>      @@ fftools/ffmpeg_filter.c: void check_filter_outputs(void)
>       +                    subscale_h = input->height;
>       +                    break;
>       +                }
>      -+            }
>      -+        }
>      -+
>      +             }
>      +         }
>      +-        if (!(w && h)) {
>      +-            w = FFMAX(w, 720);
>      +-            h = FFMAX(h, 576);
>      +-        }
>      +-        av_log(avf, AV_LOG_INFO, "sub2video: using %dx%d canvas\n",
> w, h);
>      +-    }
>      +-    ist->sub2video.w = ifilter->width  = w;
>      +-    ist->sub2video.h = ifilter->height = h;
>      +
>      +-    ifilter->width  = ist->dec_ctx->width  ? ist->dec_ctx->width  :
> ist->sub2video.w;
>      +-    ifilter->height = ist->dec_ctx->height ? ist->dec_ctx->height :
> ist->sub2video.h;
>       +        if (subscale_w && subscale_h) {
>       +            char subscale_params[64];
>       +            snprintf(subscale_params, sizeof(subscale_params),
> "w=%d:h=%d", subscale_w, subscale_h);
>      @@ fftools/ffmpeg_filter.c: void check_filter_outputs(void)
>       +            if (ret < 0)
>       +                return ret;
>       +        }
>      -+
>      +
>      +-    /* rectangles are AV_PIX_FMT_PAL8, but we have no guarantee
> that the
>      +-       palettes for all rectangles are identical or compatible */
>      +-    ifilter->format = AV_PIX_FMT_RGB32;
>       +        av_log(NULL, AV_LOG_INFO, "Auto-inserting graphicsub2video
> filter\n");
>       +        ret = insert_filter(&last_filter, &pad_idx,
> "graphicsub2video", NULL);
>       +        if (ret < 0)
>       +            return ret;
>       +    }
>      -+
>      +
>      +-    ist->sub2video.frame = av_frame_alloc();
>      +-    if (!ist->sub2video.frame)
>      +-        return AVERROR(ENOMEM);
>      +-    ist->sub2video.last_pts = INT64_MIN;
>      +-    ist->sub2video.end_pts  = INT64_MIN;
>      ++    if (copy_ts) {
>      ++        tsoffset = f->start_time == AV_NOPTS_VALUE ? 0 :
> f->start_time;
>      ++        if (!start_at_zero && f->ctx->start_time != AV_NOPTS_VALUE)
>      ++            tsoffset += f->ctx->start_time;
>      ++    }
>      ++    ret = insert_trim(((f->start_time == AV_NOPTS_VALUE) ||
> !f->accurate_seek) ?
>      ++                      AV_NOPTS_VALUE : tsoffset, f->recording_time,
>      ++                      &last_filter, &pad_idx, name);
>      ++    if (ret < 0)
>      ++        return ret;
>      +
>      +-    /* sub2video structure has been (re-)initialized.
>      +-       Mark it as such so that the system will be
>      +-       initialized with the first received heartbeat. */
>      +-    ist->sub2video.initialize = 1;
>       +    if ((ret = avfilter_link(last_filter, 0, in->filter_ctx,
> in->pad_idx)) < 0)
>       +        return ret;
>
>      @@ fftools/ffmpeg_filter.c: static int
> configure_input_video_filter(FilterGraph *fg
>            int64_t tsoffset = 0;
>       -    AVBufferSrcParameters *par = av_buffersrc_parameters_alloc();
>       +    AVBufferSrcParameters *par;
>      -
>      ++
>       +    if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_SUBTITLE) {
>       +        // Automatically insert conversion filter to retain
> compatibility
>       +        // with sub2video command lines
>       +        return configure_input_subtitle_filter(fg, ifilter, in);
>       +    }
>      -+
>      +
>       +    par = av_buffersrc_parameters_alloc();
>            if (!par)
>                return AVERROR(ENOMEM);
>      @@ fftools/ffmpeg_opt.c: static void add_input_streams(OptionsContext
> *o, AVFormatC
>                    break;
>                }
>                case AVMEDIA_TYPE_ATTACHMENT:
>      +@@ fftools/ffmpeg_opt.c: static char *get_ost_filters(OptionsContext
> *o, AVFormatContext *oc,
>      +
>      +     if (ost->filters_script)
>      +         return read_file(ost->filters_script);
>      +-    else if (ost->filters)
>      ++    if (ost->filters)
>      +         return av_strdup(ost->filters);
>      +
>      +-    return av_strdup(st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO
> ?
>      +-                     "null" : "anull");
>      ++    switch (st->codecpar->codec_type) {
>      ++    case AVMEDIA_TYPE_VIDEO: return av_strdup("null");
>      ++    case AVMEDIA_TYPE_AUDIO: return av_strdup("anull");
>      ++    case AVMEDIA_TYPE_SUBTITLE: return av_strdup("snull");
>      ++    default: av_assert0(0); return NULL;
>      ++    }
>      + }
>      +
>      + static void check_streamcopy_filters(OptionsContext *o,
> AVFormatContext *oc,
>      +@@ fftools/ffmpeg_opt.c: static OutputStream
> *new_subtitle_stream(OptionsContext *o, AVFormatContext *oc,
>      +
>      +     subtitle_enc->codec_type = AVMEDIA_TYPE_SUBTITLE;
>      +
>      ++    MATCH_PER_STREAM_OPT(filter_scripts, str, ost->filters_script,
> oc, st);
>      ++    MATCH_PER_STREAM_OPT(filters,        str, ost->filters,
> oc, st);
>      ++
>      +     if (!ost->stream_copy) {
>      +         char *frame_size = NULL;
>      +
>      +@@ fftools/ffmpeg_opt.c: static OutputStream
> *new_subtitle_stream(OptionsContext *o, AVFormatContext *oc,
>      +             av_log(NULL, AV_LOG_FATAL, "Invalid frame size: %s.\n",
> frame_size);
>      +             exit_program(1);
>      +         }
>      ++
>      ++        ost->avfilter = get_ost_filters(o, oc, ost);
>      ++        if (!ost->avfilter)
>      ++            exit_program(1);
>      +     }
>      +
>      +     return ost;
>       @@ fftools/ffmpeg_opt.c: static void init_output_filter(OutputFilter
> *ofilter, OptionsContext *o,
>            switch (ofilter->type) {
>            case AVMEDIA_TYPE_VIDEO: ost = new_video_stream(o, oc, -1);
> break;
>      @@ fftools/ffmpeg_opt.c: static void init_output_filter(OutputFilter
> *ofilter, Opti
>                       "currently.\n");
>                exit_program(1);
>            }
>      +@@ fftools/ffmpeg_opt.c: loop_end:
>      +             ist->processing_needed = 1;
>      +
>      +             if (ost->st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO
> ||
>      +-                ost->st->codecpar->codec_type ==
> AVMEDIA_TYPE_AUDIO) {
>      ++                ost->st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO
> ||
>      ++                ost->st->codecpar->codec_type ==
> AVMEDIA_TYPE_SUBTITLE) {
>      +                 err = init_simple_filtergraph(ist, ost);
>      +                 if (err < 0) {
>      +                     av_log(NULL, AV_LOG_ERROR,
>      +@@ fftools/ffmpeg_opt.c: loop_end:
>      +                 } else if (ost->enc->ch_layouts) {
>      +                     f->ch_layouts = ost->enc->ch_layouts;
>      +                 }
>      ++                break;
>      ++            case AVMEDIA_TYPE_SUBTITLE:
>      ++                f->format     = ost->enc_ctx->subtitle_type;
>      ++
>      +                 break;
>      +             }
>      +         }
>
>        ## tests/ref/fate/filter-overlay-dvdsub-2397 ##
>       @@
>      @@ tests/ref/fate/sub-dvb
>       +0,   31400000,   31400000,   479000,       14, 0x0959015b
>       +0,   31879000,   31879000,   479000,       14, 0x09c9016b
>
>      + ## tests/ref/fate/sub-scc ##
>      +@@ tests/ref/fate/sub-scc: Style:
> Default,Monospace,16,&Hffffff,&Hffffff,&H0,&H0,0,0,0,0,100,100,0,0,3,1,0,
>      +
>      + [Events]
>      + Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV,
> Effect, Text
>      +-Dialogue:
> 0,0:00:-2.-47,0:00:00.70,Default,,0,0,0,,{\an7}{\pos(76,228)}WE HAVE FOUND
> A WITCH !\N{\an7}{\pos(76,243)}MAY WE BURN HER ?
>      + Dialogue:
> 0,0:00:00.69,0:00:03.29,Default,,0,0,0,,{\an7}{\pos(115,228)}[ Crowd
> ]\N{\an7}{\pos(115,243)}BURN HER !  BURN HER !
>      + Dialogue:
> 0,0:00:03.30,0:00:07.07,Default,,0,0,0,,{\an7}{\pos(38,197)}HOW DO YOU
> KNOW\N{\an7}{\pos(38,213)}SHE IS A WITCH ?\N{\an7}{\pos(153,243)}SHE LOOKS
> LIKE ONE !
>      + Dialogue:
> 0,0:00:07.07,0:00:09.27,Default,,0,0,0,,{\an7}{\pos(192,228)}[
> Shouting\N{\an7}{\pos(192,243)}\h\hAffirmations ]
>      +
>        ## tests/ref/fate/sub2video ##
>       @@
>        0,         47,         47,        1,   518400, 0xde69683f
>  23:  a66debd96e = 25:  a90a6e1086 avcodec/dvbsubdec: Fix conditions for
> fallback to default resolution
>
> --
> ffmpeg-codebot
> _______________________________________________
> 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".
>
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-07-02 16:39         ` [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022 Paul B Mahol
@ 2022-07-02 17:18           ` Nicolas George
  2022-07-02 17:27             ` Paul B Mahol
                               ` (2 more replies)
  2022-07-02 19:03           ` Soft Works
  1 sibling, 3 replies; 217+ messages in thread
From: Nicolas George @ 2022-07-02 17:18 UTC (permalink / raw)
  To: FFmpeg development discussions and patches
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt


[-- Attachment #1.1: Type: text/plain, Size: 314 bytes --]

Paul B Mahol (12022-07-02):
> Can this be properly finally be fully reviewed and accepted?

As long as the patch does not have a solution to have all the utility
filters (setpts, trim, concat, etc.) working with subtitles too without
more code duplication, the review is just "NAK".

-- 
  Nicolas George

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

[-- Attachment #2: Type: text/plain, Size: 251 bytes --]

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-07-02 17:27             ` Paul B Mahol
@ 2022-07-02 17:26               ` Nicolas George
  0 siblings, 0 replies; 217+ messages in thread
From: Nicolas George @ 2022-07-02 17:26 UTC (permalink / raw)
  To: FFmpeg development discussions and patches
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt


[-- Attachment #1.1: Type: text/plain, Size: 237 bytes --]

Paul B Mahol (12022-07-02):
> Could you please mention in relevant thread of those subtitle work where
> duplication in code happened?

No. Currently, there is no solution, with or without code duplication.

-- 
  Nicolas George

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

[-- Attachment #2: Type: text/plain, Size: 251 bytes --]

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-07-02 17:18           ` Nicolas George
@ 2022-07-02 17:27             ` Paul B Mahol
  2022-07-02 17:26               ` Nicolas George
  2022-07-02 19:11             ` Soft Works
  2022-07-03 17:07             ` Michael Niedermayer
  2 siblings, 1 reply; 217+ messages in thread
From: Paul B Mahol @ 2022-07-02 17:27 UTC (permalink / raw)
  To: FFmpeg development discussions and patches
  Cc: Michael Niedermayer, softworkz, Andriy Gelman, Andreas Rheinhardt

On Sat, Jul 2, 2022 at 7:18 PM Nicolas George <george@nsup.org> wrote:

> Paul B Mahol (12022-07-02):
> > Can this be properly finally be fully reviewed and accepted?
>
> As long as the patch does not have a solution to have all the utility
> filters (setpts, trim, concat, etc.) working with subtitles too without
> more code duplication, the review is just "NAK".
>

Could you please mention in relevant thread of those subtitle work where
duplication in code happened?


>
> --
>   Nicolas George
> _______________________________________________
> 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".
>
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-07-02 16:39         ` [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022 Paul B Mahol
  2022-07-02 17:18           ` Nicolas George
@ 2022-07-02 19:03           ` Soft Works
  1 sibling, 0 replies; 217+ messages in thread
From: Soft Works @ 2022-07-02 19:03 UTC (permalink / raw)
  To: Paul B Mahol, FFmpeg development discussions and patches
  Cc: Michael Niedermayer, Andriy Gelman, Andreas Rheinhardt



From: Paul B Mahol <onemda@gmail.com>
Sent: Saturday, July 2, 2022 6:39 PM
To: FFmpeg development discussions and patches <ffmpeg-devel@ffmpeg.org>
Cc: Michael Niedermayer <michael@niedermayer.cc>; softworkz <softworkz@hotmail.com>; Andriy Gelman <andriy.gelman@gmail.com>; Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
Subject: Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022



On Sat, Jun 25, 2022 at 11:58 AM ffmpegagent <ffmpegagent@gmail.com<mailto:ffmpegagent@gmail.com>> wrote:

Subtitle Filtering 2022
=======================

This is a substantial update to the earlier subtitle filtering patch series.
A primary goal has been to address others' concerns as much as possible on
one side and to provide more clarity and control over the way things are
working. Clarity is is specifically important to allow for a better
understanding of the need for a subtitle start pts value that can be
different from the frame's pts value. This is done by refactoring the
subtitle timing fields in AVFrame, adding a frame field to indicate repeated
subtitle frames, and finally the full removal of the heartbeat
functionality, replaced by a new 'subfeed' filter that provides different
modes for arbitrating subtitle frames in a filter graph. Finally, each
subtitle filter's documentation has been amended by a section describing the
filter's timeline behavior (in v3 update).


Subtitle Filtering Demos
========================

I published a demonstration of subtitle filtering capabilities with OCR,
text and bitmap subtitle manipulation involved: Demo 1: Text-Manipulation
with Bitmap Subtitles
[https://github.com/softworkz/SubtitleFilteringDemos/tree/master/Demo1]


v5 - Conversion to Graphic Subtitles, and other enhancements
============================================================

 * I'm glad to announce that Traian (@tcoza) has joined the project and
   contributed a new 'text2graphicsub' filter to convert text subtitles to
   graphic subtitles, which can in turn be encoded as dvd, dvb or x-subs
   (and any other encoder for graphic subs that might be added in the
   future). This filter closes the last open "gap" in subtitle processing.
 * stripstyles filter: now allows very fine-grained control over which ASS
   style codes should be preserved or stripped
 * stripstyles: do not drop dialog margin values
 * subfeed filter: eliminates duplicate frames with duplicate start times
   when 'fix_overlap' is specified
 * textmod: do not drop effect values
 * graphicsub2text: reduce font size jitter
 * ass_split: add function to selectively preserve elements when splitting
 * add strim, snull and ssink and further unify subtitle frame handling with
   audio and video
 * ffmpeg_filter: get simple filter notation working for subtitles


v4 - Quality Improvements
=========================

 * finally an updated version
 * includes many improvements from internal testing
 * all FATE tests passed
 * all example commands from the docs verified to work
 * can't list all the detail changes..
 * I have left out the extra commits which can be handled separately, just
   in case somebody wonders why these are missing:
   * avcodec/webvttenc: Don't encode drawing codes and empty lines
   * avcodec/webvttenc: convert hard-space tags to
   * avutil/ass_split: Add parsing of hard-space tags (\h)
   * avutil/ass_split: Treat all content in curly braces as hidden
   * avutil/ass_split: Fix ass parsing of style codes with comments


v3 - Rebase
===========

due to merge conflicts - apologies.


Changes in v2
=============

 * added .gitattributes file to enforce binary diffs for the test refs that
   cannot be applied when being sent via e-mail
 * perform filter graph re-init due to subtitle "frame size" change only
   when the size was unknown before and not set via -canvas_size
 * overlaytextsubs: Make sure to request frames on the subtitle input
 * avfilter/splitcc: Start parsing cc data on key frames only
 * avcodec/webvttenc: Don't encode ass drawing codes and empty lines
 * stripstyles: fix mem leak
 * gs2t: improve color detection
 * gs2t: empty frames must not be skipped
 * subfeed: fix name
 * textmod: preserve margins
 * added .gitattributes file to enforce binary diffs for the test refs that
   cannot be applied when being sent via e-mail
 * perform filter graph re-init due to subtitle "frame size" change only
   when the size was unknown before and not set via -canvas_size
 * avcodec/dvbsubdec: Fix conditions for fallback to default resolution
 * Made changes suggested by Andreas
 * Fixed failing command line reported by Michael

Changes from previous version v24:


AVFrame
=======

 * Removed sub_start_time The start time is now added to the subtitle
   start_pts during decoding The sub_end_time field is adjusted accordingly
 * Renamed sub_end_time to duration which it is effectively after removing
   the start_time
 * Added a sub-struct 'subtitle_timing' to av frame Contains subtitle_pts
   renamed to 'subtitle_timing.start_pts' and 'subtitle_timing.duration'
 * Change both fields to (fixed) time_base AV_TIMEBASE
 * add repeat_sub field provides a clear indication whether a subtitle frame
   is an actual subtitle event or a repeated subtitle frame in a filter
   graph


Heartbeat Removal
=================

 * completely removed the earlier heartbeat implementation
 * filtering arbitration is now implemented in a new filter: 'subfeed'
 * subfeed will be auto-inserted for compatiblity with sub2video command
   lines
 * the new behavior is not exactly identical to the earlier behavior, but it
   basically allows to achieve the same results
 * there's a small remainder, now named subtitle kickoff which serves to get
   things (in the filter graph) going right from the start


New 'subfeed' Filter
====================

 * a versatile filter for solving all kinds of problems with subtile frame
   flow in filter graphs
 * Can be inserted at any position in a graph
 * Auto-inserted for sub2video command lines (in repeat-mode)
 * Allows duration fixup delay input frames with unknown duration and infer
   duration from start of subsequent frame
 * Provides multiple modes of operation:
   * repeat mode (default) Queues input frames Outputs frames at a fixed
     (configurable) rate Either sends a matching input frame (repeatedly) or
     empty frames otherwise
   * scatter mode similar to repeat mode, but splits input frames by
     duration into small segments with same content
   * forward mode No fixed output rate Useful in combination with duration
     fixup or overlap fixup


ffmpeg Tool Changes
===================

 * delay subtitle output stream initialization (like for audio and video)
   This is needed for example when a format header depends on having
   received an initial frame to derive certain header values from
 * decoding: set subtitle frame size from decoding context
 * re-init graph when subtitle size changes
 * always insert subscale filter for sub2video command lines (to ensure
   correct scaling)


Subtitle Encoding
=================

 * ignore repeated frames for encoding based on repeat_sub field in AVFrame
 * support multi-area encoding for text subtitles Subtitle OCR can create
   multiple areas at different positions. Previously, the texts were always
   squashed into a single area ('subtitle rect'), which was not ideal.
   Multiple text areas are now generally supported:
   * ASS Encoder Changed to use the 'receive_packet' encoding API A single
     frame with multiple text areas will create multiple packets now
   * All other text subtitle encoders A newline is inserted between the text
     from multiple areas


graphicsub2text (OCR)
=====================

 * enhanced preprocessing
   * using elbg algorithm for color quantization
   * detection and removal of text outlines
   * map-based identification of colors per word (text, outline, background)
 * add option for duration fixup
 * add option to dump preprocessing bitmaps
 * Recognize formatting and apply as ASS inline styles
   * per word(!)
   * paragraph alignment
   * positioning
   * font names
   * font size
   * font style (italic, underline, bold)
   * text color, outline color


Other Filter Changes
====================

 * all: Make sure to forward all link properties (time base, frame rate, w,
   h) where appropriate
 * overlaytextsubs: request frames on the subtitle input
 * overlaytextsubs: disable read-order checking
 * overlaytextsubs: improve implementation of render_latest_only
 * overlaytextsubs: ensure equal in/out video formats
 * splitcc: derive framerate from realtime_latency
 * graphicsub2video: implement caching of converted frames
 * graphicsub2video: use 1x1 output frame size as long as subtitle size is
   unknown (0x0)

Plus a dozen of things I forgot..

softworkz (24):
  avcodec,avutil: Move enum AVSubtitleType to avutil, add new and
    deprecate old values
  avutil/frame: Prepare AVFrame for subtitle handling
  avcodec/subtitles: Introduce new frame-based subtitle decoding API
  avcodec/libzvbi: set subtitle type
  avfilter/subtitles: Update vf_subtitles to use new decoding api
  avcodec,avutil: Move ass helper functions to avutil as avpriv_ and
    extend ass dialog parsing
  avcodec/subtitles: Replace deprecated enum values
  fftools/play,probe: Adjust for subtitle changes
  avfilter/subtitles: Add subtitles.c for subtitle frame allocation
  avfilter/avfilter: Handle subtitle frames
  avfilter/avfilter: Fix hardcoded input index
  avfilter/sbuffer: Add sbuffersrc and sbuffersink filters
  avfilter/overlaygraphicsubs: Add overlaygraphicsubs and
    graphicsub2video filters
  avfilter/overlaytextsubs: Add overlaytextsubs and textsubs2video
    filters
  avfilter/textmod: Add textmod, censor and show_speaker filters
  avfilter/stripstyles: Add stripstyles filter
  avfilter/splitcc: Add splitcc filter for closed caption handling
  avfilter/graphicsub2text: Add new graphicsub2text filter (OCR)
  avfilter/subscale: Add filter for scaling and/or re-arranging
    graphical subtitles
  avfilter/subfeed: add subtitle feed filter
  avfilter/snull,strim: Add snull and strim filters
  avcodec/subtitles: Migrate subtitle encoders to frame-based API
  fftools/ffmpeg: Introduce subtitle filtering and new frame-based
    subtitle encoding
  avcodec/dvbsubdec: Fix conditions for fallback to default resolution


Can this be properly finally be fully reviewed and accepted?

Otherwise if its kept mainly ignored than it should be regarded as spam.

Current status quo is bad.

Do you think the patchset is bad or the situation is bad?

In the latter case I agree.

I would ask everybody to voice their concerns and objections so we
can get those addressed.

Thanks,
softworkz

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-07-02 17:18           ` Nicolas George
  2022-07-02 17:27             ` Paul B Mahol
@ 2022-07-02 19:11             ` Soft Works
  2022-07-02 19:17               ` Nicolas George
  2022-07-03 17:07             ` Michael Niedermayer
  2 siblings, 1 reply; 217+ messages in thread
From: Soft Works @ 2022-07-02 19:11 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Michael Niedermayer, Andriy Gelman, Andreas Rheinhardt



> -----Original Message-----
> From: Nicolas George <george@nsup.org>
> Sent: Saturday, July 2, 2022 7:18 PM
> To: FFmpeg development discussions and patches <ffmpeg-
> devel@ffmpeg.org>
> Cc: Michael Niedermayer <michael@niedermayer.cc>; softworkz
> <softworkz@hotmail.com>; Andriy Gelman <andriy.gelman@gmail.com>;
> Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
> Subject: Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
> 
> Paul B Mahol (12022-07-02):
> > Can this be properly finally be fully reviewed and accepted?
> 
> As long as the patch does not have a solution to have all the utility
> filters (setpts, trim, concat, etc.) working with subtitles too
> without
> more code duplication, the review is just "NAK".

The latest versions has added support for trim (strim).

I strongly disagree on the "code duplication" argument. AFAIU, this
is about having similar filters duplicated for each media type, 
e.g. trim, atrim and strim.

The duplication has always existed already between audio and video
filters. You could have unified and eliminated that duplication for
years, but you didn't.

Now that a third media type is being added, it is totally arbitrary
to ask for unification and de-duplication of the code. You didn't 
do it for 2 media types, so why should 3 be the magic number where 
this is suddenly a requirement? This is an unjustified request.

Even when I would follow that demand, it wouldn't be reasonable, 
because in that case, the patchset would dramatically widen its 
scope and start affecting audio and video as well - which is much 
too huge to get it tackled in a single patchset.

It has always been possible to de-duplicate between audio and video,
and in the future, it will always be possible to de-duplicate between
audio, video and subtitles.

It might make sense to do so at some point in time, but it is out
of the scope of this patchset.
And when this is the only objection, then I think that the patchset
is actually in really good shape for getting merged.

Thanks,
softworkz







_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-07-02 19:11             ` Soft Works
@ 2022-07-02 19:17               ` Nicolas George
  2022-07-02 19:21                 ` Soft Works
                                   ` (2 more replies)
  0 siblings, 3 replies; 217+ messages in thread
From: Nicolas George @ 2022-07-02 19:17 UTC (permalink / raw)
  To: FFmpeg development discussions and patches
  Cc: Michael Niedermayer, Andriy Gelman, Andreas Rheinhardt

Soft Works (12022-07-02):
> The duplication has always existed already between audio and video
> filters. You could have unified and eliminated that duplication for
> years, but you didn't.

I have been preparing for it for years. You did not help.

> Even when I would follow that demand, it wouldn't be reasonable, 
> because in that case, the patchset would dramatically widen its 
> scope and start affecting audio and video as well - which is much 
> too huge to get it tackled in a single patchset.

We will not accept a bad patch just because you have bitten more than
you could chew and reverted to a quick and dirty solution.

-- 
  Nicolas George
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-07-02 19:17               ` Nicolas George
@ 2022-07-02 19:21                 ` Soft Works
  2022-07-02 19:29                 ` Soft Works
  2022-07-02 21:10                 ` Matt Zagrabelny
  2 siblings, 0 replies; 217+ messages in thread
From: Soft Works @ 2022-07-02 19:21 UTC (permalink / raw)
  To: FFmpeg development discussions and patches
  Cc: Michael Niedermayer, Andriy Gelman, Andreas Rheinhardt



> -----Original Message-----
> From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of
> Nicolas George
> Sent: Saturday, July 2, 2022 9:17 PM
> To: FFmpeg development discussions and patches <ffmpeg-
> devel@ffmpeg.org>
> Cc: Michael Niedermayer <michael@niedermayer.cc>; Andriy Gelman
> <andriy.gelman@gmail.com>; Andreas Rheinhardt
> <andreas.rheinhardt@outlook.com>
> Subject: Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
> 
> Soft Works (12022-07-02):
> > The duplication has always existed already between audio and video
> > filters. You could have unified and eliminated that duplication for
> > years, but you didn't.
> 
> I have been preparing for it for years. You did not help.
> 
> > Even when I would follow that demand, it wouldn't be reasonable,
> > because in that case, the patchset would dramatically widen its
> > scope and start affecting audio and video as well - which is much
> > too huge to get it tackled in a single patchset.
> 
> We will not accept a bad patch just because you have bitten more than
> you could chew and reverted to a quick and dirty solution.

All you do is keep spitting discrediting keywords, this is just disgusting.

sw
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-07-02 19:17               ` Nicolas George
  2022-07-02 19:21                 ` Soft Works
@ 2022-07-02 19:29                 ` Soft Works
  2022-07-02 19:36                   ` Nicolas George
  2022-07-02 21:10                 ` Matt Zagrabelny
  2 siblings, 1 reply; 217+ messages in thread
From: Soft Works @ 2022-07-02 19:29 UTC (permalink / raw)
  To: FFmpeg development discussions and patches
  Cc: Michael Niedermayer, Andriy Gelman, Andreas Rheinhardt



> -----Original Message-----
> From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of
> Nicolas George
> Sent: Saturday, July 2, 2022 9:17 PM
> To: FFmpeg development discussions and patches <ffmpeg-
> devel@ffmpeg.org>
> Cc: Michael Niedermayer <michael@niedermayer.cc>; Andriy Gelman
> <andriy.gelman@gmail.com>; Andreas Rheinhardt
> <andreas.rheinhardt@outlook.com>
> Subject: Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
> 
> Soft Works (12022-07-02):
> > The duplication has always existed already between audio and video
> > filters. You could have unified and eliminated that duplication for
> > years, but you didn't.
> 
> I have been preparing for it for years. You did not help.

This can easily be done AFTER my patchset has been merged. Despite 
all of your negative attitude, I will do my best to assist you in 
that process later.

Thanks,
sw
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-07-02 19:29                 ` Soft Works
@ 2022-07-02 19:36                   ` Nicolas George
  2022-07-02 20:32                     ` Soft Works
  0 siblings, 1 reply; 217+ messages in thread
From: Nicolas George @ 2022-07-02 19:36 UTC (permalink / raw)
  To: FFmpeg development discussions and patches
  Cc: Michael Niedermayer, Andriy Gelman, Andreas Rheinhardt


[-- Attachment #1.1: Type: text/plain, Size: 216 bytes --]

Soft Works (12022-07-02):
> This can easily be done AFTER my patchset has been merged.

With exponentially more work. Out of question.

This is all I have to say to you on the subject.

-- 
  Nicolas George

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

[-- Attachment #2: Type: text/plain, Size: 251 bytes --]

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-07-02 19:36                   ` Nicolas George
@ 2022-07-02 20:32                     ` Soft Works
  2022-07-02 20:40                       ` Paul B Mahol
  0 siblings, 1 reply; 217+ messages in thread
From: Soft Works @ 2022-07-02 20:32 UTC (permalink / raw)
  To: FFmpeg development discussions and patches
  Cc: Michael Niedermayer, Andriy Gelman, Andreas Rheinhardt



> -----Original Message-----
> From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of
> Nicolas George
> Sent: Saturday, July 2, 2022 9:36 PM
> To: FFmpeg development discussions and patches <ffmpeg-
> devel@ffmpeg.org>
> Cc: Michael Niedermayer <michael@niedermayer.cc>; Andriy Gelman
> <andriy.gelman@gmail.com>; Andreas Rheinhardt
> <andreas.rheinhardt@outlook.com>
> Subject: Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
> 
> Soft Works (12022-07-02):
> > This can easily be done AFTER my patchset has been merged.
> 
> With exponentially more work. Out of question.

Previously it would have been about like:

- Merging audio filter code with the video filter code
  (for the filters in question)

Now it will be

- Merging audio and subtitle filter code with the video filter code
  (for the same filters)

TBH, I can't see any exponent here. I think "double work" would be 
closer to the truth and realistically it will be much less than that 
because the work for merging audio and subtitle code is very similar,
so when you have merged audio code in a filter, merging the subtitle
part will be very analogous, so in total it would be less than 
double.

And when we look at the required amount of work in total, that 
calculation would only be valid when you would consider the value
of MY work that I've already done as zero. 

I even think that it's a better approach overall to do the deduplication
afterwards, because now - with the subtitle filtering patchset - the
specific requirements for subtitle filtering are visible on the table
and that way, the deduplication can already provision for those 
specific requirements whereas focusing on audio/video only, might have
led to do changes that wouldn't accommodate for the needs of subtitle 
filtering.

I am convinced that doing deduplication afterwards is a better order
for getting this done. I'm also convinced that my patchset is pretty
solid in the way it does handle subtitles, and I'm further convinced
that you know that very well. During all the process I have watched 
very closely, and in several cases where others had objections about
things I had done, you kept quiet, presumably because you were the only
other one to know why it had to be done that way. Also, you never 
named any specific detail that would be wrong, and I'm sure you would
have done if there had been any significant one.
My impression is that your primary reason for objection is that my
patchset interferes with your plans and visions you probably had in
mind for quite a while and I'm very sorry about that.
But in the end, my patchset doesn't stand in opposition to your plans,
it just requires a bit of adaption regarding the order of doing the work.
Neither do I stand in opposition to your plans. I respect the technical
architecture of libavfilter, especially regarding its simplicity and
effectivity compared to other filtering frameworks (like DirectShow)
and my interest in Ffmpeg filtering is not limited to subtitles. 
We don't need to be friends, but when you would manage to act and
communicate in a friendly way, you might gain somebody to help with
and support your plans in the future and you would also do a favor 
to all readers of the ML by not having them read through despicable
conversations.

Best regards,
softworkz

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-07-02 20:32                     ` Soft Works
@ 2022-07-02 20:40                       ` Paul B Mahol
  2022-07-02 20:50                         ` Soft Works
  2022-07-03  7:58                         ` Jean-Baptiste Kempf
  0 siblings, 2 replies; 217+ messages in thread
From: Paul B Mahol @ 2022-07-02 20:40 UTC (permalink / raw)
  To: FFmpeg development discussions and patches
  Cc: Michael Niedermayer, Andriy Gelman, Andreas Rheinhardt

On Sat, Jul 2, 2022 at 10:32 PM Soft Works <softworkz@hotmail.com> wrote:

>
>
> > -----Original Message-----
> > From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of
> > Nicolas George
> > Sent: Saturday, July 2, 2022 9:36 PM
> > To: FFmpeg development discussions and patches <ffmpeg-
> > devel@ffmpeg.org>
> > Cc: Michael Niedermayer <michael@niedermayer.cc>; Andriy Gelman
> > <andriy.gelman@gmail.com>; Andreas Rheinhardt
> > <andreas.rheinhardt@outlook.com>
> > Subject: Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
> >
> > Soft Works (12022-07-02):
> > > This can easily be done AFTER my patchset has been merged.
> >
> > With exponentially more work. Out of question.
>
> Previously it would have been about like:
>
> - Merging audio filter code with the video filter code
>   (for the filters in question)
>
> Now it will be
>
> - Merging audio and subtitle filter code with the video filter code
>   (for the same filters)
>
> TBH, I can't see any exponent here. I think "double work" would be
> closer to the truth and realistically it will be much less than that
> because the work for merging audio and subtitle code is very similar,
> so when you have merged audio code in a filter, merging the subtitle
> part will be very analogous, so in total it would be less than
> double.
>
> And when we look at the required amount of work in total, that
> calculation would only be valid when you would consider the value
> of MY work that I've already done as zero.
>
> I even think that it's a better approach overall to do the deduplication
> afterwards, because now - with the subtitle filtering patchset - the
> specific requirements for subtitle filtering are visible on the table
> and that way, the deduplication can already provision for those
> specific requirements whereas focusing on audio/video only, might have
> led to do changes that wouldn't accommodate for the needs of subtitle
> filtering.
>
> I am convinced that doing deduplication afterwards is a better order
> for getting this done. I'm also convinced that my patchset is pretty
> solid in the way it does handle subtitles, and I'm further convinced
> that you know that very well. During all the process I have watched
> very closely, and in several cases where others had objections about
> things I had done, you kept quiet, presumably because you were the only
> other one to know why it had to be done that way. Also, you never
> named any specific detail that would be wrong, and I'm sure you would
> have done if there had been any significant one.
> My impression is that your primary reason for objection is that my
> patchset interferes with your plans and visions you probably had in
> mind for quite a while and I'm very sorry about that.
> But in the end, my patchset doesn't stand in opposition to your plans,
> it just requires a bit of adaption regarding the order of doing the work.
> Neither do I stand in opposition to your plans. I respect the technical
> architecture of libavfilter, especially regarding its simplicity and
> effectivity compared to other filtering frameworks (like DirectShow)
> and my interest in Ffmpeg filtering is not limited to subtitles.
> We don't need to be friends, but when you would manage to act and
> communicate in a friendly way, you might gain somebody to help with
> and support your plans in the future and you would also do a favor
> to all readers of the ML by not having them read through despicable
> conversations.
>

AFAIK only NIcolas is for this merge of different types of filters into
single filter,
and filter type negotiation.

Both ideas are very bad. And Nicolas is (still) not (yet) FFmpeg dictator.


> Best regards,
> softworkz
>
> _______________________________________________
> 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".
>
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-07-02 20:40                       ` Paul B Mahol
@ 2022-07-02 20:50                         ` Soft Works
  2022-07-03  7:58                         ` Jean-Baptiste Kempf
  1 sibling, 0 replies; 217+ messages in thread
From: Soft Works @ 2022-07-02 20:50 UTC (permalink / raw)
  To: FFmpeg development discussions and patches
  Cc: Michael Niedermayer, Andriy Gelman, Andreas Rheinhardt



> -----Original Message-----
> From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of
> Paul B Mahol
> Sent: Saturday, July 2, 2022 10:40 PM
> To: FFmpeg development discussions and patches <ffmpeg-
> devel@ffmpeg.org>
> Cc: Michael Niedermayer <michael@niedermayer.cc>; Andriy Gelman
> <andriy.gelman@gmail.com>; Andreas Rheinhardt
> <andreas.rheinhardt@outlook.com>
> Subject: Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
> 
> On Sat, Jul 2, 2022 at 10:32 PM Soft Works <softworkz@hotmail.com>
> wrote:
> 
> >
> >
> > > -----Original Message-----
> > > From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of
> > > Nicolas George
> > > Sent: Saturday, July 2, 2022 9:36 PM
> > > To: FFmpeg development discussions and patches <ffmpeg-
> > > devel@ffmpeg.org>
> > > Cc: Michael Niedermayer <michael@niedermayer.cc>; Andriy Gelman
> > > <andriy.gelman@gmail.com>; Andreas Rheinhardt
> > > <andreas.rheinhardt@outlook.com>
> > > Subject: Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering
> 2022
> > >
> > > Soft Works (12022-07-02):
> > > > This can easily be done AFTER my patchset has been merged.
> > >
> > > With exponentially more work. Out of question.
> >
> > Previously it would have been about like:
> >
> > - Merging audio filter code with the video filter code
> >   (for the filters in question)
> >
> > Now it will be
> >
> > - Merging audio and subtitle filter code with the video filter code
> >   (for the same filters)
> >
> > TBH, I can't see any exponent here. I think "double work" would be
> > closer to the truth and realistically it will be much less than
> that
> > because the work for merging audio and subtitle code is very
> similar,
> > so when you have merged audio code in a filter, merging the
> subtitle
> > part will be very analogous, so in total it would be less than
> > double.
> >
> > And when we look at the required amount of work in total, that
> > calculation would only be valid when you would consider the value
> > of MY work that I've already done as zero.
> >
> > I even think that it's a better approach overall to do the
> deduplication
> > afterwards, because now - with the subtitle filtering patchset -
> the
> > specific requirements for subtitle filtering are visible on the
> table
> > and that way, the deduplication can already provision for those
> > specific requirements whereas focusing on audio/video only, might
> have
> > led to do changes that wouldn't accommodate for the needs of
> subtitle
> > filtering.
> >
> > I am convinced that doing deduplication afterwards is a better
> order
> > for getting this done. I'm also convinced that my patchset is
> pretty
> > solid in the way it does handle subtitles, and I'm further
> convinced
> > that you know that very well. During all the process I have watched
> > very closely, and in several cases where others had objections
> about
> > things I had done, you kept quiet, presumably because you were the
> only
> > other one to know why it had to be done that way. Also, you never
> > named any specific detail that would be wrong, and I'm sure you
> would
> > have done if there had been any significant one.
> > My impression is that your primary reason for objection is that my
> > patchset interferes with your plans and visions you probably had in
> > mind for quite a while and I'm very sorry about that.
> > But in the end, my patchset doesn't stand in opposition to your
> plans,
> > it just requires a bit of adaption regarding the order of doing the
> work.
> > Neither do I stand in opposition to your plans. I respect the
> technical
> > architecture of libavfilter, especially regarding its simplicity
> and
> > effectivity compared to other filtering frameworks (like
> DirectShow)
> > and my interest in Ffmpeg filtering is not limited to subtitles.
> > We don't need to be friends, but when you would manage to act and
> > communicate in a friendly way, you might gain somebody to help with
> > and support your plans in the future and you would also do a favor
> > to all readers of the ML by not having them read through despicable
> > conversations.
> >
> 
> AFAIK only NIcolas is for this merge of different types of filters
> into
> single filter,
> and filter type negotiation.
> 
> Both ideas are very bad. And Nicolas is (still) not (yet) FFmpeg
> dictator.

I think it makes sense for certain filters like buffer source, buffer
sink, null sink, trim, showinfo, copy, delay, repeat, etc.

Yet there is no point in making this a pre-condition for subtitle 
filtering, it's out of scope.

sw



_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-07-02 19:17               ` Nicolas George
  2022-07-02 19:21                 ` Soft Works
  2022-07-02 19:29                 ` Soft Works
@ 2022-07-02 21:10                 ` Matt Zagrabelny
  2022-07-02 21:22                   ` Nicolas George
  2 siblings, 1 reply; 217+ messages in thread
From: Matt Zagrabelny @ 2022-07-02 21:10 UTC (permalink / raw)
  To: FFmpeg development discussions and patches

On Sat, Jul 2, 2022 at 2:17 PM Nicolas George <george@nsup.org> wrote:

> Soft Works (12022-07-02):
> > The duplication has always existed already between audio and video
> > filters. You could have unified and eliminated that duplication for
> > years, but you didn't.
>
> I have been preparing for it for years. You did not help.
>

Because SW didn't help deduplicate audio and video, he can't help add
subtitle support?

Also, maybe he wasn't hacking on ffmpeg when you started preparing years
ago. Because he might be a recent hacker, his contributions are
disqualified?



> > Even when I would follow that demand, it wouldn't be reasonable,
> > because in that case, the patchset would dramatically widen its
> > scope and start affecting audio and video as well - which is much
> > too huge to get it tackled in a single patchset.
>
> We will not accept a bad patch just because you have bitten more than
> you could chew and reverted to a quick and dirty solution.
>

You are literally asking SW to bite off more.

Which one is it? He bit off more than he can chew, or you need him to bite
more off?

Nicolas' arguments seem to be "The perfect is the enemy of the good."

-m
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-07-02 21:10                 ` Matt Zagrabelny
@ 2022-07-02 21:22                   ` Nicolas George
  2022-07-02 21:27                     ` Paul B Mahol
  2022-07-02 21:45                     ` Matt Zagrabelny
  0 siblings, 2 replies; 217+ messages in thread
From: Nicolas George @ 2022-07-02 21:22 UTC (permalink / raw)
  To: FFmpeg development discussions and patches


[-- Attachment #1.1: Type: text/plain, Size: 472 bytes --]

Matt Zagrabelny (12022-07-02):
> Nicolas' arguments seem to be "The perfect is the enemy of the good."

No, my arguments are: you don't build a third floor when your
foundations are shaky.

I have posted on this very mailing list a long time ago simple tasks
that need to be done before considering adding more media types for
libavfilter. They have been rudely ignored.

Are you here to help, or are you here to pour oil on the flame?

-- 
  Nicolas George

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

[-- Attachment #2: Type: text/plain, Size: 251 bytes --]

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-07-02 21:22                   ` Nicolas George
@ 2022-07-02 21:27                     ` Paul B Mahol
  2022-07-02 21:45                     ` Matt Zagrabelny
  1 sibling, 0 replies; 217+ messages in thread
From: Paul B Mahol @ 2022-07-02 21:27 UTC (permalink / raw)
  To: FFmpeg development discussions and patches

On Sat, Jul 2, 2022 at 11:22 PM Nicolas George <george@nsup.org> wrote:

> Matt Zagrabelny (12022-07-02):
> > Nicolas' arguments seem to be "The perfect is the enemy of the good."
>
> No, my arguments are: you don't build a third floor when your
> foundations are shaky.
>
> I have posted on this very mailing list a long time ago simple tasks
> that need to be done before considering adding more media types for
> libavfilter. They have been rudely ignored.
>

"Simple" tasks that are not necessary at all.


>
> Are you here to help, or are you here to pour oil on the flame?
>
> --
>   Nicolas George
> _______________________________________________
> 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".
>
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-07-02 21:22                   ` Nicolas George
  2022-07-02 21:27                     ` Paul B Mahol
@ 2022-07-02 21:45                     ` Matt Zagrabelny
  2022-07-02 22:16                       ` Nicolas George
  1 sibling, 1 reply; 217+ messages in thread
From: Matt Zagrabelny @ 2022-07-02 21:45 UTC (permalink / raw)
  To: FFmpeg development discussions and patches

On Sat, Jul 2, 2022 at 4:22 PM Nicolas George <george@nsup.org> wrote:

> Matt Zagrabelny (12022-07-02):
> > Nicolas' arguments seem to be "The perfect is the enemy of the good."
>
> No, my arguments are: you don't build a third floor when your
> foundations are shaky.
>
> I have posted on this very mailing list a long time ago simple tasks
> that need to be done before considering adding more media types for
> libavfilter. They have been rudely ignored.
>

I guess your idea of "rudely" is different from mine. I find SW to be
polite.


> Are you here to help, or are you here to pour oil on the flame?
>

I am a simpleton that just reads what others write. I find subtitle support
to be desirable and SW's work to be a good step in that direction. Software
development is inherently iterative. There will always be more iterations
on code.

Sorry I cannot code up any solutions.

-m
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-07-02 21:45                     ` Matt Zagrabelny
@ 2022-07-02 22:16                       ` Nicolas George
  0 siblings, 0 replies; 217+ messages in thread
From: Nicolas George @ 2022-07-02 22:16 UTC (permalink / raw)
  To: FFmpeg development discussions and patches


[-- Attachment #1.1: Type: text/plain, Size: 584 bytes --]

Matt Zagrabelny (12022-07-02):
> I guess your idea of "rudely" is different from mine. I find SW to be
> polite.

I guess you have not read everything since the beginning.

> I am a simpleton that just reads what others write. I find subtitle support
> to be desirable and SW's work to be a good step in that direction.

It might seem so from the point of view of a user. From my point of view
of the person who maintains the framework of libavfilter, support for
subtitles is desirable, but SW's patches make my work later
significantly harder.

-- 
  Nicolas George

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

[-- Attachment #2: Type: text/plain, Size: 251 bytes --]

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-07-02 20:40                       ` Paul B Mahol
  2022-07-02 20:50                         ` Soft Works
@ 2022-07-03  7:58                         ` Jean-Baptiste Kempf
  2022-07-03 10:42                           ` Paul B Mahol
  1 sibling, 1 reply; 217+ messages in thread
From: Jean-Baptiste Kempf @ 2022-07-03  7:58 UTC (permalink / raw)
  To: ffmpeg-devel

Hello Paul,

On Sat, 2 Jul 2022, at 22:40, Paul B Mahol wrote:
>> I am convinced that doing deduplication afterwards is a better order
>> for getting this done. I'm also convinced that my patchset is pretty
>> [...]
>
> AFAIK only NIcolas is for this merge of different types of filters into
> single filter,
> and filter type negotiation.
>
> Both ideas are very bad.

If you really feel so, you have a way to unlock this situation: call the TC, whose precise role is to do work on this.
You can even request a GA vote on this topic, if you feel that more people should step in.

But here, the discussion goes nowhere "I'm right", "No, I'm right", "No, I'm correct" and will only evolve in insults and slander.

Best,

-- 
Jean-Baptiste Kempf -  President
+33 672 704 734
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-07-03  7:58                         ` Jean-Baptiste Kempf
@ 2022-07-03 10:42                           ` Paul B Mahol
  2022-07-04  8:45                             ` Jean-Baptiste Kempf
  0 siblings, 1 reply; 217+ messages in thread
From: Paul B Mahol @ 2022-07-03 10:42 UTC (permalink / raw)
  To: FFmpeg development discussions and patches

On Sun, Jul 3, 2022 at 9:59 AM Jean-Baptiste Kempf <jb@videolan.org> wrote:

> Hello Paul,
>
> On Sat, 2 Jul 2022, at 22:40, Paul B Mahol wrote:
> >> I am convinced that doing deduplication afterwards is a better order
> >> for getting this done. I'm also convinced that my patchset is pretty
> >> [...]
> >
> > AFAIK only NIcolas is for this merge of different types of filters into
> > single filter,
> > and filter type negotiation.
> >
> > Both ideas are very bad.
>
> If you really feel so, you have a way to unlock this situation: call the
> TC, whose precise role is to do work on this.
> You can even request a GA vote on this topic, if you feel that more people
> should step in.
>
> But here, the discussion goes nowhere "I'm right", "No, I'm right", "No,
> I'm correct" and will only evolve in insults and slander.
>
>
Why should I do this work?

I will just create my own fork. (Already did.)



> Best,
>
> --
> Jean-Baptiste Kempf -  President
> +33 672 704 734
> _______________________________________________
> 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".
>
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-07-02 17:18           ` Nicolas George
  2022-07-02 17:27             ` Paul B Mahol
  2022-07-02 19:11             ` Soft Works
@ 2022-07-03 17:07             ` Michael Niedermayer
  2022-07-03 17:25               ` Paul B Mahol
  2022-07-24 15:10               ` Nicolas George
  2 siblings, 2 replies; 217+ messages in thread
From: Michael Niedermayer @ 2022-07-03 17:07 UTC (permalink / raw)
  To: FFmpeg development discussions and patches


[-- Attachment #1.1: Type: text/plain, Size: 1291 bytes --]

On Sat, Jul 02, 2022 at 07:18:16PM +0200, Nicolas George wrote:
> Paul B Mahol (12022-07-02):
> > Can this be properly finally be fully reviewed and accepted?
> 
> As long as the patch does not have a solution to have all the utility
> filters (setpts, trim, concat, etc.) working with subtitles too without
> more code duplication, the review is just "NAK".

What is the timeline for the audio+video merge ?
IIUC this would resolve this deadlock (with extra work adapting the patchset
so it would be work for SW adapting it and it would be work for you finishing
the merge)
Also can others help nicolas moving his work forward

What i suggest is to pick a time and then try to finish the merge before.
If it succeeds this patchset needs updating and can move forward without
this main objection
OTOH if the time is not hit, we agree that the objection can no longer be
used

nothing above is meant as a objection to anything, just my oppinion. Some thought 
i had which i hope could move this forward.
If people agree to anything else iam happy with that too, this mail is only
meant to help!

thx

[...]

-- 
Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB

Observe your enemies, for they first find out your faults. -- Antisthenes

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 195 bytes --]

[-- Attachment #2: Type: text/plain, Size: 251 bytes --]

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-07-03 17:07             ` Michael Niedermayer
@ 2022-07-03 17:25               ` Paul B Mahol
  2022-07-24 15:10               ` Nicolas George
  1 sibling, 0 replies; 217+ messages in thread
From: Paul B Mahol @ 2022-07-03 17:25 UTC (permalink / raw)
  To: FFmpeg development discussions and patches

On Sun, Jul 3, 2022 at 7:07 PM Michael Niedermayer <michael@niedermayer.cc>
wrote:

> On Sat, Jul 02, 2022 at 07:18:16PM +0200, Nicolas George wrote:
> > Paul B Mahol (12022-07-02):
> > > Can this be properly finally be fully reviewed and accepted?
> >
> > As long as the patch does not have a solution to have all the utility
> > filters (setpts, trim, concat, etc.) working with subtitles too without
> > more code duplication, the review is just "NAK".
>
> What is the timeline for the audio+video merge ?
>

Eternity.


> IIUC this would resolve this deadlock (with extra work adapting the
> patchset
> so it would be work for SW adapting it and it would be work for you
> finishing
> the merge)
> Also can others help nicolas moving his work forward
>
> What i suggest is to pick a time and then try to finish the merge before.
> If it succeeds this patchset needs updating and can move forward without
> this main objection
> OTOH if the time is not hit, we agree that the objection can no longer be
> used
>
> nothing above is meant as a objection to anything, just my oppinion. Some
> thought
> i had which i hope could move this forward.
> If people agree to anything else iam happy with that too, this mail is only
> meant to help!
>
> thx
>
> [...]
>
> --
> Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB
>
> Observe your enemies, for they first find out your faults. -- Antisthenes
> _______________________________________________
> 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".
>
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-07-03 10:42                           ` Paul B Mahol
@ 2022-07-04  8:45                             ` Jean-Baptiste Kempf
  0 siblings, 0 replies; 217+ messages in thread
From: Jean-Baptiste Kempf @ 2022-07-04  8:45 UTC (permalink / raw)
  To: ffmpeg-devel

On Sun, 3 Jul 2022, at 12:42, Paul B Mahol wrote:
>> But here, the discussion goes nowhere "I'm right", "No, I'm right", "No,
>> I'm correct" and will only evolve in insults and slander.
>>
> Why should I do this work?
>
> I will just create my own fork. (Already did.)

History shows that it's very hard to do a  successful FFmpeg fork.

Working together is more powerful, IMVHO.

-- 
Jean-Baptiste Kempf -  President
+33 672 704 734
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-07-03 17:07             ` Michael Niedermayer
  2022-07-03 17:25               ` Paul B Mahol
@ 2022-07-24 15:10               ` Nicolas George
  2022-07-24 18:38                 ` Soft Works
                                   ` (3 more replies)
  1 sibling, 4 replies; 217+ messages in thread
From: Nicolas George @ 2022-07-24 15:10 UTC (permalink / raw)
  To: FFmpeg development discussions and patches


[-- Attachment #1.1: Type: text/plain, Size: 5638 bytes --]

I hesitated a long time before replying, but all considering, at this
point expressing what is weighing on my heart cannot make things worse.


Michael Niedermayer (12022-07-03):
> What is the timeline for the audio+video merge ?

I cannot give a timeline because I do not work on FFmpeg on a schedule,
I work on FFmpeg in my free time, for fun. And lately, working on FFmpeg
has been really un-fun. Recently, my contributions have been met with
indifference at best (see when I proposed a XML parser six months ago:
nobody cared), outright hostility at worst. Under these circumstances,
whenever I am considering working on something FFmpeg-related, I usually
find something else more fun to do.

I do not recognize the project I started contributing to more than
fifteen years ago. I do not even recognize the project that boasted the
clever optimization framework that made FFVP9 possible, it has become
increasingly hostile to trying new and more efficient ways of doing
things in favor of a corporate never-take-risks style of coding. I am
more and more often considering giving up and cutting my losses.

> IIUC this would resolve this deadlock (with extra work adapting the patchset
> so it would be work for SW adapting it and it would be work for you finishing
> the merge)
> Also can others help nicolas moving his work forward
> 
> What i suggest is to pick a time and then try to finish the merge before.
> If it succeeds this patchset needs updating and can move forward without
> this main objection
> OTOH if the time is not hit, we agree that the objection can no longer be
> used

My answer as maintainer of the framework of libavfilter is: no.

Of course, maintainers are not dictators. The members of the project can
collectively decide otherwise. But I have to warn you about the
consequences.

First, the issue about negotiation is not he only severe flaw in this
patch series. I can immediately quote another one: for text subtitles,
the approach of this proposal to synchronization is to feed everything
to libass as it comes and see what comes out. It will work on easy
cases, when the subtitles are interleaved with the video or come from a
separate file. But as soon as a filter will, for example, adjust the
timing to make the subtitles more early, it will just not work. Of
course, it was not tested because this patch series does not even offer
the feature to adjust the time of subtitles, which is frankly
ridiculous, it is one of the most obvious thing people might want to do.

Note that I did not have to perform a full review of the patch series to
find this flaw. I have been preparing to implement subtitles filtering
for years now, I know which aspects are tricky and hard to implement
properly. I only had to check precisely how it was done. And it turns
out it was not done at all.

I suspect that if I were to do a full review, I would find a few other
flaws. But the author has made painfully clear that they did not respect
my expertise in this area, I have no reason to waste my time doing it.

It illustrates what it means to be a maintainer. It does not only mean
the task of reviewing and applying bug fixes. The maintainer holds in
their head a knowledge of the code that cannot be fully shared in
writing. The maintainer also holds in their head plans to evolve and
extend the code.

I have plans for libavfilter. Not just vague ideas, but precise plans on
how to reach the goal. I have plans for subtitles filtering, of course.
But not only.

I have plans for filtering data packets, so that bistream filters do not
need to have a separate and redundant API and ffmpeg does not need a
separate code path for -c copy.

I have plans for threading, or more precisely integrating filters in a
global parallelized task and I/O scheduler.

I have plans for seeking, with the seek target going back from the
output to the input(s).

I have plans for partial graph reconfiguration, to accommodate format
changes mid-stream and let applications alter filtering in the middle.

All of this is exciting. I am eager to work on any of this.

Unfortunately, before it can happen, boring things need to be done.
Parts of the framework of libavfilter are very fragile. Working on any
of these is likely to break cases that were specifically fixed in the
past.

I can work on boring things if they are necessary to reach the exciting
parts.

What I cannot do is motivate myself to work on the boring things with
the threat that the exciting things will be snatched under me by an
inferior version from somebody who just refuses to engage with the
boring necessary things.

If this patch series gets applied, it will make the boring things a lot
harder to do and it will ruin some of the plans I mentioned above. Under
these circumstances, do not expect me to work on libavfilter again any
time soon, even if it is to apply an obviously valid fix for an
exploitable security issue.

So, the choice is:

- Apply this patch series, find a new maintainer for libavfilter and
  kiss goodbye to seeking, threading, reconfiguration (and subtitles
  filtering that work usefully).

- Make it clear that this patch series is rejected until the framework
  is robust enough and has enough test coverage.

- Let the situation continue to rot.

Note: The most urgent and boring task is adding FATE tests to the
heuristics of the negotiation process. It is actually rather easy. I
would be happy to offer advice and even tutoring if somebody wants to
contribute.

Regards,

-- 
  Nicolas George

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

[-- Attachment #2: Type: text/plain, Size: 251 bytes --]

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-07-24 15:10               ` Nicolas George
@ 2022-07-24 18:38                 ` Soft Works
  2022-07-24 19:21                 ` Soft Works
                                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 217+ messages in thread
From: Soft Works @ 2022-07-24 18:38 UTC (permalink / raw)
  To: FFmpeg development discussions and patches



> -----Original Message-----
> From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of
> Nicolas George
> Sent: Sunday, July 24, 2022 5:10 PM
> To: FFmpeg development discussions and patches <ffmpeg-
> devel@ffmpeg.org>
> Subject: Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
> 
> I hesitated a long time before replying, but all considering, at this
> point expressing what is weighing on my heart cannot make things
> worse.
> 
> 
> Michael Niedermayer (12022-07-03):
> > What is the timeline for the audio+video merge ?
> 
> I cannot give a timeline because I do not work on FFmpeg on a
> schedule,
> I work on FFmpeg in my free time, for fun. And lately, working on
> FFmpeg
> has been really un-fun. Recently, my contributions have been met with
> indifference at best (see when I proposed a XML parser six months
> ago:
> nobody cared), outright hostility at worst. Under these
> circumstances,
> whenever I am considering working on something FFmpeg-related, I
> usually
> find something else more fun to do.
> 
> I do not recognize the project I started contributing to more than
> fifteen years ago. I do not even recognize the project that boasted
> the
> clever optimization framework that made FFVP9 possible, it has become
> increasingly hostile to trying new and more efficient ways of doing
> things in favor of a corporate never-take-risks style of coding. I am
> more and more often considering giving up and cutting my losses.
> 
> > IIUC this would resolve this deadlock (with extra work adapting the
> patchset
> > so it would be work for SW adapting it and it would be work for you
> finishing
> > the merge)
> > Also can others help nicolas moving his work forward
> >
> > What i suggest is to pick a time and then try to finish the merge
> before.
> > If it succeeds this patchset needs updating and can move forward
> without
> > this main objection
> > OTOH if the time is not hit, we agree that the objection can no
> longer be
> > used
> 
> My answer as maintainer of the framework of libavfilter is: no.
> 
> Of course, maintainers are not dictators. The members of the project
> can
> collectively decide otherwise. But I have to warn you about the
> consequences.
> 
> First, the issue about negotiation is not he only severe flaw in this
> patch series. 

Negotiation hasn't been implemented for audio+video yet. Neither 
does that patchset do it for audio+video+subtitles.
It is out of scope of this patchset. It can be done later or never,
not everybody is a fan of doing so, as comments have shown.
Clearly, this is in no way a showstopping reason as you had
conceded yourself recently.

> I can immediately quote another one: for text
> subtitles,
> the approach of this proposal to synchronization is to feed
> everything
> to libass as it comes and see what comes out. It will work on easy
> cases, when the subtitles are interleaved with the video or come from
> a separate file. 

- or come from decoded closed captions
- or come from graphic subtitles converted with the graphicsub2text
  filter
- or come from the subfeed filter after fixing durations
- or come from the subfeed filter ensuring a regular repetition
  (heartbeat)

Subtitle events don't need to come in linear order. Multiple
events can have identical start times, subtitle events can
overlap. 

The overlaytextsubs filter is meant to be a direct replacement 
for the existing subtitles filter, which performs additional 
opening, parsing and decoding of the source file in parallel, 
and avoiding that was one of the primary objectives I had for 
starting development.
That's why it was very important for me to preserve the exact 
same behavior as the overlaytextsubs filter exposes.

Other approaches for implementatino are surely possible as well. 
Traian, who did  the text2graphicsub filter had initially an 
implementation that handled the timing manually instead of letting 
libass do it, but it turned out that this can quickly become a 
really complex task, especially when overlapping events or 
animations are part of the game, so it came down to feeding 
everything to libass in the end, like the overlaytextsubs 
filter and the subtitles filters do.

The nice thing about having subtitle filtering is that there
is no fixed functionality involved where you can argue about 
right or wrong: anyone is free to contribute another filter 
which is pursuing a different approach. I would welcome that 
and there may be cases where an alternative method could be
advantageous, but it surely won't be superior in general.


> But as soon as a filter will, for example, adjust the
> timing to make the subtitles more early, it will just not work. Of
> course, it was not tested because this patch series does not even
> offer the feature to adjust the time of subtitles, which is frankly
> ridiculous, it is one of the most obvious thing people might want to
> do.

The only reason why there is no timing adjustment filter is that
I didn't need one. It is really easy to implement such filter.
The abilities of such kind of filter are limited by the individual
circumstances, though:
You can always delay subtitle presentation, but the amount of 
time that you can move them ahead is limited by the situation.
It depends on the amount of time they are muxed ahead in the 
source. This can range from zero (for example when originating
from closed captions) to a few seconds (e.g. with ocr-ed 
DVB subs).
In the latter case it's easy to subtract one or two seconds
to show subtitles earlier - in the former case, there is no
room for this - unless you would let the video frames queue
up at the overlaytextsubs filter to synchronize with the 
subtitle frames. 
You are absolutely right here: the overlaytextsubs filter doesn't 
do that, it doesn't use framesync. 
Besides the reason to produce equal results to the existing subtitles 
filter (which framesync would interfere with), there's another 
consideration which kept me from using framesync: 

The benefit would be small and very limited.

Let's look at an example: assuming we have a 4k 30fps video onto
which we want to overlay text subtitles. The text subtitles
are muxed "just-in-time" in the source and we want the subtitles
to be shown 3 seconds earlier.
In order to make this possible, it would be required to queue up 
3s * 30 fps = 90 frames at the overlay filter. And this is not 
the muxing queue, it's inside a filtergraph where we have 
uncompressed frames.
Assuming 4 bytes per pixel for simplicity * 3840 * 2160 = 32 MB
per frame. For 90 frames, this makes 2.8 GB memory for 3 seconds
delay. 
It is not unusual that subtitles are off by even larger time
spans (e.g. video has cut off intro but sub timings assume intro
to be present).
Even with sufficient RAM - as soon as hardware acceleration
is being used, you really wouldn't want to use previous GPU
memory for subtitle timing adjustments.

Eventually this brought me to the conclusion that this isn't 
a suitable approach for adjusting subtitle offsets except for 
small corrections. 
It's easy to add such filter to change timings and it's very
well possible to implement an overlay filter with a different 
behavior - I would be sincerely interested to see which 
alternative solution you would come up for this.


> this patch series does not even
> offer the feature to adjust the time of subtitles, which is frankly
> ridiculous, it is one of the most obvious thing people might want to
> do.

How did you come to assume that it wouldn't be possible to adjust 
the timing of subtitles at all? 

It's just that a much better and universal way than using a filter
is the typical approach used for audio video sync correction, 
which can even be combined with the overlay filter:

ffmpeg -i subtitlevideo.mkv -itsoffset -3 -i subtitlevideo.mkv -filter_complex "[1:s]subfeed[sub1];[0:v][sub1]overlaytextsubs[fout]" -map [fout] -map 0:a -sn -y out.mkv




> Note that I did not have to perform a full review of the patch series
> to
> find this flaw. I have been preparing to implement subtitles
> filtering
> for years now, I know which aspects are tricky and hard to implement
> properly. I only had to check precisely how it was done. And it turns
> out it was not done at all.

Then it's very weird that it's all working: dozens of examples for 
new functionality and all existing functionality is preserved.

 
> I suspect that if I were to do a full review, I would find a few
> other
> flaws. But the author has made painfully clear that they did not
> respect
> my expertise in this area, 

You kept talking about your expertise and the lack of mine, without
ever talking about any technical matters.

This time, you mentioned technical issues and you see me answering
in a very detailed way. We have seen how it cannot work and now you
see how it could work. If you stick to technical matters without 
dropping the typical discrediting subtext, it could all go well.

Thanks,
softworkz




_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-07-24 15:10               ` Nicolas George
  2022-07-24 18:38                 ` Soft Works
@ 2022-07-24 19:21                 ` Soft Works
  2022-07-25  6:44                 ` Ronald S. Bultje
  2022-07-25 11:32                 ` Michael Niedermayer
  3 siblings, 0 replies; 217+ messages in thread
From: Soft Works @ 2022-07-24 19:21 UTC (permalink / raw)
  To: FFmpeg development discussions and patches



> -----Original Message-----
> From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of
> Nicolas George
> Sent: Sunday, July 24, 2022 5:10 PM
> To: FFmpeg development discussions and patches <ffmpeg-
> devel@ffmpeg.org>
> Subject: Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
> 


> I suspect that if I were to do a full review, I would find a few
> other flaws. 


This is a very good example for the kind of behavior I'd kindly 
ask you to stop with. 


Do you think you have found a flaw?
===================================

=> please name it and explain it, ideally asking before calling it
   a "flaw"

(like your one "flaw" wasn't really one as I had laid out)


Do you think you COULD find a flaw?
===================================

=> Go ahead and find one - no need to tell


Thanks,
softworkz
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-07-24 15:10               ` Nicolas George
  2022-07-24 18:38                 ` Soft Works
  2022-07-24 19:21                 ` Soft Works
@ 2022-07-25  6:44                 ` Ronald S. Bultje
  2022-07-25 17:44                   ` Nicolas George
  2022-07-25 11:32                 ` Michael Niedermayer
  3 siblings, 1 reply; 217+ messages in thread
From: Ronald S. Bultje @ 2022-07-25  6:44 UTC (permalink / raw)
  To: FFmpeg development discussions and patches

Hi,

On Sun, Jul 24, 2022 at 11:10 PM Nicolas George <george@nsup.org> wrote:

> I do not recognize the project I started contributing to more than
> fifteen years ago. I do not even recognize the project that boasted the
> clever optimization framework that made FFVP9 possible, it has become
> increasingly hostile to trying new and more efficient ways of doing
> things in favor of a corporate never-take-risks style of coding.
>

We still do this, but currently outside FFmpeg (dav1d). So sadly, it's a
bit more out of the sphere of this mailinglist. So we take risks, and if
AV2-9 comes around, we might again. But we still consider ourselves part of
the FFmpeg community, and I still try to review ffvp9 patches when they
come up every once in a while. Don't lose hope, just find the positive
things and work on them. Ignore the rest. I know I did. I'm having fun
again.

I don't know if I should offer advice, but my $.02: maybe adding tests (I
don't mean ffmpeg invocations; some people would call this desired
outcomes) might help here. You probably remember how Michael tested patches
pre-FATE: for a particular patch, he'd send an FFmpeg commandline that
either A) gives (undesirable) different output before vs. after patch, or
B) should give some correct/desired output after the patch but (still)
doesn't (and this second would be what I'm inviting you to do). Tests don't
have to be scripted, they can simply be a list of features or behaviors
desired in the new design. It's true that perfection is the enemy of
progress, but I think you're right that we should try to strive for further
improvement if it is within reach, especially for base design or things
with API implications. (FFmpeg's API is a testament to poor design in some
places.) You're likely better at making a comprehensive list than anyone
else. Making the list public/explicit means other people can help
accomplish the full list of features.

<3
Ronald
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-07-24 15:10               ` Nicolas George
                                   ` (2 preceding siblings ...)
  2022-07-25  6:44                 ` Ronald S. Bultje
@ 2022-07-25 11:32                 ` Michael Niedermayer
  2022-07-25 19:01                   ` Nicolas George
  3 siblings, 1 reply; 217+ messages in thread
From: Michael Niedermayer @ 2022-07-25 11:32 UTC (permalink / raw)
  To: FFmpeg development discussions and patches


[-- Attachment #1.1: Type: text/plain, Size: 4982 bytes --]

On Sun, Jul 24, 2022 at 05:10:17PM +0200, Nicolas George wrote:
> I hesitated a long time before replying, but all considering, at this
> point expressing what is weighing on my heart cannot make things worse.
> 
> 
> Michael Niedermayer (12022-07-03):
> > What is the timeline for the audio+video merge ?
> 
> I cannot give a timeline because I do not work on FFmpeg on a schedule,
> I work on FFmpeg in my free time, for fun. And lately, working on FFmpeg
> has been really un-fun. Recently, my contributions have been met with

I think many if us, myself included have had un-fun periods in ffmpeg.
This on its own is already bad and we should strive to make project
contributions more fun to all.



> indifference at best (see when I proposed a XML parser six months ago:
> nobody cared), outright hostility at worst. Under these circumstances,
> whenever I am considering working on something FFmpeg-related, I usually
> find something else more fun to do.

If we add a XML parser to FFmpeg. Such a thing would be used by several
bits and we need to ensure it has redundant maintainership.
So i think a xml parser needs broad support by developers otherwise we
run the risk that if it has a single maintainer only and that maintainer
stops maintaining it that could be annoying to more than just the parser
itself.
I cannot remember exactly without re-reading the old thread what the issues
people had with the xml parser. If it was some component of it or in general.
But i think its mostly a question if theres more than one person who wants
to maintain it.


[...]

> It illustrates what it means to be a maintainer. It does not only mean
> the task of reviewing and applying bug fixes. The maintainer holds in
> their head a knowledge of the code that cannot be fully shared in
> writing. The maintainer also holds in their head plans to evolve and
> extend the code.
> 
> I have plans for libavfilter. Not just vague ideas, but precise plans on
> how to reach the goal. I have plans for subtitles filtering, of course.
> But not only.
> 
> I have plans for filtering data packets, so that bistream filters do not
> need to have a separate and redundant API and ffmpeg does not need a
> separate code path for -c copy.
> 
> I have plans for threading, or more precisely integrating filters in a
> global parallelized task and I/O scheduler.
> 
> I have plans for seeking, with the seek target going back from the
> output to the input(s).
> 
> I have plans for partial graph reconfiguration, to accommodate format
> changes mid-stream and let applications alter filtering in the middle.
> 
> All of this is exciting. I am eager to work on any of this.
> 
> Unfortunately, before it can happen, boring things need to be done.
> Parts of the framework of libavfilter are very fragile. Working on any
> of these is likely to break cases that were specifically fixed in the
> past.
> 
> I can work on boring things if they are necessary to reach the exciting
> parts.
> 

> What I cannot do is motivate myself to work on the boring things with
> the threat that the exciting things will be snatched under me by an
> inferior version from somebody who just refuses to engage with the
> boring necessary things.

If you work on libavfilter, noone will snatch it from you. If you
dont work on it, eventually someone else will attempt to take over and
push libavfilter in the direction he feels best which may or may not 
match what you want.
If theres something i can do to make libavfilter work more fun for you
please tell me.

I think it would be best if people worked a bit more together on this.
The current situation where work is split between your vission and this
patchset is kind of a bad thing.
Its a bit unfortunate noone involved seems to have the carismatic leader
skills to pull everyone on his side and then get that side to the point
that everyone is happy with the code 
and neither has a consensus emerged based on technical arguments.
I think maybe more a "what is best for the project to move forward" and a
less "how can i get my hard work in first" approuch might help
but we can also try the technical commiitee of course 
but iam not sure how much that would really help, it would be better if
some consensus would be reached and everyone would then work together on
implementing that
theres of course also the fork and merge approuch. Each side does whatever
they like in their repository and then we at some point compare and merge one
or both into ffmpeg. Iam not sure thats a good idea or not.

thx

[...]

-- 
Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB

Old school: Use the lowest level language in which you can solve the problem
            conveniently.
New school: Use the highest level language in which the latest supercomputer
            can solve the problem without the user falling asleep waiting.

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 195 bytes --]

[-- Attachment #2: Type: text/plain, Size: 251 bytes --]

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-07-25  6:44                 ` Ronald S. Bultje
@ 2022-07-25 17:44                   ` Nicolas George
  0 siblings, 0 replies; 217+ messages in thread
From: Nicolas George @ 2022-07-25 17:44 UTC (permalink / raw)
  To: FFmpeg development discussions and patches


[-- Attachment #1.1: Type: text/plain, Size: 4247 bytes --]

Ronald S. Bultje (12022-07-25):
> We still do this, but currently outside FFmpeg (dav1d). So sadly, it's a
> bit more out of the sphere of this mailinglist. So we take risks, and if
> AV2-9 comes around, we might again. But we still consider ourselves part of
> the FFmpeg community, and I still try to review ffvp9 patches when they
> come up every once in a while. Don't lose hope, just find the positive
> things and work on them. Ignore the rest. I know I did. I'm having fun
> again.
> 
> I don't know if I should offer advice, but my $.02: maybe adding tests (I
> don't mean ffmpeg invocations; some people would call this desired
> outcomes) might help here. You probably remember how Michael tested patches
> pre-FATE: for a particular patch, he'd send an FFmpeg commandline that
> either A) gives (undesirable) different output before vs. after patch, or
> B) should give some correct/desired output after the patch but (still)
> doesn't (and this second would be what I'm inviting you to do). Tests don't
> have to be scripted, they can simply be a list of features or behaviors
> desired in the new design. It's true that perfection is the enemy of
> progress, but I think you're right that we should try to strive for further
> improvement if it is within reach, especially for base design or things
> with API implications. (FFmpeg's API is a testament to poor design in some
> places.) You're likely better at making a comprehensive list than anyone
> else. Making the list public/explicit means other people can help
> accomplish the full list of features.

Thank you for these kind and encouraging words.

The fact that the new developments happen in separate project kinds of
confirms my impression that the project has become afraid of taking
risks.

It makes me think of these artists who found a rich patron and, dazzled
by the money and luxury, become afraid of offending them and have their
art lose its edge. FFmpeg is so eager to please our corporate users, to
look “serious”, to look “professional” that sometimes it almost feels
like grovelling, and it stifles novelty.

Another symptom of this I see is that Michael spends most of his time
managing releases and fuzzing and fixing trivial bugs, tasks that are
way below his skills and talent, instead of writing new code. Or maybe I
am just wrong and that is what he likes to do most.

As for me… I am not good at shaving cycles from inner loops, which is
the heart of FFmpeg's superiority, I cannot contribute to it. What I
think I can contribute (and thank you for suggesting I am not wrong) is
in areas of API and architecture.

Unfortunately, while support for a new codec or optimization on an
existing one will receive no objection on principle, work on API or
architecture affect everybody and therefore is subject to much more
discussion, possibly to the point of deadly bikeshedding or outright
rejection.

That makes it hard to invest time in it without some kind of a-priori
assurance. Unfortunately my attempts to spark discussions have been met
with indifference:

https://ffmpeg.org/pipermail/ffmpeg-devel/2020-December/274167.html
https://ffmpeg.org/pipermail/ffmpeg-devel/2020-December/thread.html#274167

(I realize I could probably work on internal error messages. As for
event loop, the other one that has received positive feedback, I was
trying to find a more efficient way out of a small quandary, I should
probably go back to it.)

Or even worse:

https://ffmpeg.org/pipermail/ffmpeg-devel/2021-December/290226.html
https://ffmpeg.org/pipermail/ffmpeg-devel/2021-December/thread.html#290226

(A good string API is a pre-requisite for several other projects.)

What I think this project need is a leader, or a leading committee:
somebody who can decide beforehand if a change in API or architecture is
worth it, and therefore worth the time of the developer, with a decision
that is binding to the project: when the code is written, other
developers may discuss technical details but not reject the change
itself.

Maybe the technical committee could endorse this role, even though it
was not exactly why it was elected.

Thanks,

-- 
  Nicolas George

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

[-- Attachment #2: Type: text/plain, Size: 251 bytes --]

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-07-25 11:32                 ` Michael Niedermayer
@ 2022-07-25 19:01                   ` Nicolas George
  2022-07-25 19:39                     ` Michael Niedermayer
  0 siblings, 1 reply; 217+ messages in thread
From: Nicolas George @ 2022-07-25 19:01 UTC (permalink / raw)
  To: FFmpeg development discussions and patches


[-- Attachment #1.1: Type: text/plain, Size: 2048 bytes --]

Michael Niedermayer (12022-07-25):
> I think many if us, myself included have had un-fun periods in ffmpeg.
> This on its own is already bad and we should strive to make project
> contributions more fun to all.

In my opinion, it would require at least two things:

- That the members of the Community Committee step in more proactively
  in budding conflicts. And take sides: not “both be nice” but “you
  started it, shut up”.

- A person or group of person capable of deciding ahead if a change that
  will affect every body is worth investing time implementing it.

> If we add a XML parser to FFmpeg. Such a thing would be used by several
> bits and we need to ensure it has redundant maintainership.
> So i think a xml parser needs broad support by developers otherwise we
> run the risk that if it has a single maintainer only and that maintainer
> stops maintaining it that could be annoying to more than just the parser
> itself.
> I cannot remember exactly without re-reading the old thread what the issues
> people had with the xml parser. If it was some component of it or in general.
> But i think its mostly a question if theres more than one person who wants
> to maintain it.

There is nothing to re-read:

https://ffmpeg.org/pipermail/ffmpeg-devel/2022-May/thread.html#296107

Nobody answered. Even to send files to make sure they would be
supported.

We would need reliable maintainers, you are right. But right now, we
rely on libxml2, and that is not ideal: it is not a standard library on
non-Linux systems, and even if it seems better nowadays, it used to be a
“security issue of the week” kind of library.

> If you work on libavfilter, noone will snatch it from you. If you
> dont work on it, eventually someone else will attempt to take over and
> push libavfilter in the direction he feels best which may or may not 
> match what you want.

I will try to motivate myself to advance the FATE coverage, which is the
blocking issue.

Thanks,

-- 
  Nicolas George

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

[-- Attachment #2: Type: text/plain, Size: 251 bytes --]

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-07-25 19:01                   ` Nicolas George
@ 2022-07-25 19:39                     ` Michael Niedermayer
  0 siblings, 0 replies; 217+ messages in thread
From: Michael Niedermayer @ 2022-07-25 19:39 UTC (permalink / raw)
  To: FFmpeg development discussions and patches


[-- Attachment #1.1: Type: text/plain, Size: 1337 bytes --]

On Mon, Jul 25, 2022 at 09:01:03PM +0200, Nicolas George wrote:
[...]
> > If we add a XML parser to FFmpeg. Such a thing would be used by several
> > bits and we need to ensure it has redundant maintainership.
> > So i think a xml parser needs broad support by developers otherwise we
> > run the risk that if it has a single maintainer only and that maintainer
> > stops maintaining it that could be annoying to more than just the parser
> > itself.
> > I cannot remember exactly without re-reading the old thread what the issues
> > people had with the xml parser. If it was some component of it or in general.
> > But i think its mostly a question if theres more than one person who wants
> > to maintain it.
> 
> There is nothing to re-read:
> 
> https://ffmpeg.org/pipermail/ffmpeg-devel/2022-May/thread.html#296107
> 
> Nobody answered. Even to send files to make sure they would be
> supported.

that says "WIP" in the subject. I think some people when they see WIP they
skip it and wait for a final version to review.

thx

[...]

-- 
Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB

The real ebay dictionary, page 2
"100% positive feedback" - "All either got their money back or didnt complain"
"Best seller ever, very honest" - "Seller refunded buyer after failed scam"

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 195 bytes --]

[-- Attachment #2: Type: text/plain, Size: 251 bytes --]

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-06-25  9:57       ` [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022 ffmpegagent
                           ` (26 preceding siblings ...)
  2022-07-02 16:39         ` [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022 Paul B Mahol
@ 2022-08-11 22:50         ` Soft Works
  2022-08-21  9:41           ` Paul B Mahol
  27 siblings, 1 reply; 217+ messages in thread
From: Soft Works @ 2022-08-11 22:50 UTC (permalink / raw)
  To: ffmpegagent, ffmpeg-devel
  Cc: Michael Niedermayer, Andriy Gelman, Andreas Rheinhardt



> -----Original Message-----
> From: ffmpegagent <ffmpegagent@gmail.com>
> Sent: Saturday, June 25, 2022 11:58 AM
> To: ffmpeg-devel@ffmpeg.org
> Cc: Michael Niedermayer <michael@niedermayer.cc>; Andreas Rheinhardt
> <andreas.rheinhardt@outlook.com>; Soft Works <softworkz@hotmail.com>;
> Andriy Gelman <andriy.gelman@gmail.com>; softworkz
> <softworkz@hotmail.com>
> Subject: [PATCH v5 00/25] Subtitle Filtering 2022
> 
> 
> Subtitle Filtering 2022
> =======================
> 
> This is a substantial update to the earlier subtitle filtering patch
> series.
> A primary goal has been to address others' concerns as much as
> possible on
> one side and to provide more clarity and control over the way things
> are
> working. Clarity is is specifically important to allow for a better
> understanding of the need for a subtitle start pts value that can be
> different from the frame's pts value. This is done by refactoring the
> subtitle timing fields in AVFrame, adding a frame field to indicate
> repeated
> subtitle frames, and finally the full removal of the heartbeat
> functionality, replaced by a new 'subfeed' filter that provides
> different
> modes for arbitrating subtitle frames in a filter graph. Finally,
> each
> subtitle filter's documentation has been amended by a section
> describing the
> filter's timeline behavior (in v3 update).
> 
> 
> Subtitle Filtering Demos
> ========================
> 
> I published a demonstration of subtitle filtering capabilities with
> OCR,
> text and bitmap subtitle manipulation involved: Demo 1: Text-
> Manipulation
> with Bitmap Subtitles
> [https://github.com/softworkz/SubtitleFilteringDemos/tree/master/Demo
> 1]


As there were no objections in any regard, I hope that we could make 
some progress regarding subtitle filtering soon.

The only recent statement that has been made was something like

"this may work in a limited number of cases but probably won't be 
capable to work for the whole range of real-life situations"

I understand how easily such statements can induce doubt into the 
audience, even on those who tend and try to be neutral in their 
assessments. I also think that replying once another time with 
something like "no, this isn't true" won't be much impressive.


Instead, I'm sharing a video with you, documenting my internal
test runs for text subtitle overlay (with and without subtitle 
filtering, with and without hw overlay, with a range of hw accelerations 
for encoding and decoding, scaling or no scaling and two different 
kinds of source files).

The diagrams at the right are a rather consumer focused, but still 
accurate. Just hwupload/download/hwmap are omitted and implied
by the data changing the "swim lanes".

https://github.com/softworkz/SubtitleFilteringDemos/tree/master/TestRun1

Commands and logs on request.

Best wishes,
softworkz
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-08-11 22:50         ` Soft Works
@ 2022-08-21  9:41           ` Paul B Mahol
  2022-08-21  9:51             ` Nicolas George
  2022-08-21 10:41             ` Jean-Baptiste Kempf
  0 siblings, 2 replies; 217+ messages in thread
From: Paul B Mahol @ 2022-08-21  9:41 UTC (permalink / raw)
  To: FFmpeg development discussions and patches
  Cc: ffmpegagent, Michael Niedermayer, Andriy Gelman, Andreas Rheinhardt

On Fri, Aug 12, 2022 at 12:51 AM Soft Works <softworkz@hotmail.com> wrote:

>
>
> > -----Original Message-----
> > From: ffmpegagent <ffmpegagent@gmail.com>
> > Sent: Saturday, June 25, 2022 11:58 AM
> > To: ffmpeg-devel@ffmpeg.org
> > Cc: Michael Niedermayer <michael@niedermayer.cc>; Andreas Rheinhardt
> > <andreas.rheinhardt@outlook.com>; Soft Works <softworkz@hotmail.com>;
> > Andriy Gelman <andriy.gelman@gmail.com>; softworkz
> > <softworkz@hotmail.com>
> > Subject: [PATCH v5 00/25] Subtitle Filtering 2022
> >
> >
> > Subtitle Filtering 2022
> > =======================
> >
> > This is a substantial update to the earlier subtitle filtering patch
> > series.
> > A primary goal has been to address others' concerns as much as
> > possible on
> > one side and to provide more clarity and control over the way things
> > are
> > working. Clarity is is specifically important to allow for a better
> > understanding of the need for a subtitle start pts value that can be
> > different from the frame's pts value. This is done by refactoring the
> > subtitle timing fields in AVFrame, adding a frame field to indicate
> > repeated
> > subtitle frames, and finally the full removal of the heartbeat
> > functionality, replaced by a new 'subfeed' filter that provides
> > different
> > modes for arbitrating subtitle frames in a filter graph. Finally,
> > each
> > subtitle filter's documentation has been amended by a section
> > describing the
> > filter's timeline behavior (in v3 update).
> >
> >
> > Subtitle Filtering Demos
> > ========================
> >
> > I published a demonstration of subtitle filtering capabilities with
> > OCR,
> > text and bitmap subtitle manipulation involved: Demo 1: Text-
> > Manipulation
> > with Bitmap Subtitles
> > [https://github.com/softworkz/SubtitleFilteringDemos/tree/master/Demo
> > 1]
>
>
> As there were no objections in any regard, I hope that we could make
> some progress regarding subtitle filtering soon.
>
> The only recent statement that has been made was something like
>
> "this may work in a limited number of cases but probably won't be
> capable to work for the whole range of real-life situations"
>
> I understand how easily such statements can induce doubt into the
> audience, even on those who tend and try to be neutral in their
> assessments. I also think that replying once another time with
> something like "no, this isn't true" won't be much impressive.
>
>
> Instead, I'm sharing a video with you, documenting my internal
> test runs for text subtitle overlay (with and without subtitle
> filtering, with and without hw overlay, with a range of hw accelerations
> for encoding and decoding, scaling or no scaling and two different
> kinds of source files).
>
> The diagrams at the right are a rather consumer focused, but still
> accurate. Just hwupload/download/hwmap are omitted and implied
> by the data changing the "swim lanes".
>
> https://github.com/softworkz/SubtitleFilteringDemos/tree/master/TestRun1
>
> Commands and logs on request.
>
>
We should more forward and merge this considerable subtitle work instead of
waiting for broken promises from other inactive developers.


> Best wishes,
> softworkz
> _______________________________________________
> 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".
>
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-08-21  9:41           ` Paul B Mahol
@ 2022-08-21  9:51             ` Nicolas George
  2022-08-21 10:41             ` Jean-Baptiste Kempf
  1 sibling, 0 replies; 217+ messages in thread
From: Nicolas George @ 2022-08-21  9:51 UTC (permalink / raw)
  To: FFmpeg development discussions and patches


[-- Attachment #1.1: Type: text/plain, Size: 227 bytes --]

Paul B Mahol (12022-08-21):
> We should more forward and merge this considerable subtitle work instead of
> waiting for broken promises from other inactive developers.
> 

Then push to your fork.

-- 
  Nicolas George

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

[-- Attachment #2: Type: text/plain, Size: 251 bytes --]

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-08-21  9:41           ` Paul B Mahol
  2022-08-21  9:51             ` Nicolas George
@ 2022-08-21 10:41             ` Jean-Baptiste Kempf
  2022-08-22 12:18               ` Anton Khirnov
  1 sibling, 1 reply; 217+ messages in thread
From: Jean-Baptiste Kempf @ 2022-08-21 10:41 UTC (permalink / raw)
  To: ffmpeg-devel


On Sun, 21 Aug 2022, at 11:41, Paul B Mahol wrote:
> We should more forward and merge this considerable subtitle work 

Are there parts of this work that have reach majority consensus?

-- 
Jean-Baptiste Kempf -  President
+33 672 704 734
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-08-21 10:41             ` Jean-Baptiste Kempf
@ 2022-08-22 12:18               ` Anton Khirnov
  2022-08-22 14:38                 ` Jean-Baptiste Kempf
  2022-08-22 22:08                 ` Soft Works
  0 siblings, 2 replies; 217+ messages in thread
From: Anton Khirnov @ 2022-08-22 12:18 UTC (permalink / raw)
  To: ffmpeg-devel

Quoting Jean-Baptiste Kempf (2022-08-21 12:41:20)
> 
> On Sun, 21 Aug 2022, at 11:41, Paul B Mahol wrote:
> > We should more forward and merge this considerable subtitle work 
> 
> Are there parts of this work that have reach majority consensus?

Almost exactly identical objections to the basic aspects of the API were
raised independently by me, Lynne, and Hendrik.
IIUC Soft Works still refuses to address them (though it's not so easy
to tell in a 200-email thread).

-- 
Anton Khirnov
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-08-22 12:18               ` Anton Khirnov
@ 2022-08-22 14:38                 ` Jean-Baptiste Kempf
  2022-08-24 22:19                   ` Soft Works
  2022-08-26 20:47                   ` Anton Khirnov
  2022-08-22 22:08                 ` Soft Works
  1 sibling, 2 replies; 217+ messages in thread
From: Jean-Baptiste Kempf @ 2022-08-22 14:38 UTC (permalink / raw)
  To: ffmpeg-devel

On Mon, 22 Aug 2022, at 14:18, Anton Khirnov wrote:
> Almost exactly identical objections to the basic aspects of the API were
> raised independently by me, Lynne, and Hendrik.
> IIUC Soft Works still refuses to address them (though it's not so easy
> to tell in a 200-email thread).

OK. I lost the lists of objections, then.

-- 
Jean-Baptiste Kempf -  President
+33 672 704 734
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-08-22 12:18               ` Anton Khirnov
  2022-08-22 14:38                 ` Jean-Baptiste Kempf
@ 2022-08-22 22:08                 ` Soft Works
  2022-08-22 23:08                   ` Soft Works
  1 sibling, 1 reply; 217+ messages in thread
From: Soft Works @ 2022-08-22 22:08 UTC (permalink / raw)
  To: FFmpeg development discussions and patches



________________________________________
From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> on behalf of Anton Khirnov <anton@khirnov.net>
Sent: Monday, August 22, 2022 2:18 PM
To: ffmpeg-devel
Subject: Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022

Quoting Jean-Baptiste Kempf (2022-08-21 12:41:20)
>
> On Sun, 21 Aug 2022, at 11:41, Paul B Mahol wrote:
> > We should more forward and merge this considerable subtitle work
>
> Are there parts of this work that have reach majority consensus?

Almost exactly identical objections to the basic aspects of the API were
raised independently by me, Lynne, and Hendrik.
IIUC Soft Works still refuses to address them (though it's not so easy
to tell in a 200-email thread).

---

Anton,

thanks for the reply. Please correct me if I'm wrong. From my memory
and understanding, the one any only remaining point of discussion was
the necessity to have a separate field for subtitle start PTS in addition to
the AVFrame's PTS field.

I wasn't refusing to make a change, but I have taken a lot of effort to 
explain the reasons for that necessity.
I did that in several chats on IRC, on the ML, and recently, I have written 
an article especially to address that concern and better explain the 
background:

https://github.com/softworkz/SubtitleFilteringDemos/issues/1

It remained unresponded (but maybe unnoticed?).

Bottom line is that without having the separate subtitle pts field,
the whole patchset cannot work.

@Anton: you said the reason for this is because I would have 
designed it like this. But this not the case. The reason why this
field is needed it due to the way how libavfilter is designed to 
work.
The only way to avoid that second field would be to fundamentally
rework the scheduling of frames in filter graphs and making changes
to a core part like that (which is working pretty well and reliably btw)
would impose a huge risk of regressions and incompatibilities 
(it's more a guarantee for issues rather than an abstract risk only),
which doesn't make any sense to do at this time and in this 
context.

My conclusion is that having that one additional field in AVFrame
is by far the better option.

Please let me know what you think should be done and what kind
of concern you have with regards to the additional field in AVFrame.
It might not qualify as a top candidate for "API Beauty", but it's none
of the worst ones either.
Your preceding arguments were based on the assumption that it
could easily be avoided. I hope the referenced article helps to 
understand why it can't (without fundamental changes to libavfilter).

Please also let me know whether there are any other objections or
desired changes and I'll try to address them. I am in no way refusing
to make changes, as long as they are feasible.

Thanks,
softworkz

PS: Please excuse potentially bad e.mail formatting, I'm off-site

 














_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-08-22 22:08                 ` Soft Works
@ 2022-08-22 23:08                   ` Soft Works
  0 siblings, 0 replies; 217+ messages in thread
From: Soft Works @ 2022-08-22 23:08 UTC (permalink / raw)
  To: FFmpeg development discussions and patches



________________________________________
From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> on behalf of Soft Works <softworkz@hotmail.com>
Sent: Tuesday, August 23, 2022 12:08 AM
To: FFmpeg development discussions and patches
Subject: Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022



________________________________________
From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> on behalf of Anton Khirnov <anton@khirnov.net>
Sent: Monday, August 22, 2022 2:18 PM
To: ffmpeg-devel
Subject: Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022

Quoting Jean-Baptiste Kempf (2022-08-21 12:41:20)
>
> On Sun, 21 Aug 2022, at 11:41, Paul B Mahol wrote:
> > We should more forward and merge this considerable subtitle work
>
> Are there parts of this work that have reach majority consensus?

Almost exactly identical objections to the basic aspects of the API were
raised independently by me, Lynne, and Hendrik.
IIUC Soft Works still refuses to address them (though it's not so easy
to tell in a 200-email thread).

---

Anton,

thanks for the reply. Please correct me if I'm wrong. From my memory
and understanding, the one any only remaining point of discussion was
the necessity to have a separate field for subtitle start PTS in addition to
the AVFrame's PTS field.

I wasn't refusing to make a change, but I have taken a lot of effort to
explain the reasons for that necessity.
I did that in several chats on IRC, on the ML, and recently, I have written
an article especially to address that concern and better explain the
background:

https://github.com/softworkz/SubtitleFilteringDemos/issues/1

It remained unresponded (but maybe unnoticed?).

Bottom line is that without having the separate subtitle pts field,
the whole patchset cannot work.

@Anton: you said the reason for this is because I would have
designed it like this. But this not the case. The reason why this
field is needed it due to the way how libavfilter is designed to
work.
The only way to avoid that second field would be to fundamentally
rework the scheduling of frames in filter graphs and making changes
to a core part like that (which is working pretty well and reliably btw)
would impose a huge risk of regressions and incompatibilities
(it's more a guarantee for issues rather than an abstract risk only),
which doesn't make any sense to do at this time and in this
context.

My conclusion is that having that one additional field in AVFrame
is by far the better option.

Please let me know what you think should be done and what kind
of concern you have with regards to the additional field in AVFrame.
It might not qualify as a top candidate for "API Beauty", but it's none
of the worst ones either.
Your preceding arguments were based on the assumption that it
could easily be avoided. I hope the referenced article helps to
understand why it can't (without fundamental changes to libavfilter).

Please also let me know whether there are any other objections or
desired changes and I'll try to address them. I am in no way refusing
to make changes, as long as they are feasible.

Thanks,
softworkz


------------

Other alternatives that were discussed:

1. Move the SubtitleStartTime field to AVSubtitleArea

One AVFrame can have multiple AVSubtitleArea instances
(bitmaps in case of graphic subs or text events in case of text subs).

But all AVSubtitleArea instances of an AVFrame must always have the
same start and duration - so it would be semantically incorrect
to have those values at the area level (when a frame has multiple
areas, with different timings, which value is valid? The ones from 
area0 and the values from area1 to areaN are irrelevant?)


2. Put the SubtitleStartTime into AVFrame's user/opaque data field

This is what Lynne had suggested. It avoids adding an additional 
field to AVFrame but it blocks the use of the user/opaque field for 
actual user data in case of subtitle frames.
Also, each subtitle decoder, each subtitle encoder and many subtitle
filters would need to cast the user/opaque field of AVFrame back and
forth to a time value.


Neither of the two options does make much sense to me, but when 
there would be consensus on any of those, then I'd be ok with it and
make those changes.

Thanks,
softworkz
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-08-22 14:38                 ` Jean-Baptiste Kempf
@ 2022-08-24 22:19                   ` Soft Works
  2022-08-26 20:47                   ` Anton Khirnov
  1 sibling, 0 replies; 217+ messages in thread
From: Soft Works @ 2022-08-24 22:19 UTC (permalink / raw)
  To: FFmpeg development discussions and patches


> -----Original Message-----
> From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of
> Jean-Baptiste Kempf
> Sent: Monday, August 22, 2022 4:39 PM
> To: ffmpeg-devel@ffmpeg.org
> Subject: Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
> 
> On Mon, 22 Aug 2022, at 14:18, Anton Khirnov wrote:
> > Almost exactly identical objections to the basic aspects of the API
> were
> > raised independently by me, Lynne, and Hendrik.
> > IIUC Soft Works still refuses to address them (though it's not so
> easy
> > to tell in a 200-email thread).
> 
> OK. I lost the lists of objections, then.
> 
> --
> Jean-Baptiste Kempf -  President


Could everybody who still has any objection PLEASE name it with reasoning
and explain in which way it should be resolved?


> On Mon, 22 Aug 2022, at 14:18, Anton Khirnov wrote:
...
> (though it's not so easy to tell in a 200-email thread)

Yes that's true. For that reason it is not helpful to talk about
unspecified objections from more than half a year ago.

This is not further actionable without having a list of specific objections.
When nobody responds, we need to assume that there aren't any left.

Thanks,
softworkz
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-08-22 14:38                 ` Jean-Baptiste Kempf
  2022-08-24 22:19                   ` Soft Works
@ 2022-08-26 20:47                   ` Anton Khirnov
  2022-08-26 22:48                     ` Soft Works
  2022-08-31  1:39                     ` Anton Khirnov
  1 sibling, 2 replies; 217+ messages in thread
From: Anton Khirnov @ 2022-08-26 20:47 UTC (permalink / raw)
  To: FFmpeg development discussions and patches

Quoting Soft Works (2022-08-25 00:19:33)
> 
> > -----Original Message-----
> > From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of
> > Jean-Baptiste Kempf
> > Sent: Monday, August 22, 2022 4:39 PM
> > To: ffmpeg-devel@ffmpeg.org
> > Subject: Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
> > 
> > On Mon, 22 Aug 2022, at 14:18, Anton Khirnov wrote:
> > > Almost exactly identical objections to the basic aspects of the API
> > were
> > > raised independently by me, Lynne, and Hendrik.
> > > IIUC Soft Works still refuses to address them (though it's not so
> > easy
> > > to tell in a 200-email thread).
> > 
> > OK. I lost the lists of objections, then.
> > 
> > --
> > Jean-Baptiste Kempf -  President
> 
> 
> Could everybody who still has any objection PLEASE name it with reasoning
> and explain in which way it should be resolved?

Most of the main objections are mentioned in [1]. As far as I can tell,
none of them were adequately addressed.

> I wasn't refusing to make a change, but I have taken a lot of effort to 
> explain the reasons for that necessity.
> I did that in several chats on IRC, on the ML, and recently, I have
> written 
> an article especially to address that concern and better explain the 
> background:
> 
> https://github.com/softworkz/SubtitleFilteringDemos/issues/1
> 
> It remained unresponded (but maybe unnoticed?).

Sorry, but all explanations I've seen from you are walls of text from
which I was never able to extract a solid argument that is not circular
and does not leap to unsupported conclusions.

And it is not just me, as far as I can tell, none of the others were
convinced either.

Frankly, you write too many words. A good argument about something like
this should fit in a paragraph. Maybe followed by some extended
explanations and clarifications, but the core of it should be quite
short and right at the top, not buried under heaps of introductory
remarks and examples. And if you cannot boil down your argument to a few
words then maybe it's not a very strong one.

> > On Mon, 22 Aug 2022, at 14:18, Anton Khirnov wrote:
> ...
> > (though it's not so easy to tell in a 200-email thread)
> 
> Yes that's true. For that reason it is not helpful to talk about
> unspecified objections from more than half a year ago.

You are the author of this set, it is _your_ job to keep track of what
has and has not been addressed. And if you want reviews, you should also
try to make things easy to review.

> This is not further actionable without having a list of specific objections.
> When nobody responds, we need to assume that there aren't any left.

Or maybe people just got tired of repeating the same objections to the
same patches being submitted again and again.

[1] http://lists.ffmpeg.org/pipermail/ffmpeg-devel/2021-December/288894.html
    Message-Id <MqASpFF--3-2@lynne.ee>

-- 
Anton Khirnov
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-08-26 20:47                   ` Anton Khirnov
@ 2022-08-26 22:48                     ` Soft Works
  2022-08-31  1:39                     ` Anton Khirnov
  1 sibling, 0 replies; 217+ messages in thread
From: Soft Works @ 2022-08-26 22:48 UTC (permalink / raw)
  To: FFmpeg development discussions and patches



> -----Original Message-----
> From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of
> Anton Khirnov
> Sent: Friday, August 26, 2022 10:47 PM
> To: FFmpeg development discussions and patches <ffmpeg-
> devel@ffmpeg.org>
> Subject: Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
> 
> Quoting Soft Works (2022-08-25 00:19:33)
> >
> > > -----Original Message-----
> > > From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of
> > > Jean-Baptiste Kempf
> > > Sent: Monday, August 22, 2022 4:39 PM
> > > To: ffmpeg-devel@ffmpeg.org
> > > Subject: Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering
> 2022
> > >
> > > On Mon, 22 Aug 2022, at 14:18, Anton Khirnov wrote:
> > > > Almost exactly identical objections to the basic aspects of the
> API
> > > were
> > > > raised independently by me, Lynne, and Hendrik.
> > > > IIUC Soft Works still refuses to address them (though it's not
> so
> > > easy
> > > > to tell in a 200-email thread).
> > >
> > > OK. I lost the lists of objections, then.
> > >
> > > --
> > > Jean-Baptiste Kempf -  President
> >
> >
> > Could everybody who still has any objection PLEASE name it with
> reasoning
> > and explain in which way it should be resolved?
> 
> Most of the main objections are mentioned in [1]. As far as I can
> tell, none of them were adequately addressed.

Hi Anton,

thanks a lot for your reply.

In fact, all of these were addressed long ago.
http://lists.ffmpeg.org/pipermail/ffmpeg-devel/2021-December/288894.html

That message you referenced is about 4 issues:


1. "Milliseconds?"

I have changed fields to int64_t values in AV_TIME_BASE,
please see below.


2. "There's no reason why this cannot be handled using the buffer
    and data fields"

I had explained the reasons and in conversation on IRC, Lynne was
no longer insisting on this AFAIR.


3. "I'm not going to accept a field which is instantly deprecated."

I have reworked this and there is no longer a field that is 
instantly deprecated.


3. "As we've discussed multiple times, please merge this into
   the regular frame PTS field. We already have _2_ necessary
   stard/end fields."

I have addressed this as well already. There are no longer 3 
but only 2 fields remaining (int64_t in AV_TIME_BASE): 

AVFrame.subtitle_timing.start_pts
AVFrame.subtitle_timing.duration



> Frankly, you write too many words. A good argument about something
> like
> this should fit in a paragraph. Maybe followed by some extended
> explanations and clarifications, but the core of it should be quite
> short and right at the top, not buried under heaps of introductory
> remarks and examples. And if you cannot boil down your argument to a
> few words then maybe it's not a very strong one.

It's a complicated matter and after I had seen that the situation 
wasn't understood, I started to get more into detail.

I will try to write less text in the future.


> Or maybe people just got tired of repeating the same objections to
> the
> same patches being submitted again and again.

Or maybe people didn't realize that the objections were addressed
already?

> You are the author of this set, it is _your_ job to keep track of
> what has and has not been addressed

I do that, and the count of unaddressed objections that I'm aware
of is zero.

That's why I'm asking whether anybody would still have an objection
or might not see her/his objection being addressed adequately.


Thanks again,
softworkz


PS: the one thing that was under discussion on IRC was about why
the subtitle start_pts cannot be the same as the AVFrame pts
and that was the point I was explaining in so much detail.




_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-08-26 20:47                   ` Anton Khirnov
  2022-08-26 22:48                     ` Soft Works
@ 2022-08-31  1:39                     ` Anton Khirnov
  2022-08-31  4:02                       ` Soft Works
  2022-10-10 11:08                       ` Soft Works
  1 sibling, 2 replies; 217+ messages in thread
From: Anton Khirnov @ 2022-08-31  1:39 UTC (permalink / raw)
  To: FFmpeg development discussions and patches

Quoting Soft Works (2022-08-27 00:48:01)
> 2. "There's no reason why this cannot be handled using the buffer
>     and data fields"
> 
> I had explained the reasons and in conversation on IRC, Lynne was
> no longer insisting on this AFAIR.

I did not see this explanation, can you restate it here?

If you claim the other points were addressed I will look at the patches
again.

-- 
Anton Khirnov
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-08-31  1:39                     ` Anton Khirnov
@ 2022-08-31  4:02                       ` Soft Works
  2022-09-20 14:30                         ` Soft Works
  2022-11-14 11:10                         ` Soft Works
  2022-10-10 11:08                       ` Soft Works
  1 sibling, 2 replies; 217+ messages in thread
From: Soft Works @ 2022-08-31  4:02 UTC (permalink / raw)
  To: FFmpeg development discussions and patches



> -----Original Message-----
> From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of
> Anton Khirnov
> Sent: Wednesday, August 31, 2022 3:40 AM
> To: FFmpeg development discussions and patches <ffmpeg-
> devel@ffmpeg.org>
> Subject: Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
> 
> Quoting Soft Works (2022-08-27 00:48:01)
> > 2. "There's no reason why this cannot be handled using the buffer
> >     and data fields"
> >
> > I had explained the reasons and in conversation on IRC, Lynne was
> > no longer insisting on this AFAIR.
> 
> I did not see this explanation, can you restate it here?

Sure. Let's look at the AVSubtitleArea struct first:


  typedef struct AVSubtitleArea {

    enum AVSubtitleType type;
    int flags;

    int x;         ///< top left corner  of area.
    int y;         ///< top left corner  of area.
    int w;         ///< width            of area.
    int h;         ///< height           of area.
    int nb_colors; ///< number of colors in bitmap palette (@ref pal).

    /**
     * Buffers and line sizes for the bitmap of this subtitle.
     *
     * @{
     */
    AVBufferRef *buf[AV_NUM_BUFFER_POINTERS];
    int linesize[AV_NUM_BUFFER_POINTERS];
    /**
     * @}
     */

    uint32_t pal[256]; ///< RGBA palette for the bitmap.

    char *text;        ///< 0-terminated plain UTF-8 text
    char *ass;         ///< 0-terminated ASS/SSA compatible event line.

  } AVSubtitleArea;



These structs are stored in AVFrame like this:


    /**
     * Number of items in the @ref subtitle_areas array.
     */
    unsigned num_subtitle_areas;

    /**
     * Array of subtitle areas, may be empty.
     */
    AVSubtitleArea **subtitle_areas;



The question was "why this cannot be handled using the buffer
and data fields?" - there are multiple reasons:

1. Area Count

In ASS subtitles it's not uncommon that animations are defined
through subtitle events (regular ass events). This can go as 
far as that dust particles are flying around on the screen and
each particle has its own subtitle event/line which defines 
how it flies from x to y and how fast, etc.
Not yet, but in a future update, the ass decoder should be 
improved in a way that it doesn't emit an individual AVFrame
for each event line but bundles subsequent events together 
when these would have the same start time and duration.
As a result, we can have AVFrames with dozens or even a hundred 
AVSubtitleArea items in extreme cases.

The buffer and data fields are an array of fixed size (currently
8). Increasing to 16 or 32 would not help: there will still be
cases where this does not suffice.

2. What exactly should be stored in frame->buf[]?

Should we store the AVSubtitleArea structs as AVBuffer there?

Or should we only store data in those buffers? If yes, which 
data? The subtitle bitmap probably. How about the subtitle 
text - maybe and area has even both. And should we also
store the palette in some frame->buf[n]?
If yes, how to keep track of which data is stored in which 
buffer? 
Further, there's a documented convention that requires that
non-zero buffers are contiguous, i.e. there must not be
an empty buffer pointer between two other a non-empty buffer
pointers. This would require to re-arrange the buffers to
close any gap when some subtitle area data is removed, zeroed
out or has been converted to another format.
Closing such gap also require to update any mapping information
("which buffer is related to which area?")

That's a lot of tedious work and every API user (or codec/filter
developer) would need to do it in the right way.



3. AVFrame Code Logic Mistmatch

A subtitle frame can have 0 to N areas, which means that a 
frame without any area is still valid. Opposed to that, existing
(code all over the place in ffmpeg) is treating an AVFrame
as invalid when the first data buffer is empty,
i.e. frame->buf[0] != NULL

To accommodate to this situation, subtitle frames are always 
creating a 1-byte buffer for buf[0].

When we would want subtitle data to be stored in those buffers,
every developer would need to be aware about the requirement
to have that dummy buffer in the first array element. When something
is to be stored, the dummy buffer would need to be freed first
before storing the actual data.
And when the last buffer gets deleted, API users would need to
know to create a new dummy buffer.


That fixed size array might be a good fit for image data, but 
it's not for subtitle data.


The approach in my patchset is simple, intuitive and everybody
can easily work with it without needing any special knowledge:


    unsigned num_subtitle_areas;
    AVSubtitleArea **subtitle_areas;

IMHO, this is the most suitable pattern for this situation.



> If you claim the other points were addressed I will look at the
> patches
> again.

Oh cool, that would be great!

Thanks,
softworkz
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-08-31  4:02                       ` Soft Works
@ 2022-09-20 14:30                         ` Soft Works
  2022-11-14 11:10                         ` Soft Works
  1 sibling, 0 replies; 217+ messages in thread
From: Soft Works @ 2022-09-20 14:30 UTC (permalink / raw)
  To: FFmpeg development discussions and patches



> -----Original Message-----
> From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of
> Soft Works
> Sent: Wednesday, August 31, 2022 6:02 AM
> To: FFmpeg development discussions and patches <ffmpeg-
> devel@ffmpeg.org>
> Subject: Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
> 
> 
> 
> > -----Original Message-----
> > From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of
> > Anton Khirnov
> > Sent: Wednesday, August 31, 2022 3:40 AM
> > To: FFmpeg development discussions and patches <ffmpeg-
> > devel@ffmpeg.org>
> > Subject: Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering
> 2022
> >
> > Quoting Soft Works (2022-08-27 00:48:01)
> > > 2. "There's no reason why this cannot be handled using the buffer
> > >     and data fields"
> > >
> > > I had explained the reasons and in conversation on IRC, Lynne was
> > > no longer insisting on this AFAIR.
> >
> > I did not see this explanation, can you restate it here?
> 
> Sure. Let's look at the AVSubtitleArea struct first:
> 
> 
>   typedef struct AVSubtitleArea {
> 
>     enum AVSubtitleType type;
>     int flags;
> 
>     int x;         ///< top left corner  of area.
>     int y;         ///< top left corner  of area.
>     int w;         ///< width            of area.
>     int h;         ///< height           of area.
>     int nb_colors; ///< number of colors in bitmap palette (@ref
> pal).
> 
>     /**
>      * Buffers and line sizes for the bitmap of this subtitle.
>      *
>      * @{
>      */
>     AVBufferRef *buf[AV_NUM_BUFFER_POINTERS];
>     int linesize[AV_NUM_BUFFER_POINTERS];
>     /**
>      * @}
>      */
> 
>     uint32_t pal[256]; ///< RGBA palette for the bitmap.
> 
>     char *text;        ///< 0-terminated plain UTF-8 text
>     char *ass;         ///< 0-terminated ASS/SSA compatible event
> line.
> 
>   } AVSubtitleArea;
> 
> 
> 
> These structs are stored in AVFrame like this:
> 
> 
>     /**
>      * Number of items in the @ref subtitle_areas array.
>      */
>     unsigned num_subtitle_areas;
> 
>     /**
>      * Array of subtitle areas, may be empty.
>      */
>     AVSubtitleArea **subtitle_areas;
> 
> 
> 
> The question was "why this cannot be handled using the buffer
> and data fields?" - there are multiple reasons:
> 
> 1. Area Count
> 
> In ASS subtitles it's not uncommon that animations are defined
> through subtitle events (regular ass events). This can go as
> far as that dust particles are flying around on the screen and
> each particle has its own subtitle event/line which defines
> how it flies from x to y and how fast, etc.
> Not yet, but in a future update, the ass decoder should be
> improved in a way that it doesn't emit an individual AVFrame
> for each event line but bundles subsequent events together
> when these would have the same start time and duration.
> As a result, we can have AVFrames with dozens or even a hundred
> AVSubtitleArea items in extreme cases.
> 
> The buffer and data fields are an array of fixed size (currently
> 8). Increasing to 16 or 32 would not help: there will still be
> cases where this does not suffice.
> 
> 2. What exactly should be stored in frame->buf[]?
> 
> Should we store the AVSubtitleArea structs as AVBuffer there?
> 
> Or should we only store data in those buffers? If yes, which
> data? The subtitle bitmap probably. How about the subtitle
> text - maybe and area has even both. And should we also
> store the palette in some frame->buf[n]?
> If yes, how to keep track of which data is stored in which
> buffer?
> Further, there's a documented convention that requires that
> non-zero buffers are contiguous, i.e. there must not be
> an empty buffer pointer between two other a non-empty buffer
> pointers. This would require to re-arrange the buffers to
> close any gap when some subtitle area data is removed, zeroed
> out or has been converted to another format.
> Closing such gap also require to update any mapping information
> ("which buffer is related to which area?")
> 
> That's a lot of tedious work and every API user (or codec/filter
> developer) would need to do it in the right way.
> 
> 
> 
> 3. AVFrame Code Logic Mistmatch
> 
> A subtitle frame can have 0 to N areas, which means that a
> frame without any area is still valid. Opposed to that, existing
> (code all over the place in ffmpeg) is treating an AVFrame
> as invalid when the first data buffer is empty,
> i.e. frame->buf[0] != NULL
> 
> To accommodate to this situation, subtitle frames are always
> creating a 1-byte buffer for buf[0].
> 
> When we would want subtitle data to be stored in those buffers,
> every developer would need to be aware about the requirement
> to have that dummy buffer in the first array element. When something
> is to be stored, the dummy buffer would need to be freed first
> before storing the actual data.
> And when the last buffer gets deleted, API users would need to
> know to create a new dummy buffer.
> 
> 
> That fixed size array might be a good fit for image data, but
> it's not for subtitle data.
> 
> 
> The approach in my patchset is simple, intuitive and everybody
> can easily work with it without needing any special knowledge:
> 
> 
>     unsigned num_subtitle_areas;
>     AVSubtitleArea **subtitle_areas;
> 
> IMHO, this is the most suitable pattern for this situation.
> 
> 
> 
> > If you claim the other points were addressed I will look at the
> > patches
> > again.
> 
> Oh cool, that would be great!

Hi Anton,

when do you think you could find some time to review the patch set?
Just let me know, then I'd rebase and submit an updated version.

Thanks,
softworkz




_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-08-31  1:39                     ` Anton Khirnov
  2022-08-31  4:02                       ` Soft Works
@ 2022-10-10 11:08                       ` Soft Works
  1 sibling, 0 replies; 217+ messages in thread
From: Soft Works @ 2022-10-10 11:08 UTC (permalink / raw)
  To: FFmpeg development discussions and patches



> -----Original Message-----
> From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of
> Anton Khirnov
> Sent: Wednesday, August 31, 2022 3:40 AM
> To: FFmpeg development discussions and patches <ffmpeg-
> devel@ffmpeg.org>
> Subject: Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
> 
> Quoting Soft Works (2022-08-27 00:48:01)
> > 2. "There's no reason why this cannot be handled using the buffer
> >     and data fields"
> >
> > I had explained the reasons and in conversation on IRC, Lynne was
> > no longer insisting on this AFAIR.
> 
> I did not see this explanation, can you restate it here?
> 
> If you claim the other points were addressed I will look at the
> patches
> again.
> 
> --
> Anton Khirnov
> _______________________________________________


Friendly Ping :-)
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

* Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
  2022-08-31  4:02                       ` Soft Works
  2022-09-20 14:30                         ` Soft Works
@ 2022-11-14 11:10                         ` Soft Works
  1 sibling, 0 replies; 217+ messages in thread
From: Soft Works @ 2022-11-14 11:10 UTC (permalink / raw)
  To: FFmpeg development discussions and patches



> -----Original Message-----
> From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of
> Soft Works
> Sent: Wednesday, August 31, 2022 6:02 AM
> To: FFmpeg development discussions and patches <ffmpeg-
> devel@ffmpeg.org>
> Subject: Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022
> 
> 
> 
> > -----Original Message-----
> > From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of
> > Anton Khirnov
> > Sent: Wednesday, August 31, 2022 3:40 AM
> > To: FFmpeg development discussions and patches <ffmpeg-
> > devel@ffmpeg.org>
> > Subject: Re: [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering
> 2022
> >
> > Quoting Soft Works (2022-08-27 00:48:01)
> > > 2. "There's no reason why this cannot be handled using the buffer
> > >     and data fields"
> > >
> > > I had explained the reasons and in conversation on IRC, Lynne was
> > > no longer insisting on this AFAIR.
> >
> > I did not see this explanation, can you restate it here?
> 

You had asked me to restate the explanation.

I did that (below) but you never responded.

Thanks,
softworkz



> Sure. Let's look at the AVSubtitleArea struct first:
> 
> 
>   typedef struct AVSubtitleArea {
> 
>     enum AVSubtitleType type;
>     int flags;
> 
>     int x;         ///< top left corner  of area.
>     int y;         ///< top left corner  of area.
>     int w;         ///< width            of area.
>     int h;         ///< height           of area.
>     int nb_colors; ///< number of colors in bitmap palette (@ref
> pal).
> 
>     /**
>      * Buffers and line sizes for the bitmap of this subtitle.
>      *
>      * @{
>      */
>     AVBufferRef *buf[AV_NUM_BUFFER_POINTERS];
>     int linesize[AV_NUM_BUFFER_POINTERS];
>     /**
>      * @}
>      */
> 
>     uint32_t pal[256]; ///< RGBA palette for the bitmap.
> 
>     char *text;        ///< 0-terminated plain UTF-8 text
>     char *ass;         ///< 0-terminated ASS/SSA compatible event
> line.
> 
>   } AVSubtitleArea;
> 
> 
> 
> These structs are stored in AVFrame like this:
> 
> 
>     /**
>      * Number of items in the @ref subtitle_areas array.
>      */
>     unsigned num_subtitle_areas;
> 
>     /**
>      * Array of subtitle areas, may be empty.
>      */
>     AVSubtitleArea **subtitle_areas;
> 
> 
> 
> The question was "why this cannot be handled using the buffer
> and data fields?" - there are multiple reasons:
> 
> 1. Area Count
> 
> In ASS subtitles it's not uncommon that animations are defined
> through subtitle events (regular ass events). This can go as
> far as that dust particles are flying around on the screen and
> each particle has its own subtitle event/line which defines
> how it flies from x to y and how fast, etc.
> Not yet, but in a future update, the ass decoder should be
> improved in a way that it doesn't emit an individual AVFrame
> for each event line but bundles subsequent events together
> when these would have the same start time and duration.
> As a result, we can have AVFrames with dozens or even a hundred
> AVSubtitleArea items in extreme cases.
> 
> The buffer and data fields are an array of fixed size (currently
> 8). Increasing to 16 or 32 would not help: there will still be
> cases where this does not suffice.
> 
> 2. What exactly should be stored in frame->buf[]?
> 
> Should we store the AVSubtitleArea structs as AVBuffer there?
> 
> Or should we only store data in those buffers? If yes, which
> data? The subtitle bitmap probably. How about the subtitle
> text - maybe and area has even both. And should we also
> store the palette in some frame->buf[n]?
> If yes, how to keep track of which data is stored in which
> buffer?
> Further, there's a documented convention that requires that
> non-zero buffers are contiguous, i.e. there must not be
> an empty buffer pointer between two other a non-empty buffer
> pointers. This would require to re-arrange the buffers to
> close any gap when some subtitle area data is removed, zeroed
> out or has been converted to another format.
> Closing such gap also require to update any mapping information
> ("which buffer is related to which area?")
> 
> That's a lot of tedious work and every API user (or codec/filter
> developer) would need to do it in the right way.
> 
> 
> 
> 3. AVFrame Code Logic Mistmatch
> 
> A subtitle frame can have 0 to N areas, which means that a
> frame without any area is still valid. Opposed to that, existing
> (code all over the place in ffmpeg) is treating an AVFrame
> as invalid when the first data buffer is empty,
> i.e. frame->buf[0] != NULL
> 
> To accommodate to this situation, subtitle frames are always
> creating a 1-byte buffer for buf[0].
> 
> When we would want subtitle data to be stored in those buffers,
> every developer would need to be aware about the requirement
> to have that dummy buffer in the first array element. When something
> is to be stored, the dummy buffer would need to be freed first
> before storing the actual data.
> And when the last buffer gets deleted, API users would need to
> know to create a new dummy buffer.
> 
> 
> That fixed size array might be a good fit for image data, but
> it's not for subtitle data.
> 
> 
> The approach in my patchset is simple, intuitive and everybody
> can easily work with it without needing any special knowledge:
> 
> 
>     unsigned num_subtitle_areas;
>     AVSubtitleArea **subtitle_areas;
> 
> IMHO, this is the most suitable pattern for this situation.
> 
> 
> 
> > If you claim the other points were addressed I will look at the
> > patches
> > again.
> 
> Oh cool, that would be great!
> 
> Thanks,
> softworkz
> _______________________________________________
> 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".
_______________________________________________
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".

^ permalink raw reply	[flat|nested] 217+ messages in thread

end of thread, other threads:[~2022-11-14 11:10 UTC | newest]

Thread overview: 217+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-01-14  1:13 [FFmpeg-devel] [PATCH 00/24] Subtitle Filtering 2022 ffmpegagent
2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 01/24] avcodec, avutil: Move enum AVSubtitleType to avutil, add new and deprecate old values ffmpegagent
2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 02/24] avutil/frame: Prepare AVFrame for subtitle handling ffmpegagent
2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 03/24] avcodec/subtitles: Introduce new frame-based subtitle decoding API ffmpegagent
2022-01-14 17:53   ` Andreas Rheinhardt
2022-01-15  7:59     ` Soft Works
2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 04/24] avfilter/subtitles: Update vf_subtitles to use new decoding api ffmpegagent
2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 05/24] avcodec, avutil: Move ass helper functions to avutil as avpriv_ and extend ass dialog parsing ffmpegagent
2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 06/24] avcodec/subtitles: Migrate subtitle encoders to frame-based API ffmpegagent
2022-01-14 17:22   ` Michael Niedermayer
2022-01-15  8:03     ` Soft Works
2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 07/24] avcodec/subtitles: Replace deprecated enum values ffmpegagent
2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 08/24] fftools/play, probe: Adjust for subtitle changes ffmpegagent
2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 09/24] avfilter/subtitles: Add subtitles.c for subtitle frame allocation ffmpegagent
2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 10/24] avfilter/avfilter: Handle subtitle frames ffmpegagent
2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 11/24] avfilter/avfilter: Fix hardcoded input index ffmpegagent
2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 12/24] avfilter/sbuffer: Add sbuffersrc and sbuffersink filters ffmpegagent
2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 13/24] avfilter/overlaygraphicsubs: Add overlaygraphicsubs and graphicsub2video filters ffmpegagent
2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 14/24] avfilter/overlaytextsubs: Add overlaytextsubs and textsubs2video filters ffmpegagent
2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 15/24] avfilter/textmod: Add textmod, censor and show_speaker filters ffmpegagent
2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 16/24] avfilter/stripstyles: Add stripstyles filter ffmpegagent
2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 17/24] avfilter/splitcc: Add splitcc filter for closed caption handling ffmpegagent
2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 18/24] avfilter/graphicsub2text: Add new graphicsub2text filter (OCR) ffmpegagent
2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 19/24] avfilter/subscale: Add filter for scaling and/or re-arranging graphical subtitles ffmpegagent
2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 20/24] avfilter/subfeed: add subtitle feed filter ffmpegagent
2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 21/24] fftools/ffmpeg: Introduce subtitle filtering new frame-based subtitle encoding ffmpegagent
2022-01-14 16:52   ` Michael Niedermayer
2022-01-15  8:36     ` Soft Works
2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 22/24] avutil/ass_split: Add parsing of hard-space tags (\h) ffmpegagent
2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 23/24] avcodec/webvttenc: convert hard-space tags to &nbsp; ffmpegagent
2022-01-14  1:13 ` [FFmpeg-devel] [PATCH 24/24] doc/APIchanges: update for subtitle filtering changes ffmpegagent
2022-01-20  2:48 ` [FFmpeg-devel] [PATCH v2 00/26] Subtitle Filtering 2022 ffmpegagent
2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 01/26] avcodec, avutil: Move enum AVSubtitleType to avutil, add new and deprecate old values ffmpegagent
2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 02/26] avutil/frame: Prepare AVFrame for subtitle handling ffmpegagent
2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 03/26] avcodec/subtitles: Introduce new frame-based subtitle decoding API ffmpegagent
2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 04/26] avfilter/subtitles: Update vf_subtitles to use new decoding api ffmpegagent
2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 05/26] avcodec, avutil: Move ass helper functions to avutil as avpriv_ and extend ass dialog parsing ffmpegagent
2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 06/26] avcodec/subtitles: Replace deprecated enum values ffmpegagent
2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 07/26] fftools/play, probe: Adjust for subtitle changes ffmpegagent
2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 08/26] avfilter/subtitles: Add subtitles.c for subtitle frame allocation ffmpegagent
2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 09/26] avfilter/avfilter: Handle subtitle frames ffmpegagent
2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 10/26] avfilter/avfilter: Fix hardcoded input index ffmpegagent
2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 11/26] avfilter/sbuffer: Add sbuffersrc and sbuffersink filters ffmpegagent
2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 12/26] avfilter/overlaygraphicsubs: Add overlaygraphicsubs and graphicsub2video filters ffmpegagent
2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 13/26] avfilter/overlaytextsubs: Add overlaytextsubs and textsubs2video filters ffmpegagent
2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 14/26] avfilter/textmod: Add textmod, censor and show_speaker filters ffmpegagent
2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 15/26] avfilter/stripstyles: Add stripstyles filter ffmpegagent
2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 16/26] avfilter/splitcc: Add splitcc filter for closed caption handling ffmpegagent
2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 17/26] avfilter/graphicsub2text: Add new graphicsub2text filter (OCR) ffmpegagent
2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 18/26] avfilter/subscale: Add filter for scaling and/or re-arranging graphical subtitles ffmpegagent
2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 19/26] avfilter/subfeed: add subtitle feed filter ffmpegagent
2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 20/26] avcodec/subtitles: Migrate subtitle encoders to frame-based API ffmpegagent
2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 21/26] fftools/ffmpeg: Introduce subtitle filtering and new frame-based subtitle encoding ffmpegagent
2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 22/26] avutil/ass_split: Add parsing of hard-space tags (\h) ffmpegagent
2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 23/26] avcodec/webvttenc: convert hard-space tags to &nbsp; ffmpegagent
2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 24/26] doc/APIchanges: update for subtitle filtering changes ffmpegagent
2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 25/26] avcodec/webvttenc: Don't encode drawing codes and empty lines ffmpegagent
2022-01-20  2:48   ` [FFmpeg-devel] [PATCH v2 26/26] avcodec/dvbsubdec: Fix conditions for fallback to default resolution ffmpegagent
2022-01-20  3:25   ` [FFmpeg-devel] [PATCH v3 00/26] Subtitle Filtering 2022 ffmpegagent
2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 01/26] avcodec, avutil: Move enum AVSubtitleType to avutil, add new and deprecate old values ffmpegagent
2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 02/26] avutil/frame: Prepare AVFrame for subtitle handling ffmpegagent
2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 03/26] avcodec/subtitles: Introduce new frame-based subtitle decoding API ffmpegagent
2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 04/26] avfilter/subtitles: Update vf_subtitles to use new decoding api ffmpegagent
2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 05/26] avcodec, avutil: Move ass helper functions to avutil as avpriv_ and extend ass dialog parsing ffmpegagent
2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 06/26] avcodec/subtitles: Replace deprecated enum values ffmpegagent
2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 07/26] fftools/play, probe: Adjust for subtitle changes ffmpegagent
2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 08/26] avfilter/subtitles: Add subtitles.c for subtitle frame allocation ffmpegagent
2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 09/26] avfilter/avfilter: Handle subtitle frames ffmpegagent
2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 10/26] avfilter/avfilter: Fix hardcoded input index ffmpegagent
2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 11/26] avfilter/sbuffer: Add sbuffersrc and sbuffersink filters ffmpegagent
2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 12/26] avfilter/overlaygraphicsubs: Add overlaygraphicsubs and graphicsub2video filters ffmpegagent
2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 13/26] avfilter/overlaytextsubs: Add overlaytextsubs and textsubs2video filters ffmpegagent
2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 14/26] avfilter/textmod: Add textmod, censor and show_speaker filters ffmpegagent
2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 15/26] avfilter/stripstyles: Add stripstyles filter ffmpegagent
2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 16/26] avfilter/splitcc: Add splitcc filter for closed caption handling ffmpegagent
2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 17/26] avfilter/graphicsub2text: Add new graphicsub2text filter (OCR) ffmpegagent
2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 18/26] avfilter/subscale: Add filter for scaling and/or re-arranging graphical subtitles ffmpegagent
2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 19/26] avfilter/subfeed: add subtitle feed filter ffmpegagent
2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 20/26] avcodec/subtitles: Migrate subtitle encoders to frame-based API ffmpegagent
2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 21/26] fftools/ffmpeg: Introduce subtitle filtering and new frame-based subtitle encoding ffmpegagent
2022-01-20 10:06       ` Michael Niedermayer
2022-01-20 15:17         ` Soft Works
2022-01-20 18:00           ` Andriy Gelman
2022-01-20 20:01             ` Soft Works
2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 22/26] avutil/ass_split: Add parsing of hard-space tags (\h) ffmpegagent
2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 23/26] avcodec/webvttenc: convert hard-space tags to &nbsp; ffmpegagent
2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 24/26] doc/APIchanges: update for subtitle filtering changes ffmpegagent
2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 25/26] avcodec/webvttenc: Don't encode drawing codes and empty lines ffmpegagent
2022-01-20  3:25     ` [FFmpeg-devel] [PATCH v3 26/26] avcodec/dvbsubdec: Fix conditions for fallback to default resolution ffmpegagent
2022-05-28 13:25     ` [FFmpeg-devel] [PATCH v4 00/23] Subtitle Filtering 2022 ffmpegagent
2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 01/23] avcodec, avutil: Move enum AVSubtitleType to avutil, add new and deprecate old values softworkz
2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 02/23] avutil/frame: Prepare AVFrame for subtitle handling softworkz
2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 03/23] avcodec/subtitles: Introduce new frame-based subtitle decoding API softworkz
2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 04/23] avcodec/libzvbi: set subtitle type softworkz
2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 05/23] avfilter/subtitles: Update vf_subtitles to use new decoding api softworkz
2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 06/23] avcodec, avutil: Move ass helper functions to avutil as avpriv_ and extend ass dialog parsing softworkz
2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 07/23] avcodec/subtitles: Replace deprecated enum values softworkz
2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 08/23] fftools/play, probe: Adjust for subtitle changes softworkz
2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 09/23] avfilter/subtitles: Add subtitles.c for subtitle frame allocation softworkz
2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 10/23] avfilter/avfilter: Handle subtitle frames softworkz
2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 11/23] avfilter/avfilter: Fix hardcoded input index softworkz
2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 12/23] avfilter/sbuffer: Add sbuffersrc and sbuffersink filters softworkz
2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 13/23] avfilter/overlaygraphicsubs: Add overlaygraphicsubs and graphicsub2video filters softworkz
2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 14/23] avfilter/overlaytextsubs: Add overlaytextsubs and textsubs2video filters softworkz
2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 15/23] avfilter/textmod: Add textmod, censor and show_speaker filters softworkz
2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 16/23] avfilter/stripstyles: Add stripstyles filter softworkz
2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 17/23] avfilter/splitcc: Add splitcc filter for closed caption handling softworkz
2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 18/23] avfilter/graphicsub2text: Add new graphicsub2text filter (OCR) softworkz
2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 19/23] avfilter/subscale: Add filter for scaling and/or re-arranging graphical subtitles softworkz
2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 20/23] avfilter/subfeed: add subtitle feed filter softworkz
2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 21/23] avcodec/subtitles: Migrate subtitle encoders to frame-based API softworkz
2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 22/23] fftools/ffmpeg: Introduce subtitle filtering and new frame-based subtitle encoding softworkz
2022-05-28 13:25       ` [FFmpeg-devel] [PATCH v4 23/23] avcodec/dvbsubdec: Fix conditions for fallback to default resolution softworkz
2022-06-25  9:57       ` [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022 ffmpegagent
2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 01/25] avcodec, avutil: Move enum AVSubtitleType to avutil, add new and deprecate old values softworkz
2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 02/25] avutil/frame: Prepare AVFrame for subtitle handling softworkz
2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 03/25] avcodec/subtitles: Introduce new frame-based subtitle decoding API softworkz
2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 04/25] avcodec/libzvbi: set subtitle type softworkz
2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 05/25] avfilter/subtitles: Update vf_subtitles to use new decoding api softworkz
2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 06/25] avcodec, avutil: Move ass helper functions to avutil as avpriv_ and extend ass dialog parsing softworkz
2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 07/25] avcodec/subtitles: Replace deprecated enum values softworkz
2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 08/25] fftools/play, probe: Adjust for subtitle changes softworkz
2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 09/25] avfilter/subtitles: Add subtitles.c for subtitle frame allocation softworkz
2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 10/25] avfilter/avfilter: Handle subtitle frames softworkz
2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 11/25] avfilter/avfilter: Fix hardcoded input index softworkz
2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 12/25] avfilter/sbuffer: Add sbuffersrc and sbuffersink filters softworkz
2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 13/25] avfilter/overlaygraphicsubs: Add overlaygraphicsubs and graphicsub2video filters softworkz
2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 14/25] avfilter/overlaytextsubs: Add overlaytextsubs and textsubs2video filters softworkz
2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 15/25] avfilter/textmod: Add textmod, censor and show_speaker filters softworkz
2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 16/25] avfilter/stripstyles: Add stripstyles filter softworkz
2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 17/25] avfilter/splitcc: Add splitcc filter for closed caption handling softworkz
2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 18/25] avfilter/graphicsub2text: Add new graphicsub2text filter (OCR) softworkz
2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 19/25] avfilter/subscale: Add filter for scaling and/or re-arranging graphical subtitles softworkz
2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 20/25] avfilter/subfeed: add subtitle feed filter softworkz
2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 21/25] avfilter/text2graphicsub: Added text2graphicsub subtitle filter tcoza
2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 22/25] avfilter/snull, strim: Add snull and strim filters softworkz
2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 23/25] avcodec/subtitles: Migrate subtitle encoders to frame-based API softworkz
2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 24/25] fftools/ffmpeg: Introduce subtitle filtering and new frame-based subtitle encoding softworkz
2022-06-25 11:34           ` Michael Niedermayer
2022-06-25 11:42             ` Andreas Rheinhardt
2022-06-25 12:31               ` Soft Works
2022-06-25 13:00                 ` Andreas Rheinhardt
2022-06-26 16:27                   ` Soft Works
2022-06-25  9:57         ` [FFmpeg-devel] [PATCH v5 25/25] avcodec/dvbsubdec: Fix conditions for fallback to default resolution softworkz
2022-06-26 16:34         ` [FFmpeg-devel] [PATCH v6 00/25] Subtitle Filtering 2022 ffmpegagent
2022-06-26 16:34           ` [FFmpeg-devel] [PATCH v6 01/25] avcodec, avutil: Move enum AVSubtitleType to avutil, add new and deprecate old values softworkz
2022-06-26 16:34           ` [FFmpeg-devel] [PATCH v6 02/25] avutil/frame: Prepare AVFrame for subtitle handling softworkz
2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 03/25] avcodec/subtitles: Introduce new frame-based subtitle decoding API softworkz
2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 04/25] avcodec/libzvbi: set subtitle type softworkz
2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 05/25] avfilter/subtitles: Update vf_subtitles to use new decoding api softworkz
2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 06/25] avcodec, avutil: Move ass helper functions to avutil as avpriv_ and extend ass dialog parsing softworkz
2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 07/25] avcodec/subtitles: Replace deprecated enum values softworkz
2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 08/25] fftools/play, probe: Adjust for subtitle changes softworkz
2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 09/25] avfilter/subtitles: Add subtitles.c for subtitle frame allocation softworkz
2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 10/25] avfilter/avfilter: Handle subtitle frames softworkz
2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 11/25] avfilter/avfilter: Fix hardcoded input index softworkz
2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 12/25] avfilter/sbuffer: Add sbuffersrc and sbuffersink filters softworkz
2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 13/25] avfilter/overlaygraphicsubs: Add overlaygraphicsubs and graphicsub2video filters softworkz
2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 14/25] avfilter/overlaytextsubs: Add overlaytextsubs and textsubs2video filters softworkz
2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 15/25] avfilter/textmod: Add textmod, censor and show_speaker filters softworkz
2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 16/25] avfilter/stripstyles: Add stripstyles filter softworkz
2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 17/25] avfilter/splitcc: Add splitcc filter for closed caption handling softworkz
2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 18/25] avfilter/graphicsub2text: Add new graphicsub2text filter (OCR) softworkz
2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 19/25] avfilter/subscale: Add filter for scaling and/or re-arranging graphical subtitles softworkz
2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 20/25] avfilter/subfeed: add subtitle feed filter softworkz
2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 21/25] avfilter/text2graphicsub: Added text2graphicsub subtitle filter softworkz
2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 22/25] avfilter/snull, strim: Add snull and strim filters softworkz
2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 23/25] avcodec/subtitles: Migrate subtitle encoders to frame-based API softworkz
2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 24/25] fftools/ffmpeg: Introduce subtitle filtering and new frame-based subtitle encoding softworkz
2022-06-26 16:35           ` [FFmpeg-devel] [PATCH v6 25/25] avcodec/dvbsubdec: Fix conditions for fallback to default resolution softworkz
2022-07-02 16:39         ` [FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022 Paul B Mahol
2022-07-02 17:18           ` Nicolas George
2022-07-02 17:27             ` Paul B Mahol
2022-07-02 17:26               ` Nicolas George
2022-07-02 19:11             ` Soft Works
2022-07-02 19:17               ` Nicolas George
2022-07-02 19:21                 ` Soft Works
2022-07-02 19:29                 ` Soft Works
2022-07-02 19:36                   ` Nicolas George
2022-07-02 20:32                     ` Soft Works
2022-07-02 20:40                       ` Paul B Mahol
2022-07-02 20:50                         ` Soft Works
2022-07-03  7:58                         ` Jean-Baptiste Kempf
2022-07-03 10:42                           ` Paul B Mahol
2022-07-04  8:45                             ` Jean-Baptiste Kempf
2022-07-02 21:10                 ` Matt Zagrabelny
2022-07-02 21:22                   ` Nicolas George
2022-07-02 21:27                     ` Paul B Mahol
2022-07-02 21:45                     ` Matt Zagrabelny
2022-07-02 22:16                       ` Nicolas George
2022-07-03 17:07             ` Michael Niedermayer
2022-07-03 17:25               ` Paul B Mahol
2022-07-24 15:10               ` Nicolas George
2022-07-24 18:38                 ` Soft Works
2022-07-24 19:21                 ` Soft Works
2022-07-25  6:44                 ` Ronald S. Bultje
2022-07-25 17:44                   ` Nicolas George
2022-07-25 11:32                 ` Michael Niedermayer
2022-07-25 19:01                   ` Nicolas George
2022-07-25 19:39                     ` Michael Niedermayer
2022-07-02 19:03           ` Soft Works
2022-08-11 22:50         ` Soft Works
2022-08-21  9:41           ` Paul B Mahol
2022-08-21  9:51             ` Nicolas George
2022-08-21 10:41             ` Jean-Baptiste Kempf
2022-08-22 12:18               ` Anton Khirnov
2022-08-22 14:38                 ` Jean-Baptiste Kempf
2022-08-24 22:19                   ` Soft Works
2022-08-26 20:47                   ` Anton Khirnov
2022-08-26 22:48                     ` Soft Works
2022-08-31  1:39                     ` Anton Khirnov
2022-08-31  4:02                       ` Soft Works
2022-09-20 14:30                         ` Soft Works
2022-11-14 11:10                         ` Soft Works
2022-10-10 11:08                       ` Soft Works
2022-08-22 22:08                 ` Soft Works
2022-08-22 23:08                   ` Soft Works

Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

This inbox may be cloned and mirrored by anyone:

	git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
		ffmpegdev@gitmailbox.com
	public-inbox-index ffmpegdev

Example config snippet for mirrors.


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git