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