* sub.c
*****************************************************************************
* Copyright (C) 1999-2003 VideoLAN
- * $Id: sub.c,v 1.28 2003/10/11 22:40:04 hartman Exp $
+ * $Id: sub.c,v 1.35 2003/11/05 00:39:16 gbazin Exp $
*
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
*
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
+#include <ctype.h>
#include <vlc/vlc.h>
#include <vlc/input.h>
#include "vlc_video.h"
+#include <codecs.h>
#include "sub.h"
static void sub_fix( subtitle_demux_t *p_sub );
-static char *ppsz_sub_type[] = { "microdvd", "subrip", "ssa1", "ssa2-4", "vplayer", "sami", NULL };
-
+static char *ppsz_sub_type[] = { "auto", "microdvd", "subrip", "ssa1",
+ "ssa2-4", "vplayer", "sami", "vobsub" };
/*****************************************************************************
* Module descriptor
"It will only work with MicroDVD subtitles."
#define SUB_TYPE_LONGTEXT \
"One from \"microdvd\", \"subrip\", \"ssa1\", \"ssa2-4\", \"vplayer\" " \
- "\"sami\" (nothing for autodetection, it should always work)."
+ "\"sami\" (auto for autodetection, it should always work)."
vlc_module_begin();
set_description( _("Text subtitles demux") );
add_integer( "sub-delay", 0, NULL,
"Delay subtitles (in 1/10s)",
SUB_DELAY_LONGTEXT, VLC_TRUE );
- add_string_from_list( "sub-type", NULL, ppsz_sub_type, NULL,
- "subtitles type",
- SUB_TYPE_LONGTEXT, VLC_TRUE );
+ add_string( "sub-type", "auto", NULL, "subtitles type",
+ SUB_TYPE_LONGTEXT, VLC_TRUE );
+ change_string_list( ppsz_sub_type, 0, 0 );
set_callbacks( Open, NULL );
vlc_module_end();
txt->i_line = 0;
}
-static int sub_MicroDvdRead( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe );
-static int sub_SubRipRead ( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe );
-static int sub_SSA1Read ( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe );
-static int sub_SSA2_4Read ( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe );
-static int sub_Vplayer ( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe );
-static int sub_Sami ( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe );
+static int sub_MicroDvdRead( subtitle_demux_t *p_sub, text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe );
+static int sub_SubRipRead ( subtitle_demux_t *p_sub, text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe );
+static int sub_SSARead ( subtitle_demux_t *p_sub, text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe );
+static int sub_Vplayer ( subtitle_demux_t *p_sub, text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe );
+static int sub_Sami ( subtitle_demux_t *p_sub, text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe );
+static int sub_VobSub ( subtitle_demux_t *p_sub, text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe );
static struct
{
char *psz_type_name;
int i_type;
char *psz_name;
- int (*pf_read_subtitle) ( text_t *, subtitle_t*, mtime_t );
+ int (*pf_read_subtitle) ( subtitle_demux_t *, text_t *, subtitle_t*, mtime_t );
} sub_read_subtitle_function [] =
{
{ "microdvd", SUB_TYPE_MICRODVD, "MicroDVD", sub_MicroDvdRead },
{ "subrip", SUB_TYPE_SUBRIP, "SubRIP", sub_SubRipRead },
- { "ssa1", SUB_TYPE_SSA1, "SSA-1", sub_SSA1Read },
- { "ssa2-4", SUB_TYPE_SSA2_4, "SSA-2/3/4",sub_SSA2_4Read },
+ { "ssa1", SUB_TYPE_SSA1, "SSA-1", sub_SSARead },
+ { "ssa2-4", SUB_TYPE_SSA2_4, "SSA-2/3/4",sub_SSARead },
{ "vplayer", SUB_TYPE_VPLAYER, "VPlayer", sub_Vplayer },
{ "sami", SUB_TYPE_SAMI, "SAMI", sub_Sami },
- { NULL, SUB_TYPE_UNKNOWN, "Unknow", NULL }
+ { "vobsub", SUB_TYPE_VOBSUB, "VobSub", sub_VobSub },
+ { NULL, SUB_TYPE_UNKNOWN, "Unknown", NULL }
};
+static char * local_stristr( char *psz_big, char *psz_little)
+{
+ char *p_pos = psz_big;
+
+ if (!psz_big || !psz_little || !*psz_little) return psz_big;
+
+ while (*p_pos)
+ {
+ if (toupper(*p_pos) == toupper(*psz_little))
+ {
+ char * psz_cur1 = p_pos + 1;
+ char * psz_cur2 = psz_little + 1;
+ while (*psz_cur1 && *psz_cur2 && toupper(*psz_cur1) == toupper(*psz_cur2))
+ {
+ psz_cur1++;
+ psz_cur2++;
+ }
+ if (!*psz_cur2) return p_pos;
+ }
+ p_pos++;
+ }
+ return NULL;
+}
+
/*****************************************************************************
* sub_open: Open a subtitle file and add subtitle ES
*****************************************************************************/
int i;
int i_sub_type;
int i_max;
- int (*pf_read_subtitle)( text_t *, subtitle_t *, mtime_t ) = NULL;
+ int (*pf_read_subtitle)( subtitle_demux_t *, text_t *, subtitle_t *, mtime_t ) = NULL;
p_sub->i_sub_type = SUB_TYPE_UNKNOWN;
p_sub->p_es = NULL;
if( psz_name ) free( psz_name );
return VLC_EGENERIC;
}
-
+
/* *** load the file *** */
if( text_load( &txt, psz_name ) )
{
break;
}
- if( strstr( s, "<SAMI>" ) )
+ if( local_stristr( s, "<SAMI>" ) )
{
i_sub_type = SUB_TYPE_SAMI;
break;
}
else
{
- i_sub_type = SUB_TYPE_SSA2_4; // I hop this will work
+ i_sub_type = SUB_TYPE_SSA2_4; /* I hope this will work */
}
break;
}
- else if( strstr( s, "This is a Sub Station Alpha v4 script" ) )
+ else if( local_stristr( s, "This is a Sub Station Alpha v4 script" ) )
{
- i_sub_type = SUB_TYPE_SSA2_4; // I hop this will work
+ i_sub_type = SUB_TYPE_SSA2_4; /* I hope this will work */
+ break;
}
- else if( !strncmp( s, "Dialogue: Marked", 16 ) )
+ else if( !strncasecmp( s, "Dialogue: Marked", 16 ) )
{
- i_sub_type = SUB_TYPE_SSA2_4; // could be wrong
+ i_sub_type = SUB_TYPE_SSA2_4; /* could be wrong */
break;
}
else if( sscanf( s, "%d:%d:%d:", &i_dummy, &i_dummy, &i_dummy ) == 3 ||
i_sub_type = SUB_TYPE_VPLAYER;
break;
}
+ else if( local_stristr( s, "# VobSub index file" ) )
+ {
+ i_sub_type = SUB_TYPE_VOBSUB;
+ break;
+ }
}
text_rewind( &txt );
{
msg_Dbg( p_input, "detected %s format",
sub_read_subtitle_function[i].psz_name );
+ p_sub->i_sub_type = i_sub_type;
pf_read_subtitle = sub_read_subtitle_function[i].pf_read_subtitle;
break;
}
i_max += 128;
if( p_sub->subtitle )
{
- p_sub->subtitle = realloc( p_sub->subtitle,
- sizeof( subtitle_t ) * i_max );
+ if( !( p_sub->subtitle = realloc( p_sub->subtitle,
+ sizeof( subtitle_t ) * i_max ) ) )
+ {
+ msg_Err( p_sub, "out of memory");
+ return VLC_ENOMEM;
+ }
}
else
{
- p_sub->subtitle = malloc( sizeof( subtitle_t ) * i_max );
+ if( !( p_sub->subtitle = malloc( sizeof( subtitle_t ) * i_max ) ) )
+ {
+ msg_Err( p_sub, "out of memory");
+ return VLC_ENOMEM;
+ }
}
}
- if( pf_read_subtitle( &txt,
+ if( pf_read_subtitle( p_sub, &txt,
p_sub->subtitle + p_sub->i_subtitles,
i_microsecperframe ) < 0 )
{
text_unload( &txt );
/* *** fix subtitle (order and time) *** */
- p_sub->i_subtitle = 0; // will be modified by sub_fix
- sub_fix( p_sub );
+ p_sub->i_subtitle = 0; /* will be modified by sub_fix */
+ if( p_sub->i_sub_type != SUB_TYPE_VOBSUB )
+ {
+ sub_fix( p_sub );
+ }
/* *** add subtitle ES *** */
vlc_mutex_lock( &p_input->stream.stream_lock );
vlc_mutex_unlock( &p_input->stream.stream_lock );
p_sub->p_es->i_stream_id = 0xff - i_track_id; /* FIXME */
- p_sub->p_es->i_fourcc = VLC_FOURCC( 's','u','b','t' );
+
+ if( p_sub->psz_header != NULL )
+ {
+ p_sub->p_es->p_demux_data = malloc( sizeof( subtitle_data_t ) );
+ p_sub->p_es->p_demux_data->psz_header = strdup( p_sub->psz_header );
+ free( p_sub->psz_header );
+ }
+
+ if( p_sub->i_sub_type == SUB_TYPE_VOBSUB )
+ {
+ p_sub->p_es->i_fourcc = VLC_FOURCC( 's','p','u',' ' );
+ /* open vobsub file */
+ }
+ else if( p_sub->i_sub_type == SUB_TYPE_SSA1 ||
+ p_sub->i_sub_type == SUB_TYPE_SSA2_4 )
+ {
+ p_sub->p_es->i_fourcc = VLC_FOURCC( 's','s','a',' ' );
+ }
+ else
+ {
+ p_sub->p_es->i_fourcc = VLC_FOURCC( 's','u','b','t' );
+ }
p_sub->i_previously_selected = 0;
return VLC_SUCCESS;
free( p_sub->subtitle );
}
}
+
/*****************************************************************************
- *
* sub_fix: fix time stamp and order of subtitle
*****************************************************************************/
static void sub_fix( subtitle_demux_t *p_sub )
/*****************************************************************************
* Specific Subtitle function
*****************************************************************************/
-static int sub_MicroDvdRead( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe)
+static int sub_MicroDvdRead( subtitle_demux_t *p_sub, text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe)
{
/*
* each line:
return( 0 );
}
-static int sub_SubRipRead( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe )
+static int sub_SubRipRead( subtitle_demux_t *p_sub, text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe )
{
/*
* n
i_len = strlen( s );
if( i_len <= 1 )
{
- // empty line -> end of this subtitle
+ /* empty line -> end of this subtitle */
buffer_text[__MAX( i_buffer_text - 1, 0 )] = '\0';
p_subtitle->i_start = i_start;
p_subtitle->i_stop = i_stop;
}
-static int sub_SSARead( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe, int i_comma_count )
+static int sub_SSARead( subtitle_demux_t *p_sub, text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe )
{
char buffer_text[ 10 * MAX_LINE];
char *s;
- char *p_buffer_text;
mtime_t i_start;
mtime_t i_stop;
- int i_comma;
- int i_text;
for( ;; )
{
{
return( VLC_EGENERIC );
}
+ p_subtitle->psz_text = malloc( strlen( s ) );
+
if( sscanf( s,
- "Dialogue: Marked=%d,%d:%d:%d.%d,%d:%d:%d.%d,%[^\r\n]",
+ "Dialogue: Marked=%d,%d:%d:%d.%d,%d:%d:%d.%d%[^\r\n]",
&i_dummy,
&h1, &m1, &s1, &c1,
&h2, &m2, &s2, &c2,
(mtime_t)s2 * 1000 +
(mtime_t)c2 * 10 ) * 1000;
- p_buffer_text = buffer_text;
- i_comma = 3;
- while( i_comma < i_comma_count &&
- *p_buffer_text != '\0' )
+ /* The dec expects: ReadOrder, Layer, Style, Name, MarginL, MarginR, MarginV, Effect, Text */
+ if( p_sub->i_sub_type == SUB_TYPE_SSA1 )
{
- if( *p_buffer_text == ',' )
- {
- i_comma++;
- }
- p_buffer_text++;
+ sprintf( p_subtitle->psz_text, ",%d%s", i_dummy, strdup( buffer_text) );
}
- p_subtitle->psz_text = malloc( strlen( p_buffer_text ) + 1);
- i_text = 0;
- while( *p_buffer_text )
+ else
{
- if( *p_buffer_text == '\\' && ( *p_buffer_text =='n' || *p_buffer_text =='N' ) )
- {
- p_subtitle->psz_text[i_text] = '\n';
- i_text++;
- p_buffer_text += 2;
- }
- else if( *p_buffer_text == '{' && *p_buffer_text == '\\')
+ sprintf( p_subtitle->psz_text, ",%d,%s", i_dummy, strdup( buffer_text) );
+ }
+ p_subtitle->i_start = i_start;
+ p_subtitle->i_stop = i_stop;
+ return( 0 );
+ }
+ else
+ {
+ /* All the other stuff we add to the header field */
+ if( p_sub->psz_header != NULL )
+ {
+ if( !( p_sub->psz_header = realloc( p_sub->psz_header,
+ strlen( p_sub->psz_header ) + strlen( s ) + 2 ) ) )
{
- while( *p_buffer_text && *p_buffer_text != '}' )
- {
- p_buffer_text++;
- }
+ msg_Err( p_sub, "out of memory");
+ return VLC_ENOMEM;
}
- else
+ p_sub->psz_header = strcat( p_sub->psz_header, strdup( s ) );
+ p_sub->psz_header = strcat( p_sub->psz_header, "\n" );
+ }
+ else
+ {
+ if( !( p_sub->psz_header = malloc( strlen( s ) + 2 ) ) )
{
- p_subtitle->psz_text[i_text] = *p_buffer_text;
- i_text++;
- p_buffer_text++;
+ msg_Err( p_sub, "out of memory");
+ return VLC_ENOMEM;
}
+ p_sub->psz_header = strdup( s );
+ p_sub->psz_header = strcat( p_sub->psz_header, "\n" );
}
- p_subtitle->psz_text[i_text] = '\0';
- p_subtitle->i_start = i_start;
- p_subtitle->i_stop = i_stop;
- return( 0 );
}
}
}
-static int sub_SSA1Read( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe )
-{
- return( sub_SSARead( txt, p_subtitle, i_microsecperframe, 8 ) );
-}
-static int sub_SSA2_4Read( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe )
-{
- return( sub_SSARead( txt, p_subtitle, i_microsecperframe, 9 ) );
-}
-
-static int sub_Vplayer( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe)
+static int sub_Vplayer( subtitle_demux_t *p_sub, text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe)
{
/*
* each line:
{
if( psz_start )
{
- if( strstr( psz_start, psz_str ) )
+ if( local_stristr( psz_start, psz_str ) )
{
- char *s = strstr( psz_start, psz_str );
+ char *s = local_stristr( psz_start, psz_str );
s += strlen( psz_str );
{
return NULL;
}
- if( strstr( p, psz_str ) )
+ if( local_stristr( p, psz_str ) )
{
- char *s = strstr( p, psz_str );
+ char *s = local_stristr( p, psz_str );
s += strlen( psz_str );
}
}
-static int sub_Sami( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe )
+static int sub_Sami( subtitle_demux_t *p_sub, text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe )
{
char *p;
int i_start;
{
if( *p == '<' )
{
- if( !strncmp( p, "<br", 3 ) || !strncmp( p, "<BR", 3 ) )
+ if( !strncasecmp( p, "<br", 3 ) )
{
ADDC( '\n' );
}
- else if( strstr( p, "Start=" ) )
+ else if( local_stristr( p, "Start=" ) )
{
text_previous_line( txt );
break;
#undef ADDC
}
+static int sub_VobSub( subtitle_demux_t *p_sub, text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe)
+{
+ /*
+ * Parse the idx file. Each line:
+ * timestamp: hh:mm:ss:mss, filepos: loc
+ * hexint is the hex location of the vobsub in the .sub file
+ *
+ */
+ char *p;
+
+ char buffer_text[MAX_LINE + 1];
+ uint32_t i_start, i_location;
+
+ for( ;; )
+ {
+ unsigned int h, m, s, ms, loc;
+
+ if( ( p = text_get_line( txt ) ) == NULL )
+ {
+ return( VLC_EGENERIC );
+ }
+ i_start = 0;
+
+ memset( buffer_text, '\0', MAX_LINE );
+ if( sscanf( p, "timestamp: %d:%d:%d:%d, filepos: %x%[^\r\n]",
+ &h, &m, &s, &ms, &loc, buffer_text ) == 5 )
+ {
+ i_start = ( (mtime_t)h * 3600*1000 +
+ (mtime_t)m * 60*1000 +
+ (mtime_t)s * 1000 +
+ (mtime_t)ms ) * 1000;
+ i_location = loc;
+ break;
+ }
+ }
+ p_subtitle->i_start = (mtime_t)i_start;
+ p_subtitle->i_stop = 0;
+ p_subtitle->i_vobsub_location = i_location;
+ fprintf( stderr, "time: %x, location: %x\n", i_start, i_location );
+ return( 0 );
+}
+
+