Coverage Report

Created: 2025-07-11 07:16

/src/vlc/modules/demux/webvtt.c
Line
Count
Source (jump to first uncovered line)
1
/*****************************************************************************
2
 * webvtt.c: WEBVTT text demuxer (as ISO1446-30 payload)
3
 *****************************************************************************
4
 * Copyright (C) 2017 VideoLabs, VLC authors and VideoLAN
5
 *
6
 * This program is free software; you can redistribute it and/or modify it
7
 * under the terms of the GNU Lesser General Public License as published by
8
 * the Free Software Foundation; either version 2.1 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
 * GNU Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General Public License
17
 * along with this program; if not, write to the Free Software Foundation,
18
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19
 *****************************************************************************/
20
21
/*****************************************************************************
22
 * Preamble
23
 *****************************************************************************/
24
25
#ifdef HAVE_CONFIG_H
26
# include "config.h"
27
#endif
28
29
#include <vlc_common.h>
30
#include <vlc_demux.h>
31
#include <vlc_memstream.h>
32
33
#include "../codec/webvtt/webvtt.h"
34
35
/*****************************************************************************
36
 * Prototypes:
37
 *****************************************************************************/
38
39
struct index_entry_s
40
{
41
    vlc_tick_t time;
42
    unsigned active;
43
};
44
45
typedef struct
46
{
47
    es_out_id_t *es;
48
    bool         b_slave;
49
    bool         b_first_time;
50
    int          i_next_block_flags;
51
    vlc_tick_t   i_next_demux_time;
52
    vlc_tick_t   i_length;
53
    struct
54
    {
55
        void    *p_data;
56
        size_t   i_data;
57
    } regions_headers, styles_headers;
58
59
    struct
60
    {
61
        webvtt_cue_t *p_array;
62
        size_t  i_alloc;
63
        size_t  i_count;
64
    } cues;
65
66
    struct
67
    {
68
        struct index_entry_s *p_array;
69
        size_t   i_alloc;
70
        size_t   i_count;
71
        size_t   i_current;
72
    } index;
73
74
    webvtt_text_parser_t *p_streamparser;
75
} demux_sys_t;
76
77
27.9k
#define WEBVTT_PREALLOC 64
78
79
/*****************************************************************************
80
 *
81
 *****************************************************************************/
82
static int cue_Compare( const void *a_, const void *b_ )
83
0
{
84
0
    webvtt_cue_t *a = (webvtt_cue_t *)a_;
85
0
    webvtt_cue_t *b = (webvtt_cue_t *)b_;
86
0
    if( a->i_start == b->i_start )
87
0
    {
88
0
        if( a->i_stop > b->i_stop )
89
0
            return -1;
90
0
        else
91
0
            return ( a->i_stop < b->i_stop ) ? 1 : 0;
92
0
    }
93
0
    else return a->i_start < b->i_start ? -1 : 1;
94
0
}
95
96
static block_t *ConvertWEBVTT( const webvtt_cue_t *p_cue, bool b_continued )
97
291k
{
98
291k
    struct vlc_memstream stream;
99
100
291k
    if( vlc_memstream_open( &stream ) )
101
0
        return NULL;
102
103
291k
    const size_t paylsize = 8 + strlen( p_cue->psz_text );
104
291k
    const size_t idensize = (p_cue->psz_id) ? 8 + strlen( p_cue->psz_id ) : 0;
105
291k
    const size_t attrsize = (p_cue->psz_attrs) ? 8 + strlen( p_cue->psz_attrs ) : 0;
106
291k
    const size_t vttcsize = 8 + paylsize + attrsize + idensize;
107
108
291k
    uint8_t vttcbox[8] = { 0, 0, 0, 0, 'v', 't', 't', 'c' };
109
291k
    if( b_continued )
110
291k
        vttcbox[7] = 'x';
111
291k
    SetDWBE( vttcbox, vttcsize );
112
291k
    vlc_memstream_write( &stream, vttcbox, 8 );
113
114
291k
    if( p_cue->psz_id )
115
210k
    {
116
210k
        uint8_t idenbox[8] = { 0, 0, 0, 0, 'i', 'd', 'e', 'n' };
117
210k
        SetDWBE( idenbox, idensize );
118
210k
        vlc_memstream_write( &stream, idenbox, 8 );
119
210k
        vlc_memstream_write( &stream, p_cue->psz_id, idensize - 8 );
120
210k
    }
121
122
291k
    if( p_cue->psz_attrs )
123
105k
    {
124
105k
        uint8_t attrbox[8] = { 0, 0, 0, 0, 's', 't', 't', 'g' };
125
105k
        SetDWBE( attrbox, attrsize );
126
105k
        vlc_memstream_write( &stream, attrbox, 8 );
127
105k
        vlc_memstream_write( &stream, p_cue->psz_attrs, attrsize - 8 );
128
105k
    }
129
130
291k
    uint8_t paylbox[8] = { 0, 0, 0, 0, 'p', 'a', 'y', 'l' };
131
291k
    SetDWBE( paylbox, paylsize );
132
291k
    vlc_memstream_write( &stream, paylbox, 8 );
133
291k
    vlc_memstream_write( &stream, p_cue->psz_text, paylsize - 8 );
134
135
291k
    if( vlc_memstream_close( &stream ) == 0 )
136
291k
        return block_heap_Alloc( stream.ptr, stream.length );
137
0
    return NULL;
138
291k
}
139
140
struct memstream_wrap
141
{
142
    struct vlc_memstream memstream;
143
    bool b_opened;
144
};
145
146
static void memstream_Append( struct memstream_wrap *mw, const char *psz )
147
95.8k
{
148
95.8k
    if( mw->b_opened )
149
95.8k
    {
150
95.8k
        vlc_memstream_puts( &mw->memstream, psz );
151
95.8k
        vlc_memstream_putc( &mw->memstream, '\n' );
152
95.8k
    }
153
95.8k
}
154
155
static void memstream_Grab( struct memstream_wrap *mw, void **pp, size_t *pi )
156
12.4k
{
157
12.4k
    if( mw->b_opened && vlc_memstream_close( &mw->memstream ) == 0 )
158
12.4k
    {
159
12.4k
        *pp = mw->memstream.ptr;
160
12.4k
        *pi = mw->memstream.length;
161
12.4k
    }
162
12.4k
}
163
164
/*****************************************************************************
165
 * Seekable demux Open() parser callbacks
166
 *****************************************************************************/
167
struct callback_ctx
168
{
169
    demux_t *p_demux;
170
    struct memstream_wrap regions, styles;
171
    bool b_ordered;
172
};
173
174
static webvtt_cue_t * ParserGetCueHandler( void *priv )
175
87.1k
{
176
87.1k
    struct callback_ctx *ctx = (struct callback_ctx *) priv;
177
87.1k
    demux_sys_t *p_sys = ctx->p_demux->p_sys;
178
    /* invalid recycled cue */
179
87.1k
    if( p_sys->cues.i_count &&
180
87.1k
        p_sys->cues.p_array[p_sys->cues.i_count - 1].psz_text == NULL )
181
0
    {
182
0
        return &p_sys->cues.p_array[p_sys->cues.i_count - 1];
183
0
    }
184
185
87.1k
    if( p_sys->cues.i_alloc <= p_sys->cues.i_count &&
186
87.1k
       (SIZE_MAX / sizeof(webvtt_cue_t)) - WEBVTT_PREALLOC > p_sys->cues.i_alloc )
187
4.66k
    {
188
4.66k
        webvtt_cue_t *p_realloc = realloc( p_sys->cues.p_array,
189
4.66k
                sizeof(webvtt_cue_t) * ( p_sys->cues.i_alloc + WEBVTT_PREALLOC ) );
190
4.66k
        if( p_realloc )
191
4.66k
        {
192
4.66k
            p_sys->cues.p_array = p_realloc;
193
4.66k
            p_sys->cues.i_alloc += WEBVTT_PREALLOC;
194
4.66k
        }
195
4.66k
    }
196
197
87.1k
    if( p_sys->cues.i_alloc > p_sys->cues.i_count )
198
87.1k
        return &p_sys->cues.p_array[p_sys->cues.i_count++];
199
200
0
    return NULL;
201
87.1k
}
202
203
static void ParserCueDoneHandler( void *priv, webvtt_cue_t *p_cue )
204
87.1k
{
205
87.1k
    struct callback_ctx *ctx = (struct callback_ctx *) priv;
206
87.1k
    demux_sys_t *p_sys = ctx->p_demux->p_sys;
207
87.1k
    if( p_cue->psz_text == NULL )
208
0
    {
209
0
        webvtt_cue_Clean( p_cue );
210
0
        webvtt_cue_Init( p_cue );
211
0
        return;
212
0
    }
213
87.1k
    if( p_cue->i_stop > p_sys->i_length )
214
4.72k
        p_sys->i_length = p_cue->i_stop;
215
87.1k
    if( p_sys->cues.i_count > 0 &&
216
87.1k
        p_sys->cues.p_array[p_sys->cues.i_count - 1].i_start != p_cue->i_start )
217
0
        ctx->b_ordered = false;
218
219
    /* Store timings */
220
87.1k
    if( p_sys->index.i_alloc <= p_sys->index.i_count &&
221
87.1k
       (SIZE_MAX / sizeof(struct index_entry_s)) - WEBVTT_PREALLOC * 2 > p_sys->index.i_alloc )
222
4.66k
    {
223
4.66k
        void *p_realloc = realloc( p_sys->index.p_array,
224
4.66k
                                   sizeof(struct index_entry_s) *
225
4.66k
                                   ( p_sys->index.i_alloc + WEBVTT_PREALLOC * 2 ) );
226
4.66k
        if( p_realloc )
227
4.66k
        {
228
4.66k
            p_sys->index.p_array = p_realloc;
229
4.66k
            p_sys->index.i_alloc += WEBVTT_PREALLOC * 2;
230
4.66k
        }
231
4.66k
    }
232
87.1k
    if( p_sys->index.i_alloc > p_sys->index.i_count )
233
87.1k
    {
234
87.1k
        p_sys->index.p_array[p_sys->index.i_count].active = 1; /* tmp start tag */
235
87.1k
        p_sys->index.p_array[p_sys->index.i_count++].time = p_cue->i_start;
236
87.1k
        p_sys->index.p_array[p_sys->index.i_count].active = 0;
237
87.1k
        p_sys->index.p_array[p_sys->index.i_count++].time = p_cue->i_stop;
238
87.1k
    }
239
87.1k
}
240
241
static void ParserHeaderHandler( void *priv, enum webvtt_header_line_e s,
242
                                 bool b_new, const char *psz_line )
243
95.8k
{
244
95.8k
    VLC_UNUSED(b_new);
245
95.8k
    struct callback_ctx *ctx = (struct callback_ctx *) priv;
246
95.8k
    if( s == WEBVTT_HEADER_STYLE )
247
73.6k
        memstream_Append( &ctx->styles, psz_line );
248
22.2k
    else if( s == WEBVTT_HEADER_REGION )
249
22.2k
        memstream_Append( &ctx->regions, psz_line );
250
95.8k
}
251
252
/*****************************************************************************
253
 * Streamed cues DemuxStream() parser callbacks
254
 *****************************************************************************/
255
256
static webvtt_cue_t * StreamParserGetCueHandler( void *priv )
257
0
{
258
0
    VLC_UNUSED(priv);
259
0
    return malloc( sizeof(webvtt_cue_t) );
260
0
}
261
262
static void StreamParserCueDoneHandler( void *priv, webvtt_cue_t *p_cue )
263
0
{
264
0
    demux_t *p_demux = (demux_t *) priv;
265
0
    demux_sys_t *p_sys = p_demux->p_sys;
266
267
0
    if( p_cue->psz_text )
268
0
    {
269
0
        block_t *p_block = ConvertWEBVTT( p_cue, true );
270
0
        if( p_block )
271
0
        {
272
0
            if ( p_sys->b_first_time )
273
0
            {
274
0
                es_out_SetPCR( p_demux->out, p_cue->i_start + VLC_TICK_0 );
275
0
                p_sys->b_first_time = false;
276
0
            }
277
0
            p_sys->i_next_demux_time = p_cue->i_start;
278
0
            p_block->i_dts =
279
0
                    p_block->i_pts = VLC_TICK_0 + p_cue->i_start;
280
0
            if( p_cue->i_stop >= 0 && p_cue->i_stop >= p_cue->i_start )
281
0
                p_block->i_length = p_cue->i_stop - p_cue->i_start;
282
0
            es_out_Send( p_demux->out, p_sys->es, p_block );
283
0
            es_out_SetPCR( p_demux->out, p_cue->i_start + VLC_TICK_0 );
284
0
        }
285
0
    }
286
0
    webvtt_cue_Clean( p_cue );
287
0
    free( p_cue );
288
0
}
289
290
/*****************************************************************************
291
 * Demux Index
292
 *****************************************************************************/
293
294
static int index_Compare( const void *a_, const void *b_ )
295
1.55M
{
296
1.55M
    struct index_entry_s *a = (struct index_entry_s *) a_;
297
1.55M
    struct index_entry_s *b = (struct index_entry_s *) b_;
298
1.55M
    if( a->time == b->time )
299
917k
    {
300
917k
        if( a->active > b->active )
301
88
            return -1;
302
917k
        else
303
917k
            return b->active - a->active;
304
917k
    }
305
634k
    else return a->time < b->time ? -1 : 1;
306
1.55M
}
307
308
static size_t getIndexByTime( demux_sys_t *p_sys, vlc_tick_t i_time )
309
0
{
310
0
    for( size_t i=0; i<p_sys->index.i_count; i++ )
311
0
    {
312
0
        if( p_sys->index.p_array[i].time >= i_time )
313
0
            return i;
314
0
    }
315
0
    return 0;
316
0
}
317
318
static void BuildIndex( demux_t *p_demux )
319
6.20k
{
320
6.20k
    demux_sys_t *p_sys = p_demux->p_sys;
321
322
    /* Order time entries ascending, start time before end time */
323
6.20k
    qsort( p_sys->index.p_array, p_sys->index.i_count,
324
6.20k
           sizeof(struct index_entry_s), index_Compare );
325
326
    /* Build actives count
327
    TIME 3000 count 1
328
    TIME 14500 count 2 (1 overlap)
329
    TIME 16100 count 3 (2 overlaps)
330
    TIME 16100 count 2 (1 overlap.. because there next start == end)
331
    TIME 18000 count 3
332
    TIME 18000 count 2 */
333
6.20k
    unsigned i_overlaps = 0;
334
180k
    for( size_t i=0; i<p_sys->index.i_count; i++ )
335
174k
    {
336
174k
        if( p_sys->index.p_array[i].active )
337
87.1k
            p_sys->index.p_array[i].active = ++i_overlaps;
338
87.1k
        else
339
87.1k
            p_sys->index.p_array[i].active = --i_overlaps;
340
174k
    }
341
6.20k
}
342
343
static block_t *demux_From( demux_t *p_demux, vlc_tick_t i_start )
344
13.0k
{
345
13.0k
    demux_sys_t *p_sys = p_demux->p_sys;
346
347
13.0k
    block_t *p_list = NULL;
348
13.0k
    block_t **pp_append = &p_list;
349
501k
    for( size_t i=0; i<p_sys->cues.i_count; i++ )
350
492k
    {
351
492k
        const webvtt_cue_t *p_cue = &p_sys->cues.p_array[i];
352
492k
        if( p_cue->i_start > i_start )
353
4.16k
        {
354
4.16k
            break;
355
4.16k
        }
356
488k
        else if( p_cue->i_start <= i_start && p_cue->i_stop > i_start )
357
291k
        {
358
291k
            *pp_append = ConvertWEBVTT( p_cue, p_sys->index.i_current > 0 );
359
291k
            if( *pp_append )
360
291k
                pp_append = &((*pp_append)->p_next);
361
291k
        }
362
492k
    }
363
364
13.0k
    return ( p_list ) ? block_ChainGather( p_list ) : NULL;
365
13.0k
}
366
367
static int ReadWEBVTT( demux_t *p_demux )
368
6.20k
{
369
6.20k
    demux_sys_t *p_sys = p_demux->p_sys;
370
371
6.20k
    struct callback_ctx ctx;
372
6.20k
    ctx.p_demux = p_demux;
373
6.20k
    ctx.b_ordered = true;
374
375
6.20k
    webvtt_text_parser_t *p_parser =
376
6.20k
            webvtt_text_parser_New( &ctx, ParserGetCueHandler,
377
6.20k
                                          ParserCueDoneHandler,
378
6.20k
                                          ParserHeaderHandler );
379
6.20k
    if( p_parser == NULL )
380
0
        return VLC_EGENERIC;
381
382
6.20k
    ctx.regions.b_opened = !vlc_memstream_open( &ctx.regions.memstream );
383
6.20k
    ctx.styles.b_opened = !vlc_memstream_open( &ctx.styles.memstream );
384
385
6.20k
    char *psz_line;
386
685k
    while( (psz_line = vlc_stream_ReadLine( p_demux->s )) )
387
679k
        webvtt_text_parser_Feed( p_parser, psz_line );
388
6.20k
    webvtt_text_parser_Feed( p_parser, NULL );
389
390
6.20k
    if( !ctx.b_ordered )
391
0
        qsort( p_sys->cues.p_array, p_sys->cues.i_count, sizeof(webvtt_cue_t), cue_Compare );
392
393
6.20k
    BuildIndex( p_demux );
394
395
6.20k
    memstream_Grab( &ctx.regions, &p_sys->regions_headers.p_data,
396
6.20k
                                  &p_sys->regions_headers.i_data );
397
6.20k
    memstream_Grab( &ctx.styles, &p_sys->styles_headers.p_data,
398
6.20k
                                 &p_sys->styles_headers.i_data );
399
400
6.20k
    webvtt_text_parser_Delete( p_parser );
401
402
6.20k
    return VLC_SUCCESS;
403
6.20k
}
404
405
static void MakeExtradata( demux_sys_t *p_sys, void **p_extra, size_t *pi_extra )
406
6.20k
{
407
6.20k
    struct vlc_memstream extradata;
408
6.20k
    if( vlc_memstream_open( &extradata ) )
409
0
        return;
410
6.20k
    vlc_memstream_puts( &extradata, "WEBVTT\n\n");
411
6.20k
    vlc_memstream_write( &extradata, p_sys->regions_headers.p_data,
412
6.20k
                                     p_sys->regions_headers.i_data );
413
6.20k
    vlc_memstream_write( &extradata, p_sys->styles_headers.p_data,
414
6.20k
                                     p_sys->styles_headers.i_data );
415
6.20k
    if( vlc_memstream_close( &extradata ) == 0 )
416
6.20k
    {
417
6.20k
        if( extradata.length )
418
6.20k
        {
419
6.20k
            *p_extra = extradata.ptr;
420
6.20k
            *pi_extra = extradata.length;
421
6.20k
        }
422
0
        else free( extradata.ptr );
423
6.20k
    }
424
6.20k
}
425
426
/*****************************************************************************
427
 * Control:
428
 *****************************************************************************/
429
static int Control( demux_t *p_demux, int i_query, va_list args )
430
0
{
431
0
    demux_sys_t *p_sys = p_demux->p_sys;
432
0
    double *pf;
433
434
0
    switch( i_query )
435
0
    {
436
0
        case DEMUX_CAN_SEEK:
437
0
            *va_arg( args, bool * ) = true;
438
0
            return VLC_SUCCESS;
439
440
0
        case DEMUX_GET_LENGTH:
441
0
            *(va_arg( args, vlc_tick_t * )) = p_sys->i_length;
442
0
            return VLC_SUCCESS;
443
444
0
        case DEMUX_GET_TIME:
445
0
            *va_arg( args, vlc_tick_t * ) = p_sys->i_next_demux_time;
446
0
            return VLC_SUCCESS;
447
448
0
        case DEMUX_SET_TIME:
449
0
            if( p_sys->cues.i_count )
450
0
            {
451
0
                p_sys->index.i_current = getIndexByTime( p_sys, va_arg( args, vlc_tick_t ) );
452
0
                p_sys->b_first_time = true;
453
0
                p_sys->i_next_demux_time =
454
0
                        p_sys->index.p_array[p_sys->index.i_current].time;
455
0
                p_sys->i_next_block_flags |= BLOCK_FLAG_DISCONTINUITY;
456
0
                return VLC_SUCCESS;
457
0
            }
458
0
            break;
459
460
0
        case DEMUX_GET_POSITION:
461
0
            pf = va_arg( args, double * );
462
0
            if( p_sys->index.i_current >= p_sys->index.i_count )
463
0
            {
464
0
                *pf = 1.0;
465
0
            }
466
0
            else if( p_sys->index.i_count > 0 )
467
0
            {
468
0
                *pf = (double) p_sys->i_next_demux_time /
469
0
                      (p_sys->i_length + VLC_TICK_FROM_MS(500));
470
0
            }
471
0
            else
472
0
            {
473
0
                *pf = 0.0;
474
0
            }
475
0
            return VLC_SUCCESS;
476
477
0
        case DEMUX_SET_POSITION:
478
0
            if( p_sys->cues.i_count )
479
0
            {
480
0
                double f = va_arg( args, double );
481
0
                p_sys->index.i_current = getIndexByTime( p_sys, p_sys->i_length * f );
482
0
                p_sys->b_first_time = true;
483
0
                p_sys->i_next_demux_time =
484
0
                        p_sys->index.p_array[p_sys->index.i_current].time;
485
0
                p_sys->i_next_block_flags |= BLOCK_FLAG_DISCONTINUITY;
486
0
                return VLC_SUCCESS;
487
0
            }
488
0
            break;
489
490
0
        case DEMUX_SET_NEXT_DEMUX_TIME:
491
0
            p_sys->b_slave = true;
492
0
            p_sys->i_next_demux_time = va_arg( args, vlc_tick_t ) - VLC_TICK_0;
493
0
            return VLC_SUCCESS;
494
495
0
        case DEMUX_CAN_PAUSE:
496
0
        case DEMUX_SET_PAUSE_STATE:
497
0
        case DEMUX_CAN_CONTROL_PACE:
498
0
            return demux_vaControlHelper( p_demux->s, 0, -1, 0, 1, i_query, args );
499
500
0
        case DEMUX_GET_PTS_DELAY:
501
0
        case DEMUX_GET_FPS:
502
0
        case DEMUX_GET_META:
503
0
        case DEMUX_GET_ATTACHMENTS:
504
0
        case DEMUX_GET_TITLE_INFO:
505
0
        case DEMUX_HAS_UNSUPPORTED_META:
506
0
        case DEMUX_CAN_RECORD:
507
0
        default:
508
0
            break;
509
510
0
    }
511
0
    return VLC_EGENERIC;
512
0
}
513
514
static int ControlStream( demux_t *p_demux, int i_query, va_list args )
515
0
{
516
0
    demux_sys_t *p_sys = p_demux->p_sys;
517
518
0
    switch( i_query )
519
0
    {
520
0
        case DEMUX_GET_TIME:
521
0
            if( p_sys->i_next_demux_time != VLC_TICK_INVALID )
522
0
            {
523
0
                *va_arg( args, vlc_tick_t * ) = p_sys->i_next_demux_time;
524
0
                return VLC_SUCCESS;
525
0
            }
526
0
        default:
527
0
            break;
528
0
    }
529
530
0
    return VLC_EGENERIC;
531
0
}
532
533
/*****************************************************************************
534
 * Demux: Send subtitle to decoder
535
 *****************************************************************************/
536
static int Demux( demux_t *p_demux )
537
2.75G
{
538
2.75G
    demux_sys_t *p_sys = p_demux->p_sys;
539
540
2.75G
    vlc_tick_t i_barrier = p_sys->i_next_demux_time;
541
542
2.75G
    while( p_sys->index.i_current < p_sys->index.i_count &&
543
2.75G
           p_sys->index.p_array[p_sys->index.i_current].time <= i_barrier )
544
13.0k
    {
545
        /* Find start and end of our interval */
546
13.0k
        vlc_tick_t i_start_time = p_sys->index.p_array[p_sys->index.i_current].time;
547
13.0k
        vlc_tick_t i_end_time = i_start_time;
548
        /* use next interval time as end time */
549
174k
        while( ++p_sys->index.i_current < p_sys->index.i_count )
550
170k
        {
551
170k
            if( i_start_time != p_sys->index.p_array[p_sys->index.i_current].time )
552
9.57k
            {
553
9.57k
                i_end_time = p_sys->index.p_array[p_sys->index.i_current].time;
554
9.57k
                break;
555
9.57k
            }
556
170k
        }
557
558
13.0k
        block_t *p_block = demux_From( p_demux, i_start_time );
559
13.0k
        if( p_block )
560
8.87k
        {
561
8.87k
            p_block->i_length = i_end_time - i_start_time;
562
8.87k
            p_block->i_dts = p_block->i_pts = VLC_TICK_0 + i_start_time;
563
564
8.87k
            if( p_sys->i_next_block_flags )
565
4.41k
            {
566
4.41k
                p_block->i_flags = p_sys->i_next_block_flags;
567
4.41k
                p_sys->i_next_block_flags = 0;
568
4.41k
            }
569
570
8.87k
            if ( !p_sys->b_slave && p_sys->b_first_time )
571
0
            {
572
0
                es_out_SetPCR( p_demux->out, p_block->i_dts );
573
0
                p_sys->b_first_time = false;
574
0
            }
575
576
8.87k
            es_out_Send( p_demux->out, p_sys->es, p_block );
577
8.87k
        }
578
579
13.0k
        if( p_sys->index.i_current < p_sys->index.i_count &&
580
13.0k
            p_sys->index.p_array[p_sys->index.i_current].active > 1 )
581
4.68k
        {
582
            /* we'll need to clear up overlaps */
583
4.68k
            p_sys->i_next_block_flags |= BLOCK_FLAG_DISCONTINUITY;
584
4.68k
        }
585
13.0k
    }
586
587
2.75G
    if ( !p_sys->b_slave )
588
2.75G
    {
589
2.75G
        es_out_SetPCR( p_demux->out, VLC_TICK_0 + i_barrier );
590
2.75G
        p_sys->i_next_demux_time += VLC_TICK_FROM_SEC(1);
591
2.75G
    }
592
593
2.75G
    if( p_sys->index.i_current >= p_sys->index.i_count )
594
6.20k
        return VLC_DEMUXER_EOF;
595
596
2.75G
    return VLC_DEMUXER_SUCCESS;
597
2.75G
}
598
599
static int DemuxStream( demux_t *p_demux )
600
0
{
601
0
    demux_sys_t *p_sys = p_demux->p_sys;
602
603
0
    char *psz_line = vlc_stream_ReadLine( p_demux->s );
604
0
    webvtt_text_parser_Feed( p_sys->p_streamparser, psz_line );
605
606
0
    return ( psz_line == NULL ) ? VLC_DEMUXER_EOF : VLC_DEMUXER_SUCCESS;
607
0
}
608
609
/*****************************************************************************
610
 * Module initializers common
611
 *****************************************************************************/
612
static int ProbeWEBVTT( demux_t *p_demux )
613
6.21k
{
614
6.21k
    const uint8_t *p_peek;
615
6.21k
    size_t i_peek = vlc_stream_Peek( p_demux->s, &p_peek, 16 );
616
6.21k
    if( i_peek < 16 )
617
16
        return VLC_EGENERIC;
618
619
6.20k
    if( !memcmp( p_peek, "\xEF\xBB\xBF", 3 ) )
620
2.01k
        p_peek += 3;
621
622
6.20k
    if( ( memcmp( p_peek, "WEBVTT", 6 ) ||
623
6.20k
          ( p_peek[6] != '\n' &&
624
2.94k
            p_peek[6] != ' ' &&
625
2.94k
            p_peek[6] != '\t' &&
626
2.94k
           ( p_peek[6] != '\r' || p_peek[7] != '\n' ) )
627
6.20k
        ) && !p_demux->obj.force )
628
0
    {
629
0
        msg_Dbg( p_demux, "subtitle demux discarded" );
630
0
        return VLC_EGENERIC;
631
0
    }
632
633
6.20k
    return VLC_SUCCESS;
634
6.20k
}
635
636
/*****************************************************************************
637
 * Module initializers
638
 *****************************************************************************/
639
int webvtt_OpenDemux ( vlc_object_t *p_this )
640
6.21k
{
641
6.21k
    demux_t        *p_demux = (demux_t*)p_this;
642
6.21k
    demux_sys_t    *p_sys;
643
644
6.21k
    int i_ret = ProbeWEBVTT( p_demux );
645
6.21k
    if( i_ret != VLC_SUCCESS )
646
8
        return i_ret;
647
648
6.20k
    p_demux->pf_demux = Demux;
649
6.20k
    p_demux->pf_control = Control;
650
651
6.20k
    p_demux->p_sys = p_sys = calloc( 1, sizeof( demux_sys_t ) );
652
6.20k
    if( p_sys == NULL )
653
0
        return VLC_ENOMEM;
654
655
6.20k
    if( ReadWEBVTT( p_demux ) != VLC_SUCCESS )
656
0
    {
657
0
        webvtt_CloseDemux( p_this );
658
0
        return VLC_EGENERIC;
659
0
    }
660
661
6.20k
    es_format_t fmt;
662
6.20k
    es_format_Init( &fmt, SPU_ES, VLC_CODEC_WEBVTT );
663
6.20k
    size_t i_extra = 0;
664
6.20k
    MakeExtradata( p_sys, &fmt.p_extra, &i_extra );
665
6.20k
    fmt.i_extra = i_extra;
666
6.20k
    fmt.i_id = 0;
667
6.20k
    p_sys->es = es_out_Add( p_demux->out, &fmt );
668
6.20k
    es_format_Clean( &fmt );
669
6.20k
    if( p_sys->es == NULL )
670
0
    {
671
0
        webvtt_CloseDemux( p_this );
672
0
        return VLC_EGENERIC;
673
0
    }
674
675
6.20k
    return VLC_SUCCESS;
676
6.20k
}
677
678
int webvtt_OpenDemuxStream ( vlc_object_t *p_this )
679
8
{
680
8
    demux_t        *p_demux = (demux_t*)p_this;
681
8
    demux_sys_t    *p_sys;
682
683
8
    int i_ret = ProbeWEBVTT( p_demux );
684
8
    if( i_ret != VLC_SUCCESS )
685
8
        return i_ret;
686
687
0
    p_demux->pf_demux = DemuxStream;
688
0
    p_demux->pf_control = ControlStream;
689
690
0
    p_demux->p_sys = p_sys = calloc( 1, sizeof( demux_sys_t ) );
691
0
    if( p_sys == NULL )
692
0
        return VLC_ENOMEM;
693
694
0
    p_sys->p_streamparser = webvtt_text_parser_New( p_demux,
695
0
                                          StreamParserGetCueHandler,
696
0
                                          StreamParserCueDoneHandler,
697
0
                                          NULL );
698
0
    if( !p_sys->p_streamparser )
699
0
    {
700
0
        webvtt_CloseDemux( p_this );
701
0
        return VLC_EGENERIC;
702
0
    }
703
704
0
    es_format_t fmt;
705
0
    es_format_Init( &fmt, SPU_ES, VLC_CODEC_WEBVTT );
706
0
    p_sys->es = es_out_Add( p_demux->out, &fmt );
707
0
    es_format_Clean( &fmt );
708
0
    if( p_sys->es == NULL )
709
0
    {
710
0
        webvtt_CloseDemux( p_this );
711
0
        return VLC_EGENERIC;
712
0
    }
713
714
0
    return VLC_SUCCESS;
715
0
}
716
717
/*****************************************************************************
718
 * Close: Close subtitle demux
719
 *****************************************************************************/
720
void webvtt_CloseDemux( vlc_object_t *p_this )
721
6.20k
{
722
6.20k
    demux_t *p_demux = (demux_t*)p_this;
723
6.20k
    demux_sys_t *p_sys = p_demux->p_sys;
724
725
93.3k
    for( size_t i=0; i< p_sys->cues.i_count; i++ )
726
87.1k
        webvtt_cue_Clean( &p_sys->cues.p_array[i] );
727
6.20k
    free( p_sys->cues.p_array );
728
729
6.20k
    free( p_sys->index.p_array );
730
731
6.20k
    if( p_sys->p_streamparser )
732
0
    {
733
0
        webvtt_text_parser_Feed( p_sys->p_streamparser, NULL );
734
0
        webvtt_text_parser_Delete( p_sys->p_streamparser );
735
0
    }
736
737
6.20k
    free( p_sys->regions_headers.p_data );
738
6.20k
    free( p_sys->styles_headers.p_data );
739
740
6.20k
    free( p_sys );
741
6.20k
}