]> git.sesse.net Git - vlc/blob - modules/demux/util/sub.c
* input: handle also SPU in es_out_Add (and fix a bad lock).
[vlc] / modules / demux / util / sub.c
1 /*****************************************************************************
2  * sub.c
3  *****************************************************************************
4  * Copyright (C) 1999-2003 VideoLAN
5  * $Id: sub.c,v 1.36 2003/11/13 13:31:12 fenrir 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 #include <codecs.h>
36
37 #include "sub.h"
38
39 #if (!defined( WIN32 ) || defined(__MINGW32__))
40 #    include <dirent.h>
41 #endif
42
43 static int  Open ( vlc_object_t *p_this );
44
45 static int  sub_open ( subtitle_demux_t *p_sub,
46                        input_thread_t  *p_input,
47                        char  *psz_name,
48                        mtime_t i_microsecperframe,
49                        int i_track_id );
50 static int  sub_demux( subtitle_demux_t *p_sub, mtime_t i_maxdate );
51 static int  sub_seek ( subtitle_demux_t *p_sub, mtime_t i_date );
52 static void sub_close( subtitle_demux_t *p_sub );
53
54 static void sub_fix( subtitle_demux_t *p_sub );
55
56 static char *ppsz_sub_type[] = { "auto", "microdvd", "subrip", "ssa1",
57   "ssa2-4", "vplayer", "sami", "vobsub" };
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( "sub-type", "auto", NULL, "subtitles type",
82                     SUB_TYPE_LONGTEXT, VLC_TRUE );
83             change_string_list( ppsz_sub_type, 0, 0 );
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( subtitle_demux_t *p_sub, text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe );
208 static int  sub_SubRipRead  ( subtitle_demux_t *p_sub, text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe );
209 static int  sub_SSARead     ( subtitle_demux_t *p_sub, text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe );
210 static int  sub_Vplayer     ( subtitle_demux_t *p_sub, text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe );
211 static int  sub_Sami        ( subtitle_demux_t *p_sub, text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe );
212 static int  sub_VobSub      ( subtitle_demux_t *p_sub, text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe );
213
214 static struct
215 {
216     char *psz_type_name;
217     int  i_type;
218     char *psz_name;
219     int  (*pf_read_subtitle)    ( subtitle_demux_t *, text_t *, subtitle_t*, mtime_t );
220 } sub_read_subtitle_function [] =
221 {
222     { "microdvd",   SUB_TYPE_MICRODVD,  "MicroDVD", sub_MicroDvdRead },
223     { "subrip",     SUB_TYPE_SUBRIP,    "SubRIP",   sub_SubRipRead },
224     { "ssa1",       SUB_TYPE_SSA1,      "SSA-1",    sub_SSARead },
225     { "ssa2-4",     SUB_TYPE_SSA2_4,    "SSA-2/3/4",sub_SSARead },
226     { "vplayer",    SUB_TYPE_VPLAYER,   "VPlayer",  sub_Vplayer },
227     { "sami",       SUB_TYPE_SAMI,      "SAMI",     sub_Sami },
228     { "vobsub",     SUB_TYPE_VOBSUB,    "VobSub",   sub_VobSub },
229     { NULL,         SUB_TYPE_UNKNOWN,   "Unknown",  NULL }
230 };
231
232 static char * local_stristr( char *psz_big, char *psz_little)
233 {
234     char *p_pos = psz_big;
235
236     if (!psz_big || !psz_little || !*psz_little) return psz_big;
237
238     while (*p_pos)
239     {
240         if (toupper(*p_pos) == toupper(*psz_little))
241         {
242             char * psz_cur1 = p_pos + 1;
243             char * psz_cur2 = psz_little + 1;
244             while (*psz_cur1 && *psz_cur2 && toupper(*psz_cur1) == toupper(*psz_cur2))
245             {
246                 psz_cur1++;
247                 psz_cur2++;
248             }
249             if (!*psz_cur2) return p_pos;
250         }
251         p_pos++;
252     }
253     return NULL;
254 }
255
256 /*****************************************************************************
257  * sub_open: Open a subtitle file and add subtitle ES
258  *****************************************************************************/
259 static int  sub_open ( subtitle_demux_t *p_sub,
260                        input_thread_t  *p_input,
261                        char     *psz_name,
262                        mtime_t i_microsecperframe,
263                        int i_track_id )
264 {
265     text_t  txt;
266     vlc_value_t val;
267     es_format_t  fmt;
268
269     int     i;
270     int     i_sub_type;
271     int     i_max;
272     int (*pf_read_subtitle)( subtitle_demux_t *, 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                 if( !( p_sub->subtitle = realloc( p_sub->subtitle,
437                                            sizeof( subtitle_t ) * i_max ) ) )
438                 {
439                     msg_Err( p_sub, "out of memory");
440                     return VLC_ENOMEM;
441                 }
442             }
443             else
444             {
445                 if( !(  p_sub->subtitle = malloc( sizeof( subtitle_t ) * i_max ) ) )
446                 {
447                     msg_Err( p_sub, "out of memory");
448                     return VLC_ENOMEM;
449                 }
450             }
451         }
452         if( pf_read_subtitle( p_sub, &txt,
453                               p_sub->subtitle + p_sub->i_subtitles,
454                               i_microsecperframe ) < 0 )
455         {
456             break;
457         }
458         p_sub->i_subtitles++;
459     }
460     msg_Dbg( p_sub, "loaded %d subtitles", p_sub->i_subtitles );
461
462     /* *** Close the file *** */
463     text_unload( &txt );
464
465     /* *** fix subtitle (order and time) *** */
466     p_sub->i_subtitle = 0;  /* will be modified by sub_fix */
467     if( p_sub->i_sub_type != SUB_TYPE_VOBSUB )
468     {
469         sub_fix( p_sub );
470     }
471
472     /* *** add subtitle ES *** */
473     if( p_sub->i_sub_type == SUB_TYPE_VOBSUB )
474     {
475         es_format_Init( &fmt, SPU_ES, VLC_FOURCC( 's','p','u',' ' ) );
476     }
477     else if( p_sub->i_sub_type == SUB_TYPE_SSA1 ||
478              p_sub->i_sub_type == SUB_TYPE_SSA2_4 )
479     {
480         es_format_Init( &fmt, SPU_ES, VLC_FOURCC( 's','s','a',' ' ) );
481     }
482     else
483     {
484         es_format_Init( &fmt, SPU_ES, VLC_FOURCC( 's','u','b','t' ) );
485     }
486     if( p_sub->psz_header != NULL )
487     {
488         fmt.i_extra_type = ES_EXTRA_TYPE_SUBHEADER;
489         fmt.i_extra = strlen( p_sub->psz_header ) + 1;
490         fmt.p_extra = strdup( p_sub->psz_header );
491     }
492     p_sub->p_es = es_out_Add( p_input->p_es_out, &fmt );
493
494     p_sub->i_previously_selected = 0;
495     return VLC_SUCCESS;
496 }
497
498 /*****************************************************************************
499  * sub_demux: Send subtitle to decoder until i_maxdate
500  *****************************************************************************/
501 static int  sub_demux( subtitle_demux_t *p_sub, mtime_t i_maxdate )
502 {
503     input_thread_t *p_input = p_sub->p_input;
504     vlc_bool_t     b;
505
506     es_out_Control( p_input->p_es_out, ES_OUT_GET_SELECT, p_sub->p_es, &b );
507     if( b && !p_sub->i_previously_selected )
508     {
509         p_sub->i_previously_selected = 1;
510         p_sub->pf_seek( p_sub, i_maxdate );
511         return VLC_SUCCESS;
512     }
513     else if( !b && p_sub->i_previously_selected )
514     {
515         p_sub->i_previously_selected = 0;
516         return VLC_SUCCESS;
517     }
518
519     while( p_sub->i_subtitle < p_sub->i_subtitles &&
520            p_sub->subtitle[p_sub->i_subtitle].i_start < i_maxdate )
521     {
522         pes_packet_t    *p_pes;
523         data_packet_t   *p_data;
524
525         int i_len;
526
527         i_len = strlen( p_sub->subtitle[p_sub->i_subtitle].psz_text ) + 1;
528
529         if( i_len <= 1 )
530         {
531             /* empty subtitle */
532             p_sub->i_subtitle++;
533             continue;
534         }
535         if( !( p_pes = input_NewPES( p_sub->p_input->p_method_data ) ) )
536         {
537             p_sub->i_subtitle++;
538             continue;
539         }
540
541         if( !( p_data = input_NewPacket( p_sub->p_input->p_method_data,
542                                          i_len ) ) )
543         {
544             input_DeletePES( p_sub->p_input->p_method_data, p_pes );
545             p_sub->i_subtitle++;
546             continue;
547         }
548         p_data->p_payload_end = p_data->p_payload_start + i_len;
549
550         p_pes->i_pts =
551             input_ClockGetTS( p_sub->p_input,
552                               p_sub->p_input->stream.p_selected_program,
553                               p_sub->subtitle[p_sub->i_subtitle].i_start*9/100);
554         if( p_sub->subtitle[p_sub->i_subtitle].i_stop > 0 )
555         {
556             /* FIXME kludge ...
557              * i_dts means end of display...
558              */
559             p_pes->i_dts =
560                 input_ClockGetTS( p_sub->p_input,
561                               p_sub->p_input->stream.p_selected_program,
562                               p_sub->subtitle[p_sub->i_subtitle].i_stop *9/100);
563         }
564         else
565         {
566             p_pes->i_dts = 0;
567         }
568
569         p_pes->i_nb_data = 1;
570         p_pes->p_first =
571         p_pes->p_last = p_data;
572         p_pes->i_pes_size = i_len;
573
574         memcpy( p_data->p_payload_start,
575                 p_sub->subtitle[p_sub->i_subtitle].psz_text,
576                 i_len );
577
578         if( p_pes->i_pts > 0 )
579         {
580             es_out_Send( p_input->p_es_out, p_sub->p_es, p_pes );
581         }
582         else
583         {
584             input_DeletePES( p_sub->p_input->p_method_data, p_pes );
585         }
586
587         p_sub->i_subtitle++;
588     }
589     return VLC_SUCCESS;
590 }
591
592 /*****************************************************************************
593  * sub_seek: Seek to i_date
594  *****************************************************************************/
595 static int  sub_seek ( subtitle_demux_t *p_sub, mtime_t i_date )
596 {
597     /* should be fast enough... */
598     p_sub->i_subtitle = 0;
599     while( p_sub->i_subtitle < p_sub->i_subtitles &&
600            p_sub->subtitle[p_sub->i_subtitle].i_start < i_date )
601     {
602         p_sub->i_subtitle++;
603     }
604     return( 0 );
605 }
606
607 /*****************************************************************************
608  * sub_close: Close subtitle demux
609  *****************************************************************************/
610 static void sub_close( subtitle_demux_t *p_sub )
611 {
612     if( p_sub->subtitle )
613     {
614         int i;
615         for( i = 0; i < p_sub->i_subtitles; i++ )
616         {
617             if( p_sub->subtitle[i].psz_text )
618             {
619                 free( p_sub->subtitle[i].psz_text );
620             }
621         }
622         free( p_sub->subtitle );
623     }
624 }
625
626 /*****************************************************************************
627  * sub_fix: fix time stamp and order of subtitle
628  *****************************************************************************/
629 static void  sub_fix( subtitle_demux_t *p_sub )
630 {
631     int     i;
632     mtime_t i_delay;
633     int     i_index;
634     int     i_done;
635     vlc_value_t val;
636
637     /* *** fix order (to be sure...) *** */
638     /* We suppose that there are near in order and this durty bubble sort
639      * wont take too much time
640      */
641     do
642     {
643         i_done = 1;
644         for( i_index = 1; i_index < p_sub->i_subtitles; i_index++ )
645         {
646             if( p_sub->subtitle[i_index].i_start <
647                     p_sub->subtitle[i_index - 1].i_start )
648             {
649                 subtitle_t sub_xch;
650                 memcpy( &sub_xch,
651                         p_sub->subtitle + i_index - 1,
652                         sizeof( subtitle_t ) );
653                 memcpy( p_sub->subtitle + i_index - 1,
654                         p_sub->subtitle + i_index,
655                         sizeof( subtitle_t ) );
656                 memcpy( p_sub->subtitle + i_index,
657                         &sub_xch,
658                         sizeof( subtitle_t ) );
659                 i_done = 0;
660             }
661         }
662     } while( !i_done );
663
664     /* *** and at the end add delay *** */
665     var_Get( p_sub, "sub-delay", &val );
666     i_delay = (mtime_t) val.i_int * 100000;
667     if( i_delay != 0 )
668     {
669         for( i = 0; i < p_sub->i_subtitles; i++ )
670         {
671             p_sub->subtitle[i].i_start += i_delay;
672             p_sub->subtitle[i].i_stop += i_delay;
673             if( p_sub->subtitle[i].i_start < 0 )
674             {
675                 p_sub->i_subtitle = i + 1;
676             }
677         }
678     }
679 }
680
681
682
683 /*****************************************************************************
684  * Specific Subtitle function
685  *****************************************************************************/
686 static int  sub_MicroDvdRead( subtitle_demux_t *p_sub, text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe)
687 {
688     /*
689      * each line:
690      *  {n1}{n2}Line1|Line2|Line3....
691      * where n1 and n2 are the video frame number...
692      *
693      */
694     char *s;
695
696     char buffer_text[MAX_LINE + 1];
697     uint32_t    i_start;
698     uint32_t    i_stop;
699     unsigned int i;
700
701     for( ;; )
702     {
703         if( ( s = text_get_line( txt ) ) == NULL )
704         {
705             return( VLC_EGENERIC );
706         }
707         i_start = 0;
708         i_stop  = 0;
709
710         memset( buffer_text, '\0', MAX_LINE );
711         if( sscanf( s, "{%d}{}%[^\r\n]", &i_start, buffer_text ) == 2 ||
712             sscanf( s, "{%d}{%d}%[^\r\n]", &i_start, &i_stop, buffer_text ) == 3)
713         {
714             break;
715         }
716     }
717     /* replace | by \n */
718     for( i = 0; i < strlen( buffer_text ); i++ )
719     {
720         if( buffer_text[i] == '|' )
721         {
722             buffer_text[i] = '\n';
723         }
724     }
725     p_subtitle->i_start = (mtime_t)i_start * (mtime_t)i_microsecperframe;
726     p_subtitle->i_stop  = (mtime_t)i_stop  * (mtime_t)i_microsecperframe;
727     p_subtitle->psz_text = strndup( buffer_text, MAX_LINE );
728     return( 0 );
729 }
730
731 static int  sub_SubRipRead( subtitle_demux_t *p_sub, text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe )
732 {
733     /*
734      * n
735      * h1:m1:s1,d1 --> h2:m2:s2,d2
736      * Line1
737      * Line2
738      * ...
739      * [empty line]
740      *
741      */
742     char *s;
743     char buffer_text[ 10 * MAX_LINE];
744     int  i_buffer_text;
745     mtime_t     i_start;
746     mtime_t     i_stop;
747
748     for( ;; )
749     {
750         int h1, m1, s1, d1, h2, m2, s2, d2;
751         if( ( s = text_get_line( txt ) ) == NULL )
752         {
753             return( VLC_EGENERIC );
754         }
755         if( sscanf( s,
756                     "%d:%d:%d,%d --> %d:%d:%d,%d",
757                     &h1, &m1, &s1, &d1,
758                     &h2, &m2, &s2, &d2 ) == 8 )
759         {
760             i_start = ( (mtime_t)h1 * 3600*1000 +
761                         (mtime_t)m1 * 60*1000 +
762                         (mtime_t)s1 * 1000 +
763                         (mtime_t)d1 ) * 1000;
764
765             i_stop  = ( (mtime_t)h2 * 3600*1000 +
766                         (mtime_t)m2 * 60*1000 +
767                         (mtime_t)s2 * 1000 +
768                         (mtime_t)d2 ) * 1000;
769
770             /* Now read text until an empty line */
771             for( i_buffer_text = 0;; )
772             {
773                 int i_len;
774                 if( ( s = text_get_line( txt ) ) == NULL )
775                 {
776                     return( VLC_EGENERIC );
777                 }
778
779                 i_len = strlen( s );
780                 if( i_len <= 1 )
781                 {
782                     /* empty line -> end of this subtitle */
783                     buffer_text[__MAX( i_buffer_text - 1, 0 )] = '\0';
784                     p_subtitle->i_start = i_start;
785                     p_subtitle->i_stop = i_stop;
786                     p_subtitle->psz_text = strdup( buffer_text );
787                     return( 0 );
788                 }
789                 else
790                 {
791                     if( i_buffer_text + i_len + 1 < 10 * MAX_LINE )
792                     {
793                         memcpy( buffer_text + i_buffer_text,
794                                 s,
795                                 i_len );
796                         i_buffer_text += i_len;
797
798                         buffer_text[i_buffer_text] = '\n';
799                         i_buffer_text++;
800                     }
801                 }
802             }
803         }
804     }
805 }
806
807
808 static int  sub_SSARead( subtitle_demux_t *p_sub, text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe )
809 {
810     char buffer_text[ 10 * MAX_LINE];
811     char *s;
812     mtime_t     i_start;
813     mtime_t     i_stop;
814
815     for( ;; )
816     {
817         int h1, m1, s1, c1, h2, m2, s2, c2;
818         int i_dummy;
819
820         if( ( s = text_get_line( txt ) ) == NULL )
821         {
822             return( VLC_EGENERIC );
823         }
824         p_subtitle->psz_text = malloc( strlen( s ) );
825
826         if( sscanf( s,
827                     "Dialogue: Marked=%d,%d:%d:%d.%d,%d:%d:%d.%d%[^\r\n]",
828                     &i_dummy,
829                     &h1, &m1, &s1, &c1,
830                     &h2, &m2, &s2, &c2,
831                     buffer_text ) == 10 )
832         {
833             i_start = ( (mtime_t)h1 * 3600*1000 +
834                         (mtime_t)m1 * 60*1000 +
835                         (mtime_t)s1 * 1000 +
836                         (mtime_t)c1 * 10 ) * 1000;
837
838             i_stop  = ( (mtime_t)h2 * 3600*1000 +
839                         (mtime_t)m2 * 60*1000 +
840                         (mtime_t)s2 * 1000 +
841                         (mtime_t)c2 * 10 ) * 1000;
842
843             /* The dec expects: ReadOrder, Layer, Style, Name, MarginL, MarginR, MarginV, Effect, Text */
844             if( p_sub->i_sub_type == SUB_TYPE_SSA1 )
845             {
846                 sprintf( p_subtitle->psz_text, ",%d%s", i_dummy, strdup( buffer_text) );
847             }
848             else
849             {
850                 sprintf( p_subtitle->psz_text, ",%d,%s", i_dummy, strdup( buffer_text) );
851             }
852             p_subtitle->i_start = i_start;
853             p_subtitle->i_stop = i_stop;
854             return( 0 );
855         }
856         else
857         {
858             /* All the other stuff we add to the header field */
859             if( p_sub->psz_header != NULL )
860             {
861                 if( !( p_sub->psz_header = realloc( p_sub->psz_header,
862                           strlen( p_sub->psz_header ) + strlen( s ) + 2 ) ) )
863                 {
864                     msg_Err( p_sub, "out of memory");
865                     return VLC_ENOMEM;
866                 }
867                 p_sub->psz_header = strcat( p_sub->psz_header, strdup( s ) );
868                 p_sub->psz_header = strcat( p_sub->psz_header, "\n" );
869             }
870             else
871             {
872                 if( !( p_sub->psz_header = malloc( strlen( s ) + 2 ) ) )
873                 {
874                     msg_Err( p_sub, "out of memory");
875                     return VLC_ENOMEM;
876                 }
877                 p_sub->psz_header = strdup( s );
878                 p_sub->psz_header = strcat( p_sub->psz_header, "\n" );
879             }
880         }
881     }
882 }
883
884 static int  sub_Vplayer( subtitle_demux_t *p_sub, text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe)
885 {
886     /*
887      * each line:
888      *  h:m:s:Line1|Line2|Line3....
889      *  or
890      *  h:m:s Line1|Line2|Line3....
891      * where n1 and n2 are the video frame number...
892      *
893      */
894     char *p;
895     char buffer_text[MAX_LINE + 1];
896     mtime_t    i_start;
897     unsigned int i;
898
899     for( ;; )
900     {
901         int h, m, s;
902         char c;
903
904         if( ( p = text_get_line( txt ) ) == NULL )
905         {
906             return( VLC_EGENERIC );
907         }
908
909         i_start = 0;
910
911         memset( buffer_text, '\0', MAX_LINE );
912         if( sscanf( p, "%d:%d:%d%[ :]%[^\r\n]", &h, &m, &s, &c, buffer_text ) == 5 )
913         {
914             i_start = ( (mtime_t)h * 3600*1000 +
915                         (mtime_t)m * 60*1000 +
916                         (mtime_t)s * 1000 ) * 1000;
917             break;
918         }
919     }
920
921     /* replace | by \n */
922     for( i = 0; i < strlen( buffer_text ); i++ )
923     {
924         if( buffer_text[i] == '|' )
925         {
926             buffer_text[i] = '\n';
927         }
928     }
929     p_subtitle->i_start = i_start;
930
931     p_subtitle->i_stop  = 0;
932     p_subtitle->psz_text = strndup( buffer_text, MAX_LINE );
933     return( 0 );
934 }
935
936 static char *sub_SamiSearch( text_t *txt, char *psz_start, char *psz_str )
937 {
938     if( psz_start )
939     {
940         if( local_stristr( psz_start, psz_str ) )
941         {
942             char *s = local_stristr( psz_start, psz_str );
943
944             s += strlen( psz_str );
945
946             return( s );
947         }
948     }
949     for( ;; )
950     {
951         char *p;
952         if( ( p = text_get_line( txt ) ) == NULL )
953         {
954             return NULL;
955         }
956         if( local_stristr( p, psz_str ) )
957         {
958             char *s = local_stristr( p, psz_str );
959
960             s += strlen( psz_str );
961
962             return(  s);
963         }
964     }
965 }
966
967 static int  sub_Sami( subtitle_demux_t *p_sub, text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe )
968 {
969     char *p;
970     int i_start;
971
972     int  i_text;
973     char buffer_text[10*MAX_LINE + 1];
974 #define ADDC( c ) \
975     if( i_text < 10*MAX_LINE )      \
976     {                               \
977         buffer_text[i_text++] = c;  \
978         buffer_text[i_text] = '\0'; \
979     }
980
981     /* search "Start=" */
982     if( !( p = sub_SamiSearch( txt, NULL, "Start=" ) ) )
983     {
984         return VLC_EGENERIC;
985     }
986
987     /* get start value */
988     i_start = strtol( p, &p, 0 );
989
990     /* search <P */
991     if( !( p = sub_SamiSearch( txt, p, "<P" ) ) )
992     {
993         return VLC_EGENERIC;
994     }
995     /* search > */
996     if( !( p = sub_SamiSearch( txt, p, ">" ) ) )
997     {
998         return VLC_EGENERIC;
999     }
1000
1001     i_text = 0;
1002     buffer_text[0] = '\0';
1003     /* now get all txt until  a "Start=" line */
1004     for( ;; )
1005     {
1006         if( *p )
1007         {
1008             if( *p == '<' )
1009             {
1010                 if( !strncasecmp( p, "<br", 3 ) )
1011                 {
1012                     ADDC( '\n' );
1013                 }
1014                 else if( local_stristr( p, "Start=" ) )
1015                 {
1016                     text_previous_line( txt );
1017                     break;
1018                 }
1019                 p = sub_SamiSearch( txt, p, ">" );
1020             }
1021             else if( !strncmp( p, "&nbsp;", 6 ) )
1022             {
1023                 ADDC( ' ' );
1024                 p += 6;
1025             }
1026             else if( *p == '\t' )
1027             {
1028                 ADDC( ' ' );
1029                 p++;
1030             }
1031             else
1032             {
1033                 ADDC( *p );
1034                 p++;
1035             }
1036         }
1037         else
1038         {
1039             p = text_get_line( txt );
1040         }
1041
1042         if( p == NULL )
1043         {
1044             break;
1045         }
1046     }
1047
1048     p_subtitle->i_start = i_start * 1000;
1049     p_subtitle->i_stop  = 0;
1050     p_subtitle->psz_text = strndup( buffer_text, 10*MAX_LINE );
1051
1052     return( VLC_SUCCESS );
1053 #undef ADDC
1054 }
1055
1056 static int  sub_VobSub( subtitle_demux_t *p_sub, text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe)
1057 {
1058     /*
1059      * Parse the idx file. Each line:
1060      * timestamp: hh:mm:ss:mss, filepos: loc
1061      * hexint is the hex location of the vobsub in the .sub file
1062      *
1063      */
1064     char *p;
1065
1066     char buffer_text[MAX_LINE + 1];
1067     uint32_t    i_start, i_location;
1068
1069     for( ;; )
1070     {
1071         unsigned int h, m, s, ms, loc;
1072
1073         if( ( p = text_get_line( txt ) ) == NULL )
1074         {
1075             return( VLC_EGENERIC );
1076         }
1077         i_start = 0;
1078
1079         memset( buffer_text, '\0', MAX_LINE );
1080         if( sscanf( p, "timestamp: %d:%d:%d:%d, filepos: %x%[^\r\n]",
1081                     &h, &m, &s, &ms, &loc, buffer_text ) == 5 )
1082         {
1083             i_start = ( (mtime_t)h * 3600*1000 +
1084                         (mtime_t)m * 60*1000 +
1085                         (mtime_t)s * 1000 +
1086                         (mtime_t)ms ) * 1000;
1087             i_location = loc;
1088             break;
1089         }
1090     }
1091     p_subtitle->i_start = (mtime_t)i_start;
1092     p_subtitle->i_stop  = 0;
1093     p_subtitle->i_vobsub_location = i_location;
1094     fprintf( stderr, "time: %x, location: %x\n", i_start, i_location );
1095     return( 0 );
1096 }
1097
1098