]> git.sesse.net Git - vlc/blob - modules/demux/util/sub.c
+ configure.ac:
[vlc] / modules / demux / util / sub.c
1 /*****************************************************************************
2  * sub.c
3  *****************************************************************************
4  * Copyright (C) 1999-2003 VideoLAN
5  * $Id: sub.c,v 1.33 2003/11/04 11:11:30 titer Exp $
6  *
7  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <stdlib.h>
28 #include <errno.h>
29 #include <sys/types.h>
30 #include <ctype.h>
31
32 #include <vlc/vlc.h>
33 #include <vlc/input.h>
34 #include "vlc_video.h"
35
36 #include "sub.h"
37
38 #if (!defined( WIN32 ) || defined(__MINGW32__))
39 #    include <dirent.h>
40 #endif
41
42 static int  Open ( vlc_object_t *p_this );
43
44 static int  sub_open ( subtitle_demux_t *p_sub,
45                        input_thread_t  *p_input,
46                        char  *psz_name,
47                        mtime_t i_microsecperframe,
48                        int i_track_id );
49 static int  sub_demux( subtitle_demux_t *p_sub, mtime_t i_maxdate );
50 static int  sub_seek ( subtitle_demux_t *p_sub, mtime_t i_date );
51 static void sub_close( subtitle_demux_t *p_sub );
52
53 static void sub_fix( subtitle_demux_t *p_sub );
54
55 static char *ppsz_sub_type[] = { "auto", "microdvd", "subrip", "ssa1", "ssa2-4",
56                                  "vplayer", "sami", "vobsub", NULL };
57
58
59 /*****************************************************************************
60  * Module descriptor
61  *****************************************************************************/
62 #define SUB_DELAY_LONGTEXT \
63     "Delay subtitles (in 1/10s)"
64 #define SUB_FPS_LONGTEXT \
65     "Override frames per second. " \
66     "It will only work with MicroDVD subtitles."
67 #define SUB_TYPE_LONGTEXT \
68     "One from \"microdvd\", \"subrip\", \"ssa1\", \"ssa2-4\", \"vplayer\" " \
69     "\"sami\" (auto for autodetection, it should always work)."
70
71 vlc_module_begin();
72     set_description( _("Text subtitles demux") );
73     set_capability( "subtitle demux", 12 );
74     add_category_hint( "Subtitles", NULL, VLC_TRUE );
75         add_float( "sub-fps", 0.0, NULL,
76                    "Frames per second",
77                    SUB_FPS_LONGTEXT, VLC_TRUE );
78         add_integer( "sub-delay", 0, NULL,
79                      "Delay subtitles (in 1/10s)",
80                      SUB_DELAY_LONGTEXT, VLC_TRUE );
81         add_string_from_list( "sub-type", "auto", ppsz_sub_type, NULL,
82                               "subtitles type",
83                               SUB_TYPE_LONGTEXT, VLC_TRUE );
84     set_callbacks( Open, NULL );
85 vlc_module_end();
86
87 /*****************************************************************************
88  * Module initializer
89  *****************************************************************************/
90 static int Open ( vlc_object_t *p_this )
91 {
92     subtitle_demux_t *p_sub = (subtitle_demux_t*)p_this;
93
94     p_sub->pf_open  = sub_open;
95     p_sub->pf_demux = sub_demux;
96     p_sub->pf_seek  = sub_seek;
97     p_sub->pf_close = sub_close;
98
99     /* Initialize the variables */
100     var_Create( p_this, "sub-fps", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT );
101     var_Create( p_this, "sub-delay", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
102     var_Create( p_this, "sub-type", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
103
104     return VLC_SUCCESS;
105 }
106 #define MAX_TRY     256
107 #define MAX_LINE    2048
108
109 #define FREE( p ) if( p ) { free( p); (p) = NULL; }
110
111 typedef struct
112 {
113     int     i_line_count;
114     int     i_line;
115     char    **line;
116 } text_t;
117
118 static int  text_load( text_t *txt, char *psz_name )
119 {
120     FILE *f;
121     int   i_line_max;
122
123     /* init txt */
124     i_line_max          = 100;
125     txt->i_line_count   = 0;
126     txt->i_line         = 0;
127     txt->line           = calloc( i_line_max, sizeof( char * ) );
128
129     /* open file */
130     if( !( f = fopen( psz_name, "rb" ) ) )
131     {
132         return VLC_EGENERIC;
133     }
134
135     /* load the complete file */
136     for( ;; )
137     {
138         char buffer[8096];
139         char *p;
140
141         if( fgets( buffer, 8096, f ) <= 0)
142         {
143             break;
144         }
145         while( ( p = strchr( buffer, '\r' ) ) )
146         {
147             *p = '\0';
148         }
149         while( ( p = strchr( buffer, '\n' ) ) )
150         {
151             *p = '\0';
152         }
153
154         txt->line[txt->i_line_count++] = strdup( buffer );
155
156         if( txt->i_line_count >= i_line_max )
157         {
158             i_line_max += 100;
159             txt->line = realloc( txt->line, i_line_max * sizeof( char*) );
160         }
161     }
162
163     fclose( f );
164
165     if( txt->i_line_count <= 0 )
166     {
167         FREE( txt->line );
168         return( VLC_EGENERIC );
169     }
170
171     return( VLC_SUCCESS );
172 }
173 static void text_unload( text_t *txt )
174 {
175     int i;
176
177     for( i = 0; i < txt->i_line_count; i++ )
178     {
179         FREE( txt->line[i] );
180     }
181     FREE( txt->line );
182     txt->i_line       = 0;
183     txt->i_line_count = 0;
184 }
185
186 static char *text_get_line( text_t *txt )
187 {
188     if( txt->i_line >= txt->i_line_count )
189     {
190         return( NULL );
191     }
192
193     return( txt->line[txt->i_line++] );
194 }
195 static void text_previous_line( text_t *txt )
196 {
197     if( txt->i_line > 0 )
198     {
199         txt->i_line--;
200     }
201 }
202 static void text_rewind( text_t *txt )
203 {
204     txt->i_line = 0;
205 }
206
207 static int  sub_MicroDvdRead( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe );
208 static int  sub_SubRipRead  ( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe );
209 static int  sub_SSA1Read    ( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe );
210 static int  sub_SSA2_4Read  ( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe );
211 static int  sub_Vplayer     ( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe );
212 static int  sub_Sami        ( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe );
213 static int  sub_VobSub      ( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe );
214
215 static struct
216 {
217     char *psz_type_name;
218     int  i_type;
219     char *psz_name;
220     int  (*pf_read_subtitle)    ( text_t *, subtitle_t*, mtime_t );
221 } sub_read_subtitle_function [] =
222 {
223     { "microdvd",   SUB_TYPE_MICRODVD,  "MicroDVD", sub_MicroDvdRead },
224     { "subrip",     SUB_TYPE_SUBRIP,    "SubRIP",   sub_SubRipRead },
225     { "ssa1",       SUB_TYPE_SSA1,      "SSA-1",    sub_SSA1Read },
226     { "ssa2-4",     SUB_TYPE_SSA2_4,    "SSA-2/3/4",sub_SSA2_4Read },
227     { "vplayer",    SUB_TYPE_VPLAYER,   "VPlayer",  sub_Vplayer },
228     { "sami",       SUB_TYPE_SAMI,      "SAMI",     sub_Sami },
229     { "vobsub",     SUB_TYPE_VOBSUB,    "VobSub",   sub_VobSub },
230     { NULL,         SUB_TYPE_UNKNOWN,   "Unknown",  NULL }
231 };
232
233 static char * local_stristr( char *psz_big, char *psz_little)
234 {
235     char *p_pos = psz_big;
236
237     if (!psz_big || !psz_little || !*psz_little) return psz_big;
238
239     while (*p_pos)
240     {
241         if (toupper(*p_pos) == toupper(*psz_little))
242         {
243             char * psz_cur1 = p_pos + 1;
244             char * psz_cur2 = psz_little + 1;
245             while (*psz_cur1 && *psz_cur2 && toupper(*psz_cur1) == toupper(*psz_cur2))
246             {
247                 psz_cur1++;
248                 psz_cur2++;
249             }
250             if (!*psz_cur2) return p_pos;
251         }
252         p_pos++;
253     }
254     return NULL;
255 }
256
257 /*****************************************************************************
258  * sub_open: Open a subtitle file and add subtitle ES
259  *****************************************************************************/
260 static int  sub_open ( subtitle_demux_t *p_sub,
261                        input_thread_t  *p_input,
262                        char     *psz_name,
263                        mtime_t i_microsecperframe,
264                        int i_track_id )
265 {
266     text_t  txt;
267     vlc_value_t val;
268
269     int     i;
270     int     i_sub_type;
271     int     i_max;
272     int (*pf_read_subtitle)( text_t *, subtitle_t *, mtime_t ) = NULL;
273
274     p_sub->i_sub_type = SUB_TYPE_UNKNOWN;
275     p_sub->p_es = NULL;
276     p_sub->i_subtitles = 0;
277     p_sub->subtitle = NULL;
278     p_sub->p_input = p_input;
279
280     if( !psz_name || !*psz_name )
281     {
282         msg_Err( p_sub, "no subtitle file specified" );
283         if( psz_name ) free( psz_name );
284         return VLC_EGENERIC;
285     }
286
287     /* *** load the file *** */
288     if( text_load( &txt, psz_name ) )
289     {
290         msg_Err( p_sub, "cannot open `%s' subtitle file", psz_name );
291         free( psz_name );
292         return VLC_EGENERIC;
293     }
294     msg_Dbg( p_sub, "opened `%s'", psz_name );
295     free( psz_name );
296
297     var_Get( p_sub, "sub-fps", &val );
298     if( val.i_int >= 1.0 )
299     {
300         var_Get( p_sub, "sub-fps", &val );
301         i_microsecperframe = (mtime_t)( (float)1000000 / val.f_float );
302     }
303     else if( i_microsecperframe <= 0 )
304     {
305         i_microsecperframe = 40000; /* default: 25fps */
306     }
307
308     var_Get( p_sub, "sub-type", &val);
309     if( val.psz_string && *val.psz_string )
310     {
311         int i;
312
313         for( i = 0; ; i++ )
314         {
315             if( sub_read_subtitle_function[i].psz_type_name == NULL )
316             {
317                 i_sub_type = SUB_TYPE_UNKNOWN;
318                 break;
319             }
320             if( !strcmp( sub_read_subtitle_function[i].psz_type_name,
321                          val.psz_string ) )
322             {
323                 i_sub_type = sub_read_subtitle_function[i].i_type;
324                 break;
325             }
326         }
327     }
328     else
329     {
330         i_sub_type = SUB_TYPE_UNKNOWN;
331     }
332     FREE( val.psz_string );
333
334     /* *** Now try to autodetect subtitle format *** */
335     if( i_sub_type == SUB_TYPE_UNKNOWN )
336     {
337         int     i_try;
338         char    *s;
339
340         msg_Dbg( p_input, "trying to autodetect file format" );
341         for( i_try = 0; i_try < MAX_TRY; i_try++ )
342         {
343             int i_dummy;
344
345             if( ( s = text_get_line( &txt ) ) == NULL )
346             {
347                 break;
348             }
349
350             if( local_stristr( s, "<SAMI>" ) )
351             {
352                 i_sub_type = SUB_TYPE_SAMI;
353                 break;
354             }
355             else if( sscanf( s, "{%d}{%d}", &i_dummy, &i_dummy ) == 2 ||
356                      sscanf( s, "{%d}{}", &i_dummy ) == 1)
357             {
358                 i_sub_type = SUB_TYPE_MICRODVD;
359                 break;
360             }
361             else if( sscanf( s,
362                              "%d:%d:%d,%d --> %d:%d:%d,%d",
363                              &i_dummy,&i_dummy,&i_dummy,&i_dummy,
364                              &i_dummy,&i_dummy,&i_dummy,&i_dummy ) == 8 )
365             {
366                 i_sub_type = SUB_TYPE_SUBRIP;
367                 break;
368             }
369             else if( sscanf( s,
370                              "!: This is a Sub Station Alpha v%d.x script.",
371                              &i_dummy ) == 1)
372             {
373                 if( i_dummy <= 1 )
374                 {
375                     i_sub_type = SUB_TYPE_SSA1;
376                 }
377                 else
378                 {
379                     i_sub_type = SUB_TYPE_SSA2_4; /* I hope this will work */
380                 }
381                 break;
382             }
383             else if( local_stristr( s, "This is a Sub Station Alpha v4 script" ) )
384             {
385                 i_sub_type = SUB_TYPE_SSA2_4; /* I hope this will work */
386                 break;
387             }
388             else if( !strncasecmp( s, "Dialogue: Marked", 16  ) )
389             {
390                 i_sub_type = SUB_TYPE_SSA2_4; /* could be wrong */
391                 break;
392             }
393             else if( sscanf( s, "%d:%d:%d:", &i_dummy, &i_dummy, &i_dummy ) == 3 ||
394                      sscanf( s, "%d:%d:%d ", &i_dummy, &i_dummy, &i_dummy ) == 3 )
395             {
396                 i_sub_type = SUB_TYPE_VPLAYER;
397                 break;
398             }
399             else if( local_stristr( s, "# VobSub index file" ) )
400             {
401                 i_sub_type = SUB_TYPE_VOBSUB;
402                 break;
403             }
404         }
405
406         text_rewind( &txt );
407     }
408
409     /* *** Load this file in memory *** */
410     for( i = 0; ; i++ )
411     {
412         if( sub_read_subtitle_function[i].i_type == SUB_TYPE_UNKNOWN )
413         {
414             msg_Dbg( p_input, "unknown subtitle file" );
415             text_unload( &txt );
416             return VLC_EGENERIC;
417         }
418
419         if( sub_read_subtitle_function[i].i_type == i_sub_type )
420         {
421             msg_Dbg( p_input, "detected %s format",
422                     sub_read_subtitle_function[i].psz_name );
423             p_sub->i_sub_type = i_sub_type;
424             pf_read_subtitle = sub_read_subtitle_function[i].pf_read_subtitle;
425             break;
426         }
427     }
428
429     for( i_max = 0;; )
430     {
431         if( p_sub->i_subtitles >= i_max )
432         {
433             i_max += 128;
434             if( p_sub->subtitle )
435             {
436                 p_sub->subtitle = realloc( p_sub->subtitle,
437                                            sizeof( subtitle_t ) * i_max );
438             }
439             else
440             {
441                 p_sub->subtitle = malloc( sizeof( subtitle_t ) * i_max );
442             }
443         }
444         if( pf_read_subtitle( &txt,
445                               p_sub->subtitle + p_sub->i_subtitles,
446                               i_microsecperframe ) < 0 )
447         {
448             break;
449         }
450         p_sub->i_subtitles++;
451     }
452     msg_Dbg( p_sub, "loaded %d subtitles", p_sub->i_subtitles );
453
454     /* *** Close the file *** */
455     text_unload( &txt );
456
457     /* *** fix subtitle (order and time) *** */
458     p_sub->i_subtitle = 0;  /* will be modified by sub_fix */
459     sub_fix( p_sub );
460
461     /* *** add subtitle ES *** */
462     vlc_mutex_lock( &p_input->stream.stream_lock );
463     p_sub->p_es = input_AddES( p_input, p_input->stream.p_selected_program,
464                                0xff - i_track_id,    /* FIXME */
465                                SPU_ES, NULL, 0 );
466     vlc_mutex_unlock( &p_input->stream.stream_lock );
467
468     p_sub->p_es->i_stream_id = 0xff - i_track_id;    /* FIXME */
469
470     if( p_sub->i_sub_type != SUB_TYPE_VOBSUB )
471     {
472         p_sub->p_es->i_fourcc    = VLC_FOURCC( 's','u','b','t' );
473     }
474     else
475     {
476         p_sub->p_es->i_fourcc    = VLC_FOURCC( 's','p','u',' ' );
477         /* open vobsub file */
478     }
479
480     p_sub->i_previously_selected = 0;
481     return VLC_SUCCESS;
482 }
483
484 /*****************************************************************************
485  * sub_demux: Send subtitle to decoder until i_maxdate
486  *****************************************************************************/
487 static int  sub_demux( subtitle_demux_t *p_sub, mtime_t i_maxdate )
488 {
489     if( p_sub->p_es->p_decoder_fifo && !p_sub->i_previously_selected )
490     {
491         p_sub->i_previously_selected = 1;
492         p_sub->pf_seek( p_sub, i_maxdate );
493         return VLC_SUCCESS;
494     }
495     else if( !p_sub->p_es->p_decoder_fifo && p_sub->i_previously_selected )
496     {
497         p_sub->i_previously_selected = 0;
498         return VLC_SUCCESS;
499     }
500
501     while( p_sub->i_subtitle < p_sub->i_subtitles &&
502            p_sub->subtitle[p_sub->i_subtitle].i_start < i_maxdate )
503     {
504         pes_packet_t    *p_pes;
505         data_packet_t   *p_data;
506
507         int i_len;
508
509         i_len = strlen( p_sub->subtitle[p_sub->i_subtitle].psz_text ) + 1;
510
511         if( i_len <= 1 )
512         {
513             /* empty subtitle */
514             p_sub->i_subtitle++;
515             continue;
516         }
517         if( !( p_pes = input_NewPES( p_sub->p_input->p_method_data ) ) )
518         {
519             p_sub->i_subtitle++;
520             continue;
521         }
522
523         if( !( p_data = input_NewPacket( p_sub->p_input->p_method_data,
524                                          i_len ) ) )
525         {
526             input_DeletePES( p_sub->p_input->p_method_data, p_pes );
527             p_sub->i_subtitle++;
528             continue;
529         }
530         p_data->p_payload_end = p_data->p_payload_start + i_len;
531
532         p_pes->i_pts =
533             input_ClockGetTS( p_sub->p_input,
534                               p_sub->p_input->stream.p_selected_program,
535                               p_sub->subtitle[p_sub->i_subtitle].i_start*9/100);
536         if( p_sub->subtitle[p_sub->i_subtitle].i_stop > 0 )
537         {
538             /* FIXME kludge ...
539              * i_dts means end of display...
540              */
541             p_pes->i_dts =
542                 input_ClockGetTS( p_sub->p_input,
543                               p_sub->p_input->stream.p_selected_program,
544                               p_sub->subtitle[p_sub->i_subtitle].i_stop *9/100);
545         }
546         else
547         {
548             p_pes->i_dts = 0;
549         }
550
551         p_pes->i_nb_data = 1;
552         p_pes->p_first =
553             p_pes->p_last = p_data;
554         p_pes->i_pes_size = i_len;
555
556         memcpy( p_data->p_payload_start,
557                 p_sub->subtitle[p_sub->i_subtitle].psz_text,
558                 i_len );
559         if( p_sub->p_es->p_decoder_fifo && p_pes->i_pts > 0 )
560         {
561
562             input_DecodePES( p_sub->p_es->p_decoder_fifo, p_pes );
563         }
564         else
565         {
566             input_DeletePES( p_sub->p_input->p_method_data, p_pes );
567         }
568
569         p_sub->i_subtitle++;
570     }
571     return( 0 );
572 }
573
574 /*****************************************************************************
575  * sub_seek: Seek to i_date
576  *****************************************************************************/
577 static int  sub_seek ( subtitle_demux_t *p_sub, mtime_t i_date )
578 {
579     /* should be fast enough... */
580     p_sub->i_subtitle = 0;
581     while( p_sub->i_subtitle < p_sub->i_subtitles &&
582            p_sub->subtitle[p_sub->i_subtitle].i_start < i_date )
583     {
584         p_sub->i_subtitle++;
585     }
586
587     return( 0 );
588 }
589
590 /*****************************************************************************
591  * sub_close: Close subtitle demux
592  *****************************************************************************/
593 static void sub_close( subtitle_demux_t *p_sub )
594 {
595     if( p_sub->subtitle )
596     {
597         int i;
598         for( i = 0; i < p_sub->i_subtitles; i++ )
599         {
600             if( p_sub->subtitle[i].psz_text )
601             {
602                 free( p_sub->subtitle[i].psz_text );
603             }
604         }
605         free( p_sub->subtitle );
606     }
607 }
608 /*****************************************************************************
609  *
610  * sub_fix: fix time stamp and order of subtitle
611  *****************************************************************************/
612 static void  sub_fix( subtitle_demux_t *p_sub )
613 {
614     int     i;
615     mtime_t i_delay;
616     int     i_index;
617     int     i_done;
618     vlc_value_t val;
619
620     /* *** fix order (to be sure...) *** */
621     /* We suppose that there are near in order and this durty bubble sort
622      * wont take too much time
623      */
624     do
625     {
626         i_done = 1;
627         for( i_index = 1; i_index < p_sub->i_subtitles; i_index++ )
628         {
629             if( p_sub->subtitle[i_index].i_start <
630                     p_sub->subtitle[i_index - 1].i_start )
631             {
632                 subtitle_t sub_xch;
633                 memcpy( &sub_xch,
634                         p_sub->subtitle + i_index - 1,
635                         sizeof( subtitle_t ) );
636                 memcpy( p_sub->subtitle + i_index - 1,
637                         p_sub->subtitle + i_index,
638                         sizeof( subtitle_t ) );
639                 memcpy( p_sub->subtitle + i_index,
640                         &sub_xch,
641                         sizeof( subtitle_t ) );
642                 i_done = 0;
643             }
644         }
645     } while( !i_done );
646
647     /* *** and at the end add delay *** */
648     var_Get( p_sub, "sub-delay", &val );
649     i_delay = (mtime_t) val.i_int * 100000;
650     if( i_delay != 0 )
651     {
652         for( i = 0; i < p_sub->i_subtitles; i++ )
653         {
654             p_sub->subtitle[i].i_start += i_delay;
655             p_sub->subtitle[i].i_stop += i_delay;
656             if( p_sub->subtitle[i].i_start < 0 )
657             {
658                 p_sub->i_subtitle = i + 1;
659             }
660         }
661     }
662 }
663
664
665
666 /*****************************************************************************
667  * Specific Subtitle function
668  *****************************************************************************/
669 static int  sub_MicroDvdRead( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe)
670 {
671     /*
672      * each line:
673      *  {n1}{n2}Line1|Line2|Line3....
674      * where n1 and n2 are the video frame number...
675      *
676      */
677     char *s;
678
679     char buffer_text[MAX_LINE + 1];
680     uint32_t    i_start;
681     uint32_t    i_stop;
682     unsigned int i;
683
684     for( ;; )
685     {
686         if( ( s = text_get_line( txt ) ) == NULL )
687         {
688             return( VLC_EGENERIC );
689         }
690         i_start = 0;
691         i_stop  = 0;
692
693         memset( buffer_text, '\0', MAX_LINE );
694         if( sscanf( s, "{%d}{}%[^\r\n]", &i_start, buffer_text ) == 2 ||
695             sscanf( s, "{%d}{%d}%[^\r\n]", &i_start, &i_stop, buffer_text ) == 3)
696         {
697             break;
698         }
699     }
700     /* replace | by \n */
701     for( i = 0; i < strlen( buffer_text ); i++ )
702     {
703         if( buffer_text[i] == '|' )
704         {
705             buffer_text[i] = '\n';
706         }
707     }
708     p_subtitle->i_start = (mtime_t)i_start * (mtime_t)i_microsecperframe;
709     p_subtitle->i_stop  = (mtime_t)i_stop  * (mtime_t)i_microsecperframe;
710     p_subtitle->psz_text = strndup( buffer_text, MAX_LINE );
711     return( 0 );
712 }
713
714 static int  sub_SubRipRead( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe )
715 {
716     /*
717      * n
718      * h1:m1:s1,d1 --> h2:m2:s2,d2
719      * Line1
720      * Line2
721      * ...
722      * [empty line]
723      *
724      */
725     char *s;
726     char buffer_text[ 10 * MAX_LINE];
727     int  i_buffer_text;
728     mtime_t     i_start;
729     mtime_t     i_stop;
730
731     for( ;; )
732     {
733         int h1, m1, s1, d1, h2, m2, s2, d2;
734         if( ( s = text_get_line( txt ) ) == NULL )
735         {
736             return( VLC_EGENERIC );
737         }
738         if( sscanf( s,
739                     "%d:%d:%d,%d --> %d:%d:%d,%d",
740                     &h1, &m1, &s1, &d1,
741                     &h2, &m2, &s2, &d2 ) == 8 )
742         {
743             i_start = ( (mtime_t)h1 * 3600*1000 +
744                         (mtime_t)m1 * 60*1000 +
745                         (mtime_t)s1 * 1000 +
746                         (mtime_t)d1 ) * 1000;
747
748             i_stop  = ( (mtime_t)h2 * 3600*1000 +
749                         (mtime_t)m2 * 60*1000 +
750                         (mtime_t)s2 * 1000 +
751                         (mtime_t)d2 ) * 1000;
752
753             /* Now read text until an empty line */
754             for( i_buffer_text = 0;; )
755             {
756                 int i_len;
757                 if( ( s = text_get_line( txt ) ) == NULL )
758                 {
759                     return( VLC_EGENERIC );
760                 }
761
762                 i_len = strlen( s );
763                 if( i_len <= 1 )
764                 {
765                     // empty line -> end of this subtitle
766                     buffer_text[__MAX( i_buffer_text - 1, 0 )] = '\0';
767                     p_subtitle->i_start = i_start;
768                     p_subtitle->i_stop = i_stop;
769                     p_subtitle->psz_text = strdup( buffer_text );
770                     return( 0 );
771                 }
772                 else
773                 {
774                     if( i_buffer_text + i_len + 1 < 10 * MAX_LINE )
775                     {
776                         memcpy( buffer_text + i_buffer_text,
777                                 s,
778                                 i_len );
779                         i_buffer_text += i_len;
780
781                         buffer_text[i_buffer_text] = '\n';
782                         i_buffer_text++;
783                     }
784                 }
785             }
786         }
787     }
788 }
789
790
791 static int  sub_SSARead( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe, int i_comma_count )
792 {
793     char buffer_text[ 10 * MAX_LINE];
794     char *s;
795     char *p_buffer_text;
796     mtime_t     i_start;
797     mtime_t     i_stop;
798     int         i_comma;
799     int         i_text;
800
801     for( ;; )
802     {
803         int h1, m1, s1, c1, h2, m2, s2, c2;
804         int i_dummy;
805
806         if( ( s = text_get_line( txt ) ) == NULL )
807         {
808             return( VLC_EGENERIC );
809         }
810         if( sscanf( s,
811                     "Dialogue: Marked=%d,%d:%d:%d.%d,%d:%d:%d.%d,%[^\r\n]",
812                     &i_dummy,
813                     &h1, &m1, &s1, &c1,
814                     &h2, &m2, &s2, &c2,
815                     buffer_text ) == 10 )
816         {
817             i_start = ( (mtime_t)h1 * 3600*1000 +
818                         (mtime_t)m1 * 60*1000 +
819                         (mtime_t)s1 * 1000 +
820                         (mtime_t)c1 * 10 ) * 1000;
821
822             i_stop  = ( (mtime_t)h2 * 3600*1000 +
823                         (mtime_t)m2 * 60*1000 +
824                         (mtime_t)s2 * 1000 +
825                         (mtime_t)c2 * 10 ) * 1000;
826
827             p_buffer_text = buffer_text;
828             i_comma = 3;
829             while( i_comma < i_comma_count &&
830                    *p_buffer_text != '\0' )
831             {
832                 if( *p_buffer_text == ',' )
833                 {
834                     i_comma++;
835                 }
836                 p_buffer_text++;
837             }
838             p_subtitle->psz_text = malloc( strlen( p_buffer_text ) + 1);
839             i_text = 0;
840             while( p_buffer_text[0] != '\0' )
841             {
842                 if( p_buffer_text[0] == '\\' && ( p_buffer_text[1] =='n' || p_buffer_text[1] =='N' ) )
843                 {
844                     p_subtitle->psz_text[i_text] = '\n';
845                     i_text++;
846                     p_buffer_text += 2;
847                 }
848                 else if( p_buffer_text[0] == '{' && p_buffer_text[1] == '\\' )
849                 {
850                     /* SSA control code */
851                     while( p_buffer_text[0] != '\0' && p_buffer_text[0] != '}' )
852                     {
853                         p_buffer_text++;
854                     }
855                     p_buffer_text++;
856                 }
857                 else
858                 {
859                     p_subtitle->psz_text[i_text] = p_buffer_text[0];
860                     i_text++;
861                     p_buffer_text++;
862                 }
863             }
864             p_subtitle->psz_text[++i_text] = '\0';
865             p_subtitle->i_start = i_start;
866             p_subtitle->i_stop = i_stop;
867             return( 0 );
868         }
869     }
870 }
871
872 static int  sub_SSA1Read( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe )
873 {
874     return( sub_SSARead( txt, p_subtitle, i_microsecperframe, 8 ) );
875 }
876 static int  sub_SSA2_4Read( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe )
877 {
878     return( sub_SSARead( txt, p_subtitle, i_microsecperframe, 9 ) );
879 }
880
881 static int  sub_Vplayer( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe)
882 {
883     /*
884      * each line:
885      *  h:m:s:Line1|Line2|Line3....
886      *  or
887      *  h:m:s Line1|Line2|Line3....
888      * where n1 and n2 are the video frame number...
889      *
890      */
891     char *p;
892     char buffer_text[MAX_LINE + 1];
893     mtime_t    i_start;
894     unsigned int i;
895
896     for( ;; )
897     {
898         int h, m, s;
899         char c;
900
901         if( ( p = text_get_line( txt ) ) == NULL )
902         {
903             return( VLC_EGENERIC );
904         }
905
906         i_start = 0;
907
908         memset( buffer_text, '\0', MAX_LINE );
909         if( sscanf( p, "%d:%d:%d%[ :]%[^\r\n]", &h, &m, &s, &c, buffer_text ) == 5 )
910         {
911             i_start = ( (mtime_t)h * 3600*1000 +
912                         (mtime_t)m * 60*1000 +
913                         (mtime_t)s * 1000 ) * 1000;
914             break;
915         }
916     }
917
918     /* replace | by \n */
919     for( i = 0; i < strlen( buffer_text ); i++ )
920     {
921         if( buffer_text[i] == '|' )
922         {
923             buffer_text[i] = '\n';
924         }
925     }
926     p_subtitle->i_start = i_start;
927
928     p_subtitle->i_stop  = 0;
929     p_subtitle->psz_text = strndup( buffer_text, MAX_LINE );
930     return( 0 );
931 }
932
933 static char *sub_SamiSearch( text_t *txt, char *psz_start, char *psz_str )
934 {
935     if( psz_start )
936     {
937         if( local_stristr( psz_start, psz_str ) )
938         {
939             char *s = local_stristr( psz_start, psz_str );
940
941             s += strlen( psz_str );
942
943             return( s );
944         }
945     }
946     for( ;; )
947     {
948         char *p;
949         if( ( p = text_get_line( txt ) ) == NULL )
950         {
951             return NULL;
952         }
953         if( local_stristr( p, psz_str ) )
954         {
955             char *s = local_stristr( p, psz_str );
956
957             s += strlen( psz_str );
958
959             return(  s);
960         }
961     }
962 }
963
964 static int  sub_Sami( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe )
965 {
966     char *p;
967     int i_start;
968
969     int  i_text;
970     char buffer_text[10*MAX_LINE + 1];
971 #define ADDC( c ) \
972     if( i_text < 10*MAX_LINE )      \
973     {                               \
974         buffer_text[i_text++] = c;  \
975         buffer_text[i_text] = '\0'; \
976     }
977
978     /* search "Start=" */
979     if( !( p = sub_SamiSearch( txt, NULL, "Start=" ) ) )
980     {
981         return VLC_EGENERIC;
982     }
983
984     /* get start value */
985     i_start = strtol( p, &p, 0 );
986
987     /* search <P */
988     if( !( p = sub_SamiSearch( txt, p, "<P" ) ) )
989     {
990         return VLC_EGENERIC;
991     }
992     /* search > */
993     if( !( p = sub_SamiSearch( txt, p, ">" ) ) )
994     {
995         return VLC_EGENERIC;
996     }
997
998     i_text = 0;
999     buffer_text[0] = '\0';
1000     /* now get all txt until  a "Start=" line */
1001     for( ;; )
1002     {
1003         if( *p )
1004         {
1005             if( *p == '<' )
1006             {
1007                 if( !strncasecmp( p, "<br", 3 ) )
1008                 {
1009                     ADDC( '\n' );
1010                 }
1011                 else if( local_stristr( p, "Start=" ) )
1012                 {
1013                     text_previous_line( txt );
1014                     break;
1015                 }
1016                 p = sub_SamiSearch( txt, p, ">" );
1017             }
1018             else if( !strncmp( p, "&nbsp;", 6 ) )
1019             {
1020                 ADDC( ' ' );
1021                 p += 6;
1022             }
1023             else if( *p == '\t' )
1024             {
1025                 ADDC( ' ' );
1026                 p++;
1027             }
1028             else
1029             {
1030                 ADDC( *p );
1031                 p++;
1032             }
1033         }
1034         else
1035         {
1036             p = text_get_line( txt );
1037         }
1038
1039         if( p == NULL )
1040         {
1041             break;
1042         }
1043     }
1044
1045     p_subtitle->i_start = i_start * 1000;
1046     p_subtitle->i_stop  = 0;
1047     p_subtitle->psz_text = strndup( buffer_text, 10*MAX_LINE );
1048
1049     return( VLC_SUCCESS );
1050 #undef ADDC
1051 }
1052
1053 static int  sub_VobSub( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe)
1054 {
1055     /*
1056      * Parse the idx file. Each line:
1057      * timestamp: hh:mm:ss:mss, filepos: loc
1058      * hexint is the hex location of the vobsub in the .sub file
1059      *
1060      */
1061     char *p;
1062
1063     char buffer_text[MAX_LINE + 1];
1064     uint32_t    i_start, i_location;
1065
1066     for( ;; )
1067     {
1068         unsigned int h, m, s, ms, loc;
1069
1070         if( ( p = text_get_line( txt ) ) == NULL )
1071         {
1072             return( VLC_EGENERIC );
1073         }
1074         i_start = 0;
1075
1076         memset( buffer_text, '\0', MAX_LINE );
1077         if( sscanf( p, "timestamp: %d:%d:%d:%d, filepos: %x%[^\r\n]",
1078                     &h, &m, &s, &ms, &loc, buffer_text ) == 5 )
1079         {
1080             i_start = ( (mtime_t)h * 3600*1000 +
1081                         (mtime_t)m * 60*1000 +
1082                         (mtime_t)s * 1000 +
1083                         (mtime_t)ms ) * 1000;
1084             i_location = loc;
1085             break;
1086         }
1087     }
1088     p_subtitle->i_start = (mtime_t)i_start;
1089     p_subtitle->i_stop  = 0;
1090     p_subtitle->i_vobsub_location = i_location;
1091     fprintf( stderr, "time: %x, location: %x\n", i_start, i_location );
1092     return( 0 );
1093 }
1094
1095