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