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

/*****************************************************************************
 * Preamble
 *****************************************************************************/
29

30
31
32
33
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

34
#include <vlc_common.h>
35
#include <vlc_plugin.h>
zorglub's avatar
zorglub committed
36
#include <vlc_input.h>
37
#include <vlc_memory.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
38

39
#include <ctype.h>
40

zorglub's avatar
zorglub committed
41
42
#include <vlc_demux.h>
#include <vlc_charset.h>
43
44
45
46

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
Laurent Aimar's avatar
   
Laurent Aimar committed
47
48
49
static int  Open ( vlc_object_t *p_this );
static void Close( vlc_object_t *p_this );

gbazin's avatar
   
gbazin committed
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
54
    N_("Override the normal frames per second settings. " \
    "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
58
#define SUB_DESCRIPTION_LONGTEXT \
    N_("Override the default track description.")
59

60
static const char *const ppsz_sub_type[] =
Laurent Aimar's avatar
   
Laurent Aimar committed
61
62
{
    "auto", "microdvd", "subrip", "subviewer", "ssa1",
63
    "ssa2-4", "ass", "vplayer", "sami", "dvdsubtitle", "mpl2",
64
    "aqt", "pjs", "mpsub", "jacosub", "psb", "realtext", "dks",
Arun Pandian G's avatar
Arun Pandian G committed
65
    "subviewer1","vtt"
Laurent Aimar's avatar
   
Laurent Aimar committed
66
};
67

68
69
vlc_module_begin ()
    set_shortname( N_("Subtitles"))
70
    set_description( N_("Text subtitle parser") )
71
72
73
    set_capability( "demux", 0 )
    set_category( CAT_INPUT )
    set_subcategory( SUBCAT_INPUT_DEMUX )
74
    add_float( "sub-fps", 0.0,
75
               N_("Frames per Second"),
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
76
               SUB_FPS_LONGTEXT, true )
77
    add_integer( "sub-delay", 0,
78
               N_("Subtitle delay"),
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
79
               SUB_DELAY_LONGTEXT, true )
80
    add_string( "sub-type", "auto", N_("Subtitle format"),
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
81
                SUB_TYPE_LONGTEXT, true )
82
        change_string_list( ppsz_sub_type, ppsz_sub_type )
83
    add_string( "sub-description", NULL, N_("Subtitle description"),
84
                SUB_DESCRIPTION_LONGTEXT, true )
85
    set_callbacks( Open, Close )
Laurent Aimar's avatar
   
Laurent Aimar committed
86

87
88
    add_shortcut( "subtitle" )
vlc_module_end ()
89
90

/*****************************************************************************
Laurent Aimar's avatar
   
Laurent Aimar committed
91
 * Prototypes:
92
 *****************************************************************************/
Laurent Aimar's avatar
   
Laurent Aimar committed
93
enum
94
{
Laurent Aimar's avatar
   
Laurent Aimar committed
95
96
97
98
99
    SUB_TYPE_UNKNOWN = -1,
    SUB_TYPE_MICRODVD,
    SUB_TYPE_SUBRIP,
    SUB_TYPE_SSA1,
    SUB_TYPE_SSA2_4,
100
    SUB_TYPE_ASS,
Laurent Aimar's avatar
   
Laurent Aimar committed
101
102
    SUB_TYPE_VPLAYER,
    SUB_TYPE_SAMI,
103
104
    SUB_TYPE_SUBVIEWER, /* SUBVIEWER 2 */
    SUB_TYPE_DVDSUBTITLE, /* Mplayer calls it subviewer2 */
105
    SUB_TYPE_MPL2,
106
    SUB_TYPE_AQT,
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
107
    SUB_TYPE_PJS,
108
    SUB_TYPE_MPSUB,
109
    SUB_TYPE_JACOSUB,
110
    SUB_TYPE_PSB,
111
    SUB_TYPE_RT,
112
    SUB_TYPE_DKS,
Arun Pandian G's avatar
Arun Pandian G committed
113
    SUB_TYPE_SUBVIEW1, /* SUBVIEWER 1 - mplayer calls it subrip09,
114
                         and Gnome subtitles SubViewer 1.0 */
Arun Pandian G's avatar
Arun Pandian G committed
115
    SUB_TYPE_VTT
Laurent Aimar's avatar
   
Laurent Aimar committed
116
};
117
118
119
120
121
122
123

typedef struct
{
    int     i_line_count;
    int     i_line;
    char    **line;
} text_t;
124

Laurent Aimar's avatar
   
Laurent Aimar committed
125
126
static int  TextLoad( text_t *, stream_t *s );
static void TextUnload( text_t * );
127

Laurent Aimar's avatar
   
Laurent Aimar committed
128
typedef struct
129
{
130
131
    int64_t i_start;
    int64_t i_stop;
132

Laurent Aimar's avatar
   
Laurent Aimar committed
133
134
    char    *psz_text;
} subtitle_t;
135
136


Laurent Aimar's avatar
   
Laurent Aimar committed
137
struct demux_sys_t
138
{
Laurent Aimar's avatar
   
Laurent Aimar committed
139
140
141
    int         i_type;
    text_t      txt;
    es_out_id_t *es;
142

Laurent Aimar's avatar
   
Laurent Aimar committed
143
144
    int64_t     i_next_demux_date;
    int64_t     i_microsecperframe;
145

Laurent Aimar's avatar
   
Laurent Aimar committed
146
147
148
149
    char        *psz_header;
    int         i_subtitle;
    int         i_subtitles;
    subtitle_t  *subtitle;
150

Laurent Aimar's avatar
   
Laurent Aimar committed
151
    int64_t     i_length;
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168

    /* */
    struct
    {
        bool b_inited;

        int i_comment;
        int i_time_resolution;
        int i_time_shift;
    } jss;
    struct
    {
        bool  b_inited;

        float f_total;
        float f_factor;
    } mpsub;
Laurent Aimar's avatar
   
Laurent Aimar committed
169
170
};

171
172
173
174
175
176
177
178
static int  ParseMicroDvd   ( demux_t *, subtitle_t *, int );
static int  ParseSubRip     ( demux_t *, subtitle_t *, int );
static int  ParseSubViewer  ( demux_t *, subtitle_t *, int );
static int  ParseSSA        ( demux_t *, subtitle_t *, int );
static int  ParseVplayer    ( demux_t *, subtitle_t *, int );
static int  ParseSami       ( demux_t *, subtitle_t *, int );
static int  ParseDVDSubtitle( demux_t *, subtitle_t *, int );
static int  ParseMPL2       ( demux_t *, subtitle_t *, int );
179
static int  ParseAQT        ( demux_t *, subtitle_t *, int );
180
static int  ParsePJS        ( demux_t *, subtitle_t *, int );
181
182
static int  ParseMPSub      ( demux_t *, subtitle_t *, int );
static int  ParseJSS        ( demux_t *, subtitle_t *, int );
183
static int  ParsePSB        ( demux_t *, subtitle_t *, int );
184
static int  ParseRealText   ( demux_t *, subtitle_t *, int );
185
static int  ParseDKS        ( demux_t *, subtitle_t *, int );
186
static int  ParseSubViewer1 ( demux_t *, subtitle_t *, int );
Arun Pandian G's avatar
Arun Pandian G committed
187
static int  ParseVTT        ( demux_t *, subtitle_t *, int );
188

189
static const struct
190
{
hartman's avatar
hartman committed
191
    const char *psz_type_name;
192
    int  i_type;
hartman's avatar
hartman committed
193
    const char *psz_name;
194
    int  (*pf_read)( demux_t *, subtitle_t*, int );
195
196
} sub_read_subtitle_function [] =
{
197
198
199
200
201
202
203
204
205
    { "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 },
206
    { "mpl2",       SUB_TYPE_MPL2,        "MPL2",        ParseMPL2 },
207
    { "aqt",        SUB_TYPE_AQT,         "AQTitle",     ParseAQT },
208
    { "pjs",        SUB_TYPE_PJS,         "PhoenixSub",  ParsePJS },
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
209
    { "mpsub",      SUB_TYPE_MPSUB,       "MPSub",       ParseMPSub },
210
    { "jacosub",    SUB_TYPE_JACOSUB,     "JacoSub",     ParseJSS },
211
    { "psb",        SUB_TYPE_PSB,         "PowerDivx",   ParsePSB },
212
    { "realtext",   SUB_TYPE_RT,          "RealText",    ParseRealText },
213
    { "dks",        SUB_TYPE_DKS,         "DKS",         ParseDKS },
214
    { "subviewer1", SUB_TYPE_SUBVIEW1,    "Subviewer 1", ParseSubViewer1 },
Arun Pandian G's avatar
Arun Pandian G committed
215
    { "text/vtt",   SUB_TYPE_VTT,         "WebVTT",      ParseVTT },
216
    { NULL,         SUB_TYPE_UNKNOWN,     "Unknown",     NULL }
217
};
218
219
220
/* When adding support for more formats, be sure to add their file extension
 * to src/input/subtitles.c to enable auto-detection.
 */
221

Laurent Aimar's avatar
   
Laurent Aimar committed
222
223
224
static int Demux( demux_t * );
static int Control( demux_t *, int, va_list );

225
static void Fix( demux_t * );
226
static char * get_language_from_filename( const char * );
Laurent Aimar's avatar
   
Laurent Aimar committed
227

228
/*****************************************************************************
Laurent Aimar's avatar
   
Laurent Aimar committed
229
 * Module initializer
230
 *****************************************************************************/
Laurent Aimar's avatar
   
Laurent Aimar committed
231
static int Open ( vlc_object_t *p_this )
232
{
233
234
235
236
237
    demux_t        *p_demux = (demux_t*)p_this;
    demux_sys_t    *p_sys;
    es_format_t    fmt;
    float          f_fps;
    char           *psz_type;
238
    int  (*pf_read)( demux_t *, subtitle_t*, int );
239
    int            i, i_max;
Eric Petit's avatar
Eric Petit committed
240

241
    if( !p_demux->b_force )
242
    {
Laurent Aimar's avatar
   
Laurent Aimar committed
243
        msg_Dbg( p_demux, "subtitle demux discarded" );
244
245
        return VLC_EGENERIC;
    }
gbazin's avatar
   
gbazin committed
246

Laurent Aimar's avatar
   
Laurent Aimar committed
247
248
249
    p_demux->pf_demux = Demux;
    p_demux->pf_control = Control;
    p_demux->p_sys = p_sys = malloc( sizeof( demux_sys_t ) );
250
251
252
    if( p_sys == NULL )
        return VLC_ENOMEM;

253
254
255
256
    p_sys->psz_header         = NULL;
    p_sys->i_subtitle         = 0;
    p_sys->i_subtitles        = 0;
    p_sys->subtitle           = NULL;
hartman's avatar
hartman committed
257
    p_sys->i_microsecperframe = 40000;
Laurent Aimar's avatar
   
Laurent Aimar committed
258

259
260
261
    p_sys->jss.b_inited       = false;
    p_sys->mpsub.b_inited     = false;

Laurent Aimar's avatar
   
Laurent Aimar committed
262
    /* Get the FPS */
263
    f_fps = var_CreateGetFloat( p_demux, "sub-original-fps" ); /* FIXME */
264
265
    if( f_fps >= 1.0 )
        p_sys->i_microsecperframe = (int64_t)( (float)1000000 / f_fps );
266

267
    msg_Dbg( p_demux, "Movie fps: %f", f_fps );
zorglub's avatar
zorglub committed
268

hartman's avatar
hartman committed
269
270
271
272
273
    /* Check for override of the fps */
    f_fps = var_CreateGetFloat( p_demux, "sub-fps" );
    if( f_fps >= 1.0 )
    {
        p_sys->i_microsecperframe = (int64_t)( (float)1000000 / f_fps );
hartman's avatar
hartman committed
274
        msg_Dbg( p_demux, "Override subtitle fps %f", f_fps );
hartman's avatar
hartman committed
275
276
    }

Laurent Aimar's avatar
   
Laurent Aimar committed
277
278
279
    /* Get or probe the type */
    p_sys->i_type = SUB_TYPE_UNKNOWN;
    psz_type = var_CreateGetString( p_demux, "sub-type" );
ivoire's avatar
ivoire committed
280
    if( psz_type && *psz_type )
281
    {
282
283
284
        int i;

        for( i = 0; ; i++ )
285
        {
286
287
            if( sub_read_subtitle_function[i].psz_type_name == NULL )
                break;
Laurent Aimar's avatar
   
Laurent Aimar committed
288

289
            if( !strcmp( sub_read_subtitle_function[i].psz_type_name,
Laurent Aimar's avatar
   
Laurent Aimar committed
290
                         psz_type ) )
291
            {
Laurent Aimar's avatar
   
Laurent Aimar committed
292
                p_sys->i_type = sub_read_subtitle_function[i].i_type;
293
294
                break;
            }
295
296
        }
    }
Laurent Aimar's avatar
   
Laurent Aimar committed
297
    free( psz_type );
Laurent Aimar's avatar
Laurent Aimar committed
298

299
300
301
302
303
304
305
306
307
308
309
    /* Detect Unicode while skipping the UTF-8 Byte Order Mark */
    bool unicode = false;
    const uint8_t *p_data;
    if( stream_Peek( p_demux->s, &p_data, 3 ) >= 3
     && !memcmp( p_data, "\xEF\xBB\xBF", 3 ) )
    {
        unicode = true;
        stream_Seek( p_demux->s, 3 ); /* skip BOM */
        msg_Dbg( p_demux, "detected Unicode Byte Order Mark" );
    }

Laurent Aimar's avatar
   
Laurent Aimar committed
310
311
    /* Probe if unknown type */
    if( p_sys->i_type == SUB_TYPE_UNKNOWN )
312
    {
313
        int     i_try;
314
        char    *s = NULL;
315

Laurent Aimar's avatar
   
Laurent Aimar committed
316
317
        msg_Dbg( p_demux, "autodetecting subtitle format" );
        for( i_try = 0; i_try < 256; i_try++ )
318
319
        {
            int i_dummy;
320
            char p_dummy;
321

Laurent Aimar's avatar
   
Laurent Aimar committed
322
            if( ( s = stream_ReadLine( p_demux->s ) ) == NULL )
323
324
                break;

325
            if( strcasestr( s, "<SAMI>" ) )
326
            {
Laurent Aimar's avatar
   
Laurent Aimar committed
327
                p_sys->i_type = SUB_TYPE_SAMI;
328
329
330
331
                break;
            }
            else if( sscanf( s, "{%d}{%d}", &i_dummy, &i_dummy ) == 2 ||
                     sscanf( s, "{%d}{}", &i_dummy ) == 1)
332
            {
Laurent Aimar's avatar
   
Laurent Aimar committed
333
                p_sys->i_type = SUB_TYPE_MICRODVD;
334
335
                break;
            }
336
            else if( sscanf( s, "%d:%d:%d,%d --> %d:%d:%d,%d",
Laurent Aimar's avatar
Laurent Aimar committed
337
                             &i_dummy,&i_dummy,&i_dummy,&i_dummy,
338
339
340
341
342
                             &i_dummy,&i_dummy,&i_dummy,&i_dummy ) == 8 ||
                     sscanf( s, "%d:%d:%d --> %d:%d:%d,%d",
                             &i_dummy,&i_dummy,&i_dummy,&i_dummy,
                             &i_dummy,&i_dummy,&i_dummy ) == 7 ||
                     sscanf( s, "%d:%d:%d,%d --> %d:%d:%d",
343
                             &i_dummy,&i_dummy,&i_dummy,&i_dummy,
344
345
346
347
348
349
350
351
352
353
354
355
356
                             &i_dummy,&i_dummy,&i_dummy ) == 7 ||
                     sscanf( s, "%d:%d:%d.%d --> %d:%d:%d.%d",
                             &i_dummy,&i_dummy,&i_dummy,&i_dummy,
                             &i_dummy,&i_dummy,&i_dummy,&i_dummy ) == 8 ||
                     sscanf( s, "%d:%d:%d --> %d:%d:%d.%d",
                             &i_dummy,&i_dummy,&i_dummy,&i_dummy,
                             &i_dummy,&i_dummy,&i_dummy ) == 7 ||
                     sscanf( s, "%d:%d:%d.%d --> %d:%d:%d",
                             &i_dummy,&i_dummy,&i_dummy,&i_dummy,
                             &i_dummy,&i_dummy,&i_dummy ) == 7 ||
                     sscanf( s, "%d:%d:%d --> %d:%d:%d",
                             &i_dummy,&i_dummy,&i_dummy,
                             &i_dummy,&i_dummy,&i_dummy ) == 6 )
357
            {
358
                p_sys->i_type = SUB_TYPE_SUBRIP;
359
360
                break;
            }
361
            else if( !strncasecmp( s, "!: This is a Sub Station Alpha v1", 33 ) )
362
            {
363
                p_sys->i_type = SUB_TYPE_SSA1;
364
                break;
365
            }
366
            else if( !strncasecmp( s, "ScriptType: v4.00+", 18 ) )
367
            {
368
369
370
371
372
373
                p_sys->i_type = SUB_TYPE_ASS;
                break;
            }
            else if( !strncasecmp( s, "ScriptType: v4.00", 17 ) )
            {
                p_sys->i_type = SUB_TYPE_SSA2_4;
374
                break;
375
            }
376
            else if( !strncasecmp( s, "Dialogue: Marked", 16  ) )
377
            {
378
379
380
381
382
383
                p_sys->i_type = SUB_TYPE_SSA2_4;
                break;
            }
            else if( !strncasecmp( s, "Dialogue:", 9  ) )
            {
                p_sys->i_type = SUB_TYPE_ASS;
384
385
                break;
            }
386
            else if( strcasestr( s, "[INFORMATION]" ) )
387
            {
Laurent Aimar's avatar
   
Laurent Aimar committed
388
                p_sys->i_type = SUB_TYPE_SUBVIEWER; /* I hope this will work */
389
390
                break;
            }
391
392
393
394
            else if( sscanf( s, "%d:%d:%d.%d %d:%d:%d",
                                 &i_dummy, &i_dummy, &i_dummy, &i_dummy,
                                 &i_dummy, &i_dummy, &i_dummy ) == 7 ||
                     sscanf( s, "@%d @%d", &i_dummy, &i_dummy) == 2)
395
396
            {
                p_sys->i_type = SUB_TYPE_JACOSUB;
397
                break;
398
            }
399
400
            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 )
401
            {
Laurent Aimar's avatar
   
Laurent Aimar committed
402
                p_sys->i_type = SUB_TYPE_VPLAYER;
403
                break;
404
            }
405
406
407
408
409
410
            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;
            }
411
412
413
414
415
416
417
418
419
420
421
            else if( sscanf( s, "[%d:%d:%d]%c",
                     &i_dummy, &i_dummy, &i_dummy, &p_dummy ) == 4 )
            {
                p_sys->i_type = SUB_TYPE_DKS;
                break;
            }
            else if( strstr( s, "*** START SCRIPT" ) )
            {
                p_sys->i_type = SUB_TYPE_SUBVIEW1;
                break;
            }
422
423
424
425
426
            else if( sscanf( s, "[%d][%d]", &i_dummy, &i_dummy ) == 2 ||
                     sscanf( s, "[%d][]", &i_dummy ) == 1)
            {
                p_sys->i_type = SUB_TYPE_MPL2;
                break;
427
            }
428
429
430
            else if( sscanf (s, "FORMAT=%d", &i_dummy) == 1 ||
                     ( sscanf (s, "FORMAT=TIM%c", &p_dummy) == 1
                       && p_dummy =='E' ) )
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
431
432
            {
                p_sys->i_type = SUB_TYPE_MPSUB;
433
                break;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
434
            }
435
            else if( sscanf( s, "-->> %d", &i_dummy) == 1 )
436
437
            {
                p_sys->i_type = SUB_TYPE_AQT;
438
                break;
439
            }
440
441
442
            else if( sscanf( s, "%d,%d,", &i_dummy, &i_dummy ) == 2 )
            {
                p_sys->i_type = SUB_TYPE_PJS;
443
                break;
444
            }
445
446
            else if( sscanf( s, "{%d:%d:%d}",
                                &i_dummy, &i_dummy, &i_dummy ) == 3 )
447
448
            {
                p_sys->i_type = SUB_TYPE_PSB;
449
                break;
450
            }
451
452
453
            else if( strcasestr( s, "<time" ) )
            {
                p_sys->i_type = SUB_TYPE_RT;
454
                break;
455
            }
Arun Pandian G's avatar
Arun Pandian G committed
456
457
458
459
460
            else if( !strncasecmp( s, "WEBVTT",6 ) )
            {
                p_sys->i_type = SUB_TYPE_VTT;
                break;
            }
461
462
463

            free( s );
            s = NULL;
464
        }
465

466
        free( s );
467

Laurent Aimar's avatar
   
Laurent Aimar committed
468
        /* It will nearly always work even for non seekable stream thanks the
trax's avatar
trax committed
469
         * caching system, and if it fails we lose just a few sub */
470
        if( stream_Seek( p_demux->s, unicode ? 3 : 0 ) )
Laurent Aimar's avatar
   
Laurent Aimar committed
471
472
            msg_Warn( p_demux, "failed to rewind" );
    }
473
474

    /* Quit on unknown subtitles */
Laurent Aimar's avatar
   
Laurent Aimar committed
475
476
    if( p_sys->i_type == SUB_TYPE_UNKNOWN )
    {
477
        stream_Seek( p_demux->s, 0 );
478
        msg_Warn( p_demux, "failed to recognize subtitle type" );
479
        free( p_sys );
Laurent Aimar's avatar
   
Laurent Aimar committed
480
        return VLC_EGENERIC;
481
482
    }

483
    for( i = 0; ; i++ )
484
    {
Laurent Aimar's avatar
   
Laurent Aimar committed
485
        if( sub_read_subtitle_function[i].i_type == p_sys->i_type )
486
        {
Laurent Aimar's avatar
   
Laurent Aimar committed
487
488
489
            msg_Dbg( p_demux, "detected %s format",
                     sub_read_subtitle_function[i].psz_name );
            pf_read = sub_read_subtitle_function[i].pf_read;
490
491
            break;
        }
492
    }
493

Laurent Aimar's avatar
   
Laurent Aimar committed
494
495
496
497
498
499
    msg_Dbg( p_demux, "loading all subtitles..." );

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

    /* Parse it */
500
    for( i_max = 0;; )
Laurent Aimar's avatar
Laurent Aimar committed
501
    {
Laurent Aimar's avatar
   
Laurent Aimar committed
502
        if( p_sys->i_subtitles >= i_max )
503
        {
Laurent Aimar's avatar
   
Laurent Aimar committed
504
            i_max += 500;
505
            if( !( p_sys->subtitle = realloc_or_free( p_sys->subtitle,
gbazin's avatar
   
gbazin committed
506
                                              sizeof(subtitle_t) * i_max ) ) )
507
            {
508
509
                TextUnload( &p_sys->txt );
                free( p_sys );
gbazin's avatar
   
gbazin committed
510
                return VLC_ENOMEM;
511
512
            }
        }
Laurent Aimar's avatar
   
Laurent Aimar committed
513

514
515
        if( pf_read( p_demux, &p_sys->subtitle[p_sys->i_subtitles],
                     p_sys->i_subtitles ) )
516
            break;
Laurent Aimar's avatar
   
Laurent Aimar committed
517
518

        p_sys->i_subtitles++;
519
    }
Laurent Aimar's avatar
   
Laurent Aimar committed
520
521
    /* Unload */
    TextUnload( &p_sys->txt );
522

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

Laurent Aimar's avatar
   
Laurent Aimar committed
525
526
527
528
    /* Fix subtitle (order and time) *** */
    p_sys->i_subtitle = 0;
    p_sys->i_length = 0;
    if( p_sys->i_subtitles > 0 )
529
    {
Laurent Aimar's avatar
   
Laurent Aimar committed
530
531
532
533
        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;
534
    }
Laurent Aimar's avatar
Laurent Aimar committed
535

536
    /* *** add subtitle ES *** */
hartman's avatar
hartman committed
537
    if( p_sys->i_type == SUB_TYPE_SSA1 ||
538
539
             p_sys->i_type == SUB_TYPE_SSA2_4 ||
             p_sys->i_type == SUB_TYPE_ASS )
540
    {
541
        Fix( p_demux );
542
        es_format_Init( &fmt, SPU_ES, VLC_CODEC_SSA );
543
544
    }
    else
545
        es_format_Init( &fmt, SPU_ES, VLC_CODEC_SUBT );
546
547
548
549
550
551
552
553
554
555
556

    /* Stupid language detection in the filename */
    char * psz_language = get_language_from_filename( p_demux->psz_file );

    if( psz_language )
    {
        fmt.psz_language = psz_language;
        msg_Dbg( p_demux, "detected language %s of subtitle: %s", psz_language,
                 p_demux->psz_location );
    }

557
558
    if( unicode )
        fmt.subs.psz_encoding = strdup( "UTF-8" );
559
560
561
562
563
    char *psz_description = var_InheritString( p_demux, "sub-description" );
    if( psz_description && *psz_description )
        fmt.psz_description = psz_description;
    else
        free( psz_description );
Laurent Aimar's avatar
   
Laurent Aimar committed
564
    if( p_sys->psz_header != NULL )
565
    {
Laurent Aimar's avatar
   
Laurent Aimar committed
566
567
        fmt.i_extra = strlen( p_sys->psz_header ) + 1;
        fmt.p_extra = strdup( p_sys->psz_header );
568
    }
Laurent Aimar's avatar
   
Laurent Aimar committed
569
    p_sys->es = es_out_Add( p_demux->out, &fmt );
570
    es_format_Clean( &fmt );
hartman's avatar
hartman committed
571

Laurent Aimar's avatar
Laurent Aimar committed
572
    return VLC_SUCCESS;
573
574
575
}

/*****************************************************************************
Laurent Aimar's avatar
   
Laurent Aimar committed
576
 * Close: Close subtitle demux
577
 *****************************************************************************/
Laurent Aimar's avatar
   
Laurent Aimar committed
578
static void Close( vlc_object_t *p_this )
579
{
Laurent Aimar's avatar
   
Laurent Aimar committed
580
581
582
    demux_t *p_demux = (demux_t*)p_this;
    demux_sys_t *p_sys = p_demux->p_sys;
    int i;
583

Laurent Aimar's avatar
   
Laurent Aimar committed
584
    for( i = 0; i < p_sys->i_subtitles; i++ )
585
586
        free( p_sys->subtitle[i].psz_text );
    free( p_sys->subtitle );
Laurent Aimar's avatar
   
Laurent Aimar committed
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618

    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;
619
            while( p_sys->i_subtitle < p_sys->i_subtitles )
Laurent Aimar's avatar
   
Laurent Aimar committed
620
            {
621
622
623
624
625
626
627
                const subtitle_t *p_subtitle = &p_sys->subtitle[p_sys->i_subtitle];

                if( p_subtitle->i_start > i64 )
                    break;
                if( p_subtitle->i_stop > p_subtitle->i_start && p_subtitle->i_stop > i64 )
                    break;

Laurent Aimar's avatar
   
Laurent Aimar committed
628
629
                p_sys->i_subtitle++;
            }
630

Laurent Aimar's avatar
   
Laurent Aimar committed
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
            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;

670
        case DEMUX_GET_PTS_DELAY:
Laurent Aimar's avatar
   
Laurent Aimar committed
671
672
        case DEMUX_GET_FPS:
        case DEMUX_GET_META:
673
        case DEMUX_GET_ATTACHMENTS:
Laurent Aimar's avatar
   
Laurent Aimar committed
674
        case DEMUX_GET_TITLE_INFO:
675
        case DEMUX_HAS_UNSUPPORTED_META:
Laurent Aimar's avatar
Laurent Aimar committed
676
        case DEMUX_CAN_RECORD:
Laurent Aimar's avatar
   
Laurent Aimar committed
677
678
679
            return VLC_EGENERIC;

        default:
680
            msg_Err( p_demux, "unknown query %d in subtitle control", i_query );
Laurent Aimar's avatar
   
Laurent Aimar committed
681
            return VLC_EGENERIC;
682
    }
Laurent Aimar's avatar
   
Laurent Aimar committed
683
684
685
686
687
688
689
690
691
692
693
694
695
}

/*****************************************************************************
 * 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
696
    i_maxdate = p_sys->i_next_demux_date - var_GetTime( p_demux->p_parent, "spu-delay" );;
Laurent Aimar's avatar
   
Laurent Aimar committed
697
    if( i_maxdate <= 0 && p_sys->i_subtitle < p_sys->i_subtitles )
698
    {
Laurent Aimar's avatar
   
Laurent Aimar committed
699
700
        /* Should not happen */
        i_maxdate = p_sys->subtitle[p_sys->i_subtitle].i_start + 1;
701
702
    }

hartman's avatar
hartman committed
703
704
    while( p_sys->i_subtitle < p_sys->i_subtitles &&
           p_sys->subtitle[p_sys->i_subtitle].i_start < i_maxdate )
705
    {
706
707
        const subtitle_t *p_subtitle = &p_sys->subtitle[p_sys->i_subtitle];

hartman's avatar
hartman committed
708
        block_t *p_block;
709
        int i_len = strlen( p_subtitle->psz_text ) + 1;
710

711
        if( i_len <= 1 || p_subtitle->i_start < 0 )
hartman's avatar
hartman committed
712
        {
Laurent Aimar's avatar
   
Laurent Aimar committed
713
            p_sys->i_subtitle++;
hartman's avatar
hartman committed
714
            continue;
715
        }
716

717
        if( ( p_block = block_Alloc( i_len ) ) == NULL )
hartman's avatar
hartman committed
718
719
720
721
        {
            p_sys->i_subtitle++;
            continue;
        }
722

723
        p_block->i_dts =
724
725
        p_block->i_pts = VLC_TS_0 + p_subtitle->i_start;
        if( p_subtitle->i_stop >= 0 && p_subtitle->i_stop >= p_subtitle->i_start )
726
            p_block->i_length = p_subtitle->i_stop - p_subtitle->i_start;
727

728
729
730
        memcpy( p_block->p_buffer, p_subtitle->psz_text, i_len );

        es_out_Send( p_demux->out, p_sys->es, p_block );
731

hartman's avatar
hartman committed
732
        p_sys->i_subtitle++;
733
734
    }

Laurent Aimar's avatar
   
Laurent Aimar committed
735
736
    /* */
    p_sys->i_next_demux_date = 0;
737

Laurent Aimar's avatar
   
Laurent Aimar committed
738
    return 1;
739
}
740

741
/*****************************************************************************
Laurent Aimar's avatar
   
Laurent Aimar committed
742
 * Fix: fix time stamp and order of subtitle
743
 *****************************************************************************/
Laurent Aimar's avatar
   
Laurent Aimar committed
744
static void Fix( demux_t *p_demux )
745
{
Laurent Aimar's avatar
   
Laurent Aimar committed
746
    demux_sys_t *p_sys = p_demux->p_sys;
747
    bool b_done;
748
749
750

    /* *** fix order (to be sure...) *** */
    /* We suppose that there are near in order and this durty bubble sort
Konstantin Pavlov's avatar
Konstantin Pavlov committed
751
     * would not take too much time
752
753
754
     */
    do
    {
755
        b_done = true;
756
        for( int i_index = 1; i_index < p_sys->i_subtitles; i_index++ )
757
        {
Laurent Aimar's avatar
   
Laurent Aimar committed
758
            if( p_sys->subtitle[i_index].i_start <
759
                p_sys->subtitle[i_index - 1].i_start )
760
761
            {
                subtitle_t sub_xch;
Laurent Aimar's avatar
Laurent Aimar committed
762
                memcpy( &sub_xch,
Laurent Aimar's avatar
   
Laurent Aimar committed
763
                        p_sys->subtitle + i_index - 1,
764
                        sizeof( subtitle_t ) );
Laurent Aimar's avatar
   
Laurent Aimar committed
765
766
                memcpy( p_sys->subtitle + i_index - 1,
                        p_sys->subtitle + i_index,
767
                        sizeof( subtitle_t ) );
Laurent Aimar's avatar
   
Laurent Aimar committed
768
                memcpy( p_sys->subtitle + i_index,
769
770
                        &sub_xch,
                        sizeof( subtitle_t ) );
771
                b_done = false;
772
773
            }
        }
Laurent Aimar's avatar
   
Laurent Aimar committed
774
775
776
777
778
779
780
781
782
783
784
785
    } while( !b_done );
}

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 * ) );
786
787
    if( !txt->line )
        return VLC_ENOMEM;
Laurent Aimar's avatar
   
Laurent Aimar committed
788
789
790
791
792
793
794
795
796
797
798
799
800

    /* 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;
801
802
803
            txt->line = realloc_or_free( txt->line, i_line_max * sizeof( char * ) );
            if( !txt->line )
                return VLC_ENOMEM;
Laurent Aimar's avatar
   
Laurent Aimar committed
804
805
806
807
808
809
810
811
812
813
        }
    }

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

    return VLC_SUCCESS;
814
}
Laurent Aimar's avatar
   
Laurent Aimar committed
815
816
817
static void TextUnload( text_t *txt )
{
    int i;
818

Laurent Aimar's avatar
   
Laurent Aimar committed
819
820
821
822
823
824
825
826
    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;
}
827

Laurent Aimar's avatar
   
Laurent Aimar committed
828
829
830
831
832
833
834
835
836
837
838
839
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--;
}
840
841

/*****************************************************************************
Laurent Aimar's avatar
Laurent Aimar committed
842
 * Specific Subtitle function
843
 *****************************************************************************/
844
845
846
847
848
/* ParseMicroDvd:
 *  Format:
 *      {n1}{n2}Line1|Line2|Line3....
 *  where n1 and n2 are the video frame number (n2 can be empty)
 */
849
850
static int ParseMicroDvd( demux_t *p_demux, subtitle_t *p_subtitle,
                          int i_idx )
851
{
852
    VLC_UNUSED( i_idx );
Laurent Aimar's avatar
   
Laurent Aimar committed
853
854
    demux_sys_t *p_sys = p_demux->p_sys;
    text_t      *txt = &p_sys->txt;
855
856
857
858
    char *psz_text;
    int  i_start;
    int  i_stop;
    int  i;
Laurent Aimar's avatar
   
Laurent Aimar committed
859

860
861
    for( ;; )
    {
862
863
864
865
866
867
868
869
        const char *s = TextGetLine( txt );
        if( !s )
            return VLC_EGENERIC;

        psz_text = malloc( strlen(s) + 1 );
        if( !psz_text )
            return VLC_ENOMEM;

870
        i_start = 0;
871
        i_stop  = -1;
872
873
        if( sscanf( s, "{%d}{}%[^\r\n]", &i_start, psz_text ) == 2 ||
            sscanf( s, "{%d}{%d}%[^\r\n]", &i_start, &i_stop, psz_text ) == 3)
874
        {
875
876
877
878
879
880
881
882
883
            float f_fps;
            if( i_start != 1 || i_stop != 1 )
                break;

            /* We found a possible setting of the framerate "{1}{1}23.976" */
            /* Check if it's usable, and if the sub-fps is not set */
            f_fps = us_strtod( psz_text, NULL );
            if( f_fps > 0.0 && var_GetFloat( p_demux, "sub-fps" ) <= 0.0 )
                p_sys->i_microsecperframe = (int64_t)((float)1000000 / f_fps);
884
        }
885
        free( psz_text );
886
887
    }

888
    /* replace | by \n */
889
    for( i = 0; psz_text[i] != '\0'; i++ )
890
    {
891
892
        if( psz_text[i] == '|' )
            psz_text[i] = '\n';
893
    }
Laurent Aimar's avatar
   
Laurent Aimar committed
894

895
896
    /* */
    p_subtitle->i_start  = i_start * p_sys->i_microsecperframe;
897
    p_subtitle->i_stop   = i_stop >= 0 ? (i_stop  * p_sys->i_microsecperframe) : -1;
898
899
    p_subtitle->psz_text = psz_text;
    return VLC_SUCCESS;
900
901
}

902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
/* ParseSubRipSubViewer
 *  Format SubRip
 *      n
 *      h1:m1:s1,d1 --> h2:m2:s2,d2
 *      Line1
 *      Line2
 *      ....
 *      [Empty line]
 *  Format SubViewer v1/v2
 *      h1:m1:s1.d1,h2:m2:s2.d2
 *      Line1[br]Line2
 *      Line3
 *      ...
 *      [empty line]
 *  We ignore line number for SubRip
 */
static int ParseSubRipSubViewer( demux_t *p_demux, subtitle_t *p_subtitle,
919
                                 int (* pf_parse_timing)(subtitle_t *, const char *),
920
                                 bool b_replace_br )
921
{
Laurent Aimar's avatar
   
Laurent Aimar committed
922
923
    demux_sys_t *p_sys = p_demux->p_sys;
    text_t      *txt = &p_sys->txt;
924
    char    *psz_text;
925

926
927
    for( ;; )
    {
928
929
930
931
932
        const char *s = TextGetLine( txt );

        if( !s )
            return VLC_EGENERIC;

933
934
        if( pf_parse_timing( p_subtitle, s) == VLC_SUCCESS &&
            p_subtitle->i_start < p_subtitle->i_stop )
935
        {
936
            break;
937
938
        }
    }
939

940
941
942