qml: Create MediaGroupDisplay
[vlc.git] / modules / demux / webvtt.c
blobdd835e2d126ce3d7bc69437225a1bc4c5ffdd6bc
1 /*****************************************************************************
2 * webvtt.c: WEBVTT text demuxer (as ISO1446-30 payload)
3 *****************************************************************************
4 * Copyright (C) 2017 VideoLabs, VLC authors and VideoLAN
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.
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.
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 *****************************************************************************/
21 /*****************************************************************************
22 * Preamble
23 *****************************************************************************/
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
29 #include <vlc_common.h>
30 #include <vlc_demux.h>
31 #include <vlc_memstream.h>
33 #include "../codec/webvtt/webvtt.h"
35 /*****************************************************************************
36 * Prototypes:
37 *****************************************************************************/
39 struct index_entry_s
41 vlc_tick_t time;
42 unsigned active;
45 typedef struct
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
55 void *p_data;
56 size_t i_data;
57 } regions_headers, styles_headers;
59 struct
61 webvtt_cue_t *p_array;
62 size_t i_alloc;
63 size_t i_count;
64 } cues;
66 struct
68 struct index_entry_s *p_array;
69 size_t i_alloc;
70 size_t i_count;
71 size_t i_current;
72 } index;
74 webvtt_text_parser_t *p_streamparser;
75 } demux_sys_t;
77 #define WEBVTT_PREALLOC 64
79 /*****************************************************************************
81 *****************************************************************************/
82 static int cue_Compare( const void *a_, const void *b_ )
84 webvtt_cue_t *a = (webvtt_cue_t *)a_;
85 webvtt_cue_t *b = (webvtt_cue_t *)b_;
86 if( a->i_start == b->i_start )
88 if( a->i_stop > b->i_stop )
89 return -1;
90 else
91 return ( a->i_stop < b->i_stop ) ? 1 : 0;
93 else return a->i_start < b->i_start ? -1 : 1;
96 static block_t *ConvertWEBVTT( const webvtt_cue_t *p_cue, bool b_continued )
98 struct vlc_memstream stream;
100 if( vlc_memstream_open( &stream ) )
101 return NULL;
103 const size_t paylsize = 8 + strlen( p_cue->psz_text );
104 const size_t idensize = (p_cue->psz_id) ? 8 + strlen( p_cue->psz_id ) : 0;
105 const size_t attrsize = (p_cue->psz_attrs) ? 8 + strlen( p_cue->psz_attrs ) : 0;
106 const size_t vttcsize = 8 + paylsize + attrsize + idensize;
108 uint8_t vttcbox[8] = { 0, 0, 0, 0, 'v', 't', 't', 'c' };
109 if( b_continued )
110 vttcbox[7] = 'x';
111 SetDWBE( vttcbox, vttcsize );
112 vlc_memstream_write( &stream, vttcbox, 8 );
114 if( p_cue->psz_id )
116 uint8_t idenbox[8] = { 0, 0, 0, 0, 'i', 'd', 'e', 'n' };
117 SetDWBE( idenbox, idensize );
118 vlc_memstream_write( &stream, idenbox, 8 );
119 vlc_memstream_write( &stream, p_cue->psz_id, idensize - 8 );
122 if( p_cue->psz_attrs )
124 uint8_t attrbox[8] = { 0, 0, 0, 0, 's', 't', 't', 'g' };
125 SetDWBE( attrbox, attrsize );
126 vlc_memstream_write( &stream, attrbox, 8 );
127 vlc_memstream_write( &stream, p_cue->psz_attrs, attrsize - 8 );
130 uint8_t paylbox[8] = { 0, 0, 0, 0, 'p', 'a', 'y', 'l' };
131 SetDWBE( paylbox, paylsize );
132 vlc_memstream_write( &stream, paylbox, 8 );
133 vlc_memstream_write( &stream, p_cue->psz_text, paylsize - 8 );
135 if( vlc_memstream_close( &stream ) == VLC_SUCCESS )
136 return block_heap_Alloc( stream.ptr, stream.length );
137 else
138 return NULL;
141 struct memstream_wrap
143 struct vlc_memstream memstream;
144 bool b_opened;
147 static void memstream_Append( struct memstream_wrap *mw, const char *psz )
149 if( mw->b_opened )
151 vlc_memstream_puts( &mw->memstream, psz );
152 vlc_memstream_putc( &mw->memstream, '\n' );
156 static void memstream_Grab( struct memstream_wrap *mw, void **pp, size_t *pi )
158 if( mw->b_opened && vlc_memstream_close( &mw->memstream ) == VLC_SUCCESS )
160 if( mw->memstream.length == 0 )
162 free( mw->memstream.ptr );
163 mw->memstream.ptr = NULL;
165 *pp = mw->memstream.ptr;
166 *pi = mw->memstream.length;
170 /*****************************************************************************
171 * Seekable demux Open() parser callbacks
172 *****************************************************************************/
173 struct callback_ctx
175 demux_t *p_demux;
176 struct memstream_wrap regions, styles;
177 bool b_ordered;
180 static webvtt_cue_t * ParserGetCueHandler( void *priv )
182 struct callback_ctx *ctx = (struct callback_ctx *) priv;
183 demux_sys_t *p_sys = ctx->p_demux->p_sys;
184 /* invalid recycled cue */
185 if( p_sys->cues.i_count &&
186 p_sys->cues.p_array[p_sys->cues.i_count - 1].psz_text == NULL )
188 return &p_sys->cues.p_array[p_sys->cues.i_count - 1];
191 if( p_sys->cues.i_alloc <= p_sys->cues.i_count &&
192 (SIZE_MAX / sizeof(webvtt_cue_t)) - WEBVTT_PREALLOC > p_sys->cues.i_alloc )
194 webvtt_cue_t *p_realloc = realloc( p_sys->cues.p_array,
195 sizeof(webvtt_cue_t) * ( p_sys->cues.i_alloc + WEBVTT_PREALLOC ) );
196 if( p_realloc )
198 p_sys->cues.p_array = p_realloc;
199 p_sys->cues.i_alloc += WEBVTT_PREALLOC;
203 if( p_sys->cues.i_alloc > p_sys->cues.i_count )
204 return &p_sys->cues.p_array[p_sys->cues.i_count++];
206 return NULL;
209 static void ParserCueDoneHandler( void *priv, webvtt_cue_t *p_cue )
211 struct callback_ctx *ctx = (struct callback_ctx *) priv;
212 demux_sys_t *p_sys = ctx->p_demux->p_sys;
213 if( p_cue->psz_text == NULL )
215 webvtt_cue_Clean( p_cue );
216 webvtt_cue_Init( p_cue );
217 return;
219 if( p_cue->i_stop > p_sys->i_length )
220 p_sys->i_length = p_cue->i_stop;
221 if( p_sys->cues.i_count > 0 &&
222 p_sys->cues.p_array[p_sys->cues.i_count - 1].i_start != p_cue->i_start )
223 ctx->b_ordered = false;
225 /* Store timings */
226 if( p_sys->index.i_alloc <= p_sys->index.i_count &&
227 (SIZE_MAX / sizeof(struct index_entry_s)) - WEBVTT_PREALLOC * 2 > p_sys->index.i_alloc )
229 void *p_realloc = realloc( p_sys->index.p_array,
230 sizeof(struct index_entry_s) *
231 ( p_sys->index.i_alloc + WEBVTT_PREALLOC * 2 ) );
232 if( p_realloc )
234 p_sys->index.p_array = p_realloc;
235 p_sys->index.i_alloc += WEBVTT_PREALLOC * 2;
238 if( p_sys->index.i_alloc > p_sys->index.i_count )
240 p_sys->index.p_array[p_sys->index.i_count].active = 1; /* tmp start tag */
241 p_sys->index.p_array[p_sys->index.i_count++].time = p_cue->i_start;
242 p_sys->index.p_array[p_sys->index.i_count].active = 0;
243 p_sys->index.p_array[p_sys->index.i_count++].time = p_cue->i_stop;
247 static void ParserHeaderHandler( void *priv, enum webvtt_header_line_e s,
248 bool b_new, const char *psz_line )
250 VLC_UNUSED(b_new);
251 struct callback_ctx *ctx = (struct callback_ctx *) priv;
252 if( s == WEBVTT_HEADER_STYLE )
253 memstream_Append( &ctx->styles, psz_line );
254 else if( s == WEBVTT_HEADER_REGION )
255 memstream_Append( &ctx->regions, psz_line );
258 /*****************************************************************************
259 * Streamed cues DemuxStream() parser callbacks
260 *****************************************************************************/
262 static webvtt_cue_t * StreamParserGetCueHandler( void *priv )
264 VLC_UNUSED(priv);
265 return malloc( sizeof(webvtt_cue_t) );
268 static void StreamParserCueDoneHandler( void *priv, webvtt_cue_t *p_cue )
270 demux_t *p_demux = (demux_t *) priv;
271 demux_sys_t *p_sys = p_demux->p_sys;
273 if( p_cue->psz_text )
275 block_t *p_block = ConvertWEBVTT( p_cue, true );
276 if( p_block )
278 if ( p_sys->b_first_time )
280 es_out_SetPCR( p_demux->out, p_cue->i_start + VLC_TICK_0 );
281 p_sys->b_first_time = false;
283 p_sys->i_next_demux_time = p_cue->i_start;
284 p_block->i_dts =
285 p_block->i_pts = VLC_TICK_0 + p_cue->i_start;
286 if( p_cue->i_stop >= 0 && p_cue->i_stop >= p_cue->i_start )
287 p_block->i_length = p_cue->i_stop - p_cue->i_start;
288 es_out_Send( p_demux->out, p_sys->es, p_block );
289 es_out_SetPCR( p_demux->out, p_cue->i_start + VLC_TICK_0 );
292 webvtt_cue_Clean( p_cue );
293 free( p_cue );
296 /*****************************************************************************
297 * Demux Index
298 *****************************************************************************/
300 static int index_Compare( const void *a_, const void *b_ )
302 struct index_entry_s *a = (struct index_entry_s *) a_;
303 struct index_entry_s *b = (struct index_entry_s *) b_;
304 if( a->time == b->time )
306 if( a->active > b->active )
307 return -1;
308 else
309 return b->active - a->active;
311 else return a->time < b->time ? -1 : 1;
314 static size_t getIndexByTime( demux_sys_t *p_sys, vlc_tick_t i_time )
316 for( size_t i=0; i<p_sys->index.i_count; i++ )
318 if( p_sys->index.p_array[i].time >= i_time )
319 return i;
321 return 0;
324 static void BuildIndex( demux_t *p_demux )
326 demux_sys_t *p_sys = p_demux->p_sys;
328 /* Order time entries ascending, start time before end time */
329 qsort( p_sys->index.p_array, p_sys->index.i_count,
330 sizeof(struct index_entry_s), index_Compare );
332 /* Build actives count
333 TIME 3000 count 1
334 TIME 14500 count 2 (1 overlap)
335 TIME 16100 count 3 (2 overlaps)
336 TIME 16100 count 2 (1 overlap.. because there next start == end)
337 TIME 18000 count 3
338 TIME 18000 count 2 */
339 unsigned i_overlaps = 0;
340 for( size_t i=0; i<p_sys->index.i_count; i++ )
342 if( p_sys->index.p_array[i].active )
343 p_sys->index.p_array[i].active = ++i_overlaps;
344 else
345 p_sys->index.p_array[i].active = --i_overlaps;
349 static block_t *demux_From( demux_t *p_demux, vlc_tick_t i_start )
351 demux_sys_t *p_sys = p_demux->p_sys;
353 block_t *p_list = NULL;
354 block_t **pp_append = &p_list;
355 for( size_t i=0; i<p_sys->cues.i_count; i++ )
357 const webvtt_cue_t *p_cue = &p_sys->cues.p_array[i];
358 if( p_cue->i_start > i_start )
360 break;
362 else if( p_cue->i_start <= i_start && p_cue->i_stop > i_start )
364 *pp_append = ConvertWEBVTT( p_cue, p_sys->index.i_current > 0 );
365 if( *pp_append )
366 pp_append = &((*pp_append)->p_next);
370 return ( p_list ) ? block_ChainGather( p_list ) : NULL;
373 static int ReadWEBVTT( demux_t *p_demux )
375 demux_sys_t *p_sys = p_demux->p_sys;
377 struct callback_ctx ctx;
378 ctx.p_demux = p_demux;
379 ctx.b_ordered = true;
381 webvtt_text_parser_t *p_parser =
382 webvtt_text_parser_New( &ctx, ParserGetCueHandler,
383 ParserCueDoneHandler,
384 ParserHeaderHandler );
385 if( p_parser == NULL )
386 return VLC_EGENERIC;
388 ctx.regions.b_opened = !vlc_memstream_open( &ctx.regions.memstream );
389 ctx.styles.b_opened = !vlc_memstream_open( &ctx.styles.memstream );
391 char *psz_line;
392 while( (psz_line = vlc_stream_ReadLine( p_demux->s )) )
393 webvtt_text_parser_Feed( p_parser, psz_line );
394 webvtt_text_parser_Feed( p_parser, NULL );
396 if( !ctx.b_ordered )
397 qsort( p_sys->cues.p_array, p_sys->cues.i_count, sizeof(webvtt_cue_t), cue_Compare );
399 BuildIndex( p_demux );
401 memstream_Grab( &ctx.regions, &p_sys->regions_headers.p_data,
402 &p_sys->regions_headers.i_data );
403 memstream_Grab( &ctx.styles, &p_sys->styles_headers.p_data,
404 &p_sys->styles_headers.i_data );
406 webvtt_text_parser_Delete( p_parser );
408 return VLC_SUCCESS;
411 static void MakeExtradata( demux_sys_t *p_sys, void **p_extra, size_t *pi_extra )
413 struct vlc_memstream extradata;
414 if( vlc_memstream_open( &extradata ) )
415 return;
416 vlc_memstream_puts( &extradata, "WEBVTT\n\n");
417 vlc_memstream_write( &extradata, p_sys->regions_headers.p_data,
418 p_sys->regions_headers.i_data );
419 vlc_memstream_write( &extradata, p_sys->styles_headers.p_data,
420 p_sys->styles_headers.i_data );
421 if( vlc_memstream_close( &extradata ) == VLC_SUCCESS )
423 if( extradata.length )
425 *p_extra = extradata.ptr;
426 *pi_extra = extradata.length;
428 else free( extradata.ptr );
432 /*****************************************************************************
433 * Control:
434 *****************************************************************************/
435 static int Control( demux_t *p_demux, int i_query, va_list args )
437 demux_sys_t *p_sys = p_demux->p_sys;
438 double *pf;
440 switch( i_query )
442 case DEMUX_CAN_SEEK:
443 *va_arg( args, bool * ) = true;
444 return VLC_SUCCESS;
446 case DEMUX_GET_LENGTH:
447 *(va_arg( args, vlc_tick_t * )) = p_sys->i_length;
448 return VLC_SUCCESS;
450 case DEMUX_GET_TIME:
451 *va_arg( args, vlc_tick_t * ) = p_sys->i_next_demux_time;
452 return VLC_SUCCESS;
454 case DEMUX_SET_TIME:
455 if( p_sys->cues.i_count )
457 p_sys->index.i_current = getIndexByTime( p_sys, va_arg( args, vlc_tick_t ) );
458 p_sys->b_first_time = true;
459 p_sys->i_next_demux_time =
460 p_sys->index.p_array[p_sys->index.i_current].time;
461 p_sys->i_next_block_flags |= BLOCK_FLAG_DISCONTINUITY;
462 return VLC_SUCCESS;
464 break;
466 case DEMUX_GET_POSITION:
467 pf = va_arg( args, double * );
468 if( p_sys->index.i_current >= p_sys->index.i_count )
470 *pf = 1.0;
472 else if( p_sys->index.i_count > 0 )
474 *pf = (double) p_sys->i_next_demux_time /
475 (p_sys->i_length + VLC_TICK_FROM_MS(500));
477 else
479 *pf = 0.0;
481 return VLC_SUCCESS;
483 case DEMUX_SET_POSITION:
484 if( p_sys->cues.i_count )
486 double f = va_arg( args, double );
487 p_sys->index.i_current = getIndexByTime( p_sys, p_sys->i_length * f );
488 p_sys->b_first_time = true;
489 p_sys->i_next_demux_time =
490 p_sys->index.p_array[p_sys->index.i_current].time;
491 p_sys->i_next_block_flags |= BLOCK_FLAG_DISCONTINUITY;
492 return VLC_SUCCESS;
494 break;
496 case DEMUX_SET_NEXT_DEMUX_TIME:
497 p_sys->b_slave = true;
498 p_sys->i_next_demux_time = va_arg( args, vlc_tick_t ) - VLC_TICK_0;
499 return VLC_SUCCESS;
501 case DEMUX_CAN_PAUSE:
502 case DEMUX_SET_PAUSE_STATE:
503 case DEMUX_CAN_CONTROL_PACE:
504 return demux_vaControlHelper( p_demux->s, 0, -1, 0, 1, i_query, args );
506 case DEMUX_GET_PTS_DELAY:
507 case DEMUX_GET_FPS:
508 case DEMUX_GET_META:
509 case DEMUX_GET_ATTACHMENTS:
510 case DEMUX_GET_TITLE_INFO:
511 case DEMUX_HAS_UNSUPPORTED_META:
512 case DEMUX_CAN_RECORD:
513 default:
514 break;
517 return VLC_EGENERIC;
520 static int ControlStream( demux_t *p_demux, int i_query, va_list args )
522 demux_sys_t *p_sys = p_demux->p_sys;
524 switch( i_query )
526 case DEMUX_GET_TIME:
527 if( p_sys->i_next_demux_time != VLC_TICK_INVALID )
529 *va_arg( args, vlc_tick_t * ) = p_sys->i_next_demux_time;
530 return VLC_SUCCESS;
532 default:
533 break;
536 return VLC_EGENERIC;
539 /*****************************************************************************
540 * Demux: Send subtitle to decoder
541 *****************************************************************************/
542 static int Demux( demux_t *p_demux )
544 demux_sys_t *p_sys = p_demux->p_sys;
546 vlc_tick_t i_barrier = p_sys->i_next_demux_time;
548 while( p_sys->index.i_current < p_sys->index.i_count &&
549 p_sys->index.p_array[p_sys->index.i_current].time <= i_barrier )
551 /* Find start and end of our interval */
552 vlc_tick_t i_start_time = p_sys->index.p_array[p_sys->index.i_current].time;
553 vlc_tick_t i_end_time = i_start_time;
554 /* use next interval time as end time */
555 while( ++p_sys->index.i_current < p_sys->index.i_count )
557 if( i_start_time != p_sys->index.p_array[p_sys->index.i_current].time )
559 i_end_time = p_sys->index.p_array[p_sys->index.i_current].time;
560 break;
564 block_t *p_block = demux_From( p_demux, i_start_time );
565 if( p_block )
567 p_block->i_length = i_end_time - i_start_time;
568 p_block->i_dts = p_block->i_pts = VLC_TICK_0 + i_start_time;
570 if( p_sys->i_next_block_flags )
572 p_block->i_flags = p_sys->i_next_block_flags;
573 p_sys->i_next_block_flags = 0;
576 if ( !p_sys->b_slave && p_sys->b_first_time )
578 es_out_SetPCR( p_demux->out, p_block->i_dts );
579 p_sys->b_first_time = false;
582 es_out_Send( p_demux->out, p_sys->es, p_block );
585 if( p_sys->index.i_current < p_sys->index.i_count &&
586 p_sys->index.p_array[p_sys->index.i_current].active > 1 )
588 /* we'll need to clear up overlaps */
589 p_sys->i_next_block_flags |= BLOCK_FLAG_DISCONTINUITY;
593 if ( !p_sys->b_slave )
595 es_out_SetPCR( p_demux->out, VLC_TICK_0 + i_barrier );
596 p_sys->i_next_demux_time += VLC_TICK_FROM_SEC(1);
599 if( p_sys->index.i_current >= p_sys->index.i_count )
600 return VLC_DEMUXER_EOF;
602 return VLC_DEMUXER_SUCCESS;
605 static int DemuxStream( demux_t *p_demux )
607 demux_sys_t *p_sys = p_demux->p_sys;
609 char *psz_line = vlc_stream_ReadLine( p_demux->s );
610 webvtt_text_parser_Feed( p_sys->p_streamparser, psz_line );
612 return ( psz_line == NULL ) ? VLC_DEMUXER_EOF : VLC_DEMUXER_SUCCESS;
615 /*****************************************************************************
616 * Module initializers common
617 *****************************************************************************/
618 static int ProbeWEBVTT( demux_t *p_demux )
620 const uint8_t *p_peek;
621 size_t i_peek = vlc_stream_Peek( p_demux->s, &p_peek, 16 );
622 if( i_peek < 16 )
623 return VLC_EGENERIC;
625 if( !memcmp( p_peek, "\xEF\xBB\xBF", 3 ) )
626 p_peek += 3;
628 if( ( memcmp( p_peek, "WEBVTT", 6 ) ||
629 ( p_peek[6] != '\n' &&
630 p_peek[6] != ' ' &&
631 p_peek[6] != '\t' &&
632 ( p_peek[6] != '\r' || p_peek[7] != '\n' ) )
633 ) && !p_demux->obj.force )
635 msg_Dbg( p_demux, "subtitle demux discarded" );
636 return VLC_EGENERIC;
639 return VLC_SUCCESS;
642 /*****************************************************************************
643 * Module initializers
644 *****************************************************************************/
645 int webvtt_OpenDemux ( vlc_object_t *p_this )
647 demux_t *p_demux = (demux_t*)p_this;
648 demux_sys_t *p_sys;
650 int i_ret = ProbeWEBVTT( p_demux );
651 if( i_ret != VLC_SUCCESS )
652 return i_ret;
654 p_demux->pf_demux = Demux;
655 p_demux->pf_control = Control;
657 p_demux->p_sys = p_sys = calloc( 1, sizeof( demux_sys_t ) );
658 if( p_sys == NULL )
659 return VLC_ENOMEM;
661 if( ReadWEBVTT( p_demux ) != VLC_SUCCESS )
663 webvtt_CloseDemux( p_this );
664 return VLC_EGENERIC;
667 es_format_t fmt;
668 es_format_Init( &fmt, SPU_ES, VLC_CODEC_WEBVTT );
669 size_t i_extra = 0;
670 MakeExtradata( p_sys, &fmt.p_extra, &i_extra );
671 fmt.i_extra = i_extra;
672 fmt.i_id = 0;
673 p_sys->es = es_out_Add( p_demux->out, &fmt );
674 es_format_Clean( &fmt );
675 if( p_sys->es == NULL )
677 webvtt_CloseDemux( p_this );
678 return VLC_EGENERIC;
681 return VLC_SUCCESS;
684 int webvtt_OpenDemuxStream ( vlc_object_t *p_this )
686 demux_t *p_demux = (demux_t*)p_this;
687 demux_sys_t *p_sys;
689 int i_ret = ProbeWEBVTT( p_demux );
690 if( i_ret != VLC_SUCCESS )
691 return i_ret;
693 p_demux->pf_demux = DemuxStream;
694 p_demux->pf_control = ControlStream;
696 p_demux->p_sys = p_sys = calloc( 1, sizeof( demux_sys_t ) );
697 if( p_sys == NULL )
698 return VLC_ENOMEM;
700 p_sys->p_streamparser = webvtt_text_parser_New( p_demux,
701 StreamParserGetCueHandler,
702 StreamParserCueDoneHandler,
703 NULL );
704 if( !p_sys->p_streamparser )
706 webvtt_CloseDemux( p_this );
707 return VLC_EGENERIC;
710 es_format_t fmt;
711 es_format_Init( &fmt, SPU_ES, VLC_CODEC_WEBVTT );
712 p_sys->es = es_out_Add( p_demux->out, &fmt );
713 es_format_Clean( &fmt );
714 if( p_sys->es == NULL )
716 webvtt_CloseDemux( p_this );
717 return VLC_EGENERIC;
720 return VLC_SUCCESS;
723 /*****************************************************************************
724 * Close: Close subtitle demux
725 *****************************************************************************/
726 void webvtt_CloseDemux( vlc_object_t *p_this )
728 demux_t *p_demux = (demux_t*)p_this;
729 demux_sys_t *p_sys = p_demux->p_sys;
731 for( size_t i=0; i< p_sys->cues.i_count; i++ )
732 webvtt_cue_Clean( &p_sys->cues.p_array[i] );
733 free( p_sys->cues.p_array );
735 free( p_sys->index.p_array );
737 if( p_sys->p_streamparser )
739 webvtt_text_parser_Feed( p_sys->p_streamparser, NULL );
740 webvtt_text_parser_Delete( p_sys->p_streamparser );
743 free( p_sys );