subtitle.c 59.9 KB
Newer Older
1
/*****************************************************************************
2
 * subtitle.c: Demux for subtitle text files.
3
 *****************************************************************************
Antoine Cellerier's avatar
Antoine Cellerier committed
4
 * Copyright (C) 1999-2007 the VideoLAN team
5
 * $Id$
6 7
 *
 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
Derk-Jan Hartman's avatar
Derk-Jan 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
Antoine Cellerier's avatar
Antoine Cellerier 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

34
#include <vlc_common.h>
35
#include <vlc_plugin.h>
Clément Stenac's avatar
Clément Stenac 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

Clément Stenac's avatar
Clément Stenac 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 );

Gildas Bazin's avatar
 
Gildas Bazin 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 65
    "aqt", "pjs", "mpsub", "jacosub", "psb", "realtext", "dks",
    "subviewer1"
Laurent Aimar's avatar
 
Laurent Aimar committed
66
};
67

68 69 70 71 72 73
vlc_module_begin ()
    set_shortname( N_("Subtitles"))
    set_description( N_("Text subtitles parser") )
    set_capability( "demux", 0 )
    set_category( CAT_INPUT )
    set_subcategory( SUBCAT_INPUT_DEMUX )
74
    add_float( "sub-fps", 0.0,
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
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,
Clément Stenac's avatar
Clément Stenac committed
78
               N_("Subtitles delay"),
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
79
               SUB_DELAY_LONGTEXT, true )
80
    add_string( "sub-type", "auto", N_("Subtitles 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, NULL )
83
    add_string( "sub-description", NULL, N_("Subtitles 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
    SUB_TYPE_UNKNOWN = -1,
    SUB_TYPE_MICRODVD,
    SUB_TYPE_SUBRIP,
98
    SUB_TYPE_SUBRIP_DOT, /* Invalid SubRip file (dot instead of comma) */
Laurent Aimar's avatar
 
Laurent Aimar committed
99 100
    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 105
    SUB_TYPE_SUBVIEWER, /* SUBVIEWER 2 */
    SUB_TYPE_DVDSUBTITLE, /* Mplayer calls it subviewer2 */
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
    SUB_TYPE_PSB,
112
    SUB_TYPE_RT,
113 114 115
    SUB_TYPE_DKS,
    SUB_TYPE_SUBVIEW1 /* SUBVIEWER 1 - mplayer calls it subrip09,
                         and Gnome subtitles SubViewer 1.0 */
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
static int  ParseMicroDvd   ( demux_t *, subtitle_t *, int );
static int  ParseSubRip     ( demux_t *, subtitle_t *, int );
173
static int  ParseSubRipDot  ( demux_t *, subtitle_t *, int );
174 175 176 177 178 179
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 );
180
static int  ParseAQT        ( demux_t *, subtitle_t *, int );
181
static int  ParsePJS        ( demux_t *, subtitle_t *, int );
182 183
static int  ParseMPSub      ( demux_t *, subtitle_t *, int );
static int  ParseJSS        ( demux_t *, subtitle_t *, int );
184
static int  ParsePSB        ( demux_t *, subtitle_t *, int );
185
static int  ParseRealText   ( demux_t *, subtitle_t *, int );
186
static int  ParseDKS        ( demux_t *, subtitle_t *, int );
187
static int  ParseSubViewer1 ( demux_t *, subtitle_t *, int );
188

189
static const struct
190
{
191
    const char *psz_type_name;
192
    int  i_type;
193
    const char *psz_name;
194
    int  (*pf_read)( demux_t *, subtitle_t*, int );
195 196
} sub_read_subtitle_function [] =
{
197 198
    { "microdvd",   SUB_TYPE_MICRODVD,    "MicroDVD",    ParseMicroDvd },
    { "subrip",     SUB_TYPE_SUBRIP,      "SubRIP",      ParseSubRip },
199
    { "subrip-dot", SUB_TYPE_SUBRIP_DOT,  "SubRIP(Dot)", ParseSubRipDot },
200 201 202 203 204 205 206
    { "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 },
207
    { "mpl2",       SUB_TYPE_MPL2,        "MPL2",        ParseMPL2 },
208
    { "aqt",        SUB_TYPE_AQT,         "AQTitle",     ParseAQT },
209
    { "pjs",        SUB_TYPE_PJS,         "PhoenixSub",  ParsePJS },
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
210
    { "mpsub",      SUB_TYPE_MPSUB,       "MPSub",       ParseMPSub },
211
    { "jacosub",    SUB_TYPE_JACOSUB,     "JacoSub",     ParseJSS },
212
    { "psb",        SUB_TYPE_PSB,         "PowerDivx",   ParsePSB },
213
    { "realtext",   SUB_TYPE_RT,          "RealText",    ParseRealText },
214
    { "dks",        SUB_TYPE_DKS,         "DKS",         ParseDKS },
215
    { "subviewer1", SUB_TYPE_SUBVIEW1,    "Subviewer 1", ParseSubViewer1 },
216
    { NULL,         SUB_TYPE_UNKNOWN,     "Unknown",     NULL }
217
};
218 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 * );
Laurent Aimar's avatar
 
Laurent Aimar committed
226

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

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

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

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

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

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

266
    msg_Dbg( p_demux, "Movie fps: %f", f_fps );
Clément Stenac's avatar
Clément Stenac committed
267

268 269 270 271 272
    /* 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 );
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
273
        msg_Dbg( p_demux, "Override subtitle fps %f", f_fps );
274 275
    }

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

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

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

298 299 300 301 302 303 304 305 306 307 308
    /* 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
309 310
    /* Probe if unknown type */
    if( p_sys->i_type == SUB_TYPE_UNKNOWN )
311
    {
312
        int     i_try;
313
        char    *s = NULL;
314

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

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

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

            free( s );
            s = NULL;
450
        }
451

452
        free( s );
453

Laurent Aimar's avatar
 
Laurent Aimar committed
454
        /* It will nearly always work even for non seekable stream thanks the
455
         * caching system, and if it fails we lose just a few sub */
456
        if( stream_Seek( p_demux->s, unicode ? 3 : 0 ) )
Laurent Aimar's avatar
 
Laurent Aimar committed
457 458
            msg_Warn( p_demux, "failed to rewind" );
    }
459 460

    /* Quit on unknown subtitles */
Laurent Aimar's avatar
 
Laurent Aimar committed
461 462
    if( p_sys->i_type == SUB_TYPE_UNKNOWN )
    {
463
        stream_Seek( p_demux->s, 0 );
464
        msg_Warn( p_demux, "failed to recognize subtitle type" );
465
        free( p_sys );
Laurent Aimar's avatar
 
Laurent Aimar committed
466
        return VLC_EGENERIC;
467 468
    }

469
    for( i = 0; ; i++ )
470
    {
Laurent Aimar's avatar
 
Laurent Aimar committed
471
        if( sub_read_subtitle_function[i].i_type == p_sys->i_type )
472
        {
Laurent Aimar's avatar
 
Laurent Aimar committed
473 474 475
            msg_Dbg( p_demux, "detected %s format",
                     sub_read_subtitle_function[i].psz_name );
            pf_read = sub_read_subtitle_function[i].pf_read;
476 477
            break;
        }
478
    }
479

Laurent Aimar's avatar
 
Laurent Aimar committed
480 481 482 483 484 485
    msg_Dbg( p_demux, "loading all subtitles..." );

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

    /* Parse it */
486
    for( i_max = 0;; )
Laurent Aimar's avatar
Laurent Aimar committed
487
    {
Laurent Aimar's avatar
 
Laurent Aimar committed
488
        if( p_sys->i_subtitles >= i_max )
489
        {
Laurent Aimar's avatar
 
Laurent Aimar committed
490
            i_max += 500;
491
            if( !( p_sys->subtitle = realloc_or_free( p_sys->subtitle,
Gildas Bazin's avatar
 
Gildas Bazin committed
492
                                              sizeof(subtitle_t) * i_max ) ) )
493
            {
494 495
                TextUnload( &p_sys->txt );
                free( p_sys );
Gildas Bazin's avatar
 
Gildas Bazin committed
496
                return VLC_ENOMEM;
497 498
            }
        }
Laurent Aimar's avatar
 
Laurent Aimar committed
499

500 501
        if( pf_read( p_demux, &p_sys->subtitle[p_sys->i_subtitles],
                     p_sys->i_subtitles ) )
502
            break;
Laurent Aimar's avatar
 
Laurent Aimar committed
503 504

        p_sys->i_subtitles++;
505
    }
Laurent Aimar's avatar
 
Laurent Aimar committed
506 507
    /* Unload */
    TextUnload( &p_sys->txt );
508

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

Laurent Aimar's avatar
 
Laurent Aimar committed
511 512 513 514
    /* Fix subtitle (order and time) *** */
    p_sys->i_subtitle = 0;
    p_sys->i_length = 0;
    if( p_sys->i_subtitles > 0 )
515
    {
Laurent Aimar's avatar
 
Laurent Aimar committed
516 517 518 519
        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;
520
    }
Laurent Aimar's avatar
Laurent Aimar committed
521

522
    /* *** add subtitle ES *** */
523
    if( p_sys->i_type == SUB_TYPE_SSA1 ||
524 525
             p_sys->i_type == SUB_TYPE_SSA2_4 ||
             p_sys->i_type == SUB_TYPE_ASS )
526
    {
527
        Fix( p_demux );
528
        es_format_Init( &fmt, SPU_ES, VLC_CODEC_SSA );
529 530
    }
    else
531
        es_format_Init( &fmt, SPU_ES, VLC_CODEC_SUBT );
532 533
    if( unicode )
        fmt.subs.psz_encoding = strdup( "UTF-8" );
534 535 536 537 538
    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
539
    if( p_sys->psz_header != NULL )
540
    {
Laurent Aimar's avatar
 
Laurent Aimar committed
541 542
        fmt.i_extra = strlen( p_sys->psz_header ) + 1;
        fmt.p_extra = strdup( p_sys->psz_header );
543
    }
Laurent Aimar's avatar
 
Laurent Aimar committed
544
    p_sys->es = es_out_Add( p_demux->out, &fmt );
545
    es_format_Clean( &fmt );
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
546

Laurent Aimar's avatar
Laurent Aimar committed
547
    return VLC_SUCCESS;
548 549 550
}

/*****************************************************************************
Laurent Aimar's avatar
 
Laurent Aimar committed
551
 * Close: Close subtitle demux
552
 *****************************************************************************/
Laurent Aimar's avatar
 
Laurent Aimar committed
553
static void Close( vlc_object_t *p_this )
554
{
Laurent Aimar's avatar
 
Laurent Aimar committed
555 556 557
    demux_t *p_demux = (demux_t*)p_this;
    demux_sys_t *p_sys = p_demux->p_sys;
    int i;
558

Laurent Aimar's avatar
 
Laurent Aimar committed
559
    for( i = 0; i < p_sys->i_subtitles; i++ )
560 561
        free( p_sys->subtitle[i].psz_text );
    free( p_sys->subtitle );
Laurent Aimar's avatar
 
Laurent Aimar committed
562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593

    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;
594
            while( p_sys->i_subtitle < p_sys->i_subtitles )
Laurent Aimar's avatar
 
Laurent Aimar committed
595
            {
596 597 598 599 600 601 602
                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
603 604
                p_sys->i_subtitle++;
            }
605

Laurent Aimar's avatar
 
Laurent Aimar committed
606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644
            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;

645
        case DEMUX_GET_PTS_DELAY:
Laurent Aimar's avatar
 
Laurent Aimar committed
646 647
        case DEMUX_GET_FPS:
        case DEMUX_GET_META:
648
        case DEMUX_GET_ATTACHMENTS:
Laurent Aimar's avatar
 
Laurent Aimar committed
649
        case DEMUX_GET_TITLE_INFO:
650
        case DEMUX_HAS_UNSUPPORTED_META:
Laurent Aimar's avatar
Laurent Aimar committed
651
        case DEMUX_CAN_RECORD:
Laurent Aimar's avatar
 
Laurent Aimar committed
652 653 654
            return VLC_EGENERIC;

        default:
655
            msg_Err( p_demux, "unknown query %d in subtitle control", i_query );
Laurent Aimar's avatar
 
Laurent Aimar committed
656
            return VLC_EGENERIC;
657
    }
Laurent Aimar's avatar
 
Laurent Aimar committed
658 659 660 661 662 663 664 665 666 667 668 669 670
}

/*****************************************************************************
 * 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;

Clément Stenac's avatar
Clément Stenac committed
671
    i_maxdate = p_sys->i_next_demux_date - var_GetTime( p_demux->p_parent, "spu-delay" );;
Laurent Aimar's avatar
 
Laurent Aimar committed
672
    if( i_maxdate <= 0 && p_sys->i_subtitle < p_sys->i_subtitles )
673
    {
Laurent Aimar's avatar
 
Laurent Aimar committed
674 675
        /* Should not happen */
        i_maxdate = p_sys->subtitle[p_sys->i_subtitle].i_start + 1;
676 677
    }

678 679
    while( p_sys->i_subtitle < p_sys->i_subtitles &&
           p_sys->subtitle[p_sys->i_subtitle].i_start < i_maxdate )
680
    {
681 682
        const subtitle_t *p_subtitle = &p_sys->subtitle[p_sys->i_subtitle];

683
        block_t *p_block;
684
        int i_len = strlen( p_subtitle->psz_text ) + 1;
685

686
        if( i_len <= 1 || p_subtitle->i_start < 0 )
687
        {
Laurent Aimar's avatar
 
Laurent Aimar committed
688
            p_sys->i_subtitle++;
689
            continue;
690
        }
691

692 693 694 695 696
        if( ( p_block = block_New( p_demux, i_len ) ) == NULL )
        {
            p_sys->i_subtitle++;
            continue;
        }
697

698
        p_block->i_dts =
699 700
        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 )
701
            p_block->i_length = p_subtitle->i_stop - p_subtitle->i_start;
702

703 704 705
        memcpy( p_block->p_buffer, p_subtitle->psz_text, i_len );

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

707
        p_sys->i_subtitle++;
708 709
    }

Laurent Aimar's avatar
 
Laurent Aimar committed
710 711
    /* */
    p_sys->i_next_demux_date = 0;
712

Laurent Aimar's avatar
 
Laurent Aimar committed
713
    return 1;
714
}
715

716
/*****************************************************************************
Laurent Aimar's avatar
 
Laurent Aimar committed
717
 * Fix: fix time stamp and order of subtitle
718
 *****************************************************************************/
Laurent Aimar's avatar
 
Laurent Aimar committed
719
static void Fix( demux_t *p_demux )
720
{
Laurent Aimar's avatar
 
Laurent Aimar committed
721
    demux_sys_t *p_sys = p_demux->p_sys;
722
    bool b_done;
723 724 725

    /* *** 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
726
     * would not take too much time
727 728 729
     */
    do
    {
730
        b_done = true;
731
        for( int i_index = 1; i_index < p_sys->i_subtitles; i_index++ )
732
        {
Laurent Aimar's avatar
 
Laurent Aimar committed
733
            if( p_sys->subtitle[i_index].i_start <
734
                p_sys->subtitle[i_index - 1].i_start )
735 736
            {
                subtitle_t sub_xch;
Laurent Aimar's avatar
Laurent Aimar committed
737
                memcpy( &sub_xch,
Laurent Aimar's avatar
 
Laurent Aimar committed
738
                        p_sys->subtitle + i_index - 1,
739
                        sizeof( subtitle_t ) );
Laurent Aimar's avatar
 
Laurent Aimar committed
740 741
                memcpy( p_sys->subtitle + i_index - 1,
                        p_sys->subtitle + i_index,
742
                        sizeof( subtitle_t ) );
Laurent Aimar's avatar
 
Laurent Aimar committed
743
                memcpy( p_sys->subtitle + i_index,
744 745
                        &sub_xch,
                        sizeof( subtitle_t ) );
746
                b_done = false;
747 748
            }
        }
Laurent Aimar's avatar
 
Laurent Aimar committed
749 750 751 752 753 754 755 756 757 758 759 760
    } 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 * ) );
761 762
    if( !txt->line )
        return VLC_ENOMEM;
Laurent Aimar's avatar
 
Laurent Aimar committed
763 764 765 766 767 768 769 770 771 772 773 774 775

    /* 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;
776 777 778
            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
779 780 781 782 783 784 785 786 787 788
        }
    }

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

    return VLC_SUCCESS;
789
}
Laurent Aimar's avatar
 
Laurent Aimar committed
790 791 792
static void TextUnload( text_t *txt )
{
    int i;
793

Laurent Aimar's avatar
 
Laurent Aimar committed
794 795 796 797 798 799 800 801
    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;
}
802

Laurent Aimar's avatar
 
Laurent Aimar committed
803 804 805 806 807 808 809 810 811 812 813 814
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--;
}
815 816

/*****************************************************************************
Laurent Aimar's avatar
Laurent Aimar committed
817
 * Specific Subtitle function