]> git.sesse.net Git - vlc/blob - modules/demux/subtitle.c
LGPL
[vlc] / modules / demux / subtitle.c
1 /*****************************************************************************
2  * subtitle.c: Demux for subtitle text files.
3  *****************************************************************************
4  * Copyright (C) 1999-2007 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8  *          Derk-Jan Hartman <hartman at videolan dot org>
9  *          Jean-Baptiste Kempf <jb@videolan.org>
10  *
11  * This program is free software; you can redistribute it and/or modify it
12  * under the terms of the GNU Lesser General Public License as published by
13  * the Free Software Foundation; either version 2.1 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * along with this program; if not, write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
33
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
36 #include <vlc_input.h>
37 #include <vlc_memory.h>
38
39 #include <ctype.h>
40
41 #include <vlc_demux.h>
42 #include <vlc_charset.h>
43
44 /*****************************************************************************
45  * Module descriptor
46  *****************************************************************************/
47 static int  Open ( vlc_object_t *p_this );
48 static void Close( vlc_object_t *p_this );
49
50 #define SUB_DELAY_LONGTEXT \
51     N_("Apply a delay to all subtitles (in 1/10s, eg 100 means 10s).")
52 #define SUB_FPS_LONGTEXT \
53     N_("Override the normal frames per second settings. " \
54     "This will only work with MicroDVD and SubRIP (SRT) subtitles.")
55 #define SUB_TYPE_LONGTEXT \
56     N_("Force the subtiles format. Selecting \"auto\" means autodetection and should always work.")
57 #define SUB_DESCRIPTION_LONGTEXT \
58     N_("Override the default track description.")
59
60 static const char *const ppsz_sub_type[] =
61 {
62     "auto", "microdvd", "subrip", "subviewer", "ssa1",
63     "ssa2-4", "ass", "vplayer", "sami", "dvdsubtitle", "mpl2",
64     "aqt", "pjs", "mpsub", "jacosub", "psb", "realtext", "dks",
65     "subviewer1"
66 };
67
68 vlc_module_begin ()
69     set_shortname( N_("Subtitles"))
70     set_description( N_("Text subtitles parser") )
71     set_capability( "demux", 0 )
72     set_category( CAT_INPUT )
73     set_subcategory( SUBCAT_INPUT_DEMUX )
74     add_float( "sub-fps", 0.0,
75                N_("Frames per second"),
76                SUB_FPS_LONGTEXT, true )
77     add_integer( "sub-delay", 0,
78                N_("Subtitles delay"),
79                SUB_DELAY_LONGTEXT, true )
80     add_string( "sub-type", "auto", N_("Subtitles format"),
81                 SUB_TYPE_LONGTEXT, true )
82         change_string_list( ppsz_sub_type, ppsz_sub_type )
83     add_string( "sub-description", NULL, N_("Subtitles description"),
84                 SUB_DESCRIPTION_LONGTEXT, true )
85     set_callbacks( Open, Close )
86
87     add_shortcut( "subtitle" )
88 vlc_module_end ()
89
90 /*****************************************************************************
91  * Prototypes:
92  *****************************************************************************/
93 enum
94 {
95     SUB_TYPE_UNKNOWN = -1,
96     SUB_TYPE_MICRODVD,
97     SUB_TYPE_SUBRIP,
98     SUB_TYPE_SUBRIP_DOT, /* Invalid SubRip file (dot instead of comma) */
99     SUB_TYPE_SSA1,
100     SUB_TYPE_SSA2_4,
101     SUB_TYPE_ASS,
102     SUB_TYPE_VPLAYER,
103     SUB_TYPE_SAMI,
104     SUB_TYPE_SUBVIEWER, /* SUBVIEWER 2 */
105     SUB_TYPE_DVDSUBTITLE, /* Mplayer calls it subviewer2 */
106     SUB_TYPE_MPL2,
107     SUB_TYPE_AQT,
108     SUB_TYPE_PJS,
109     SUB_TYPE_MPSUB,
110     SUB_TYPE_JACOSUB,
111     SUB_TYPE_PSB,
112     SUB_TYPE_RT,
113     SUB_TYPE_DKS,
114     SUB_TYPE_SUBVIEW1 /* SUBVIEWER 1 - mplayer calls it subrip09,
115                          and Gnome subtitles SubViewer 1.0 */
116 };
117
118 typedef struct
119 {
120     int     i_line_count;
121     int     i_line;
122     char    **line;
123 } text_t;
124
125 static int  TextLoad( text_t *, stream_t *s );
126 static void TextUnload( text_t * );
127
128 typedef struct
129 {
130     int64_t i_start;
131     int64_t i_stop;
132
133     char    *psz_text;
134 } subtitle_t;
135
136
137 struct demux_sys_t
138 {
139     int         i_type;
140     text_t      txt;
141     es_out_id_t *es;
142
143     int64_t     i_next_demux_date;
144     int64_t     i_microsecperframe;
145
146     char        *psz_header;
147     int         i_subtitle;
148     int         i_subtitles;
149     subtitle_t  *subtitle;
150
151     int64_t     i_length;
152
153     /* */
154     struct
155     {
156         bool b_inited;
157
158         int i_comment;
159         int i_time_resolution;
160         int i_time_shift;
161     } jss;
162     struct
163     {
164         bool  b_inited;
165
166         float f_total;
167         float f_factor;
168     } mpsub;
169 };
170
171 static int  ParseMicroDvd   ( demux_t *, subtitle_t *, int );
172 static int  ParseSubRip     ( demux_t *, subtitle_t *, int );
173 static int  ParseSubRipDot  ( demux_t *, subtitle_t *, int );
174 static int  ParseSubViewer  ( demux_t *, subtitle_t *, int );
175 static int  ParseSSA        ( demux_t *, subtitle_t *, int );
176 static int  ParseVplayer    ( demux_t *, subtitle_t *, int );
177 static int  ParseSami       ( demux_t *, subtitle_t *, int );
178 static int  ParseDVDSubtitle( demux_t *, subtitle_t *, int );
179 static int  ParseMPL2       ( demux_t *, subtitle_t *, int );
180 static int  ParseAQT        ( demux_t *, subtitle_t *, int );
181 static int  ParsePJS        ( demux_t *, subtitle_t *, int );
182 static int  ParseMPSub      ( demux_t *, subtitle_t *, int );
183 static int  ParseJSS        ( demux_t *, subtitle_t *, int );
184 static int  ParsePSB        ( demux_t *, subtitle_t *, int );
185 static int  ParseRealText   ( demux_t *, subtitle_t *, int );
186 static int  ParseDKS        ( demux_t *, subtitle_t *, int );
187 static int  ParseSubViewer1 ( demux_t *, subtitle_t *, int );
188
189 static const struct
190 {
191     const char *psz_type_name;
192     int  i_type;
193     const char *psz_name;
194     int  (*pf_read)( demux_t *, subtitle_t*, int );
195 } sub_read_subtitle_function [] =
196 {
197     { "microdvd",   SUB_TYPE_MICRODVD,    "MicroDVD",    ParseMicroDvd },
198     { "subrip",     SUB_TYPE_SUBRIP,      "SubRIP",      ParseSubRip },
199     { "subrip-dot", SUB_TYPE_SUBRIP_DOT,  "SubRIP(Dot)", ParseSubRipDot },
200     { "subviewer",  SUB_TYPE_SUBVIEWER,   "SubViewer",   ParseSubViewer },
201     { "ssa1",       SUB_TYPE_SSA1,        "SSA-1",       ParseSSA },
202     { "ssa2-4",     SUB_TYPE_SSA2_4,      "SSA-2/3/4",   ParseSSA },
203     { "ass",        SUB_TYPE_ASS,         "SSA/ASS",     ParseSSA },
204     { "vplayer",    SUB_TYPE_VPLAYER,     "VPlayer",     ParseVplayer },
205     { "sami",       SUB_TYPE_SAMI,        "SAMI",        ParseSami },
206     { "dvdsubtitle",SUB_TYPE_DVDSUBTITLE, "DVDSubtitle", ParseDVDSubtitle },
207     { "mpl2",       SUB_TYPE_MPL2,        "MPL2",        ParseMPL2 },
208     { "aqt",        SUB_TYPE_AQT,         "AQTitle",     ParseAQT },
209     { "pjs",        SUB_TYPE_PJS,         "PhoenixSub",  ParsePJS },
210     { "mpsub",      SUB_TYPE_MPSUB,       "MPSub",       ParseMPSub },
211     { "jacosub",    SUB_TYPE_JACOSUB,     "JacoSub",     ParseJSS },
212     { "psb",        SUB_TYPE_PSB,         "PowerDivx",   ParsePSB },
213     { "realtext",   SUB_TYPE_RT,          "RealText",    ParseRealText },
214     { "dks",        SUB_TYPE_DKS,         "DKS",         ParseDKS },
215     { "subviewer1", SUB_TYPE_SUBVIEW1,    "Subviewer 1", ParseSubViewer1 },
216     { NULL,         SUB_TYPE_UNKNOWN,     "Unknown",     NULL }
217 };
218 /* When adding support for more formats, be sure to add their file extension
219  * to src/input/subtitles.c to enable auto-detection.
220  */
221
222 static int Demux( demux_t * );
223 static int Control( demux_t *, int, va_list );
224
225 static void Fix( demux_t * );
226
227 /*****************************************************************************
228  * Module initializer
229  *****************************************************************************/
230 static int Open ( vlc_object_t *p_this )
231 {
232     demux_t        *p_demux = (demux_t*)p_this;
233     demux_sys_t    *p_sys;
234     es_format_t    fmt;
235     float          f_fps;
236     char           *psz_type;
237     int  (*pf_read)( demux_t *, subtitle_t*, int );
238     int            i, i_max;
239
240     if( !p_demux->b_force )
241     {
242         msg_Dbg( p_demux, "subtitle demux discarded" );
243         return VLC_EGENERIC;
244     }
245
246     p_demux->pf_demux = Demux;
247     p_demux->pf_control = Control;
248     p_demux->p_sys = p_sys = malloc( sizeof( demux_sys_t ) );
249     if( p_sys == NULL )
250         return VLC_ENOMEM;
251
252     p_sys->psz_header         = NULL;
253     p_sys->i_subtitle         = 0;
254     p_sys->i_subtitles        = 0;
255     p_sys->subtitle           = NULL;
256     p_sys->i_microsecperframe = 40000;
257
258     p_sys->jss.b_inited       = false;
259     p_sys->mpsub.b_inited     = false;
260
261     /* Get the FPS */
262     f_fps = var_CreateGetFloat( p_demux, "sub-original-fps" ); /* FIXME */
263     if( f_fps >= 1.0 )
264         p_sys->i_microsecperframe = (int64_t)( (float)1000000 / f_fps );
265
266     msg_Dbg( p_demux, "Movie fps: %f", f_fps );
267
268     /* Check for override of the fps */
269     f_fps = var_CreateGetFloat( p_demux, "sub-fps" );
270     if( f_fps >= 1.0 )
271     {
272         p_sys->i_microsecperframe = (int64_t)( (float)1000000 / f_fps );
273         msg_Dbg( p_demux, "Override subtitle fps %f", f_fps );
274     }
275
276     /* Get or probe the type */
277     p_sys->i_type = SUB_TYPE_UNKNOWN;
278     psz_type = var_CreateGetString( p_demux, "sub-type" );
279     if( psz_type && *psz_type )
280     {
281         int i;
282
283         for( i = 0; ; i++ )
284         {
285             if( sub_read_subtitle_function[i].psz_type_name == NULL )
286                 break;
287
288             if( !strcmp( sub_read_subtitle_function[i].psz_type_name,
289                          psz_type ) )
290             {
291                 p_sys->i_type = sub_read_subtitle_function[i].i_type;
292                 break;
293             }
294         }
295     }
296     free( psz_type );
297
298     /* Detect Unicode while skipping the UTF-8 Byte Order Mark */
299     bool unicode = false;
300     const uint8_t *p_data;
301     if( stream_Peek( p_demux->s, &p_data, 3 ) >= 3
302      && !memcmp( p_data, "\xEF\xBB\xBF", 3 ) )
303     {
304         unicode = true;
305         stream_Seek( p_demux->s, 3 ); /* skip BOM */
306         msg_Dbg( p_demux, "detected Unicode Byte Order Mark" );
307     }
308
309     /* Probe if unknown type */
310     if( p_sys->i_type == SUB_TYPE_UNKNOWN )
311     {
312         int     i_try;
313         char    *s = NULL;
314
315         msg_Dbg( p_demux, "autodetecting subtitle format" );
316         for( i_try = 0; i_try < 256; i_try++ )
317         {
318             int i_dummy;
319             char p_dummy;
320
321             if( ( s = stream_ReadLine( p_demux->s ) ) == NULL )
322                 break;
323
324             if( strcasestr( s, "<SAMI>" ) )
325             {
326                 p_sys->i_type = SUB_TYPE_SAMI;
327                 break;
328             }
329             else if( sscanf( s, "{%d}{%d}", &i_dummy, &i_dummy ) == 2 ||
330                      sscanf( s, "{%d}{}", &i_dummy ) == 1)
331             {
332                 p_sys->i_type = SUB_TYPE_MICRODVD;
333                 break;
334             }
335             else if( sscanf( s,
336                              "%d:%d:%d,%d --> %d:%d:%d,%d",
337                              &i_dummy,&i_dummy,&i_dummy,&i_dummy,
338                              &i_dummy,&i_dummy,&i_dummy,&i_dummy ) == 8 )
339             {
340                 p_sys->i_type = SUB_TYPE_SUBRIP;
341                 break;
342             }
343             else if( sscanf( s,
344                              "%d:%d:%d.%d --> %d:%d:%d.%d",
345                              &i_dummy,&i_dummy,&i_dummy,&i_dummy,
346                              &i_dummy,&i_dummy,&i_dummy,&i_dummy ) == 8 )
347             {
348                 msg_Err( p_demux, "Detected invalid SubRip file, playing anyway" );
349                 p_sys->i_type = SUB_TYPE_SUBRIP_DOT;
350                 break;
351             }
352             else if( !strncasecmp( s, "!: This is a Sub Station Alpha v1", 33 ) )
353             {
354                 p_sys->i_type = SUB_TYPE_SSA1;
355                 break;
356             }
357             else if( !strncasecmp( s, "ScriptType: v4.00+", 18 ) )
358             {
359                 p_sys->i_type = SUB_TYPE_ASS;
360                 break;
361             }
362             else if( !strncasecmp( s, "ScriptType: v4.00", 17 ) )
363             {
364                 p_sys->i_type = SUB_TYPE_SSA2_4;
365                 break;
366             }
367             else if( !strncasecmp( s, "Dialogue: Marked", 16  ) )
368             {
369                 p_sys->i_type = SUB_TYPE_SSA2_4;
370                 break;
371             }
372             else if( !strncasecmp( s, "Dialogue:", 9  ) )
373             {
374                 p_sys->i_type = SUB_TYPE_ASS;
375                 break;
376             }
377             else if( strcasestr( s, "[INFORMATION]" ) )
378             {
379                 p_sys->i_type = SUB_TYPE_SUBVIEWER; /* I hope this will work */
380                 break;
381             }
382             else if( sscanf( s, "%d:%d:%d.%d %d:%d:%d",
383                                  &i_dummy, &i_dummy, &i_dummy, &i_dummy,
384                                  &i_dummy, &i_dummy, &i_dummy ) == 7 ||
385                      sscanf( s, "@%d @%d", &i_dummy, &i_dummy) == 2)
386             {
387                 p_sys->i_type = SUB_TYPE_JACOSUB;
388                 break;
389             }
390             else if( sscanf( s, "%d:%d:%d:", &i_dummy, &i_dummy, &i_dummy ) == 3 ||
391                      sscanf( s, "%d:%d:%d ", &i_dummy, &i_dummy, &i_dummy ) == 3 )
392             {
393                 p_sys->i_type = SUB_TYPE_VPLAYER;
394                 break;
395             }
396             else if( sscanf( s, "{T %d:%d:%d:%d", &i_dummy, &i_dummy,
397                              &i_dummy, &i_dummy ) == 4 )
398             {
399                 p_sys->i_type = SUB_TYPE_DVDSUBTITLE;
400                 break;
401             }
402             else if( sscanf( s, "[%d:%d:%d]%c",
403                      &i_dummy, &i_dummy, &i_dummy, &p_dummy ) == 4 )
404             {
405                 p_sys->i_type = SUB_TYPE_DKS;
406                 break;
407             }
408             else if( strstr( s, "*** START SCRIPT" ) )
409             {
410                 p_sys->i_type = SUB_TYPE_SUBVIEW1;
411                 break;
412             }
413             else if( sscanf( s, "[%d][%d]", &i_dummy, &i_dummy ) == 2 ||
414                      sscanf( s, "[%d][]", &i_dummy ) == 1)
415             {
416                 p_sys->i_type = SUB_TYPE_MPL2;
417                 break;
418             }
419             else if( sscanf (s, "FORMAT=%d", &i_dummy) == 1 ||
420                      ( sscanf (s, "FORMAT=TIM%c", &p_dummy) == 1
421                        && p_dummy =='E' ) )
422             {
423                 p_sys->i_type = SUB_TYPE_MPSUB;
424                 break;
425             }
426             else if( sscanf( s, "-->> %d", &i_dummy) == 1 )
427             {
428                 p_sys->i_type = SUB_TYPE_AQT;
429                 break;
430             }
431             else if( sscanf( s, "%d,%d,", &i_dummy, &i_dummy ) == 2 )
432             {
433                 p_sys->i_type = SUB_TYPE_PJS;
434                 break;
435             }
436             else if( sscanf( s, "{%d:%d:%d}",
437                                 &i_dummy, &i_dummy, &i_dummy ) == 3 )
438             {
439                 p_sys->i_type = SUB_TYPE_PSB;
440                 break;
441             }
442             else if( strcasestr( s, "<time" ) )
443             {
444                 p_sys->i_type = SUB_TYPE_RT;
445                 break;
446             }
447
448             free( s );
449             s = NULL;
450         }
451
452         free( s );
453
454         /* It will nearly always work even for non seekable stream thanks the
455          * caching system, and if it fails we lose just a few sub */
456         if( stream_Seek( p_demux->s, unicode ? 3 : 0 ) )
457             msg_Warn( p_demux, "failed to rewind" );
458     }
459
460     /* Quit on unknown subtitles */
461     if( p_sys->i_type == SUB_TYPE_UNKNOWN )
462     {
463         stream_Seek( p_demux->s, 0 );
464         msg_Warn( p_demux, "failed to recognize subtitle type" );
465         free( p_sys );
466         return VLC_EGENERIC;
467     }
468
469     for( i = 0; ; i++ )
470     {
471         if( sub_read_subtitle_function[i].i_type == p_sys->i_type )
472         {
473             msg_Dbg( p_demux, "detected %s format",
474                      sub_read_subtitle_function[i].psz_name );
475             pf_read = sub_read_subtitle_function[i].pf_read;
476             break;
477         }
478     }
479
480     msg_Dbg( p_demux, "loading all subtitles..." );
481
482     /* Load the whole file */
483     TextLoad( &p_sys->txt, p_demux->s );
484
485     /* Parse it */
486     for( i_max = 0;; )
487     {
488         if( p_sys->i_subtitles >= i_max )
489         {
490             i_max += 500;
491             if( !( p_sys->subtitle = realloc_or_free( p_sys->subtitle,
492                                               sizeof(subtitle_t) * i_max ) ) )
493             {
494                 TextUnload( &p_sys->txt );
495                 free( p_sys );
496                 return VLC_ENOMEM;
497             }
498         }
499
500         if( pf_read( p_demux, &p_sys->subtitle[p_sys->i_subtitles],
501                      p_sys->i_subtitles ) )
502             break;
503
504         p_sys->i_subtitles++;
505     }
506     /* Unload */
507     TextUnload( &p_sys->txt );
508
509     msg_Dbg(p_demux, "loaded %d subtitles", p_sys->i_subtitles );
510
511     /* Fix subtitle (order and time) *** */
512     p_sys->i_subtitle = 0;
513     p_sys->i_length = 0;
514     if( p_sys->i_subtitles > 0 )
515     {
516         p_sys->i_length = p_sys->subtitle[p_sys->i_subtitles-1].i_stop;
517         /* +1 to avoid 0 */
518         if( p_sys->i_length <= 0 )
519             p_sys->i_length = p_sys->subtitle[p_sys->i_subtitles-1].i_start+1;
520     }
521
522     /* *** add subtitle ES *** */
523     if( p_sys->i_type == SUB_TYPE_SSA1 ||
524              p_sys->i_type == SUB_TYPE_SSA2_4 ||
525              p_sys->i_type == SUB_TYPE_ASS )
526     {
527         Fix( p_demux );
528         es_format_Init( &fmt, SPU_ES, VLC_CODEC_SSA );
529     }
530     else
531         es_format_Init( &fmt, SPU_ES, VLC_CODEC_SUBT );
532     if( unicode )
533         fmt.subs.psz_encoding = strdup( "UTF-8" );
534     char *psz_description = var_InheritString( p_demux, "sub-description" );
535     if( psz_description && *psz_description )
536         fmt.psz_description = psz_description;
537     else
538         free( psz_description );
539     if( p_sys->psz_header != NULL )
540     {
541         fmt.i_extra = strlen( p_sys->psz_header ) + 1;
542         fmt.p_extra = strdup( p_sys->psz_header );
543     }
544     p_sys->es = es_out_Add( p_demux->out, &fmt );
545     es_format_Clean( &fmt );
546
547     return VLC_SUCCESS;
548 }
549
550 /*****************************************************************************
551  * Close: Close subtitle demux
552  *****************************************************************************/
553 static void Close( vlc_object_t *p_this )
554 {
555     demux_t *p_demux = (demux_t*)p_this;
556     demux_sys_t *p_sys = p_demux->p_sys;
557     int i;
558
559     for( i = 0; i < p_sys->i_subtitles; i++ )
560         free( p_sys->subtitle[i].psz_text );
561     free( p_sys->subtitle );
562
563     free( p_sys );
564 }
565
566 /*****************************************************************************
567  * Control:
568  *****************************************************************************/
569 static int Control( demux_t *p_demux, int i_query, va_list args )
570 {
571     demux_sys_t *p_sys = p_demux->p_sys;
572     int64_t *pi64, i64;
573     double *pf, f;
574
575     switch( i_query )
576     {
577         case DEMUX_GET_LENGTH:
578             pi64 = (int64_t*)va_arg( args, int64_t * );
579             *pi64 = p_sys->i_length;
580             return VLC_SUCCESS;
581
582         case DEMUX_GET_TIME:
583             pi64 = (int64_t*)va_arg( args, int64_t * );
584             if( p_sys->i_subtitle < p_sys->i_subtitles )
585             {
586                 *pi64 = p_sys->subtitle[p_sys->i_subtitle].i_start;
587                 return VLC_SUCCESS;
588             }
589             return VLC_EGENERIC;
590
591         case DEMUX_SET_TIME:
592             i64 = (int64_t)va_arg( args, int64_t );
593             p_sys->i_subtitle = 0;
594             while( p_sys->i_subtitle < p_sys->i_subtitles )
595             {
596                 const subtitle_t *p_subtitle = &p_sys->subtitle[p_sys->i_subtitle];
597
598                 if( p_subtitle->i_start > i64 )
599                     break;
600                 if( p_subtitle->i_stop > p_subtitle->i_start && p_subtitle->i_stop > i64 )
601                     break;
602
603                 p_sys->i_subtitle++;
604             }
605
606             if( p_sys->i_subtitle >= p_sys->i_subtitles )
607                 return VLC_EGENERIC;
608             return VLC_SUCCESS;
609
610         case DEMUX_GET_POSITION:
611             pf = (double*)va_arg( args, double * );
612             if( p_sys->i_subtitle >= p_sys->i_subtitles )
613             {
614                 *pf = 1.0;
615             }
616             else if( p_sys->i_subtitles > 0 )
617             {
618                 *pf = (double)p_sys->subtitle[p_sys->i_subtitle].i_start /
619                       (double)p_sys->i_length;
620             }
621             else
622             {
623                 *pf = 0.0;
624             }
625             return VLC_SUCCESS;
626
627         case DEMUX_SET_POSITION:
628             f = (double)va_arg( args, double );
629             i64 = f * p_sys->i_length;
630
631             p_sys->i_subtitle = 0;
632             while( p_sys->i_subtitle < p_sys->i_subtitles &&
633                    p_sys->subtitle[p_sys->i_subtitle].i_start < i64 )
634             {
635                 p_sys->i_subtitle++;
636             }
637             if( p_sys->i_subtitle >= p_sys->i_subtitles )
638                 return VLC_EGENERIC;
639             return VLC_SUCCESS;
640
641         case DEMUX_SET_NEXT_DEMUX_TIME:
642             p_sys->i_next_demux_date = (int64_t)va_arg( args, int64_t );
643             return VLC_SUCCESS;
644
645         case DEMUX_GET_PTS_DELAY:
646         case DEMUX_GET_FPS:
647         case DEMUX_GET_META:
648         case DEMUX_GET_ATTACHMENTS:
649         case DEMUX_GET_TITLE_INFO:
650         case DEMUX_HAS_UNSUPPORTED_META:
651         case DEMUX_CAN_RECORD:
652             return VLC_EGENERIC;
653
654         default:
655             msg_Err( p_demux, "unknown query %d in subtitle control", i_query );
656             return VLC_EGENERIC;
657     }
658 }
659
660 /*****************************************************************************
661  * Demux: Send subtitle to decoder
662  *****************************************************************************/
663 static int Demux( demux_t *p_demux )
664 {
665     demux_sys_t *p_sys = p_demux->p_sys;
666     int64_t i_maxdate;
667
668     if( p_sys->i_subtitle >= p_sys->i_subtitles )
669         return 0;
670
671     i_maxdate = p_sys->i_next_demux_date - var_GetTime( p_demux->p_parent, "spu-delay" );;
672     if( i_maxdate <= 0 && p_sys->i_subtitle < p_sys->i_subtitles )
673     {
674         /* Should not happen */
675         i_maxdate = p_sys->subtitle[p_sys->i_subtitle].i_start + 1;
676     }
677
678     while( p_sys->i_subtitle < p_sys->i_subtitles &&
679            p_sys->subtitle[p_sys->i_subtitle].i_start < i_maxdate )
680     {
681         const subtitle_t *p_subtitle = &p_sys->subtitle[p_sys->i_subtitle];
682
683         block_t *p_block;
684         int i_len = strlen( p_subtitle->psz_text ) + 1;
685
686         if( i_len <= 1 || p_subtitle->i_start < 0 )
687         {
688             p_sys->i_subtitle++;
689             continue;
690         }
691
692         if( ( p_block = block_New( p_demux, i_len ) ) == NULL )
693         {
694             p_sys->i_subtitle++;
695             continue;
696         }
697
698         p_block->i_dts =
699         p_block->i_pts = VLC_TS_0 + p_subtitle->i_start;
700         if( p_subtitle->i_stop >= 0 && p_subtitle->i_stop >= p_subtitle->i_start )
701             p_block->i_length = p_subtitle->i_stop - p_subtitle->i_start;
702
703         memcpy( p_block->p_buffer, p_subtitle->psz_text, i_len );
704
705         es_out_Send( p_demux->out, p_sys->es, p_block );
706
707         p_sys->i_subtitle++;
708     }
709
710     /* */
711     p_sys->i_next_demux_date = 0;
712
713     return 1;
714 }
715
716 /*****************************************************************************
717  * Fix: fix time stamp and order of subtitle
718  *****************************************************************************/
719 static void Fix( demux_t *p_demux )
720 {
721     demux_sys_t *p_sys = p_demux->p_sys;
722     bool b_done;
723
724     /* *** fix order (to be sure...) *** */
725     /* We suppose that there are near in order and this durty bubble sort
726      * would not take too much time
727      */
728     do
729     {
730         b_done = true;
731         for( int i_index = 1; i_index < p_sys->i_subtitles; i_index++ )
732         {
733             if( p_sys->subtitle[i_index].i_start <
734                 p_sys->subtitle[i_index - 1].i_start )
735             {
736                 subtitle_t sub_xch;
737                 memcpy( &sub_xch,
738                         p_sys->subtitle + i_index - 1,
739                         sizeof( subtitle_t ) );
740                 memcpy( p_sys->subtitle + i_index - 1,
741                         p_sys->subtitle + i_index,
742                         sizeof( subtitle_t ) );
743                 memcpy( p_sys->subtitle + i_index,
744                         &sub_xch,
745                         sizeof( subtitle_t ) );
746                 b_done = false;
747             }
748         }
749     } while( !b_done );
750 }
751
752 static int TextLoad( text_t *txt, stream_t *s )
753 {
754     int   i_line_max;
755
756     /* init txt */
757     i_line_max          = 500;
758     txt->i_line_count   = 0;
759     txt->i_line         = 0;
760     txt->line           = calloc( i_line_max, sizeof( char * ) );
761     if( !txt->line )
762         return VLC_ENOMEM;
763
764     /* load the complete file */
765     for( ;; )
766     {
767         char *psz = stream_ReadLine( s );
768
769         if( psz == NULL )
770             break;
771
772         txt->line[txt->i_line_count++] = psz;
773         if( txt->i_line_count >= i_line_max )
774         {
775             i_line_max += 100;
776             txt->line = realloc_or_free( txt->line, i_line_max * sizeof( char * ) );
777             if( !txt->line )
778                 return VLC_ENOMEM;
779         }
780     }
781
782     if( txt->i_line_count <= 0 )
783     {
784         free( txt->line );
785         return VLC_EGENERIC;
786     }
787
788     return VLC_SUCCESS;
789 }
790 static void TextUnload( text_t *txt )
791 {
792     int i;
793
794     for( i = 0; i < txt->i_line_count; i++ )
795     {
796         free( txt->line[i] );
797     }
798     free( txt->line );
799     txt->i_line       = 0;
800     txt->i_line_count = 0;
801 }
802
803 static char *TextGetLine( text_t *txt )
804 {
805     if( txt->i_line >= txt->i_line_count )
806         return( NULL );
807
808     return txt->line[txt->i_line++];
809 }
810 static void TextPreviousLine( text_t *txt )
811 {
812     if( txt->i_line > 0 )
813         txt->i_line--;
814 }
815
816 /*****************************************************************************
817  * Specific Subtitle function
818  *****************************************************************************/
819 /* ParseMicroDvd:
820  *  Format:
821  *      {n1}{n2}Line1|Line2|Line3....
822  *  where n1 and n2 are the video frame number (n2 can be empty)
823  */
824 static int ParseMicroDvd( demux_t *p_demux, subtitle_t *p_subtitle,
825                           int i_idx )
826 {
827     VLC_UNUSED( i_idx );
828     demux_sys_t *p_sys = p_demux->p_sys;
829     text_t      *txt = &p_sys->txt;
830     char *psz_text;
831     int  i_start;
832     int  i_stop;
833     int  i;
834
835     for( ;; )
836     {
837         const char *s = TextGetLine( txt );
838         if( !s )
839             return VLC_EGENERIC;
840
841         psz_text = malloc( strlen(s) + 1 );
842         if( !psz_text )
843             return VLC_ENOMEM;
844
845         i_start = 0;
846         i_stop  = -1;
847         if( sscanf( s, "{%d}{}%[^\r\n]", &i_start, psz_text ) == 2 ||
848             sscanf( s, "{%d}{%d}%[^\r\n]", &i_start, &i_stop, psz_text ) == 3)
849         {
850             float f_fps;
851             if( i_start != 1 || i_stop != 1 )
852                 break;
853
854             /* We found a possible setting of the framerate "{1}{1}23.976" */
855             /* Check if it's usable, and if the sub-fps is not set */
856             f_fps = us_strtod( psz_text, NULL );
857             if( f_fps > 0.0 && var_GetFloat( p_demux, "sub-fps" ) <= 0.0 )
858                 p_sys->i_microsecperframe = (int64_t)((float)1000000 / f_fps);
859         }
860         free( psz_text );
861     }
862
863     /* replace | by \n */
864     for( i = 0; psz_text[i] != '\0'; i++ )
865     {
866         if( psz_text[i] == '|' )
867             psz_text[i] = '\n';
868     }
869
870     /* */
871     p_subtitle->i_start  = i_start * p_sys->i_microsecperframe;
872     p_subtitle->i_stop   = i_stop >= 0 ? (i_stop  * p_sys->i_microsecperframe) : -1;
873     p_subtitle->psz_text = psz_text;
874     return VLC_SUCCESS;
875 }
876
877 /* ParseSubRipSubViewer
878  *  Format SubRip
879  *      n
880  *      h1:m1:s1,d1 --> h2:m2:s2,d2
881  *      Line1
882  *      Line2
883  *      ....
884  *      [Empty line]
885  *  Format SubViewer v1/v2
886  *      h1:m1:s1.d1,h2:m2:s2.d2
887  *      Line1[br]Line2
888  *      Line3
889  *      ...
890  *      [empty line]
891  *  We ignore line number for SubRip
892  */
893 static int ParseSubRipSubViewer( demux_t *p_demux, subtitle_t *p_subtitle,
894                                  const char *psz_fmt,
895                                  bool b_replace_br )
896 {
897     demux_sys_t *p_sys = p_demux->p_sys;
898     text_t      *txt = &p_sys->txt;
899     char    *psz_text;
900
901     for( ;; )
902     {
903         const char *s = TextGetLine( txt );
904         int h1, m1, s1, d1, h2, m2, s2, d2;
905
906         if( !s )
907             return VLC_EGENERIC;
908
909         if( sscanf( s, psz_fmt,
910                     &h1, &m1, &s1, &d1,
911                     &h2, &m2, &s2, &d2 ) == 8 )
912         {
913             p_subtitle->i_start = ( (int64_t)h1 * 3600*1000 +
914                                     (int64_t)m1 * 60*1000 +
915                                     (int64_t)s1 * 1000 +
916                                     (int64_t)d1 ) * 1000;
917
918             p_subtitle->i_stop  = ( (int64_t)h2 * 3600*1000 +
919                                     (int64_t)m2 * 60*1000 +
920                                     (int64_t)s2 * 1000 +
921                                     (int64_t)d2 ) * 1000;
922             if( p_subtitle->i_start < p_subtitle->i_stop )
923                 break;
924         }
925     }
926
927     /* Now read text until an empty line */
928     psz_text = strdup("");
929     if( !psz_text )
930         return VLC_ENOMEM;
931
932     for( ;; )
933     {
934         const char *s = TextGetLine( txt );
935         int i_len;
936         int i_old;
937
938         i_len = s ? strlen( s ) : 0;
939         if( i_len <= 0 )
940         {
941             p_subtitle->psz_text = psz_text;
942             return VLC_SUCCESS;
943         }
944
945         i_old = strlen( psz_text );
946         psz_text = realloc_or_free( psz_text, i_old + i_len + 1 + 1 );
947         if( !psz_text )
948         {
949             return VLC_ENOMEM;
950         }
951         strcat( psz_text, s );
952         strcat( psz_text, "\n" );
953
954         /* replace [br] by \n */
955         if( b_replace_br )
956         {
957             char *p;
958
959             while( ( p = strstr( psz_text, "[br]" ) ) )
960             {
961                 *p++ = '\n';
962                 memmove( p, &p[3], strlen(&p[3])+1 );
963             }
964         }
965     }
966 }
967 /* ParseSubRip
968  */
969 static int  ParseSubRip( demux_t *p_demux, subtitle_t *p_subtitle,
970                          int i_idx )
971 {
972     VLC_UNUSED( i_idx );
973     return ParseSubRipSubViewer( p_demux, p_subtitle,
974                                  "%d:%d:%d,%d --> %d:%d:%d,%d",
975                                  false );
976 }
977 /* ParseSubRipDot
978  * Special version for buggy file using '.' instead of ','
979  */
980 static int  ParseSubRipDot( demux_t *p_demux, subtitle_t *p_subtitle,
981                             int i_idx )
982 {
983     VLC_UNUSED( i_idx );
984     return ParseSubRipSubViewer( p_demux, p_subtitle,
985                                  "%d:%d:%d.%d --> %d:%d:%d.%d",
986                                  false );
987 }
988 /* ParseSubViewer
989  */
990 static int  ParseSubViewer( demux_t *p_demux, subtitle_t *p_subtitle,
991                             int i_idx )
992 {
993     VLC_UNUSED( i_idx );
994
995     return ParseSubRipSubViewer( p_demux, p_subtitle,
996                                  "%d:%d:%d.%d,%d:%d:%d.%d",
997                                  true );
998 }
999
1000 /* ParseSSA
1001  */
1002 static int  ParseSSA( demux_t *p_demux, subtitle_t *p_subtitle,
1003                       int i_idx )
1004 {
1005     demux_sys_t *p_sys = p_demux->p_sys;
1006     text_t      *txt = &p_sys->txt;
1007
1008     for( ;; )
1009     {
1010         const char *s = TextGetLine( txt );
1011         int h1, m1, s1, c1, h2, m2, s2, c2;
1012         char *psz_text, *psz_temp;
1013         char temp[16];
1014
1015         if( !s )
1016             return VLC_EGENERIC;
1017
1018         /* We expect (SSA2-4):
1019          * Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
1020          * Dialogue: Marked=0,0:02:40.65,0:02:41.79,Wolf main,Cher,0000,0000,0000,,Et les enregistrements de ses ondes delta ?
1021          *
1022          * SSA-1 is similar but only has 8 commas up untill the subtitle text. Probably the Effect field is no present, but not 100 % sure.
1023          */
1024
1025         /* For ASS:
1026          * Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
1027          * Dialogue: Layer#,0:02:40.65,0:02:41.79,Wolf main,Cher,0000,0000,0000,,Et les enregistrements de ses ondes delta ?
1028          */
1029
1030         /* The output text is - at least, not removing numbers - 18 chars shorter than the input text. */
1031         psz_text = malloc( strlen(s) );
1032         if( !psz_text )
1033             return VLC_ENOMEM;
1034
1035         if( sscanf( s,
1036                     "Dialogue: %15[^,],%d:%d:%d.%d,%d:%d:%d.%d,%[^\r\n]",
1037                     temp,
1038                     &h1, &m1, &s1, &c1,
1039                     &h2, &m2, &s2, &c2,
1040                     psz_text ) == 10 )
1041         {
1042             /* The dec expects: ReadOrder, Layer, Style, Name, MarginL, MarginR, MarginV, Effect, Text */
1043             /* (Layer comes from ASS specs ... it's empty for SSA.) */
1044             if( p_sys->i_type == SUB_TYPE_SSA1 )
1045             {
1046                 /* SSA1 has only 8 commas before the text starts, not 9 */
1047                 memmove( &psz_text[1], psz_text, strlen(psz_text)+1 );
1048                 psz_text[0] = ',';
1049             }
1050             else
1051             {
1052                 int i_layer = ( p_sys->i_type == SUB_TYPE_ASS ) ? atoi( temp ) : 0;
1053
1054                 /* ReadOrder, Layer, %s(rest of fields) */
1055                 if( asprintf( &psz_temp, "%d,%d,%s", i_idx, i_layer, psz_text ) == -1 )
1056                 {
1057                     free( psz_text );
1058                     return VLC_ENOMEM;
1059                 }
1060
1061                 free( psz_text );
1062                 psz_text = psz_temp;
1063             }
1064
1065             p_subtitle->i_start = ( (int64_t)h1 * 3600*1000 +
1066                                     (int64_t)m1 * 60*1000 +
1067                                     (int64_t)s1 * 1000 +
1068                                     (int64_t)c1 * 10 ) * 1000;
1069             p_subtitle->i_stop  = ( (int64_t)h2 * 3600*1000 +
1070                                     (int64_t)m2 * 60*1000 +
1071                                     (int64_t)s2 * 1000 +
1072                                     (int64_t)c2 * 10 ) * 1000;
1073             p_subtitle->psz_text = psz_text;
1074             return VLC_SUCCESS;
1075         }
1076         free( psz_text );
1077
1078         /* All the other stuff we add to the header field */
1079         char *psz_header;
1080         if( asprintf( &psz_header, "%s%s\n",
1081                        p_sys->psz_header ? p_sys->psz_header : "", s ) == -1 )
1082             return VLC_ENOMEM;
1083         p_sys->psz_header = psz_header;
1084     }
1085 }
1086
1087 /* ParseVplayer
1088  *  Format
1089  *      h:m:s:Line1|Line2|Line3....
1090  *  or
1091  *      h:m:s Line1|Line2|Line3....
1092  */
1093 static int ParseVplayer( demux_t *p_demux, subtitle_t *p_subtitle,
1094                           int i_idx )
1095 {
1096     VLC_UNUSED( i_idx );
1097
1098     demux_sys_t *p_sys = p_demux->p_sys;
1099     text_t      *txt = &p_sys->txt;
1100     char *psz_text;
1101     int i;
1102
1103     for( ;; )
1104     {
1105         const char *s = TextGetLine( txt );
1106         int h1, m1, s1;
1107
1108         if( !s )
1109             return VLC_EGENERIC;
1110
1111         psz_text = malloc( strlen( s ) + 1 );
1112         if( !psz_text )
1113             return VLC_ENOMEM;
1114
1115         if( sscanf( s, "%d:%d:%d%*c%[^\r\n]",
1116                     &h1, &m1, &s1, psz_text ) == 4 )
1117         {
1118             p_subtitle->i_start = ( (int64_t)h1 * 3600*1000 +
1119                                     (int64_t)m1 * 60*1000 +
1120                                     (int64_t)s1 * 1000 ) * 1000;
1121             p_subtitle->i_stop  = -1;
1122             break;
1123         }
1124         free( psz_text );
1125     }
1126
1127     /* replace | by \n */
1128     for( i = 0; psz_text[i] != '\0'; i++ )
1129     {
1130         if( psz_text[i] == '|' )
1131             psz_text[i] = '\n';
1132     }
1133     p_subtitle->psz_text = psz_text;
1134     return VLC_SUCCESS;
1135 }
1136
1137 /* ParseSami
1138  */
1139 static char *ParseSamiSearch( text_t *txt,
1140                               char *psz_start, const char *psz_str )
1141 {
1142     if( psz_start && strcasestr( psz_start, psz_str ) )
1143     {
1144         char *s = strcasestr( psz_start, psz_str );
1145         return &s[strlen( psz_str )];
1146     }
1147
1148     for( ;; )
1149     {
1150         char *p = TextGetLine( txt );
1151         if( !p )
1152             return NULL;
1153
1154         if( strcasestr( p, psz_str ) )
1155         {
1156             char *s = strcasestr( p, psz_str );
1157             return &s[strlen( psz_str )];
1158         }
1159     }
1160 }
1161 static int  ParseSami( demux_t *p_demux, subtitle_t *p_subtitle, int i_idx )
1162 {
1163     VLC_UNUSED( i_idx );
1164     demux_sys_t *p_sys = p_demux->p_sys;
1165     text_t      *txt = &p_sys->txt;
1166
1167     char *s;
1168     int64_t i_start;
1169
1170     unsigned int i_text;
1171     char text[8192]; /* Arbitrary but should be long enough */
1172
1173     /* search "Start=" */
1174     if( !( s = ParseSamiSearch( txt, NULL, "Start=" ) ) )
1175         return VLC_EGENERIC;
1176
1177     /* get start value */
1178     i_start = strtol( s, &s, 0 );
1179
1180     /* search <P */
1181     if( !( s = ParseSamiSearch( txt, s, "<P" ) ) )
1182         return VLC_EGENERIC;
1183
1184     /* search > */
1185     if( !( s = ParseSamiSearch( txt, s, ">" ) ) )
1186         return VLC_EGENERIC;
1187
1188     i_text = 0;
1189     text[0] = '\0';
1190     /* now get all txt until  a "Start=" line */
1191     for( ;; )
1192     {
1193         char c = '\0';
1194         /* Search non empty line */
1195         while( s && *s == '\0' )
1196             s = TextGetLine( txt );
1197         if( !s )
1198             break;
1199
1200         if( *s == '<' )
1201         {
1202             if( !strncasecmp( s, "<br", 3 ) )
1203             {
1204                 c = '\n';
1205             }
1206             else if( strcasestr( s, "Start=" ) )
1207             {
1208                 TextPreviousLine( txt );
1209                 break;
1210             }
1211             s = ParseSamiSearch( txt, s, ">" );
1212         }
1213         else if( !strncmp( s, "&nbsp;", 6 ) )
1214         {
1215             c = ' ';
1216             s += 6;
1217         }
1218         else if( *s == '\t' )
1219         {
1220             c = ' ';
1221             s++;
1222         }
1223         else
1224         {
1225             c = *s;
1226             s++;
1227         }
1228         if( c != '\0' && i_text+1 < sizeof(text) )
1229         {
1230             text[i_text++] = c;
1231             text[i_text] = '\0';
1232         }
1233     }
1234
1235     p_subtitle->i_start = i_start * 1000;
1236     p_subtitle->i_stop  = -1;
1237     p_subtitle->psz_text = strdup( text );
1238
1239     return VLC_SUCCESS;
1240 }
1241
1242 /* ParseDVDSubtitle
1243  *  Format
1244  *      {T h1:m1:s1:c1
1245  *      Line1
1246  *      Line2
1247  *      ...
1248  *      }
1249  * TODO it can have a header
1250  *      { HEAD
1251  *          ...
1252  *          CODEPAGE=...
1253  *          FORMAT=...
1254  *          LANG=English
1255  *      }
1256  *      LANG support would be cool
1257  *      CODEPAGE is probably mandatory FIXME
1258  */
1259 static int ParseDVDSubtitle( demux_t *p_demux, subtitle_t *p_subtitle,
1260                              int i_idx )
1261 {
1262     VLC_UNUSED( i_idx );
1263
1264     demux_sys_t *p_sys = p_demux->p_sys;
1265     text_t      *txt = &p_sys->txt;
1266     char *psz_text;
1267
1268     for( ;; )
1269     {
1270         const char *s = TextGetLine( txt );
1271         int h1, m1, s1, c1;
1272
1273         if( !s )
1274             return VLC_EGENERIC;
1275
1276         if( sscanf( s,
1277                     "{T %d:%d:%d:%d",
1278                     &h1, &m1, &s1, &c1 ) == 4 )
1279         {
1280             p_subtitle->i_start = ( (int64_t)h1 * 3600*1000 +
1281                                     (int64_t)m1 * 60*1000 +
1282                                     (int64_t)s1 * 1000 +
1283                                     (int64_t)c1 * 10) * 1000;
1284             p_subtitle->i_stop = -1;
1285             break;
1286         }
1287     }
1288
1289     /* Now read text until a line containing "}" */
1290     psz_text = strdup("");
1291     if( !psz_text )
1292         return VLC_ENOMEM;
1293     for( ;; )
1294     {
1295         const char *s = TextGetLine( txt );
1296         int i_len;
1297         int i_old;
1298
1299         if( !s )
1300         {
1301             free( psz_text );
1302             return VLC_EGENERIC;
1303         }
1304
1305         i_len = strlen( s );
1306         if( i_len == 1 && s[0] == '}')
1307         {
1308             p_subtitle->psz_text = psz_text;
1309             return VLC_SUCCESS;
1310         }
1311
1312         i_old = strlen( psz_text );
1313         psz_text = realloc_or_free( psz_text, i_old + i_len + 1 + 1 );
1314         if( !psz_text )
1315             return VLC_ENOMEM;
1316         strcat( psz_text, s );
1317         strcat( psz_text, "\n" );
1318     }
1319 }
1320
1321 /* ParseMPL2
1322  *  Format
1323  *     [n1][n2]Line1|Line2|Line3...
1324  *  where n1 and n2 are the video frame number (n2 can be empty)
1325  */
1326 static int ParseMPL2( demux_t *p_demux, subtitle_t *p_subtitle, int i_idx )
1327 {
1328     VLC_UNUSED( i_idx );
1329
1330     demux_sys_t *p_sys = p_demux->p_sys;
1331     text_t      *txt = &p_sys->txt;
1332     char *psz_text;
1333     int i;
1334
1335     for( ;; )
1336     {
1337         const char *s = TextGetLine( txt );
1338         int i_start;
1339         int i_stop;
1340
1341         if( !s )
1342             return VLC_EGENERIC;
1343
1344         psz_text = malloc( strlen(s) + 1 );
1345         if( !psz_text )
1346             return VLC_ENOMEM;
1347
1348         i_start = 0;
1349         i_stop  = -1;
1350         if( sscanf( s, "[%d][] %[^\r\n]", &i_start, psz_text ) == 2 ||
1351             sscanf( s, "[%d][%d] %[^\r\n]", &i_start, &i_stop, psz_text ) == 3)
1352         {
1353             p_subtitle->i_start = (int64_t)i_start * 100000;
1354             p_subtitle->i_stop  = i_stop >= 0 ? ((int64_t)i_stop  * 100000) : -1;
1355             break;
1356         }
1357         free( psz_text );
1358     }
1359
1360     for( i = 0; psz_text[i] != '\0'; )
1361     {
1362         /* replace | by \n */
1363         if( psz_text[i] == '|' )
1364             psz_text[i] = '\n';
1365
1366         /* Remove italic */
1367         if( psz_text[i] == '/' && ( i == 0 || psz_text[i-1] == '\n' ) )
1368             memmove( &psz_text[i], &psz_text[i+1], strlen(&psz_text[i+1])+1 );
1369         else
1370             i++;
1371     }
1372     p_subtitle->psz_text = psz_text;
1373     return VLC_SUCCESS;
1374 }
1375
1376 static int ParseAQT( demux_t *p_demux, subtitle_t *p_subtitle, int i_idx )
1377 {
1378     VLC_UNUSED( i_idx );
1379
1380     demux_sys_t *p_sys = p_demux->p_sys;
1381     text_t      *txt = &p_sys->txt;
1382     char *psz_text = strdup( "" );
1383     int i_old = 0;
1384     int i_firstline = 1;
1385
1386     for( ;; )
1387     {
1388         int t; /* Time */
1389
1390         const char *s = TextGetLine( txt );
1391
1392         if( !s )
1393         {
1394             free( psz_text );
1395             return VLC_EGENERIC;
1396         }
1397
1398         /* Data Lines */
1399         if( sscanf (s, "-->> %d", &t) == 1)
1400         {
1401             p_subtitle->i_start = (int64_t)t; /* * FPS*/
1402             p_subtitle->i_stop  = -1;
1403
1404             /* Starting of a subtitle */
1405             if( i_firstline )
1406             {
1407                 i_firstline = 0;
1408             }
1409             /* We have been too far: end of the subtitle, begin of next */
1410             else
1411             {
1412                 TextPreviousLine( txt );
1413                 break;
1414             }
1415         }
1416         /* Text Lines */
1417         else
1418         {
1419             i_old = strlen( psz_text ) + 1;
1420             psz_text = realloc_or_free( psz_text, i_old + strlen( s ) + 1 );
1421             if( !psz_text )
1422                  return VLC_ENOMEM;
1423             strcat( psz_text, s );
1424             strcat( psz_text, "\n" );
1425             if( txt->i_line == txt->i_line_count )
1426                 break;
1427         }
1428     }
1429     p_subtitle->psz_text = psz_text;
1430     return VLC_SUCCESS;
1431 }
1432
1433 static int ParsePJS( demux_t *p_demux, subtitle_t *p_subtitle, int i_idx )
1434 {
1435     VLC_UNUSED( i_idx );
1436
1437     demux_sys_t *p_sys = p_demux->p_sys;
1438     text_t      *txt = &p_sys->txt;
1439     char *psz_text;
1440     int i;
1441
1442     for( ;; )
1443     {
1444         const char *s = TextGetLine( txt );
1445         int t1, t2;
1446
1447         if( !s )
1448             return VLC_EGENERIC;
1449
1450         psz_text = malloc( strlen(s) + 1 );
1451         if( !psz_text )
1452             return VLC_ENOMEM;
1453
1454         /* Data Lines */
1455         if( sscanf (s, "%d,%d,\"%[^\n\r]", &t1, &t2, psz_text ) == 3 )
1456         {
1457             /* 1/10th of second ? Frame based ? FIXME */
1458             p_subtitle->i_start = 10 * t1;
1459             p_subtitle->i_stop = 10 * t2;
1460             /* Remove latest " */
1461             psz_text[ strlen(psz_text) - 1 ] = '\0';
1462
1463             break;
1464         }
1465         free( psz_text );
1466     }
1467
1468     /* replace | by \n */
1469     for( i = 0; psz_text[i] != '\0'; i++ )
1470     {
1471         if( psz_text[i] == '|' )
1472             psz_text[i] = '\n';
1473     }
1474
1475     p_subtitle->psz_text = psz_text;
1476     msg_Dbg( p_demux, "%s", psz_text );
1477     return VLC_SUCCESS;
1478 }
1479
1480 static int ParseMPSub( demux_t *p_demux, subtitle_t *p_subtitle, int i_idx )
1481 {
1482     VLC_UNUSED( i_idx );
1483
1484     demux_sys_t *p_sys = p_demux->p_sys;
1485     text_t      *txt = &p_sys->txt;
1486     char *psz_text = strdup( "" );
1487
1488     if( !p_sys->mpsub.b_inited )
1489     {
1490         p_sys->mpsub.f_total = 0.0;
1491         p_sys->mpsub.f_factor = 0.0;
1492
1493         p_sys->mpsub.b_inited = true;
1494     }
1495
1496     for( ;; )
1497     {
1498         float f1, f2;
1499         char p_dummy;
1500         char *psz_temp;
1501
1502         const char *s = TextGetLine( txt );
1503         if( !s )
1504         {
1505             free( psz_text );
1506             return VLC_EGENERIC;
1507         }
1508
1509         if( strstr( s, "FORMAT" ) )
1510         {
1511             if( sscanf (s, "FORMAT=TIM%c", &p_dummy ) == 1 && p_dummy == 'E')
1512             {
1513                 p_sys->mpsub.f_factor = 100.0;
1514                 break;
1515             }
1516
1517             psz_temp = malloc( strlen(s) );
1518             if( !psz_temp )
1519             {
1520                 free( psz_text );
1521                 return VLC_ENOMEM;
1522             }
1523
1524             if( sscanf( s, "FORMAT=%[^\r\n]", psz_temp ) )
1525             {
1526                 float f_fps;
1527                 f_fps = us_strtod( psz_temp, NULL );
1528                 if( f_fps > 0.0 && var_GetFloat( p_demux, "sub-fps" ) <= 0.0 )
1529                     var_SetFloat( p_demux, "sub-fps", f_fps );
1530
1531                 p_sys->mpsub.f_factor = 1.0;
1532                 free( psz_temp );
1533                 break;
1534             }
1535             free( psz_temp );
1536         }
1537         /* Data Lines */
1538         f1 = us_strtod( s, &psz_temp );
1539         if( *psz_temp )
1540         {
1541             f2 = us_strtod( psz_temp, NULL );
1542             p_sys->mpsub.f_total += f1 * p_sys->mpsub.f_factor;
1543             p_subtitle->i_start = (int64_t)(10000.0 * p_sys->mpsub.f_total);
1544             p_sys->mpsub.f_total += f2 * p_sys->mpsub.f_factor;
1545             p_subtitle->i_stop = (int64_t)(10000.0 * p_sys->mpsub.f_total);
1546             break;
1547         }
1548     }
1549
1550     for( ;; )
1551     {
1552         const char *s = TextGetLine( txt );
1553
1554         if( !s )
1555         {
1556             free( psz_text );
1557             return VLC_EGENERIC;
1558         }
1559
1560         int i_len = strlen( s );
1561         if( i_len == 0 )
1562             break;
1563
1564         int i_old = strlen( psz_text );
1565
1566         psz_text = realloc_or_free( psz_text, i_old + i_len + 1 + 1 );
1567         if( !psz_text )
1568              return VLC_ENOMEM;
1569
1570         strcat( psz_text, s );
1571         strcat( psz_text, "\n" );
1572     }
1573
1574     p_subtitle->psz_text = psz_text;
1575     return VLC_SUCCESS;
1576 }
1577
1578 static int ParseJSS( demux_t *p_demux, subtitle_t *p_subtitle, int i_idx )
1579 {
1580     VLC_UNUSED( i_idx );
1581
1582     demux_sys_t  *p_sys = p_demux->p_sys;
1583     text_t       *txt = &p_sys->txt;
1584     char         *psz_text, *psz_orig;
1585     char         *psz_text2, *psz_orig2;
1586     int h1, h2, m1, m2, s1, s2, f1, f2;
1587
1588     if( !p_sys->jss.b_inited )
1589     {
1590         p_sys->jss.i_comment = 0;
1591         p_sys->jss.i_time_resolution = 30;
1592         p_sys->jss.i_time_shift = 0;
1593
1594         p_sys->jss.b_inited = true;
1595     }
1596
1597     /* Parse the main lines */
1598     for( ;; )
1599     {
1600         const char *s = TextGetLine( txt );
1601         if( !s )
1602             return VLC_EGENERIC;
1603
1604         psz_orig = malloc( strlen( s ) + 1 );
1605         if( !psz_orig )
1606             return VLC_ENOMEM;
1607         psz_text = psz_orig;
1608
1609         /* Complete time lines */
1610         if( sscanf( s, "%d:%d:%d.%d %d:%d:%d.%d %[^\n\r]",
1611                     &h1, &m1, &s1, &f1, &h2, &m2, &s2, &f2, psz_text ) == 9 )
1612         {
1613             p_subtitle->i_start = ( (int64_t)( h1 *3600 + m1 * 60 + s1 ) +
1614                 (int64_t)( ( f1 +  p_sys->jss.i_time_shift ) /  p_sys->jss.i_time_resolution ) )
1615                 * 1000000;
1616             p_subtitle->i_stop = ( (int64_t)( h2 *3600 + m2 * 60 + s2 ) +
1617                 (int64_t)( ( f2 +  p_sys->jss.i_time_shift ) /  p_sys->jss.i_time_resolution ) )
1618                 * 1000000;
1619             break;
1620         }
1621         /* Short time lines */
1622         else if( sscanf( s, "@%d @%d %[^\n\r]", &f1, &f2, psz_text ) == 3 )
1623         {
1624             p_subtitle->i_start = (int64_t)(
1625                     ( f1 + p_sys->jss.i_time_shift ) / p_sys->jss.i_time_resolution * 1000000.0 );
1626             p_subtitle->i_stop = (int64_t)(
1627                     ( f2 + p_sys->jss.i_time_shift ) / p_sys->jss.i_time_resolution * 1000000.0 );
1628             break;
1629         }
1630         /* General Directive lines */
1631         /* Only TIME and SHIFT are supported so far */
1632         else if( s[0] == '#' )
1633         {
1634             int h = 0, m =0, sec = 1, f = 1;
1635             unsigned shift = 1;
1636             int inv = 1;
1637
1638             strcpy( psz_text, s );
1639
1640             switch( toupper( (unsigned char)psz_text[1] ) )
1641             {
1642             case 'S':
1643                  shift = isalpha( (unsigned char)psz_text[2] ) ? 6 : 2 ;
1644
1645                  if( sscanf( &psz_text[shift], "%d", &h ) )
1646                  {
1647                      /* Negative shifting */
1648                      if( h < 0 )
1649                      {
1650                          h *= -1;
1651                          inv = -1;
1652                      }
1653
1654                      if( sscanf( &psz_text[shift], "%*d:%d", &m ) )
1655                      {
1656                          if( sscanf( &psz_text[shift], "%*d:%*d:%d", &sec ) )
1657                          {
1658                              sscanf( &psz_text[shift], "%*d:%*d:%*d.%d", &f );
1659                          }
1660                          else
1661                          {
1662                              h = 0;
1663                              sscanf( &psz_text[shift], "%d:%d.%d",
1664                                      &m, &sec, &f );
1665                              m *= inv;
1666                          }
1667                      }
1668                      else
1669                      {
1670                          h = m = 0;
1671                          sscanf( &psz_text[shift], "%d.%d", &sec, &f);
1672                          sec *= inv;
1673                      }
1674                      p_sys->jss.i_time_shift = ( ( h * 3600 + m * 60 + sec )
1675                          * p_sys->jss.i_time_resolution + f ) * inv;
1676                  }
1677                  break;
1678
1679             case 'T':
1680                 shift = isalpha( (unsigned char)psz_text[2] ) ? 8 : 2 ;
1681
1682                 sscanf( &psz_text[shift], "%d", &p_sys->jss.i_time_resolution );
1683                 break;
1684             }
1685             free( psz_orig );
1686             continue;
1687         }
1688         else
1689             /* Unkown type line, probably a comment */
1690         {
1691             free( psz_orig );
1692             continue;
1693         }
1694     }
1695
1696     while( psz_text[ strlen( psz_text ) - 1 ] == '\\' )
1697     {
1698         const char *s2 = TextGetLine( txt );
1699
1700         if( !s2 )
1701         {
1702             free( psz_orig );
1703             return VLC_EGENERIC;
1704         }
1705
1706         int i_len = strlen( s2 );
1707         if( i_len == 0 )
1708             break;
1709
1710         int i_old = strlen( psz_text );
1711
1712         psz_text = realloc_or_free( psz_text, i_old + i_len + 1 );
1713         if( !psz_text )
1714              return VLC_ENOMEM;
1715
1716         psz_orig = psz_text;
1717         strcat( psz_text, s2 );
1718     }
1719
1720     /* Skip the blanks */
1721     while( *psz_text == ' ' || *psz_text == '\t' ) psz_text++;
1722
1723     /* Parse the directives */
1724     if( isalpha( (unsigned char)*psz_text ) || *psz_text == '[' )
1725     {
1726         while( *psz_text != ' ' )
1727         { psz_text++ ;};
1728
1729         /* Directives are NOT parsed yet */
1730         /* This has probably a better place in a decoder ? */
1731         /* directive = malloc( strlen( psz_text ) + 1 );
1732            if( sscanf( psz_text, "%s %[^\n\r]", directive, psz_text2 ) == 2 )*/
1733     }
1734
1735     /* Skip the blanks after directives */
1736     while( *psz_text == ' ' || *psz_text == '\t' ) psz_text++;
1737
1738     /* Clean all the lines from inline comments and other stuffs */
1739     psz_orig2 = calloc( strlen( psz_text) + 1, 1 );
1740     psz_text2 = psz_orig2;
1741
1742     for( ; *psz_text != '\0' && *psz_text != '\n' && *psz_text != '\r'; )
1743     {
1744         switch( *psz_text )
1745         {
1746         case '{':
1747             p_sys->jss.i_comment++;
1748             break;
1749         case '}':
1750             if( p_sys->jss.i_comment )
1751             {
1752                 p_sys->jss.i_comment = 0;
1753                 if( (*(psz_text + 1 ) ) == ' ' ) psz_text++;
1754             }
1755             break;
1756         case '~':
1757             if( !p_sys->jss.i_comment )
1758             {
1759                 *psz_text2 = ' ';
1760                 psz_text2++;
1761             }
1762             break;
1763         case ' ':
1764         case '\t':
1765             if( (*(psz_text + 1 ) ) == ' ' || (*(psz_text + 1 ) ) == '\t' )
1766                 break;
1767             if( !p_sys->jss.i_comment )
1768             {
1769                 *psz_text2 = ' ';
1770                 psz_text2++;
1771             }
1772             break;
1773         case '\\':
1774             if( (*(psz_text + 1 ) ) == 'n' )
1775             {
1776                 *psz_text2 = '\n';
1777                 psz_text++;
1778                 psz_text2++;
1779                 break;
1780             }
1781             if( ( toupper((unsigned char)*(psz_text + 1 ) ) == 'C' ) ||
1782                     ( toupper((unsigned char)*(psz_text + 1 ) ) == 'F' ) )
1783             {
1784                 psz_text++; psz_text++;
1785                 break;
1786             }
1787             if( (*(psz_text + 1 ) ) == 'B' || (*(psz_text + 1 ) ) == 'b' ||
1788                 (*(psz_text + 1 ) ) == 'I' || (*(psz_text + 1 ) ) == 'i' ||
1789                 (*(psz_text + 1 ) ) == 'U' || (*(psz_text + 1 ) ) == 'u' ||
1790                 (*(psz_text + 1 ) ) == 'D' || (*(psz_text + 1 ) ) == 'N' )
1791             {
1792                 psz_text++;
1793                 break;
1794             }
1795             if( (*(psz_text + 1 ) ) == '~' || (*(psz_text + 1 ) ) == '{' ||
1796                 (*(psz_text + 1 ) ) == '\\' )
1797                 psz_text++;
1798             else if( *(psz_text + 1 ) == '\r' ||  *(psz_text + 1 ) == '\n' ||
1799                      *(psz_text + 1 ) == '\0' )
1800             {
1801                 psz_text++;
1802             }
1803             break;
1804         default:
1805             if( !p_sys->jss.i_comment )
1806             {
1807                 *psz_text2 = *psz_text;
1808                 psz_text2++;
1809             }
1810         }
1811         psz_text++;
1812     }
1813
1814     p_subtitle->psz_text = psz_orig2;
1815     msg_Dbg( p_demux, "%s", p_subtitle->psz_text );
1816     free( psz_orig );
1817     return VLC_SUCCESS;
1818 }
1819
1820 static int ParsePSB( demux_t *p_demux, subtitle_t *p_subtitle, int i_idx )
1821 {
1822     VLC_UNUSED( i_idx );
1823
1824     demux_sys_t *p_sys = p_demux->p_sys;
1825     text_t      *txt = &p_sys->txt;
1826     char *psz_text;
1827     int i;
1828
1829     for( ;; )
1830     {
1831         int h1, m1, s1;
1832         int h2, m2, s2;
1833         const char *s = TextGetLine( txt );
1834
1835         if( !s )
1836             return VLC_EGENERIC;
1837
1838         psz_text = malloc( strlen( s ) + 1 );
1839         if( !psz_text )
1840             return VLC_ENOMEM;
1841
1842         if( sscanf( s, "{%d:%d:%d}{%d:%d:%d}%[^\r\n]",
1843                     &h1, &m1, &s1, &h2, &m2, &s2, psz_text ) == 7 )
1844         {
1845             p_subtitle->i_start = ( (int64_t)h1 * 3600*1000 +
1846                                     (int64_t)m1 * 60*1000 +
1847                                     (int64_t)s1 * 1000 ) * 1000;
1848             p_subtitle->i_stop  = ( (int64_t)h2 * 3600*1000 +
1849                                     (int64_t)m2 * 60*1000 +
1850                                     (int64_t)s2 * 1000 ) * 1000;
1851             break;
1852         }
1853         free( psz_text );
1854     }
1855
1856     /* replace | by \n */
1857     for( i = 0; psz_text[i] != '\0'; i++ )
1858     {
1859         if( psz_text[i] == '|' )
1860             psz_text[i] = '\n';
1861     }
1862     p_subtitle->psz_text = psz_text;
1863     return VLC_SUCCESS;
1864 }
1865
1866 static int64_t ParseRealTime( char *psz, int *h, int *m, int *s, int *f )
1867 {
1868     if( *psz == '\0' ) return 0;
1869     if( sscanf( psz, "%d:%d:%d.%d", h, m, s, f ) == 4 ||
1870             sscanf( psz, "%d:%d.%d", m, s, f ) == 3 ||
1871             sscanf( psz, "%d.%d", s, f ) == 2 ||
1872             sscanf( psz, "%d:%d", m, s ) == 2 ||
1873             sscanf( psz, "%d", s ) == 1 )
1874     {
1875         return (int64_t)((( *h * 60 + *m ) * 60 ) + *s ) * 1000 * 1000
1876                + (int64_t)*f * 10 * 1000;
1877     }
1878     else return VLC_EGENERIC;
1879 }
1880
1881 static int ParseRealText( demux_t *p_demux, subtitle_t *p_subtitle, int i_idx )
1882 {
1883     VLC_UNUSED( i_idx );
1884     demux_sys_t *p_sys = p_demux->p_sys;
1885     text_t      *txt = &p_sys->txt;
1886     char *psz_text = NULL;
1887
1888     for( ;; )
1889     {
1890         int h1 = 0, m1 = 0, s1 = 0, f1 = 0;
1891         int h2 = 0, m2 = 0, s2 = 0, f2 = 0;
1892         const char *s = TextGetLine( txt );
1893         free( psz_text );
1894
1895         if( !s )
1896             return VLC_EGENERIC;
1897
1898         psz_text = malloc( strlen( s ) + 1 );
1899         if( !psz_text )
1900             return VLC_ENOMEM;
1901
1902         /* Find the good begining. This removes extra spaces at the beginning
1903            of the line.*/
1904         char *psz_temp = strcasestr( s, "<time");
1905         if( psz_temp != NULL )
1906         {
1907             char psz_end[12], psz_begin[12];
1908             /* Line has begin and end */
1909             if( ( sscanf( psz_temp,
1910                   "<%*[t|T]ime %*[b|B]egin=\"%11[^\"]\" %*[e|E]nd=\"%11[^\"]%*[^>]%[^\n\r]",
1911                             psz_begin, psz_end, psz_text) != 3 ) &&
1912                     /* Line has begin and no end */
1913                     ( sscanf( psz_temp,
1914                               "<%*[t|T]ime %*[b|B]egin=\"%11[^\"]\"%*[^>]%[^\n\r]",
1915                               psz_begin, psz_text ) != 2) )
1916                 /* Line is not recognized */
1917             {
1918                 continue;
1919             }
1920
1921             /* Get the times */
1922             int64_t i_time = ParseRealTime( psz_begin, &h1, &m1, &s1, &f1 );
1923             p_subtitle->i_start = i_time >= 0 ? i_time : 0;
1924
1925             i_time = ParseRealTime( psz_end, &h2, &m2, &s2, &f2 );
1926             p_subtitle->i_stop = i_time >= 0 ? i_time : -1;
1927             break;
1928         }
1929     }
1930
1931     /* Get the following Lines */
1932     for( ;; )
1933     {
1934         const char *s = TextGetLine( txt );
1935
1936         if( !s )
1937         {
1938             free( psz_text );
1939             return VLC_EGENERIC;
1940         }
1941
1942         int i_len = strlen( s );
1943         if( i_len == 0 ) break;
1944
1945         if( strcasestr( s, "<time" ) ||
1946             strcasestr( s, "<clear/") )
1947         {
1948             TextPreviousLine( txt );
1949             break;
1950         }
1951
1952         int i_old = strlen( psz_text );
1953
1954         psz_text = realloc_or_free( psz_text, i_old + i_len + 1 + 1 );
1955         if( !psz_text )
1956             return VLC_ENOMEM;
1957
1958         strcat( psz_text, s );
1959         strcat( psz_text, "\n" );
1960     }
1961
1962     /* Remove the starting ">" that remained after the sscanf */
1963     memmove( &psz_text[0], &psz_text[1], strlen( psz_text ) );
1964
1965     p_subtitle->psz_text = psz_text;
1966
1967     return VLC_SUCCESS;
1968 }
1969
1970 static int ParseDKS( demux_t *p_demux, subtitle_t *p_subtitle, int i_idx )
1971 {
1972     VLC_UNUSED( i_idx );
1973
1974     demux_sys_t *p_sys = p_demux->p_sys;
1975     text_t      *txt = &p_sys->txt;
1976     char *psz_text;
1977
1978     for( ;; )
1979     {
1980         int h1, m1, s1;
1981         int h2, m2, s2;
1982         char *s = TextGetLine( txt );
1983
1984         if( !s )
1985             return VLC_EGENERIC;
1986
1987         psz_text = malloc( strlen( s ) + 1 );
1988         if( !psz_text )
1989             return VLC_ENOMEM;
1990
1991         if( sscanf( s, "[%d:%d:%d]%[^\r\n]",
1992                     &h1, &m1, &s1, psz_text ) == 4 )
1993         {
1994             p_subtitle->i_start = ( (int64_t)h1 * 3600*1000 +
1995                                     (int64_t)m1 * 60*1000 +
1996                                     (int64_t)s1 * 1000 ) * 1000;
1997
1998             char *s = TextGetLine( txt );
1999             if( !s )
2000             {
2001                 free( psz_text );
2002                 return VLC_EGENERIC;
2003             }
2004
2005             if( sscanf( s, "[%d:%d:%d]", &h2, &m2, &s2 ) == 3 )
2006                 p_subtitle->i_stop  = ( (int64_t)h2 * 3600*1000 +
2007                                         (int64_t)m2 * 60*1000 +
2008                                         (int64_t)s2 * 1000 ) * 1000;
2009             else
2010                 p_subtitle->i_stop  = -1;
2011             break;
2012         }
2013         free( psz_text );
2014     }
2015
2016     /* replace [br] by \n */
2017     char *p;
2018     while( ( p = strstr( psz_text, "[br]" ) ) )
2019     {
2020         *p++ = '\n';
2021         memmove( p, &p[3], strlen(&p[3])+1 );
2022     }
2023
2024     p_subtitle->psz_text = psz_text;
2025     return VLC_SUCCESS;
2026 }
2027
2028 static int ParseSubViewer1( demux_t *p_demux, subtitle_t *p_subtitle, int i_idx )
2029 {
2030     VLC_UNUSED( i_idx );
2031
2032     demux_sys_t *p_sys = p_demux->p_sys;
2033     text_t      *txt = &p_sys->txt;
2034     char *psz_text;
2035
2036     for( ;; )
2037     {
2038         int h1, m1, s1;
2039         int h2, m2, s2;
2040         char *s = TextGetLine( txt );
2041
2042         if( !s )
2043             return VLC_EGENERIC;
2044
2045         if( sscanf( s, "[%d:%d:%d]", &h1, &m1, &s1 ) == 3 )
2046         {
2047             p_subtitle->i_start = ( (int64_t)h1 * 3600*1000 +
2048                                     (int64_t)m1 * 60*1000 +
2049                                     (int64_t)s1 * 1000 ) * 1000;
2050
2051             char *s = TextGetLine( txt );
2052             if( !s )
2053                 return VLC_EGENERIC;
2054
2055             psz_text = strdup( s );
2056             if( !psz_text )
2057                 return VLC_ENOMEM;
2058
2059             s = TextGetLine( txt );
2060             if( !s )
2061             {
2062                 free( psz_text );
2063                 return VLC_EGENERIC;
2064             }
2065
2066             if( sscanf( s, "[%d:%d:%d]", &h2, &m2, &s2 ) == 3 )
2067                 p_subtitle->i_stop  = ( (int64_t)h2 * 3600*1000 +
2068                                         (int64_t)m2 * 60*1000 +
2069                                         (int64_t)s2 * 1000 ) * 1000;
2070             else
2071                 p_subtitle->i_stop  = -1;
2072
2073             break;
2074         }
2075     }
2076
2077     p_subtitle->psz_text = psz_text;
2078
2079     return VLC_SUCCESS;
2080 }
2081