subtitle.c 53.5 KB
Newer Older
1
/*****************************************************************************
hartman's avatar
hartman committed
2
 * subtitle.c: Demux for subtitle text files.
3
 *****************************************************************************
dionoea's avatar
dionoea committed
4
 * Copyright (C) 1999-2007 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
 *          Jean-Baptiste Kempf <jb@videolan.org>
10
11
12
13
14
 *
 * 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
15
 *
16
17
18
19
20
21
22
 * 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
23
 * 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

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

38
#include <errno.h>
39
40
41
#ifdef HAVE_SYS_TYPES_H
#   include <sys/types.h>
#endif
42
#include <ctype.h>
43

zorglub's avatar
zorglub committed
44
45
#include <vlc_demux.h>
#include <vlc_charset.h>
46
47
48
49

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
Laurent Aimar's avatar
   
Laurent Aimar committed
50
51
52
static int  Open ( vlc_object_t *p_this );
static void Close( vlc_object_t *p_this );

gbazin's avatar
   
gbazin committed
53
#define SUB_DELAY_LONGTEXT \
54
    N_("Apply a delay to all subtitles (in 1/10s, eg 100 means 10s).")
55
#define SUB_FPS_LONGTEXT \
56
57
    N_("Override the normal frames per second settings. " \
    "This will only work with MicroDVD and SubRIP (SRT) subtitles.")
58
#define SUB_TYPE_LONGTEXT \
59
60
    N_("Force the subtiles format. Valid values are : \"microdvd\", " \
    "\"subrip\",  \"ssa1\", \"ssa2-4\", \"ass\", \"vplayer\" " \
61
    "\"sami\", \"dvdsubtitle\", \"mpl2\", \"aqt\", \"pjs\" "\
62
    "\"mpsub\" \"jacosub\" \"psb\" and \"auto\" (meaning autodetection, this " \
63
    "should always work).")
hartman's avatar
hartman committed
64
static const char *ppsz_sub_type[] =
Laurent Aimar's avatar
   
Laurent Aimar committed
65
66
{
    "auto", "microdvd", "subrip", "subviewer", "ssa1",
67
    "ssa2-4", "ass", "vplayer", "sami", "dvdsubtitle", "mpl2",
68
    "aqt", "pjs", "mpsub", "jacosub", "psb"
Laurent Aimar's avatar
   
Laurent Aimar committed
69
};
70
71

vlc_module_begin();
72
    set_shortname( _("Subtitles"));
zorglub's avatar
zorglub committed
73
    set_description( _("Text subtitles parser") );
74
    set_capability( "demux", 0 );
zorglub's avatar
zorglub committed
75
76
    set_category( CAT_INPUT );
    set_subcategory( SUBCAT_INPUT_DEMUX );
77
    add_float( "sub-fps", 0.0, NULL,
hartman's avatar
hartman committed
78
               N_("Frames per second"),
79
               SUB_FPS_LONGTEXT, true );
zorglub's avatar
zorglub committed
80
81
    add_integer( "sub-delay", 0, NULL,
               N_("Subtitles delay"),
82
               SUB_DELAY_LONGTEXT, true );
zorglub's avatar
zorglub committed
83
    add_string( "sub-type", "auto", NULL, N_("Subtitles format"),
84
                SUB_TYPE_LONGTEXT, true );
hartman's avatar
hartman committed
85
        change_string_list( ppsz_sub_type, NULL, NULL );
Laurent Aimar's avatar
   
Laurent Aimar committed
86
87
88
    set_callbacks( Open, Close );

    add_shortcut( "subtitle" );
89
90
91
vlc_module_end();

/*****************************************************************************
Laurent Aimar's avatar
   
Laurent Aimar committed
92
 * Prototypes:
93
 *****************************************************************************/
Laurent Aimar's avatar
   
Laurent Aimar committed
94
enum
95
{
Laurent Aimar's avatar
   
Laurent Aimar committed
96
97
98
99
100
    SUB_TYPE_UNKNOWN = -1,
    SUB_TYPE_MICRODVD,
    SUB_TYPE_SUBRIP,
    SUB_TYPE_SSA1,
    SUB_TYPE_SSA2_4,
101
    SUB_TYPE_ASS,
Laurent Aimar's avatar
   
Laurent Aimar committed
102
103
    SUB_TYPE_VPLAYER,
    SUB_TYPE_SAMI,
104
    SUB_TYPE_SUBVIEWER, //SUBVIEWER 2!
105
    SUB_TYPE_DVDSUBTITLE,
106
    SUB_TYPE_MPL2,
107
    SUB_TYPE_AQT,
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
108
    SUB_TYPE_PJS,
109
    SUB_TYPE_MPSUB,
110
    SUB_TYPE_JACOSUB,
111
112
    SUB_TYPE_PSB,
    SUB_TYPE_RT
Laurent Aimar's avatar
   
Laurent Aimar committed
113
};
114
115
116
117
118
119
120

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

Laurent Aimar's avatar
   
Laurent Aimar committed
122
123
static int  TextLoad( text_t *, stream_t *s );
static void TextUnload( text_t * );
124

Laurent Aimar's avatar
   
Laurent Aimar committed
125
typedef struct
126
{
127
128
    int64_t i_start;
    int64_t i_stop;
129

Laurent Aimar's avatar
   
Laurent Aimar committed
130
131
    char    *psz_text;
} subtitle_t;
132
133


Laurent Aimar's avatar
   
Laurent Aimar committed
134
struct demux_sys_t
135
{
Laurent Aimar's avatar
   
Laurent Aimar committed
136
137
138
    int         i_type;
    text_t      txt;
    es_out_id_t *es;
139

Laurent Aimar's avatar
   
Laurent Aimar committed
140
141
    int64_t     i_next_demux_date;
    int64_t     i_microsecperframe;
142

Laurent Aimar's avatar
   
Laurent Aimar committed
143
144
145
146
    char        *psz_header;
    int         i_subtitle;
    int         i_subtitles;
    subtitle_t  *subtitle;
147

Laurent Aimar's avatar
   
Laurent Aimar committed
148
149
150
    int64_t     i_length;
};

151
152
153
154
155
156
157
158
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 );
159
static int  ParseAQT        ( demux_t *, subtitle_t *, int );
160
static int  ParsePJS        ( demux_t *, subtitle_t *, int );
161
162
static int  ParseMPSub      ( demux_t *, subtitle_t *, int );
static int  ParseJSS        ( demux_t *, subtitle_t *, int );
163
static int  ParsePSB        ( demux_t *, subtitle_t *, int );
164
static int  ParseRealText   ( demux_t *, subtitle_t *, int );
165

166
167
static struct
{
hartman's avatar
hartman committed
168
    const char *psz_type_name;
169
    int  i_type;
hartman's avatar
hartman committed
170
    const char *psz_name;
171
    int  (*pf_read)( demux_t *, subtitle_t*, int );
172
173
} sub_read_subtitle_function [] =
{
174
175
176
177
178
179
180
181
182
    { "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 },
183
    { "mpl2",       SUB_TYPE_MPL2,        "MPL2",        ParseMPL2 },
184
    { "aqt",        SUB_TYPE_AQT,         "AQTitle",     ParseAQT },
185
    { "pjs",        SUB_TYPE_PJS,         "PhoenixSub",  ParsePJS },
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
186
    { "mpsub",      SUB_TYPE_MPSUB,       "MPSub",       ParseMPSub },
187
    { "jacosub",    SUB_TYPE_JACOSUB,     "JacoSub",     ParseJSS },
188
    { "psb",        SUB_TYPE_PSB,         "PowerDivx",   ParsePSB },
189
    { "realtext",   SUB_TYPE_RT,          "RealText",    ParseRealText },
190
    { NULL,         SUB_TYPE_UNKNOWN,     "Unknown",     NULL }
191
192
};

193
194
195
196
197
198
/* Missing Detect
    SubViewer 1
    Subrip09
   */


Laurent Aimar's avatar
   
Laurent Aimar committed
199
200
201
static int Demux( demux_t * );
static int Control( demux_t *, int, va_list );

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

204
/*****************************************************************************
Laurent Aimar's avatar
   
Laurent Aimar committed
205
 * Module initializer
206
 *****************************************************************************/
Laurent Aimar's avatar
   
Laurent Aimar committed
207
static int Open ( vlc_object_t *p_this )
208
{
209
210
211
212
213
    demux_t        *p_demux = (demux_t*)p_this;
    demux_sys_t    *p_sys;
    es_format_t    fmt;
    float          f_fps;
    char           *psz_type;
214
    int  (*pf_read)( demux_t *, subtitle_t*, int );
215
    int            i, i_max;
Eric Petit's avatar
Eric Petit committed
216

217
    if( !p_demux->b_force )
218
    {
Laurent Aimar's avatar
   
Laurent Aimar committed
219
        msg_Dbg( p_demux, "subtitle demux discarded" );
220
221
        return VLC_EGENERIC;
    }
gbazin's avatar
   
gbazin committed
222

Laurent Aimar's avatar
   
Laurent Aimar committed
223
224
225
    p_demux->pf_demux = Demux;
    p_demux->pf_control = Control;
    p_demux->p_sys = p_sys = malloc( sizeof( demux_sys_t ) );
226
227
228
    if( p_sys == NULL )
        return VLC_ENOMEM;

229
230
231
232
    p_sys->psz_header         = NULL;
    p_sys->i_subtitle         = 0;
    p_sys->i_subtitles        = 0;
    p_sys->subtitle           = NULL;
hartman's avatar
hartman committed
233
    p_sys->i_microsecperframe = 40000;
Laurent Aimar's avatar
   
Laurent Aimar committed
234
235

    /* Get the FPS */
236
237
238
    f_fps = var_CreateGetFloat( p_demux, "sub-original-fps" );
    if( f_fps >= 1.0 )
        p_sys->i_microsecperframe = (int64_t)( (float)1000000 / f_fps );
239

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

hartman's avatar
hartman committed
242
243
244
245
246
    /* 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
247
        msg_Dbg( p_demux, "Override subtitle fps %f", f_fps );
hartman's avatar
hartman committed
248
249
    }

Laurent Aimar's avatar
   
Laurent Aimar committed
250
251
252
253
    /* Get or probe the type */
    p_sys->i_type = SUB_TYPE_UNKNOWN;
    psz_type = var_CreateGetString( p_demux, "sub-type" );
    if( *psz_type )
254
    {
255
256
257
        int i;

        for( i = 0; ; i++ )
258
        {
259
260
            if( sub_read_subtitle_function[i].psz_type_name == NULL )
                break;
Laurent Aimar's avatar
   
Laurent Aimar committed
261

262
            if( !strcmp( sub_read_subtitle_function[i].psz_type_name,
Laurent Aimar's avatar
   
Laurent Aimar committed
263
                         psz_type ) )
264
            {
Laurent Aimar's avatar
   
Laurent Aimar committed
265
                p_sys->i_type = sub_read_subtitle_function[i].i_type;
266
267
                break;
            }
268
269
        }
    }
Laurent Aimar's avatar
   
Laurent Aimar committed
270
    free( psz_type );
Laurent Aimar's avatar
Laurent Aimar committed
271

Laurent Aimar's avatar
   
Laurent Aimar committed
272
273
    /* Probe if unknown type */
    if( p_sys->i_type == SUB_TYPE_UNKNOWN )
274
    {
275
        int     i_try;
276
        char    *s = NULL;
277

Laurent Aimar's avatar
   
Laurent Aimar committed
278
279
        msg_Dbg( p_demux, "autodetecting subtitle format" );
        for( i_try = 0; i_try < 256; i_try++ )
280
281
        {
            int i_dummy;
282
            char p_dummy;
283

Laurent Aimar's avatar
   
Laurent Aimar committed
284
            if( ( s = stream_ReadLine( p_demux->s ) ) == NULL )
285
286
                break;

287
            if( strcasestr( s, "<SAMI>" ) )
288
            {
Laurent Aimar's avatar
   
Laurent Aimar committed
289
                p_sys->i_type = SUB_TYPE_SAMI;
290
291
292
293
                break;
            }
            else if( sscanf( s, "{%d}{%d}", &i_dummy, &i_dummy ) == 2 ||
                     sscanf( s, "{%d}{}", &i_dummy ) == 1)
294
            {
Laurent Aimar's avatar
   
Laurent Aimar committed
295
                p_sys->i_type = SUB_TYPE_MICRODVD;
296
297
                break;
            }
298
            else if( sscanf( s,
299
                             "%d:%d:%d,%d --> %d:%d:%d,%d",
Laurent Aimar's avatar
Laurent Aimar committed
300
                             &i_dummy,&i_dummy,&i_dummy,&i_dummy,
301
302
                             &i_dummy,&i_dummy,&i_dummy,&i_dummy ) == 8 )
            {
Laurent Aimar's avatar
   
Laurent Aimar committed
303
                p_sys->i_type = SUB_TYPE_SUBRIP;
304
305
                break;
            }
306
            else if( !strncasecmp( s, "!: This is a Sub Station Alpha v1", 33 ) )
307
            {
308
                p_sys->i_type = SUB_TYPE_SSA1;
309
                break;
310
            }
311
            else if( !strncasecmp( s, "ScriptType: v4.00+", 18 ) )
312
            {
313
314
315
316
317
318
                p_sys->i_type = SUB_TYPE_ASS;
                break;
            }
            else if( !strncasecmp( s, "ScriptType: v4.00", 17 ) )
            {
                p_sys->i_type = SUB_TYPE_SSA2_4;
319
                break;
320
            }
321
            else if( !strncasecmp( s, "Dialogue: Marked", 16  ) )
322
            {
323
324
325
326
327
328
                p_sys->i_type = SUB_TYPE_SSA2_4;
                break;
            }
            else if( !strncasecmp( s, "Dialogue:", 9  ) )
            {
                p_sys->i_type = SUB_TYPE_ASS;
329
330
                break;
            }
331
            else if( strcasestr( s, "[INFORMATION]" ) )
332
            {
Laurent Aimar's avatar
   
Laurent Aimar committed
333
                p_sys->i_type = SUB_TYPE_SUBVIEWER; /* I hope this will work */
334
335
                break;
            }
336
337
338
339
340
            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)
            {
                p_sys->i_type = SUB_TYPE_JACOSUB;
            }
341
342
            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 )
343
            {
Laurent Aimar's avatar
   
Laurent Aimar committed
344
                p_sys->i_type = SUB_TYPE_VPLAYER;
345
                break;
346
            }
347
348
349
350
351
352
            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;
            }
353
354
355
356
357
            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;
358
            }
359
360
361
            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
362
363
364
            {
                p_sys->i_type = SUB_TYPE_MPSUB;
            }
365
            else if( sscanf( s, "-->> %d", &i_dummy) == 1 )
366
367
            {
                p_sys->i_type = SUB_TYPE_AQT;
368
            }
369
370
371
372
            else if( sscanf( s, "%d,%d,", &i_dummy, &i_dummy ) == 2 )
            {
                p_sys->i_type = SUB_TYPE_PJS;
            }
373
374
            else if( sscanf( s, "{%d:%d:%d}",
                                &i_dummy, &i_dummy, &i_dummy ) == 3 )
375
376
377
            {
                p_sys->i_type = SUB_TYPE_PSB;
            }
378
379
380
381
            else if( strcasestr( s, "<time" ) )
            {
                p_sys->i_type = SUB_TYPE_RT;
            }
382
383
384

            free( s );
            s = NULL;
385
        }
386

387
        free( s );
388

Laurent Aimar's avatar
   
Laurent Aimar committed
389
        /* It will nearly always work even for non seekable stream thanks the
trax's avatar
trax committed
390
         * caching system, and if it fails we lose just a few sub */
Laurent Aimar's avatar
   
Laurent Aimar committed
391
392
393
394
395
        if( stream_Seek( p_demux->s, 0 ) )
        {
            msg_Warn( p_demux, "failed to rewind" );
        }
    }
396
397

    /* Quit on unknown subtitles */
Laurent Aimar's avatar
   
Laurent Aimar committed
398
399
400
    if( p_sys->i_type == SUB_TYPE_UNKNOWN )
    {
        msg_Err( p_demux, "failed to recognize subtitle type" );
401
        free( p_sys );
Laurent Aimar's avatar
   
Laurent Aimar committed
402
        return VLC_EGENERIC;
403
404
    }

405
    for( i = 0; ; i++ )
406
    {
Laurent Aimar's avatar
   
Laurent Aimar committed
407
        if( sub_read_subtitle_function[i].i_type == p_sys->i_type )
408
        {
Laurent Aimar's avatar
   
Laurent Aimar committed
409
410
411
            msg_Dbg( p_demux, "detected %s format",
                     sub_read_subtitle_function[i].psz_name );
            pf_read = sub_read_subtitle_function[i].pf_read;
412
413
            break;
        }
414
    }
415

Laurent Aimar's avatar
   
Laurent Aimar committed
416
417
418
419
420
421
    msg_Dbg( p_demux, "loading all subtitles..." );

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

    /* Parse it */
422
    for( i_max = 0;; )
Laurent Aimar's avatar
Laurent Aimar committed
423
    {
Laurent Aimar's avatar
   
Laurent Aimar committed
424
        if( p_sys->i_subtitles >= i_max )
425
        {
Laurent Aimar's avatar
   
Laurent Aimar committed
426
427
            i_max += 500;
            if( !( p_sys->subtitle = realloc( p_sys->subtitle,
gbazin's avatar
   
gbazin committed
428
                                              sizeof(subtitle_t) * i_max ) ) )
429
            {
Laurent Aimar's avatar
   
Laurent Aimar committed
430
                msg_Err( p_demux, "out of memory");
431
                free( p_sys->subtitle );
432
433
                TextUnload( &p_sys->txt );
                free( p_sys );
gbazin's avatar
   
gbazin committed
434
                return VLC_ENOMEM;
435
436
            }
        }
Laurent Aimar's avatar
   
Laurent Aimar committed
437

438
439
        if( pf_read( p_demux, &p_sys->subtitle[p_sys->i_subtitles],
                     p_sys->i_subtitles ) )
440
            break;
Laurent Aimar's avatar
   
Laurent Aimar committed
441
442

        p_sys->i_subtitles++;
443
    }
Laurent Aimar's avatar
   
Laurent Aimar committed
444
445
    /* Unload */
    TextUnload( &p_sys->txt );
446

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

Laurent Aimar's avatar
   
Laurent Aimar committed
449
450
451
452
    /* Fix subtitle (order and time) *** */
    p_sys->i_subtitle = 0;
    p_sys->i_length = 0;
    if( p_sys->i_subtitles > 0 )
453
    {
Laurent Aimar's avatar
   
Laurent Aimar committed
454
455
456
457
        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;
458
    }
Laurent Aimar's avatar
Laurent Aimar committed
459

460
    /* *** add subtitle ES *** */
hartman's avatar
hartman committed
461
    if( p_sys->i_type == SUB_TYPE_SSA1 ||
462
463
             p_sys->i_type == SUB_TYPE_SSA2_4 ||
             p_sys->i_type == SUB_TYPE_ASS )
464
    {
465
        es_format_Init( &fmt, SPU_ES, VLC_FOURCC( 's','s','a',' ' ) );
466
467
468
    }
    else
    {
469
        es_format_Init( &fmt, SPU_ES, VLC_FOURCC( 's','u','b','t' ) );
470
    }
Laurent Aimar's avatar
   
Laurent Aimar committed
471
    if( p_sys->psz_header != NULL )
472
    {
Laurent Aimar's avatar
   
Laurent Aimar committed
473
474
        fmt.i_extra = strlen( p_sys->psz_header ) + 1;
        fmt.p_extra = strdup( p_sys->psz_header );
475
    }
Laurent Aimar's avatar
   
Laurent Aimar committed
476
    p_sys->es = es_out_Add( p_demux->out, &fmt );
hartman's avatar
hartman committed
477

Laurent Aimar's avatar
Laurent Aimar committed
478
    return VLC_SUCCESS;
479
480
481
}

/*****************************************************************************
Laurent Aimar's avatar
   
Laurent Aimar committed
482
 * Close: Close subtitle demux
483
 *****************************************************************************/
Laurent Aimar's avatar
   
Laurent Aimar committed
484
static void Close( vlc_object_t *p_this )
485
{
Laurent Aimar's avatar
   
Laurent Aimar committed
486
487
488
    demux_t *p_demux = (demux_t*)p_this;
    demux_sys_t *p_sys = p_demux->p_sys;
    int i;
489

Laurent Aimar's avatar
   
Laurent Aimar committed
490
    for( i = 0; i < p_sys->i_subtitles; i++ )
491
492
        free( p_sys->subtitle[i].psz_text );
    free( p_sys->subtitle );
Laurent Aimar's avatar
   
Laurent Aimar committed
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529

    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++;
            }
530

Laurent Aimar's avatar
   
Laurent Aimar committed
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
            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:
572
        case DEMUX_GET_ATTACHMENTS:
Laurent Aimar's avatar
   
Laurent Aimar committed
573
        case DEMUX_GET_TITLE_INFO:
574
        case DEMUX_HAS_UNSUPPORTED_META:
Laurent Aimar's avatar
   
Laurent Aimar committed
575
576
577
            return VLC_EGENERIC;

        default:
578
            msg_Err( p_demux, "unknown query %d in subtitle control", i_query );
Laurent Aimar's avatar
   
Laurent Aimar committed
579
            return VLC_EGENERIC;
580
    }
Laurent Aimar's avatar
   
Laurent Aimar committed
581
582
583
584
585
586
587
588
589
590
591
592
593
}

/*****************************************************************************
 * 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
594
    i_maxdate = p_sys->i_next_demux_date - var_GetTime( p_demux->p_parent, "spu-delay" );;
Laurent Aimar's avatar
   
Laurent Aimar committed
595
    if( i_maxdate <= 0 && p_sys->i_subtitle < p_sys->i_subtitles )
596
    {
Laurent Aimar's avatar
   
Laurent Aimar committed
597
598
        /* Should not happen */
        i_maxdate = p_sys->subtitle[p_sys->i_subtitle].i_start + 1;
599
600
    }

hartman's avatar
hartman committed
601
602
    while( p_sys->i_subtitle < p_sys->i_subtitles &&
           p_sys->subtitle[p_sys->i_subtitle].i_start < i_maxdate )
603
    {
hartman's avatar
hartman committed
604
605
        block_t *p_block;
        int i_len = strlen( p_sys->subtitle[p_sys->i_subtitle].psz_text ) + 1;
606

hartman's avatar
hartman committed
607
608
609
        if( i_len <= 1 )
        {
            /* empty subtitle */
Laurent Aimar's avatar
   
Laurent Aimar committed
610
            p_sys->i_subtitle++;
hartman's avatar
hartman committed
611
            continue;
612
        }
613

hartman's avatar
hartman committed
614
615
616
617
618
        if( ( p_block = block_New( p_demux, i_len ) ) == NULL )
        {
            p_sys->i_subtitle++;
            continue;
        }
619

hartman's avatar
hartman committed
620
621
622
623
624
        if( p_sys->subtitle[p_sys->i_subtitle].i_start < 0 )
        {
            p_sys->i_subtitle++;
            continue;
        }
625

hartman's avatar
hartman committed
626
627
628
629
630
631
632
        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;
        }
633

hartman's avatar
hartman committed
634
635
636
637
638
        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 );
639
        }
hartman's avatar
hartman committed
640
641
642
643
644
        else
        {
            block_Release( p_block );
        }
        p_sys->i_subtitle++;
645
646
    }

Laurent Aimar's avatar
   
Laurent Aimar committed
647
648
    /* */
    p_sys->i_next_demux_date = 0;
649

Laurent Aimar's avatar
   
Laurent Aimar committed
650
    return 1;
651
}
652

653
/*****************************************************************************
Laurent Aimar's avatar
   
Laurent Aimar committed
654
 * Fix: fix time stamp and order of subtitle
655
 *****************************************************************************/
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
656
#ifdef USE_THIS_UNUSED_PIECE_OF_CODE
Laurent Aimar's avatar
   
Laurent Aimar committed
657
static void Fix( demux_t *p_demux )
658
{
Laurent Aimar's avatar
   
Laurent Aimar committed
659
    demux_sys_t *p_sys = p_demux->p_sys;
660
    bool b_done;
661
662
663
664
665
666
667
668
    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
    {
669
        b_done = true;
Laurent Aimar's avatar
   
Laurent Aimar committed
670
        for( i_index = 1; i_index < p_sys->i_subtitles; i_index++ )
671
        {
Laurent Aimar's avatar
   
Laurent Aimar committed
672
673
            if( p_sys->subtitle[i_index].i_start <
                    p_sys->subtitle[i_index - 1].i_start )
674
675
            {
                subtitle_t sub_xch;
Laurent Aimar's avatar
Laurent Aimar committed
676
                memcpy( &sub_xch,
Laurent Aimar's avatar
   
Laurent Aimar committed
677
                        p_sys->subtitle + i_index - 1,
678
                        sizeof( subtitle_t ) );
Laurent Aimar's avatar
   
Laurent Aimar committed
679
680
                memcpy( p_sys->subtitle + i_index - 1,
                        p_sys->subtitle + i_index,
681
                        sizeof( subtitle_t ) );
Laurent Aimar's avatar
   
Laurent Aimar committed
682
                memcpy( p_sys->subtitle + i_index,
683
684
                        &sub_xch,
                        sizeof( subtitle_t ) );
685
                b_done = false;
686
687
            }
        }
Laurent Aimar's avatar
   
Laurent Aimar committed
688
689
    } while( !b_done );
}
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
690
#endif
Laurent Aimar's avatar
   
Laurent Aimar committed
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713

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
714
            txt->line = realloc( txt->line, i_line_max * sizeof( char * ) );
Laurent Aimar's avatar
   
Laurent Aimar committed
715
716
717
718
719
720
721
722
723
724
        }
    }

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

    return VLC_SUCCESS;
725
}
Laurent Aimar's avatar
   
Laurent Aimar committed
726
727
728
static void TextUnload( text_t *txt )
{
    int i;
729

Laurent Aimar's avatar
   
Laurent Aimar committed
730
731
732
733
734
735
736
737
    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;
}
738

Laurent Aimar's avatar
   
Laurent Aimar committed
739
740
741
742
743
744
745
746
747
748
749
750
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--;
}
751
752

/*****************************************************************************
Laurent Aimar's avatar
Laurent Aimar committed
753
 * Specific Subtitle function
754
 *****************************************************************************/
755
756
757
758
759
/* ParseMicroDvd:
 *  Format:
 *      {n1}{n2}Line1|Line2|Line3....
 *  where n1 and n2 are the video frame number (n2 can be empty)
 */
760
761
static int ParseMicroDvd( demux_t *p_demux, subtitle_t *p_subtitle,
                          int i_idx )
762
{
763
    VLC_UNUSED( i_idx );
Laurent Aimar's avatar
   
Laurent Aimar committed
764
765
    demux_sys_t *p_sys = p_demux->p_sys;
    text_t      *txt = &p_sys->txt;
766
767
768
769
    char *psz_text;
    int  i_start;
    int  i_stop;
    int  i;
Laurent Aimar's avatar
   
Laurent Aimar committed
770

771
772
    for( ;; )
    {
773
774
775
776
777
778
779
780
        const char *s = TextGetLine( txt );
        if( !s )
            return VLC_EGENERIC;

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

781
782
        i_start = 0;
        i_stop  = 0;
783
784
        if( sscanf( s, "{%d}{}%[^\r\n]", &i_start, psz_text ) == 2 ||
            sscanf( s, "{%d}{%d}%[^\r\n]", &i_start, &i_stop, psz_text ) == 3)
785
        {
786
787
788
789
790
791
792
793
794
            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);
795
        }
796
        free( psz_text );
797
798
    }

799
    /* replace | by \n */
800
    for( i = 0; psz_text[i] != '\0'; i++ )
801
    {
802
803
        if( psz_text[i] == '|' )
            psz_text[i] = '\n';
804
    }
Laurent Aimar's avatar
   
Laurent Aimar committed
805

806
807
808
809
810
    /* */
    p_subtitle->i_start  = i_start * p_sys->i_microsecperframe;
    p_subtitle->i_stop   = i_stop  * p_sys->i_microsecperframe;
    p_subtitle->psz_text = psz_text;
    return VLC_SUCCESS;
811
812
}

813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
/* 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,
                                 const char *psz_fmt,
831
                                 bool b_replace_br )
832
{
Laurent Aimar's avatar
   
Laurent Aimar committed
833
834
    demux_sys_t *p_sys = p_demux->p_sys;
    text_t      *txt = &p_sys->txt;
835
    char    *psz_text;
836

837
838
    for( ;; )
    {
839
        const char *s = TextGetLine( txt );
840
        int h1, m1, s1, d1, h2, m2, s2, d2;
841
842
843
844
845

        if( !s )
            return VLC_EGENERIC;

        if( sscanf( s, psz_fmt,
846
847
848
                    &h1, &m1, &s1, &d1,
                    &h2, &m2, &s2, &d2 ) == 8 )
        {
849
850
851
852
853
854
855
856
857
858
            p_subtitle->i_start = ( (int64_t)h1 * 3600*1000 +
                                    (int64_t)m1 * 60*1000 +
                                    (int64_t)s1 * 1000 +
                                    (int64_t)d1 ) * 1000;

            p_subtitle->i_stop  = ( (int64_t)h2 * 3600*1000 +
                                    (int64_t)m2 * 60*1000 +
                                    (int64_t)s2 * 1000 +
                                    (int64_t)d2 ) * 1000;
            break;
859
860
        }
    }
861

862
863
864
865
    /* Now read text until an empty line */
    psz_text = strdup("");
    if( !psz_text )
        return VLC_ENOMEM;
866
867
    for( ;; )
    {
868
869
870
871
872
        const char *s = TextGetLine( txt );
        int i_len;
        int i_old;

        if( !s )
873
        {
874
875
            free( psz_text );
            return VLC_EGENERIC;
876
        }
877
878
879

        i_len = strlen( s );
        if( i_len <= 0 )
880
        {
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
            p_subtitle->psz_text = psz_text;
            return VLC_SUCCESS;
        }

        i_old = strlen( psz_text );
        psz_text = realloc( psz_text, i_old + i_len + 1 + 1 );
        if( !psz_text )
            return VLC_ENOMEM;
        strcat( psz_text, s );
        strcat( psz_text, "\n" );

        /* replace [br] by \n */
        if( b_replace_br )
        {
            char *p;
896
 
897
            while( ( p = strstr( psz_text, "[br]" ) ) )
898
            {
899
900
                *p++ = '\n';
                memmove( p, &p[3], strlen(&p[3])+1 );
901
902
903
904
            }
        }
    }
}
905
906
/* ParseSubRip
 */
907
908
static int  ParseSubRip( demux_t *p_demux, subtitle_t *p_subtitle,
                         int i_idx )
909
{
910
    VLC_UNUSED( i_idx );
911
912
    return ParseSubRipSubViewer( p_demux, p_subtitle,
                                 "%d:%d:%d,%d --> %d:%d:%d,%d",
913
                                 false );
914
915
916
}
/* ParseSubViewer
 */
917
918
static int  ParseSubViewer( demux_t *p_demux, subtitle_t *p_subtitle,
                            int i_idx )
919
{
920
921
    VLC_UNUSED( i_idx );

922
923
    return ParseSubRipSubViewer( p_demux, p_subtitle,
                                 "%d:%d:%d.%d,%d:%d:%d.%d",
924
                                 true );
925
}
926

927
928
/* ParseSSA
 */
929
930
static int  ParseSSA( demux_t *p_demux, subtitle_t *p_subtitle,
                      int i_idx )
931
{
Laurent Aimar's avatar
   
Laurent Aimar committed
932
933
934
    demux_sys_t *p_sys = p_demux->p_sys;
    text_t      *txt = &p_sys->txt;

935
936
    for( ;; )
    {
937
        const char *s = TextGetLine( txt );
938
        int h1, m1, s1, c1, h2, m2, s2, c2;
939
        char *psz_text;
940
        char temp[16];
941

942
943
        if( !s )
            return VLC_EGENERIC;
944

945
946
947
948
949
950
951
952
953
954
955
        /* 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 ?
         */
956
957
958

        /* The output text is - at least, not removing numbers - 18 chars shorter than the input text. */
        psz_text = malloc( strlen(s) );
959
960
961
        if( !psz_text )
            return VLC_ENOMEM;