Coverage Report

Created: 2026-03-07 07:40

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