Commit 8eb35251 authored by Laurent Aimar's avatar Laurent Aimar

Added STL demuxer/decoder.

It is a preliminary work.
parent c20bd366
...@@ -37,6 +37,7 @@ SOURCES_subsdec = subsdec.c substext.h ...@@ -37,6 +37,7 @@ SOURCES_subsdec = subsdec.c substext.h
SOURCES_subsusf = subsusf.c SOURCES_subsusf = subsusf.c
SOURCES_t140 = t140.c SOURCES_t140 = t140.c
SOURCES_crystalhd = crystalhd.c SOURCES_crystalhd = crystalhd.c
SOURCES_stl = stl.c
libvlc_LTLIBRARIES += \ libvlc_LTLIBRARIES += \
liba52_plugin.la \ liba52_plugin.la \
...@@ -55,4 +56,5 @@ libvlc_LTLIBRARIES += \ ...@@ -55,4 +56,5 @@ libvlc_LTLIBRARIES += \
libsubsdec_plugin.la \ libsubsdec_plugin.la \
libsubsusf_plugin.la \ libsubsusf_plugin.la \
libt140_plugin.la \ libt140_plugin.la \
libstl_plugin.la \
$(NULL) $(NULL)
/*****************************************************************************
* stl.c: EBU STL decoder
*****************************************************************************
* Copyright (C) 2010 Laurent Aimar
* $Id$
*
* Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
*
* 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 <assert.h>
#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_codec.h>
#include <vlc_memory.h>
/*****************************************************************************
* 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), "<i>");
else if (code == 0x81)
snprintf(tmp, sizeof(tmp), "</i>");
else if (code == 0x82)
snprintf(tmp, sizeof(tmp), "<u>");
else if (code == 0x83)
snprintf(tmp, sizeof(tmp), "</u>");
#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);
}
...@@ -32,6 +32,7 @@ SOURCES_gme = gme.c dummy.cpp ...@@ -32,6 +32,7 @@ SOURCES_gme = gme.c dummy.cpp
SOURCES_sid = sid.cpp SOURCES_sid = sid.cpp
SOURCES_dirac = dirac.c SOURCES_dirac = dirac.c
SOURCES_image = image.c SOURCES_image = image.c
SOURCES_demux_stl = stl.c
libvlc_LTLIBRARIES += \ libvlc_LTLIBRARIES += \
libaiff_plugin.la \ libaiff_plugin.la \
...@@ -60,6 +61,7 @@ libvlc_LTLIBRARIES += \ ...@@ -60,6 +61,7 @@ libvlc_LTLIBRARIES += \
libwav_plugin.la \ libwav_plugin.la \
libxa_plugin.la \ libxa_plugin.la \
libimage_plugin.la \ libimage_plugin.la \
libdemux_stl_plugin.la \
$(NULL) $(NULL)
libts_plugin_la_SOURCES = ts.c ../mux/mpeg/csa.c dvb-text.h libts_plugin_la_SOURCES = ts.c ../mux/mpeg/csa.c dvb-text.h
......
/*****************************************************************************
* stl.c: EBU STL demuxer
*****************************************************************************
* Copyright (C) 2010 Laurent Aimar
* $Id$
*
* Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
*
* 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 <assert.h>
#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_demux.h>
/*****************************************************************************
* 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);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment