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