[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