[FFmpeg-devel] avcodec: add HDMV Text Subtitle decoder

Submitted by Petri Hintukainen on Jan. 31, 2017, 4 p.m.

Details

Message ID 1485878406.3882.2.camel@gmail.com
State New
Headers show

Commit Message

Petri Hintukainen Jan. 31, 2017, 4 p.m.
Hello,

I think I posted HDMV text subtitle decoder patch year+ ago. But I
can't find it from list archives (?).

I'll attach the original patch for reference / documentation of the
format.
There's also more complete implementation (text styles, positioning,
...) in libbluray: http://git.videolan.org/?p=libbluray.git;a=blob;f=sr
c/libbluray/decoders/textst_decode.c .

ti, 2017-01-31 kello 15:22 +0100, Paul B Mahol kirjoitti:
> Signed-off-by: Paul B Mahol <onemda@gmail.com>
> ---
>  libavcodec/Makefile        |   2 +
>  libavcodec/allcodecs.c     |   2 +
>  libavcodec/textst_parser.c |  49 ++++++++++++++++++++
>  libavcodec/textstdec.c     | 108
> +++++++++++++++++++++++++++++++++++++++++++++
>  libavformat/utils.c        |   1 +
>  5 files changed, 162 insertions(+)
>  create mode 100644 libavcodec/textst_parser.c
>  create mode 100644 libavcodec/textstdec.c
> 
> diff --git a/libavcodec/Makefile b/libavcodec/Makefile
> index 43a6add..edadb0f 100644
> --- a/libavcodec/Makefile
> +++ b/libavcodec/Makefile
> @@ -539,6 +539,7 @@ OBJS-$(CONFIG_SVQ1_ENCODER)            +=
> svq1enc.o svq1.o  h263data.o  \
>  OBJS-$(CONFIG_SVQ3_DECODER)            += svq3.o svq13.o mpegutils.o
> h264data.o
>  OBJS-$(CONFIG_TEXT_DECODER)            += textdec.o ass.o
>  OBJS-$(CONFIG_TEXT_ENCODER)            += srtenc.o ass_split.o
> +OBJS-$(CONFIG_TEXTST_DECODER)          += textstdec.o ass.o
>  OBJS-$(CONFIG_TAK_DECODER)             += takdec.o tak.o takdsp.o
>  OBJS-$(CONFIG_TARGA_DECODER)           += targa.o
>  OBJS-$(CONFIG_TARGA_ENCODER)           += targaenc.o rle.o
> @@ -945,6 +946,7 @@ OBJS-$(CONFIG_RV30_PARSER)             +=
> rv34_parser.o
>  OBJS-$(CONFIG_RV40_PARSER)             += rv34_parser.o
>  OBJS-$(CONFIG_SIPR_PARSER)             += sipr_parser.o
>  OBJS-$(CONFIG_TAK_PARSER)              += tak_parser.o tak.o
> +OBJS-$(CONFIG_TEXTST_PARSER)           += textst_parser.o
>  OBJS-$(CONFIG_VC1_PARSER)              += vc1_parser.o vc1.o
> vc1data.o  \
>                                            simple_idct.o wmv2data.o
>  OBJS-$(CONFIG_VP3_PARSER)              += vp3_parser.o
> diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
> index f92b2b7..9a90533 100644
> --- a/libavcodec/allcodecs.c
> +++ b/libavcodec/allcodecs.c
> @@ -581,6 +581,7 @@ void avcodec_register_all(void)
>      REGISTER_DECODER(SUBVIEWER,         subviewer);
>      REGISTER_DECODER(SUBVIEWER1,        subviewer1);
>      REGISTER_ENCDEC (TEXT,              text);
> +    REGISTER_DECODER(TEXTST,            textst);
>      REGISTER_DECODER(VPLAYER,           vplayer);
>      REGISTER_ENCDEC (WEBVTT,            webvtt);
>      REGISTER_ENCDEC (XSUB,              xsub);
> @@ -704,6 +705,7 @@ void avcodec_register_all(void)
>      REGISTER_PARSER(RV40,               rv40);
>      REGISTER_PARSER(SIPR,               sipr);
>      REGISTER_PARSER(TAK,                tak);
> +    REGISTER_PARSER(TEXTST,             textst);
>      REGISTER_PARSER(VC1,                vc1);
>      REGISTER_PARSER(VORBIS,             vorbis);
>      REGISTER_PARSER(VP3,                vp3);
> diff --git a/libavcodec/textst_parser.c b/libavcodec/textst_parser.c
> new file mode 100644
> index 0000000..5079a96
> --- /dev/null
> +++ b/libavcodec/textst_parser.c
> @@ -0,0 +1,49 @@
> +/*
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later
> version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> 02110-1301 USA
> + */
> +
> +/**
> + * @file
> + * HDMV TextST subtitle parser
> + */
> +
> +#include "libavutil/intreadwrite.h"
> +#include "parser.h"
> +
> +static int textst_parse(AVCodecParserContext *s1, AVCodecContext
> *avctx,
> +                        const uint8_t **poutbuf, int *poutbuf_size,
> +                        const uint8_t *buf, int buf_size)
> +{
> +    if (buf_size > 13) {
> +        int64_t end;
> +
> +        s1->pts = ((int64_t)(buf[3] & 1) << 32) | AV_RB32(&buf[4]);

I think there's no pts in dialog style segment.

I don't know if style segment is present when subtitles are muxed to
matroska, but it is present in the original .m2ts file.

> +        end = ((int64_t)(buf[8] & 1) << 32) | AV_RB32(&buf[9]);
> +        s1->duration = (end - s1->pts);
> +    }
> +
> +    /* always return the full packet. this parser isn't doing any
> splitting or
> +       combining, only packet analysis */
> +    *poutbuf      = buf;
> +    *poutbuf_size = buf_size;
> +    return buf_size;
> +}
> +
> +AVCodecParser ff_textst_parser = {
> +    .codec_ids      = { AV_CODEC_ID_HDMV_TEXT_SUBTITLE },
> +    .parser_parse   = textst_parse,
> +};
> diff --git a/libavcodec/textstdec.c b/libavcodec/textstdec.c
> new file mode 100644
> index 0000000..a259d2d
> --- /dev/null
> +++ b/libavcodec/textstdec.c
> @@ -0,0 +1,108 @@
> +/*
> + * HDMV TextST decoder
> + * Copyright (c) 2017 Paul B Mahol
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later
> version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> 02110-1301 USA
> + */
> +
> +#include <string.h>
> +
> +#include "libavutil/bprint.h"
> +#include "avcodec.h"
> +#include "ass.h"
> +#include "bytestream.h"
> +
> +static int textst_event_to_ass(AVBPrint *buf, const char *p, int
> size)
> +{
> +    GetByteContext gb;
> +    int i, count;
> +
> +    bytestream2_init(&gb, p, size);
> +    count = bytestream2_get_byte(&gb);
> +
> +    if (count > 127) {
> +        int size = bytestream2_get_be16(&gb);
> +        bytestream2_skip(&gb, size);
> +    }
> +    if (bytestream2_get_bytes_left(&gb) > 2) {
> +        count = bytestream2_get_byte(&gb);
> +
> +        for (i = 0; i < count; i++) {
> +            int dlength;
> +
> +            bytestream2_skip(&gb, 2);
> +            dlength = bytestream2_get_be16(&gb);
> +
> +            while (dlength > 3) {
> +                int type, length;
> +                int code = bytestream2_get_byte(&gb);
> +
> +                dlength--;
> +                if (code != 0x1b)
> +                    continue;
> +
> +                type   = bytestream2_get_byte(&gb);
> +                length = bytestream2_get_byte(&gb);
> +                dlength -= (2 + length);
> +
> +                switch (type) {
> +                case 1:
> +                    av_bprint_append_data(buf, gb.buffer,
> FFMIN(length, bytestream2_get_bytes_left(&gb)));
> +                    bytestream2_skip(&gb, length);
> +                    break;
> +                case 0x0a:
> +                    av_bprintf(buf, "\n");
> +                default:
> +                    bytestream2_skip(&gb, length);
> +                    break;
> +                }
> +            }
> +        }
> +    }
> +
> +    return 0;
> +}
> +
> +static int textst_decode_frame(AVCodecContext *avctx,
> +                               void *data, int *got_sub_ptr,
> AVPacket *avpkt)
> +{
> +    FFASSDecoderContext *s = avctx->priv_data;
> +    const char *ptr = avpkt->data;
> +    AVSubtitle *sub = data;
> +    AVBPrint buf;
> +    int ret = 0;
> +
> +    av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
> +    if (ptr && avpkt->size > 13 && !textst_event_to_ass(&buf, ptr +
> 13, avpkt->size - 13))

I'd check segment type here (buf[0] == 0x82).

> +        ret = ff_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL,
> NULL);
> +    av_bprint_finalize(&buf, NULL);
> +    if (ret < 0)
> +        return ret;
> +    *got_sub_ptr = sub->num_rects > 0;
> +    return avpkt->size;
> +}
> +
> +AVCodec ff_textst_decoder = {
> +    .name           = "textst",
> +    .long_name      = NULL_IF_CONFIG_SMALL("HDMV TextST subtitle"),
> +    .type           = AVMEDIA_TYPE_SUBTITLE,
> +    .id             = AV_CODEC_ID_HDMV_TEXT_SUBTITLE,
> +    .decode         = textst_decode_frame,
> +    .init           = ff_ass_subtitle_header_default,
> +    .flush          = ff_ass_decoder_flush,
> +    .priv_data_size = sizeof(FFASSDecoderContext),
> +};
> diff --git a/libavformat/utils.c b/libavformat/utils.c
> index 0711310..74f808f 100644
> --- a/libavformat/utils.c
> +++ b/libavformat/utils.c
> @@ -1445,6 +1445,7 @@ static int parse_packet(AVFormatContext *s,
> AVPacket *pkt, int stream_index)
>  
>          out_pkt.stream_index = st->index;
>          out_pkt.pts          = st->parser->pts;
> +        out_pkt.duration     = st->parser->duration;
>          out_pkt.dts          = st->parser->dts;
>          out_pkt.pos          = st->parser->pos;
>
Simple decoder that decodes only the dialog texts and ignores the formatting.
---
 libavcodec/Makefile         |   1 +
 libavcodec/allcodecs.c      |   1 +
 libavcodec/hdmv_textstdec.c | 240 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 242 insertions(+)
 create mode 100644 libavcodec/hdmv_textstdec.c

Comments

Paul B Mahol Jan. 31, 2017, 7:07 p.m.
On 1/31/17, Petri Hintukainen <phintuka@gmail.com> wrote:
> Hello,
>
> I think I posted HDMV text subtitle decoder patch year+ ago. But I
> can't find it from list archives (?).
>
> I'll attach the original patch for reference / documentation of the
> format.
> There's also more complete implementation (text styles, positioning,
> ...) in libbluray: http://git.videolan.org/?p=libbluray.git;a=blob;f=sr
> c/libbluray/decoders/textst_decode.c .
>
>

Thank you much for this patch, do you have sample file where various
styles are present?
Carl Eugen Hoyos Jan. 31, 2017, 7:11 p.m.
2017-01-31 17:00 GMT+01:00 Petri Hintukainen <phintuka@gmail.com>:

> I think I posted HDMV text subtitle decoder patch year+ ago. But I
> can't find it from list archives (?).

I suspect because you didn't sent it =-(
http://ffmpeg.org/pipermail/ffmpeg-devel/2015-September/178194.html

Thank for for providing it now!

Carl Eugen
Petri Hintukainen Feb. 2, 2017, 10:30 a.m.
ti, 2017-01-31 kello 20:07 +0100, Paul B Mahol kirjoitti:
> On 1/31/17, Petri Hintukainen <phintuka@gmail.com> wrote:
> > Hello,
> > 
> > I think I posted HDMV text subtitle decoder patch year+ ago. But I
> > can't find it from list archives (?).
> > 
> > I'll attach the original patch for reference / documentation of the
> > format.
> > There's also more complete implementation (text styles,
> > positioning,
> > ...) in libbluray: http://git.videolan.org/?p=libbluray.git;a=blob;
> > f=sr
> > c/libbluray/decoders/textst_decode.c .
> > 
> > 
> 
> Thank you much for this patch, do you have sample file where various
> styles are present?

I checked my samples, but I didn't find anything "interesting".

There's style segment in every file. But I haven't seen any exotic
styles used, just "normal" colors, font, font size, position, text flow
etc.

One file seems to use inline markers (italic text and style reset).
There are also couple of user styles, but those require some kind of
user interaction. Deltas in selected user style are applied to active
region style. Ideally user style selection should be available in the
player.

Matroska samples can be created with recent enough mkvmerge. It seems
to parse also language information from playlist file (if present).

Also MakeMKV can create files with text subtitles, but I didn't manage
to get it working with incomplete discs (all my samples have truncated
a/v streams).

If we need files with exotic styles, I can probably create some.

> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel

Patch hide | download patch | download mbox

diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 407c6c3..6d4b7dd 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -297,6 +297,7 @@  OBJS-$(CONFIG_H264_QSV_DECODER)        += qsvdec_h2645.o
 OBJS-$(CONFIG_H264_QSV_ENCODER)        += qsvenc_h264.o
 OBJS-$(CONFIG_HAP_DECODER)             += hapdec.o hap.o
 OBJS-$(CONFIG_HAP_ENCODER)             += hapenc.o hap.o
+OBJS-$(CONFIG_HDMV_TEXTST_DECODER)     += hdmv_textstdec.o ass.o
 OBJS-$(CONFIG_HEVC_DECODER)            += hevc.o hevc_mvs.o hevc_ps.o hevc_sei.o \
                                           hevc_cabac.o hevc_refs.o hevcpred.o    \
                                           hevcdsp.o hevc_filter.o hevc_parse.o hevc_data.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index dbf3927..188e224 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -526,6 +526,7 @@  void avcodec_register_all(void)
     REGISTER_DECODER(CCAPTION,          ccaption);
     REGISTER_ENCDEC (DVBSUB,            dvbsub);
     REGISTER_ENCDEC (DVDSUB,            dvdsub);
+    REGISTER_DECODER(HDMV_TEXTST,       hdmv_textst);
     REGISTER_DECODER(JACOSUB,           jacosub);
     REGISTER_DECODER(MICRODVD,          microdvd);
     REGISTER_ENCDEC (MOVTEXT,           movtext);
diff --git a/libavcodec/hdmv_textstdec.c b/libavcodec/hdmv_textstdec.c
new file mode 100644
index 0000000..d7f99c5
--- /dev/null
+++ b/libavcodec/hdmv_textstdec.c
@@ -0,0 +1,240 @@ 
+/*
+ * BluRay text subtitle decoder
+ * Copyright (c) 2014  Petri Hintukainen <phintuka@users.sourceforge.net>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/bprint.h"
+#include "libavutil/intreadwrite.h"
+#include "avcodec.h"
+#include "ass.h"
+#include "bytestream.h"
+
+enum {
+    DIALOG_STYLE_SEGMENT        = 0x81,
+    DIALOG_PRESENTATION_SEGMENT = 0x82,
+};
+
+enum {
+    BD_TEXTST_DATA_ESCAPE      = 0x1b,
+    BD_TEXTST_DATA_STRING      = 1,
+    BD_TEXTST_DATA_FONT_ID     = 2,
+    BD_TEXTST_DATA_FONT_STYLE  = 3,
+    BD_TEXTST_DATA_FONT_SIZE   = 4,
+    BD_TEXTST_DATA_FONT_COLOR  = 5,
+    BD_TEXTST_DATA_NEWLINE     = 0x0a,
+    BD_TEXTST_DATA_RESET_STYLE = 0x0b,
+};
+
+
+static void decode_region_data(AVCodecContext *avctx, GetByteContext *gb, AVBPrint *sub)
+{
+
+  while (bytestream2_get_bytes_left(gb) > 2) {
+
+        unsigned int code, type, length;
+
+        /* parse header */
+
+        code = bytestream2_get_byte(gb);
+        if (code != BD_TEXTST_DATA_ESCAPE) {
+            continue;
+        }
+        type   = bytestream2_get_byte(gb);
+        length = bytestream2_get_byte(gb);
+
+        /* parse content */
+
+        if (length > bytestream2_get_bytes_left(gb)) {
+            av_log(avctx, AV_LOG_WARNING, "decode_dialog_region(): unexpected end of data\n");
+            return;
+        }
+
+        switch (type) {
+            case BD_TEXTST_DATA_STRING:
+                av_bprint_append_data(sub, gb->buffer, length);
+                break;
+            case BD_TEXTST_DATA_NEWLINE:
+                av_bprint_append_data(sub, "\\N", 2);
+                break;
+            default:
+                break;
+        }
+
+        bytestream2_skip(gb, length);
+    }
+}
+
+static int decode_region(AVCodecContext *avctx, GetByteContext *gb, AVBPrint *sub, int *forced_on_flag)
+{
+    GetByteContext gb_region;
+    int flags, data_length;
+
+    flags = bytestream2_get_byte(gb);
+    /*continous_present_flag = !!(flags & 0x80);*/
+    *forced_on_flag        = !!(flags & 0x40);
+    /*region_style_id_ref=*/ bytestream2_get_byte(gb);
+    data_length            = bytestream2_get_be16(gb);
+
+    if (data_length > bytestream2_get_bytes_left(gb)) {
+        av_log(avctx, AV_LOG_WARNING, "decode_dialog_region(): unexpected end of data\n");
+        return -1;
+    }
+
+    bytestream2_init(&gb_region, gb->buffer, data_length);
+    decode_region_data(avctx, &gb_region, sub);
+
+    bytestream2_skip(gb, data_length);
+
+    av_bprintf(sub, "\r\n");
+
+    return 1;
+}
+
+static int64_t decode_pts(GetByteContext *gb)
+{
+    return (((uint64_t)bytestream2_get_byte(gb) & 1) << 32) | bytestream2_get_be32(gb);
+}
+
+static int decode_palette(AVCodecContext *avctx, GetByteContext *gb)
+{
+    unsigned int length;
+
+    length = bytestream2_get_be16(gb);
+
+    if (length > bytestream2_get_bytes_left(gb)) {
+        av_log(avctx, AV_LOG_WARNING, "decode_palette(): unexpected end of data\n");
+        return -1;
+    }
+
+    /* skip palette update */
+    av_log(avctx, AV_LOG_WARNING, "ignoring palette update\n");
+    bytestream2_skip(gb, length);
+
+    return 1;
+}
+
+static void decode_presentation_segment(AVCodecContext *avctx, GetByteContext *gb, AVSubtitle *sub)
+{
+    unsigned ii, palette_update_flag, region_count;
+    int64_t start_pts, end_pts;
+    int64_t ts_start, ts_end;
+
+    start_pts = decode_pts(gb);
+    end_pts   = decode_pts(gb);
+    sub->pts  = start_pts;
+
+    ts_start = av_rescale_q(start_pts,
+                            avctx->time_base,
+                            (AVRational){1,100});
+    ts_end   = av_rescale_q(end_pts,
+                            avctx->time_base,
+                            (AVRational){1,100});
+
+    palette_update_flag = bytestream2_get_byte(gb) >> 7;
+    if (palette_update_flag) {
+        if (decode_palette(avctx, gb) < 0) {
+            return;
+        }
+    }
+
+    region_count = bytestream2_get_byte(gb);
+    if (region_count > 2) {
+        av_log(avctx, AV_LOG_WARNING, "too many regions (%d)\n", region_count);
+        return;
+    }
+
+    for (ii = 0; ii < region_count; ii++) {
+        AVBPrint buffer;
+        char *dec_sub;
+        int forced_on_flag;
+
+        av_bprint_init(&buffer, 1024, 1024);
+
+        if (decode_region(avctx, gb, &buffer, &forced_on_flag) < 0) {
+            av_bprint_finalize(&buffer, NULL);
+            return;
+        }
+
+        av_bprint_finalize(&buffer, &dec_sub);
+
+        ff_ass_add_rect(sub, dec_sub, ts_start, ts_end - ts_start, 0);
+        av_free(dec_sub);
+
+        if (forced_on_flag && sub->num_rects > 0) {
+            sub->rects[sub->num_rects - 1]->flags |= AV_SUBTITLE_FLAG_FORCED;
+        }
+    }
+
+    if (bytestream2_get_bytes_left(gb)) {
+        av_log(avctx, AV_LOG_WARNING, "unknown data after dialog segment (%d bytes)\n", bytestream2_get_bytes_left(gb));
+    }
+}
+
+static int hdmv_decode_frame(AVCodecContext *avctx,
+                             void *data, int *got_sub_ptr, AVPacket *avpkt)
+{
+    AVSubtitle *sub = data;
+    int segment_type, segment_size;
+    GetByteContext gb;
+
+    if (avpkt->size < 3) {
+        return avpkt->size;
+    }
+
+    bytestream2_init(&gb, avpkt->data, avpkt->size);
+
+    segment_type = bytestream2_get_byte(&gb);
+    segment_size = bytestream2_get_be16(&gb);
+
+    if (avpkt->size < segment_size + 3) {
+        av_log(avctx, AV_LOG_WARNING, "segment 0x%02x size mismatch: segment %d bytes, packet %d bytes\n",
+               segment_type, segment_size, avpkt->size);
+        return avpkt->size;
+    }
+
+    switch (segment_type) {
+        case DIALOG_STYLE_SEGMENT:
+            break;
+
+        case DIALOG_PRESENTATION_SEGMENT:
+            decode_presentation_segment(avctx, &gb, sub);
+            break;
+        default:
+            av_log(avctx, AV_LOG_WARNING, "unknown segment type 0x%02x\n", segment_type);
+            break;
+    }
+
+    *got_sub_ptr = (sub->num_rects > 0);
+
+    return avpkt->size;
+}
+
+static int init_codec(AVCodecContext *avctx)
+{
+    return ff_ass_subtitle_header_default(avctx);
+}
+
+AVCodec ff_hdmv_textst_decoder = {
+    .name           = "hdmv_textst",
+    .long_name      = NULL_IF_CONFIG_SMALL("HDMV Text subtitle"),
+    .type           = AVMEDIA_TYPE_SUBTITLE,
+    .id             = AV_CODEC_ID_HDMV_TEXT_SUBTITLE,
+    .init           = init_codec,
+    .decode         = hdmv_decode_frame,
+};