]> git.sesse.net Git - vlc/blob - modules/demux/subtitle.c
* Re-added sub-delay option
[vlc] / modules / demux / subtitle.c
1 /*****************************************************************************
2  * subtitle.c: Demux for subtitle text files.
3  *****************************************************************************
4  * Copyright (C) 1999-2004 VideoLAN
5  * $Id$
6  *
7  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8  *          Derk-Jan Hartman <hartman at videolan dot org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include <stdlib.h>
29
30 #include <errno.h>
31 #include <sys/types.h>
32 #include <ctype.h>
33
34 #include <vlc/vlc.h>
35 #include <vlc/input.h>
36 #include "vlc_video.h"
37
38
39 #if (!defined( WIN32 ) || defined(__MINGW32__))
40 #    include <dirent.h>
41 #endif
42
43 /*****************************************************************************
44  * Module descriptor
45  *****************************************************************************/
46 static int  Open ( vlc_object_t *p_this );
47 static void Close( vlc_object_t *p_this );
48
49 #define SUB_DELAY_LONGTEXT \
50     "Delay subtitles (in 1/10s)"
51 #define SUB_FPS_LONGTEXT \
52     "Override frames per second. " \
53     "It will only work with MicroDVD subtitles."
54 #define SUB_TYPE_LONGTEXT \
55     "One from \"microdvd\", \"subrip\", \"ssa1\", \"ssa2-4\", \"vplayer\" " \
56     "\"sami\" (auto for autodetection, it should always work)."
57 static char *ppsz_sub_type[] =
58 {
59     "auto", "microdvd", "subrip", "subviewer", "ssa1",
60     "ssa2-4", "vplayer", "sami"
61 };
62
63 vlc_module_begin();
64     set_description( _("Text subtitles demux") );
65     set_capability( "demux2", 0 );
66     add_float( "sub-fps", 0.0, NULL,
67                N_("Frames per second"),
68                SUB_FPS_LONGTEXT, VLC_TRUE );
69     add_integer( "sub-delay", 0, NULL,
70                N_("Subtitles delay"),
71                SUB_DELAY_LONGTEXT, VLC_TRUE );
72     add_string( "sub-type", "auto", NULL, "Subtitles fileformat",
73                 SUB_TYPE_LONGTEXT, VLC_TRUE );
74         change_string_list( ppsz_sub_type, 0, 0 );
75     set_callbacks( Open, Close );
76
77     add_shortcut( "subtitle" );
78 vlc_module_end();
79
80 /*****************************************************************************
81  * Prototypes:
82  *****************************************************************************/
83 enum
84 {
85     SUB_TYPE_UNKNOWN = -1,
86     SUB_TYPE_MICRODVD,
87     SUB_TYPE_SUBRIP,
88     SUB_TYPE_SSA1,
89     SUB_TYPE_SSA2_4,
90     SUB_TYPE_VPLAYER,
91     SUB_TYPE_SAMI,
92     SUB_TYPE_SUBVIEWER,
93 };
94
95 typedef struct
96 {
97     int     i_line_count;
98     int     i_line;
99     char    **line;
100 } text_t;
101 static int  TextLoad( text_t *, stream_t *s );
102 static void TextUnload( text_t * );
103
104 typedef struct
105 {
106     mtime_t i_start;
107     mtime_t i_stop;
108
109     char    *psz_text;
110 } subtitle_t;
111
112
113 struct demux_sys_t
114 {
115     int         i_type;
116     text_t      txt;
117     es_out_id_t *es;
118
119     int64_t     i_next_demux_date;
120
121     int64_t     i_microsecperframe;
122     mtime_t     i_original_mspf;
123
124     char        *psz_header;
125     int         i_subtitle;
126     int         i_subtitles;
127     subtitle_t  *subtitle;
128
129     int64_t     i_length;
130 };
131
132 static int  ParseMicroDvd ( demux_t *, subtitle_t * );
133 static int  ParseSubRip   ( demux_t *, subtitle_t * );
134 static int  ParseSubViewer( demux_t *, subtitle_t * );
135 static int  ParseSSA      ( demux_t *, subtitle_t * );
136 static int  ParseVplayer  ( demux_t *, subtitle_t * );
137 static int  ParseSami     ( demux_t *, subtitle_t * );
138
139 static struct
140 {
141     char *psz_type_name;
142     int  i_type;
143     char *psz_name;
144     int  (*pf_read)( demux_t *, subtitle_t* );
145 } sub_read_subtitle_function [] =
146 {
147     { "microdvd",   SUB_TYPE_MICRODVD,  "MicroDVD", ParseMicroDvd },
148     { "subrip",     SUB_TYPE_SUBRIP,    "SubRIP",   ParseSubRip },
149     { "subviewer",  SUB_TYPE_SUBVIEWER, "SubViewer",ParseSubViewer },
150     { "ssa1",       SUB_TYPE_SSA1,      "SSA-1",    ParseSSA },
151     { "ssa2-4",     SUB_TYPE_SSA2_4,    "SSA-2/3/4",ParseSSA },
152     { "vplayer",    SUB_TYPE_VPLAYER,   "VPlayer",  ParseVplayer },
153     { "sami",       SUB_TYPE_SAMI,      "SAMI",     ParseSami },
154     { NULL,         SUB_TYPE_UNKNOWN,   "Unknown",  NULL }
155 };
156
157 static int Demux( demux_t * );
158 static int Control( demux_t *, int, va_list );
159
160 static void Fix( demux_t * );
161
162 /*****************************************************************************
163  * Module initializer
164  *****************************************************************************/
165 static int Open ( vlc_object_t *p_this )
166 {
167     demux_t     *p_demux = (demux_t*)p_this;
168     demux_sys_t *p_sys;
169     es_format_t fmt;
170     float f_fps;
171     char *psz_type;
172     int  (*pf_read)( demux_t *, subtitle_t* );
173     int i, i_max;
174
175     if( strcmp( p_demux->psz_demux, "subtitle" ) )
176     {
177         msg_Dbg( p_demux, "subtitle demux discarded" );
178         return VLC_EGENERIC;
179     }
180
181     p_demux->pf_demux = Demux;
182     p_demux->pf_control = Control;
183     p_demux->p_sys = p_sys = malloc( sizeof( demux_sys_t ) );
184     p_sys->psz_header = NULL;
185     p_sys->i_subtitle = 0;
186     p_sys->i_subtitles= 0;
187     p_sys->subtitle   = NULL;
188
189
190     /* Get the FPS */
191     p_sys->i_microsecperframe = 40000; /* default to 25 fps */
192     f_fps = var_CreateGetFloat( p_demux, "sub-fps" );
193     if( f_fps >= 1.0 )
194     {
195         p_sys->i_microsecperframe = (mtime_t)( (float)1000000 / f_fps );
196     }
197
198     f_fps = var_CreateGetFloat( p_demux, "sub-original-fps" );
199     if( f_fps >= 1.0 )
200     {
201         p_sys->i_original_mspf = (mtime_t)( (float)1000000 / f_fps );
202     }
203     else
204     {
205         p_sys->i_original_mspf = 0;
206     }
207
208     /* Get or probe the type */
209     p_sys->i_type = SUB_TYPE_UNKNOWN;
210     psz_type = var_CreateGetString( p_demux, "sub-type" );
211     if( *psz_type )
212     {
213         int i;
214
215         for( i = 0; ; i++ )
216         {
217             if( sub_read_subtitle_function[i].psz_type_name == NULL )
218                 break;
219
220             if( !strcmp( sub_read_subtitle_function[i].psz_type_name,
221                          psz_type ) )
222             {
223                 p_sys->i_type = sub_read_subtitle_function[i].i_type;
224                 break;
225             }
226         }
227     }
228     free( psz_type );
229
230     /* Probe if unknown type */
231     if( p_sys->i_type == SUB_TYPE_UNKNOWN )
232     {
233         int     i_try;
234         char    *s = NULL;
235
236         msg_Dbg( p_demux, "autodetecting subtitle format" );
237         for( i_try = 0; i_try < 256; i_try++ )
238         {
239             int i_dummy;
240
241             if( ( s = stream_ReadLine( p_demux->s ) ) == NULL )
242                 break;
243
244             if( strcasestr( s, "<SAMI>" ) )
245             {
246                 p_sys->i_type = SUB_TYPE_SAMI;
247                 break;
248             }
249             else if( sscanf( s, "{%d}{%d}", &i_dummy, &i_dummy ) == 2 ||
250                      sscanf( s, "{%d}{}", &i_dummy ) == 1)
251             {
252                 p_sys->i_type = SUB_TYPE_MICRODVD;
253                 break;
254             }
255             else if( sscanf( s,
256                              "%d:%d:%d,%d --> %d:%d:%d,%d",
257                              &i_dummy,&i_dummy,&i_dummy,&i_dummy,
258                              &i_dummy,&i_dummy,&i_dummy,&i_dummy ) == 8 )
259             {
260                 p_sys->i_type = SUB_TYPE_SUBRIP;
261                 break;
262             }
263             else if( sscanf( s,
264                              "!: This is a Sub Station Alpha v%d.x script.",
265                              &i_dummy ) == 1)
266             {
267                 if( i_dummy <= 1 )
268                 {
269                     p_sys->i_type = SUB_TYPE_SSA1;
270                 }
271                 else
272                 {
273                     p_sys->i_type = SUB_TYPE_SSA2_4; /* I hope this will work */
274                 }
275                 break;
276             }
277             else if( strcasestr( s, "This is a Sub Station Alpha v4 script" ) )
278             {
279                 p_sys->i_type = SUB_TYPE_SSA2_4; /* I hope this will work */
280                 break;
281             }
282             else if( !strncasecmp( s, "Dialogue: Marked", 16  ) )
283             {
284                 p_sys->i_type = SUB_TYPE_SSA2_4; /* could be wrong */
285                 break;
286             }
287             else if( strcasestr( s, "[INFORMATION]" ) )
288             {
289                 p_sys->i_type = SUB_TYPE_SUBVIEWER; /* I hope this will work */
290                 break;
291             }
292             else if( sscanf( s, "%d:%d:%d:", &i_dummy, &i_dummy, &i_dummy ) == 3 ||
293                      sscanf( s, "%d:%d:%d ", &i_dummy, &i_dummy, &i_dummy ) == 3 )
294             {
295                 p_sys->i_type = SUB_TYPE_VPLAYER;
296                 break;
297             }
298
299             free( s );
300             s = NULL;
301         }
302
303         if( s ) free( s );
304
305         /* It will nearly always work even for non seekable stream thanks the
306          * caching system, and if it fails we loose just a few sub */
307         if( stream_Seek( p_demux->s, 0 ) )
308         {
309             msg_Warn( p_demux, "failed to rewind" );
310         }
311     }
312     if( p_sys->i_type == SUB_TYPE_UNKNOWN )
313     {
314         msg_Err( p_demux, "failed to recognize subtitle type" );
315         return VLC_EGENERIC;
316     }
317
318     for( i = 0; ; i++ )
319     {
320         if( sub_read_subtitle_function[i].i_type == p_sys->i_type )
321         {
322             msg_Dbg( p_demux, "detected %s format",
323                      sub_read_subtitle_function[i].psz_name );
324             pf_read = sub_read_subtitle_function[i].pf_read;
325             break;
326         }
327     }
328
329     msg_Dbg( p_demux, "loading all subtitles..." );
330
331     /* Load the whole file */
332     TextLoad( &p_sys->txt, p_demux->s );
333
334     /* Parse it */
335     for( i_max = 0;; )
336     {
337         if( p_sys->i_subtitles >= i_max )
338         {
339             i_max += 500;
340             if( !( p_sys->subtitle = realloc( p_sys->subtitle,
341                                               sizeof(subtitle_t) * i_max ) ) )
342             {
343                 msg_Err( p_demux, "out of memory");
344                 return VLC_ENOMEM;
345             }
346         }
347
348         if( pf_read( p_demux, &p_sys->subtitle[p_sys->i_subtitles] ) )
349             break;
350
351         p_sys->i_subtitles++;
352     }
353     /* Unload */
354     TextUnload( &p_sys->txt );
355
356     msg_Dbg(p_demux, "loaded %d subtitles", p_sys->i_subtitles );
357
358     /* Fix subtitle (order and time) *** */
359     p_sys->i_subtitle = 0;
360     p_sys->i_length = 0;
361     if( p_sys->i_subtitles > 0 )
362     {
363         p_sys->i_length = p_sys->subtitle[p_sys->i_subtitles-1].i_stop;
364         /* +1 to avoid 0 */
365         if( p_sys->i_length <= 0 )
366             p_sys->i_length = p_sys->subtitle[p_sys->i_subtitles-1].i_start+1;
367     }
368
369     /* *** add subtitle ES *** */
370     if( p_sys->i_type == SUB_TYPE_SSA1 ||
371              p_sys->i_type == SUB_TYPE_SSA2_4 )
372     {
373         es_format_Init( &fmt, SPU_ES, VLC_FOURCC( 's','s','a',' ' ) );
374     }
375     else
376     {
377         es_format_Init( &fmt, SPU_ES, VLC_FOURCC( 's','u','b','t' ) );
378     }
379     if( p_sys->psz_header != NULL )
380     {
381         fmt.i_extra = strlen( p_sys->psz_header ) + 1;
382         fmt.p_extra = strdup( p_sys->psz_header );
383     }
384     p_sys->es = es_out_Add( p_demux->out, &fmt );
385
386     return VLC_SUCCESS;
387 }
388
389 /*****************************************************************************
390  * Close: Close subtitle demux
391  *****************************************************************************/
392 static void Close( vlc_object_t *p_this )
393 {
394     demux_t *p_demux = (demux_t*)p_this;
395     demux_sys_t *p_sys = p_demux->p_sys;
396     int i;
397
398     for( i = 0; i < p_sys->i_subtitles; i++ )
399     {
400         if( p_sys->subtitle[i].psz_text )
401             free( p_sys->subtitle[i].psz_text );
402     }
403     if( p_sys->subtitle )
404         free( p_sys->subtitle );
405
406     free( p_sys );
407 }
408
409 /*****************************************************************************
410  * Control:
411  *****************************************************************************/
412 static int Control( demux_t *p_demux, int i_query, va_list args )
413 {
414     demux_sys_t *p_sys = p_demux->p_sys;
415     int64_t *pi64, i64;
416     double *pf, f;
417
418     switch( i_query )
419     {
420         case DEMUX_GET_LENGTH:
421             pi64 = (int64_t*)va_arg( args, int64_t * );
422             *pi64 = p_sys->i_length;
423             return VLC_SUCCESS;
424
425         case DEMUX_GET_TIME:
426             pi64 = (int64_t*)va_arg( args, int64_t * );
427             if( p_sys->i_subtitle < p_sys->i_subtitles )
428             {
429                 *pi64 = p_sys->subtitle[p_sys->i_subtitle].i_start;
430                 return VLC_SUCCESS;
431             }
432             return VLC_EGENERIC;
433
434         case DEMUX_SET_TIME:
435             i64 = (int64_t)va_arg( args, int64_t );
436             p_sys->i_subtitle = 0;
437             while( p_sys->i_subtitle < p_sys->i_subtitles &&
438                    p_sys->subtitle[p_sys->i_subtitle].i_start < i64 )
439             {
440                 p_sys->i_subtitle++;
441             }
442
443             if( p_sys->i_subtitle >= p_sys->i_subtitles )
444                 return VLC_EGENERIC;
445             return VLC_SUCCESS;
446
447         case DEMUX_GET_POSITION:
448             pf = (double*)va_arg( args, double * );
449             if( p_sys->i_subtitle >= p_sys->i_subtitles )
450             {
451                 *pf = 1.0;
452             }
453             else if( p_sys->i_subtitles > 0 )
454             {
455                 *pf = (double)p_sys->subtitle[p_sys->i_subtitle].i_start /
456                       (double)p_sys->i_length;
457             }
458             else
459             {
460                 *pf = 0.0;
461             }
462             return VLC_SUCCESS;
463
464         case DEMUX_SET_POSITION:
465             f = (double)va_arg( args, double );
466             i64 = f * p_sys->i_length;
467
468             p_sys->i_subtitle = 0;
469             while( p_sys->i_subtitle < p_sys->i_subtitles &&
470                    p_sys->subtitle[p_sys->i_subtitle].i_start < i64 )
471             {
472                 p_sys->i_subtitle++;
473             }
474             if( p_sys->i_subtitle >= p_sys->i_subtitles )
475                 return VLC_EGENERIC;
476             return VLC_SUCCESS;
477
478         case DEMUX_SET_NEXT_DEMUX_TIME:
479             p_sys->i_next_demux_date = (int64_t)va_arg( args, int64_t );
480             return VLC_SUCCESS;
481
482         case DEMUX_GET_FPS:
483         case DEMUX_GET_META:
484         case DEMUX_GET_TITLE_INFO:
485             return VLC_EGENERIC;
486
487         default:
488             msg_Err( p_demux, "unknown query in subtitle control" );
489             return VLC_EGENERIC;
490     }
491 }
492
493 /*****************************************************************************
494  * Demux: Send subtitle to decoder
495  *****************************************************************************/
496 static int Demux( demux_t *p_demux )
497 {
498     demux_sys_t *p_sys = p_demux->p_sys;
499     int64_t i_maxdate;
500
501     if( p_sys->i_subtitle >= p_sys->i_subtitles )
502         return 0;
503
504     i_maxdate = p_sys->i_next_demux_date;
505     if( i_maxdate <= 0 && p_sys->i_subtitle < p_sys->i_subtitles )
506     {
507         /* Should not happen */
508         i_maxdate = p_sys->subtitle[p_sys->i_subtitle].i_start + 1;
509     }
510
511     while( p_sys->i_subtitle < p_sys->i_subtitles &&
512            p_sys->subtitle[p_sys->i_subtitle].i_start < i_maxdate )
513     {
514         block_t *p_block;
515         int i_len = strlen( p_sys->subtitle[p_sys->i_subtitle].psz_text ) + 1;
516
517         if( i_len <= 1 )
518         {
519             /* empty subtitle */
520             p_sys->i_subtitle++;
521             continue;
522         }
523
524         if( ( p_block = block_New( p_demux, i_len ) ) == NULL )
525         {
526             p_sys->i_subtitle++;
527             continue;
528         }
529
530         if( p_sys->subtitle[p_sys->i_subtitle].i_start < 0 )
531         {
532             p_sys->i_subtitle++;
533             continue;
534         }
535
536         p_block->i_pts = p_sys->subtitle[p_sys->i_subtitle].i_start;
537         p_block->i_dts = p_block->i_pts;
538         if( p_sys->subtitle[p_sys->i_subtitle].i_stop > 0 )
539         {
540             p_block->i_length =
541                 p_sys->subtitle[p_sys->i_subtitle].i_stop - p_block->i_pts;
542         }
543
544         memcpy( p_block->p_buffer,
545                 p_sys->subtitle[p_sys->i_subtitle].psz_text, i_len );
546         if( p_block->i_pts > 0 )
547         {
548             es_out_Send( p_demux->out, p_sys->es, p_block );
549         }
550         else
551         {
552             block_Release( p_block );
553         }
554         p_sys->i_subtitle++;
555     }
556
557     /* */
558     p_sys->i_next_demux_date = 0;
559
560     return 1;
561 }
562
563 /*****************************************************************************
564  * Fix: fix time stamp and order of subtitle
565  *****************************************************************************/
566 static void Fix( demux_t *p_demux )
567 {
568     demux_sys_t *p_sys = p_demux->p_sys;
569     vlc_bool_t b_done;
570     int     i_index;
571
572     /* *** fix order (to be sure...) *** */
573     /* We suppose that there are near in order and this durty bubble sort
574      * wont take too much time
575      */
576     do
577     {
578         b_done = VLC_TRUE;
579         for( i_index = 1; i_index < p_sys->i_subtitles; i_index++ )
580         {
581             if( p_sys->subtitle[i_index].i_start <
582                     p_sys->subtitle[i_index - 1].i_start )
583             {
584                 subtitle_t sub_xch;
585                 memcpy( &sub_xch,
586                         p_sys->subtitle + i_index - 1,
587                         sizeof( subtitle_t ) );
588                 memcpy( p_sys->subtitle + i_index - 1,
589                         p_sys->subtitle + i_index,
590                         sizeof( subtitle_t ) );
591                 memcpy( p_sys->subtitle + i_index,
592                         &sub_xch,
593                         sizeof( subtitle_t ) );
594                 b_done = VLC_FALSE;
595             }
596         }
597     } while( !b_done );
598 }
599
600 static int TextLoad( text_t *txt, stream_t *s )
601 {
602     int   i_line_max;
603
604     /* init txt */
605     i_line_max          = 500;
606     txt->i_line_count   = 0;
607     txt->i_line         = 0;
608     txt->line           = calloc( i_line_max, sizeof( char * ) );
609
610     /* load the complete file */
611     for( ;; )
612     {
613         char *psz = stream_ReadLine( s );
614
615         if( psz == NULL )
616             break;
617
618         txt->line[txt->i_line_count++] = psz;
619         if( txt->i_line_count >= i_line_max )
620         {
621             i_line_max += 100;
622             txt->line = realloc( txt->line, i_line_max * sizeof( char*) );
623         }
624     }
625
626     if( txt->i_line_count <= 0 )
627     {
628         free( txt->line );
629         return VLC_EGENERIC;
630     }
631
632     return VLC_SUCCESS;
633 }
634 static void TextUnload( text_t *txt )
635 {
636     int i;
637
638     for( i = 0; i < txt->i_line_count; i++ )
639     {
640         free( txt->line[i] );
641     }
642     free( txt->line );
643     txt->i_line       = 0;
644     txt->i_line_count = 0;
645 }
646
647 static char *TextGetLine( text_t *txt )
648 {
649     if( txt->i_line >= txt->i_line_count )
650         return( NULL );
651
652     return txt->line[txt->i_line++];
653 }
654 static void TextPreviousLine( text_t *txt )
655 {
656     if( txt->i_line > 0 )
657         txt->i_line--;
658 }
659
660 /*****************************************************************************
661  * Specific Subtitle function
662  *****************************************************************************/
663 #define MAX_LINE 8192
664 static int ParseMicroDvd( demux_t *p_demux, subtitle_t *p_subtitle )
665 {
666     demux_sys_t *p_sys = p_demux->p_sys;
667     text_t      *txt = &p_sys->txt;
668     /*
669      * each line:
670      *  {n1}{n2}Line1|Line2|Line3....
671      * where n1 and n2 are the video frame number...
672      *
673      */
674     char *s;
675
676     char buffer_text[MAX_LINE + 1];
677     unsigned int    i_start;
678     unsigned int    i_stop;
679     unsigned int i;
680
681     p_subtitle->i_start = 0;
682     p_subtitle->i_stop  = 0;
683     p_subtitle->psz_text = NULL;
684
685     for( ;; )
686     {
687         if( ( s = TextGetLine( txt ) ) == NULL )
688         {
689             return( VLC_EGENERIC );
690         }
691         i_start = 0;
692         i_stop  = 0;
693
694         memset( buffer_text, '\0', MAX_LINE );
695         if( sscanf( s, "{%d}{}%[^\r\n]", &i_start, buffer_text ) == 2 ||
696             sscanf( s, "{%d}{%d}%[^\r\n]", &i_start, &i_stop, buffer_text ) == 3)
697         {
698             break;
699         }
700     }
701     /* replace | by \n */
702     for( i = 0; i < strlen( buffer_text ); i++ )
703     {
704         if( buffer_text[i] == '|' )
705         {
706             buffer_text[i] = '\n';
707         }
708     }
709
710     p_subtitle->i_start = (mtime_t)i_start * p_sys->i_microsecperframe;
711     p_subtitle->i_stop  = (mtime_t)i_stop  * p_sys->i_microsecperframe;
712     p_subtitle->psz_text = strndup( buffer_text, MAX_LINE );
713     return( 0 );
714 }
715
716 static int  ParseSubRip( demux_t *p_demux, subtitle_t *p_subtitle )
717 {
718     demux_sys_t *p_sys = p_demux->p_sys;
719     text_t      *txt = &p_sys->txt;
720
721     /*
722      * n
723      * h1:m1:s1,d1 --> h2:m2:s2,d2
724      * Line1
725      * Line2
726      * ...
727      * [empty line]
728      *
729      */
730     char *s;
731     char buffer_text[ 10 * MAX_LINE];
732     int  i_buffer_text;
733     mtime_t     i_start;
734     mtime_t     i_stop;
735
736     p_subtitle->i_start = 0;
737     p_subtitle->i_stop  = 0;
738     p_subtitle->psz_text = NULL;
739
740     for( ;; )
741     {
742         int h1, m1, s1, d1, h2, m2, s2, d2;
743         if( ( s = TextGetLine( txt ) ) == NULL )
744         {
745             return( VLC_EGENERIC );
746         }
747         if( sscanf( s,
748                     "%d:%d:%d,%d --> %d:%d:%d,%d",
749                     &h1, &m1, &s1, &d1,
750                     &h2, &m2, &s2, &d2 ) == 8 )
751         {
752             i_start = ( (mtime_t)h1 * 3600*1000 +
753                         (mtime_t)m1 * 60*1000 +
754                         (mtime_t)s1 * 1000 +
755                         (mtime_t)d1 ) * 1000;
756
757             i_stop  = ( (mtime_t)h2 * 3600*1000 +
758                         (mtime_t)m2 * 60*1000 +
759                         (mtime_t)s2 * 1000 +
760                         (mtime_t)d2 ) * 1000;
761
762             /* Now read text until an empty line */
763             for( i_buffer_text = 0;; )
764             {
765                 int i_len;
766                 if( ( s = TextGetLine( txt ) ) == NULL )
767                 {
768                     return( VLC_EGENERIC );
769                 }
770
771                 i_len = strlen( s );
772                 if( i_len <= 0 )
773                 {
774                     /* empty line -> end of this subtitle */
775                     buffer_text[__MAX( i_buffer_text - 1, 0 )] = '\0';
776                     p_subtitle->i_start = i_start;
777                     p_subtitle->i_stop = i_stop;
778                     p_subtitle->psz_text = strdup( buffer_text );
779                     /* If framerate is available, use sub-fps */
780                     if( p_sys->i_microsecperframe != 0 &&
781                         p_sys->i_original_mspf != 0)
782                     {
783                         p_subtitle->i_start = (mtime_t)i_start *
784                                               p_sys->i_microsecperframe/
785                                               p_sys->i_original_mspf;
786                         p_subtitle->i_stop  = (mtime_t)i_stop  *
787                                               p_sys->i_microsecperframe /
788                                               p_sys->i_original_mspf;
789                     }
790                     return 0;
791                 }
792                 else
793                 {
794                     if( i_buffer_text + i_len + 1 < 10 * MAX_LINE )
795                     {
796                         memcpy( buffer_text + i_buffer_text,
797                                 s,
798                                 i_len );
799                         i_buffer_text += i_len;
800
801                         buffer_text[i_buffer_text] = '\n';
802                         i_buffer_text++;
803                     }
804                 }
805             }
806         }
807     }
808 }
809
810 static int  ParseSubViewer( demux_t *p_demux, subtitle_t *p_subtitle )
811 {
812     demux_sys_t *p_sys = p_demux->p_sys;
813     text_t      *txt = &p_sys->txt;
814
815     /*
816      * h1:m1:s1.d1,h2:m2:s2.d2
817      * Line1[br]Line2
818      * Line3
819      * ...
820      * [empty line]
821      * ( works with subviewer and subviewer v2 )
822      */
823     char *s;
824     char buffer_text[ 10 * MAX_LINE];
825     int  i_buffer_text;
826     mtime_t     i_start;
827     mtime_t     i_stop;
828
829     p_subtitle->i_start = 0;
830     p_subtitle->i_stop  = 0;
831     p_subtitle->psz_text = NULL;
832
833     for( ;; )
834     {
835         int h1, m1, s1, d1, h2, m2, s2, d2;
836         if( ( s = TextGetLine( txt ) ) == NULL )
837         {
838             return( VLC_EGENERIC );
839         }
840         if( sscanf( s,
841                     "%d:%d:%d.%d,%d:%d:%d.%d",
842                     &h1, &m1, &s1, &d1,
843                     &h2, &m2, &s2, &d2 ) == 8 )
844         {
845             i_start = ( (mtime_t)h1 * 3600*1000 +
846                         (mtime_t)m1 * 60*1000 +
847                         (mtime_t)s1 * 1000 +
848                         (mtime_t)d1 ) * 1000;
849
850             i_stop  = ( (mtime_t)h2 * 3600*1000 +
851                         (mtime_t)m2 * 60*1000 +
852                         (mtime_t)s2 * 1000 +
853                         (mtime_t)d2 ) * 1000;
854
855             /* Now read text until an empty line */
856             for( i_buffer_text = 0;; )
857             {
858                 int i_len, i;
859                 if( ( s = TextGetLine( txt ) ) == NULL )
860                 {
861                     return( VLC_EGENERIC );
862                 }
863
864                 i_len = strlen( s );
865                 if( i_len <= 0 )
866                 {
867                     /* empty line -> end of this subtitle */
868                     buffer_text[__MAX( i_buffer_text - 1, 0 )] = '\0';
869                     p_subtitle->i_start = i_start;
870                     p_subtitle->i_stop = i_stop;
871
872                     /* replace [br] by \n */
873                     for( i = 0; i < i_buffer_text - 3; i++ )
874                     {
875                         if( buffer_text[i] == '[' && buffer_text[i+1] == 'b' &&
876                             buffer_text[i+2] == 'r' && buffer_text[i+3] == ']' )
877                         {
878                             char *temp = buffer_text + i + 1;
879                             buffer_text[i] = '\n';
880                             memmove( temp, temp+3, strlen( temp ) -3 );
881                             temp[strlen( temp )-3] = '\0';
882                         }
883                     }
884                     p_subtitle->psz_text = strdup( buffer_text );
885                     return( 0 );
886                 }
887                 else
888                 {
889                     if( i_buffer_text + i_len + 1 < 10 * MAX_LINE )
890                     {
891                         memcpy( buffer_text + i_buffer_text,
892                                 s,
893                                 i_len );
894                         i_buffer_text += i_len;
895
896                         buffer_text[i_buffer_text] = '\n';
897                         i_buffer_text++;
898                     }
899                 }
900             }
901         }
902     }
903 }
904
905
906 static int  ParseSSA( demux_t *p_demux, subtitle_t *p_subtitle )
907 {
908     demux_sys_t *p_sys = p_demux->p_sys;
909     text_t      *txt = &p_sys->txt;
910
911     char buffer_text[ 10 * MAX_LINE];
912     char *s;
913     mtime_t     i_start;
914     mtime_t     i_stop;
915
916     p_subtitle->i_start = 0;
917     p_subtitle->i_stop  = 0;
918     p_subtitle->psz_text = NULL;
919
920     for( ;; )
921     {
922         int h1, m1, s1, c1, h2, m2, s2, c2;
923         int i_dummy;
924
925         if( ( s = TextGetLine( txt ) ) == NULL )
926         {
927             return( VLC_EGENERIC );
928         }
929         p_subtitle->psz_text = malloc( strlen( s ) );
930
931         if( sscanf( s,
932                     "Dialogue: Marked=%d,%d:%d:%d.%d,%d:%d:%d.%d%[^\r\n]",
933                     &i_dummy,
934                     &h1, &m1, &s1, &c1,
935                     &h2, &m2, &s2, &c2,
936                     buffer_text ) == 10 )
937         {
938             i_start = ( (mtime_t)h1 * 3600*1000 +
939                         (mtime_t)m1 * 60*1000 +
940                         (mtime_t)s1 * 1000 +
941                         (mtime_t)c1 * 10 ) * 1000;
942
943             i_stop  = ( (mtime_t)h2 * 3600*1000 +
944                         (mtime_t)m2 * 60*1000 +
945                         (mtime_t)s2 * 1000 +
946                         (mtime_t)c2 * 10 ) * 1000;
947
948             /* The dec expects: ReadOrder, Layer, Style, Name, MarginL, MarginR, MarginV, Effect, Text */
949             if( p_sys->i_type == SUB_TYPE_SSA1 )
950             {
951                 sprintf( p_subtitle->psz_text,
952                          ",%d%s", i_dummy, strdup( buffer_text) );
953             }
954             else
955             {
956                 sprintf( p_subtitle->psz_text,
957                          ",%d,%s", i_dummy, strdup( buffer_text) );
958             }
959             p_subtitle->i_start = i_start;
960             p_subtitle->i_stop = i_stop;
961             return 0;
962         }
963         else
964         {
965             /* All the other stuff we add to the header field */
966             if( p_sys->psz_header != NULL )
967             {
968                 if( !( p_sys->psz_header = realloc( p_sys->psz_header,
969                           strlen( p_sys->psz_header ) + strlen( s ) + 2 ) ) )
970                 {
971                     msg_Err( p_demux, "out of memory");
972                     return VLC_ENOMEM;
973                 }
974                 p_sys->psz_header = strcat( p_sys->psz_header, strdup( s ) );
975                 p_sys->psz_header = strcat( p_sys->psz_header, "\n" );
976             }
977             else
978             {
979                 if( !( p_sys->psz_header = malloc( strlen( s ) + 2 ) ) )
980                 {
981                     msg_Err( p_demux, "out of memory");
982                     return VLC_ENOMEM;
983                 }
984                 p_sys->psz_header = strdup( s );
985                 p_sys->psz_header = strcat( p_sys->psz_header, "\n" );
986             }
987         }
988     }
989 }
990
991 static int  ParseVplayer( demux_t *p_demux, subtitle_t *p_subtitle )
992 {
993     demux_sys_t *p_sys = p_demux->p_sys;
994     text_t      *txt = &p_sys->txt;
995
996     /*
997      * each line:
998      *  h:m:s:Line1|Line2|Line3....
999      *  or
1000      *  h:m:s Line1|Line2|Line3....
1001      *
1002      */
1003     char *p;
1004     char buffer_text[MAX_LINE + 1];
1005     mtime_t    i_start;
1006     unsigned int i;
1007
1008     p_subtitle->i_start = 0;
1009     p_subtitle->i_stop  = 0;
1010     p_subtitle->psz_text = NULL;
1011
1012     for( ;; )
1013     {
1014         int h, m, s;
1015         char c;
1016
1017         if( ( p = TextGetLine( txt ) ) == NULL )
1018         {
1019             return( VLC_EGENERIC );
1020         }
1021
1022         i_start = 0;
1023
1024         memset( buffer_text, '\0', MAX_LINE );
1025         if( sscanf( p, "%d:%d:%d%[ :]%[^\r\n]", &h, &m, &s, &c, buffer_text ) == 5 )
1026         {
1027             i_start = ( (mtime_t)h * 3600*1000 +
1028                         (mtime_t)m * 60*1000 +
1029                         (mtime_t)s * 1000 ) * 1000;
1030             break;
1031         }
1032     }
1033
1034     /* replace | by \n */
1035     for( i = 0; i < strlen( buffer_text ); i++ )
1036     {
1037         if( buffer_text[i] == '|' )
1038         {
1039             buffer_text[i] = '\n';
1040         }
1041     }
1042     p_subtitle->i_start = i_start;
1043
1044     p_subtitle->i_stop  = 0;
1045     p_subtitle->psz_text = strndup( buffer_text, MAX_LINE );
1046     return( 0 );
1047 }
1048
1049 static char *ParseSamiSearch( text_t *txt, char *psz_start, char *psz_str )
1050 {
1051     if( psz_start )
1052     {
1053         if( strcasestr( psz_start, psz_str ) )
1054         {
1055             char *s = strcasestr( psz_start, psz_str );
1056
1057             s += strlen( psz_str );
1058
1059             return( s );
1060         }
1061     }
1062     for( ;; )
1063     {
1064         char *p;
1065         if( ( p = TextGetLine( txt ) ) == NULL )
1066         {
1067             return NULL;
1068         }
1069         if( strcasestr( p, psz_str ) )
1070         {
1071             char *s = strcasestr( p, psz_str );
1072
1073             s += strlen( psz_str );
1074
1075             return(  s);
1076         }
1077     }
1078 }
1079
1080 static int  ParseSami( demux_t *p_demux, subtitle_t *p_subtitle )
1081 {
1082     demux_sys_t *p_sys = p_demux->p_sys;
1083     text_t      *txt = &p_sys->txt;
1084
1085     char *p;
1086     int i_start;
1087
1088     int  i_text;
1089     char buffer_text[10*MAX_LINE + 1];
1090
1091     p_subtitle->i_start = 0;
1092     p_subtitle->i_stop  = 0;
1093     p_subtitle->psz_text = NULL;
1094
1095 #define ADDC( c ) \
1096     if( i_text < 10*MAX_LINE )      \
1097     {                               \
1098         buffer_text[i_text++] = c;  \
1099         buffer_text[i_text] = '\0'; \
1100     }
1101
1102     /* search "Start=" */
1103     if( !( p = ParseSamiSearch( txt, NULL, "Start=" ) ) )
1104     {
1105         return VLC_EGENERIC;
1106     }
1107
1108     /* get start value */
1109     i_start = strtol( p, &p, 0 );
1110
1111     /* search <P */
1112     if( !( p = ParseSamiSearch( txt, p, "<P" ) ) )
1113     {
1114         return VLC_EGENERIC;
1115     }
1116     /* search > */
1117     if( !( p = ParseSamiSearch( txt, p, ">" ) ) )
1118     {
1119         return VLC_EGENERIC;
1120     }
1121
1122     i_text = 0;
1123     buffer_text[0] = '\0';
1124     /* now get all txt until  a "Start=" line */
1125     for( ;; )
1126     {
1127         if( *p )
1128         {
1129             if( *p == '<' )
1130             {
1131                 if( !strncasecmp( p, "<br", 3 ) )
1132                 {
1133                     ADDC( '\n' );
1134                 }
1135                 else if( strcasestr( p, "Start=" ) )
1136                 {
1137                     TextPreviousLine( txt );
1138                     break;
1139                 }
1140                 p = ParseSamiSearch( txt, p, ">" );
1141             }
1142             else if( !strncmp( p, "&nbsp;", 6 ) )
1143             {
1144                 ADDC( ' ' );
1145                 p += 6;
1146             }
1147             else if( *p == '\t' )
1148             {
1149                 ADDC( ' ' );
1150                 p++;
1151             }
1152             else
1153             {
1154                 ADDC( *p );
1155                 p++;
1156             }
1157         }
1158         else
1159         {
1160             p = TextGetLine( txt );
1161         }
1162
1163         if( p == NULL )
1164         {
1165             break;
1166         }
1167     }
1168
1169     p_subtitle->i_start = i_start * 1000;
1170     p_subtitle->i_stop  = 0;
1171     p_subtitle->psz_text = strndup( buffer_text, 10*MAX_LINE );
1172
1173     return( VLC_SUCCESS );
1174 #undef ADDC
1175 }