subtitle.c 53.4 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

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
34
#include <vlc/vlc.h>
35
#include <vlc_plugin.h>
Clément Stenac's avatar
Clément Stenac 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

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

Gildas Bazin's avatar
 
Gildas Bazin 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).")
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"));
Clément Stenac's avatar
Clément Stenac committed
73
    set_description( _("Text subtitles parser") );
74
    set_capability( "demux", 0 );
Clément Stenac's avatar
Clément Stenac committed
75 76
    set_category( CAT_INPUT );
    set_subcategory( SUBCAT_INPUT_DEMUX );
77
    add_float( "sub-fps", 0.0, NULL,
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
78
               N_("Frames per second"),
79
               SUB_FPS_LONGTEXT, true );
Clément Stenac's avatar
Clément Stenac committed
80 81
    add_integer( "sub-delay", 0, NULL,
               N_("Subtitles delay"),
82
               SUB_DELAY_LONGTEXT, true );
Clément Stenac's avatar
Clément Stenac committed
83
    add_string( "sub-type", "auto", NULL, N_("Subtitles format"),
84
                SUB_TYPE_LONGTEXT, true );
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
{
168
    const char *psz_type_name;
169
    int  i_type;
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;
    }
Gildas Bazin's avatar
 
Gildas Bazin 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;
Derk-Jan Hartman's avatar
Derk-Jan 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 );
Clément Stenac's avatar
Clément Stenac committed
241

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 );
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
247
        msg_Dbg( p_demux, "Override subtitle fps %f", f_fps );
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
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,
Gildas Bazin's avatar
 
Gildas Bazin 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 );
Gildas Bazin's avatar
 
Gildas Bazin 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 *** */
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 );
Derk-Jan Hartman's avatar
Derk-Jan 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;

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

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

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

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

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

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

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
        }
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;
Derk-Jan Hartman's avatar
Derk-Jan 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