* modules/codec/subsdec/subsdec.c: reordered the list of encodings. Should
[vlc.git] / modules / demux / util / sub.c
1 /*****************************************************************************
2  * sub.c
3  *****************************************************************************
4  * Copyright (C) 1999-2003 VideoLAN
5  * $Id: sub.c,v 1.21 2003/08/23 19:20:29 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                 for (i = 0; ppsz_sub_exts[i]; i++) {
298                     if( strcmp( ppsz_sub_exts[i], 1 + strrchr( p_dir_afile->d_name, '.' ) ) == 0 ) {
299                         i_found = 1;
300                         break;
301                     }
302                 }
303                 if( i_found )
304                 {
305                     msg_Dbg( p_sub, "autodetected subtitlefile: %s", strdup( p_dir_afile->d_name ) );
306                     if( psz_dir )
307                     {
308                         psz_result = (char*)malloc( i_dirlen + strlen( p_dir_afile->d_name ) +1 );
309                         strncpy( psz_result, psz_dir, i_dirlen );
310                         char *psz_append = psz_result + i_dirlen;
311                         strncpy( psz_append, p_dir_afile->d_name, strlen( p_dir_afile->d_name ) );
312                         psz_result[i_dirlen + strlen( p_dir_afile->d_name )] = '\0';
313                         return psz_result;
314                     }
315                     else return strdup( p_dir_afile->d_name );
316                 }
317             }
318             closedir( p_dir_handle );
319         }
320         
321     }
322     return "";
323 }
324
325 /*****************************************************************************
326  * sub_open: Open a subtitle file and add subtitle ES
327  *****************************************************************************/
328 static int  sub_open ( subtitle_demux_t *p_sub,
329                        input_thread_t  *p_input,
330                        char     *psz_name,
331                        mtime_t i_microsecperframe )
332 {
333     text_t  txt;
334     vlc_value_t val;
335
336     int     i;
337     int     i_sub_type;
338     int     i_max;
339     int (*pf_read_subtitle)( text_t *, subtitle_t *, mtime_t ) = NULL;
340
341     p_sub->i_sub_type = SUB_TYPE_UNKNOWN;
342     p_sub->p_es = NULL;
343     p_sub->i_subtitles = 0;
344     p_sub->subtitle = NULL;
345     p_sub->p_input = p_input;
346
347     if( !psz_name || !*psz_name)
348     {
349         msg_Dbg( p_sub, "variable psz_name empty" );
350         var_Get( p_sub, "sub-file", &val );
351         if( !val.psz_string || !*val.psz_string )
352         {
353             msg_Dbg( p_sub, "variable sub-file empty too" );
354             var_Get( p_sub, "sub-autodetect-file", &val );
355             if( val.b_bool )
356             {
357                 msg_Dbg( p_sub, "lets try autodetect" );
358                 psz_name = strdup( sub_detect( p_sub, p_input->psz_source));
359             }
360             else
361             {
362                 if( val.psz_string ) free( val.psz_string );
363                 return VLC_EGENERIC;
364             }
365         }
366         else
367         {
368             psz_name = strdup( val.psz_string );
369             if( val.psz_string ) free( val.psz_string );
370         }
371     }
372     
373     /* *** load the file *** */
374     if( text_load( &txt, psz_name ) )
375     {
376         msg_Err( p_sub, "cannot open `%s' subtitle file", psz_name );
377         free( psz_name );
378         return VLC_EGENERIC;
379     }
380     msg_Dbg( p_sub, "opened `%s'", psz_name );
381     free( psz_name );
382
383     var_Get( p_sub, "sub-fps", &val );
384     if( val.i_int >= 1.0 )
385     {
386         var_Get( p_sub, "sub-fps", &val );
387         i_microsecperframe = (mtime_t)( (float)1000000 / val.f_float );
388     }
389     else if( i_microsecperframe <= 0 )
390     {
391         i_microsecperframe = 40000; /* default: 25fps */
392     }
393
394     var_Get( p_sub, "sub-type", &val);
395     if( val.psz_string && *val.psz_string )
396     {
397         int i;
398
399         for( i = 0; ; i++ )
400         {
401             if( sub_read_subtitle_function[i].psz_type_name == NULL )
402             {
403                 i_sub_type = SUB_TYPE_UNKNOWN;
404                 break;
405             }
406             if( !strcmp( sub_read_subtitle_function[i].psz_type_name,
407                          val.psz_string ) )
408             {
409                 i_sub_type = sub_read_subtitle_function[i].i_type;
410                 break;
411             }
412         }
413     }
414     else
415     {
416         i_sub_type = SUB_TYPE_UNKNOWN;
417     }
418     FREE( val.psz_string );
419
420     /* *** Now try to autodetect subtitle format *** */
421     if( i_sub_type == SUB_TYPE_UNKNOWN )
422     {
423         int     i_try;
424         char    *s;
425
426         msg_Dbg( p_input, "trying to autodetect file format" );
427         for( i_try = 0; i_try < MAX_TRY; i_try++ )
428         {
429             int i_dummy;
430
431             if( ( s = text_get_line( &txt ) ) == NULL )
432             {
433                 break;
434             }
435
436             if( strstr( s, "<SAMI>" ) )
437             {
438                 i_sub_type = SUB_TYPE_SAMI;
439                 break;
440             }
441             else if( sscanf( s, "{%d}{%d}", &i_dummy, &i_dummy ) == 2 ||
442                      sscanf( s, "{%d}{}", &i_dummy ) == 1)
443             {
444                 i_sub_type = SUB_TYPE_MICRODVD;
445                 break;
446             }
447             else if( sscanf( s,
448                              "%d:%d:%d,%d --> %d:%d:%d,%d",
449                              &i_dummy,&i_dummy,&i_dummy,&i_dummy,
450                              &i_dummy,&i_dummy,&i_dummy,&i_dummy ) == 8 )
451             {
452                 i_sub_type = SUB_TYPE_SUBRIP;
453                 break;
454             }
455             else if( sscanf( s,
456                              "!: This is a Sub Station Alpha v%d.x script.",
457                              &i_dummy ) == 1)
458             {
459                 if( i_dummy <= 1 )
460                 {
461                     i_sub_type = SUB_TYPE_SSA1;
462                 }
463                 else
464                 {
465                     i_sub_type = SUB_TYPE_SSA2_4; // I hop this will work
466                 }
467                 break;
468             }
469             else if( strstr( s, "This is a Sub Station Alpha v4 script" ) )
470             {
471                 i_sub_type = SUB_TYPE_SSA2_4; // I hop this will work
472             }
473             else if( !strncmp( s, "Dialogue: Marked", 16  ) )
474             {
475                 i_sub_type = SUB_TYPE_SSA2_4; // could be wrong
476                 break;
477             }
478             else if( sscanf( s, "%d:%d:%d:", &i_dummy, &i_dummy, &i_dummy ) == 3 ||
479                      sscanf( s, "%d:%d:%d ", &i_dummy, &i_dummy, &i_dummy ) == 3 )
480             {
481                 i_sub_type = SUB_TYPE_VPLAYER;
482                 break;
483             }
484         }
485
486         text_rewind( &txt );
487     }
488
489     /* *** Load this file in memory *** */
490     for( i = 0; ; i++ )
491     {
492         if( sub_read_subtitle_function[i].i_type == SUB_TYPE_UNKNOWN )
493         {
494             msg_Dbg( p_input, "unknown subtitle file" );
495             text_unload( &txt );
496             return VLC_EGENERIC;
497         }
498
499         if( sub_read_subtitle_function[i].i_type == i_sub_type )
500         {
501             msg_Dbg( p_input, "detected %s format",
502                     sub_read_subtitle_function[i].psz_name );
503             pf_read_subtitle = sub_read_subtitle_function[i].pf_read_subtitle;
504             break;
505         }
506     }
507
508     for( i_max = 0;; )
509     {
510         if( p_sub->i_subtitles >= i_max )
511         {
512             i_max += 128;
513             if( p_sub->subtitle )
514             {
515                 p_sub->subtitle = realloc( p_sub->subtitle,
516                                            sizeof( subtitle_t ) * i_max );
517             }
518             else
519             {
520                 p_sub->subtitle = malloc( sizeof( subtitle_t ) * i_max );
521             }
522         }
523         if( pf_read_subtitle( &txt,
524                               p_sub->subtitle + p_sub->i_subtitles,
525                               i_microsecperframe ) < 0 )
526         {
527             break;
528         }
529         p_sub->i_subtitles++;
530     }
531     msg_Dbg( p_sub, "loaded %d subtitles", p_sub->i_subtitles );
532
533     /* *** Close the file *** */
534     text_unload( &txt );
535
536     /* *** fix subtitle (order and time) *** */
537     p_sub->i_subtitle = 0;  // will be modified by sub_fix
538     sub_fix( p_sub );
539
540     /* *** add subtitle ES *** */
541     vlc_mutex_lock( &p_input->stream.stream_lock );
542     p_sub->p_es = input_AddES( p_input, p_input->stream.p_selected_program,
543                                0xff,    // FIXME
544                                SPU_ES, NULL, 0 );
545     vlc_mutex_unlock( &p_input->stream.stream_lock );
546
547     p_sub->p_es->i_stream_id = 0xff;    // FIXME
548     p_sub->p_es->i_fourcc    = VLC_FOURCC( 's','u','b','t' );
549
550     p_sub->i_previously_selected = 0;
551     return VLC_SUCCESS;
552 }
553
554 /*****************************************************************************
555  * sub_demux: Send subtitle to decoder until i_maxdate
556  *****************************************************************************/
557 static int  sub_demux( subtitle_demux_t *p_sub, mtime_t i_maxdate )
558 {
559     if( p_sub->p_es->p_decoder_fifo && !p_sub->i_previously_selected )
560     {
561         p_sub->i_previously_selected = 1;
562         p_sub->pf_seek( p_sub, i_maxdate );
563         return VLC_SUCCESS;
564     }
565     else if( !p_sub->p_es->p_decoder_fifo && p_sub->i_previously_selected )
566     {
567         p_sub->i_previously_selected = 0;
568         return VLC_SUCCESS;
569     }
570
571     while( p_sub->i_subtitle < p_sub->i_subtitles &&
572            p_sub->subtitle[p_sub->i_subtitle].i_start < i_maxdate )
573     {
574         pes_packet_t    *p_pes;
575         data_packet_t   *p_data;
576
577         int i_len;
578
579         i_len = strlen( p_sub->subtitle[p_sub->i_subtitle].psz_text ) + 1;
580
581         if( i_len <= 1 )
582         {
583             /* empty subtitle */
584             p_sub->i_subtitle++;
585             continue;
586         }
587         if( !( p_pes = input_NewPES( p_sub->p_input->p_method_data ) ) )
588         {
589             p_sub->i_subtitle++;
590             continue;
591         }
592
593         if( !( p_data = input_NewPacket( p_sub->p_input->p_method_data,
594                                          i_len ) ) )
595         {
596             input_DeletePES( p_sub->p_input->p_method_data, p_pes );
597             p_sub->i_subtitle++;
598             continue;
599         }
600         p_data->p_payload_end = p_data->p_payload_start + i_len;
601
602         p_pes->i_pts =
603             input_ClockGetTS( p_sub->p_input,
604                               p_sub->p_input->stream.p_selected_program,
605                               p_sub->subtitle[p_sub->i_subtitle].i_start*9/100);
606         if( p_sub->subtitle[p_sub->i_subtitle].i_stop > 0 )
607         {
608             /* FIXME kludge ...
609              * i_dts means end of display...
610              */
611             p_pes->i_dts =
612                 input_ClockGetTS( p_sub->p_input,
613                               p_sub->p_input->stream.p_selected_program,
614                               p_sub->subtitle[p_sub->i_subtitle].i_stop *9/100);
615         }
616         else
617         {
618             p_pes->i_dts = 0;
619         }
620
621         p_pes->i_nb_data = 1;
622         p_pes->p_first =
623             p_pes->p_last = p_data;
624         p_pes->i_pes_size = i_len;
625
626         memcpy( p_data->p_payload_start,
627                 p_sub->subtitle[p_sub->i_subtitle].psz_text,
628                 i_len );
629         if( p_sub->p_es->p_decoder_fifo && p_pes->i_pts > 0 )
630         {
631
632             input_DecodePES( p_sub->p_es->p_decoder_fifo, p_pes );
633         }
634         else
635         {
636             input_DeletePES( p_sub->p_input->p_method_data, p_pes );
637         }
638
639         p_sub->i_subtitle++;
640     }
641     return( 0 );
642 }
643
644 /*****************************************************************************
645  * sub_seek: Seek to i_date
646  *****************************************************************************/
647 static int  sub_seek ( subtitle_demux_t *p_sub, mtime_t i_date )
648 {
649     /* should be fast enough... */
650     p_sub->i_subtitle = 0;
651     while( p_sub->i_subtitle < p_sub->i_subtitles &&
652            p_sub->subtitle[p_sub->i_subtitle].i_start < i_date )
653     {
654         p_sub->i_subtitle++;
655     }
656
657     return( 0 );
658 }
659
660 /*****************************************************************************
661  * sub_close: Close subtitle demux
662  *****************************************************************************/
663 static void sub_close( subtitle_demux_t *p_sub )
664 {
665     if( p_sub->subtitle )
666     {
667         int i;
668         for( i = 0; i < p_sub->i_subtitles; i++ )
669         {
670             if( p_sub->subtitle[i].psz_text )
671             {
672                 free( p_sub->subtitle[i].psz_text );
673             }
674         }
675         free( p_sub->subtitle );
676     }
677 }
678 /*****************************************************************************
679  *
680  * sub_fix: fix time stamp and order of subtitle
681  *****************************************************************************/
682 static void  sub_fix( subtitle_demux_t *p_sub )
683 {
684     int     i;
685     mtime_t i_delay;
686     int     i_index;
687     int     i_done;
688     vlc_value_t val;
689
690     /* *** fix order (to be sure...) *** */
691     /* We suppose that there are near in order and this durty bubble sort
692      * wont take too much time
693      */
694     do
695     {
696         i_done = 1;
697         for( i_index = 1; i_index < p_sub->i_subtitles; i_index++ )
698         {
699             if( p_sub->subtitle[i_index].i_start <
700                     p_sub->subtitle[i_index - 1].i_start )
701             {
702                 subtitle_t sub_xch;
703                 memcpy( &sub_xch,
704                         p_sub->subtitle + i_index - 1,
705                         sizeof( subtitle_t ) );
706                 memcpy( p_sub->subtitle + i_index - 1,
707                         p_sub->subtitle + i_index,
708                         sizeof( subtitle_t ) );
709                 memcpy( p_sub->subtitle + i_index,
710                         &sub_xch,
711                         sizeof( subtitle_t ) );
712                 i_done = 0;
713             }
714         }
715     } while( !i_done );
716
717     /* *** and at the end add delay *** */
718     var_Get( p_sub, "sub-delay", &val );
719     i_delay = (mtime_t) val.i_int * 100000;
720     if( i_delay != 0 )
721     {
722         for( i = 0; i < p_sub->i_subtitles; i++ )
723         {
724             p_sub->subtitle[i].i_start += i_delay;
725             p_sub->subtitle[i].i_stop += i_delay;
726             if( p_sub->subtitle[i].i_start < 0 )
727             {
728                 p_sub->i_subtitle = i + 1;
729             }
730         }
731     }
732 }
733
734
735
736 /*****************************************************************************
737  * Specific Subtitle function
738  *****************************************************************************/
739 static int  sub_MicroDvdRead( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe)
740 {
741     /*
742      * each line:
743      *  {n1}{n2}Line1|Line2|Line3....
744      * where n1 and n2 are the video frame number...
745      *
746      */
747     char *s;
748
749     char buffer_text[MAX_LINE + 1];
750     uint32_t    i_start;
751     uint32_t    i_stop;
752     unsigned int i;
753
754     for( ;; )
755     {
756         if( ( s = text_get_line( txt ) ) == NULL )
757         {
758             return( VLC_EGENERIC );
759         }
760         i_start = 0;
761         i_stop  = 0;
762
763         memset( buffer_text, '\0', MAX_LINE );
764         if( sscanf( s, "{%d}{}%[^\r\n]", &i_start, buffer_text ) == 2 ||
765             sscanf( s, "{%d}{%d}%[^\r\n]", &i_start, &i_stop, buffer_text ) == 3)
766         {
767             break;
768         }
769     }
770     /* replace | by \n */
771     for( i = 0; i < strlen( buffer_text ); i++ )
772     {
773         if( buffer_text[i] == '|' )
774         {
775             buffer_text[i] = '\n';
776         }
777     }
778     p_subtitle->i_start = (mtime_t)i_start * (mtime_t)i_microsecperframe;
779     p_subtitle->i_stop  = (mtime_t)i_stop  * (mtime_t)i_microsecperframe;
780     p_subtitle->psz_text = strndup( buffer_text, MAX_LINE );
781     return( 0 );
782 }
783
784 static int  sub_SubRipRead( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe )
785 {
786     /*
787      * n
788      * h1:m1:s1,d1 --> h2:m2:s2,d2
789      * Line1
790      * Line2
791      * ...
792      * [empty line]
793      *
794      */
795     char *s;
796     char buffer_text[ 10 * MAX_LINE];
797     int  i_buffer_text;
798     mtime_t     i_start;
799     mtime_t     i_stop;
800
801     for( ;; )
802     {
803         int h1, m1, s1, d1, h2, m2, s2, d2;
804         if( ( s = text_get_line( txt ) ) == NULL )
805         {
806             return( VLC_EGENERIC );
807         }
808         if( sscanf( s,
809                     "%d:%d:%d,%d --> %d:%d:%d,%d",
810                     &h1, &m1, &s1, &d1,
811                     &h2, &m2, &s2, &d2 ) == 8 )
812         {
813             i_start = ( (mtime_t)h1 * 3600*1000 +
814                         (mtime_t)m1 * 60*1000 +
815                         (mtime_t)s1 * 1000 +
816                         (mtime_t)d1 ) * 1000;
817
818             i_stop  = ( (mtime_t)h2 * 3600*1000 +
819                         (mtime_t)m2 * 60*1000 +
820                         (mtime_t)s2 * 1000 +
821                         (mtime_t)d2 ) * 1000;
822
823             /* Now read text until an empty line */
824             for( i_buffer_text = 0;; )
825             {
826                 int i_len;
827                 if( ( s = text_get_line( txt ) ) == NULL )
828                 {
829                     return( VLC_EGENERIC );
830                 }
831
832                 i_len = strlen( s );
833                 if( i_len <= 1 )
834                 {
835                     // empty line -> end of this subtitle
836                     buffer_text[__MAX( i_buffer_text - 1, 0 )] = '\0';
837                     p_subtitle->i_start = i_start;
838                     p_subtitle->i_stop = i_stop;
839                     p_subtitle->psz_text = strdup( buffer_text );
840                     return( 0 );
841                 }
842                 else
843                 {
844                     if( i_buffer_text + i_len + 1 < 10 * MAX_LINE )
845                     {
846                         memcpy( buffer_text + i_buffer_text,
847                                 s,
848                                 i_len );
849                         i_buffer_text += i_len;
850
851                         buffer_text[i_buffer_text] = '\n';
852                         i_buffer_text++;
853                     }
854                 }
855             }
856         }
857     }
858 }
859
860
861 static int  sub_SSARead( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe, int i_comma_count )
862 {
863     char buffer_text[ 10 * MAX_LINE];
864     char *s;
865     char *p_buffer_text;
866     mtime_t     i_start;
867     mtime_t     i_stop;
868     int         i_comma;
869     int         i_text;
870
871     for( ;; )
872     {
873         int h1, m1, s1, c1, h2, m2, s2, c2;
874         int i_dummy;
875
876         if( ( s = text_get_line( txt ) ) == NULL )
877         {
878             return( VLC_EGENERIC );
879         }
880         if( sscanf( s,
881                     "Dialogue: Marked=%d,%d:%d:%d.%d,%d:%d:%d.%d,%[^\r\n]",
882                     &i_dummy,
883                     &h1, &m1, &s1, &c1,
884                     &h2, &m2, &s2, &c2,
885                     buffer_text ) == 10 )
886         {
887             i_start = ( (mtime_t)h1 * 3600*1000 +
888                         (mtime_t)m1 * 60*1000 +
889                         (mtime_t)s1 * 1000 +
890                         (mtime_t)c1 * 10 ) * 1000;
891
892             i_stop  = ( (mtime_t)h2 * 3600*1000 +
893                         (mtime_t)m2 * 60*1000 +
894                         (mtime_t)s2 * 1000 +
895                         (mtime_t)c2 * 10 ) * 1000;
896
897             p_buffer_text = buffer_text;
898             i_comma = 3;
899             while( i_comma < i_comma_count &&
900                    *p_buffer_text != '\0' )
901             {
902                 if( *p_buffer_text == ',' )
903                 {
904                     i_comma++;
905                 }
906                 p_buffer_text++;
907             }
908             p_subtitle->psz_text = malloc( strlen( p_buffer_text ) + 1);
909             i_text = 0;
910             while( *p_buffer_text )
911             {
912                 if( *p_buffer_text == '\\' && ( *p_buffer_text =='n' || *p_buffer_text =='N' ) )
913                 {
914                     p_subtitle->psz_text[i_text] = '\n';
915                     i_text++;
916                     p_buffer_text += 2;
917                 }
918                 else if( *p_buffer_text == '{' && *p_buffer_text == '\\')
919                 {
920                     while( *p_buffer_text && *p_buffer_text != '}' )
921                     {
922                         p_buffer_text++;
923                     }
924                 }
925                 else
926                 {
927                     p_subtitle->psz_text[i_text] = *p_buffer_text;
928                     i_text++;
929                     p_buffer_text++;
930                 }
931             }
932             p_subtitle->psz_text[i_text] = '\0';
933             p_subtitle->i_start = i_start;
934             p_subtitle->i_stop = i_stop;
935             return( 0 );
936         }
937     }
938 }
939
940 static int  sub_SSA1Read( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe )
941 {
942     return( sub_SSARead( txt, p_subtitle, i_microsecperframe, 8 ) );
943 }
944 static int  sub_SSA2_4Read( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe )
945 {
946     return( sub_SSARead( txt, p_subtitle, i_microsecperframe, 9 ) );
947 }
948
949 static int  sub_Vplayer( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe)
950 {
951     /*
952      * each line:
953      *  h:m:s:Line1|Line2|Line3....
954      *  or
955      *  h:m:s Line1|Line2|Line3....
956      * where n1 and n2 are the video frame number...
957      *
958      */
959     char *p;
960     char buffer_text[MAX_LINE + 1];
961     mtime_t    i_start;
962     unsigned int i;
963
964     for( ;; )
965     {
966         int h, m, s;
967         char c;
968
969         if( ( p = text_get_line( txt ) ) == NULL )
970         {
971             return( VLC_EGENERIC );
972         }
973
974         i_start = 0;
975
976         memset( buffer_text, '\0', MAX_LINE );
977         if( sscanf( p, "%d:%d:%d%[ :]%[^\r\n]", &h, &m, &s, &c, buffer_text ) == 5 )
978         {
979             i_start = ( (mtime_t)h * 3600*1000 +
980                         (mtime_t)m * 60*1000 +
981                         (mtime_t)s * 1000 ) * 1000;
982             break;
983         }
984     }
985
986     /* replace | by \n */
987     for( i = 0; i < strlen( buffer_text ); i++ )
988     {
989         if( buffer_text[i] == '|' )
990         {
991             buffer_text[i] = '\n';
992         }
993     }
994     p_subtitle->i_start = i_start;
995
996     p_subtitle->i_stop  = 0;
997     p_subtitle->psz_text = strndup( buffer_text, MAX_LINE );
998     return( 0 );
999 }
1000
1001 static char *sub_SamiSearch( text_t *txt, char *psz_start, char *psz_str )
1002 {
1003     if( psz_start )
1004     {
1005         if( strstr( psz_start, psz_str ) )
1006         {
1007             char *s = strstr( psz_start, psz_str );
1008
1009             s += strlen( psz_str );
1010
1011             return( s );
1012         }
1013     }
1014     for( ;; )
1015     {
1016         char *p;
1017         if( ( p = text_get_line( txt ) ) == NULL )
1018         {
1019             return NULL;
1020         }
1021         if( strstr( p, psz_str ) )
1022         {
1023             char *s = strstr( p, psz_str );
1024
1025             s += strlen( psz_str );
1026
1027             return(  s);
1028         }
1029     }
1030 }
1031
1032 static int  sub_Sami( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe )
1033 {
1034     char *p;
1035     int i_start;
1036
1037     int  i_text;
1038     char buffer_text[10*MAX_LINE + 1];
1039 #define ADDC( c ) \
1040     if( i_text < 10*MAX_LINE )      \
1041     {                               \
1042         buffer_text[i_text++] = c;  \
1043         buffer_text[i_text] = '\0'; \
1044     }
1045
1046     /* search "Start=" */
1047     if( !( p = sub_SamiSearch( txt, NULL, "Start=" ) ) )
1048     {
1049         return VLC_EGENERIC;
1050     }
1051
1052     /* get start value */
1053     i_start = strtol( p, &p, 0 );
1054
1055     /* search <P */
1056     if( !( p = sub_SamiSearch( txt, p, "<P" ) ) )
1057     {
1058         return VLC_EGENERIC;
1059     }
1060     /* search > */
1061     if( !( p = sub_SamiSearch( txt, p, ">" ) ) )
1062     {
1063         return VLC_EGENERIC;
1064     }
1065
1066     i_text = 0;
1067     buffer_text[0] = '\0';
1068     /* now get all txt until  a "Start=" line */
1069     for( ;; )
1070     {
1071         if( *p )
1072         {
1073             if( *p == '<' )
1074             {
1075                 if( !strncmp( p, "<br", 3 ) || !strncmp( p, "<BR", 3 ) )
1076                 {
1077                     ADDC( '\n' );
1078                 }
1079                 else if( strstr( p, "Start=" ) )
1080                 {
1081                     text_previous_line( txt );
1082                     break;
1083                 }
1084                 p = sub_SamiSearch( txt, p, ">" );
1085             }
1086             else if( !strncmp( p, "&nbsp;", 6 ) )
1087             {
1088                 ADDC( ' ' );
1089                 p += 6;
1090             }
1091             else if( *p == '\t' )
1092             {
1093                 ADDC( ' ' );
1094                 p++;
1095             }
1096             else
1097             {
1098                 ADDC( *p );
1099                 p++;
1100             }
1101         }
1102         else
1103         {
1104             p = text_get_line( txt );
1105         }
1106
1107         if( p == NULL )
1108         {
1109             break;
1110         }
1111     }
1112
1113     p_subtitle->i_start = i_start * 1000;
1114     p_subtitle->i_stop  = 0;
1115     p_subtitle->psz_text = strndup( buffer_text, 10*MAX_LINE );
1116
1117     return( VLC_SUCCESS );
1118 #undef ADDC
1119 }
1120