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