6c81292bc4e048a8410e7b0cff1fcb2cf950a1d1
[vlc.git] / modules / demux / ttml.c
1 /*****************************************************************************
2  * ttml.c : TTML subtitles demux
3  *****************************************************************************
4  * Copyright (C) 2015-2017 VLC authors and VideoLAN
5  *
6  * Authors: Hugo BeauzĂ©e-Luyssen <hugo@beauzee.fr>
7  *          Sushma Reddy <sushma.reddy@research.iiit.ac.in>
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 #include <vlc_common.h>
29 #include <vlc_demux.h>
30 #include <vlc_xml.h>
31 #include <vlc_strings.h>
32 #include <vlc_memory.h>
33 #include <vlc_memstream.h>
34 #include <vlc_es_out.h>
35 #include <vlc_charset.h>          /* FromCharset */
36
37 #include <assert.h>
38 #include <stdlib.h>
39
40 #include "../codec/ttml/ttml.h"
41
42 //#define TTML_DEMUX_DEBUG
43
44 struct demux_sys_t
45 {
46     xml_t*          p_xml;
47     xml_reader_t*   p_reader;
48     es_out_id_t*    p_es;
49     int64_t         i_next_demux_time;
50     bool            b_slave;
51     bool            b_first_time;
52
53     tt_node_t         *p_rootnode;
54
55     tt_timings_t    temporal_extent;
56
57     /*
58      * All timings are stored unique and ordered.
59      * Being begin or end times of sub sequence,
60      * we use them as 'point of change' for output filtering.
61     */
62     struct
63     {
64         tt_time_t *p_array;
65         size_t   i_count;
66         size_t   i_current;
67     } times;
68 };
69
70 static char *tt_genTiming( tt_time_t t )
71 {
72     if( !tt_time_Valid( &t ) )
73         t.base = 0;
74     unsigned f = t.base % CLOCK_FREQ;
75     t.base /= CLOCK_FREQ;
76     unsigned h = t.base / 3600;
77     unsigned m = t.base % 3600 / 60;
78     unsigned s = t.base % 60;
79
80     int i_ret;
81     char *psz;
82     if( f )
83     {
84         const char *lz = "000000";
85         const char *psz_lz = &lz[6];
86         /* add leading zeroes */
87         for( unsigned i=10*f; i<CLOCK_FREQ; i *= 10 )
88             psz_lz--;
89         /* strip trailing zeroes */
90         for( ; f > 0 && (f % 10) == 0; f /= 10 );
91         i_ret = asprintf( &psz, "%02u:%02u:%02u.%s%u",
92                                  h, m, s, psz_lz, f );
93     }
94     else if( t.frames )
95     {
96         i_ret = asprintf( &psz, "%02u:%02u:%02u:%s%u",
97                                  h, m, s, t.frames < 10 ? "0" : "", t.frames );
98     }
99     else
100     {
101         i_ret = asprintf( &psz, "%02u:%02u:%02u",
102                                  h, m, s );
103     }
104
105     return i_ret < 0 ? NULL : psz;
106 }
107
108 static void tt_node_AttributesToText( struct vlc_memstream *p_stream, const tt_node_t* p_node )
109 {
110     bool b_timed_node = false;
111     const vlc_dictionary_t* p_attr_dict = &p_node->attr_dict;
112     for( int i = 0; i < p_attr_dict->i_size; ++i )
113     {
114         for ( vlc_dictionary_entry_t* p_entry = p_attr_dict->p_entries[i];
115                                       p_entry != NULL; p_entry = p_entry->p_next )
116         {
117             const char *psz_value = NULL;
118
119             if( !strcmp(p_entry->psz_key, "begin") ||
120                 !strcmp(p_entry->psz_key, "end") ||
121                 !strcmp(p_entry->psz_key, "dur") )
122             {
123                 b_timed_node = true;
124                 /* will remove duration */
125                 continue;
126             }
127             else if( !strcmp(p_entry->psz_key, "timeContainer") )
128             {
129                 /* also remove sequential timings info (all abs now) */
130                 continue;
131             }
132             else
133             {
134                 psz_value = (char const*)p_entry->p_value;
135             }
136
137             if( psz_value == NULL )
138                 continue;
139
140             vlc_memstream_printf( p_stream, " %s=\"%s\"",
141                                   p_entry->psz_key, psz_value );
142         }
143     }
144
145     if( b_timed_node )
146     {
147         if( tt_time_Valid( &p_node->timings.begin ) )
148         {
149             char *psz = tt_genTiming( p_node->timings.begin );
150             vlc_memstream_printf( p_stream, " begin=\"%s\"", psz );
151             free( psz );
152         }
153
154         if( tt_time_Valid( &p_node->timings.end ) )
155         {
156             char *psz = tt_genTiming( p_node->timings.end );
157             vlc_memstream_printf( p_stream, " end=\"%s\"", psz );
158             free( psz );
159         }
160     }
161 }
162
163 static void tt_node_ToText( struct vlc_memstream *p_stream, const tt_basenode_t *p_basenode,
164                             const tt_time_t *playbacktime )
165 {
166     if( p_basenode->i_type == TT_NODE_TYPE_ELEMENT )
167     {
168         const tt_node_t *p_node = (const tt_node_t *) p_basenode;
169
170         if( tt_time_Valid( playbacktime ) &&
171            !tt_timings_Contains( &p_node->timings, playbacktime ) )
172             return;
173
174         vlc_memstream_putc( p_stream, '<' );
175         vlc_memstream_puts( p_stream, p_node->psz_node_name );
176
177         tt_node_AttributesToText( p_stream, p_node );
178
179         if( tt_node_HasChild( p_node ) )
180         {
181             vlc_memstream_putc( p_stream, '>' );
182
183 #ifdef TTML_DEMUX_DEBUG
184             vlc_memstream_printf( p_stream, "<!-- starts %ld ends %ld -->",
185                                   tt_time_Convert( &p_node->timings.begin ),
186                                   tt_time_Convert( &p_node->timings.end ) );
187 #endif
188
189             for( const tt_basenode_t *p_child = p_node->p_child;
190                                    p_child; p_child = p_child->p_next )
191             {
192                 tt_node_ToText( p_stream, p_child, playbacktime );
193             }
194
195             vlc_memstream_printf( p_stream, "</%s>", p_node->psz_node_name );
196         }
197         else
198             vlc_memstream_puts( p_stream, "/>" );
199     }
200     else
201     {
202         const tt_textnode_t *p_textnode = (const tt_textnode_t *) p_basenode;
203         vlc_memstream_puts( p_stream, p_textnode->psz_text );
204     }
205 }
206
207 static int Control( demux_t* p_demux, int i_query, va_list args )
208 {
209     demux_sys_t *p_sys = p_demux->p_sys;
210     int64_t *pi64, i64;
211     double *pf, f;
212     bool b;
213
214     switch( i_query )
215     {
216         case DEMUX_CAN_SEEK:
217             *va_arg( args, bool * ) = true;
218             return VLC_SUCCESS;
219         case DEMUX_GET_TIME:
220             pi64 = va_arg( args, int64_t * );
221             *pi64 = p_sys->i_next_demux_time;
222             return VLC_SUCCESS;
223         case DEMUX_SET_TIME:
224             i64 = va_arg( args, int64_t );
225             if( p_sys->times.i_count )
226             {
227                 tt_time_t t = tt_time_Create( i64 - VLC_TS_0 );
228                 size_t i_index = tt_timings_FindLowerIndex( p_sys->times.p_array,
229                                                             p_sys->times.i_count, t, &b );
230                 p_sys->times.i_current = i_index;
231                 p_sys->b_first_time = true;
232                 return VLC_SUCCESS;
233             }
234             break;
235         case DEMUX_SET_NEXT_DEMUX_TIME:
236             i64 = va_arg( args, int64_t );
237             p_sys->i_next_demux_time = i64;
238             p_sys->b_slave = true;
239             return VLC_SUCCESS;
240         case DEMUX_GET_LENGTH:
241             pi64 = va_arg( args, int64_t * );
242             if( p_sys->times.i_count )
243             {
244                 tt_time_t t = tt_time_Sub( p_sys->times.p_array[p_sys->times.i_count - 1],
245                                            p_sys->temporal_extent.begin );
246                 *pi64 = tt_time_Convert( &t );
247                 return VLC_SUCCESS;
248             }
249             break;
250         case DEMUX_GET_POSITION:
251             pf = va_arg( args, double * );
252             if( p_sys->times.i_current >= p_sys->times.i_count )
253             {
254                 *pf = 1.0;
255             }
256             else if( p_sys->times.i_count > 0 )
257             {
258                 i64 = tt_time_Convert( &p_sys->times.p_array[p_sys->times.i_count - 1] );
259                 *pf = (double) p_sys->i_next_demux_time / (i64 + 0.5);
260             }
261             else
262             {
263                 *pf = 0.0;
264             }
265             return VLC_SUCCESS;
266         case DEMUX_SET_POSITION:
267             f = va_arg( args, double );
268             if( p_sys->times.i_count )
269             {
270                 i64 = f * tt_time_Convert( &p_sys->times.p_array[p_sys->times.i_count - 1] );
271                 tt_time_t t = tt_time_Create( i64 );
272                 size_t i_index = tt_timings_FindLowerIndex( p_sys->times.p_array,
273                                                             p_sys->times.i_count, t, &b );
274                 p_sys->times.i_current = i_index;
275                 p_sys->b_first_time = true;
276                 return VLC_SUCCESS;
277             }
278             break;
279         case DEMUX_GET_PTS_DELAY:
280         case DEMUX_GET_FPS:
281         case DEMUX_GET_META:
282         case DEMUX_GET_ATTACHMENTS:
283         case DEMUX_GET_TITLE_INFO:
284         case DEMUX_HAS_UNSUPPORTED_META:
285         case DEMUX_CAN_RECORD:
286         default:
287             break;
288     }
289
290     return VLC_EGENERIC;
291 }
292
293 static int ReadTTML( demux_t* p_demux )
294 {
295     demux_sys_t* p_sys = p_demux->p_sys;
296     const char* psz_node_name;
297
298     do
299     {
300         int i_type = xml_ReaderNextNode( p_sys->p_reader, &psz_node_name );
301         bool b_empty = xml_ReaderIsEmptyElement( p_sys->p_reader );
302
303         if( i_type <= XML_READER_NONE )
304             break;
305
306         switch(i_type)
307         {
308             default:
309                 break;
310
311             case XML_READER_STARTELEM:
312                 if( tt_node_NameCompare( psz_node_name, "tt" ) ||
313                     p_sys->p_rootnode != NULL )
314                     return VLC_EGENERIC;
315
316                 p_sys->p_rootnode = tt_node_New( p_sys->p_reader, NULL, psz_node_name );
317                 if( b_empty )
318                     break;
319                 if( !p_sys->p_rootnode ||
320                     tt_nodes_Read( p_sys->p_reader, p_sys->p_rootnode ) != VLC_SUCCESS )
321                     return VLC_EGENERIC;
322                 break;
323
324             case XML_READER_ENDELEM:
325                 if( !p_sys->p_rootnode ||
326                     tt_node_NameCompare( psz_node_name, p_sys->p_rootnode->psz_node_name ) )
327                     return VLC_EGENERIC;
328                 break;
329         }
330
331     } while( 1 );
332
333     if( p_sys->p_rootnode == NULL )
334         return VLC_EGENERIC;
335
336     return VLC_SUCCESS;
337 }
338
339 static int Demux( demux_t* p_demux )
340 {
341     demux_sys_t* p_sys = p_demux->p_sys;
342
343     /* Last one must be an end time */
344     while( p_sys->times.i_current + 1 < p_sys->times.i_count &&
345            tt_time_Convert( &p_sys->times.p_array[p_sys->times.i_current] ) <= p_sys->i_next_demux_time )
346     {
347         const int64_t i_playbacktime =
348                 tt_time_Convert( &p_sys->times.p_array[p_sys->times.i_current] );
349         const int64_t i_playbackendtime =
350                 tt_time_Convert( &p_sys->times.p_array[p_sys->times.i_current + 1] ) - 1;
351
352         if ( !p_sys->b_slave && p_sys->b_first_time )
353         {
354             es_out_Control( p_demux->out, ES_OUT_SET_PCR, VLC_TS_0 + i_playbacktime );
355             p_sys->b_first_time = false;
356         }
357
358         struct vlc_memstream stream;
359
360         if( vlc_memstream_open( &stream ) )
361             return VLC_DEMUXER_EGENERIC;
362
363         tt_node_ToText( &stream, (tt_basenode_t *) p_sys->p_rootnode,
364                         &p_sys->times.p_array[p_sys->times.i_current] );
365
366         if( vlc_memstream_close( &stream ) == VLC_SUCCESS )
367         {
368             block_t* p_block = block_heap_Alloc( stream.ptr, stream.length );
369             if( p_block )
370             {
371                 p_block->i_dts =
372                     p_block->i_pts = VLC_TS_0 + i_playbacktime;
373                 p_block->i_length = i_playbackendtime - i_playbacktime;
374
375                 es_out_Send( p_demux->out, p_sys->p_es, p_block );
376             }
377         }
378
379         p_sys->times.i_current++;
380     }
381
382     if ( !p_sys->b_slave )
383     {
384         es_out_Control( p_demux->out, ES_OUT_SET_PCR, VLC_TS_0 + p_sys->i_next_demux_time );
385         p_sys->i_next_demux_time += CLOCK_FREQ / 8;
386     }
387
388     if( p_sys->times.i_current + 1 >= p_sys->times.i_count )
389         return VLC_DEMUXER_EOF;
390
391     return VLC_DEMUXER_SUCCESS;
392 }
393
394 int OpenDemux( vlc_object_t* p_this )
395 {
396     demux_t     *p_demux = (demux_t*)p_this;
397     demux_sys_t *p_sys;
398
399     const uint8_t *p_peek;
400     ssize_t i_peek = vlc_stream_Peek( p_demux->s, &p_peek, 2048 );
401     if( unlikely( i_peek <= 32 ) )
402         return VLC_EGENERIC;
403
404     const char *psz_xml = (const char *) p_peek;
405     size_t i_xml  = i_peek;
406
407     char *psz_alloc = NULL;
408     switch( GetQWBE(p_peek) )
409     {
410         /* See RFC 3023 Part 4 */
411         case 0xFFFE3C003F007800UL: /* UTF16 BOM<?xml */
412         case 0xFEFF003C003F0078UL: /* UTF16 BOM<?xml */
413             psz_alloc = FromCharset( "UTF-16", p_peek, i_peek );
414             break;
415         case 0x3C003F0078006D00UL: /* UTF16-LE <?xml */
416             psz_alloc = FromCharset( "UTF-16LE", p_peek, i_peek );
417             break;
418         case 0x003C003F0078006DUL: /* UTF16-BE <?xml */
419             psz_alloc = FromCharset( "UTF-16BE", p_peek, i_peek );
420             break;
421         case 0x3C3F786D6C207665UL: /* UTF8 <?xml */
422             break;
423         default:
424             return VLC_EGENERIC;
425     }
426
427     if( psz_alloc )
428     {
429         psz_xml = psz_alloc;
430         i_xml = strlen( psz_alloc );
431     }
432
433     /* Simplified probing. Valid TTML must have a namespace declaration */
434     const char *psz_tt = strnstr( psz_xml, "tt ", i_xml );
435     if( !psz_tt || psz_tt == psz_xml ||
436         (psz_tt[-1] != ':' && psz_tt[-1] != '<') )
437     {
438         free( psz_alloc );
439         return VLC_EGENERIC;
440     }
441     else
442     {
443         const char * const rgsz[] =
444         {
445             "=\"http://www.w3.org/ns/ttml\"",
446             "=\"http://www.w3.org/2004/11/ttaf1\"",
447             "=\"http://www.w3.org/2006/04/ttaf1\"",
448             "=\"http://www.w3.org/2006/10/ttaf1\"",
449         };
450         const char *psz_ns = NULL;
451         for( size_t i=0; i<ARRAY_SIZE(rgsz) && !psz_ns; i++ )
452         {
453             psz_ns = strnstr( psz_xml, rgsz[i],
454                               i_xml - (psz_tt - psz_xml) );
455         }
456         free( psz_alloc );
457         if( !psz_ns )
458             return VLC_EGENERIC;
459     }
460
461     p_demux->p_sys = p_sys = calloc( 1, sizeof( *p_sys ) );
462     if( unlikely( p_sys == NULL ) )
463         return VLC_ENOMEM;
464
465     p_sys->b_first_time = true;
466     p_sys->temporal_extent.i_type = TT_TIMINGS_PARALLEL;
467     tt_time_Init( &p_sys->temporal_extent.begin );
468     tt_time_Init( &p_sys->temporal_extent.end );
469     tt_time_Init( &p_sys->temporal_extent.dur );
470     p_sys->temporal_extent.begin.base = 0;
471
472     p_sys->p_xml = xml_Create( p_demux );
473     if( !p_sys->p_xml )
474         goto error;
475
476     p_sys->p_reader = xml_ReaderCreate( p_sys->p_xml, p_demux->s );
477     if( !p_sys->p_reader )
478         goto error;
479
480 #ifndef TTML_DEMUX_DEBUG
481     p_sys->p_reader->obj.flags |= OBJECT_FLAGS_QUIET;
482 #endif
483
484     if( ReadTTML( p_demux ) != VLC_SUCCESS )
485         goto error;
486
487     tt_timings_Resolve( (tt_basenode_t *) p_sys->p_rootnode, &p_sys->temporal_extent,
488                         &p_sys->times.p_array, &p_sys->times.i_count );
489
490 #ifdef TTML_DEMUX_DEBUG
491     {
492         struct vlc_memstream stream;
493
494         if( vlc_memstream_open( &stream ) )
495             goto error;
496
497         tt_time_t t;
498         tt_time_Init( &t );
499         tt_node_ToText( &stream, (tt_basenode_t*)p_sys->p_rootnode, &t /* invalid */ );
500
501         vlc_memstream_putc( &stream, '\0' );
502
503         if( vlc_memstream_close( &stream ) == VLC_SUCCESS )
504         {
505             msg_Dbg( p_demux, "%s", stream.ptr );
506             free( stream.ptr );
507         }
508     }
509 #endif
510
511     p_demux->pf_demux = Demux;
512     p_demux->pf_control = Control;
513
514     es_format_t fmt;
515     es_format_Init( &fmt, SPU_ES, VLC_CODEC_TTML );
516     p_sys->p_es = es_out_Add( p_demux->out, &fmt );
517     if( !p_sys->p_es )
518         goto error;
519
520     es_format_Clean( &fmt );
521
522     return VLC_SUCCESS;
523
524 error:
525     CloseDemux( p_demux );
526
527     return VLC_EGENERIC;
528 }
529
530 void CloseDemux( demux_t* p_demux )
531 {
532     demux_sys_t* p_sys = p_demux->p_sys;
533
534     if( p_sys->p_rootnode )
535         tt_node_RecursiveDelete( p_sys->p_rootnode );
536
537     if( p_sys->p_es )
538         es_out_Del( p_demux->out, p_sys->p_es );
539
540     if( p_sys->p_reader )
541         xml_ReaderDelete( p_sys->p_reader );
542
543     if( p_sys->p_xml )
544         xml_Delete( p_sys->p_xml );
545
546     free( p_sys->times.p_array );
547
548     free( p_sys );
549 }