From 8eb3525172b894b28191f465fbb4afa5d54931fc Mon Sep 17 00:00:00 2001 From: Laurent Aimar Date: Fri, 27 Aug 2010 21:05:12 +0200 Subject: [PATCH] Added STL demuxer/decoder. It is a preliminary work. --- modules/codec/Modules.am | 2 + modules/codec/stl.c | 175 ++++++++++++++++++++++++++++++++++ modules/demux/Modules.am | 2 + modules/demux/stl.c | 242 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 421 insertions(+) create mode 100644 modules/codec/stl.c create mode 100644 modules/demux/stl.c diff --git a/modules/codec/Modules.am b/modules/codec/Modules.am index 8dff8b794f..5e35edab5e 100644 --- a/modules/codec/Modules.am +++ b/modules/codec/Modules.am @@ -37,6 +37,7 @@ SOURCES_subsdec = subsdec.c substext.h SOURCES_subsusf = subsusf.c SOURCES_t140 = t140.c SOURCES_crystalhd = crystalhd.c +SOURCES_stl = stl.c libvlc_LTLIBRARIES += \ liba52_plugin.la \ @@ -55,4 +56,5 @@ libvlc_LTLIBRARIES += \ libsubsdec_plugin.la \ libsubsusf_plugin.la \ libt140_plugin.la \ + libstl_plugin.la \ $(NULL) diff --git a/modules/codec/stl.c b/modules/codec/stl.c new file mode 100644 index 0000000000..51138546ed --- /dev/null +++ b/modules/codec/stl.c @@ -0,0 +1,175 @@ +/***************************************************************************** + * stl.c: EBU STL decoder + ***************************************************************************** + * Copyright (C) 2010 Laurent Aimar + * $Id$ + * + * Authors: Laurent Aimar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include + +#include +#include +#include +#include + +/***************************************************************************** + * Module descriptor + *****************************************************************************/ +static int Open (vlc_object_t *); +static void Close(vlc_object_t *); + +vlc_module_begin() + set_description(N_("EBU STL subtitles decoder")) + set_category(CAT_INPUT) + set_subcategory(SUBCAT_INPUT_SCODEC) + set_capability("decoder", 10) + set_callbacks(Open, Close) +vlc_module_end() + +/***************************************************************************** + * Local definitions/prototypes + *****************************************************************************/ +struct decoder_sys_t { + int dummy; +}; + +static char *ParseText(uint8_t *data, int size) +{ + char *text = strdup(""); + int text_size = 0; + + for (int i = 0; i < size; i++) { + uint8_t code = data[i]; + + if (code == 0x8f) + break; + + char tmp[16] = ""; + char *t = tmp; + if (code >= 0x20 && code <= 0x7f) + snprintf(tmp, sizeof(tmp), "%c", code); +#if 0 + else if (code == 0x80) + snprintf(tmp, sizeof(tmp), ""); + else if (code == 0x81) + snprintf(tmp, sizeof(tmp), ""); + else if (code == 0x82) + snprintf(tmp, sizeof(tmp), ""); + else if (code == 0x83) + snprintf(tmp, sizeof(tmp), ""); +#endif + else if (code == 0x8a) + snprintf(tmp, sizeof(tmp), "\n"); + else { + fprintf(stderr, "--> %2.2x\n", code); + t = NULL; + } + + if (!t) + continue; + size_t t_size = strlen(t); + text = realloc_or_free(text, t_size + text_size + 1); + if (!text) + continue; + memcpy(&text[text_size], t, t_size); + text_size += t_size; + text[text_size] = '\0'; + } + return text; +} + +static subpicture_t *Decode(decoder_t *dec, block_t **block) +{ + if (block == NULL || *block == NULL) + return NULL; + + subpicture_t *sub = NULL; + + block_t *b = *block; *block = NULL; + if (b->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED)) + goto exit; + if (b->i_buffer < 128) + goto exit; + + int payload_size = (b->i_buffer / 128) * 112; + uint8_t *payload = malloc(payload_size); + if (!payload) + goto exit; + for (int i = 0; i < b->i_buffer / 128; i++) + memcpy(&payload[112 * i], &b->p_buffer[128 * i + 16], 112); + + sub = decoder_NewSubpicture(dec, NULL); + if (!sub) { + free(payload); + goto exit; + } + sub->i_start = b->i_pts; + sub->i_stop = b->i_pts + b->i_length; + sub->b_ephemer = b->i_length == 0; + sub->b_absolute = false; + //sub->i_original_picture_width = 0; + //sub->i_original_picture_height = 0; + + video_format_t fmt; + video_format_Init(&fmt, VLC_CODEC_TEXT); + sub->p_region = subpicture_region_New(&fmt); + video_format_Clean(&fmt); + + if (sub->p_region) { + sub->p_region->psz_text = ParseText(payload, payload_size); + sub->p_region->psz_html = NULL; + } + + free(payload); + +exit: + block_Release(b); + return sub; +} + +static int Open(vlc_object_t *object) +{ + decoder_t *dec = (decoder_t*)object; + + if (dec->fmt_in.i_codec != VLC_CODEC_EBU_STL) + return VLC_EGENERIC; + + decoder_sys_t *sys = malloc(sizeof(*sys)); + + dec->p_sys = sys; + dec->pf_decode_sub = Decode; + dec->fmt_out.i_cat = SPU_ES; + dec->fmt_out.i_codec = 0; + return VLC_SUCCESS; +} + +static void Close(vlc_object_t *object) +{ + decoder_t *dec = (decoder_t*)object; + decoder_sys_t *sys = dec->p_sys; + + free(sys); +} + diff --git a/modules/demux/Modules.am b/modules/demux/Modules.am index c17947c300..eae4be95ab 100644 --- a/modules/demux/Modules.am +++ b/modules/demux/Modules.am @@ -32,6 +32,7 @@ SOURCES_gme = gme.c dummy.cpp SOURCES_sid = sid.cpp SOURCES_dirac = dirac.c SOURCES_image = image.c +SOURCES_demux_stl = stl.c libvlc_LTLIBRARIES += \ libaiff_plugin.la \ @@ -60,6 +61,7 @@ libvlc_LTLIBRARIES += \ libwav_plugin.la \ libxa_plugin.la \ libimage_plugin.la \ + libdemux_stl_plugin.la \ $(NULL) libts_plugin_la_SOURCES = ts.c ../mux/mpeg/csa.c dvb-text.h diff --git a/modules/demux/stl.c b/modules/demux/stl.c new file mode 100644 index 0000000000..3b6c7ddadf --- /dev/null +++ b/modules/demux/stl.c @@ -0,0 +1,242 @@ +/***************************************************************************** + * stl.c: EBU STL demuxer + ***************************************************************************** + * Copyright (C) 2010 Laurent Aimar + * $Id$ + * + * Authors: Laurent Aimar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include + +#include +#include +#include + +/***************************************************************************** + * Module descriptor + *****************************************************************************/ +static int Open (vlc_object_t *); +static void Close(vlc_object_t *); + +vlc_module_begin() + set_description(N_("EBU STL subtitles parser")) + set_category(CAT_INPUT) + set_subcategory(SUBCAT_INPUT_DEMUX) + set_capability("demux", 1) + set_callbacks(Open, Close) + add_shortcut("stl", "subtitle") +vlc_module_end() + +/***************************************************************************** + * Local definitions/prototypes + *****************************************************************************/ +typedef struct { + mtime_t start; + mtime_t stop; + int index; + int count; +} stl_entry_t; + +struct demux_sys_t { + int count; + stl_entry_t *index; + + es_out_id_t *es; + + int current; + int64_t next_date; +}; + +static int ParseInteger(uint8_t *data, int size) +{ + char tmp[16]; + assert(size < sizeof(tmp)); + memcpy(tmp, data, size); + tmp[size] = '\0'; + + return strtol(tmp, NULL, 10); +} +static int64_t ParseTimeCode(uint8_t *data, double fps) +{ + return INT64_C(1000000) * (data[0] * 3600 + + data[1] * 60 + + data[2] * 1 + + data[3] / fps); +} +static int64_t ParseTextTimeCode(uint8_t *data, double fps) +{ + uint8_t tmp[4]; + for (int i = 0; i < 4; i++) + tmp[i] = ParseInteger(&data[2 * i], 2); + return ParseTimeCode(tmp, fps); +} + +static int Control(demux_t *demux, int query, va_list args) +{ + demux_sys_t *sys = demux->p_sys; + switch(query) { + case DEMUX_GET_LENGTH: { + int64_t *l = va_arg(args, int64_t *); + *l = sys->count > 0 ? sys->index[sys->count-1].stop : 0; + return VLC_SUCCESS; + } + case DEMUX_GET_TIME: { + int64_t *t = va_arg(args, int64_t *); + *t = sys->current < sys->count ? sys->index[sys->count-1].start : 0; + return VLC_SUCCESS; + } + case DEMUX_SET_NEXT_DEMUX_TIME: { + sys->next_date = va_arg(args, int64_t); + return VLC_SUCCESS; + } + case DEMUX_SET_TIME: { + int64_t t = va_arg(args, int64_t); + sys->current = 0; + while (sys->current < sys->count) { + if (sys->index[sys->current].stop > t) { + stream_Seek(demux->s, 1024 + 128LL * sys->index[sys->current].index); + break; + } + sys->current++; + } + return VLC_SUCCESS; + } + case DEMUX_SET_POSITION: + case DEMUX_GET_POSITION: + default: + return VLC_EGENERIC; + } +} + +static int Demux(demux_t *demux) +{ + demux_sys_t *sys = demux->p_sys; + + while(sys->current < sys->count) { + stl_entry_t *s = &sys->index[sys->current]; + if (s->start > sys->next_date) + break; + + block_t *b = stream_Block(demux->s, 128 * s->count); + if (b) { + b->i_dts = + b->i_pts = VLC_TS_0 + s->start; + if (s->stop > s->start) + b->i_length = s->stop - s->start; + es_out_Send(demux->out, sys->es, b); + } + sys->current++; + } + return sys->current < sys->count ? 1 : 0; +} + +static int Open(vlc_object_t *object) +{ + demux_t *demux = (demux_t*)object; + + const uint8_t *peek; + if (stream_Peek(demux->s, &peek, 11) != 11) + return VLC_EGENERIC; + + bool is_stl_25 = !memcmp(&peek[3], "STL25.01", 8); + bool is_stl_30 = !memcmp(&peek[3], "STL30.01", 8); + if (!is_stl_25 && !is_stl_30) + return VLC_EGENERIC; + const double fps = is_stl_25 ? 25 : 30; + + uint8_t header[1024]; + if (stream_Read(demux->s, header, sizeof(header)) != sizeof(header)) { + msg_Err(demux, "Incomplete EBU STL header"); + return VLC_EGENERIC; + } + const int cct = ParseInteger(&header[12], 2); + const mtime_t program_start = ParseTextTimeCode(&header[256], fps); + const int tti_count = ParseInteger(&header[238], 5); + msg_Err(demux, "Detected EBU STL : CCT=%d TTI=%d start=%8.8s %lld", cct, tti_count, &header[256], program_start); + + demux_sys_t *sys = malloc(sizeof(*sys)); + sys->next_date = 0; + sys->current = 0; + sys->count = 0; + sys->index = calloc(tti_count, sizeof(*sys->index)); + + + bool comment = false; + stl_entry_t *s = &sys->index[0]; + s->count = 0; + + for (int i = 0; i < tti_count; i++) { + uint8_t tti[16]; + if (stream_Read(demux->s, tti, 16) != 16 || + stream_Read(demux->s, NULL, 112) != 112) { + msg_Warn(demux, "Incomplete EBU STL file"); + break; + } + const int ebn = tti[3]; + if (ebn >= 0xf0 && ebn <= 0xfd) + continue; + if (ebn == 0xfe) + continue; + + if (s->count <= 0) { + comment = tti[15] != 0; + s->start = ParseTimeCode(&tti[5], fps) - program_start; + s->stop = ParseTimeCode(&tti[9], fps) - program_start; + s->index = i; + } + s->count++; + if (ebn == 0xff && !comment) + s = &sys->index[++sys->count]; + if (ebn == 0xff && sys->count < tti_count) + s->count = 0; + } + if (sys->count > 0) + stream_Seek(demux->s, 1024 + 128LL * sys->index[0].index); + + es_format_t fmt; + es_format_Init(&fmt, SPU_ES, VLC_CODEC_EBU_STL); + fmt.i_extra = sizeof(header); + fmt.p_extra = header; + + sys->es = es_out_Add(demux->out, &fmt); + + fmt.i_extra = NULL; + fmt.p_extra = NULL; + es_format_Clean(&fmt); + + demux->p_sys = sys; + demux->pf_demux = Demux; + demux->pf_control = Control; + return VLC_SUCCESS; +} + +static void Close(vlc_object_t *object) +{ + demux_t *demux = (demux_t*)object; + demux_sys_t *sys = demux->p_sys; + + free(sys->index); + free(sys); +} + -- 2.11.4.GIT