[FFmpeg-devel] [PATCH] Decoder for CEA-608 and World System Teletext [WIP]

Michael Niedermayer michaelni at gmx.at
Fri Jan 23 04:42:22 CET 2015


From: Shan <shan.wb at gmail.com>

Patch fixed by Shan so it builds and passes fate

Signed-off-by: Michael Niedermayer <michaelni at gmx.at>
---
 Changelog                        |    2 +
 MAINTAINERS                      |    1 +
 doc/decoders.texi                |   33 +
 doc/general.texi                 |    5 +-
 doc/muxers.texi                  |    8 +-
 libavcodec/Makefile              |    5 +
 libavcodec/allcodecs.c           |    4 +
 libavcodec/avcodec.h             |    6 +-
 libavcodec/ccaption_dec.c        |    2 +-
 libavcodec/codec_desc.c          |   34 +-
 libavcodec/dvbsub_parser.c       |  115 ++
 libavcodec/h264.h                |    3 +
 libavcodec/h264_sei.c            |   71 +-
 libavcodec/h264_slice.c          |    8 +
 libavcodec/libzvbi-teletextdec.c |   34 +-
 libavcodec/vbisubdec.c           | 2444 ++++++++++++++++++++++++++++++++++++++
 libavcodec/version.h             |    2 +-
 libavdevice/lavfi.c              |    2 +-
 libavformat/isom.c               |    3 +-
 libavformat/mpeg.c               |    3 +
 libavformat/mpeg.h               |    1 +
 libavformat/mpegenc.c            |    9 +-
 libavformat/mpegts.c             |  129 +-
 libavformat/mpegtsenc.c          |  119 +-
 libavformat/mxfdec.c             |    8 +-
 libavformat/nut.c                |    2 +-
 libavformat/wtvdec.c             |    2 +-
 27 files changed, 2944 insertions(+), 111 deletions(-)
 create mode 100644 libavcodec/vbisubdec.c

diff --git a/Changelog b/Changelog
index 04f1728..4a9c144 100644
--- a/Changelog
+++ b/Changelog
@@ -16,6 +16,8 @@ version <next>:
 - Closed caption Decoder
 - fspp, uspp, pp7 MPlayer postprocessing filters ported to native filters
 - showpalette filter
+- native decoder for CEA-608, EIA-708, Teletext, DVB VBI and MXF VANC
+- support in mpegts for Fortis OEM recordings with no PAT
 
 
 version 2.5:
diff --git a/MAINTAINERS b/MAINTAINERS
index 13b211e..ae7610b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -267,6 +267,7 @@ Codecs:
   ulti*                                 Kostya Shishkov
   v410*.c                               Derek Buitenhuis
   vb.c                                  Kostya Shishkov
+  vbidec.c                              Shan Weber
   vble.c                                Derek Buitenhuis
   vc1*                                  Kostya Shishkov, Christophe Gisquet
   vcr1.c                                Michael Niedermayer
diff --git a/doc/decoders.texi b/doc/decoders.texi
index 01fca9f..2931b99 100644
--- a/doc/decoders.texi
+++ b/doc/decoders.texi
@@ -201,6 +201,39 @@ and non-forced subtitles in the same track. Setting this flag to @code{1}
 will only keep the forced subtitles. Default value is @code{0}.
 @end table
 
+ at section vbi
+The decoder allows libavcodec to decode VBI Teletext subtitles and CEA-608
+captions from either EBU DVB, SMPTE MXF VANC, quicktime or raw packets.  It also
+can decode raw EIA-708 from formats such as WTV, however this is
+untested.
+
+ at subsection Options
+
+ at table @option
+ at item cc_channel
+The CEA-608 caption channel to decode from 1 - 4, when available. Defaults to 1.
+ at item wts_offset
+The Teletext VBI offset to decode, when available. Defaults to 21.
+ at item cc_rollup
+When decoding CEA-608 roll-up style captions, this sets the max number of lines used,
+if unset the default is the roll-up scrollback that is set in the stream.
+ at item vbi_rate
+When set this overrides the packet duration used to calculate the duration
+of each subtitle in MXF VANC streams.  This defaults to 1001/30000 for CEA-608
+and 1/25 for Teletext. For example, some Teletext inserters incorrectly insert
+only one field per packet, which would need a value of 1/50 set.
+ at vbi_delay
+When set this will offset the timestamps by the specified time.  For example, real time
+captioned events can have a delay of 2 to 5 seconds.
+ at vbi_rescale
+When set this will rescale timestamps by the specified value.  For example, a number
+of 23.978 fps videos that are converted to 25 frames require the timestamps rescaled
+by 1000/1001 due to the speed up caused by the decreased frame duration.
+ at vbi_live
+When set to 1, the text will be output as it's received to reduce the delay caused when
+decoding and rendering the text in real time.
+ at end table
+
 @section libzvbi-teletext
 
 Libzvbi allows libavcodec to decode DVB teletext pages and DVB teletext
diff --git a/doc/general.texi b/doc/general.texi
index 49f5ade..608e0ab 100644
--- a/doc/general.texi
+++ b/doc/general.texi
@@ -1029,11 +1029,14 @@ performance on systems without hardware floating point support).
 @item 3GPP Timed Text  @tab   @tab   @tab X @tab X
 @item AQTitle          @tab   @tab X @tab   @tab X
 @item DVB              @tab X @tab X @tab X @tab X
- at item DVB teletext     @tab   @tab X @tab   @tab E
+ at item DVB VBI          @tab   @tab X @tab   @tab X
+ at item CEA-608          @tab   @tab X @tab   @tab X
 @item DVD              @tab X @tab X @tab X @tab X
+ at item EIA-708          @tab   @tab X @tab   @tab X
 @item JACOsub          @tab X @tab X @tab   @tab X
 @item MicroDVD         @tab X @tab X @tab   @tab X
 @item MPL2             @tab   @tab X @tab   @tab X
+ at item MXF VANC         @tab   @tab X @tab   @tab X
 @item MPsub (MPlayer)  @tab   @tab X @tab   @tab X
 @item PGS              @tab   @tab   @tab   @tab X
 @item PJS (Phoenix)    @tab   @tab X @tab   @tab X
diff --git a/doc/muxers.texi b/doc/muxers.texi
index e356235..6b66d05 100644
--- a/doc/muxers.texi
+++ b/doc/muxers.texi
@@ -672,9 +672,11 @@ MPEG transport stream muxer.
 This muxer implements ISO 13818-1 and part of ETSI EN 300 468.
 
 The recognized metadata settings in mpegts muxer are @code{service_provider}
-and @code{service_name}. If they are not set the default for
- at code{service_provider} is "FFmpeg" and the default for
- at code{service_name} is "Service01".
+, @code{service_name}, @code{teletext_page} and @code{teletext_type}.
+If they are not set the default for @code{service_provider} is "FFmpeg",
+the default for @code{service_name} is "Service01" and the default for
+ at code{teletext_type} is "int,sub".  If @code{teletext_page} is not set
+then the VBI stream defaults to CEA-608 instead of Teletext.
 
 @subsection Options
 
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 80ee389..d04b683 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -208,6 +208,7 @@ OBJS-$(CONFIG_DVBSUB_ENCODER)          += dvbsub.o
 OBJS-$(CONFIG_DVDSUB_DECODER)          += dvdsubdec.o
 OBJS-$(CONFIG_DVDSUB_ENCODER)          += dvdsubenc.o
 OBJS-$(CONFIG_DVVIDEO_DECODER)         += dvdec.o dv.o dvdata.o
+OBJS-$(CONFIG_DVDVBI_DECODER)          += vbisubdec.o ass.o
 OBJS-$(CONFIG_DVVIDEO_ENCODER)         += dvenc.o dv.o dvdata.o
 OBJS-$(CONFIG_DXA_DECODER)             += dxa.o
 OBJS-$(CONFIG_DXTORY_DECODER)          += dxtory.o
@@ -219,6 +220,8 @@ OBJS-$(CONFIG_EAMAD_DECODER)           += eamad.o eaidct.o mpeg12.o \
 OBJS-$(CONFIG_EATGQ_DECODER)           += eatgq.o eaidct.o
 OBJS-$(CONFIG_EATGV_DECODER)           += eatgv.o
 OBJS-$(CONFIG_EATQI_DECODER)           += eatqi.o eaidct.o
+OBJS-$(CONFIG_CEA608_DECODER)          += vbisubdec.o ass.o
+OBJS-$(CONFIG_EIA708_DECODER)          += vbisubdec.o ass.o
 OBJS-$(CONFIG_EIGHTBPS_DECODER)        += 8bps.o
 OBJS-$(CONFIG_EIGHTSVX_EXP_DECODER)    += 8svx.o
 OBJS-$(CONFIG_EIGHTSVX_FIB_DECODER)    += 8svx.o
@@ -344,6 +347,7 @@ OBJS-$(CONFIG_MSZH_DECODER)            += lcldec.o
 OBJS-$(CONFIG_MTS2_DECODER)            += mss4.o mss34dsp.o
 OBJS-$(CONFIG_MVC1_DECODER)            += mvcdec.o
 OBJS-$(CONFIG_MVC2_DECODER)            += mvcdec.o
+OBJS-$(CONFIG_MXFVBI_DECODER)          += vbisubdec.o ass.o
 OBJS-$(CONFIG_MXPEG_DECODER)           += mxpegdec.o
 OBJS-$(CONFIG_NELLYMOSER_DECODER)      += nellymoserdec.o nellymoser.o
 OBJS-$(CONFIG_NELLYMOSER_ENCODER)      += nellymoserenc.o nellymoser.o
@@ -811,6 +815,7 @@ OBJS-$(CONFIG_PNM_PARSER)              += pnm_parser.o pnm.o
 OBJS-$(CONFIG_RV30_PARSER)             += rv34_parser.o
 OBJS-$(CONFIG_RV40_PARSER)             += rv34_parser.o
 OBJS-$(CONFIG_TAK_PARSER)              += tak_parser.o tak.o
+OBJS-$(CONFIG_VBIDVB_PARSER)           += vbisub_parser.o
 OBJS-$(CONFIG_VC1_PARSER)              += vc1_parser.o vc1.o vc1data.o vc1dsp.o \
                                           msmpeg4.o msmpeg4data.o mpeg4video.o \
                                           h263.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index 29b45f3..f323119 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -484,10 +484,13 @@ void avcodec_register_all(void)
     REGISTER_DECODER(CCAPTION,          ccaption);
     REGISTER_ENCDEC (DVBSUB,            dvbsub);
     REGISTER_ENCDEC (DVDSUB,            dvdsub);
+    REGISTER_DECODER (CEA608,           cea608);
+    REGISTER_DECODER (EIA708,           eia708);
     REGISTER_DECODER(JACOSUB,           jacosub);
     REGISTER_DECODER(MICRODVD,          microdvd);
     REGISTER_ENCDEC (MOVTEXT,           movtext);
     REGISTER_DECODER(MPL2,              mpl2);
+    REGISTER_DECODER (MXFVBI,           mxfvbi);
     REGISTER_DECODER(PGSSUB,            pgssub);
     REGISTER_DECODER(PJS,               pjs);
     REGISTER_DECODER(REALTEXT,          realtext);
@@ -555,6 +558,7 @@ void avcodec_register_all(void)
     REGISTER_PARSER(DNXHD,              dnxhd);
     REGISTER_PARSER(DPX,                dpx);
     REGISTER_PARSER(DVBSUB,             dvbsub);
+    REGISTER_PARSER(DVBVBI,             dvbvbi);
     REGISTER_PARSER(DVDSUB,             dvdsub);
     REGISTER_PARSER(DVD_NAV,            dvd_nav);
     REGISTER_PARSER(FLAC,               flac);
diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index 99467bb..6a0fb5b 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -503,11 +503,13 @@ enum AVCodecID {
     AV_CODEC_ID_SSA,
     AV_CODEC_ID_MOV_TEXT,
     AV_CODEC_ID_HDMV_PGS_SUBTITLE,
-    AV_CODEC_ID_DVB_TELETEXT,
+    AV_CODEC_ID_DVB_VBI,
     AV_CODEC_ID_SRT,
     AV_CODEC_ID_MICRODVD   = MKBETAG('m','D','V','D'),
-    AV_CODEC_ID_EIA_608    = MKBETAG('c','6','0','8'),
+    AV_CODEC_ID_CEA_608    = MKBETAG('c','6','0','8'),
+    AV_CODEC_ID_EIA_708    = MKBETAG('c','7','0','8'),
     AV_CODEC_ID_JACOSUB    = MKBETAG('J','S','U','B'),
+    AV_CODEC_ID_MXF_VANC   = MKBETAG('M','V','A','N'),
     AV_CODEC_ID_SAMI       = MKBETAG('S','A','M','I'),
     AV_CODEC_ID_REALTEXT   = MKBETAG('R','T','X','T'),
     AV_CODEC_ID_STL        = MKBETAG('S','p','T','L'),
diff --git a/libavcodec/ccaption_dec.c b/libavcodec/ccaption_dec.c
index 1666797..4187d3d 100644
--- a/libavcodec/ccaption_dec.c
+++ b/libavcodec/ccaption_dec.c
@@ -528,7 +528,7 @@ AVCodec ff_ccaption_decoder = {
     .name           = "cc_dec",
     .long_name      = NULL_IF_CONFIG_SMALL("Closed Caption (EIA-608 / CEA-708) Decoder"),
     .type           = AVMEDIA_TYPE_SUBTITLE,
-    .id             = AV_CODEC_ID_EIA_608,
+    .id             = AV_CODEC_ID_EIA_708,
     .priv_data_size = sizeof(CCaptionSubContext),
     .init           = init_decoder,
     .close          = close_decoder,
diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
index 0af66f4..0cb1d39 100644
--- a/libavcodec/codec_desc.c
+++ b/libavcodec/codec_desc.c
@@ -2575,10 +2575,10 @@ static const AVCodecDescriptor codec_descriptors[] = {
         .props     = AV_CODEC_PROP_BITMAP_SUB,
     },
     {
-        .id        = AV_CODEC_ID_DVB_TELETEXT,
+        .id        = AV_CODEC_ID_DVB_VBI,
         .type      = AVMEDIA_TYPE_SUBTITLE,
-        .name      = "dvb_teletext",
-        .long_name = NULL_IF_CONFIG_SMALL("DVB teletext"),
+        .name      = "dvbvbi",
+        .long_name = NULL_IF_CONFIG_SMALL("DVB VBI"),
     },
     {
         .id        = AV_CODEC_ID_SRT,
@@ -2609,10 +2609,32 @@ static const AVCodecDescriptor codec_descriptors[] = {
         .props     = AV_CODEC_PROP_TEXT_SUB,
     },
     {
-        .id        = AV_CODEC_ID_EIA_608,
+        .id        = AV_CODEC_ID_CEA_608,
         .type      = AVMEDIA_TYPE_SUBTITLE,
-        .name      = "eia_608",
-        .long_name = NULL_IF_CONFIG_SMALL("EIA-608 closed captions"),
+        .name      = "cea608",
+        .long_name = NULL_IF_CONFIG_SMALL("CEA-608 closed captions"),
+        .props     = AV_CODEC_PROP_TEXT_SUB,
+    },
+    {
+        .id        = AV_CODEC_ID_EIA_708,
+        .type      = AVMEDIA_TYPE_SUBTITLE,
+        .name      = "eia708",
+        .long_name = NULL_IF_CONFIG_SMALL("EIA-708 DTV closed captions"),
+        .props     = AV_CODEC_PROP_TEXT_SUB,
+    },
+    {
+        .id        = AV_CODEC_ID_DVB_VBI,
+        .type      = AVMEDIA_TYPE_SUBTITLE,
+        .name      = "dvb_vbi",
+        .long_name = NULL_IF_CONFIG_SMALL("EBU DVB Vertical Blanking Interval"),
+        .props     = AV_CODEC_PROP_TEXT_SUB,
+    },
+    {
+        .id        = AV_CODEC_ID_MXF_VANC,
+        .type      = AVMEDIA_TYPE_SUBTITLE,
+        .name      = "mxf_vanc",
+        .long_name = NULL_IF_CONFIG_SMALL("SMPTE MXF Vertical Ancillary Data Space"),
+        .props     = AV_CODEC_PROP_TEXT_SUB,
     },
     {
         .id        = AV_CODEC_ID_JACOSUB,
diff --git a/libavcodec/dvbsub_parser.c b/libavcodec/dvbsub_parser.c
index d15c891..a0ef868 100644
--- a/libavcodec/dvbsub_parser.c
+++ b/libavcodec/dvbsub_parser.c
@@ -42,6 +42,113 @@ static av_cold int dvbsub_parse_init(AVCodecParserContext *s)
     return 0;
 }
 
+static int dvbvbi_parse(AVCodecParserContext *s,
+                        AVCodecContext *avctx,
+                        const uint8_t **poutbuf, int *poutbuf_size,
+                        const uint8_t *buf, int buf_size)
+{
+    DVBSubParseContext *pc = s->priv_data;
+    uint8_t *p, *p_end;
+    int i, len, buf_pos = 0;
+
+    av_dlog(avctx, "DVB parse packet pts=%"PRIx64", lpts=%"PRIx64", cpts=%"PRIx64":\n",
+            s->pts, s->last_pts, s->cur_frame_pts[s->cur_frame_start_index]);
+
+    for (i=0; i < buf_size; i++)
+    {
+        av_dlog(avctx, "%02x ", buf[i]);
+        if (i % 16 == 15)
+            av_dlog(avctx, "\n");
+    }
+
+    if (i % 16 != 0)
+        av_dlog(avctx, "\n");
+
+    *poutbuf = NULL;
+    *poutbuf_size = 0;
+
+    s->fetch_timestamp = 1;
+
+    if (s->last_pts != s->pts && s->pts != AV_NOPTS_VALUE) /* Start of a new packet */
+    {
+        if (pc->packet_index != pc->packet_start)
+        {
+            av_dlog(avctx, "Discarding %d bytes\n",
+                    pc->packet_index - pc->packet_start);
+        }
+
+        pc->packet_start = 0;
+        pc->packet_index = 0;
+
+        if (!buf_size || buf[0]&0xf0 != 0x10 || buf[0]&0xfc != 0x98) {
+            av_dlog(avctx, "Bad packet header\n");
+            return -1;
+        }
+
+        buf_pos = 1;
+
+        pc->in_packet = 1;
+    } else {
+        if (pc->packet_start != 0)
+        {
+            if (pc->packet_index != pc->packet_start)
+            {
+                memmove(pc->packet_buf, pc->packet_buf + pc->packet_start,
+                            pc->packet_index - pc->packet_start);
+
+                pc->packet_index -= pc->packet_start;
+                pc->packet_start = 0;
+            } else {
+                pc->packet_start = 0;
+                pc->packet_index = 0;
+            }
+        }
+    }
+
+    if (buf_size - buf_pos + pc->packet_index > PARSE_BUF_SIZE)
+        return -1;
+
+/* if not currently in a packet, discard data */
+    if (pc->in_packet == 0)
+        return buf_size;
+
+    memcpy(pc->packet_buf + pc->packet_index, buf + buf_pos, buf_size - buf_pos);
+    pc->packet_index += buf_size - buf_pos;
+
+    p = pc->packet_buf;
+    p_end = pc->packet_buf + pc->packet_index;
+
+    while (p < p_end)
+    {
+
+        if (p + 2 <= p_end)
+        {
+            len = p[1];
+
+            if (p + len + 2 <= p_end)
+            {
+                *poutbuf_size += len + 2;
+
+                p += len + 2;
+            } else
+                break;
+        } else
+            break;
+
+    }
+
+    if (*poutbuf_size > 0)
+    {
+        *poutbuf = pc->packet_buf;
+        pc->packet_start = *poutbuf_size;
+    }
+
+    if (s->pts == AV_NOPTS_VALUE)
+        s->pts = s->last_pts;
+
+    return buf_size;
+}
+
 static int dvbsub_parse(AVCodecParserContext *s,
                         AVCodecContext *avctx,
                         const uint8_t **poutbuf, int *poutbuf_size,
@@ -170,6 +277,14 @@ static av_cold void dvbsub_parse_close(AVCodecParserContext *s)
     av_freep(&pc->packet_buf);
 }
 
+AVCodecParser ff_dvbvbi_parser = {
+    .codec_ids      = { AV_CODEC_ID_DVB_VBI },
+    .priv_data_size = sizeof(DVBSubParseContext),
+    .parser_init    = dvbsub_parse_init,
+    .parser_parse   = dvbsub_parse,
+    .parser_close   = dvbsub_parse_close,
+};
+
 AVCodecParser ff_dvbsub_parser = {
     .codec_ids      = { AV_CODEC_ID_DVB_SUBTITLE },
     .priv_data_size = sizeof(DVBSubParseContext),
diff --git a/libavcodec/h264.h b/libavcodec/h264.h
index cf4998f..7899016 100644
--- a/libavcodec/h264.h
+++ b/libavcodec/h264.h
@@ -349,6 +349,9 @@ typedef struct H264Context {
     GetBitContext gb;
     ERContext er;
 
+    uint8_t *a53_caption;
+    int a53_caption_size;
+
     H264Picture *DPB;
     H264Picture *cur_pic_ptr;
     H264Picture cur_pic;
diff --git a/libavcodec/h264_sei.c b/libavcodec/h264_sei.c
index 8e1697a..9e5749a 100644
--- a/libavcodec/h264_sei.c
+++ b/libavcodec/h264_sei.c
@@ -111,20 +111,72 @@ static int decode_picture_timing(H264Context *h)
 static int decode_user_data_itu_t_t35(H264Context *h, int size)
 {
     uint32_t user_identifier;
+    uint8_t country_code;
+    uint16_t provider_code;
     int dtg_active_format;
 
-    if (size < 7)
+    if (size < 3)
         return -1;
-    size -= 7;
-
-    skip_bits(&h->gb, 8);   // country_code
-    skip_bits(&h->gb, 16);  // provider_code
-    user_identifier = get_bits_long(&h->gb, 32);
+    size -= 3;
+
+    country_code = get_bits(&h->gb, 8);
+    provider_code = get_bits(&h->gb, 16);
+    if(country_code == 181 && provider_code == 47) // directv is GA94 without the user_identifier
+        user_identifier = 0x47413934;
+    else if(country_code == 181 && provider_code == 49) // ATSC user_identifier codes
+    {
+        if (size < 4)
+            return -1;
+        size -= 4;
+
+        user_identifier = get_bits_long(&h->gb, 32);
+    }
 
-    switch (user_identifier) {
-        case 0x44544731:    // "DTG1" - AFD_data
+    if(country_code == 181 && (provider_code == 47 || provider_code == 49))
+    {
+        switch (user_identifier)
+        {
+        case 0x47413934: // ATSC1_data
             if (size < 1)
                 return -1;
+            size--;
+            /*  ATSC1 codes are 3 = ATSC cc_data; 4 = SCTE cc_data; 6 = bar_data */
+            if(get_bits(&h->gb, 8) == 3)
+            {
+                if(provider_code == 47 && size > 0)
+                {
+                    if (size < 1)
+                        return -1;
+                    size--;
+                    skip_bits(&h->gb, 8); // directv length
+                }
+                if (size < 1)
+                    return -1;
+                size--;
+                if (get_bits(&h->gb, 1))
+                {
+                    skip_bits(&h->gb, 1);
+                    /* extract A53 Part 4 CC data */
+                    int cc_count = get_bits(&h->gb, 5);
+                    if (cc_count > 0 &&  size >= 1 + cc_count * 3)
+                    {
+                        size--;
+                        skip_bits(&h->gb, 8); // unused em data field
+                        av_freep(&h->a53_caption);
+                        h->a53_caption_size = cc_count * 3;
+                        size -= h->a53_caption_size;
+                        h->a53_caption      = av_malloc(h->a53_caption_size);
+                        if (h->a53_caption)
+                            for(int i; i < cc_count * 3; i++)
+                                h->a53_caption[i] = get_bits(&h->gb, 8);
+                    }
+                }
+                if(size > 0) skip_bits(&h->gb, size * 8);
+            }
+            break;
+        case 0x44544731:    // "DTG1" - AFD_data
+            if (size < 1)
+                 return -1;
             skip_bits(&h->gb, 1);
             if (get_bits(&h->gb, 1)) {
                 skip_bits(&h->gb, 6);
@@ -140,7 +192,8 @@ static int decode_user_data_itu_t_t35(H264Context *h, int size)
         default:
             skip_bits(&h->gb, size * 8);
             break;
-    }
+        }
+    } else skip_bits(&h->gb, size * 8);
 
     return 0;
 }
diff --git a/libavcodec/h264_slice.c b/libavcodec/h264_slice.c
index 6d19c73..f4791a6 100644
--- a/libavcodec/h264_slice.c
+++ b/libavcodec/h264_slice.c
@@ -1691,6 +1691,14 @@ int ff_h264_decode_slice_header(H264Context *h, H264Context *h0)
                 h0->first_field = 0;
                 return AVERROR_INVALIDDATA;
             }
+            if (h->a53_caption) {
+                AVFrameSideData *sd = av_frame_new_side_data(
+                    &h->cur_pic_ptr->f, AV_FRAME_DATA_A53_CC,
+                    h->a53_caption_size);
+                if (sd)
+                    memcpy(sd->data, h->a53_caption, h->a53_caption_size);
+                av_freep(&h->a53_caption);
+            }
         } else {
             release_unused_pictures(h, 0);
         }
diff --git a/libavcodec/libzvbi-teletextdec.c b/libavcodec/libzvbi-teletextdec.c
index 15c1a5d..0055766 100644
--- a/libavcodec/libzvbi-teletextdec.c
+++ b/libavcodec/libzvbi-teletextdec.c
@@ -359,12 +359,6 @@ static void handler(vbi_event *ev, void *user_data)
     vbi_unref_page(&page);
 }
 
-static inline int data_identifier_is_teletext(int data_identifier) {
-    /* See EN 301 775 section 4.4.2. */
-    return (data_identifier >= 0x10 && data_identifier <= 0x1F ||
-            data_identifier >= 0x99 && data_identifier <= 0x9B);
-}
-
 static int slice_to_vbi_lines(TeletextContext *ctx, uint8_t* buf, int size)
 {
     int lines = 0;
@@ -424,22 +418,20 @@ static int teletext_decode_frame(AVCodecContext *avctx, void *data, int *data_si
 
         ctx->handler_ret = pkt->size;
 
-        if (data_identifier_is_teletext(*pkt->data)) {
-            if ((lines = slice_to_vbi_lines(ctx, pkt->data + 1, pkt->size - 1)) < 0)
-                return lines;
-            av_dlog(avctx, "ctx=%p buf_size=%d lines=%u pkt_pts=%7.3f\n",
-                    ctx, pkt->size, lines, (double)pkt->pts/90000.0);
-            if (lines > 0) {
+        if ((lines = slice_to_vbi_lines(ctx, pkt->data + 1, pkt->size - 1)) < 0)
+            return lines;
+        av_dlog(avctx, "ctx=%p buf_size=%d lines=%u pkt_pts=%7.3f\n",
+                ctx, pkt->size, lines, (double)pkt->pts/90000.0);
+        if (lines > 0) {
 #ifdef DEBUG
-                int i;
-                av_log(avctx, AV_LOG_DEBUG, "line numbers:");
-                for(i = 0; i < lines; i++)
-                    av_log(avctx, AV_LOG_DEBUG, " %d", ctx->sliced[i].line);
-                av_log(avctx, AV_LOG_DEBUG, "\n");
+            int i;
+            av_log(avctx, AV_LOG_DEBUG, "line numbers:");
+            for(i = 0; i < lines; i++)
+                av_log(avctx, AV_LOG_DEBUG, " %d", ctx->sliced[i].line);
+            av_log(avctx, AV_LOG_DEBUG, "\n");
 #endif
-                vbi_decode(ctx->vbi, ctx->sliced, lines, 0.0);
-                ctx->lines_processed += lines;
-            }
+            vbi_decode(ctx->vbi, ctx->sliced, lines, 0.0);
+            ctx->lines_processed += lines;
         }
         ctx->pts = AV_NOPTS_VALUE;
         ret = ctx->handler_ret;
@@ -560,7 +552,7 @@ AVCodec ff_libzvbi_teletext_decoder = {
     .name      = "libzvbi_teletextdec",
     .long_name = NULL_IF_CONFIG_SMALL("Libzvbi DVB teletext decoder"),
     .type      = AVMEDIA_TYPE_SUBTITLE,
-    .id        = AV_CODEC_ID_DVB_TELETEXT,
+    .id        = AV_CODEC_ID_DVB_VBI,
     .priv_data_size = sizeof(TeletextContext),
     .init      = teletext_init_decoder,
     .close     = teletext_close_decoder,
diff --git a/libavcodec/vbisubdec.c b/libavcodec/vbisubdec.c
new file mode 100644
index 0000000..541d3da
--- /dev/null
+++ b/libavcodec/vbisubdec.c
@@ -0,0 +1,2444 @@
+
+/*
+ * WTS and CEA-608 VBI decoding for ffmpeg
+ *
+ * Copyright (c) 2014 Shan Weber
+ *
+ * Specs. for WTS from ETSI ETS 300 706, DVB VBI from EN 301 775,
+ * WTS SDP VANC from:
+ * http://www.freetv.com.au/media/Engineering/Free_TV_OP_47_Storage_and_Distribution_of_Teletext_Subtitle_and_VBI_Data_for_High_Definition_Television_Issue%205_December_2012.pdf
+ *
+ * CEA-608 from http://en.wikipedia.org/wiki/CEA-608 and
+ *              http://www.geocities.com/mcpoodle43/SCC_TOOLS/DOCS/CC_CODES.HTML
+ * EIA-708 VANC encapsulation from http://en.wikipedia.org/wiki/EIA-708
+ *
+ * This library 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 "avcodec.h"
+#include "ass.h"
+#include "libavutil/opt.h"
+#include "libavutil/avstring.h"
+#include "libavutil/common.h"
+#include "libavutil/bprint.h"
+#include "libavutil/intreadwrite.h"
+
+// for development of 708
+//#define av_dlog(pctx, ...) av_log(pctx, AV_LOG_DEBUG, __VA_ARGS__)
+
+typedef struct VBICodecContext {
+    AVClass *class;
+    int64_t start;
+    uint16_t frames;
+    AVBPrint buf[2];
+    uint8_t tbuf[6][41];
+    AVRational rate;
+    AVRational rescale;
+    int live;
+    int offset;
+    int channel;
+    int service;
+    int rt_lines;
+    int64_t delay;
+    uint8_t last_pos;
+    uint16_t last_line;
+    uint16_t last_cmd;
+    uint8_t meta_len;
+    uint8_t active_chan;
+    uint8_t active_buf;
+    uint8_t bg_load;
+    uint8_t line_count;
+    uint8_t emp_opened;
+    uint8_t clr_opened;
+    uint32_t charset;
+} VBICodecContext;
+
+static uint8_t deparity(uint8_t seq, uint8_t ebu) {
+    /*
+     * Here we check odd parity and return acceptable values on error
+     * ebu set to 7 (default) reorders bits back to 7 -> 0 bit order
+     * ebu set to 4 reorders bits back to 3 -> 0 bit order
+     */
+    uint8_t test = 0;
+
+    for (uint8_t i = 128; i > 0; i=(i/2))
+        if (seq & i) test++;
+
+    if (!(test % 2))
+    {
+        if (ebu == 4) return 15; // all bits on for nibble error
+        else if (ebu) return 32; // space char for char error
+        else return 0; // null for 608 error
+    }
+
+    if (ebu == 4)
+        return ((seq&64)/64) + ((seq&16)/8) + (seq&4) + ((seq&1)*8);
+    else if (ebu) return (seq & 2) << 5 | (seq & 4) << 3 | (seq & 8) << 1 |
+         (seq & 16) >> 1 | (seq & 32) >> 3 | (seq & 64) >> 5 | (seq & 128) >> 7;
+    else return seq & 0x7f;
+}
+
+static uint32_t deparity3(uint8_t seq1, uint8_t seq2, uint8_t seq3) {
+    /*
+     * Here we check odd parity and return a null value on error
+     * We also reorder bits back to 17 -> 0 bit order
+     */
+    uint8_t test = 0;
+
+    for (uint8_t i = 128; i > 0; i=(i/2))
+    {
+        if (seq1 & i) test++;
+        if (seq2 & i) test++;
+        if (seq3 & i) test++;
+    }
+
+    if (!(test % 2))
+        return 0; // null for error
+
+    return ((seq1&32)/32) + ((seq1&8)/4) + (seq1&4) + ((seq1&2)*4) +
+            ((seq2&128)/8) + ((seq2&64)/2) + ((seq2&32)*2) + ((seq2&16)*8) +
+            ((seq2&8)*32) + ((seq2&4)*128) + ((seq2&2)*512) + ((seq3&128)*16) +
+            ((seq3&64)*64) + ((seq3&32)*256) + ((seq3&16)*1024) + ((seq3&8)*3096) +
+            ((seq3&4)*12384) + ((seq3&2)*49536);
+
+}
+
+static int process_char(AVBPrint *buf, char text, int charset) {
+    if(charset == 1) // Special North American character set
+    {
+        switch (text)
+        {
+        case 0:
+            av_bprintf(buf, "(R)");
+            break;
+        case 1:
+            av_bprintf(buf, " degrees");
+            break;
+        case 2:
+            av_bprintf(buf, " 1/2");
+            break;
+        case 3: // inverted question mark
+            av_bprint_chars(buf, 0xc2, 1);
+            av_bprint_chars(buf, 0xbf, 1);
+            break;
+        case 4:
+            av_bprintf(buf, "  TM");
+            break;
+        case 5:
+            av_bprintf(buf, " cents");
+            break;
+        case 6: // british pound
+            av_bprint_chars(buf, 0xc2, 1);
+            av_bprint_chars(buf, 0xa3, 1);
+            break;
+        case 7: // musical notation to beamed sixteenth notes
+            av_bprint_chars(buf,0xe2,1);
+            av_bprint_chars(buf,0x99,1);
+            av_bprint_chars(buf,0xac,1);
+            break;
+        case 8: // a w/ grave
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0xa0, 1);
+            break;
+        /* 9 is ignored due to being a transparent space for minor col alignments */
+        case 10: // e w/ grave
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0xa8, 1);
+            break;
+        case 11: // a w/ circumflex
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0xa2, 1);
+            break;
+        case 12: // e w/ circumflex
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0xaa, 1);
+            break;
+        case 13: // i w/ circumflex
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0xae, 1);
+            break;
+        case 14: // o w/ circumflex
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0xb4, 1);
+            break;
+        case 15: // u w/ circumflex
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0xbb, 1);
+            break;
+        }
+    }
+    else if(charset == 2) // Extended Spanish/French character set
+    {
+        switch (text)
+        {
+        case 0: // A w/ accute
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0x81, 1);
+            break;
+        case 1: // E w/ accute
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0x89, 1);
+            break;
+        case 2: // O w/ accute
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0x93, 1);
+            break;
+        case 3: // U w/ accute
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0x9a, 1);
+            break;
+        case 4: // U w/ diaeresis
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0x9c, 1);
+            break;
+        case 5: // u w/ diaeresis
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0xbc, 1);
+            break;
+        case 6: // quotation mark
+            av_bprintf(buf, "'");
+            break;
+        case 7: // inverted exclaimation mark
+            av_bprint_chars(buf, 0xc2, 1);
+            av_bprint_chars(buf, 0xa1, 1);
+            break;
+        case 8:
+            av_bprintf(buf, "*");
+            break;
+        case 9: // quotation mark
+            av_bprintf(buf, "'");
+            break;
+        case 10: // emdash
+            av_bprintf(buf, "--");
+            break;
+        case 11:
+            av_bprintf(buf, "(C)");
+            break;
+        case 12:
+            av_bprintf(buf, "  SM");
+            break;
+        case 13: // bullet mark
+            av_bprintf(buf, "*");
+            break;
+        case 14: // quotation mark
+            av_bprintf(buf, "\"");
+            break;
+        case 15: // quotation mark
+            av_bprintf(buf, "\"");
+            break;
+        case 16: // A w/ grave
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0x80, 1);
+            break;
+        case 17: // A w/ circumflex
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0x82, 1);
+            break;
+        case 18: // C w/ cedilla
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0xa7, 1);
+            break;
+        case 19: // E w/ grave
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0x88, 1);
+            break;
+        case 20: // E w/ circumflex
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0x8a, 1);
+            break;
+        case 21: // E w/ diaeresis
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0x8b, 1);
+            break;
+        case 22: // e w/ diaeresis
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0xab, 1);
+            break;
+        case 23: // I w/ circumflex
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0x8e, 1);
+            break;
+        case 24: // I w/ diaeresis
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0xef, 1);
+            break;
+        case 25: // i w/ diaeresis
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0xaf, 1);
+            break;
+        case 26: // O w/ circumflex
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0x94, 1);
+            break;
+        case 27: // U w/ grave
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0x99, 1);
+            break;
+        case 28: // u w/ grave
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0xb9, 1);
+            break;
+        case 29: // U w/ circumflex
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0x9b, 1);
+            break;
+        case 30: // gillemet
+            av_bprintf(buf, "<");
+            break;
+        case 31: // gillemet
+            av_bprintf(buf, ">");
+            break;
+        }
+    }
+    else if(charset == 3) // Extended Portuguese/German/Danish character set
+    {
+        switch (text)
+        {
+        case 0: // A w/ tilde
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0x83, 1);
+            break;
+        case 1: // a w/ tilde
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0xa3, 1);
+            break;
+        case 2: // I w/ accute
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0x8d, 1);
+            break;
+        case 3: // I w/ grave
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0x8c, 1);
+            break;
+        case 4: // i w/ grave
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0xac, 1);
+            break;
+        case 5: // O w/ accute
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0x92, 1);
+            break;
+        case 6: // o w/ grave
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0xb2, 1);
+            break;
+        case 7: // O w/ tilde
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0x95, 1);
+            break;
+        case 8: // o w/ tilde
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0xb5, 1);
+            break;
+        case 9: // brace to bracket
+            av_bprintf(buf, "[");
+            break;
+        case 10: // brace to bracket
+            av_bprintf(buf, "]");
+            break;
+        case 11: // backslash to slash
+            av_bprintf(buf, "/");
+            break;
+        case 12:
+            av_bprintf(buf, "^");
+            break;
+        case 13:
+            av_bprintf(buf, "_");
+            break;
+        case 14:
+            av_bprintf(buf, "|");
+            break;
+        case 15:
+            av_bprintf(buf, "~");
+            break;
+        case 16: // A w/ diaeresis
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0x84, 1);
+            break;
+        case 17: // a w/ diaeresis
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0xa4, 1);
+            break;
+        case 18: // O w/ diaeresis
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0x96, 1);
+            break;
+        case 19: // o w/ diaeresis
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0xb6, 1);
+            break;
+        case 20: // s sharp
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0x9f, 1);
+            break;
+        case 21: // YEN sign
+            av_bprint_chars(buf, 0xc2, 1);
+            av_bprint_chars(buf, 0xa5, 1);
+            break;
+        case 22: // currency sign
+            av_bprint_chars(buf, 0xc2, 1);
+            av_bprint_chars(buf, 0xa4, 1);
+            break;
+        case 23: // vertical bar
+            av_bprintf(buf, "|");
+            break;
+        case 24: // A w/ ring
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0x85, 1);
+            break;
+        case 25: // a w/ ring
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0xa5, 1);
+            break;
+        case 26: // O w/ stroke
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0x98, 1);
+            break;
+        case 27: // o w/ stroke
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0xb8, 1);
+            break;
+        case 28 ... 31: // box corners
+            av_bprintf(buf, "+");
+            break;
+        }
+    }
+    else // Standard North American character set
+    {
+        switch (text)
+        {
+        case 0: // parity error to space
+            av_bprintf(buf, " ");
+            break;
+        case 42: // a w/ acute
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0xa1, 1);
+            break;
+        case 92: // e w/ acute
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0xa9, 1);
+            break;
+        case 94: // i w/ acute
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0xad, 1);
+            break;
+        case 95: // o w/ acute
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0xb3, 1);
+            break;
+        case 96: // u w/ acute
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0xba, 1);
+            break;
+        case 123: // c w/ cedilla
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0xa7, 1);
+            break;
+        case 124: // division sign
+            av_bprintf(buf, "/");
+            break;
+        case 125: // N w/ tilde
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0x91, 1);
+            break;
+        case 126: // n w/ tilde
+            av_bprint_chars(buf, 0xc3, 1);
+            av_bprint_chars(buf, 0xb1, 1);
+            break;
+        case 127: // solid block
+            av_bprintf(buf, "[]");
+            break;
+        default:
+            av_bprint_chars(buf, text, 1);
+        }
+    }
+    return 0;
+}
+
+static int process_line(AVBPrint *buf, const uint8_t *text, int line_end, int row, int charset, int last_pos) {
+
+    char text_char;
+
+    int pos=1,speaker=0;
+
+    uint8_t col,text_start=0,bracket_opened=0,clr_opened=0,emphesis_opened=0;
+
+    for (col=0 ; col < line_end ; col++)
+    {
+        if (text_start)
+        {
+            text_char = deparity(*text,7);
+            switch (text_char)
+            {
+            //case 0 ... 7: // color change in box denotes emphesis
+                //emphesis_opened^=1;
+                //av_bprintf(buf, "*");
+            case 0:
+            case 7:
+                clr_opened = 0;
+                av_bprintf(buf, "{\\c}");
+                break;
+            case 1:
+                if(clr_opened) av_bprintf(buf, "{\\c}");
+                clr_opened = 1;
+                av_bprintf(buf, "{\\c&HFF&}");
+                break;
+            case 2:
+                if(clr_opened) av_bprintf(buf, "{\\c}");
+                clr_opened = 1;
+                av_bprintf(buf, "{\\c&HFF00&}");
+                break;
+            case 3:
+                if(clr_opened) av_bprintf(buf, "{\\c}");
+                clr_opened = 1;
+                av_bprintf(buf, "{\\c&HFFFF&}");
+                break;
+            case 4:
+                if(clr_opened) av_bprintf(buf, "{\\c}");
+                clr_opened = 1;
+                av_bprintf(buf, "{\\c&HFF0000&}");
+                break;
+            case 5:
+                if(clr_opened) av_bprintf(buf, "{\\c}");
+                clr_opened = 1;
+                av_bprintf(buf, "{\\c&HFF00FF&}");
+                break;
+            case 6:
+                if(clr_opened) av_bprintf(buf, "{\\c}");
+                clr_opened = 1;
+                av_bprintf(buf, "{\\c&HFFFF00&}");
+                break;
+            case 10: // end of text box
+                col=line_end;
+                break;
+            case 32 ... 127:
+                if(charset == 1  || charset == 9  ||
+                        charset == 17  || charset == 65) // french
+                {
+                    switch (text_char)
+                    {
+                    case 35: // e w/ acute
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xa9, 1);
+                        break;
+                    case 36: // i w/ diaeresis
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xaf, 1);
+                        break;
+                    case 64: // a w/ grave
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xa0, 1);
+                        break;
+                    case 91: // e w/ diaeresis
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xab, 1);
+                        break;
+                    case 92: // e w/ circumflex
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xaa, 1);
+                        break;
+                    case 93: // u w/ grave
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xb9, 1);
+                        break;
+                    case 94: // i w/ circumflex
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xae, 1);
+                        break;
+                    case 95: // pound sign
+                        av_bprint_chars(buf, '#', 1);
+                        break;
+                    case 96: // e w/ grave
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xa8, 1);
+                        break;
+                    case 123: // a w/ circumflex
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xa2, 1);
+                        break;
+                    case 124: // o w/ circumflex
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xb4, 1);
+                        break;
+                    case 125: // u w/ circumflex
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xbb, 1);
+                        break;
+                    case 126: // c w/ cedilla
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xa7, 1);
+                        break;
+                    default:
+                        av_bprint_chars(buf, text_char, 1);
+                    }
+                }
+                else if(charset == 2 || charset == 10  || charset == 18) // scandinavian
+                {
+                    switch (text_char)
+                    {
+                    case 64: // E w/ acute
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0x89, 1);
+                        break;
+                    case 91: // A w/ diaeresis
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0x84, 1);
+                        break;
+                    case 92: // O w/ diaeresis
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0x96, 1);
+                        break;
+                    case 93: // A w/ ring
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0x85, 1);
+                        break;
+                    case 96: // e w/ acute
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xa9, 1);
+                        break;
+                    case 123: // a w/ diaeresis
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xa4, 1);
+                        break;
+                    case 124: // o w/ diaeresis
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xb6, 1);
+                        break;
+                    case 125: // a w/ ring
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xc5, 1);
+                        break;
+                    case 126: // u w/ diaeresis
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xbc, 1);
+                        break;
+                    default:
+                        av_bprint_chars(buf, text_char, 1);
+                    }
+                }
+                else if(charset == 3 || charset == 11 || charset == 35) // czech
+                {
+                    switch (text_char)
+                    {
+                    case 36: // u w/ ring above
+                        av_bprint_chars(buf, 0xc5, 1);
+                        av_bprint_chars(buf, 0xaf, 1);
+                        break;
+                    case 64: // c w/ caron
+                        av_bprint_chars(buf, 0xc4, 1);
+                        av_bprint_chars(buf, 0x8d, 1);
+                        break;
+                    case 91: // t w/ caron
+                        av_bprint_chars(buf, 0xc5, 1);
+                        av_bprint_chars(buf, 0xa5, 1);
+                        break;
+                    case 92: // z w/ caron
+                        av_bprint_chars(buf, 0xc7, 1);
+                        av_bprint_chars(buf, 0xae, 1);
+                        break;
+                    case 93: // y w/ acute
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xbd, 1);
+                        break;
+                    case 94: // i w/ acute
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xad, 1);
+                        break;
+                    case 95: // r w/ caron
+                        av_bprint_chars(buf, 0xc5, 1);
+                        av_bprint_chars(buf, 0x99, 1);
+                        break;
+                    case 96: // e w/ acute
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xa9, 1);
+                        break;
+                    case 123: // a w/ acute
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xa1, 1);
+                        break;
+                    case 124: // e w/ caron
+                        av_bprint_chars(buf, 0xc4, 1);
+                        av_bprint_chars(buf, 0x9b, 1);
+                        break;
+                    case 125: // u w/ acute
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xba, 1);
+                        break;
+                    case 126: // s w/ caron
+                        av_bprint_chars(buf, 0xc5, 1);
+                        av_bprint_chars(buf, 0xa1, 1);
+                        break;
+                    default:
+                        av_bprint_chars(buf, text_char, 1);
+                    }
+                }
+                else if(charset == 4 || charset == 12 || charset == 20) // german
+                {
+                    switch (text_char)
+                    {
+                    case 64: // section sign
+                        av_bprint_chars(buf, 0xc2, 1);
+                        av_bprint_chars(buf, 0xa7, 1);
+                        break;
+                    case 91: // A w/ diaeresis
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0x84, 1);
+                        break;
+                    case 92: // O w/ diaeresis
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0x96, 1);
+                        break;
+                    case 93: // U w/ diaeresis
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0x9c, 1);
+                        break;
+                    case 96: // degree sign
+                        av_bprint_chars(buf, 0xc2, 1);
+                        av_bprint_chars(buf, 0xb0, 1);
+                        break;
+                    case 123: // a w/ diaeresis
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xa4, 1);
+                        break;
+                    case 124: // o w/ diaeresis
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xb6, 1);
+                        break;
+                    case 125: // u w/ diaeresis
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xbc, 1);
+                        break;
+                    case 126: // s sharp
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0x9f, 1);
+                        break;
+                    default:
+                        av_bprint_chars(buf, text_char, 1);
+                    }
+                }
+                else if(charset == 5 || charset == 21) // spanish
+                {
+                    switch (text_char)
+                    {
+                    case 35: // c w/ cedilla
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xa7, 1);
+                        break;
+                    case 64: // inverted exclaimation mark
+                        av_bprint_chars(buf, 0xc2, 1);
+                        av_bprint_chars(buf, 0xa1, 1);
+                        break;
+                    case 91: // a w/ acute
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xa1, 1);
+                        break;
+                    case 92: // e w/ acute
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xa9, 1);
+                        break;
+                    case 93: // i w/ acute
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xad, 1);
+                        break;
+                    case 94: // o w/ acute
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xb3, 1);
+                        break;
+                    case 95: // u w/ acute
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xba, 1);
+                        break;
+                    case 96: // inverted question mark
+                        av_bprint_chars(buf, 0xc2, 1);
+                        av_bprint_chars(buf, 0xbf, 1);
+                        break;
+                    case 123: // u w/ diaeresis
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xbc, 1);
+                        break;
+                    case 124: // n w/ tilde
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xb1, 1);
+                        break;
+                    case 125: // e w/ grave
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xa8, 1);
+                        break;
+                    case 126: // a w/ grave
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xa0, 1);
+                        break;
+                    default:
+                        av_bprint_chars(buf, text_char, 1);
+                    }
+                }
+                else if(charset == 6 || charset == 14  || charset == 22) // italian
+                {
+                    switch (*text)
+                    {
+                    case 35: // british pound
+                        av_bprint_chars(buf, 0xc2, 1);
+                        av_bprint_chars(buf, 0xa3, 1);
+                        break;
+                    case 64: // e w/ acute
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xa9, 1);
+                        break;
+                    case 91: // degree sign
+                        av_bprint_chars(buf, 0xc2, 1);
+                        av_bprint_chars(buf, 0xb0, 1);
+                        break;
+                    case 92: // c w/ cedilla
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xa7, 1);
+                        break;
+                    case 95: // pound sign
+                        av_bprint_chars(buf, '#', 1);
+                        break;
+                    case 96: // u w/ grave
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xb9, 1);
+                        break;
+                    case 123: // a w/ grave
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xa0, 1);
+                        break;
+                    case 124: // o w/ grave
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xb2, 1);
+                        break;
+                    case 125: // e w/ grave
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xa8, 1);
+                        break;
+                    case 126: // i w/ grave
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xac, 1);
+                        break;
+                    default:
+                        av_bprint_chars(buf, text_char, 1);
+                    }
+                }
+                else if(charset == 8) // polish
+                {
+                    switch (text_char)
+                    {
+                    case 36: // n w/ acute
+                        av_bprint_chars(buf, 0xc4, 1);
+                        av_bprint_chars(buf, 0xa4, 1);
+                        break;
+                    case 64: // a w/ ogonek
+                        av_bprint_chars(buf, 0xc4, 1);
+                        av_bprint_chars(buf, 0x85, 1);
+                        break;
+                    case 91: // Z w/ stroke
+                        av_bprint_chars(buf, 0xc6, 1);
+                        av_bprint_chars(buf, 0xb5, 1);
+                        break;
+                    case 92: // S w/ acute
+                        av_bprint_chars(buf, 0xc5, 1);
+                        av_bprint_chars(buf, 0x9a, 1);
+                        break;
+                    case 93: // L w/ bar
+                        av_bprint_chars(buf, 0xc8, 1);
+                        av_bprint_chars(buf, 0xbd, 1);
+                        break;
+                    case 94: // c w/ acute
+                        av_bprint_chars(buf, 0xc4, 1);
+                        av_bprint_chars(buf, 0x87, 1);
+                        break;
+                    case 95: // o w/ acute
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xb3, 1);
+                        break;
+                    case 96: // e w/ ogonek
+                        av_bprint_chars(buf, 0xc4, 1);
+                        av_bprint_chars(buf, 0x99, 1);
+                        break;
+                    case 123: // z w/ dot
+                        av_bprint_chars(buf, 0xc5, 1);
+                        av_bprint_chars(buf, 0xbc, 1);
+                        break;
+                    case 124: // s w/ acute
+                        av_bprint_chars(buf, 0xc5, 1);
+                        av_bprint_chars(buf, 0x9b, 1);
+                        break;
+                    case 125: // l w/ bar
+                        av_bprint_chars(buf, 0xc6, 1);
+                        av_bprint_chars(buf, 0x9a, 1);
+                        break;
+                    case 126: // z w/ acute
+                        av_bprint_chars(buf, 0xc5, 1);
+                        av_bprint_chars(buf, 0xba, 1);
+                        break;
+                    default:
+                        av_bprint_chars(buf, text_char, 1);
+                    }
+                }
+                else if(charset == 19 || charset == 51) // turkish
+                {
+                    switch (text_char)
+                    {
+                    case 35: // turkish currency sign
+                        av_bprint_chars(buf, '$', 1);
+                        break;
+                    case 36: // g w/ breve
+                        av_bprint_chars(buf, 0xc4, 1);
+                        av_bprint_chars(buf, 0x9f, 1);
+                        break;
+                    case 64: // I w/ dot
+                        av_bprint_chars(buf, 0xc4, 1);
+                        av_bprint_chars(buf, 0xb0, 1);
+                        break;
+                    case 91: // S w/ cedilla
+                        av_bprint_chars(buf, 0xc5, 1);
+                        av_bprint_chars(buf, 0x9e, 1);
+                        break;
+                    case 92: // O w/ diaeresis
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0x96, 1);
+                        break;
+                    case 93: // C w/ cedilla
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xa7, 1);
+                        break;
+                    case 94: // U w/ diaeresis
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0x9c, 1);
+                        break;
+                    case 95: // G w/ caron
+                        av_bprint_chars(buf, 0xce, 1);
+                        av_bprint_chars(buf, 0xa6, 1);
+                        break;
+                    case 96: // i w/o dot
+                        av_bprint_chars(buf, 0xc4, 1);
+                        av_bprint_chars(buf, 0xb1, 1);
+                        break;
+                    case 123: // s w/ cedilla
+                        av_bprint_chars(buf, 0xc5, 1);
+                        av_bprint_chars(buf, 0x9f, 1);
+                        break;
+                    case 124: // o w/ diaeresis
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xb6, 1);
+                        break;
+                    case 125: // c w/ cedilla
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xa7, 1);
+                        break;
+                    case 126: // u w/ diaeresis
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xbc, 1);
+                        break;
+                    default:
+                        av_bprint_chars(buf, text_char, 1);
+                    }
+                }
+                else if(charset == 29) // slavic
+                {
+                    switch (text_char)
+                    {
+                    case 36: // E w/ diaeresis
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0x8b, 1);
+                        break;
+                    case 64: // C w/ caron
+                        av_bprint_chars(buf, 0xc4, 1);
+                        av_bprint_chars(buf, 0x8c, 1);
+                        break;
+                    case 91: // C w/ acute
+                        av_bprint_chars(buf, 0xc4, 1);
+                        av_bprint_chars(buf, 0x86, 1);
+                        break;
+                    case 92: // Z w/ caron
+                        av_bprint_chars(buf, 0xc5, 1);
+                        av_bprint_chars(buf, 0xbd, 1);
+                        break;
+                    case 93: // D w/ stroke
+                        av_bprint_chars(buf, 0xc4, 1);
+                        av_bprint_chars(buf, 0x90, 1);
+                        break;
+                    case 94: // S w/ caron
+                        av_bprint_chars(buf, 0xc5, 1);
+                        av_bprint_chars(buf, 0xa0, 1);
+                        break;
+                    case 95: // e w/ diaeresis
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xab, 1);
+                        break;
+                    case 96: // c w/ caron
+                        av_bprint_chars(buf, 0xc4, 1);
+                        av_bprint_chars(buf, 0x8d, 1);
+                        break;
+                    case 123: // c w/ acute
+                        av_bprint_chars(buf, 0xc4, 1);
+                        av_bprint_chars(buf, 0x87, 1);
+                        break;
+                    case 124: // z w/ caron
+                        av_bprint_chars(buf, 0xc5, 1);
+                        av_bprint_chars(buf, 0xbe, 1);
+                        break;
+                    case 125: // d w/ stroke
+                        av_bprint_chars(buf, 0xc4, 1);
+                        av_bprint_chars(buf, 0x91, 1);
+                        break;
+                    case 126: // s w/ caron
+                        av_bprint_chars(buf, 0xc5, 1);
+                        av_bprint_chars(buf, 0xa1, 1);
+                        break;
+                    default:
+                        av_bprint_chars(buf, text_char, 1);
+                    }
+                }
+                else if(charset == 31) // romanian
+                {
+                    switch (text_char)
+                    {
+                    case 64: // T w/ cedilla
+                        av_bprint_chars(buf, 0xc5, 1);
+                        av_bprint_chars(buf, 0xa2, 1);
+                        break;
+                    case 91: // A w/ circumflex
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0x82, 1);
+                        break;
+                    case 92: // S w/ cedilla
+                        av_bprint_chars(buf, 0xc5, 1);
+                        av_bprint_chars(buf, 0x9e, 1);
+                        break;
+                    case 93: // A w/ breve
+                        av_bprint_chars(buf, 0xc4, 1);
+                        av_bprint_chars(buf, 0x82, 1);
+                        break;
+                    case 94: // I w/ circumflex
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0x8e, 1);
+                        break;
+                    case 95: // i w/o dot
+                        av_bprint_chars(buf, 0xc4, 1);
+                        av_bprint_chars(buf, 0xb1, 1);
+                        break;
+                    case 96: // t w/ cedilla
+                        av_bprint_chars(buf, 0xc5, 1);
+                        av_bprint_chars(buf, 0xa3, 1);
+                        break;
+                    case 123: // a w/ circumflex
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xa2, 1);
+                        break;
+                    case 124: // s w/ cedilla
+                        av_bprint_chars(buf, 0xc5, 1);
+                        av_bprint_chars(buf, 0x9f, 1);
+                        break;
+                    case 125: // a w/ breve
+                        av_bprint_chars(buf, 0xc4, 1);
+                        av_bprint_chars(buf, 0x83, 1);
+                        break;
+                    case 126: // i w/ circumflex
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xae, 1);
+                        break;
+                    default:
+                        av_bprint_chars(buf, text_char, 1);
+                    }
+                }
+                else if(charset == 34) // baltic finnic
+                {
+                    switch (text_char)
+                    {
+                    case 36: // o w/ tilde
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xb5, 1);
+                        break;
+                    case 64: // S w/ caron
+                        av_bprint_chars(buf, 0xc5, 1);
+                        av_bprint_chars(buf, 0xa0, 1);
+                        break;
+                    case 91: // A w/ diaeresis
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0x84, 1);
+                        break;
+                    case 92: // O w/ diaeresis
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0x96, 1);
+                        break;
+                    case 93: // Z w/ caron
+                        av_bprint_chars(buf, 0xc5, 1);
+                        av_bprint_chars(buf, 0xbd, 1);
+                        break;
+                    case 94: // U w/ diaeresis
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0x9c, 1);
+                        break;
+                    case 95: // O w/ tilde
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0x95, 1);
+                        break;
+                    case 96: // s w/ caron
+                        av_bprint_chars(buf, 0xc5, 1);
+                        av_bprint_chars(buf, 0xa1, 1);
+                        break;
+                    case 123: // a w/ diaeresis
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xa4, 1);
+                        break;
+                    case 124: // o w/ diaeresis
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xb6, 1);
+                        break;
+                    case 125: // z w/ caron
+                        av_bprint_chars(buf, 0xc5, 1);
+                        av_bprint_chars(buf, 0xbe, 1);
+                        break;
+                    case 126: // u w/ diaeresis
+                        av_bprint_chars(buf, 0xc3, 1);
+                        av_bprint_chars(buf, 0xbc, 1);
+                        break;
+                    default:
+                        av_bprint_chars(buf, text_char, 1);
+                    }
+                }
+                else if(charset == 38) // baltic
+                {
+                    switch (text_char)
+                    {
+                    case 64: // S w/ caron
+                        av_bprint_chars(buf, 0xc5, 1);
+                        av_bprint_chars(buf, 0xa0, 1);
+                        break;
+                    case 91: // e w/ dot
+                        av_bprint_chars(buf, 0xc4, 1);
+                        av_bprint_chars(buf, 0x97, 1);
+                        break;
+                    case 92: // e w/ ogonek
+                        av_bprint_chars(buf, 0xc4, 1);
+                        av_bprint_chars(buf, 0x99, 1);
+                        break;
+                    case 93: // Z w/ caron
+                        av_bprint_chars(buf, 0xc5, 1);
+                        av_bprint_chars(buf, 0xbd, 1);
+                        break;
+                    case 94: // c w/ caron
+                        av_bprint_chars(buf, 0xc4, 1);
+                        av_bprint_chars(buf, 0x8d, 1);
+                        break;
+                    case 95: // u w/ macron
+                        av_bprint_chars(buf, 0xc5, 1);
+                        av_bprint_chars(buf, 0xab, 1);
+                        break;
+                    case 96: // s w/ caron
+                        av_bprint_chars(buf, 0xc5, 1);
+                        av_bprint_chars(buf, 0xa1, 1);
+                        break;
+                    case 123: // a w/ ogonek
+                        av_bprint_chars(buf, 0xc4, 1);
+                        av_bprint_chars(buf, 0x85, 1);
+                        break;
+                    case 124: // u w/ ogonek
+                        av_bprint_chars(buf, 0xc7, 1);
+                        av_bprint_chars(buf, 0xa3, 1);
+                        break;
+                    case 125: // z w/ caron
+                        av_bprint_chars(buf, 0xc5, 1);
+                        av_bprint_chars(buf, 0xbe, 1);
+                        break;
+                    case 126: // i w/ ogonek
+                        av_bprint_chars(buf, 0xc4, 1);
+                        av_bprint_chars(buf, 0xbf, 1);
+                        break;
+                    default:
+                        av_bprint_chars(buf, text_char, 1);
+                    }
+                }
+                else // standard english
+                {
+                    switch (text_char)
+                    {
+                    case '*': // used in converted CEA-608 musical notation substitutions
+                        av_bprint_chars(buf,0xe2,1);
+                        av_bprint_chars(buf,0x99,1);
+                        av_bprint_chars(buf,0xac,1);
+                        break;
+                    case '>': // used in converted CEA-608 rollup
+                        if(text[1] == 0x7c)
+                        {
+                            av_bprintf(buf,"-");
+                            text++;
+                        }
+                        else if (text[1] == 4)
+                            av_bprintf(buf,"-");
+                        else av_bprint_chars(buf, text_char, 1);
+                        break;
+                    case 35: // british pound
+                        av_bprint_chars(buf, 0xc2, 1);
+                        av_bprint_chars(buf, 0xa3, 1);
+                        break;
+                    case 92: // vulgar half fraction
+                        av_bprintf(buf, "1/2");
+                        break;
+                    case 95: // num sign or musical notation substitution
+                        if(text[1] == 4 || text[1] == 81) // avoid conflict with twitter tags
+                        {
+                            // use beamed sixteenth notes
+                            av_bprint_chars(buf,0xe2,1);
+                            av_bprint_chars(buf,0x99,1);
+                            av_bprint_chars(buf,0xac,1);
+                        }
+                        else av_bprint_chars(buf, '#', 1);
+                        break;
+                    case 123: // emdash to double dash
+                        av_bprintf(buf, "--");
+                        break;
+                    case 125: // vulgar quarter fraction
+                        av_bprintf(buf, "1/4");
+                        break;
+                    case 126: // vulgar three quarters fraction
+                        av_bprintf(buf, "3/4");
+                        break;
+                    default:
+                        av_bprint_chars(buf, text_char, 1);
+                    }
+                }
+                break;
+            }
+        }
+        else
+        {
+            switch (*text)
+            {
+            case 128:
+                clr_opened = 1;
+                break;
+            case 64: // green text for third speaker
+                //if(!bracket_opened) speaker=3;
+                clr_opened = 2;
+                break;
+            case 193: // yellow text for second speaker
+                clr_opened = 3;
+                //if(!bracket_opened) speaker=2;
+                break;
+            case 97: // cyan text for next speaker
+                clr_opened = 4;
+                //if(!bracket_opened) speaker=1;
+                break;
+            case 32:
+                clr_opened = 5;
+                break;
+            case 161:
+                clr_opened = 6;
+                break;
+            case 185: // background color change for non-speech sounds
+                bracket_opened=1;
+                av_bprintf(buf, "[");
+                col++;
+                text++;
+                break;
+            case 208: // text box start
+                text_start=col;
+                if (*text+1 == 208)
+                {
+                    col++;
+                    text++;
+                }
+                if(row)
+                {
+                    if(col > 20)
+                        pos+=2;
+                    else if (col > 10)
+                        pos++;
+                    if(row < 12)
+                        pos+=4;
+                }
+                /*
+                 * convert Teletext positioning to some sane
+                 * positions
+                 */
+                if(!last_pos && pos!=2)
+                    av_bprintf(buf, "{\\a%u}",pos);
+                else if(last_pos != pos)
+                    av_bprintf(buf, "{\\a%u}",pos);
+                if(clr_opened)
+                {
+                    switch(clr_opened)
+                    {
+                    case 1:
+                        av_bprintf(buf, "{\\c&HFF&}");
+                        break;
+                    case 2:
+                        av_bprintf(buf, "{\\c&HFF00&}");
+                        break;
+                    case 3:
+                        av_bprintf(buf, "{\\c&HFFFF&}");
+                        break;
+                    case 4:
+                        av_bprintf(buf, "{\\c&HFFFF00&}");
+                        break;
+                    case 5:
+                        av_bprintf(buf, "{\\c&HFF0000&}");
+                        break;
+                    case 6:
+                        av_bprintf(buf, "{\\c&HFF00FF&}");
+                        break;
+                    }
+                }
+                if(speaker == 1)
+                    av_bprintf(buf, "- ");
+                else if (speaker)
+                    av_bprintf(buf, "%u: ",speaker);
+                break;
+            }
+        }
+        text++;
+    }
+    if(emphesis_opened)
+        av_bprintf(buf, "*");
+    if(clr_opened)
+        av_bprintf(buf, "{\\c}");
+    if(bracket_opened)
+        av_bprintf(buf, "]");
+
+
+    return pos;
+}
+
+static int vbi_subtitle_decode_init(AVCodecContext *avctx) {
+
+    VBICodecContext *ctx = avctx->priv_data;
+    if(!avctx->time_base.num)
+        avctx->time_base = (AVRational){1,90000}; // assume an MPEG PES timebase
+
+    if(ctx->delay)
+        ctx->delay = av_rescale_q(ctx->delay,
+                                    AV_TIME_BASE_Q,
+                                     avctx->time_base);
+
+    ctx->active_chan = 1;
+
+    return ff_ass_subtitle_header_default(avctx);
+}
+
+static int vbi_subtitle_decode_frame(AVCodecContext *avctx,
+                            void *data, int *got_sub_ptr, AVPacket *avpkt)
+{
+    AVSubtitle *sub = data;
+    int ebu_smpte_type  = 197, ebu_smpte_len = 0, cc_count = 1,
+        dtv_srv=0, dtv_size=0, dtv_skip=0, dtv_ext=0, vbi_hdr;
+    uint8_t vbi_fields[5], buf[32], cc_multi=0, no_filler=0;
+    uint16_t test;
+    const char *ptr = avpkt->data;
+    const char *end;
+
+    VBICodecContext *ctx = avctx->priv_data;
+
+    if (!ptr || avpkt->size < 2)
+        return AVERROR_INVALIDDATA;
+
+    end = ptr + avpkt->size;
+
+    memset(vbi_fields, 0, 5);
+
+/*
+    if (avpkt->size > 20 && avctx->codec_id == AV_CODEC_ID_MXF_VANC)
+        av_dlog(ctx, "SMTPE ANC packets\n");
+
+    else if (avctx->codec_id == AV_CODEC_ID_DVB_VBI)
+    {
+        /* MPEG-PS demuxer doesn't pass the data_identifier byte /
+        //if(AV_RB8(ptr+1) != 44 && AV_RB8(ptr+1) != 3 && AV_RB8(ptr) != 0xc5)
+            //ptr++;
+        //av_dlog(ctx, "EBU DVB VBI packets 0x%X\n",AV_RB8(ptr));
+        av_dlog(ctx, "EBU DVB VBI packets\n");
+    } */
+
+    while (end - ptr >= 2) {
+        if (avctx->codec_id == AV_CODEC_ID_MXF_VANC)
+        {
+            ptr+=16;  // skip ANC line header
+            ebu_smpte_type = AV_RB16(ptr);  // read SMPTE ANC DID and SDID as single uint16_t
+            av_dlog(ctx, "DID: %u SDID: %u\n",ebu_smpte_type/256,ebu_smpte_type%256);
+            ptr+=2;
+        }
+        else if (avctx->codec_id == AV_CODEC_ID_DVB_VBI)
+        {
+            ebu_smpte_type = AV_RB8(ptr);
+            ptr++;
+            av_dlog(ctx, "Data Unit: %u",ebu_smpte_type);
+        }
+
+        if (avctx->codec_id == AV_CODEC_ID_DVB_VBI || avctx->codec_id == AV_CODEC_ID_MXF_VANC)
+        {
+            ebu_smpte_len = AV_RB8(ptr);
+            ptr++;
+            av_dlog(ctx, " (%u bytes)\n", ebu_smpte_len);
+        }
+        if (avctx->codec_id == AV_CODEC_ID_CEA_608)
+        {
+            no_filler=1;  // here we rely on the PTS for the sub end time
+            vbi_fields[0] = 32;  // odd field
+            if (ctx->channel > 2)
+                vbi_fields[0] = 0; // even field
+
+            // test for quicktime atomized dual field captions
+            if(!(AV_RB16(ptr)))
+            {
+                ebu_smpte_len = AV_RB32(ptr) - 8;
+                if(AV_RB8(ptr+7) == '2') // 'cdt2'
+                    vbi_fields[0] = 0;  // even field
+
+                ptr+=8;
+            }
+            else cc_count = avpkt->size/2;
+
+        }
+        else if (avctx->codec_id == AV_CODEC_ID_EIA_708)
+        {
+
+            //no_filler=1;
+            ebu_smpte_type = 24833;
+            ebu_smpte_len = avpkt->size;
+
+            // test for quicktime atomized captions
+            if(!(AV_RB16(ptr))) // 'ccdp'
+            {
+                ebu_smpte_len -= 8;
+                ptr+=8;
+            }
+        }
+
+        if (ebu_smpte_type == 2 || ebu_smpte_type == 3 || ebu_smpte_type == 17154)
+        {
+            if(!ctx->rate.num) ctx->rate = (AVRational){1,50};
+            if (ebu_smpte_type == 17154) // SMPTE OP-47 SDP
+            {
+                if (*ptr == 0x51 && AV_RB8(ptr+1) == 0x15)
+                {
+                    ptr+=3; // skip IDs and length
+                    if (*ptr != 2) return AVERROR_INVALIDDATA; // check for EBU WST
+                    ptr++;
+                    for (int i=0; i < 5; i++)
+                    {
+                        vbi_fields[i] = *ptr&31;
+                        ptr++;
+                    }
+                } else return AVERROR_INVALIDDATA;
+            }
+            else // default to EBU DVB format
+            {
+                vbi_fields[0] = (*ptr&31);
+                ptr++;
+            }
+            //ptr++; /* skip vbi byte */
+
+            for (int i=0; i < 5; i++)
+            {
+                if (vbi_fields[i])av_dlog(ctx, " |- EBU Teletext VBI line: %u (%u)\n", vbi_fields[i], ctx->offset);
+                if(vbi_fields[i] == ctx->offset)
+                {
+                    if (ctx->frames) ctx->frames++;
+                    if (ebu_smpte_type == 17154) ptr+=2; // skip run-in bytes
+                    ptr++; // skip framing code
+                    vbi_hdr = 0;
+                    if ((deparity(*ptr,4)&8)) vbi_hdr++; // row number bit 0
+                    ptr++;
+                    vbi_hdr += deparity(*ptr,4) * 2; // upper row number bits
+                    ptr++;
+
+                    av_dlog(ctx, " |- EBU Teletext packet: %u", vbi_hdr);
+                    if (!vbi_hdr)
+                    {
+                        if(ctx->frames > (ctx->rate.den/2) && ctx->live)
+                        {
+                            av_bprint_init(&ctx->buf[0], 0, AV_BPRINT_SIZE_UNLIMITED);
+                            ctx->start += (ctx->delay);
+                            if(ctx->rescale.num)
+                                ctx->start = av_rescale(ctx->start,ctx->rescale.num,ctx->rescale.den);
+
+                            ctx->last_pos = 0;
+                            /*
+                             * For various reasons transmission order isn't aways the
+                             * same as row order, so we have to do some reordering
+                             */
+                            int line_order[24];
+                            memset(line_order,0,23);
+                            for(int i=0 ; i < 6 ; i++)
+                                if(ctx->tbuf[i][0])line_order[ctx->tbuf[i][0]]=1;
+
+                            for(int i=1 ; i < 24 ; i++)
+                            {
+                                if(line_order[i])
+                                {
+                                    for(int j=0 ; j < 6 ; j++)
+                                    {
+                                        if(ctx->tbuf[j][0] == i)
+                                        {
+
+                                            if(ctx->buf[0].len > 0) av_bprintf(&ctx->buf[0], "\\N");
+
+                                            ctx->last_pos=process_line(&ctx->buf[0],&ctx->tbuf[j][1],40,ctx->tbuf[j][0],ctx->charset, ctx->last_pos);
+
+                                        }
+                                    }
+                                }
+                            }
+
+
+                            av_dlog(ctx, " |- Start: %d\n", ctx->start);
+                            av_bprintf(&ctx->buf[0], "\r\n");
+                            if (!av_bprint_is_complete(&ctx->buf[0]))
+                                return AVERROR(ENOMEM);
+                            ff_ass_add_rect(sub, ctx->buf[0].str,
+                                                    av_rescale_q(ctx->start,
+                                                        avctx->time_base,
+                                                        (AVRational){1,100}), 100, 0);
+
+                            *got_sub_ptr = sub->num_rects > 0;
+                            av_bprint_finalize(&ctx->buf[0], NULL);
+                            ctx->frames=0;
+                            ctx->tbuf[0][0] = ctx->tbuf[1][0] = ctx->tbuf[2][0] =
+                            ctx->tbuf[3][0] = ctx->tbuf[4][0] = ctx->tbuf[5][0] = 0;
+                        }
+
+                        ptr++; // skip page ones
+                        /*
+                         * ignore filler headers with pages greater than 99
+                         * and non-subtitle flagged headers
+                         */
+                        if(deparity(*ptr,4) < 10)
+                        {
+                            ptr+=2; // skip page tens and first sub-code byte
+
+                            if(deparity(AV_RB8(ptr+2),4)&8) // subtitle flag
+                            {
+                                av_dlog(ctx, " sub header");
+                                test=deparity(*ptr,4);
+
+                                if(test&8 ) // erase page flag
+                                        av_dlog(ctx, " - clear last sub");
+
+                                av_dlog(ctx, "\n");
+                                ptr+=2; // skip two next two sub-code bytes
+
+
+                                if(ctx->live)
+                                {
+                                    sub->end_display_time = 1000;
+                                    ff_ass_add_rect(sub, " \r\n", av_rescale_q(avpkt->pts,avctx->time_base,
+                                                            (AVRational){1,100}), sub->end_display_time/10, 0);
+                                    *got_sub_ptr = sub->num_rects > 0;
+                                }
+                                /*
+                                 * some Teletext encoders pre-roll a short and
+                                 * incomplete page, here we drop these
+                                 */
+                                else if(ctx->frames > (ctx->rate.den/2)) // make sure we have at least half a second of subs
+                                {
+                                    av_bprint_init(&ctx->buf[0], 0, AV_BPRINT_SIZE_UNLIMITED);
+                                    ctx->start += (ctx->delay);
+                                    ctx->frames -= 2;
+                                    if(ctx->rescale.num)
+                                    {
+                                        ctx->start = av_rescale(ctx->start,ctx->rescale.num,ctx->rescale.den);
+                                        ctx->frames = av_rescale(ctx->frames,ctx->rescale.num,ctx->rescale.den);
+                                    }
+
+                                    ctx->last_pos = 0;
+                                    /*
+                                     * For various reasons transmission order isn't aways the
+                                     * same as row order, so we have to do some reordering
+                                     */
+                                    int line_order[24];
+                                    memset(line_order,0,23);
+                                    for(int i=0 ; i < 6 ; i++)
+                                        if(ctx->tbuf[i][0])line_order[ctx->tbuf[i][0]]=1;
+
+                                    for(int i=1 ; i < 24 ; i++)
+                                    {
+                                        if(line_order[i])
+                                        {
+                                            for(int j=0 ; j < 6 ; j++)
+                                            {
+                                                if(ctx->tbuf[j][0] == i)
+                                                {
+
+                                                    if(ctx->buf[0].len > 0) av_bprintf(&ctx->buf[0], "\\N");
+
+                                                    ctx->last_pos=process_line(&ctx->buf[0],&ctx->tbuf[j][1],40,ctx->tbuf[j][0],ctx->charset, ctx->last_pos);
+
+                                                }
+                                                //else break;
+                                            }
+                                        }
+                                    }
+
+
+                                    av_dlog(ctx, " |- Start: %d End: %d\n", ctx->start, ctx->frames);
+                                    av_bprintf(&ctx->buf[0], "\r\n");
+                                    if (!av_bprint_is_complete(&ctx->buf[0]))
+                                        return AVERROR(ENOMEM);
+                                    ff_ass_add_rect(sub, ctx->buf[0].str,
+                                                            av_rescale_q(ctx->start,
+                                                                avctx->time_base,
+                                                                (AVRational){1,100}),
+                                                            av_rescale_q(ctx->frames-2,
+                                                                ctx->rate,
+                                                                (AVRational){1,100}), 0);
+
+                                    *got_sub_ptr = sub->num_rects > 0;
+                                    av_bprint_finalize(&ctx->buf[0], NULL);
+                                    ctx->frames=0;
+                                    ctx->tbuf[0][0] = ctx->tbuf[1][0] = ctx->tbuf[2][0] =
+                                    ctx->tbuf[3][0] = ctx->tbuf[4][0] = ctx->tbuf[5][0] = 0;
+                                }
+
+
+                                ptr+=2; // skip subtitle/newsflash/hide/update/sequence flags
+                                test = deparity(*ptr,4) / 2;
+                                if(test != ctx->charset)
+                                    av_dlog(ctx, " |- charset set: %u\n",test);
+                                ctx->charset=test;
+                                ptr++;
+                                if (AV_RB8(ptr) != 4) // test for header text
+                                {
+                                    test = 0;
+                                    while ((AV_RB8(ptr) != 4 || AV_RB8(ptr+1) != 4) && test < 32)
+                                    {
+                                        buf[test++] = deparity(*ptr,7);
+                                        ptr++;
+                                    }
+                                    if (test < 30)
+                                        ptr+=(32-test);
+                                    buf[test] = 0;
+                                    if (ctx->meta_len != test)
+                                    {
+                                        ctx->meta_len = test;
+                                        av_dlog(ctx, " |- metadata: %s\n", buf);
+                                    }
+                                } else ptr+=32;
+                            }
+                            else
+                            {
+                                av_dlog(ctx, " non-sub header\n");
+                                ptr+=37;
+                            }
+                        }
+                        else
+                        {
+                            av_dlog(ctx, " filler header\n");
+                            ptr+=39;
+                        }
+                    }
+                    else if (vbi_hdr < 24)
+                    {
+                        av_dlog(ctx, " line Start: %d Dur: %u\n",(int)ctx->start/90000,ctx->frames);
+
+
+                        if(!ctx->frames)
+                        {
+                            ctx->start = avpkt->pts;
+                            ctx->frames = 1;
+                        }
+                        /*
+                         * Australian live subtitles emulate
+                         * CEA-608 paint-on, by transmitting duplicate
+                         * lines.  Here only keep the last complete lines
+                         */
+                        for(int i=0 ; i < 6 ; i++)
+                        {
+                            if(ctx->tbuf[i][0] == vbi_hdr || !ctx->tbuf[i][0])
+                            {
+                                ctx->tbuf[i][0] = vbi_hdr;
+                                memcpy(&ctx->tbuf[i][1], ptr, 40);
+                                break;
+                            }
+                        }
+
+                        ptr+=40;
+                    }
+                    else if (vbi_hdr == 28 || vbi_hdr == 29)
+                    {
+                        av_dlog(ctx, " extension\n");
+                        test=deparity(*ptr,4);
+                        if(!test || test == 4)
+                        {
+                            ptr++;
+                            test = deparity3(*ptr,AV_RB8(ptr+1),AV_RB8(ptr+2));
+                            test /= 128;
+                            test &= 127;
+                            if(test != ctx->charset)
+                                av_dlog(ctx, " |- charset set: %u\n",test);
+                            ctx->charset=test;
+                        }
+                        else ptr++;
+                        ptr+=39;
+
+                        if(ctx->charset == 32 || ctx->charset == 36 || ctx->charset == 37 ||
+                            ctx->charset == 55 || ctx->charset == 71 || ctx->charset == 87 ||
+                            ctx->charset == 85)
+                        {
+                            avpriv_request_sample(avctx, "Non-Latin chars");
+
+                            return AVERROR_PATCHWELCOME;
+                        }
+                    }
+                    else
+                    {
+                        av_dlog(ctx, "\n");
+                        ptr+=40;
+                    }
+                }
+                else if (vbi_fields[i])
+                {
+                    if (ebu_smpte_type == 17154) ptr+=2;
+                    ptr+=43;
+                }
+            }
+            if (ebu_smpte_type == 17154) ptr+=4; // skip sdp leadout
+
+        }
+        else if (ebu_smpte_type == 197 || ebu_smpte_type == 24833 || ebu_smpte_type == 24834)
+        {
+            // read CDP fields and skip unneeded bytes
+            if(ebu_smpte_type == 24833)
+            {
+                if (AV_RB16(ptr) == 0x9669) // cdp sig.
+                {
+                    ptr+=3;
+                    ebu_smpte_len-=3;
+                    av_dlog(ctx, " |- EIA-708 frame rate: %x\n",*ptr/16);
+                    // test for common drop frame values, non-drop frames are never used
+                    if(!ctx->rate.num)
+                    {
+                        if((*ptr&0xf0) == 16)
+                            ctx->rate = (AVRational){1001,24000};
+                        else if((*ptr&0xf0) == 0xe0)
+                            ctx->rate = (AVRational){1001,60000};
+                    }
+                    ptr++;
+                    ebu_smpte_len--;
+                    if(!(*ptr&64) && !(*ptr&2))
+                        return AVERROR_INVALIDDATA;
+                    if(*ptr&128)
+                    {
+                        ptr+=9;
+                        ebu_smpte_len-=9;
+                    }
+                    else
+                    {
+                        ptr+=4;
+                        ebu_smpte_len-=4;
+                    }
+                    cc_count = *ptr&31;
+                    av_dlog(ctx, " |- EIA-708 data unit count: %u\n",cc_count);
+                    ptr++;
+                    ebu_smpte_len--;
+                } else cc_count = avpkt->size/3; // handle filtered ffmpeg data
+                //else return AVERROR_INVALIDDATA;
+            }
+            else if (ebu_smpte_type == 197)
+            {
+                cc_count = 1;
+                no_filler=1;
+            }
+            else if (ebu_smpte_type == 24834) cc_count = ebu_smpte_len/3;
+
+            if(!ctx->rate.num)
+                ctx->rate = (AVRational){1001,30000};
+
+            for (int i=0; i < cc_count; i++)
+            {
+                if(avctx->codec_id != AV_CODEC_ID_CEA_608)
+                {
+                    vbi_fields[0] = *ptr;
+                    ptr++;
+                    ebu_smpte_len--;
+                }
+
+                vbi_fields[1] = *ptr;
+                vbi_fields[2] = AV_RB8(ptr+1);
+                if(ebu_smpte_len) ebu_smpte_len-=2;
+                ptr+=2;
+
+
+                if(ebu_smpte_type == 24833 && vbi_fields[0]&4)
+                    av_dlog(ctx, " |- EIA-708 data unit type: %u\n",vbi_fields[0]&3);
+
+                if(ctx->service && ebu_smpte_type == 24833)
+                {
+                    if((vbi_fields[0]&6) == 6)
+                    {
+
+                        if(!dtv_size)
+                        {
+                            dtv_size=(vbi_fields[1+(vbi_fields[0]&1)]&31);
+                            dtv_srv=(vbi_fields[1+(vbi_fields[0]&1)]&0xe0)/32;
+                            av_dlog(ctx, " |- EIA-708 service: %u (%u)\n",dtv_srv,dtv_size);
+                            if(dtv_srv < 7 && dtv_srv == ctx->service)
+                                dtv_skip=0;
+                            else dtv_skip=1;
+
+                            if(!(vbi_fields[0]&1))
+                            {
+                                if(dtv_srv == 7)
+                                {
+                                    dtv_srv=(vbi_fields[2]&0x3f);
+                                    if(dtv_srv == ctx->service)
+                                        dtv_skip=0;
+                                    else dtv_skip=1;
+                                }
+                                else if(!dtv_skip)
+                                {
+                                    av_dlog(ctx, " |- EIA-708 data: %02X\n",vbi_fields[2]);
+                                }
+                                dtv_size--;
+                            } else if(dtv_srv == 7) dtv_srv = -1;
+                        }
+                        else
+                        {
+                            if(dtv_srv < 0)
+                            {
+                                dtv_srv=(vbi_fields[1]&0x3f);
+                                if(dtv_srv == ctx->service)
+                                    dtv_skip=0;
+                                else dtv_skip=1;
+                                if(!dtv_skip)av_dlog(ctx, " |- EIA-708 data: %02X\n",vbi_fields[2]);
+                            }
+                            else if(!dtv_skip)av_dlog(ctx, " |- EIA-708 data: %02X %02X\n",vbi_fields[1], vbi_fields[2]);
+                            dtv_size-=2;
+                        }
+                    }
+
+                    // TODO: decode DTVCC bytes
+                    //avpriv_request_sample(avctx, "EIA-708 DTVCC bytes");
+                    //return AVERROR_PATCHWELCOME;
+                }
+                else
+                {
+                    // select wanted field
+                    if((ctx->channel > 2 && ((ebu_smpte_type == 197 && !(vbi_fields[0]&32)) ||
+                        (ebu_smpte_type == 24834 && !(vbi_fields[0]&128)) ||
+                        (ebu_smpte_type == 24833 && (vbi_fields[0]&7) == 5))) ||
+                        (ctx->channel < 3 && ((ebu_smpte_type == 197 && (vbi_fields[0]&32)) ||
+                        (ebu_smpte_type == 24834 && (vbi_fields[0]&128)) ||
+                        (ebu_smpte_type == 24833 && (vbi_fields[0]&7) == 4))))
+                    {
+                        if (ctx->frames) ctx->frames++;
+                        cc_multi++;
+                        if(ebu_smpte_type == 24833)
+                            av_dlog(ctx, " |- EIA-708 data unit selected: %u\n",vbi_fields[0]&3);
+                        vbi_hdr = 0;
+                        vbi_hdr = deparity(vbi_fields[1],0); // first 608 byte
+                        if(vbi_hdr&0xf0) // ignore XDS, NULL and error bytes
+                        {
+                            if(vbi_hdr&0x60)
+                            {
+                                if(!(ctx->active_chan&2) && ctx->active_chan&1)
+                                {
+                                    if(!ctx->buf[ctx->active_buf].len && !ctx->buf[ctx->active_buf^1].len)
+                                        av_bprint_init(&ctx->buf[ctx->active_buf], 0, AV_BPRINT_SIZE_UNLIMITED);
+                                    if(ctx->charset > 5)
+                                    {
+                                        // TODO: support Asian chars
+                                        if(ctx->charset == 8)
+                                            avpriv_request_sample(avctx, "Chinese GB 2312 chars");
+                                        else if(ctx->charset == 9)
+                                            avpriv_request_sample(avctx, "Korean KS C 5601 chars");
+                                        else avpriv_request_sample(avctx, "Custom NorPak charset %u", ctx->charset);
+                                        return AVERROR_PATCHWELCOME;
+                                    }
+                                    process_char(&ctx->buf[ctx->active_buf^ctx->bg_load], vbi_hdr, ctx->charset);
+                                }
+                                av_dlog(ctx, "%c",vbi_hdr);
+                                vbi_hdr = deparity(vbi_fields[2],0); // second 608 char
+                                if(!(ctx->active_chan&2) && ctx->active_chan&1)
+                                    process_char(&ctx->buf[ctx->active_buf^ctx->bg_load], vbi_hdr, ctx->charset);
+                                av_dlog(ctx, "%c\n",vbi_hdr);
+                                /* minimize realtime change of speaker and story syntax >> to - and >>> to -- */
+                                if(ctx->buf[ctx->active_buf^ctx->bg_load].str[ctx->buf[ctx->active_buf^ctx->bg_load].len-2] == '>' &&
+                                    ctx->buf[ctx->active_buf^ctx->bg_load].str[ctx->buf[ctx->active_buf^ctx->bg_load].len-1] == '>')
+                                {
+                                    ctx->buf[ctx->active_buf^ctx->bg_load].str[ctx->buf[ctx->active_buf^ctx->bg_load].len-2] = '-';
+                                    ctx->buf[ctx->active_buf^ctx->bg_load].str[ctx->buf[ctx->active_buf^ctx->bg_load].len-1] = 0;
+                                    ctx->buf[ctx->active_buf^ctx->bg_load].len--;
+                                }
+                                else if(ctx->buf[ctx->active_buf^ctx->bg_load].str[ctx->buf[ctx->active_buf^ctx->bg_load].len-2] == '>' &&
+                                    ctx->buf[ctx->active_buf^ctx->bg_load].str[ctx->buf[ctx->active_buf^ctx->bg_load].len-1] == ' ')
+                                    ctx->buf[ctx->active_buf^ctx->bg_load].str[ctx->buf[ctx->active_buf^ctx->bg_load].len-2] = '-';
+                                if(ctx->live == 3)
+                                {
+                                    if(!(sub->rects = av_realloc(sub->rects, (sub->num_rects+1) * sizeof(*sub->rects))))
+                                        return AVERROR(ENOMEM);
+                                    sub->rects[sub->num_rects]  = av_mallocz(sizeof(*sub->rects[0]));
+                                    sub->rects[sub->num_rects]->type = SUBTITLE_ASS;
+                                    sub->rects[sub->num_rects]->ass = av_malloc(strlen(ctx->buf[ctx->active_buf^ctx->bg_load].str)+64);
+
+                                    ctx->start = avpkt->pts+ctx->delay;
+                                    if(ctx->rescale.num)
+                                        ctx->start = av_rescale(ctx->start,ctx->rescale.num,ctx->rescale.den);
+                                    sub->end_display_time = 33;
+                                    int ts_start = av_rescale_q(ctx->start,
+                                                avctx->time_base,
+                                                (AVRational){1,100});
+                                    if (!av_bprint_is_complete(&ctx->buf[ctx->active_buf^ctx->bg_load]))
+                                        return AVERROR(ENOMEM);
+                                    sprintf(sub->rects[sub->num_rects]->ass, "Dialogue: 0,%d:%02d:%02d.%02d,%d:%02d:%02d.%02d,Default,,0000,0000,0000,,%s\r\n",
+                                        ts_start/360000,ts_start/6000%60,ts_start/100%60,ts_start%100,
+                                        ts_start/360000,ts_start/6000%60,(ts_start+3)/100%60,(ts_start+3)%100,
+                                        ctx->buf[ctx->active_buf^ctx->bg_load].str);
+                                    sub->num_rects++;
+                                    ctx->start += avctx->time_base.den/avctx->time_base.num/20;
+                                    *got_sub_ptr = sub->num_rects > 0;
+                                }
+
+                            }
+                            else if((ctx->channel&1 && !(vbi_hdr&8)) ||
+                                    (!(ctx->channel&1) && vbi_hdr&8))
+                            {
+                                if(!ctx->active_chan) ctx->active_chan = 1;
+                                else if(ctx->active_chan > 1) av_dlog(ctx, "CEA-608 Text mode active\n");
+                                //else ctx->active_chan = 1;
+                                vbi_hdr&=7;
+                                vbi_hdr*=256;
+                                vbi_hdr+=deparity(vbi_fields[2],0);
+
+                                if(vbi_hdr&127) // ignore command on second byte error
+                                {
+                                    if (vbi_hdr&0x40 && ctx->last_cmd != vbi_hdr) // new line and/or position/color
+                                    {
+                                        av_dlog(ctx, "CEA-608 command: PAC newline row: 0x%04X last: 0x%04X TS: %d\n",
+                                            vbi_hdr&0x72e, ctx->last_line,(int)avpkt->pts/avctx->time_base.den/avctx->time_base.num);
+                                        if(ctx->active_chan == 1)
+                                        {
+                                            test=vbi_hdr&0x720;
+                                            if(ctx->last_line != test)
+                                            {
+                                                if(!ctx->buf[ctx->active_buf].len && !ctx->buf[ctx->active_buf^1].len)
+                                                    av_bprint_init(&ctx->buf[ctx->active_buf], 0, AV_BPRINT_SIZE_UNLIMITED);
+                                                if(ctx->buf[ctx->active_buf^ctx->bg_load].len)
+                                                {
+                                                    if(ctx->emp_opened)
+                                                    {
+                                                        av_bprintf(&ctx->buf[ctx->active_buf^ctx->bg_load], "{\\i0}");
+                                                        ctx->emp_opened = 0;
+                                                    }
+                                                    else if(ctx->clr_opened)
+                                                    {
+                                                        av_bprintf(&ctx->buf[ctx->active_buf^ctx->bg_load], "{\\c}");
+                                                        ctx->clr_opened = 0;
+                                                    }
+                                                    av_bprintf(&ctx->buf[ctx->active_buf^ctx->bg_load], "\\N");
+                                                }
+                                                ctx->last_line=test;
+                                                test=2;
+                                                if((vbi_hdr&24) == 24)
+                                                    test++; // row right
+
+                                                else if((vbi_hdr&28) == 16)
+                                                    test--; // row left
+
+                                                if((vbi_hdr&0x300) && (vbi_hdr&0x300) != 0x300)
+                                                    test+=4; // screen top
+
+                                                if(!ctx->buf[ctx->active_buf^ctx->bg_load].len && test != 2)
+                                                    av_bprintf(&ctx->buf[ctx->active_buf^ctx->bg_load], "{\\a%u}",test);
+                                                else if(ctx->last_pos != test)
+                                                    av_bprintf(&ctx->buf[ctx->active_buf^ctx->bg_load], "{\\a%u}",test);
+
+                                                ctx->last_pos=test;
+                                            }
+                                            else
+                                            {
+                                                if(ctx->buf[ctx->active_buf^ctx->bg_load].len)
+                                                        av_bprintf(&ctx->buf[ctx->active_buf^ctx->bg_load], " ");
+                                                ctx->last_line=test;
+                                            }
+
+                                            if(!(vbi_hdr&16))
+                                            {
+                                                switch (vbi_hdr&14)
+                                                {
+                                                case 2: // green text for third speaker
+                                                    ctx->clr_opened = 1;
+                                                    av_bprintf(&ctx->buf[ctx->active_buf^ctx->bg_load], "{\\c&HFF00&}");
+                                                    break;
+                                                case 4:
+                                                    ctx->clr_opened = 1;
+                                                    av_bprintf(&ctx->buf[ctx->active_buf^ctx->bg_load], "{\\c&HFF0000&}");
+                                                    break;
+                                                case 6: // cyan text for next speaker
+                                                    ctx->clr_opened = 1;
+                                                    av_bprintf(&ctx->buf[ctx->active_buf^ctx->bg_load], "{\\c&HFFFF00&}");
+                                                    break;
+                                                case 8:
+                                                    ctx->clr_opened = 1;
+                                                    av_bprintf(&ctx->buf[ctx->active_buf^ctx->bg_load], "{\\c&HFF&}");
+                                                    break;
+                                                case 10: // yellow text for second speaker
+                                                    ctx->clr_opened = 1;
+                                                    av_bprintf(&ctx->buf[ctx->active_buf^ctx->bg_load], "{\\c&HFFFF&}");
+                                                    break;
+                                                case 12:
+                                                    ctx->clr_opened = 1;
+                                                    av_bprintf(&ctx->buf[ctx->active_buf^ctx->bg_load], "{\\c&HFF00FF&}");
+                                                    break;
+                                                case 14: // italic text for emphesis
+                                                    av_bprintf(&ctx->buf[ctx->active_buf^ctx->bg_load], "{\\i1}");
+                                                    ctx->emp_opened = 1;
+                                                    break;
+                                                }
+                                            }
+                                        }
+
+                                    }
+                                    /* test for main commands and ignore duplicates */
+                                    else if ((vbi_hdr&0x7f0) == 0x420 && ctx->last_cmd != vbi_hdr)
+                                    {
+                                        //av_dlog(ctx, "CEA-608 control: %u\n",vbi_hdr&15);
+                                        /*
+                                         * for loaded captions or pop-up, the EOC
+                                         * signals the start time
+                                         */
+                                        if ((vbi_hdr&15) == 15 && ctx->active_chan == 1) // EOC
+                                        {
+                                            if(ctx->live > 1) ctx->live -= 2;
+                                            if(ctx->frames > 1 || ctx->live&1)
+                                            {
+                                                if(no_filler)
+                                                    ctx->frames = av_rescale_q(avpkt->pts-ctx->start,
+                                                                    avctx->time_base,
+                                                                    ctx->rate);
+                                                ctx->start += (ctx->delay);
+                                                ctx->frames += (-3+cc_multi);
+                                                if(ctx->rescale.num)
+                                                {
+                                                    ctx->start = av_rescale(ctx->start,ctx->rescale.num,ctx->rescale.den);
+                                                    ctx->frames = av_rescale(ctx->frames,ctx->rescale.num,ctx->rescale.den);
+                                                }
+                                                if(ctx->live&1)
+                                                    sub->end_display_time = 2000;
+                                                else sub->end_display_time = av_rescale_q(ctx->frames,
+                                                            ctx->rate,
+                                                            (AVRational){1,1000});
+
+                                                if(!ctx->buf[ctx->active_buf].len)
+                                                    av_bprintf(&ctx->buf[ctx->active_buf], ".");
+                                                if(ctx->emp_opened)
+                                                    av_bprintf(&ctx->buf[ctx->active_buf], "{\\i0}");
+                                                else if(ctx->clr_opened)
+                                                    av_bprintf(&ctx->buf[ctx->active_buf], "{\\c}");
+
+
+                                                av_bprintf(&ctx->buf[ctx->active_buf], "\r\n");
+                                                if (!av_bprint_is_complete(&ctx->buf[ctx->active_buf]))
+                                                    return AVERROR(ENOMEM);
+                                                ff_ass_add_rect(sub, ctx->buf[ctx->active_buf].str,
+                                                                    av_rescale_q(ctx->start,
+                                                                        avctx->time_base,
+                                                                        (AVRational){1,100}),
+                                                                    sub->end_display_time/10, 0);
+                                                av_bprint_finalize(&ctx->buf[ctx->active_buf], NULL);
+                                                *got_sub_ptr = sub->num_rects > 0;
+                                                ctx->frames = ctx->line_count = ctx->emp_opened = ctx->clr_opened = 0;
+                                                av_bprint_init(&ctx->buf[ctx->active_buf], 0, AV_BPRINT_SIZE_UNLIMITED);
+                                            }
+                                            ctx->active_buf^=ctx->bg_load;
+
+                                            ctx->bg_load=0;
+                                            /*
+                                             * test for caps with multiple start times and one end time
+                                             */
+                                            if(!ctx->frames)
+                                            {
+                                                if(cc_multi > 1)
+                                                {
+                                                    ctx->start = av_rescale_q(cc_multi-1, ctx->rate, avctx->time_base);
+                                                    ctx->start += avpkt->pts;
+
+                                                }
+                                                else ctx->start = avpkt->pts;
+                                                ctx->frames = 1;
+                                            }
+                                            av_dlog(ctx, "CEA-608 command: EOC start of sub - Start: %d active buf %u\n",
+                                                (int)ctx->start/avctx->time_base.den/avctx->time_base.num,ctx->active_buf);
+                                        }
+                                        else if ((vbi_hdr&15) == 13) // CR
+                                        {
+                                            ctx->live |= 2;
+                                            /*
+                                             * In Rollup mode the CR defines the start and end of
+                                             * single line subtitles
+                                             */
+
+                                            if(ctx->line_count && ctx->active_chan == 1)
+                                                ctx->line_count--;
+
+                                            av_dlog(ctx, "CEA-608 command: CR single line sub TS: %d dur: %u secs\n",
+                                                (int)avpkt->pts/avctx->time_base.den/avctx->time_base.num,ctx->line_count);
+
+                                        }
+                                        /*
+                                         * The EDM always signals the end of the caption
+                                         */
+                                        else if ((vbi_hdr&15) == 12) // EDM
+                                        {
+                                            av_dlog(ctx, "CEA-608 command: EDM end of sub TS: %d dur: %u secs\n",
+                                                (int)avpkt->pts/avctx->time_base.den/avctx->time_base.num,ctx->frames/(ctx->rate.den/1000));
+                                            if(ctx->active_chan == 1 && ctx->frames > 1)
+                                            {
+                                                if(no_filler)
+                                                    ctx->frames = av_rescale_q(avpkt->pts-ctx->start,
+                                                                    avctx->time_base,
+                                                                    ctx->rate);
+                                                ctx->start += (ctx->delay);
+                                                ctx->frames += (-3+cc_multi);
+                                                if(ctx->rescale.num)
+                                                {
+                                                    ctx->start = av_rescale(ctx->start,ctx->rescale.num,ctx->rescale.den);
+                                                    ctx->frames = av_rescale(ctx->frames,ctx->rescale.num,ctx->rescale.den);
+                                                }
+
+                                                if(!ctx->buf[ctx->active_buf].len)
+                                                    av_bprintf(&ctx->buf[ctx->active_buf], ".");
+                                                if(ctx->emp_opened)
+                                                    av_bprintf(&ctx->buf[ctx->active_buf], "{\\i0}");
+                                                else if(ctx->clr_opened)
+                                                    av_bprintf(&ctx->buf[ctx->active_buf^ctx->bg_load], "{\\c}");
+
+
+                                                av_bprintf(&ctx->buf[ctx->active_buf], "\r\n");
+                                                if (!av_bprint_is_complete(&ctx->buf[ctx->active_buf]))
+                                                    return AVERROR(ENOMEM);
+                                                if(ctx->live&1)
+                                                    ff_ass_add_rect(sub, " \r\n", av_rescale_q(avpkt->pts,avctx->time_base,(AVRational){1,100}), 100, 0);
+                                                else ff_ass_add_rect(sub, ctx->buf[ctx->active_buf].str,
+                                                                            av_rescale_q(ctx->start,
+                                                                                avctx->time_base,
+                                                                                (AVRational){1,100}),
+                                                                            av_rescale_q(ctx->frames,
+                                                                                ctx->rate,
+                                                                                (AVRational){1,100}), 0);
+                                                av_bprint_finalize(&ctx->buf[ctx->active_buf], NULL);
+                                                *got_sub_ptr = sub->num_rects > 0;
+                                                ctx->frames = 0;
+                                                ctx->line_count = ctx->emp_opened = ctx->clr_opened = 0;
+                                                av_bprint_init(&ctx->buf[ctx->active_buf], 0, AV_BPRINT_SIZE_UNLIMITED);
+
+                                            }
+                                        }
+                                        /*
+                                         * Text mode is for informational paged text,
+                                         * similar to non-subtitled Teletext pages.
+                                         */
+                                        else if ((vbi_hdr&15) > 9 && (vbi_hdr&15) < 12) // TXT MODE
+                                        {
+                                            av_dlog(ctx, "CEA-608 command: TXT non-caption start\n");
+                                            ctx->active_chan = 3;
+                                        }
+                                        /*
+                                         * Direct captioning are captions sent in real-time
+                                         */
+                                        else if ((vbi_hdr&15) == 9) // RDC
+                                        {
+                                            ctx->live |= 2;
+                                            av_dlog(ctx, "CEA-608 command: RDC start of sub Start: %d\n",
+                                                    (int)avpkt->pts/avctx->time_base.den/avctx->time_base.num);
+                                            ctx->active_chan = 1;
+                                            if(!ctx->buf[ctx->active_buf].len)
+                                            {
+                                                av_bprint_init(&ctx->buf[ctx->active_buf], 0, AV_BPRINT_SIZE_UNLIMITED);
+                                                if(cc_multi > 1)
+                                                {
+                                                    ctx->start = av_rescale_q(cc_multi-1, ctx->rate, avctx->time_base);
+                                                    ctx->start += avpkt->pts;
+                                                }
+                                                else ctx->start = avpkt->pts;
+                                                ctx->frames = 1;
+                                                ctx->last_line = 0;
+                                            }
+                                            ctx->last_pos = ctx->last_line = 0;
+                                        }
+                                        /*
+                                         * Direct captioning with a buffered scrowback
+                                         */
+                                        else if ((vbi_hdr&15) > 4 && (vbi_hdr&15) < 8) // RU1-4
+                                        {
+                                            ctx->live |= 2;
+                                            av_dlog(ctx, "CEA-608 command: ROLLUP lines by %u  start: %d dur: %u secs\n",
+                                                    (vbi_hdr&3)+1,(int)avpkt->pts/avctx->time_base.den/avctx->time_base.num,ctx->frames/(ctx->rate.den/1000));
+                                            ctx->active_chan = 1;
+
+                                            if(!ctx->line_count)
+                                            {
+                                                if(ctx->buf[ctx->active_buf].len)
+                                                {
+                                                    if(ctx->live&1)
+                                                        sub->end_display_time = 1000;
+                                                    else
+                                                    {
+                                                        if(no_filler)
+                                                            ctx->frames = av_rescale_q(avpkt->pts-ctx->start,
+                                                                            avctx->time_base,
+                                                                            ctx->rate);
+                                                        ctx->start += (ctx->delay);
+                                                        ctx->frames += (-3+cc_multi);
+                                                        if(ctx->rescale.num)
+                                                        {
+                                                            ctx->start = av_rescale(ctx->start,ctx->rescale.num,ctx->rescale.den);
+                                                            ctx->frames = av_rescale(ctx->frames,ctx->rescale.num,ctx->rescale.den);
+                                                        }
+                                                        sub->end_display_time = av_rescale_q(ctx->frames,
+                                                                ctx->rate,
+                                                                (AVRational){1,1000});
+                                                    }
+
+                                                    if(ctx->emp_opened)
+                                                        av_bprintf(&ctx->buf[ctx->active_buf], "{\\i0}");
+                                                    else if(ctx->clr_opened)
+                                                        av_bprintf(&ctx->buf[ctx->active_buf^ctx->bg_load], "{\\c}");
+
+
+
+                                                    av_bprintf(&ctx->buf[ctx->active_buf], "\r\n");
+                                                    if (!av_bprint_is_complete(&ctx->buf[ctx->active_buf]))
+                                                        return AVERROR(ENOMEM);
+                                                    ff_ass_add_rect(sub, ctx->buf[ctx->active_buf].str,
+                                                                        av_rescale_q(ctx->start,
+                                                                            avctx->time_base,
+                                                                            (AVRational){1,100}),
+                                                                        sub->end_display_time/10, 0);
+                                                    av_bprint_finalize(&ctx->buf[ctx->active_buf], NULL);
+                                                    *got_sub_ptr = sub->num_rects > 0;
+                                                    ctx->frames = 0;
+                                                }
+                                                av_bprint_init(&ctx->buf[ctx->active_buf], 0, AV_BPRINT_SIZE_UNLIMITED);
+                                                ctx->last_pos = ctx->emp_opened = ctx->clr_opened = 0;
+
+                                                if(cc_multi > 1)
+                                                {
+                                                    ctx->start = av_rescale_q(cc_multi-1, ctx->rate, avctx->time_base);
+                                                    ctx->start += avpkt->pts;
+                                                }
+                                                else ctx->start = avpkt->pts;
+                                                ctx->frames = 1;
+                                                if(ctx->rt_lines)
+                                                    ctx->line_count=ctx->rt_lines;
+                                                else ctx->line_count=(vbi_hdr&3)+1;
+                                            }
+                                            ctx->last_line = 0;
+                                        }
+                                        /*
+                                         * Loaded captioning are captions sent before their start time
+                                         */
+                                        else if (!(vbi_hdr&15)) // RCL
+                                        {
+                                            if(ctx->live > 1) ctx->live -= 2;
+                                            av_dlog(ctx, "CEA-608 command: RCL send to second buffer\n");
+                                            if(ctx->emp_opened)
+                                                av_bprintf(&ctx->buf[ctx->active_buf^ctx->bg_load], "{\\i0}");
+                                            else if(ctx->clr_opened)
+                                                av_bprintf(&ctx->buf[ctx->active_buf^ctx->bg_load], "{\\c}");
+
+                                            ctx->active_chan = ctx->bg_load = 1;
+
+                                            if(!ctx->buf[ctx->active_buf^ctx->bg_load].len)
+                                                av_bprint_init(&ctx->buf[ctx->active_buf^ctx->bg_load], 0, AV_BPRINT_SIZE_UNLIMITED);
+
+                                            ctx->last_pos = ctx->last_line = ctx->emp_opened = ctx->clr_opened = 0;
+                                        }
+                                        else av_dlog(ctx, "CEA-608 other command: %u\n",vbi_hdr&15);
+                                    }
+
+                                    else if ((vbi_hdr&0x7f8) == 0x720 && ctx->last_cmd != vbi_hdr) // TAB
+                                    {
+                                        av_dlog(ctx, "CEA-608 command: TAB spacing added\n");
+                                        if(ctx->active_chan == 1)
+                                        {
+                                            if(!ctx->buf[ctx->active_buf].len && !ctx->buf[ctx->active_buf^1].len)
+                                                av_bprint_init(&ctx->buf[ctx->active_buf], 0, AV_BPRINT_SIZE_UNLIMITED);
+                                            av_bprintf(&ctx->buf[ctx->active_buf^ctx->bg_load], " ");
+                                        }
+                                    }
+                                    else if ((vbi_hdr&0x7ff) == 0x724 || vbi_hdr&0x7ff == 0x725) // Norpak STD charset
+                                    {
+                                        av_dlog(ctx, "CEA-608 command: Norpak switched to norm charset\n");
+                                        if(ctx->active_chan == 1) ctx->charset = 0;
+                                    }
+                                    else if ((vbi_hdr&0x7fc) == 0x724) // Norpak other charset
+                                    {
+                                        if(ctx->active_chan == 1) ctx->charset = vbi_hdr&15;
+                                        av_dlog(ctx, "CEA-608 command: Norpak switched to extended charset %u\n", ctx->charset);
+                                    }
+                                    else if ((vbi_hdr&0x7f0) == 0x120 && ctx->last_cmd != vbi_hdr) // color change for emphesis
+                                    {
+                                        av_dlog(ctx, "CEA-608 command: color/italic set %x\n",vbi_hdr&0xe);
+                                        if(ctx->active_chan == 1)
+                                        {
+                                            if(!ctx->buf[ctx->active_buf].len && !ctx->buf[ctx->active_buf^1].len)
+                                                av_bprint_init(&ctx->buf[ctx->active_buf], 0, AV_BPRINT_SIZE_UNLIMITED);
+
+                                            if((vbi_hdr&0xe) == 0xe || ctx->emp_opened)
+                                            {
+                                                av_bprintf(&ctx->buf[ctx->active_buf^ctx->bg_load], "{\\i%d}",ctx->emp_opened^1);
+                                                ctx->emp_opened ^= 1;
+                                            }
+                                            else
+                                            {
+                                                if(!(vbi_hdr&0xe))
+                                                {
+                                                    ctx->clr_opened = 0;
+                                                    av_bprintf(&ctx->buf[ctx->active_buf^ctx->bg_load], "{\\c}");
+                                                }
+                                                else
+                                                {
+                                                    ctx->clr_opened = 1;
+                                                    av_bprintf(&ctx->buf[ctx->active_buf^ctx->bg_load], "{\\c&H");
+                                                    switch(vbi_hdr&0xe)
+                                                    {
+                                                    case 2:
+                                                        av_bprintf(&ctx->buf[ctx->active_buf^ctx->bg_load], "FF00");
+                                                        break;
+                                                    case 4:
+                                                        av_bprintf(&ctx->buf[ctx->active_buf^ctx->bg_load], "FF0000");
+                                                        break;
+                                                    case 6:
+                                                        av_bprintf(&ctx->buf[ctx->active_buf^ctx->bg_load], "FFFF00");
+                                                        break;
+                                                    case 8:
+                                                        av_bprintf(&ctx->buf[ctx->active_buf^ctx->bg_load], "FF");
+                                                        break;
+                                                    case 10:
+                                                        av_bprintf(&ctx->buf[ctx->active_buf^ctx->bg_load], "FFFF");
+                                                        break;
+                                                    case 12:
+                                                        av_bprintf(&ctx->buf[ctx->active_buf^ctx->bg_load], "FF00FF");
+                                                        break;
+                                                    }
+                                                    av_bprintf(&ctx->buf[ctx->active_buf^ctx->bg_load], "&}");
+                                                }
+                                            }
+
+                                        }
+                                    }
+                                    else if ((vbi_hdr&0x7f0) == 0x130) // charset 1 - Special North American
+                                    {
+                                        av_dlog(ctx, "CEA-608 command: Special North American char %u\n",vbi_hdr&0xf);
+                                        if(ctx->active_chan == 1)
+                                        {
+                                            if(!ctx->buf[ctx->active_buf].len && !ctx->buf[ctx->active_buf^1].len)
+                                                av_bprint_init(&ctx->buf[ctx->active_buf], 0, AV_BPRINT_SIZE_UNLIMITED);
+                                            process_char(&ctx->buf[ctx->active_buf^ctx->bg_load], vbi_hdr&15, 1);
+                                        }
+                                    }
+                                    else if (vbi_hdr&0x7e0 == 0x220) // charset 2 - Extended Western European
+                                    {
+                                        av_dlog(ctx, "CEA-608 command: Extended Spanish/French char\n");
+                                        if(ctx->active_chan == 1)
+                                        {
+                                            if(!ctx->buf[ctx->active_buf].len && !ctx->buf[ctx->active_buf^1].len)
+                                                av_bprint_init(&ctx->buf[ctx->active_buf], 0, AV_BPRINT_SIZE_UNLIMITED);
+                                            process_char(&ctx->buf[ctx->active_buf^ctx->bg_load], vbi_hdr&31, 2);
+                                        }
+                                    }
+                                    else if ((vbi_hdr&0x7e0) == 0x330) // charset 3 - Extended Western European
+                                    {
+                                        av_dlog(ctx, "CEA-608 command: Extended Portuguese/German/Danish char\n");
+                                        if(ctx->active_chan == 1)
+                                        {
+                                            if(!ctx->buf[ctx->active_buf].len && !ctx->buf[ctx->active_buf^1].len)
+                                                av_bprint_init(&ctx->buf[ctx->active_buf], 0, AV_BPRINT_SIZE_UNLIMITED);
+                                            process_char(&ctx->buf[ctx->active_buf^ctx->bg_load], vbi_hdr&31, 3);
+                                        }
+                                    }
+                                    else av_dlog(ctx, "CEA-608 other extended/duplicate command: 0x%04X\n",vbi_hdr&0x7ff);
+                                    ctx->last_cmd = vbi_hdr;
+                                } else av_dlog(ctx, "CEA-608 parity error on second byte");
+                            }
+                            else
+                            {
+                                if(ctx->active_chan > 1)
+                                    av_dlog(ctx, "CEA-608 ignoring text channel first byte: 0x%02X\n",vbi_hdr);
+                                else av_dlog(ctx, "CEA-608 ignoring other caption channel first byte: 0x%02X\n",vbi_hdr);
+                            }
+                        } else if (vbi_hdr) av_dlog(ctx, "CEA-608 ignoring XDS first byte: 0x%02X\n",vbi_hdr);
+                    }
+                }
+            }
+            // skip CDP leadout - counter and 8-bit checksum
+            if(*ptr == 0x74 && ebu_smpte_type == 24833)
+                ptr+=4;
+        }
+        else ptr += ebu_smpte_len;
+    }
+
+    return avpkt->size;
+}
+
+static int vbi_subtitle_decode_close(AVCodecContext *avctx)
+{
+    VBICodecContext *s = avctx->priv_data;
+    av_bprint_finalize(&s->buf[0], NULL);
+    av_bprint_finalize(&s->buf[1], NULL);
+    return 0;
+}
+
+#define SUB_OPT_FLAGS AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_DECODING_PARAM
+#define OFST(x) offsetof(VBICodecContext, x)
+static const AVOption options[] = {
+    {"vbi_live", "set VBI decoding mode", OFST(live), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, SUB_OPT_FLAGS},
+    {"vbi_rate", "override VBI timing rate", OFST(rate), AV_OPT_TYPE_RATIONAL, {.dbl = 0}, INT_MIN, INT_MAX, SUB_OPT_FLAGS},
+    {"vbi_rescale", "rescale VBI timestamps", OFST(rescale), AV_OPT_TYPE_RATIONAL, {.dbl = 0}, INT_MIN, INT_MAX, SUB_OPT_FLAGS},
+    {"wts_offset", "set Teletext VBI offset (defaults to 21) to use", OFST(offset), AV_OPT_TYPE_INT, {.i64 = 21}, 6, 22, SUB_OPT_FLAGS},
+    {"vbi_delay", "set subtitle delay time", OFST(delay), AV_OPT_TYPE_DURATION, {.i64 = 0}, INT_MIN, INT_MAX, SUB_OPT_FLAGS},
+    {"cc_rollup", "set number of CEA-608/EIA-708 lines", OFST(rt_lines), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 4, SUB_OPT_FLAGS},
+    {"608_channel", "set CEA-608 channel (1-4) to use", OFST(channel), AV_OPT_TYPE_INT, {.i64 = 1}, 1, 4, SUB_OPT_FLAGS},
+//    {"708_service", "set EIA-708 service (defaults to CEA-608) to use", OFST(service), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 9, SUB_OPT_FLAGS},
+    {NULL},
+};
+
+#define VBI_CLASS(c,o)\
+static const AVClass c##_decoder_class = {\
+    .class_name = #c" decoder",\
+    .item_name  = av_default_item_name,\
+    .option     = o,\
+    .version    = LIBAVUTIL_VERSION_INT,\
+};
+
+#define VBI_DECODE(l_,i_,d_) \
+AVCodec ff_ ## l_ ## _decoder = {\
+    .name           = #l_,\
+    .long_name      = NULL_IF_CONFIG_SMALL(d_ " subtitle"),\
+    .type           = AVMEDIA_TYPE_SUBTITLE,\
+    .id             = AV_CODEC_ID_##i_,\
+    .priv_data_size = sizeof(VBICodecContext),\
+    .init           = vbi_subtitle_decode_init,\
+    .decode         = vbi_subtitle_decode_frame,\
+    .priv_class     = &l_##_decoder_class,\
+}
+
+#if CONFIG_DVBVBI_DECODER
+VBI_CLASS(dvbvbi,options)
+VBI_DECODE(dvbvbi,DVB_VBI,"DVB VBI");
+#endif
+#if CONFIG_MXFVBI_DECODER
+VBI_CLASS(mxfvbi,options)
+VBI_DECODE(mxfvbi,MXF_VANC,"MXF VANC");
+#endif
+#if CONFIG_CEA608_DECODER
+VBI_CLASS(cea608,options)
+VBI_DECODE(cea608,CEA_608,"CEA-608");
+#endif
+#if CONFIG_EIA708_DECODER
+VBI_CLASS(eia708,options)
+VBI_DECODE(eia708,EIA_708,"EIA-708");
+#endif
diff --git a/libavcodec/version.h b/libavcodec/version.h
index 7e51f3b..93320d7 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -29,7 +29,7 @@
 #include "libavutil/version.h"
 
 #define LIBAVCODEC_VERSION_MAJOR 56
-#define LIBAVCODEC_VERSION_MINOR  20
+#define LIBAVCODEC_VERSION_MINOR  21
 #define LIBAVCODEC_VERSION_MICRO 100
 
 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
diff --git a/libavdevice/lavfi.c b/libavdevice/lavfi.c
index 64db376..1c30be9 100644
--- a/libavdevice/lavfi.c
+++ b/libavdevice/lavfi.c
@@ -105,7 +105,7 @@ static int create_subcc_streams(AVFormatContext *avctx)
             lavfi->sink_stream_subcc_map[sink_idx] = avctx->nb_streams;
             if (!(st = avformat_new_stream(avctx, NULL)))
                 return AVERROR(ENOMEM);
-            st->codec->codec_id = AV_CODEC_ID_EIA_608;
+            st->codec->codec_id = AV_CODEC_ID_EIA_708;
             st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;
         } else {
             lavfi->sink_stream_subcc_map[sink_idx] = -1;
diff --git a/libavformat/isom.c b/libavformat/isom.c
index 01d3dd2..6cebd3e 100644
--- a/libavformat/isom.c
+++ b/libavformat/isom.c
@@ -315,7 +315,8 @@ const AVCodecTag ff_codec_movaudio_tags[] = {
 const AVCodecTag ff_codec_movsubtitle_tags[] = {
     { AV_CODEC_ID_MOV_TEXT, MKTAG('t', 'e', 'x', 't') },
     { AV_CODEC_ID_MOV_TEXT, MKTAG('t', 'x', '3', 'g') },
-    { AV_CODEC_ID_EIA_608,  MKTAG('c', '6', '0', '8') },
+    { AV_CODEC_ID_CEA_608,  MKTAG('c', '6', '0', '8') },
+    { AV_CODEC_ID_EIA_708,  MKTAG('c', '7', '0', '8') },
     { AV_CODEC_ID_NONE, 0 },
 };
 
diff --git a/libavformat/mpeg.c b/libavformat/mpeg.c
index f98d850..35c8118 100644
--- a/libavformat/mpeg.c
+++ b/libavformat/mpeg.c
@@ -574,6 +574,9 @@ redo:
         /* Used for both AC-3 and E-AC-3 in EVOB files */
         type     = AVMEDIA_TYPE_AUDIO;
         codec_id = AV_CODEC_ID_AC3;
+    } else if ((startcode&0xf0) == 0x10 || (startcode&0x9c) == 0x98 ) {
+        type = AVMEDIA_TYPE_SUBTITLE;
+        codec_id = AV_CODEC_ID_DVB_VBI;
     } else if (startcode >= 0x20 && startcode <= 0x3f) {
         type     = AVMEDIA_TYPE_SUBTITLE;
         codec_id = AV_CODEC_ID_DVD_SUBTITLE;
diff --git a/libavformat/mpeg.h b/libavformat/mpeg.h
index b43517c..40869ca 100644
--- a/libavformat/mpeg.h
+++ b/libavformat/mpeg.h
@@ -45,6 +45,7 @@
 #define DTS_ID   0x88
 #define LPCM_ID  0xa0
 #define SUB_ID   0x20
+#define VBI_ID   0x9c
 
 #define STREAM_TYPE_VIDEO_MPEG1     0x01
 #define STREAM_TYPE_VIDEO_MPEG2     0x02
diff --git a/libavformat/mpegenc.c b/libavformat/mpegenc.c
index 3ef2b24..9bba87e 100644
--- a/libavformat/mpegenc.c
+++ b/libavformat/mpegenc.c
@@ -297,7 +297,7 @@ static int get_system_header_size(AVFormatContext *ctx)
 static av_cold int mpeg_mux_init(AVFormatContext *ctx)
 {
     MpegMuxContext *s = ctx->priv_data;
-    int bitrate, i, mpa_id, mpv_id, h264_id, mps_id, ac3_id, dts_id, lpcm_id, j;
+    int bitrate, i, mpa_id, mpv_id, h264_id, mps_id, ac3_id, dts_id, lpcm_id, vbi_id, j;
     AVStream *st;
     StreamInfo *stream;
     int audio_bitrate;
@@ -336,6 +336,7 @@ static av_cold int mpeg_mux_init(AVFormatContext *ctx)
     h264_id = H264_ID;
     mps_id  = SUB_ID;
     lpcm_id = LPCM_ID;
+    vbi_id = VBI_ID;
 
     for (i = 0; i < ctx->nb_streams; i++) {
         st     = ctx->streams[i];
@@ -406,7 +407,9 @@ static av_cold int mpeg_mux_init(AVFormatContext *ctx)
             s->video_bound++;
             break;
         case AVMEDIA_TYPE_SUBTITLE:
-            stream->id              = mps_id++;
+            if (st->codec->codec_id == AV_CODEC_ID_DVB_VBI)
+                stream->id          = vbi_id++;
+            else stream->id         = mps_id++;
             stream->max_buffer_size = 16 * 1024;
             break;
         default:
@@ -862,7 +865,7 @@ static int flush_packet(AVFormatContext *ctx, int stream_index,
                 avio_w8(ctx->pb, stream->lpcm_header[0]);
                 avio_w8(ctx->pb, stream->lpcm_header[1]);
                 avio_w8(ctx->pb, stream->lpcm_header[2]);
-            } else if (id >= 0x40) {
+            } else if (id >= 0x40 && id <= 0x90) {
                 /* AC-3 */
                 avio_w8(ctx->pb, nb_frames);
                 avio_wb16(ctx->pb, trailer_size + 1);
diff --git a/libavformat/mpegts.c b/libavformat/mpegts.c
index 6fb186e..19833e3 100644
--- a/libavformat/mpegts.c
+++ b/libavformat/mpegts.c
@@ -124,8 +124,8 @@ struct MpegTSContext {
     /** compute exact PCR for each transport stream packet */
     int mpeg2ts_compute_pcr;
 
-    /** fix dvb teletext pts                                 */
-    int fix_teletext_pts;
+    /** fix dvb vbi pts */
+    int fix_vbi_pts;
 
     int64_t cur_pcr;    /**< used to estimate the exact PCR */
     int pcr_incr;       /**< used to estimate the exact PCR */
@@ -163,7 +163,7 @@ struct MpegTSContext {
 
 static const AVOption options[] = {
     MPEGTS_OPTIONS,
-    {"fix_teletext_pts", "Try to fix pts values of dvb teletext streams.", offsetof(MpegTSContext, fix_teletext_pts), AV_OPT_TYPE_INT,
+    {"fix_vbi_pts", "Try to fix pts values of dvb vbi streams.", offsetof(MpegTSContext, fix_vbi_pts), AV_OPT_TYPE_INT,
      {.i64 = 1}, 0, 1, AV_OPT_FLAG_DECODING_PARAM },
     {"ts_packetsize", "Output option carrying the raw packet size.", offsetof(MpegTSContext, raw_packet_size), AV_OPT_TYPE_INT,
      {.i64 = 0}, 0, 0, AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_EXPORT | AV_OPT_FLAG_READONLY },
@@ -728,7 +728,7 @@ static const StreamType DESC_types[] = {
     { 0x6a, AVMEDIA_TYPE_AUDIO,    AV_CODEC_ID_AC3          }, /* AC-3 descriptor */
     { 0x7a, AVMEDIA_TYPE_AUDIO,    AV_CODEC_ID_EAC3         }, /* E-AC-3 descriptor */
     { 0x7b, AVMEDIA_TYPE_AUDIO,    AV_CODEC_ID_DTS          },
-    { 0x56, AVMEDIA_TYPE_SUBTITLE, AV_CODEC_ID_DVB_TELETEXT },
+    { 0x56, AVMEDIA_TYPE_SUBTITLE, AV_CODEC_ID_DVB_VBI      }, /* VBI for Teletext */
     { 0x59, AVMEDIA_TYPE_SUBTITLE, AV_CODEC_ID_DVB_SUBTITLE }, /* subtitling descriptor */
     { 0 },
 };
@@ -1108,7 +1108,7 @@ skip:
                     p += 5;
                     buf_size -= 5;
                 }
-                if (pes->ts->fix_teletext_pts && pes->st->codec->codec_id == AV_CODEC_ID_DVB_TELETEXT) {
+                if (pes->ts->fix_vbi_pts && pes->st->codec->codec_id == AV_CODEC_ID_DVB_VBI) {
                     AVProgram *p = NULL;
                     while ((p = av_find_program_from_stream(pes->stream, p, pes->st->index))) {
                         if (p->pcr_pid != -1 && p->discard != AVDISCARD_ALL) {
@@ -1597,45 +1597,108 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type
             }
         }
         break;
-    case 0x56: /* DVB teletext descriptor */
+    case 0x45: /* DVB VBI descriptor for CEA-608 */
         {
-            uint8_t *extradata = NULL;
+            st->codec->codec_id   = AV_CODEC_ID_DVB_VBI;
+            int data_service;
+            while(data_service > -1)
+            {
+                data_service =  get8(pp, desc_end);
+                if(data_service == 6 || data_service == 1)
+                {
+                    if(get8(pp, desc_end) > 0)
+                    {
+                        st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;
+                        if(data_service == 6)
+                            st->disposition ^= AV_DISPOSITION_HEARING_IMPAIRED;
+                        break;
+                    }
+                }
+                else *pp += get8(pp, desc_end);
+            }
+        }
+        break;
+    case 0x56: /* DVB VBI teletext descriptor */
+        {
+            uint8_t txt_info;
+            char txt_type[252];
+            memset(txt_type,0,252);
+            char txt_page[252];
+            memset(txt_page,0,252);
             int language_count = desc_len / 5;
 
             if (desc_len > 0 && desc_len % 5 != 0)
                 return AVERROR_INVALIDDATA;
 
-            if (language_count > 0) {
+            if (language_count > 0)
+            {
                 /* 4 bytes per language code (3 bytes) with comma or NUL byte should fit language buffer */
                 if (language_count > sizeof(language) / 4) {
                     language_count = sizeof(language) / 4;
                 }
 
-                if (st->codec->extradata == NULL) {
-                    if (ff_alloc_extradata(st->codec, language_count * 2)) {
-                        return AVERROR(ENOMEM);
-                    }
-                }
-
-               if (st->codec->extradata_size < language_count * 2)
-                   return AVERROR_INVALIDDATA;
 
-               extradata = st->codec->extradata;
-
-                for (i = 0; i < language_count; i++) {
+                for (i = 0; i < language_count; i++)
+                {
                     language[i * 4 + 0] = get8(pp, desc_end);
                     language[i * 4 + 1] = get8(pp, desc_end);
                     language[i * 4 + 2] = get8(pp, desc_end);
                     language[i * 4 + 3] = ',';
 
-                    memcpy(extradata, *pp, 2);
-                    extradata += 2;
-
-                    *pp += 2;
+                    txt_info = get8(pp, desc_end);
+                    switch(txt_info&0xf8)
+                    {
+                    case 8:
+                        txt_type[i * 4 + 0] = 'i';
+                        txt_type[i * 4 + 1] = 'n';
+                        txt_type[i * 4 + 2] = 't';
+                        txt_type[i * 4 + 3] = ',';
+                        break;
+                    case 16:
+                        txt_type[i * 4 + 0] = 's';
+                        txt_type[i * 4 + 1] = 'u';
+                        txt_type[i * 4 + 2] = 'b';
+                        txt_type[i * 4 + 3] = ',';
+                        break;
+                    case 24:
+                        txt_type[i * 4 + 0] = 'a';
+                        txt_type[i * 4 + 1] = 'd';
+                        txt_type[i * 4 + 2] = 'd';
+                        txt_type[i * 4 + 3] = ',';
+                        break;
+                    case 32:
+                        txt_type[i * 4 + 0] = 'p';
+                        txt_type[i * 4 + 1] = 'r';
+                        txt_type[i * 4 + 2] = 'g';
+                        txt_type[i * 4 + 3] = ',';
+                        break;
+                    case 40:
+                        txt_type[i * 4 + 0] = 'c';
+                        txt_type[i * 4 + 1] = 'a';
+                        txt_type[i * 4 + 2] = 'p';
+                        txt_type[i * 4 + 3] = ',';
+                        if (language_count == 1)
+                            st->disposition ^= AV_DISPOSITION_HEARING_IMPAIRED;
+                        break;
+                    default:
+                        txt_type[i * 4 + 0] = 'u';
+                        txt_type[i * 4 + 1] = 'n';
+                        txt_type[i * 4 + 2] = 'k';
+                        txt_type[i * 4 + 3] = ',';
+                    }
+                    if(!(txt_info&7))
+                        txt_page[i * 4 + 0] = '8';
+                    else txt_page[i * 4 + 0] = (txt_info&7)+48;
+                    txt_info = get8(pp, desc_end);
+                    txt_page[i * 4 + 1] = (txt_info&0xf0)/16+48;
+                    txt_page[i * 4 + 2] = (txt_info&15)+48;
+                    txt_page[i * 4 + 3] = ',';
                 }
 
-                language[i * 4 - 1] = 0;
+                language[i * 4 - 1] = txt_type[i * 4 - 1] = txt_page[i * 4 - 1] = 0;
                 av_dict_set(&st->metadata, "language", language, 0);
+                av_dict_set(&st->metadata, "teletext_type", txt_type, 0);
+                av_dict_set(&st->metadata, "teletext_page", txt_page, 0);
             }
         }
         break;
@@ -2130,7 +2193,25 @@ static int handle_packet(MpegTSContext *ts, const uint8_t *packet)
 
     afc = (packet[3] >> 4) & 3;
     if (afc == 0) /* reserved value */
+    {
+        if(packet[4] == 'F' && packet[5] == 'T' &&
+           packet[6] == 'R' && packet[7] == 'I' &&
+           (packet[50] > 0 || packet[51] > 0)) // Fortis DVR recording chunk
+        {
+            int prgid=AV_RL16(packet + 16);
+            if(!prgid) prgid=1;
+            clear_programs(ts);
+            AVProgram *program = av_new_program(ts->stream, prgid);
+            program->program_num = prgid;
+            add_pat_entry(ts, prgid);
+            add_pid_to_pmt(ts, prgid, pid); //add fortis pid to program
+            pid=AV_RL16(packet + 50);
+            av_log(ts->stream, AV_LOG_DEBUG, "Fortis PMT ID found: %u @ %u\n", prgid, pid);
+            mpegts_open_section_filter(ts, pid, pmt_cb, ts, 1);
+            add_pid_to_pmt(ts, prgid, pid);
+        }
         return 0;
+    }
     has_adaptation   = afc & 2;
     has_payload      = afc & 1;
     is_discontinuity = has_adaptation &&
diff --git a/libavformat/mpegtsenc.c b/libavformat/mpegtsenc.c
index 8d0da0b..ad7a9de 100644
--- a/libavformat/mpegtsenc.c
+++ b/libavformat/mpegtsenc.c
@@ -364,8 +364,15 @@ static int mpegts_write_pmt(AVFormatContext *s, MpegTSService *service)
             break;
         case AVMEDIA_TYPE_SUBTITLE:
         {
-           const char default_language[] = "und";
+            AVDictionaryEntry *txt_type = av_dict_get(st->metadata, "teletext_type", NULL,0);
+            AVDictionaryEntry *txt_page = av_dict_get(st->metadata, "teletext_page", NULL,0);
+            const char default_language[] = "und";
+            const char default_txt_type[] = "int,sub";
+            const char default_txt_page[] = "100,801";
            const char *language = lang && strlen(lang->value) >= 3 ? lang->value : default_language;
+            const char *ttype;
+            const char *tpage;
+
 
            if (st->codec->codec_id == AV_CODEC_ID_DVB_SUBTITLE) {
                uint8_t *len_ptr;
@@ -409,37 +416,78 @@ static int mpegts_write_pmt(AVFormatContext *s, MpegTSService *service)
                }
 
                *len_ptr = q - len_ptr - 1;
-           } else if (st->codec->codec_id == AV_CODEC_ID_DVB_TELETEXT) {
-               uint8_t *len_ptr = NULL;
-               int extradata_copied = 0;
+            } else if (st->codec->codec_id == AV_CODEC_ID_DVB_VBI) {
+               if(!txt_page)
+               {
+                   /* The descriptor tag. vbi_descriptor for CEA-608 */
+                   *q++ = 0x45;
+                   *q++ = 4;
+                   *q++ = 6;
+                   *q++ = 2;
+                   *q++ = 0xf5;
+                   *q++ = 0xd5;
+               }
+               else
+               {
 
-               /* The descriptor tag. teletext_descriptor */
-               *q++ = 0x56;
-               len_ptr = q++;
+                   uint8_t *len_ptr = NULL;
 
-               while (strlen(language) >= 3 && q - data < sizeof(data) - 6) {
-                   *q++ = *language++;
-                   *q++ = *language++;
-                   *q++ = *language++;
-                   /* Skip comma */
-                   if (*language != '\0')
-                       language++;
+                   ttype = txt_type && strlen(txt_type->value) >= 3 ? txt_type->value : default_txt_type;
+                   tpage = strlen(txt_page->value) >= 3 ? txt_page->value : default_txt_page;
 
-                   if (st->codec->extradata_size - 1 > extradata_copied) {
-                       memcpy(q, st->codec->extradata + extradata_copied, 2);
-                       extradata_copied += 2;
-                       q += 2;
-                   } else {
-                       /* The Teletext descriptor:
-                        * teletext_type: This 5-bit field indicates the type of Teletext page indicated. (0x01 Initial Teletext page)
-                        * teletext_magazine_number: This is a 3-bit field which identifies the magazine number.
-                        * teletext_page_number: This is an 8-bit field giving two 4-bit hex digits identifying the page number. */
-                       *q++ = 0x08;
-                       *q++ = 0x00;
-                   }
-               }
+                   /* The descriptor tag. teletext_descriptor */
+                   *q++ = 0x56;
+                   len_ptr = q++;
+
+                   while (strlen(tpage) >= 3 && q - data < sizeof(data) - 6) {
+                       if (sizeof(data) - (q - data) < 5) { /* 5 bytes per DVB teletext substream data */
+                           err = 1;
+                           break;
+                       }
+                       if(!*language == '\0')
+                           language = default_language; // when not in sync with pages
+                       *q++ = *language++;
+                       *q++ = *language++;
+                       *q++ = *language++;
+                       /* Skip comma */
+                       if (*language != '\0')
+                           language++;
+
+                       if(*ttype == '\0')
+                           ttype = default_txt_type; // when not in sync with pages
+                       switch(*ttype)
+                       {
+                       case 'i':
+                           *q++ = 0x08 | (*tpage == '8' ? 0 : (*tpage&15));
+                           break;
+                       case 's':
+                           *q++ = 0x10 | (*tpage == '8' ? 0 : (*tpage&15));
+                           break;
+                       case 'p':
+                           *q++ = 0x20 | (*tpage == '8' ? 0 : (*tpage&15));
+                           break;
+                       case 'c':
+                           *q++ = 0x28 | (*tpage == '8' ? 0 : (*tpage&15));
+                           break;
+                       default: // any thing else to additional page
+                           *q++ = 0x18 | (*tpage == '8' ? 0 : (*tpage&15));
+                           break;
+                       }
+
+                       ttype+=3;
+                       if (*ttype != '\0')
+                           ttype++;
+
+                       tpage++;
+                       *q++ = ((*tpage&15) << 4) | (*(tpage+1)&15);
+                       tpage+=2;
+
+                       if (*tpage != '\0')
+                           tpage++;
+                    }
+                    *len_ptr = q - len_ptr - 1;
+                }
 
-               *len_ptr = q - len_ptr - 1;
             }
         }
         break;
@@ -953,7 +1001,7 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st,
     MpegTSWrite *ts = s->priv_data;
     uint8_t buf[TS_PACKET_SIZE];
     uint8_t *q;
-    int val, is_start, len, header_len, write_pcr, is_dvb_subtitle, is_dvb_teletext, flags;
+    int val, is_start, len, header_len, write_pcr, is_dvb_subtitle, is_dvb_vbi, flags;
     int afc_len, stuffing_len;
     int64_t pcr = -1; /* avoid warning */
     int64_t delay = av_rescale(s->max_delay, 90000, AV_TIME_BASE);
@@ -1024,7 +1072,7 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st,
             *q++ = 0x00;
             *q++ = 0x01;
             is_dvb_subtitle = 0;
-            is_dvb_teletext = 0;
+            is_dvb_vbi = 0;
             if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
                 if (st->codec->codec_id == AV_CODEC_ID_DIRAC)
                     *q++ = 0xfd;
@@ -1044,8 +1092,8 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st,
                 if (st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE) {
                     if (st->codec->codec_id == AV_CODEC_ID_DVB_SUBTITLE) {
                         is_dvb_subtitle = 1;
-                    } else if (st->codec->codec_id == AV_CODEC_ID_DVB_TELETEXT) {
-                        is_dvb_teletext = 1;
+                    } else if (st->codec->codec_id == AV_CODEC_ID_DVB_VBI) {
+                        is_dvb_vbi = 1;
                     }
                 }
             }
@@ -1081,9 +1129,10 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st,
                         flags |= 0x01;
                         header_len += 3;
             }
-            if (is_dvb_teletext) {
+            if (is_dvb_vbi) {
                 pes_header_stuffing_bytes = 0x24 - header_len;
                 header_len = 0x24;
+                payload_size++;
             }
             len = payload_size + header_len + 3;
             /* 3 extra bytes should be added to DVB subtitle payload: 0x20 0x00 at the beginning and trailing 0xff */
@@ -1139,9 +1188,11 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st,
                 *q++ = 0x20;
                 *q++ = 0x00;
             }
-            if (is_dvb_teletext) {
+            if (is_dvb_vbi) {
                 memset(q, 0xff, pes_header_stuffing_bytes);
                 q += pes_header_stuffing_bytes;
+                if(payload[1] == 44) *q++ = 0x10;
+                else *q++ = 0xc9;
             }
             is_start = 0;
         }
diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c
index b892bc2..7ca7942 100644
--- a/libavformat/mxfdec.c
+++ b/libavformat/mxfdec.c
@@ -1093,7 +1093,8 @@ static const MXFCodecUL mxf_sound_essence_container_uls[] = {
 };
 
 static const MXFCodecUL mxf_data_essence_container_uls[] = {
-    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x09,0x0d,0x01,0x03,0x01,0x02,0x0e,0x00,0x00 }, 16, 0 },
+    { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x09,0x0D,0x01,0x03,0x01,0x02,0x0D,0x00,0x00 }, 14,   AV_CODEC_ID_NONE },    /* Generic VBI Data */
+    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x09,0x0d,0x01,0x03,0x01,0x02,0x0e,0x00,0x00 }, 16, 0, AV_CODEC_ID_MXF_VANC }, /* Generic ANC Data */
     { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },  0, AV_CODEC_ID_NONE },
 };
 
@@ -2003,7 +2004,10 @@ static int mxf_parse_structural_metadata(MXFContext *mxf)
                                             essence_container_ul)->id;
             if (codec_id >= 0 &&
                 codec_id < FF_ARRAY_ELEMS(mxf_data_essence_descriptor)) {
-                av_dict_set(&st->metadata, "data_type",
+                st->codec->codec_id = (enum AVCodecID)codec_id;
+                if(st->codec->codec_id == AV_CODEC_ID_MXF_VANC)
+                    st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;
+                else av_dict_set(&st->metadata, "data_type",
                             mxf_data_essence_descriptor[codec_id], 0);
             }
         }
diff --git a/libavformat/nut.c b/libavformat/nut.c
index 86a0301..e762d25 100644
--- a/libavformat/nut.c
+++ b/libavformat/nut.c
@@ -30,7 +30,7 @@ const AVCodecTag ff_nut_subtitle_tags[] = {
     { AV_CODEC_ID_SSA,              MKTAG('S', 'S', 'A',  0 ) },
     { AV_CODEC_ID_DVD_SUBTITLE,     MKTAG('D', 'V', 'D', 'S') },
     { AV_CODEC_ID_DVB_SUBTITLE,     MKTAG('D', 'V', 'B', 'S') },
-    { AV_CODEC_ID_DVB_TELETEXT,     MKTAG('D', 'V', 'B', 'T') },
+    { AV_CODEC_ID_DVB_VBI,          MKTAG('D', 'V', 'B', 'T') },
     { AV_CODEC_ID_NONE,             0                         }
 };
 
diff --git a/libavformat/wtvdec.c b/libavformat/wtvdec.c
index f200300..079d620 100644
--- a/libavformat/wtvdec.c
+++ b/libavformat/wtvdec.c
@@ -735,7 +735,7 @@ static AVStream * parse_media_type(AVFormatContext *s, AVStream *st, int sid,
         if (ff_guidcmp(formattype, ff_format_none))
             av_log(s, AV_LOG_WARNING, "unknown formattype:"FF_PRI_GUID"\n", FF_ARG_GUID(formattype));
         avio_skip(pb, size);
-        st->codec->codec_id = !ff_guidcmp(subtype, mediasubtype_teletext) ? AV_CODEC_ID_DVB_TELETEXT : AV_CODEC_ID_EIA_608;
+        st->codec->codec_id = !ff_guidcmp(subtype, mediasubtype_teletext) ? AV_CODEC_ID_DVB_VBI : AV_CODEC_ID_EIA_708;
         return st;
     } else if (!ff_guidcmp(mediatype, mediatype_mpeg2_sections) &&
                !ff_guidcmp(subtype, mediasubtype_mpeg2_sections)) {
-- 
1.7.9.5



More information about the ffmpeg-devel mailing list