[FFmpeg-devel] [PATCH v5 00/25] Subtitle Filtering 2022

Paul B Mahol onemda at gmail.com
Sat Jul 2 19:39:11 EEST 2022


On Sat, Jun 25, 2022 at 11:58 AM ffmpegagent <ffmpegagent at 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 at 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 at 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 at 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 at 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 at 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 at ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request at ffmpeg.org with subject "unsubscribe".
>


More information about the ffmpeg-devel mailing list