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