sub.c 34.3 KB
Newer Older
1
/*****************************************************************************
hartman's avatar
hartman committed
2
 * sub.c: subtitle demux for external subtitle files
3
 *****************************************************************************
hartman's avatar
hartman committed
4
 * Copyright (C) 1999-2004 VideoLAN
5
 * $Id: sub.c,v 1.49 2004/01/27 13:10:29 fenrir Exp $
6
7
 *
 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
hartman's avatar
hartman committed
8
 *          Derk-Jan Hartman <hartman at videolan dot org>
9
10
11
12
13
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
Laurent Aimar's avatar
Laurent Aimar committed
14
 *
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
 * 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
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
31
#include <ctype.h>
32
33
34

#include <vlc/vlc.h>
#include <vlc/input.h>
Sam Hocevar's avatar
Sam Hocevar committed
35
#include "vlc_video.h"
36
#include <codecs.h>
37
38
39

#include "sub.h"

40
41
42
#if (!defined( WIN32 ) || defined(__MINGW32__))
#    include <dirent.h>
#endif
43

44
45
#define DVD_VIDEO_LB_LEN 2048

46
47
static int  Open ( vlc_object_t *p_this );

Laurent Aimar's avatar
Laurent Aimar committed
48
static int  sub_open ( subtitle_demux_t *p_sub,
49
50
                       input_thread_t  *p_input,
                       char  *psz_name,
hartman's avatar
hartman committed
51
                       mtime_t i_microsecperframe );
52
53
54
55
56
57
static int  sub_demux( subtitle_demux_t *p_sub, mtime_t i_maxdate );
static int  sub_seek ( subtitle_demux_t *p_sub, mtime_t i_date );
static void sub_close( subtitle_demux_t *p_sub );

static void sub_fix( subtitle_demux_t *p_sub );

58
59
static char *ppsz_sub_type[] = { "auto", "microdvd", "subrip", "ssa1",
  "ssa2-4", "vplayer", "sami", "vobsub" };
60

61
62
63
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
gbazin's avatar
   
gbazin committed
64
65
#define SUB_DELAY_LONGTEXT \
    "Delay subtitles (in 1/10s)"
66
#define SUB_FPS_LONGTEXT \
67
    "Override frames per second. " \
gbazin's avatar
   
gbazin committed
68
    "It will only work with MicroDVD subtitles."
69
#define SUB_TYPE_LONGTEXT \
gbazin's avatar
   
gbazin committed
70
    "One from \"microdvd\", \"subrip\", \"ssa1\", \"ssa2-4\", \"vplayer\" " \
71
    "\"sami\" (auto for autodetection, it should always work)."
72
73

vlc_module_begin();
gbazin's avatar
   
gbazin committed
74
    set_description( _("Text subtitles demux") );
75
    set_capability( "subtitle demux", 12 );
hartman's avatar
hartman committed
76
77
78
79
80
81
82
83
84
    add_float( "sub-fps", 25.0, NULL,
               N_("Frames per second"),
               SUB_FPS_LONGTEXT, VLC_TRUE );
    add_integer( "sub-delay", 0, NULL,
                 N_("Delay subtitles (in 1/10s)"),
                 SUB_DELAY_LONGTEXT, VLC_TRUE );
    add_string( "sub-type", "auto", NULL, "Subtitles fileformat",
                SUB_TYPE_LONGTEXT, VLC_TRUE );
        change_string_list( ppsz_sub_type, 0, 0 );
85
86
87
88
89
90
91
92
93
94
95
96
97
98
    set_callbacks( Open, NULL );
vlc_module_end();

/*****************************************************************************
 * Module initializer
 *****************************************************************************/
static int Open ( vlc_object_t *p_this )
{
    subtitle_demux_t *p_sub = (subtitle_demux_t*)p_this;

    p_sub->pf_open  = sub_open;
    p_sub->pf_demux = sub_demux;
    p_sub->pf_seek  = sub_seek;
    p_sub->pf_close = sub_close;
99
    
100
    /* Initialize the variables */
gbazin's avatar
   
gbazin committed
101
102
103
    var_Create( p_this, "sub-fps", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT );
    var_Create( p_this, "sub-delay", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
    var_Create( p_this, "sub-type", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
104

105
106
107
108
    return VLC_SUCCESS;
}
#define MAX_TRY     256
#define MAX_LINE    2048
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195

#define FREE( p ) if( p ) { free( p); (p) = NULL; }

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

static int  text_load( text_t *txt, char *psz_name )
{
    FILE *f;
    int   i_line_max;

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

    /* open file */
    if( !( f = fopen( psz_name, "rb" ) ) )
    {
        return VLC_EGENERIC;
    }

    /* load the complete file */
    for( ;; )
    {
        char buffer[8096];
        char *p;

        if( fgets( buffer, 8096, f ) <= 0)
        {
            break;
        }
        while( ( p = strchr( buffer, '\r' ) ) )
        {
            *p = '\0';
        }
        while( ( p = strchr( buffer, '\n' ) ) )
        {
            *p = '\0';
        }

        txt->line[txt->i_line_count++] = strdup( buffer );

        if( txt->i_line_count >= i_line_max )
        {
            i_line_max += 100;
            txt->line = realloc( txt->line, i_line_max * sizeof( char*) );
        }
    }

    fclose( f );

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

    return( VLC_SUCCESS );
}
static void text_unload( text_t *txt )
{
    int i;

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

static char *text_get_line( text_t *txt )
{
    if( txt->i_line >= txt->i_line_count )
    {
        return( NULL );
    }

    return( txt->line[txt->i_line++] );
}
196
197
198
199
200
201
202
static void text_previous_line( text_t *txt )
{
    if( txt->i_line > 0 )
    {
        txt->i_line--;
    }
}
203
204
205
206
207
static void text_rewind( text_t *txt )
{
    txt->i_line = 0;
}

208
209
210
211
212
213
static int  sub_MicroDvdRead( subtitle_demux_t *p_sub, text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe );
static int  sub_SubRipRead  ( subtitle_demux_t *p_sub, text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe );
static int  sub_SSARead     ( subtitle_demux_t *p_sub, text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe );
static int  sub_Vplayer     ( subtitle_demux_t *p_sub, text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe );
static int  sub_Sami        ( subtitle_demux_t *p_sub, text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe );
static int  sub_VobSub      ( subtitle_demux_t *p_sub, text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe );
214

215
static int  DemuxVobSub     ( subtitle_demux_t *, block_t * );
216

217
218
219
220
221
static struct
{
    char *psz_type_name;
    int  i_type;
    char *psz_name;
222
    int  (*pf_read_subtitle)    ( subtitle_demux_t *, text_t *, subtitle_t*, mtime_t );
223
224
225
226
} sub_read_subtitle_function [] =
{
    { "microdvd",   SUB_TYPE_MICRODVD,  "MicroDVD", sub_MicroDvdRead },
    { "subrip",     SUB_TYPE_SUBRIP,    "SubRIP",   sub_SubRipRead },
227
228
    { "ssa1",       SUB_TYPE_SSA1,      "SSA-1",    sub_SSARead },
    { "ssa2-4",     SUB_TYPE_SSA2_4,    "SSA-2/3/4",sub_SSARead },
229
230
    { "vplayer",    SUB_TYPE_VPLAYER,   "VPlayer",  sub_Vplayer },
    { "sami",       SUB_TYPE_SAMI,      "SAMI",     sub_Sami },
231
232
    { "vobsub",     SUB_TYPE_VOBSUB,    "VobSub",   sub_VobSub },
    { NULL,         SUB_TYPE_UNKNOWN,   "Unknown",  NULL }
233
234
};

235
236
237
static char * local_stristr( char *psz_big, char *psz_little)
{
    char *p_pos = psz_big;
Eric Petit's avatar
Eric Petit committed
238

239
240
241
242
    if (!psz_big || !psz_little || !*psz_little) return psz_big;

    while (*p_pos)
    {
243
        if (toupper(*p_pos) == toupper(*psz_little))
244
        {
245
246
247
            char * psz_cur1 = p_pos + 1;
            char * psz_cur2 = psz_little + 1;
            while (*psz_cur1 && *psz_cur2 && toupper(*psz_cur1) == toupper(*psz_cur2))
248
            {
249
250
251
252
253
254
                psz_cur1++;
                psz_cur2++;
            }
            if (!*psz_cur2) return p_pos;
        }
        p_pos++;
255
256
257
258
    }
    return NULL;
}

259
260
261
/*****************************************************************************
 * sub_open: Open a subtitle file and add subtitle ES
 *****************************************************************************/
gbazin's avatar
   
gbazin committed
262
static int sub_open( subtitle_demux_t *p_sub, input_thread_t  *p_input,
hartman's avatar
hartman committed
263
                     char *psz_name, mtime_t i_microsecperframe )
264
{
265
    text_t  txt;
266
    vlc_value_t val;
gbazin's avatar
   
gbazin committed
267
268
269
270
    es_format_t fmt;
    int i, i_sub_type, i_max;
    int (*pf_read_subtitle)( subtitle_demux_t *, text_t *, subtitle_t *,
                             mtime_t ) = NULL;
Laurent Aimar's avatar
Laurent Aimar committed
271

272
273
274
275
    p_sub->i_sub_type = SUB_TYPE_UNKNOWN;
    p_sub->p_es = NULL;
    p_sub->i_subtitles = 0;
    p_sub->subtitle = NULL;
gbazin's avatar
   
gbazin committed
276
    p_sub->p_vobsub_file = 0;
277
278
    p_sub->p_input = p_input;

gbazin's avatar
   
gbazin committed
279
    if( !psz_name || !*psz_name )
280
    {
gbazin's avatar
   
gbazin committed
281
        msg_Err( p_sub, "no subtitle file specified" );
282
        return VLC_EGENERIC;
283
    }
Eric Petit's avatar
Eric Petit committed
284

285
286
287
288
289
290
    /* *** load the file *** */
    if( text_load( &txt, psz_name ) )
    {
        msg_Err( p_sub, "cannot open `%s' subtitle file", psz_name );
        return VLC_EGENERIC;
    }
gbazin's avatar
   
gbazin committed
291

292
293
    msg_Dbg( p_sub, "opened `%s'", psz_name );

294
295
    var_Get( p_sub, "sub-fps", &val );
    if( val.i_int >= 1.0 )
296
    {
297
        i_microsecperframe = (mtime_t)( (float)1000000 / val.f_float );
298
    }
299
    else if( val.f_float <= 0 )
300
301
302
303
    {
        i_microsecperframe = 40000; /* default: 25fps */
    }

304
305
    var_Get( p_sub, "sub-type", &val);
    if( val.psz_string && *val.psz_string )
306
    {
307
308
309
        int i;

        for( i = 0; ; i++ )
310
        {
311
312
313
314
315
316
            if( sub_read_subtitle_function[i].psz_type_name == NULL )
            {
                i_sub_type = SUB_TYPE_UNKNOWN;
                break;
            }
            if( !strcmp( sub_read_subtitle_function[i].psz_type_name,
317
                         val.psz_string ) )
318
319
320
321
            {
                i_sub_type = sub_read_subtitle_function[i].i_type;
                break;
            }
322
323
324
325
326
327
        }
    }
    else
    {
        i_sub_type = SUB_TYPE_UNKNOWN;
    }
328
    FREE( val.psz_string );
Laurent Aimar's avatar
Laurent Aimar committed
329

330
331
332
    /* *** Now try to autodetect subtitle format *** */
    if( i_sub_type == SUB_TYPE_UNKNOWN )
    {
333
334
335
        int     i_try;
        char    *s;

336
337
338
339
        msg_Dbg( p_input, "trying to autodetect file format" );
        for( i_try = 0; i_try < MAX_TRY; i_try++ )
        {
            int i_dummy;
340
341

            if( ( s = text_get_line( &txt ) ) == NULL )
342
343
344
345
            {
                break;
            }

346
            if( local_stristr( s, "<SAMI>" ) )
347
348
349
350
351
352
            {
                i_sub_type = SUB_TYPE_SAMI;
                break;
            }
            else if( sscanf( s, "{%d}{%d}", &i_dummy, &i_dummy ) == 2 ||
                     sscanf( s, "{%d}{}", &i_dummy ) == 1)
353
354
355
356
            {
                i_sub_type = SUB_TYPE_MICRODVD;
                break;
            }
357
            else if( sscanf( s,
358
                             "%d:%d:%d,%d --> %d:%d:%d,%d",
Laurent Aimar's avatar
Laurent Aimar committed
359
                             &i_dummy,&i_dummy,&i_dummy,&i_dummy,
360
361
362
363
364
                             &i_dummy,&i_dummy,&i_dummy,&i_dummy ) == 8 )
            {
                i_sub_type = SUB_TYPE_SUBRIP;
                break;
            }
365
            else if( sscanf( s,
366
367
368
369
370
371
372
373
374
                             "!: This is a Sub Station Alpha v%d.x script.",
                             &i_dummy ) == 1)
            {
                if( i_dummy <= 1 )
                {
                    i_sub_type = SUB_TYPE_SSA1;
                }
                else
                {
375
                    i_sub_type = SUB_TYPE_SSA2_4; /* I hope this will work */
376
                }
377
                break;
378
            }
379
            else if( local_stristr( s, "This is a Sub Station Alpha v4 script" ) )
380
            {
381
382
                i_sub_type = SUB_TYPE_SSA2_4; /* I hope this will work */
                break;
383
            }
384
            else if( !strncasecmp( s, "Dialogue: Marked", 16  ) )
385
            {
386
                i_sub_type = SUB_TYPE_SSA2_4; /* could be wrong */
387
388
                break;
            }
389
390
            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 )
391
392
393
            {
                i_sub_type = SUB_TYPE_VPLAYER;
                break;
394
            }
395
396
397
398
399
            else if( local_stristr( s, "# VobSub index file" ) )
            {
                i_sub_type = SUB_TYPE_VOBSUB;
                break;
            }
400
        }
401
402

        text_rewind( &txt );
403
404
405
    }

    /* *** Load this file in memory *** */
406
    for( i = 0; ; i++ )
407
    {
408
409
        if( sub_read_subtitle_function[i].i_type == SUB_TYPE_UNKNOWN )
        {
gbazin's avatar
   
gbazin committed
410
            msg_Dbg( p_input, "unknown subtitle file" );
411
            text_unload( &txt );
Laurent Aimar's avatar
Laurent Aimar committed
412
            return VLC_EGENERIC;
413
        }
Laurent Aimar's avatar
Laurent Aimar committed
414

415
416
        if( sub_read_subtitle_function[i].i_type == i_sub_type )
        {
gbazin's avatar
   
gbazin committed
417
            msg_Dbg( p_input, "detected %s format",
418
                    sub_read_subtitle_function[i].psz_name );
419
            p_sub->i_sub_type = i_sub_type;
420
421
422
            pf_read_subtitle = sub_read_subtitle_function[i].pf_read_subtitle;
            break;
        }
423
    }
424

425
    for( i_max = 0;; )
Laurent Aimar's avatar
Laurent Aimar committed
426
427
    {
        if( p_sub->i_subtitles >= i_max )
428
429
        {
            i_max += 128;
gbazin's avatar
   
gbazin committed
430
431
            if( !( p_sub->subtitle = realloc( p_sub->subtitle,
                                              sizeof(subtitle_t) * i_max ) ) )
432
            {
gbazin's avatar
   
gbazin committed
433
434
                msg_Err( p_sub, "out of memory");
                return VLC_ENOMEM;
435
436
            }
        }
437
        if( pf_read_subtitle( p_sub, &txt,
Laurent Aimar's avatar
Laurent Aimar committed
438
                              p_sub->subtitle + p_sub->i_subtitles,
439
440
441
442
443
444
445
446
447
                              i_microsecperframe ) < 0 )
        {
            break;
        }
        p_sub->i_subtitles++;
    }
    msg_Dbg( p_sub, "loaded %d subtitles", p_sub->i_subtitles );

    /* *** Close the file *** */
448
    text_unload( &txt );
449
450

    /* *** fix subtitle (order and time) *** */
451
    p_sub->i_subtitle = 0;  /* will be modified by sub_fix */
452
453
454
455
    if( p_sub->i_sub_type != SUB_TYPE_VOBSUB )
    {
        sub_fix( p_sub );
    }
Laurent Aimar's avatar
Laurent Aimar committed
456

457
    /* *** add subtitle ES *** */
458
    if( p_sub->i_sub_type == SUB_TYPE_VOBSUB )
459
    {
gbazin's avatar
   
gbazin committed
460
461
462
463
        int i_len = strlen( psz_name );
        char *psz_vobname = strdup(psz_name);

        strcpy( psz_vobname + i_len - 4, ".sub" );
hartman's avatar
hartman committed
464
465
466
467
468
469

        /* open file */
        if( !( p_sub->p_vobsub_file = fopen( psz_vobname, "rb" ) ) )
        {
            msg_Err( p_sub, "couldn't open .sub Vobsub file: %s", psz_vobname );
        }
gbazin's avatar
   
gbazin committed
470
        free( psz_vobname );
hartman's avatar
hartman committed
471

472
        es_format_Init( &fmt, SPU_ES, VLC_FOURCC( 's','p','u',' ' ) );
473
    }
474
475
476
    else if( p_sub->i_sub_type == SUB_TYPE_SSA1 ||
             p_sub->i_sub_type == SUB_TYPE_SSA2_4 )
    {
477
        es_format_Init( &fmt, SPU_ES, VLC_FOURCC( 's','s','a',' ' ) );
478
479
480
    }
    else
    {
481
        es_format_Init( &fmt, SPU_ES, VLC_FOURCC( 's','u','b','t' ) );
482
    }
483
484
485
486
487
488
    if( p_sub->psz_header != NULL )
    {
        fmt.i_extra = strlen( p_sub->psz_header ) + 1;
        fmt.p_extra = strdup( p_sub->psz_header );
    }
    p_sub->p_es = es_out_Add( p_input->p_es_out, &fmt );
489
    p_sub->i_previously_selected = 0;
hartman's avatar
hartman committed
490

Laurent Aimar's avatar
Laurent Aimar committed
491
    return VLC_SUCCESS;
492
493
494
495
496
497
498
}

/*****************************************************************************
 * sub_demux: Send subtitle to decoder until i_maxdate
 *****************************************************************************/
static int  sub_demux( subtitle_demux_t *p_sub, mtime_t i_maxdate )
{
499
500
501
    input_thread_t *p_input = p_sub->p_input;
    vlc_bool_t     b;

502
    es_out_Control( p_input->p_es_out, ES_OUT_GET_ES_STATE, p_sub->p_es, &b );
503
    if( b && !p_sub->i_previously_selected )
504
505
506
    {
        p_sub->i_previously_selected = 1;
        p_sub->pf_seek( p_sub, i_maxdate );
Laurent Aimar's avatar
Laurent Aimar committed
507
        return VLC_SUCCESS;
508
    }
509
    else if( !b && p_sub->i_previously_selected )
510
511
    {
        p_sub->i_previously_selected = 0;
Laurent Aimar's avatar
Laurent Aimar committed
512
        return VLC_SUCCESS;
513
514
    }

515
    if( p_sub->i_sub_type != SUB_TYPE_VOBSUB )
516
    {
517
518
        while( p_sub->i_subtitle < p_sub->i_subtitles &&
               p_sub->subtitle[p_sub->i_subtitle].i_start < i_maxdate )
519
        {
520
521
522
            block_t *p_block;
            int i_len = strlen( p_sub->subtitle[p_sub->i_subtitle].psz_text ) + 1;

523
524
525
526
527
528
            if( i_len <= 1 )
            {
                /* empty subtitle */
                p_sub->i_subtitle++;
                continue;
            }
529
530

            if( ( p_block = block_New( p_sub->p_input, i_len ) ) == NULL )
531
532
533
534
            {
                p_sub->i_subtitle++;
                continue;
            }
Laurent Aimar's avatar
Laurent Aimar committed
535

536
537
538
            /* XXX we should convert all demuxers to use es_out_Control to set pcr and
             * then remove that */
            p_block->i_pts =
Laurent Aimar's avatar
Laurent Aimar committed
539
                input_ClockGetTS( p_sub->p_input,
540
541
                                  p_sub->p_input->stream.p_selected_program,
                                  p_sub->subtitle[p_sub->i_subtitle].i_start*9/100);
gbazin's avatar
   
gbazin committed
542
            p_block->i_dts = 0;
543
544
545
546
547
548
549
550
            if( p_sub->subtitle[p_sub->i_subtitle].i_stop > 0 )
            {
                /* FIXME kludge i_dts means end of display... */
                p_block->i_dts =
                    input_ClockGetTS( p_sub->p_input,
                                  p_sub->p_input->stream.p_selected_program,
                                  p_sub->subtitle[p_sub->i_subtitle].i_stop *9/100);
            }
551

552
            memcpy( p_block->p_buffer, p_sub->subtitle[p_sub->i_subtitle].psz_text, i_len );
553

554
555
556
557
558
559
560
561
562
563
            if( p_block->i_pts > 0 )
            {
                es_out_Send( p_input->p_es_out, p_sub->p_es, p_block );
            }
            else
            {
                block_Release( p_block );
            }
            p_sub->i_subtitle++;
        }
564
565
566
567
568
    }
    else
    {
        while( p_sub->i_subtitle < p_sub->i_subtitles &&
               p_sub->subtitle[p_sub->i_subtitle].i_start < i_maxdate )
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
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
            int i_pos = p_sub->subtitle[p_sub->i_subtitle].i_vobsub_location;
            block_t *p_block;
            int i_size = 0;

            /* first compute SPU size */
            if( p_sub->i_subtitle + 1 < p_sub->i_subtitles )
            {
                i_size = p_sub->subtitle[p_sub->i_subtitle+1].i_vobsub_location - i_pos;
            }
            if( i_size <= 0 ) i_size = 65535;   /* Invalid or EOF */

            /* Seek at the right place (could be avoid if sub_seek is fixed to do his job) */
            if( fseek( p_sub->p_vobsub_file, i_pos, SEEK_CUR ) )
            {
                msg_Warn( p_sub, "cannot seek at right vobsub location %d", i_pos );
                p_sub->i_subtitle++;
                continue;
            }

            /* allocate a packet */
            if( ( p_block = block_New( p_sub, i_size ) ) == NULL )
            {
                p_sub->i_subtitle++;
                continue;
            }

            /* read data */
            p_block->i_buffer = fread( p_block->p_buffer, 1, i_size, p_sub->p_vobsub_file );
            if( p_block->i_buffer <= 6 )
            {
                block_Release( p_block );
                p_sub->i_subtitle++;
                continue;
            }

            /* pts */
            p_block->i_pts =
                input_ClockGetTS( p_sub->p_input,
                                  p_sub->p_input->stream.p_selected_program,
                                  p_sub->subtitle[p_sub->i_subtitle].i_start*9/100);

            /* demux this block */
            DemuxVobSub( p_sub, p_block );

614
            p_sub->i_subtitle++;
615
616
        }
    }
617
    return VLC_SUCCESS;
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
645
646
647
648
649
650
651
}

/*****************************************************************************
 * sub_seek: Seek to i_date
 *****************************************************************************/
static int  sub_seek ( subtitle_demux_t *p_sub, mtime_t i_date )
{
    /* should be fast enough... */
    p_sub->i_subtitle = 0;
    while( p_sub->i_subtitle < p_sub->i_subtitles &&
           p_sub->subtitle[p_sub->i_subtitle].i_start < i_date )
    {
        p_sub->i_subtitle++;
    }
    return( 0 );
}

/*****************************************************************************
 * sub_close: Close subtitle demux
 *****************************************************************************/
static void sub_close( subtitle_demux_t *p_sub )
{
    if( p_sub->subtitle )
    {
        int i;
        for( i = 0; i < p_sub->i_subtitles; i++ )
        {
            if( p_sub->subtitle[i].psz_text )
            {
                free( p_sub->subtitle[i].psz_text );
            }
        }
        free( p_sub->subtitle );
    }
hartman's avatar
hartman committed
652
653
654
655
    if( p_sub->p_vobsub_file )
    {
        fclose( p_sub->p_vobsub_file );
    }
656
}
657

658
659
660
661
662
663
664
665
666
/*****************************************************************************
 * sub_fix: fix time stamp and order of subtitle
 *****************************************************************************/
static void  sub_fix( subtitle_demux_t *p_sub )
{
    int     i;
    mtime_t i_delay;
    int     i_index;
    int     i_done;
667
    vlc_value_t val;
668
669
670
671
672
673
674
675
676
677
678
679
680
681

    /* *** fix order (to be sure...) *** */
    /* We suppose that there are near in order and this durty bubble sort
     * wont take too much time
     */
    do
    {
        i_done = 1;
        for( i_index = 1; i_index < p_sub->i_subtitles; i_index++ )
        {
            if( p_sub->subtitle[i_index].i_start <
                    p_sub->subtitle[i_index - 1].i_start )
            {
                subtitle_t sub_xch;
Laurent Aimar's avatar
Laurent Aimar committed
682
683
                memcpy( &sub_xch,
                        p_sub->subtitle + i_index - 1,
684
                        sizeof( subtitle_t ) );
Laurent Aimar's avatar
Laurent Aimar committed
685
686
                memcpy( p_sub->subtitle + i_index - 1,
                        p_sub->subtitle + i_index,
687
688
689
690
691
692
693
694
                        sizeof( subtitle_t ) );
                memcpy( p_sub->subtitle + i_index,
                        &sub_xch,
                        sizeof( subtitle_t ) );
                i_done = 0;
            }
        }
    } while( !i_done );
Laurent Aimar's avatar
Laurent Aimar committed
695

696
    /* *** and at the end add delay *** */
697
698
    var_Get( p_sub, "sub-delay", &val );
    i_delay = (mtime_t) val.i_int * 100000;
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
    if( i_delay != 0 )
    {
        for( i = 0; i < p_sub->i_subtitles; i++ )
        {
            p_sub->subtitle[i].i_start += i_delay;
            p_sub->subtitle[i].i_stop += i_delay;
            if( p_sub->subtitle[i].i_start < 0 )
            {
                p_sub->i_subtitle = i + 1;
            }
        }
    }
}



/*****************************************************************************
Laurent Aimar's avatar
Laurent Aimar committed
716
 * Specific Subtitle function
717
 *****************************************************************************/
718
static int  sub_MicroDvdRead( subtitle_demux_t *p_sub, text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe)
719
720
721
722
723
724
725
{
    /*
     * each line:
     *  {n1}{n2}Line1|Line2|Line3....
     * where n1 and n2 are the video frame number...
     *
     */
726
727
    char *s;

728
    char buffer_text[MAX_LINE + 1];
hartman's avatar
hartman committed
729
730
    unsigned int    i_start;
    unsigned int    i_stop;
731
    unsigned int i;
Laurent Aimar's avatar
Laurent Aimar committed
732

733
734
    for( ;; )
    {
735
        if( ( s = text_get_line( txt ) ) == NULL )
736
        {
737
            return( VLC_EGENERIC );
738
739
740
        }
        i_start = 0;
        i_stop  = 0;
Laurent Aimar's avatar
Laurent Aimar committed
741
742

        memset( buffer_text, '\0', MAX_LINE );
743
744
        if( sscanf( s, "{%d}{}%[^\r\n]", &i_start, buffer_text ) == 2 ||
            sscanf( s, "{%d}{%d}%[^\r\n]", &i_start, &i_stop, buffer_text ) == 3)
745
746
747
748
749
        {
            break;
        }
    }
    /* replace | by \n */
Laurent Aimar's avatar
Laurent Aimar committed
750
    for( i = 0; i < strlen( buffer_text ); i++ )
751
752
753
754
755
756
757
758
    {
        if( buffer_text[i] == '|' )
        {
            buffer_text[i] = '\n';
        }
    }
    p_subtitle->i_start = (mtime_t)i_start * (mtime_t)i_microsecperframe;
    p_subtitle->i_stop  = (mtime_t)i_stop  * (mtime_t)i_microsecperframe;
759
    p_subtitle->psz_text = strndup( buffer_text, MAX_LINE );
760
761
762
    return( 0 );
}

763
static int  sub_SubRipRead( subtitle_demux_t *p_sub, text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe )
764
765
766
767
768
769
770
771
{
    /*
     * n
     * h1:m1:s1,d1 --> h2:m2:s2,d2
     * Line1
     * Line2
     * ...
     * [empty line]
Laurent Aimar's avatar
Laurent Aimar committed
772
     *
773
     */
774
    char *s;
775
776
777
778
779
780
781
782
    char buffer_text[ 10 * MAX_LINE];
    int  i_buffer_text;
    mtime_t     i_start;
    mtime_t     i_stop;

    for( ;; )
    {
        int h1, m1, s1, d1, h2, m2, s2, d2;
783
        if( ( s = text_get_line( txt ) ) == NULL )
784
        {
785
            return( VLC_EGENERIC );
786
        }
787
        if( sscanf( s,
788
789
790
791
                    "%d:%d:%d,%d --> %d:%d:%d,%d",
                    &h1, &m1, &s1, &d1,
                    &h2, &m2, &s2, &d2 ) == 8 )
        {
Laurent Aimar's avatar
Laurent Aimar committed
792
793
794
            i_start = ( (mtime_t)h1 * 3600*1000 +
                        (mtime_t)m1 * 60*1000 +
                        (mtime_t)s1 * 1000 +
795
796
                        (mtime_t)d1 ) * 1000;

Laurent Aimar's avatar
Laurent Aimar committed
797
798
799
            i_stop  = ( (mtime_t)h2 * 3600*1000 +
                        (mtime_t)m2 * 60*1000 +
                        (mtime_t)s2 * 1000 +
800
                        (mtime_t)d2 ) * 1000;
801

802
803
804
805
            /* Now read text until an empty line */
            for( i_buffer_text = 0;; )
            {
                int i_len;
806
                if( ( s = text_get_line( txt ) ) == NULL )
807
                {
808
                    return( VLC_EGENERIC );
809
                }
810
811
812

                i_len = strlen( s );
                if( i_len <= 1 )
813
                {
814
                    /* empty line -> end of this subtitle */
815
816
817
818
819
820
821
822
                    buffer_text[__MAX( i_buffer_text - 1, 0 )] = '\0';
                    p_subtitle->i_start = i_start;
                    p_subtitle->i_stop = i_stop;
                    p_subtitle->psz_text = strdup( buffer_text );
                    return( 0 );
                }
                else
                {
823
824
825
                    if( i_buffer_text + i_len + 1 < 10 * MAX_LINE )
                    {
                        memcpy( buffer_text + i_buffer_text,
826
                                s,
827
828
829
830
831
832
                                i_len );
                        i_buffer_text += i_len;

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


840
static int  sub_SSARead( subtitle_demux_t *p_sub, text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe )
841
842
{
    char buffer_text[ 10 * MAX_LINE];
843
    char *s;
844
845
    mtime_t     i_start;
    mtime_t     i_stop;
Laurent Aimar's avatar
Laurent Aimar committed
846

847
848
849
850
    for( ;; )
    {
        int h1, m1, s1, c1, h2, m2, s2, c2;
        int i_dummy;
851
852

        if( ( s = text_get_line( txt ) ) == NULL )
853
        {
854
            return( VLC_EGENERIC );
855
        }
856
857
        p_subtitle->psz_text = malloc( strlen( s ) );

858
        if( sscanf( s,
859
                    "Dialogue: Marked=%d,%d:%d:%d.%d,%d:%d:%d.%d%[^\r\n]",
Laurent Aimar's avatar
Laurent Aimar committed
860
                    &i_dummy,
861
862
863
864
                    &h1, &m1, &s1, &c1,
                    &h2, &m2, &s2, &c2,
                    buffer_text ) == 10 )
        {
Laurent Aimar's avatar
Laurent Aimar committed
865
866
867
            i_start = ( (mtime_t)h1 * 3600*1000 +
                        (mtime_t)m1 * 60*1000 +
                        (mtime_t)s1 * 1000 +
868
869
                        (mtime_t)c1 * 10 ) * 1000;

Laurent Aimar's avatar
Laurent Aimar committed
870
871
872
            i_stop  = ( (mtime_t)h2 * 3600*1000 +
                        (mtime_t)m2 * 60*1000 +
                        (mtime_t)s2 * 1000 +
873
                        (mtime_t)c2 * 10 ) * 1000;
Laurent Aimar's avatar
Laurent Aimar committed
874

875
876
            /* The dec expects: ReadOrder, Layer, Style, Name, MarginL, MarginR, MarginV, Effect, Text */
            if( p_sub->i_sub_type == SUB_TYPE_SSA1 )
Laurent Aimar's avatar
Laurent Aimar committed
877
            {
878
879
880
881
882
                sprintf( p_subtitle->psz_text, ",%d%s", i_dummy, strdup( buffer_text) );
            }
            else
            {
                sprintf( p_subtitle->psz_text, ",%d,%s", i_dummy, strdup( buffer_text) );
883
            }
884
885
886
887
888
889
890
891
            p_subtitle->i_start = i_start;
            p_subtitle->i_stop = i_stop;
            return( 0 );
        }
        else
        {
            /* All the other stuff we add to the header field */
            if( p_sub->psz_header != NULL )
892
            {
893
894
                if( !( p_sub->psz_header = realloc( p_sub->psz_header,
                          strlen( p_sub->psz_header ) + strlen( s ) + 2 ) ) )
895
                {
896
897
                    msg_Err( p_sub, "out of memory");
                    return VLC_ENOMEM;
898
                }
899
900
901
902
903
904
                p_sub->psz_header = strcat( p_sub->psz_header, strdup( s ) );
                p_sub->psz_header = strcat( p_sub->psz_header, "\n" );
            }
            else
            {
                if( !( p_sub->psz_header = malloc( strlen( s ) + 2 ) ) )
905
                {
906
907
                    msg_Err( p_sub, "out of memory");
                    return VLC_ENOMEM;
908
                }
909
910
                p_sub->psz_header = strdup( s );
                p_sub->psz_header = strcat( p_sub->psz_header, "\n" );
911
912
913
914
915
            }
        }
    }
}

916
static int  sub_Vplayer( subtitle_demux_t *p_sub, text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe)
917
918
919
920
921
922
923
924
925
{
    /*
     * each line:
     *  h:m:s:Line1|Line2|Line3....
     *  or
     *  h:m:s Line1|Line2|Line3....
     * where n1 and n2 are the video frame number...
     *
     */
926
    char *p;
927
928
929
930
931
932
933
934
935
    char buffer_text[MAX_LINE + 1];
    mtime_t    i_start;
    unsigned int i;

    for( ;; )
    {
        int h, m, s;
        char c;

936
        if( ( p = text_get_line( txt ) ) == NULL )
937
        {
938
            return( VLC_EGENERIC );
939
        }
940

941
942
943
        i_start = 0;

        memset( buffer_text, '\0', MAX_LINE );
944
        if( sscanf( p, "%d:%d:%d%[ :]%[^\r\n]", &h, &m, &s, &c, buffer_text ) == 5 )
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
        {
            i_start = ( (mtime_t)h * 3600*1000 +
                        (mtime_t)m * 60*1000 +
                        (mtime_t)s * 1000 ) * 1000;
            break;
        }
    }

    /* replace | by \n */
    for( i = 0; i < strlen( buffer_text ); i++ )
    {
        if( buffer_text[i] == '|' )
        {
            buffer_text[i] = '\n';
        }
    }
    p_subtitle->i_start = i_start;

    p_subtitle->i_stop  = 0;
    p_subtitle->psz_text = strndup( buffer_text, MAX_LINE );
    return( 0 );
}
967

968
static char *sub_SamiSearch( text_t *txt, char *psz_start, char *psz_str )
969
{
970
971
    if( psz_start )
    {
972
        if( local_stristr( psz_start, psz_str ) )
973
        {
974
            char *s = local_stristr( psz_start, psz_str );
975

976
977
978
979
980
            s += strlen( psz_str );

            return( s );
        }
    }
981
982
    for( ;; )
    {
983
984
        char *p;
        if( ( p = text_get_line( txt ) ) == NULL )
985
        {
986
            return NULL;
987
        }
988
        if( local_stristr( p, psz_str ) )
989
        {
990
            char *s = local_stristr( p, psz_str );
991
992
993
994

            s += strlen( psz_str );

            return(  s);
995
        }
996
997
    }
}
998

999
static int  sub_Sami( subtitle_demux_t *p_sub, text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe )
1000
{