]> git.sesse.net Git - vlc/commitdiff
* split vobsub and textual demuxers.
authorDerk-Jan Hartman <hartman@videolan.org>
Sat, 18 Sep 2004 00:05:11 +0000 (00:05 +0000)
committerDerk-Jan Hartman <hartman@videolan.org>
Sat, 18 Sep 2004 00:05:11 +0000 (00:05 +0000)
configure.ac
modules/demux/Modules.am
modules/demux/subtitle.c
modules/demux/vobsub.c [new file with mode: 0644]

index 37a4f685d4578e19e2b8eefe3d55f2795268dcf5..8eb7361aa35b8f5bd91b686c93687aedc5f051dd 100644 (file)
@@ -955,7 +955,7 @@ VLC_ADD_PLUGINS([aout_file equalizer])
 VLC_ADD_PLUGINS([i420_rgb i420_yuy2 i422_yuy2 i420_ymga])
 VLC_ADD_PLUGINS([m3u id3 playlist export sgimb])
 VLC_ADD_PLUGINS([rawvideo blend scale])
-VLC_ADD_PLUGINS([wav araw subtitle adpcm a52sys dtssys au])
+VLC_ADD_PLUGINS([wav araw subtitle vobsub adpcm a52sys dtssys au])
 VLC_ADD_PLUGINS([access_file access_udp access_tcp access_http ipv4 access_mms])
 VLC_ADD_PLUGINS([access_ftp access_directory sap http])
 
index 9043ca22b2ccf7391592cd708852417d4809f8c8..72c6d48927e52ab56185ee66e174713bdc7f386b 100644 (file)
@@ -20,3 +20,4 @@ SOURCES_aiff = aiff.c
 SOURCES_sgimb = sgimb.c
 SOURCES_mjpeg = mjpeg.c
 SOURCES_subtitle = subtitle.c
+SOURCES_vobsub = vobsub.c
index 3746d8285cd47e2ca8e4cb837aa5df62de603cbb..2683c4cac0f4ba81210fe6fccaa6922ea8d88385 100644 (file)
@@ -1,5 +1,5 @@
 /*****************************************************************************
- * subtitle.c: Demux for text and vobsub files.
+ * subtitle.c: Demux for subtitle text files.
  *****************************************************************************
  * Copyright (C) 1999-2004 VideoLAN
  * $Id$
@@ -35,7 +35,6 @@
 #include <vlc/input.h>
 #include "vlc_video.h"
 
-#include "ps.h"
 
 #if (!defined( WIN32 ) || defined(__MINGW32__))
 #    include <dirent.h>
@@ -58,7 +57,7 @@ static void Close( vlc_object_t *p_this );
 static char *ppsz_sub_type[] =
 {
     "auto", "microdvd", "subrip", "subviewer", "ssa1",
-    "ssa2-4", "vplayer", "sami", "vobsub"
+    "ssa2-4", "vplayer", "sami"
 };
 
 vlc_module_begin();
@@ -73,7 +72,6 @@ vlc_module_begin();
     set_callbacks( Open, Close );
 
     add_shortcut( "subtitle" );
-    add_shortcut( "vobsub" );
 vlc_module_end();
 
 /*****************************************************************************
@@ -89,7 +87,7 @@ enum
     SUB_TYPE_VPLAYER,
     SUB_TYPE_SAMI,
     SUB_TYPE_SUBVIEWER,
-    SUB_TYPE_VOBSUB,
+    NULL
 };
 
 typedef struct
@@ -107,7 +105,6 @@ typedef struct
     mtime_t i_stop;
 
     char    *psz_text;
-    int     i_vobsub_location;
 } subtitle_t;
 
 
@@ -125,7 +122,6 @@ struct demux_sys_t
     int         i_subtitle;
     int         i_subtitles;
     subtitle_t  *subtitle;
-    FILE        *p_vobsub_file;
     mtime_t     i_original_mspf;
 
     int64_t     i_length;
@@ -137,9 +133,6 @@ static int  ParseSubViewer( demux_t *, subtitle_t * );
 static int  ParseSSA      ( demux_t *, subtitle_t * );
 static int  ParseVplayer  ( demux_t *, subtitle_t * );
 static int  ParseSami     ( demux_t *, subtitle_t * );
-static int  ParseVobSubIDX( demux_t *, subtitle_t * );
-
-static int  DemuxVobSub( demux_t *, block_t * );
 
 static struct
 {
@@ -156,7 +149,6 @@ static struct
     { "ssa2-4",     SUB_TYPE_SSA2_4,    "SSA-2/3/4",ParseSSA },
     { "vplayer",    SUB_TYPE_VPLAYER,   "VPlayer",  ParseVplayer },
     { "sami",       SUB_TYPE_SAMI,      "SAMI",     ParseSami },
-    { "vobsub",     SUB_TYPE_VOBSUB,    "VobSub",   ParseVobSubIDX },
     { NULL,         SUB_TYPE_UNKNOWN,   "Unknown",  NULL }
 };
 
@@ -165,8 +157,6 @@ static int Control( demux_t *, int, va_list );
 
 static void Fix( demux_t * );
 
-#define DVD_VIDEO_LB_LEN 2048
-
 /*****************************************************************************
  * Module initializer
  *****************************************************************************/
@@ -180,8 +170,7 @@ static int Open ( vlc_object_t *p_this )
     int  (*pf_read)( demux_t *, subtitle_t* );
     int i, i_max;
 
-    if( strcmp( p_demux->psz_demux, "subtitle" ) &&
-        strcmp( p_demux->psz_demux, "vobsub" ) )
+    if( strcmp( p_demux->psz_demux, "subtitle" ) )
     {
         msg_Dbg( p_demux, "subtitle demux discarded" );
         return VLC_EGENERIC;
@@ -194,7 +183,6 @@ static int Open ( vlc_object_t *p_this )
     p_sys->i_subtitle = 0;
     p_sys->i_subtitles= 0;
     p_sys->subtitle   = NULL;
-    p_sys->p_vobsub_file = NULL;
     p_sys->i_original_mspf = 0;
 
 
@@ -296,11 +284,6 @@ static int Open ( vlc_object_t *p_this )
                 p_sys->i_type = SUB_TYPE_VPLAYER;
                 break;
             }
-            else if( strcasestr( s, "# VobSub index file" ) )
-            {
-                p_sys->i_type = SUB_TYPE_VOBSUB;
-                break;
-            }
 
             free( s );
             s = NULL;
@@ -363,10 +346,6 @@ static int Open ( vlc_object_t *p_this )
 
     /* Fix subtitle (order and time) *** */
     p_sys->i_subtitle = 0;
-    if( p_sys->i_type != SUB_TYPE_VOBSUB )
-    {
-        Fix( p_demux );
-    }
     p_sys->i_length = 0;
     if( p_sys->i_subtitles > 0 )
     {
@@ -377,24 +356,7 @@ static int Open ( vlc_object_t *p_this )
     }
 
     /* *** add subtitle ES *** */
-    if( p_sys->i_type == SUB_TYPE_VOBSUB )
-    {
-        int i_len = strlen( p_demux->psz_path );
-        char *psz_vobname = strdup( p_demux->psz_path );
-
-        strcpy( psz_vobname + i_len - 4, ".sub" );
-
-        /* open file */
-        if( !( p_sys->p_vobsub_file = fopen( psz_vobname, "rb" ) ) )
-        {
-            msg_Err( p_demux, "couldn't open .sub Vobsub file: %s",
-                     psz_vobname );
-        }
-        free( psz_vobname );
-
-        es_format_Init( &fmt, SPU_ES, VLC_FOURCC( 's','p','u',' ' ) );
-    }
-    else if( p_sys->i_type == SUB_TYPE_SSA1 ||
+    if( p_sys->i_type == SUB_TYPE_SSA1 ||
              p_sys->i_type == SUB_TYPE_SSA2_4 )
     {
         es_format_Init( &fmt, SPU_ES, VLC_FOURCC( 's','s','a',' ' ) );
@@ -430,9 +392,6 @@ static void Close( vlc_object_t *p_this )
     if( p_sys->subtitle )
         free( p_sys->subtitle );
 
-    if( p_sys->p_vobsub_file )
-        fclose( p_sys->p_vobsub_file );
-
     free( p_sys );
 }
 
@@ -538,104 +497,50 @@ static int Demux( demux_t *p_demux )
         i_maxdate = p_sys->subtitle[p_sys->i_subtitle].i_start + 1;
     }
 
-    if( p_sys->i_type != SUB_TYPE_VOBSUB )
+    while( p_sys->i_subtitle < p_sys->i_subtitles &&
+           p_sys->subtitle[p_sys->i_subtitle].i_start < i_maxdate )
     {
-        while( p_sys->i_subtitle < p_sys->i_subtitles &&
-               p_sys->subtitle[p_sys->i_subtitle].i_start < i_maxdate )
-        {
-            block_t *p_block;
-            int i_len = strlen( p_sys->subtitle[p_sys->i_subtitle].psz_text ) + 1;
+        block_t *p_block;
+        int i_len = strlen( p_sys->subtitle[p_sys->i_subtitle].psz_text ) + 1;
 
-            if( i_len <= 1 )
-            {
-                /* empty subtitle */
-                p_sys->i_subtitle++;
-                continue;
-            }
-
-            if( ( p_block = block_New( p_demux, i_len ) ) == NULL )
-            {
-                p_sys->i_subtitle++;
-                continue;
-            }
-
-            if( p_sys->subtitle[p_sys->i_subtitle].i_start < 0 )
-            {
-                p_sys->i_subtitle++;
-                continue;
-            }
-
-            p_block->i_pts = p_sys->subtitle[p_sys->i_subtitle].i_start;
-            p_block->i_dts = p_block->i_pts;
-            if( p_sys->subtitle[p_sys->i_subtitle].i_stop > 0 )
-            {
-                p_block->i_length =
-                    p_sys->subtitle[p_sys->i_subtitle].i_stop - p_block->i_pts;
-            }
-
-            memcpy( p_block->p_buffer,
-                    p_sys->subtitle[p_sys->i_subtitle].psz_text, i_len );
-            if( p_block->i_pts > 0 )
-            {
-                es_out_Send( p_demux->out, p_sys->es, p_block );
-            }
-            else
-            {
-                block_Release( p_block );
-            }
+        if( i_len <= 1 )
+        {
+            /* empty subtitle */
             p_sys->i_subtitle++;
+            continue;
         }
-    }
-    else
-    {
-        while( p_sys->i_subtitle < p_sys->i_subtitles &&
-               p_sys->subtitle[p_sys->i_subtitle].i_start < i_maxdate )
-        {
-            int i_pos = p_sys->subtitle[p_sys->i_subtitle].i_vobsub_location;
-            block_t *p_block;
-            int i_size = 0;
-
-            /* first compute SPU size */
-            if( p_sys->i_subtitle + 1 < p_sys->i_subtitles )
-            {
-                i_size = p_sys->subtitle[p_sys->i_subtitle+1].i_vobsub_location - i_pos;
-            }
-            if( i_size <= 0 ) i_size = 65535;   /* Invalid or EOF */
-
-            /* Seek at the right place */
-            if( fseek( p_sys->p_vobsub_file, i_pos, SEEK_SET ) )
-            {
-                msg_Warn( p_demux,
-                          "cannot seek at right vobsub location %d", i_pos );
-                p_sys->i_subtitle++;
-                continue;
-            }
-
-            /* allocate a packet */
-            if( ( p_block = block_New( p_demux, i_size ) ) == NULL )
-            {
-                p_sys->i_subtitle++;
-                continue;
-            }
 
-            /* read data */
-            p_block->i_buffer = fread( p_block->p_buffer, 1, i_size,
-                                       p_sys->p_vobsub_file );
-            if( p_block->i_buffer <= 6 )
-            {
-                block_Release( p_block );
-                p_sys->i_subtitle++;
-                continue;
-            }
+        if( ( p_block = block_New( p_demux, i_len ) ) == NULL )
+        {
+            p_sys->i_subtitle++;
+            continue;
+        }
 
-            /* pts */
-            p_block->i_pts = p_sys->subtitle[p_sys->i_subtitle].i_start;
+        if( p_sys->subtitle[p_sys->i_subtitle].i_start < 0 )
+        {
+            p_sys->i_subtitle++;
+            continue;
+        }
 
-            /* demux this block */
-            DemuxVobSub( p_demux, p_block );
+        p_block->i_pts = p_sys->subtitle[p_sys->i_subtitle].i_start;
+        p_block->i_dts = p_block->i_pts;
+        if( p_sys->subtitle[p_sys->i_subtitle].i_stop > 0 )
+        {
+            p_block->i_length =
+                p_sys->subtitle[p_sys->i_subtitle].i_stop - p_block->i_pts;
+        }
 
-            p_sys->i_subtitle++;
+        memcpy( p_block->p_buffer,
+                p_sys->subtitle[p_sys->i_subtitle].psz_text, i_len );
+        if( p_block->i_pts > 0 )
+        {
+            es_out_Send( p_demux->out, p_sys->es, p_block );
         }
+        else
+        {
+            block_Release( p_block );
+        }
+        p_sys->i_subtitle++;
     }
 
     /* */
@@ -764,7 +669,6 @@ static int ParseMicroDvd( demux_t *p_demux, subtitle_t *p_subtitle )
 
     p_subtitle->i_start = 0;
     p_subtitle->i_stop  = 0;
-    p_subtitle->i_vobsub_location  = 0;
     p_subtitle->psz_text = NULL;
 
     for( ;; )
@@ -820,7 +724,6 @@ static int  ParseSubRip( demux_t *p_demux, subtitle_t *p_subtitle )
 
     p_subtitle->i_start = 0;
     p_subtitle->i_stop  = 0;
-    p_subtitle->i_vobsub_location  = 0;
     p_subtitle->psz_text = NULL;
 
     for( ;; )
@@ -914,7 +817,6 @@ static int  ParseSubViewer( demux_t *p_demux, subtitle_t *p_subtitle )
 
     p_subtitle->i_start = 0;
     p_subtitle->i_stop  = 0;
-    p_subtitle->i_vobsub_location  = 0;
     p_subtitle->psz_text = NULL;
 
     for( ;; )
@@ -1002,7 +904,6 @@ static int  ParseSSA( demux_t *p_demux, subtitle_t *p_subtitle )
 
     p_subtitle->i_start = 0;
     p_subtitle->i_stop  = 0;
-    p_subtitle->i_vobsub_location  = 0;
     p_subtitle->psz_text = NULL;
 
     for( ;; )
@@ -1095,7 +996,6 @@ static int  ParseVplayer( demux_t *p_demux, subtitle_t *p_subtitle )
 
     p_subtitle->i_start = 0;
     p_subtitle->i_stop  = 0;
-    p_subtitle->i_vobsub_location  = 0;
     p_subtitle->psz_text = NULL;
 
     for( ;; )
@@ -1179,7 +1079,6 @@ static int  ParseSami( demux_t *p_demux, subtitle_t *p_subtitle )
 
     p_subtitle->i_start = 0;
     p_subtitle->i_stop  = 0;
-    p_subtitle->i_vobsub_location  = 0;
     p_subtitle->psz_text = NULL;
 
 #define ADDC( c ) \
@@ -1263,117 +1162,3 @@ static int  ParseSami( demux_t *p_demux, subtitle_t *p_subtitle )
     return( VLC_SUCCESS );
 #undef ADDC
 }
-
-static int  ParseVobSubIDX( demux_t *p_demux, subtitle_t *p_subtitle )
-{
-    demux_sys_t *p_sys = p_demux->p_sys;
-    text_t      *txt = &p_sys->txt;
-
-    /*
-     * 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];
-    unsigned int    i_start, i_location;
-
-    p_subtitle->i_start = 0;
-    p_subtitle->i_stop  = 0;
-    p_subtitle->i_vobsub_location  = 0;
-    p_subtitle->psz_text = NULL;
-
-    for( ;; )
-    {
-        unsigned int h, m, s, ms, loc;
-
-        if( ( p = TextGetLine( 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->psz_text = NULL;
-    p_subtitle->i_vobsub_location = i_location;
-    fprintf( stderr, "time: %x, location: %x\n", i_start, i_location );
-    return( 0 );
-}
-
-static int  DemuxVobSub( demux_t *p_demux, block_t *p_bk )
-{
-    demux_sys_t *p_sys = p_demux->p_sys;
-    uint8_t     *p = p_bk->p_buffer;
-    uint8_t     *p_end = &p_bk->p_buffer[p_bk->i_buffer];
-
-    while( p < p_end )
-    {
-        int i_size = ps_pkt_size( p, p_end - p );
-        block_t *p_pkt;
-        int      i_id;
-        int      i_spu;
-
-        if( i_size <= 0 )
-        {
-            break;
-        }
-        if( p[0] != 0 || p[1] != 0 || p[2] != 0x01 )
-        {
-            msg_Warn( p_demux, "invalid PES" );
-            break;
-        }
-
-        if( p[3] != 0xbd )
-        {
-            msg_Dbg( p_demux, "we don't need these ps packets (id=0x1%2.2x)", p[3] );
-            p += i_size;
-            continue;
-        }
-
-        /* Create a block */
-        p_pkt = block_New( p_demux, i_size );
-        memcpy( p_pkt->p_buffer, p, i_size);
-        p += i_size;
-
-        i_id = ps_pkt_id( p_pkt );
-        if( (i_id&0xffe0) != 0xbd20 ||
-            ps_pkt_parse_pes( p_pkt, 1 ) )
-        {
-            block_Release( p_pkt );
-            continue;
-        }
-        i_spu = i_id&0x1f;
-        msg_Dbg( p_demux, "SPU track %d size %d", i_spu, i_size );
-
-        /* FIXME i_spu == determines which of the spu tracks we will show. */
-        if( p_sys->es && i_spu == 0 )
-        {
-            p_pkt->i_dts = p_pkt->i_pts = p_bk->i_pts;
-            p_pkt->i_length = 0;
-            es_out_Send( p_demux->out, p_sys->es, p_pkt );
-
-            p_bk->i_pts = 0;    /* only first packet has a pts */
-        }
-        else
-        {
-            block_Release( p_pkt );
-            continue;
-        }
-    }
-
-    return VLC_SUCCESS;
-}
diff --git a/modules/demux/vobsub.c b/modules/demux/vobsub.c
new file mode 100644 (file)
index 0000000..1c9669c
--- /dev/null
@@ -0,0 +1,549 @@
+/*****************************************************************************
+ * subtitle.c: Demux vobsub files.
+ *****************************************************************************
+ * Copyright (C) 1999-2004 VideoLAN
+ * $Id$
+ *
+ * Authors: Laurent Aimar <fenrir@via.ecp.fr>
+ *          Derk-Jan Hartman <hartman 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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#include <stdlib.h>
+
+#include <errno.h>
+#include <sys/types.h>
+
+#include <vlc/vlc.h>
+#include <vlc/input.h>
+#include "vlc_video.h"
+
+#include "ps.h"
+
+#define MAX_LINE 8192
+
+/*****************************************************************************
+ * Module descriptor
+ *****************************************************************************/
+static int  Open ( vlc_object_t *p_this );
+static void Close( vlc_object_t *p_this );
+
+vlc_module_begin();
+    set_description( _("Vobsub subtitles demux") );
+    set_capability( "demux2", 0 );
+    
+    set_callbacks( Open, Close );
+
+    add_shortcut( "vobsub" );
+vlc_module_end();
+
+/*****************************************************************************
+ * Prototypes:
+ *****************************************************************************/
+
+typedef struct
+{
+    int     i_line_count;
+    int     i_line;
+    char    **line;
+} text_t;
+static int  TextLoad( text_t *, stream_t *s );
+static void TextUnload( text_t * );
+
+typedef struct
+{
+    mtime_t i_start;
+    mtime_t i_stop;
+
+    int     i_vobsub_location;
+} subtitle_t;
+
+
+struct demux_sys_t
+{
+    int         i_type;
+    text_t      txt;
+    es_out_id_t *es;
+
+    int64_t     i_next_demux_date;
+
+    int         i_subtitle;
+    int         i_subtitles;
+    subtitle_t  *subtitle;
+    FILE        *p_vobsub_file;
+
+    int64_t     i_length;
+};
+
+static int Demux( demux_t * );
+static int Control( demux_t *, int, va_list );
+
+static int  ParseVobSubIDX( demux_t *, subtitle_t * );
+static int  DemuxVobSub( demux_t *f, block_t *);
+
+/*****************************************************************************
+ * Module initializer
+ *****************************************************************************/
+static int Open ( vlc_object_t *p_this )
+{
+    demux_t     *p_demux = (demux_t*)p_this;
+    demux_sys_t *p_sys;
+    es_format_t fmt;
+    int i_max;
+
+    if( strcmp( p_demux->psz_demux, "vobsub" ) )
+    {
+        msg_Dbg( p_demux, "vobsub demux discarded" );
+        return VLC_EGENERIC;
+    }
+
+    p_demux->pf_demux = Demux;
+    p_demux->pf_control = Control;
+    p_demux->p_sys = p_sys = malloc( sizeof( demux_sys_t ) );
+    p_sys->i_subtitle = 0;
+    p_sys->i_subtitles= 0;
+    p_sys->subtitle   = NULL;
+    p_sys->p_vobsub_file = NULL;
+
+    char *s = NULL;
+    if( ( s = stream_ReadLine( p_demux->s ) ) != NULL )
+    {
+        if( !strcasestr( s, "# VobSub index file" ) )
+        {
+            msg_Err( p_demux, "this doesn't seem to be a vobsub file, bailing" );
+            free( s );
+            return VLC_EGENERIC;
+        }
+        free( s );
+        s = NULL;
+
+        if( stream_Seek( p_demux->s, 0 ) )
+        {
+            msg_Warn( p_demux, "failed to rewind" );
+        }
+    }
+    else
+    {
+        msg_Err( p_demux, "could not read vobsub IDX file" );
+        return VLC_EGENERIC;
+    }
+
+    /* Load the whole file */
+    TextLoad( &p_sys->txt, p_demux->s );
+
+    /* Parse it */
+    for( i_max = 0;; )
+    {
+        if( p_sys->i_subtitles >= i_max )
+        {
+            i_max += 500;
+            if( !( p_sys->subtitle = realloc( p_sys->subtitle,
+                                              sizeof(subtitle_t) * i_max ) ) )
+            {
+                msg_Err( p_demux, "out of memory");
+                return VLC_ENOMEM;
+            }
+        }
+
+        if( ParseVobSubIDX( p_demux, &p_sys->subtitle[p_sys->i_subtitles] ) )
+            break;
+
+        p_sys->i_subtitles++;
+    }
+    /* Unload */
+    TextUnload( &p_sys->txt );
+
+    msg_Dbg(p_demux, "loaded %d subtitles", p_sys->i_subtitles );
+
+    /* Fix subtitle (order and time) *** */
+    p_sys->i_subtitle = 0;
+    p_sys->i_length = 0;
+    if( p_sys->i_subtitles > 0 )
+    {
+        p_sys->i_length = p_sys->subtitle[p_sys->i_subtitles-1].i_stop;
+        /* +1 to avoid 0 */
+        if( p_sys->i_length <= 0 )
+            p_sys->i_length = p_sys->subtitle[p_sys->i_subtitles-1].i_start+1;
+    }
+
+    int i_len = strlen( p_demux->psz_path );
+    char *psz_vobname = strdup( p_demux->psz_path );
+
+    strcpy( psz_vobname + i_len - 4, ".sub" );
+
+    /* open file */
+    if( !( p_sys->p_vobsub_file = fopen( psz_vobname, "rb" ) ) )
+    {
+        msg_Err( p_demux, "couldn't open .sub Vobsub file: %s",
+                 psz_vobname );
+    }
+    free( psz_vobname );
+
+    es_format_Init( &fmt, SPU_ES, VLC_FOURCC( 's','p','u',' ' ) );
+
+    p_sys->es = es_out_Add( p_demux->out, &fmt );
+
+    return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * Close: Close subtitle demux
+ *****************************************************************************/
+static void Close( vlc_object_t *p_this )
+{
+    demux_t *p_demux = (demux_t*)p_this;
+    demux_sys_t *p_sys = p_demux->p_sys;
+
+    if( p_sys->subtitle )
+        free( p_sys->subtitle );
+
+    if( p_sys->p_vobsub_file )
+        fclose( p_sys->p_vobsub_file );
+
+    free( p_sys );
+}
+
+/*****************************************************************************
+ * Control:
+ *****************************************************************************/
+static int Control( demux_t *p_demux, int i_query, va_list args )
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+    int64_t *pi64, i64;
+    double *pf, f;
+
+    switch( i_query )
+    {
+        case DEMUX_GET_LENGTH:
+            pi64 = (int64_t*)va_arg( args, int64_t * );
+            *pi64 = p_sys->i_length;
+            return VLC_SUCCESS;
+
+        case DEMUX_GET_TIME:
+            pi64 = (int64_t*)va_arg( args, int64_t * );
+            if( p_sys->i_subtitle < p_sys->i_subtitles )
+            {
+                *pi64 = p_sys->subtitle[p_sys->i_subtitle].i_start;
+                return VLC_SUCCESS;
+            }
+            return VLC_EGENERIC;
+
+        case DEMUX_SET_TIME:
+            i64 = (int64_t)va_arg( args, int64_t );
+            p_sys->i_subtitle = 0;
+            while( p_sys->i_subtitle < p_sys->i_subtitles &&
+                   p_sys->subtitle[p_sys->i_subtitle].i_start < i64 )
+            {
+                p_sys->i_subtitle++;
+            }
+
+            if( p_sys->i_subtitle >= p_sys->i_subtitles )
+                return VLC_EGENERIC;
+            return VLC_SUCCESS;
+
+        case DEMUX_GET_POSITION:
+            pf = (double*)va_arg( args, double * );
+            if( p_sys->i_subtitle >= p_sys->i_subtitles )
+            {
+                *pf = 1.0;
+            }
+            else if( p_sys->i_subtitles > 0 )
+            {
+                *pf = (double)p_sys->subtitle[p_sys->i_subtitle].i_start /
+                      (double)p_sys->i_length;
+            }
+            else
+            {
+                *pf = 0.0;
+            }
+            return VLC_SUCCESS;
+
+        case DEMUX_SET_POSITION:
+            f = (double)va_arg( args, double );
+            i64 = f * p_sys->i_length;
+
+            p_sys->i_subtitle = 0;
+            while( p_sys->i_subtitle < p_sys->i_subtitles &&
+                   p_sys->subtitle[p_sys->i_subtitle].i_start < i64 )
+            {
+                p_sys->i_subtitle++;
+            }
+            if( p_sys->i_subtitle >= p_sys->i_subtitles )
+                return VLC_EGENERIC;
+            return VLC_SUCCESS;
+
+        case DEMUX_SET_NEXT_DEMUX_TIME:
+            p_sys->i_next_demux_date = (int64_t)va_arg( args, int64_t );
+            return VLC_SUCCESS;
+
+        case DEMUX_GET_FPS:
+        case DEMUX_GET_META:
+        case DEMUX_GET_TITLE_INFO:
+            return VLC_EGENERIC;
+
+        default:
+            msg_Err( p_demux, "unknown query in subtitle control" );
+            return VLC_EGENERIC;
+    }
+}
+
+/*****************************************************************************
+ * Demux: Send subtitle to decoder
+ *****************************************************************************/
+static int Demux( demux_t *p_demux )
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+    int64_t i_maxdate;
+
+    if( p_sys->i_subtitle >= p_sys->i_subtitles )
+        return 0;
+
+    i_maxdate = p_sys->i_next_demux_date;
+    if( i_maxdate <= 0 && p_sys->i_subtitle < p_sys->i_subtitles )
+    {
+        /* Should not happen */
+        i_maxdate = p_sys->subtitle[p_sys->i_subtitle].i_start + 1;
+    }
+
+      while( p_sys->i_subtitle < p_sys->i_subtitles &&
+           p_sys->subtitle[p_sys->i_subtitle].i_start < i_maxdate )
+    {
+        int i_pos = p_sys->subtitle[p_sys->i_subtitle].i_vobsub_location;
+        block_t *p_block;
+        int i_size = 0;
+
+        /* first compute SPU size */
+        if( p_sys->i_subtitle + 1 < p_sys->i_subtitles )
+        {
+            i_size = p_sys->subtitle[p_sys->i_subtitle+1].i_vobsub_location - i_pos;
+        }
+        if( i_size <= 0 ) i_size = 65535;   /* Invalid or EOF */
+
+        /* Seek at the right place */
+        if( fseek( p_sys->p_vobsub_file, i_pos, SEEK_SET ) )
+        {
+            msg_Warn( p_demux,
+                      "cannot seek at right vobsub location %d", i_pos );
+            p_sys->i_subtitle++;
+            continue;
+        }
+
+        /* allocate a packet */
+        if( ( p_block = block_New( p_demux, i_size ) ) == NULL )
+        {
+            p_sys->i_subtitle++;
+            continue;
+        }
+
+        /* read data */
+        p_block->i_buffer = fread( p_block->p_buffer, 1, i_size,
+                                   p_sys->p_vobsub_file );
+        if( p_block->i_buffer <= 6 )
+        {
+            block_Release( p_block );
+            p_sys->i_subtitle++;
+            continue;
+        }
+
+        /* pts */
+        p_block->i_pts = p_sys->subtitle[p_sys->i_subtitle].i_start;
+
+        /* demux this block */
+        DemuxVobSub( p_demux, p_block );
+
+        p_sys->i_subtitle++;
+    }
+
+    /* */
+    p_sys->i_next_demux_date = 0;
+
+    return 1;
+}
+
+static int TextLoad( text_t *txt, stream_t *s )
+{
+    int   i_line_max;
+
+    /* init txt */
+    i_line_max          = 500;
+    txt->i_line_count   = 0;
+    txt->i_line         = 0;
+    txt->line           = calloc( i_line_max, sizeof( char * ) );
+
+    /* load the complete file */
+    for( ;; )
+    {
+        char *psz = stream_ReadLine( s );
+
+        if( psz == NULL )
+            break;
+
+        txt->line[txt->i_line_count++] = psz;
+        if( txt->i_line_count >= i_line_max )
+        {
+            i_line_max += 100;
+            txt->line = realloc( txt->line, i_line_max * sizeof( char*) );
+        }
+    }
+
+    if( txt->i_line_count <= 0 )
+    {
+        free( txt->line );
+        return VLC_EGENERIC;
+    }
+
+    return VLC_SUCCESS;
+}
+static void TextUnload( text_t *txt )
+{
+    int i;
+
+    for( i = 0; i < txt->i_line_count; i++ )
+    {
+        free( txt->line[i] );
+    }
+    free( txt->line );
+    txt->i_line       = 0;
+    txt->i_line_count = 0;
+}
+
+static char *TextGetLine( text_t *txt )
+{
+    if( txt->i_line >= txt->i_line_count )
+        return( NULL );
+
+    return txt->line[txt->i_line++];
+}
+static void TextPreviousLine( text_t *txt )
+{
+    if( txt->i_line > 0 )
+        txt->i_line--;
+}
+
+static int  ParseVobSubIDX( demux_t *p_demux, subtitle_t *p_subtitle )
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+    text_t      *txt = &p_sys->txt;
+
+    /*
+     * 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];
+    unsigned int    i_start, i_location;
+
+    p_subtitle->i_start = 0;
+    p_subtitle->i_stop  = 0;
+    p_subtitle->i_vobsub_location  = 0;
+
+    for( ;; )
+    {
+        unsigned int h, m, s, ms, loc;
+
+        if( ( p = TextGetLine( 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 );
+}
+
+static int  DemuxVobSub( demux_t *p_demux, block_t *p_bk )
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+    uint8_t     *p = p_bk->p_buffer;
+    uint8_t     *p_end = &p_bk->p_buffer[p_bk->i_buffer];
+
+    while( p < p_end )
+    {
+        int i_size = ps_pkt_size( p, p_end - p );
+        block_t *p_pkt;
+        int      i_id;
+        int      i_spu;
+
+        if( i_size <= 0 )
+        {
+            break;
+        }
+        if( p[0] != 0 || p[1] != 0 || p[2] != 0x01 )
+        {
+            msg_Warn( p_demux, "invalid PES" );
+            break;
+        }
+
+        if( p[3] != 0xbd )
+        {
+            msg_Dbg( p_demux, "we don't need these ps packets (id=0x1%2.2x)", p[3] );
+            p += i_size;
+            continue;
+        }
+
+        /* Create a block */
+        p_pkt = block_New( p_demux, i_size );
+        memcpy( p_pkt->p_buffer, p, i_size);
+        p += i_size;
+
+        i_id = ps_pkt_id( p_pkt );
+        if( (i_id&0xffe0) != 0xbd20 ||
+            ps_pkt_parse_pes( p_pkt, 1 ) )
+        {
+            block_Release( p_pkt );
+            continue;
+        }
+        i_spu = i_id&0x1f;
+        msg_Dbg( p_demux, "SPU track %d size %d", i_spu, i_size );
+
+        /* FIXME i_spu == determines which of the spu tracks we will show. */
+        if( p_sys->es && i_spu == 0 )
+        {
+            p_pkt->i_dts = p_pkt->i_pts = p_bk->i_pts;
+            p_pkt->i_length = 0;
+            es_out_Send( p_demux->out, p_sys->es, p_pkt );
+
+            p_bk->i_pts = 0;    /* only first packet has a pts */
+        }
+        else
+        {
+            block_Release( p_pkt );
+            continue;
+        }
+    }
+
+    return VLC_SUCCESS;
+}