subtitle.c 37 KB
Newer Older
1
/*****************************************************************************
hartman's avatar
hartman committed
2
 * subtitle.c: Demux for subtitle text files.
3
 *****************************************************************************
4
 * Copyright (C) 1999-2004 the VideoLAN team
5
 * $Id$
6
7
 *
 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
hartman's avatar
hartman committed
8
 *          Derk-Jan Hartman <hartman at videolan dot org>
9
10
11
12
13
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
Laurent Aimar's avatar
Laurent Aimar committed
14
 *
15
16
17
18
19
20
21
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
dionoea's avatar
dionoea committed
22
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23
24
25
26
27
28
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include <stdlib.h>
Laurent Aimar's avatar
   
Laurent Aimar committed
29

30
#include <errno.h>
31
32
33
#ifdef HAVE_SYS_TYPES_H
#   include <sys/types.h>
#endif
34
#include <ctype.h>
35
36
37

#include <vlc/vlc.h>
#include <vlc/input.h>
Sam Hocevar's avatar
Sam Hocevar committed
38
#include "vlc_video.h"
39
40
41
42

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
Laurent Aimar's avatar
   
Laurent Aimar committed
43
44
45
static int  Open ( vlc_object_t *p_this );
static void Close( vlc_object_t *p_this );

gbazin's avatar
   
gbazin committed
46
#define SUB_DELAY_LONGTEXT \
zorglub's avatar
zorglub committed
47
    "Apply a delay to all subtitles (in 1/10s, eg 100 means 10s)."
48
#define SUB_FPS_LONGTEXT \
zorglub's avatar
zorglub committed
49
50
    "Override the normal frames per second settings. " \
    "This will only work with MicroDVD and SubRIP (SRT) subtitles."
51
#define SUB_TYPE_LONGTEXT \
zorglub's avatar
zorglub committed
52
53
54
55
    "Force the subtiles format. Valid values are : \"microdvd\", \"subrip\"," \
    "\"ssa1\", \"ssa2-4\", \"ass\", \"vplayer\" " \
    "\"sami\", \"dvdsubtitle\" and \"auto\" (meaning autodetection, this " \
    "should always work)."
Laurent Aimar's avatar
   
Laurent Aimar committed
56
57
58
static char *ppsz_sub_type[] =
{
    "auto", "microdvd", "subrip", "subviewer", "ssa1",
59
    "ssa2-4", "ass", "vplayer", "sami", "dvdsubtitle"
Laurent Aimar's avatar
   
Laurent Aimar committed
60
};
61
62

vlc_module_begin();
63
    set_shortname( _("Subtitles"));
zorglub's avatar
zorglub committed
64
    set_description( _("Text subtitles parser") );
Laurent Aimar's avatar
   
Laurent Aimar committed
65
    set_capability( "demux2", 0 );
zorglub's avatar
zorglub committed
66
67
    set_category( CAT_INPUT );
    set_subcategory( SUBCAT_INPUT_DEMUX );
68
    add_float( "sub-fps", 0.0, NULL,
hartman's avatar
hartman committed
69
70
               N_("Frames per second"),
               SUB_FPS_LONGTEXT, VLC_TRUE );
zorglub's avatar
zorglub committed
71
72
73
    add_integer( "sub-delay", 0, NULL,
               N_("Subtitles delay"),
               SUB_DELAY_LONGTEXT, VLC_TRUE );
zorglub's avatar
zorglub committed
74
    add_string( "sub-type", "auto", NULL, N_("Subtitles format"),
hartman's avatar
hartman committed
75
76
                SUB_TYPE_LONGTEXT, VLC_TRUE );
        change_string_list( ppsz_sub_type, 0, 0 );
Laurent Aimar's avatar
   
Laurent Aimar committed
77
78
79
    set_callbacks( Open, Close );

    add_shortcut( "subtitle" );
80
81
82
vlc_module_end();

/*****************************************************************************
Laurent Aimar's avatar
   
Laurent Aimar committed
83
 * Prototypes:
84
 *****************************************************************************/
Laurent Aimar's avatar
   
Laurent Aimar committed
85
enum
86
{
Laurent Aimar's avatar
   
Laurent Aimar committed
87
88
89
90
91
    SUB_TYPE_UNKNOWN = -1,
    SUB_TYPE_MICRODVD,
    SUB_TYPE_SUBRIP,
    SUB_TYPE_SSA1,
    SUB_TYPE_SSA2_4,
92
    SUB_TYPE_ASS,
Laurent Aimar's avatar
   
Laurent Aimar committed
93
94
95
    SUB_TYPE_VPLAYER,
    SUB_TYPE_SAMI,
    SUB_TYPE_SUBVIEWER,
96
    SUB_TYPE_DVDSUBTITLE
Laurent Aimar's avatar
   
Laurent Aimar committed
97
};
98
99
100
101
102
103
104

typedef struct
{
    int     i_line_count;
    int     i_line;
    char    **line;
} text_t;
Laurent Aimar's avatar
   
Laurent Aimar committed
105
106
static int  TextLoad( text_t *, stream_t *s );
static void TextUnload( text_t * );
107

Laurent Aimar's avatar
   
Laurent Aimar committed
108
typedef struct
109
{
110
111
    int64_t i_start;
    int64_t i_stop;
112

Laurent Aimar's avatar
   
Laurent Aimar committed
113
114
    char    *psz_text;
} subtitle_t;
115
116


Laurent Aimar's avatar
   
Laurent Aimar committed
117
struct demux_sys_t
118
{
Laurent Aimar's avatar
   
Laurent Aimar committed
119
120
121
    int         i_type;
    text_t      txt;
    es_out_id_t *es;
122

Laurent Aimar's avatar
   
Laurent Aimar committed
123
    int64_t     i_next_demux_date;
124

Laurent Aimar's avatar
   
Laurent Aimar committed
125
    int64_t     i_microsecperframe;
126
    int64_t     i_original_mspf;
127

Laurent Aimar's avatar
   
Laurent Aimar committed
128
129
130
131
    char        *psz_header;
    int         i_subtitle;
    int         i_subtitles;
    subtitle_t  *subtitle;
132

Laurent Aimar's avatar
   
Laurent Aimar committed
133
134
135
    int64_t     i_length;
};

136
137
138
139
140
141
142
static int  ParseMicroDvd   ( demux_t *, subtitle_t * );
static int  ParseSubRip     ( demux_t *, subtitle_t * );
static int  ParseSubViewer  ( demux_t *, subtitle_t * );
static int  ParseSSA        ( demux_t *, subtitle_t * );
static int  ParseVplayer    ( demux_t *, subtitle_t * );
static int  ParseSami       ( demux_t *, subtitle_t * );
static int  ParseDVDSubtitle( demux_t *, subtitle_t * );
143

144
145
146
147
148
static struct
{
    char *psz_type_name;
    int  i_type;
    char *psz_name;
Laurent Aimar's avatar
   
Laurent Aimar committed
149
    int  (*pf_read)( demux_t *, subtitle_t* );
150
151
} sub_read_subtitle_function [] =
{
152
153
154
155
156
157
158
159
160
161
    { "microdvd",   SUB_TYPE_MICRODVD,    "MicroDVD",    ParseMicroDvd },
    { "subrip",     SUB_TYPE_SUBRIP,      "SubRIP",      ParseSubRip },
    { "subviewer",  SUB_TYPE_SUBVIEWER,   "SubViewer",   ParseSubViewer },
    { "ssa1",       SUB_TYPE_SSA1,        "SSA-1",       ParseSSA },
    { "ssa2-4",     SUB_TYPE_SSA2_4,      "SSA-2/3/4",   ParseSSA },
    { "ass",        SUB_TYPE_ASS,         "SSA/ASS",     ParseSSA },
    { "vplayer",    SUB_TYPE_VPLAYER,     "VPlayer",     ParseVplayer },
    { "sami",       SUB_TYPE_SAMI,        "SAMI",        ParseSami },
    { "dvdsubtitle",SUB_TYPE_DVDSUBTITLE, "DVDSubtitle", ParseDVDSubtitle },
    { NULL,         SUB_TYPE_UNKNOWN,     "Unknown",     NULL }
162
163
};

Laurent Aimar's avatar
   
Laurent Aimar committed
164
165
166
static int Demux( demux_t * );
static int Control( demux_t *, int, va_list );

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
167
/*static void Fix( demux_t * );*/
Laurent Aimar's avatar
   
Laurent Aimar committed
168

169
/*****************************************************************************
Laurent Aimar's avatar
   
Laurent Aimar committed
170
 * Module initializer
171
 *****************************************************************************/
Laurent Aimar's avatar
   
Laurent Aimar committed
172
static int Open ( vlc_object_t *p_this )
173
{
Laurent Aimar's avatar
   
Laurent Aimar committed
174
175
    demux_t     *p_demux = (demux_t*)p_this;
    demux_sys_t *p_sys;
gbazin's avatar
   
gbazin committed
176
    es_format_t fmt;
Laurent Aimar's avatar
   
Laurent Aimar committed
177
178
179
180
    float f_fps;
    char *psz_type;
    int  (*pf_read)( demux_t *, subtitle_t* );
    int i, i_max;
Eric Petit's avatar
Eric Petit committed
181

hartman's avatar
hartman committed
182
    if( strcmp( p_demux->psz_demux, "subtitle" ) )
183
    {
Laurent Aimar's avatar
   
Laurent Aimar committed
184
        msg_Dbg( p_demux, "subtitle demux discarded" );
185
186
        return VLC_EGENERIC;
    }
gbazin's avatar
   
gbazin committed
187

Laurent Aimar's avatar
   
Laurent Aimar committed
188
189
190
191
192
193
194
195
196
197
198
199
    p_demux->pf_demux = Demux;
    p_demux->pf_control = Control;
    p_demux->p_sys = p_sys = malloc( sizeof( demux_sys_t ) );
    p_sys->psz_header = NULL;
    p_sys->i_subtitle = 0;
    p_sys->i_subtitles= 0;
    p_sys->subtitle   = NULL;


    /* Get the FPS */
    f_fps = var_CreateGetFloat( p_demux, "sub-fps" );
    if( f_fps >= 1.0 )
200
    {
201
        p_sys->i_microsecperframe = (int64_t)( (float)1000000 / f_fps );
202
    }
203
204
205
206
    else
    {
        p_sys->i_microsecperframe = 0;
    }
207

zorglub's avatar
zorglub committed
208
209
210
    f_fps = var_CreateGetFloat( p_demux, "sub-original-fps" );
    if( f_fps >= 1.0 )
    {
211
        p_sys->i_original_mspf = (int64_t)( (float)1000000 / f_fps );
zorglub's avatar
zorglub committed
212
213
214
215
216
217
    }
    else
    {
        p_sys->i_original_mspf = 0;
    }

Laurent Aimar's avatar
   
Laurent Aimar committed
218
219
220
221
    /* Get or probe the type */
    p_sys->i_type = SUB_TYPE_UNKNOWN;
    psz_type = var_CreateGetString( p_demux, "sub-type" );
    if( *psz_type )
222
    {
223
224
225
        int i;

        for( i = 0; ; i++ )
226
        {
227
228
            if( sub_read_subtitle_function[i].psz_type_name == NULL )
                break;
Laurent Aimar's avatar
   
Laurent Aimar committed
229

230
            if( !strcmp( sub_read_subtitle_function[i].psz_type_name,
Laurent Aimar's avatar
   
Laurent Aimar committed
231
                         psz_type ) )
232
            {
Laurent Aimar's avatar
   
Laurent Aimar committed
233
                p_sys->i_type = sub_read_subtitle_function[i].i_type;
234
235
                break;
            }
236
237
        }
    }
Laurent Aimar's avatar
   
Laurent Aimar committed
238
    free( psz_type );
Laurent Aimar's avatar
Laurent Aimar committed
239

Laurent Aimar's avatar
   
Laurent Aimar committed
240
241
    /* Probe if unknown type */
    if( p_sys->i_type == SUB_TYPE_UNKNOWN )
242
    {
243
        int     i_try;
244
        char    *s = NULL;
245

Laurent Aimar's avatar
   
Laurent Aimar committed
246
247
        msg_Dbg( p_demux, "autodetecting subtitle format" );
        for( i_try = 0; i_try < 256; i_try++ )
248
249
        {
            int i_dummy;
250

Laurent Aimar's avatar
   
Laurent Aimar committed
251
            if( ( s = stream_ReadLine( p_demux->s ) ) == NULL )
252
253
                break;

254
            if( strcasestr( s, "<SAMI>" ) )
255
            {
Laurent Aimar's avatar
   
Laurent Aimar committed
256
                p_sys->i_type = SUB_TYPE_SAMI;
257
258
259
260
                break;
            }
            else if( sscanf( s, "{%d}{%d}", &i_dummy, &i_dummy ) == 2 ||
                     sscanf( s, "{%d}{}", &i_dummy ) == 1)
261
            {
Laurent Aimar's avatar
   
Laurent Aimar committed
262
                p_sys->i_type = SUB_TYPE_MICRODVD;
263
264
                break;
            }
265
            else if( sscanf( s,
266
                             "%d:%d:%d,%d --> %d:%d:%d,%d",
Laurent Aimar's avatar
Laurent Aimar committed
267
                             &i_dummy,&i_dummy,&i_dummy,&i_dummy,
268
269
                             &i_dummy,&i_dummy,&i_dummy,&i_dummy ) == 8 )
            {
Laurent Aimar's avatar
   
Laurent Aimar committed
270
                p_sys->i_type = SUB_TYPE_SUBRIP;
271
272
                break;
            }
273
            else if( !strncasecmp( s, "!: This is a Sub Station Alpha v1", 33 ) )
274
            {
275
                p_sys->i_type = SUB_TYPE_SSA1;
276
                break;
277
            }
278
            else if( !strncasecmp( s, "ScriptType: v4.00+", 18 ) )
279
            {
280
281
282
283
284
285
                p_sys->i_type = SUB_TYPE_ASS;
                break;
            }
            else if( !strncasecmp( s, "ScriptType: v4.00", 17 ) )
            {
                p_sys->i_type = SUB_TYPE_SSA2_4;
286
                break;
287
            }
288
            else if( !strncasecmp( s, "Dialogue: Marked", 16  ) )
289
            {
290
291
292
293
294
295
                p_sys->i_type = SUB_TYPE_SSA2_4;
                break;
            }
            else if( !strncasecmp( s, "Dialogue:", 9  ) )
            {
                p_sys->i_type = SUB_TYPE_ASS;
296
297
                break;
            }
298
            else if( strcasestr( s, "[INFORMATION]" ) )
299
            {
Laurent Aimar's avatar
   
Laurent Aimar committed
300
                p_sys->i_type = SUB_TYPE_SUBVIEWER; /* I hope this will work */
301
302
                break;
            }
303
304
            else if( sscanf( s, "%d:%d:%d:", &i_dummy, &i_dummy, &i_dummy ) == 3 ||
                     sscanf( s, "%d:%d:%d ", &i_dummy, &i_dummy, &i_dummy ) == 3 )
305
            {
Laurent Aimar's avatar
   
Laurent Aimar committed
306
                p_sys->i_type = SUB_TYPE_VPLAYER;
307
                break;
308
            }
309
310
311
312
313
314
            else if( sscanf( s, "{T %d:%d:%d:%d", &i_dummy, &i_dummy,
                             &i_dummy, &i_dummy ) == 4 )
            {
                p_sys->i_type = SUB_TYPE_DVDSUBTITLE;
                break;
            }
315
316
317

            free( s );
            s = NULL;
318
        }
319

320
321
        if( s ) free( s );

Laurent Aimar's avatar
   
Laurent Aimar committed
322
        /* It will nearly always work even for non seekable stream thanks the
trax's avatar
trax committed
323
         * caching system, and if it fails we lose just a few sub */
Laurent Aimar's avatar
   
Laurent Aimar committed
324
325
326
327
328
329
330
331
        if( stream_Seek( p_demux->s, 0 ) )
        {
            msg_Warn( p_demux, "failed to rewind" );
        }
    }
    if( p_sys->i_type == SUB_TYPE_UNKNOWN )
    {
        msg_Err( p_demux, "failed to recognize subtitle type" );
332
        free( p_sys );
Laurent Aimar's avatar
   
Laurent Aimar committed
333
        return VLC_EGENERIC;
334
335
    }

336
    for( i = 0; ; i++ )
337
    {
Laurent Aimar's avatar
   
Laurent Aimar committed
338
        if( sub_read_subtitle_function[i].i_type == p_sys->i_type )
339
        {
Laurent Aimar's avatar
   
Laurent Aimar committed
340
341
342
            msg_Dbg( p_demux, "detected %s format",
                     sub_read_subtitle_function[i].psz_name );
            pf_read = sub_read_subtitle_function[i].pf_read;
343
344
            break;
        }
345
    }
346

Laurent Aimar's avatar
   
Laurent Aimar committed
347
348
349
350
351
352
    msg_Dbg( p_demux, "loading all subtitles..." );

    /* Load the whole file */
    TextLoad( &p_sys->txt, p_demux->s );

    /* Parse it */
353
    for( i_max = 0;; )
Laurent Aimar's avatar
Laurent Aimar committed
354
    {
Laurent Aimar's avatar
   
Laurent Aimar committed
355
        if( p_sys->i_subtitles >= i_max )
356
        {
Laurent Aimar's avatar
   
Laurent Aimar committed
357
358
            i_max += 500;
            if( !( p_sys->subtitle = realloc( p_sys->subtitle,
gbazin's avatar
   
gbazin committed
359
                                              sizeof(subtitle_t) * i_max ) ) )
360
            {
Laurent Aimar's avatar
   
Laurent Aimar committed
361
                msg_Err( p_demux, "out of memory");
362
363
364
365
                if( p_sys->subtitle != NULL )
                    free( p_sys->subtitle );
                TextUnload( &p_sys->txt );
                free( p_sys );
gbazin's avatar
   
gbazin committed
366
                return VLC_ENOMEM;
367
368
            }
        }
Laurent Aimar's avatar
   
Laurent Aimar committed
369
370

        if( pf_read( p_demux, &p_sys->subtitle[p_sys->i_subtitles] ) )
371
            break;
Laurent Aimar's avatar
   
Laurent Aimar committed
372
373

        p_sys->i_subtitles++;
374
    }
Laurent Aimar's avatar
   
Laurent Aimar committed
375
376
    /* Unload */
    TextUnload( &p_sys->txt );
377

Laurent Aimar's avatar
   
Laurent Aimar committed
378
    msg_Dbg(p_demux, "loaded %d subtitles", p_sys->i_subtitles );
379

Laurent Aimar's avatar
   
Laurent Aimar committed
380
381
382
383
    /* Fix subtitle (order and time) *** */
    p_sys->i_subtitle = 0;
    p_sys->i_length = 0;
    if( p_sys->i_subtitles > 0 )
384
    {
Laurent Aimar's avatar
   
Laurent Aimar committed
385
386
387
388
        p_sys->i_length = p_sys->subtitle[p_sys->i_subtitles-1].i_stop;
        /* +1 to avoid 0 */
        if( p_sys->i_length <= 0 )
            p_sys->i_length = p_sys->subtitle[p_sys->i_subtitles-1].i_start+1;
389
    }
Laurent Aimar's avatar
Laurent Aimar committed
390

391
    /* *** add subtitle ES *** */
hartman's avatar
hartman committed
392
    if( p_sys->i_type == SUB_TYPE_SSA1 ||
393
394
             p_sys->i_type == SUB_TYPE_SSA2_4 ||
             p_sys->i_type == SUB_TYPE_ASS )
395
    {
396
        es_format_Init( &fmt, SPU_ES, VLC_FOURCC( 's','s','a',' ' ) );
397
398
399
    }
    else
    {
400
        es_format_Init( &fmt, SPU_ES, VLC_FOURCC( 's','u','b','t' ) );
401
    }
Laurent Aimar's avatar
   
Laurent Aimar committed
402
    if( p_sys->psz_header != NULL )
403
    {
Laurent Aimar's avatar
   
Laurent Aimar committed
404
405
        fmt.i_extra = strlen( p_sys->psz_header ) + 1;
        fmt.p_extra = strdup( p_sys->psz_header );
406
    }
Laurent Aimar's avatar
   
Laurent Aimar committed
407
    p_sys->es = es_out_Add( p_demux->out, &fmt );
hartman's avatar
hartman committed
408

Laurent Aimar's avatar
Laurent Aimar committed
409
    return VLC_SUCCESS;
410
411
412
}

/*****************************************************************************
Laurent Aimar's avatar
   
Laurent Aimar committed
413
 * Close: Close subtitle demux
414
 *****************************************************************************/
Laurent Aimar's avatar
   
Laurent Aimar committed
415
static void Close( vlc_object_t *p_this )
416
{
Laurent Aimar's avatar
   
Laurent Aimar committed
417
418
419
    demux_t *p_demux = (demux_t*)p_this;
    demux_sys_t *p_sys = p_demux->p_sys;
    int i;
420

Laurent Aimar's avatar
   
Laurent Aimar committed
421
    for( i = 0; i < p_sys->i_subtitles; i++ )
422
    {
Laurent Aimar's avatar
   
Laurent Aimar committed
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
        if( p_sys->subtitle[i].psz_text )
            free( p_sys->subtitle[i].psz_text );
    }
    if( p_sys->subtitle )
        free( p_sys->subtitle );

    free( p_sys );
}

/*****************************************************************************
 * Control:
 *****************************************************************************/
static int Control( demux_t *p_demux, int i_query, va_list args )
{
    demux_sys_t *p_sys = p_demux->p_sys;
    int64_t *pi64, i64;
    double *pf, f;

    switch( i_query )
    {
        case DEMUX_GET_LENGTH:
            pi64 = (int64_t*)va_arg( args, int64_t * );
            *pi64 = p_sys->i_length;
            return VLC_SUCCESS;

        case DEMUX_GET_TIME:
            pi64 = (int64_t*)va_arg( args, int64_t * );
            if( p_sys->i_subtitle < p_sys->i_subtitles )
            {
                *pi64 = p_sys->subtitle[p_sys->i_subtitle].i_start;
                return VLC_SUCCESS;
            }
            return VLC_EGENERIC;

        case DEMUX_SET_TIME:
            i64 = (int64_t)va_arg( args, int64_t );
            p_sys->i_subtitle = 0;
            while( p_sys->i_subtitle < p_sys->i_subtitles &&
                   p_sys->subtitle[p_sys->i_subtitle].i_start < i64 )
            {
                p_sys->i_subtitle++;
            }
465

Laurent Aimar's avatar
   
Laurent Aimar committed
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
            if( p_sys->i_subtitle >= p_sys->i_subtitles )
                return VLC_EGENERIC;
            return VLC_SUCCESS;

        case DEMUX_GET_POSITION:
            pf = (double*)va_arg( args, double * );
            if( p_sys->i_subtitle >= p_sys->i_subtitles )
            {
                *pf = 1.0;
            }
            else if( p_sys->i_subtitles > 0 )
            {
                *pf = (double)p_sys->subtitle[p_sys->i_subtitle].i_start /
                      (double)p_sys->i_length;
            }
            else
            {
                *pf = 0.0;
            }
            return VLC_SUCCESS;

        case DEMUX_SET_POSITION:
            f = (double)va_arg( args, double );
            i64 = f * p_sys->i_length;

            p_sys->i_subtitle = 0;
            while( p_sys->i_subtitle < p_sys->i_subtitles &&
                   p_sys->subtitle[p_sys->i_subtitle].i_start < i64 )
            {
                p_sys->i_subtitle++;
            }
            if( p_sys->i_subtitle >= p_sys->i_subtitles )
                return VLC_EGENERIC;
            return VLC_SUCCESS;

        case DEMUX_SET_NEXT_DEMUX_TIME:
            p_sys->i_next_demux_date = (int64_t)va_arg( args, int64_t );
            return VLC_SUCCESS;

        case DEMUX_GET_FPS:
        case DEMUX_GET_META:
        case DEMUX_GET_TITLE_INFO:
            return VLC_EGENERIC;

        default:
            msg_Err( p_demux, "unknown query in subtitle control" );
            return VLC_EGENERIC;
513
    }
Laurent Aimar's avatar
   
Laurent Aimar committed
514
515
516
517
518
519
520
521
522
523
524
525
526
}

/*****************************************************************************
 * Demux: Send subtitle to decoder
 *****************************************************************************/
static int Demux( demux_t *p_demux )
{
    demux_sys_t *p_sys = p_demux->p_sys;
    int64_t i_maxdate;

    if( p_sys->i_subtitle >= p_sys->i_subtitles )
        return 0;

zorglub's avatar
zorglub committed
527
    i_maxdate = p_sys->i_next_demux_date - var_GetTime( p_demux->p_parent, "spu-delay" );;
Laurent Aimar's avatar
   
Laurent Aimar committed
528
    if( i_maxdate <= 0 && p_sys->i_subtitle < p_sys->i_subtitles )
529
    {
Laurent Aimar's avatar
   
Laurent Aimar committed
530
531
        /* Should not happen */
        i_maxdate = p_sys->subtitle[p_sys->i_subtitle].i_start + 1;
532
533
    }

hartman's avatar
hartman committed
534
535
    while( p_sys->i_subtitle < p_sys->i_subtitles &&
           p_sys->subtitle[p_sys->i_subtitle].i_start < i_maxdate )
536
    {
hartman's avatar
hartman committed
537
538
        block_t *p_block;
        int i_len = strlen( p_sys->subtitle[p_sys->i_subtitle].psz_text ) + 1;
539

hartman's avatar
hartman committed
540
541
542
        if( i_len <= 1 )
        {
            /* empty subtitle */
Laurent Aimar's avatar
   
Laurent Aimar committed
543
            p_sys->i_subtitle++;
hartman's avatar
hartman committed
544
            continue;
545
        }
546

hartman's avatar
hartman committed
547
548
549
550
551
        if( ( p_block = block_New( p_demux, i_len ) ) == NULL )
        {
            p_sys->i_subtitle++;
            continue;
        }
552

hartman's avatar
hartman committed
553
554
555
556
557
        if( p_sys->subtitle[p_sys->i_subtitle].i_start < 0 )
        {
            p_sys->i_subtitle++;
            continue;
        }
558

hartman's avatar
hartman committed
559
560
561
562
563
564
565
        p_block->i_pts = p_sys->subtitle[p_sys->i_subtitle].i_start;
        p_block->i_dts = p_block->i_pts;
        if( p_sys->subtitle[p_sys->i_subtitle].i_stop > 0 )
        {
            p_block->i_length =
                p_sys->subtitle[p_sys->i_subtitle].i_stop - p_block->i_pts;
        }
566

hartman's avatar
hartman committed
567
568
569
570
571
        memcpy( p_block->p_buffer,
                p_sys->subtitle[p_sys->i_subtitle].psz_text, i_len );
        if( p_block->i_pts > 0 )
        {
            es_out_Send( p_demux->out, p_sys->es, p_block );
572
        }
hartman's avatar
hartman committed
573
574
575
576
577
        else
        {
            block_Release( p_block );
        }
        p_sys->i_subtitle++;
578
579
    }

Laurent Aimar's avatar
   
Laurent Aimar committed
580
581
    /* */
    p_sys->i_next_demux_date = 0;
582

Laurent Aimar's avatar
   
Laurent Aimar committed
583
    return 1;
584
}
585

586
/*****************************************************************************
Laurent Aimar's avatar
   
Laurent Aimar committed
587
 * Fix: fix time stamp and order of subtitle
588
 *****************************************************************************/
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
589
#ifdef USE_THIS_UNUSED_PIECE_OF_CODE
Laurent Aimar's avatar
   
Laurent Aimar committed
590
static void Fix( demux_t *p_demux )
591
{
Laurent Aimar's avatar
   
Laurent Aimar committed
592
593
    demux_sys_t *p_sys = p_demux->p_sys;
    vlc_bool_t b_done;
594
595
596
597
598
599
600
601
    int     i_index;

    /* *** fix order (to be sure...) *** */
    /* We suppose that there are near in order and this durty bubble sort
     * wont take too much time
     */
    do
    {
Laurent Aimar's avatar
   
Laurent Aimar committed
602
603
        b_done = VLC_TRUE;
        for( i_index = 1; i_index < p_sys->i_subtitles; i_index++ )
604
        {
Laurent Aimar's avatar
   
Laurent Aimar committed
605
606
            if( p_sys->subtitle[i_index].i_start <
                    p_sys->subtitle[i_index - 1].i_start )
607
608
            {
                subtitle_t sub_xch;
Laurent Aimar's avatar
Laurent Aimar committed
609
                memcpy( &sub_xch,
Laurent Aimar's avatar
   
Laurent Aimar committed
610
                        p_sys->subtitle + i_index - 1,
611
                        sizeof( subtitle_t ) );
Laurent Aimar's avatar
   
Laurent Aimar committed
612
613
                memcpy( p_sys->subtitle + i_index - 1,
                        p_sys->subtitle + i_index,
614
                        sizeof( subtitle_t ) );
Laurent Aimar's avatar
   
Laurent Aimar committed
615
                memcpy( p_sys->subtitle + i_index,
616
617
                        &sub_xch,
                        sizeof( subtitle_t ) );
Laurent Aimar's avatar
   
Laurent Aimar committed
618
                b_done = VLC_FALSE;
619
620
            }
        }
Laurent Aimar's avatar
   
Laurent Aimar committed
621
622
    } while( !b_done );
}
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
623
#endif
Laurent Aimar's avatar
   
Laurent Aimar committed
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646

static int TextLoad( text_t *txt, stream_t *s )
{
    int   i_line_max;

    /* init txt */
    i_line_max          = 500;
    txt->i_line_count   = 0;
    txt->i_line         = 0;
    txt->line           = calloc( i_line_max, sizeof( char * ) );

    /* load the complete file */
    for( ;; )
    {
        char *psz = stream_ReadLine( s );

        if( psz == NULL )
            break;

        txt->line[txt->i_line_count++] = psz;
        if( txt->i_line_count >= i_line_max )
        {
            i_line_max += 100;
hartman's avatar
hartman committed
647
            txt->line = realloc( txt->line, i_line_max * sizeof( char * ) );
Laurent Aimar's avatar
   
Laurent Aimar committed
648
649
650
651
652
653
654
655
656
657
        }
    }

    if( txt->i_line_count <= 0 )
    {
        free( txt->line );
        return VLC_EGENERIC;
    }

    return VLC_SUCCESS;
658
}
Laurent Aimar's avatar
   
Laurent Aimar committed
659
660
661
static void TextUnload( text_t *txt )
{
    int i;
662

Laurent Aimar's avatar
   
Laurent Aimar committed
663
664
665
666
667
668
669
670
    for( i = 0; i < txt->i_line_count; i++ )
    {
        free( txt->line[i] );
    }
    free( txt->line );
    txt->i_line       = 0;
    txt->i_line_count = 0;
}
671

Laurent Aimar's avatar
   
Laurent Aimar committed
672
673
674
675
676
677
678
679
680
681
682
683
static char *TextGetLine( text_t *txt )
{
    if( txt->i_line >= txt->i_line_count )
        return( NULL );

    return txt->line[txt->i_line++];
}
static void TextPreviousLine( text_t *txt )
{
    if( txt->i_line > 0 )
        txt->i_line--;
}
684
685

/*****************************************************************************
Laurent Aimar's avatar
Laurent Aimar committed
686
 * Specific Subtitle function
687
 *****************************************************************************/
Laurent Aimar's avatar
   
Laurent Aimar committed
688
689
#define MAX_LINE 8192
static int ParseMicroDvd( demux_t *p_demux, subtitle_t *p_subtitle )
690
{
Laurent Aimar's avatar
   
Laurent Aimar committed
691
692
    demux_sys_t *p_sys = p_demux->p_sys;
    text_t      *txt = &p_sys->txt;
693
694
695
696
    /*
     * each line:
     *  {n1}{n2}Line1|Line2|Line3....
     * where n1 and n2 are the video frame number...
697
     * {n2} can also be {}
698
     */
699
700
    char *s;

701
    char buffer_text[MAX_LINE + 1];
702
703
    int    i_start;
    int    i_stop;
704
    unsigned int i;
Laurent Aimar's avatar
   
Laurent Aimar committed
705

706
    int i_microsecperframe = 40000; /* default to 25 fps */
707
    if( p_sys->i_microsecperframe > 0 )
708
        i_microsecperframe = p_sys->i_microsecperframe;
709

710
711
712
    p_subtitle->i_start = 0;
    p_subtitle->i_stop  = 0;
    p_subtitle->psz_text = NULL;
Laurent Aimar's avatar
Laurent Aimar committed
713

714
715
    for( ;; )
    {
Laurent Aimar's avatar
   
Laurent Aimar committed
716
        if( ( s = TextGetLine( txt ) ) == NULL )
717
        {
718
            return( VLC_EGENERIC );
719
720
721
        }
        i_start = 0;
        i_stop  = 0;
Laurent Aimar's avatar
Laurent Aimar committed
722
723

        memset( buffer_text, '\0', MAX_LINE );
724
725
        if( sscanf( s, "{%d}{}%[^\r\n]", &i_start, buffer_text ) == 2 ||
            sscanf( s, "{%d}{%d}%[^\r\n]", &i_start, &i_stop, buffer_text ) == 3)
726
727
728
729
730
        {
            break;
        }
    }
    /* replace | by \n */
Laurent Aimar's avatar
Laurent Aimar committed
731
    for( i = 0; i < strlen( buffer_text ); i++ )
732
733
734
735
736
737
    {
        if( buffer_text[i] == '|' )
        {
            buffer_text[i] = '\n';
        }
    }
Laurent Aimar's avatar
   
Laurent Aimar committed
738

739
740
    p_subtitle->i_start = (int64_t)i_start * i_microsecperframe;
    p_subtitle->i_stop  = (int64_t)i_stop  * i_microsecperframe;
741
    p_subtitle->psz_text = strndup( buffer_text, MAX_LINE );
742
743
744
    return( 0 );
}

Laurent Aimar's avatar
   
Laurent Aimar committed
745
static int  ParseSubRip( demux_t *p_demux, subtitle_t *p_subtitle )
746
{
Laurent Aimar's avatar
   
Laurent Aimar committed
747
748
749
    demux_sys_t *p_sys = p_demux->p_sys;
    text_t      *txt = &p_sys->txt;

750
751
752
753
754
755
756
    /*
     * n
     * h1:m1:s1,d1 --> h2:m2:s2,d2
     * Line1
     * Line2
     * ...
     * [empty line]
Laurent Aimar's avatar
Laurent Aimar committed
757
     *
758
     */
759
    char *s;
760
761
    char buffer_text[ 10 * MAX_LINE];
    int  i_buffer_text;
762
763
    int64_t     i_start;
    int64_t     i_stop;
764

765
766
767
768
    p_subtitle->i_start = 0;
    p_subtitle->i_stop  = 0;
    p_subtitle->psz_text = NULL;

769
770
771
    for( ;; )
    {
        int h1, m1, s1, d1, h2, m2, s2, d2;
Laurent Aimar's avatar
   
Laurent Aimar committed
772
        if( ( s = TextGetLine( txt ) ) == NULL )
773
        {
774
            return( VLC_EGENERIC );
775
        }
776
        if( sscanf( s,
777
778
779
780
                    "%d:%d:%d,%d --> %d:%d:%d,%d",
                    &h1, &m1, &s1, &d1,
                    &h2, &m2, &s2, &d2 ) == 8 )
        {
781
782
783
784
            i_start = ( (int64_t)h1 * 3600*1000 +
                        (int64_t)m1 * 60*1000 +
                        (int64_t)s1 * 1000 +
                        (int64_t)d1 ) * 1000;
785

786
787
788
789
            i_stop  = ( (int64_t)h2 * 3600*1000 +
                        (int64_t)m2 * 60*1000 +
                        (int64_t)s2 * 1000 +
                        (int64_t)d2 ) * 1000;
790

791
792
793
794
            /* Now read text until an empty line */
            for( i_buffer_text = 0;; )
            {
                int i_len;
Laurent Aimar's avatar
   
Laurent Aimar committed
795
                if( ( s = TextGetLine( txt ) ) == NULL )
796
                {
797
                    return( VLC_EGENERIC );
798
                }
799
800

                i_len = strlen( s );
801
                if( i_len <= 0 )
802
                {
803
                    /* empty line -> end of this subtitle */
804
805
806
807
                    buffer_text[__MAX( i_buffer_text - 1, 0 )] = '\0';
                    p_subtitle->i_start = i_start;
                    p_subtitle->i_stop = i_stop;
                    p_subtitle->psz_text = strdup( buffer_text );
808
                    /* If framerate is available, use sub-fps */
Laurent Aimar's avatar
   
Laurent Aimar committed
809
810
                    if( p_sys->i_microsecperframe != 0 &&
                        p_sys->i_original_mspf != 0)
811
                    {
812
                        p_subtitle->i_start = (int64_t)i_start *
zorglub's avatar
zorglub committed
813
814
                                              p_sys->i_microsecperframe/
                                              p_sys->i_original_mspf;
815
                        p_subtitle->i_stop  = (int64_t)i_stop  *
zorglub's avatar
zorglub committed
816
817
                                              p_sys->i_microsecperframe /
                                              p_sys->i_original_mspf;
818
                    }
Laurent Aimar's avatar
   
Laurent Aimar committed
819
                    return 0;
820
821
822
                }
                else
                {
823
824
825
                    if( i_buffer_text + i_len + 1 < 10 * MAX_LINE )
                    {
                        memcpy( buffer_text + i_buffer_text,
826
                                s,
827
828
829
830
831
832
                                i_len );
                        i_buffer_text += i_len;

                        buffer_text[i_buffer_text] = '\n';
                        i_buffer_text++;
                    }
833
834
835
836
837
838
                }
            }
        }
    }
}

Laurent Aimar's avatar
   
Laurent Aimar committed
839
static int  ParseSubViewer( demux_t *p_demux, subtitle_t *p_subtitle )
840
{
Laurent Aimar's avatar
   
Laurent Aimar committed
841
842
843
    demux_sys_t *p_sys = p_demux->p_sys;
    text_t      *txt = &p_sys->txt;

844
845
846
847
848
849
850
851
852
853
854
    /*
     * h1:m1:s1.d1,h2:m2:s2.d2
     * Line1[br]Line2
     * Line3
     * ...
     * [empty line]
     * ( works with subviewer and subviewer v2 )
     */
    char *s;
    char buffer_text[ 10 * MAX_LINE];
    int  i_buffer_text;
855
856
    int64_t     i_start;
    int64_t     i_stop;
857
858
859
860
861
862
863
864

    p_subtitle->i_start = 0;
    p_subtitle->i_stop  = 0;
    p_subtitle->psz_text = NULL;

    for( ;; )
    {
        int h1, m1, s1, d1, h2, m2, s2, d2;
Laurent Aimar's avatar
   
Laurent Aimar committed
865
        if( ( s = TextGetLine( txt ) ) == NULL )
866
867
868
869
870
871
872
873
        {
            return( VLC_EGENERIC );
        }
        if( sscanf( s,
                    "%d:%d:%d.%d,%d:%d:%d.%d",
                    &h1, &m1, &s1, &d1,
                    &h2, &m2, &s2, &d2 ) == 8 )
        {
874
875
876
877
            i_start = ( (int64_t)h1 * 3600*1000 +
                        (int64_t)m1 * 60*1000 +
                        (int64_t)s1 * 1000 +
                        (int64_t)d1 ) * 1000;
878

879
880
881
882
            i_stop  = ( (int64_t)h2 * 3600*1000 +
                        (int64_t)m2 * 60*1000 +
                        (int64_t)s2 * 1000 +
                        (int64_t)d2 ) * 1000;
883
884
885
886
887

            /* Now read text until an empty line */
            for( i_buffer_text = 0;; )
            {
                int i_len, i;
Laurent Aimar's avatar
   
Laurent Aimar committed
888
                if( ( s = TextGetLine( txt ) ) == NULL )
889
890
891
892
893
                {
                    return( VLC_EGENERIC );
                }

                i_len = strlen( s );
894
                if( i_len <= 0 )
895
896
897
898
899
900
901
                {
                    /* empty line -> end of this subtitle */
                    buffer_text[__MAX( i_buffer_text - 1, 0 )] = '\0';
                    p_subtitle->i_start = i_start;
                    p_subtitle->i_stop = i_stop;

                    /* replace [br] by \n */
902
                    for( i = 0; i < i_buffer_text - 3; i++ )
903
904
905
906
907
908
                    {
                        if( buffer_text[i] == '[' && buffer_text[i+1] == 'b' &&
                            buffer_text[i+2] == 'r' && buffer_text[i+3] == ']' )
                        {
                            char *temp = buffer_text + i + 1;
                            buffer_text[i] = '\n';
909
910
                            memmove( temp, temp+3, strlen( temp ) -3 );
                            temp[strlen( temp )-3] = '\0';
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
                        }
                    }
                    p_subtitle->psz_text = strdup( buffer_text );
                    return( 0 );
                }
                else
                {
                    if( i_buffer_text + i_len + 1 < 10 * MAX_LINE )
                    {
                        memcpy( buffer_text + i_buffer_text,
                                s,
                                i_len );
                        i_buffer_text += i_len;

                        buffer_text[i_buffer_text] = '\n';
                        i_buffer_text++;
                    }
                }
            }
        }
    }
}

934

Laurent Aimar's avatar
   
Laurent Aimar committed
935
static int  ParseSSA( demux_t *p_demux, subtitle_t *p_subtitle )
936
{
Laurent Aimar's avatar
   
Laurent Aimar committed
937
938
939
    demux_sys_t *p_sys = p_demux->p_sys;
    text_t      *txt = &p_sys->txt;

940
    char buffer_text[ 10 * MAX_LINE];
941
    char buffer_text2[ 10 * MAX_LINE];
942
    char *s;
943
944
    int64_t     i_start;
    int64_t     i_stop;
Laurent Aimar's avatar
Laurent Aimar committed
945

946
947
948
949
    p_subtitle->i_start = 0;
    p_subtitle->i_stop  = 0;
    p_subtitle->psz_text = NULL;

950
951
952
    for( ;; )
    {
        int h1, m1, s1, c1, h2, m2, s2, c2;
953

Laurent Aimar's avatar
   
Laurent Aimar committed
954
        if( ( s = TextGetLine( txt ) ) == NULL )
955
        {
956
            return( VLC_EGENERIC );
957
        }
958
959
        p_subtitle->psz_text = malloc( strlen( s ) );

960
961
962
963
964
965
966
967
968
969
970
        /* We expect (SSA2-4):
         * Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
         * Dialogue: Marked=0,0:02:40.65,0:02:41.79,Wolf main,Cher,0000,0000,0000,,Et les enregistrements de ses ondes delta ?
         *
         * 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.
         */

        /* For ASS:
         * Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
         * Dialogue: Layer#,0:02:40.65,0:02:41.79,Wolf main,Cher,0000,0000,0000,,Et les enregistrements de ses ondes delta ?
         */
971
        if( sscanf( s,
972
973
                    "Dialogue: %[^,],%d:%d:%d.%d,%d:%d:%d.%d,%[^\r\n]",
                    buffer_text2,
974
975
976
977
                    &h1, &m1, &s1, &c1,
                    &h2, &m2, &s2, &c2,
                    buffer_text ) == 10 )
        {
978
979
980
981
            i_start = ( (int64_t)h1 * 3600*1000 +
                        (int64_t)m1 * 60*1000 +
                        (int64_t)s1 * 1000 +
                        (int64_t)c1 * 10 ) * 1000;
982

983
984
985
986
            i_stop  = ( (int64_t)h2 * 3600*1000 +
                        (int64_t)m2 * 60*1000 +
                        (int64_t)s2 * 1000 +
                        (int64_t)c2 * 10 ) * 1000;
Laurent Aimar's avatar
Laurent Aimar committed
987

988
            /* The dec expects: ReadOrder, Layer, Style, Name, MarginL, MarginR, MarginV, Effect, Text */
989
            /* (Layer comes from ASS specs ... it's empty for SSA.) */
Laurent Aimar's avatar
   
Laurent Aimar committed
990
            if( p_sys->i_type == SUB_TYPE_SSA1 )
Laurent Aimar's avatar
Laurent Aimar committed
991
            {
Laurent Aimar's avatar
   
Laurent Aimar committed
992
                sprintf( p_subtitle->psz_text,
993
                         ",%s", strdup( buffer_text) ); /* SSA1 has only 8 commas before the text starts, not 9 */
994
995
996
            }
            else
            {
Laurent Aimar's avatar
   
Laurent Aimar committed
997
                sprintf( p_subtitle->psz_text,
998
                         ",,%s", strdup( buffer_text) ); /* ReadOrder, Layer, %s(rest of fields) */
999
            }
1000
1001
            p_subtitle->i_start = i_start;
            p_subtitle->i_stop = i_stop;
Laurent Aimar's avatar
   
Laurent Aimar committed
1002
            return 0;
1003
1004
1005
1006
        }
        else
        {
            /* All the other stuff we add to the header field */
Laurent Aimar's avatar
   
Laurent Aimar committed
1007
            if( p_sys->psz_header != NULL )
1008
            {
Laurent Aimar's avatar
   
Laurent Aimar committed
1009
                if( !( p_sys->psz_header = realloc( p_sys->psz_header,
hartman's avatar
hartman committed
1010
                          strlen( p_sys->psz_header ) + 1 + strlen( s ) + 2 ) ) )
1011
                {
Laurent Aimar's avatar
   
Laurent Aimar committed
1012
                    msg_Err( p_demux, "out of memory");
1013
                    return VLC_ENOMEM;
1014
                }
hartman's avatar
hartman committed
1015
                p_sys->psz_header = strcat(