Coverage Report

Created: 2025-07-23 07:11

/src/vlc/modules/codec/ttml/ttml.c
Line
Count
Source (jump to first uncovered line)
1
/*****************************************************************************
2
 * ttml.c : TTML helpers
3
 *****************************************************************************
4
 * Copyright (C) 2017 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
#ifdef HAVE_CONFIG_H
21
# include "config.h"
22
#endif
23
24
#include <vlc_common.h>
25
#include <vlc_plugin.h>
26
#include <vlc_xml.h>
27
#include <vlc_strings.h>
28
#include <vlc_charset.h>
29
30
#include <assert.h>
31
#include <stdlib.h>
32
#include <limits.h>
33
34
#include "ttml.h"
35
36
#define ALIGN_TEXT N_("Subtitle justification")
37
#define ALIGN_LONGTEXT N_("Set the justification of subtitles")
38
39
/*****************************************************************************
40
 * Modules descriptor.
41
 *****************************************************************************/
42
43
104
vlc_module_begin ()
44
52
    set_capability( "spu decoder", 10 )
45
52
    set_shortname( N_("TTML decoder"))
46
52
    set_description( N_("TTML subtitles decoder") )
47
52
    set_callback( tt_OpenDecoder )
48
52
    set_subcategory( SUBCAT_INPUT_SCODEC )
49
52
    add_integer( "ttml-align", 0, ALIGN_TEXT, ALIGN_LONGTEXT )
50
51
52
    add_submodule()
52
52
        set_shortname( N_("TTML") )
53
52
        set_description( N_("TTML demuxer") )
54
52
        set_capability( "demux", 11 )
55
52
        set_subcategory( SUBCAT_INPUT_DEMUX )
56
52
        set_callbacks( tt_OpenDemux, tt_CloseDemux )
57
52
        add_shortcut( "ttml" )
58
52
#ifdef ENABLE_SOUT
59
52
    add_submodule()
60
52
        set_shortname( N_("TTML") )
61
52
        set_description( N_("TTML encoder") )
62
52
        set_capability( "spu encoder", 101 )
63
52
        set_subcategory( SUBCAT_INPUT_SCODEC )
64
52
        set_callbacks( tt_OpenEncoder, NULL )
65
52
#endif
66
52
vlc_module_end ()
67
68
struct tt_namespace_s
69
{
70
    char *psz_prefix;
71
    char *psz_uri;
72
    struct vlc_list links;
73
};
74
75
void tt_namespaces_Clean( tt_namespaces_t *nss )
76
28.7k
{
77
28.7k
    struct tt_namespace_s *ns;
78
28.7k
    vlc_list_foreach( ns, &nss->nodes, links )
79
67.1k
    {
80
67.1k
        free( ns->psz_prefix );
81
67.1k
        free( ns->psz_uri );
82
67.1k
        free( ns );
83
67.1k
    }
84
28.7k
}
85
86
void tt_namespaces_Init( tt_namespaces_t *nss )
87
28.7k
{
88
28.7k
    vlc_list_init( &nss->nodes );
89
28.7k
}
90
91
const char * tt_namespaces_GetURI( const tt_namespaces_t *nss,
92
                                   const char *psz_qn )
93
245k
{
94
245k
    const struct tt_namespace_s *ns;
95
245k
    vlc_list_foreach_const( ns, &nss->nodes, links )
96
993k
    {
97
        /* compares prefixed name against raw prefix */
98
993k
        for( size_t i=0; ; i++ )
99
1.74M
        {
100
1.74M
            if( ns->psz_prefix[i] == psz_qn[i] )
101
746k
            {
102
746k
                if( psz_qn[i] == '\0' )
103
613
                    return ns->psz_uri;
104
746k
            }
105
993k
            else
106
993k
            {
107
993k
                if( ns->psz_prefix[i] == '\0' && psz_qn[i] == ':' )
108
133k
                    return ns->psz_uri;
109
860k
                else
110
860k
                    break;
111
993k
            }
112
1.74M
        }
113
993k
    }
114
111k
    return NULL;
115
245k
}
116
117
const char * tt_namespaces_GetPrefix( const tt_namespaces_t *nss,
118
                                      const char *psz_uri )
119
1.10M
{
120
1.10M
    const struct tt_namespace_s *ns;
121
1.10M
    vlc_list_foreach_const( ns, &nss->nodes, links )
122
2.81M
    {
123
2.81M
        if( !strcmp( ns->psz_uri, psz_uri ) )
124
1.01M
            return ns->psz_prefix;
125
2.81M
    }
126
93.7k
    return NULL;
127
1.10M
}
128
129
void tt_namespaces_Register( tt_namespaces_t *nss, const char *psz_prefix,
130
                             const char *psz_uri )
131
1.00M
{
132
1.00M
    if( !psz_uri || tt_namespaces_GetPrefix( nss, psz_uri ) )
133
935k
        return;
134
67.1k
    struct tt_namespace_s *ns = malloc(sizeof(*ns));
135
67.1k
    if( ns )
136
67.1k
    {
137
67.1k
        const char *sep = strchr( psz_prefix, ':' );
138
67.1k
        if( sep )
139
29.2k
            ns->psz_prefix = strndup( psz_prefix, sep - psz_prefix );
140
37.9k
        else
141
37.9k
            ns->psz_prefix = strdup("");
142
67.1k
        ns->psz_uri = strdup( psz_uri );
143
67.1k
        if( !ns->psz_prefix || !ns->psz_uri )
144
0
        {
145
0
            free( ns->psz_prefix );
146
0
            free( ns->psz_uri );
147
0
            free( ns );
148
0
            return;
149
0
        }
150
67.1k
        vlc_list_append( &ns->links, &nss->nodes );
151
67.1k
    }
152
67.1k
}
153
154
static const char * tt_node_InheritNS( const tt_node_t *p_node )
155
1.51M
{
156
53.4M
    for( ; p_node ; p_node = p_node->p_parent )
157
53.4M
    {
158
53.4M
        if( p_node->psz_namespace )
159
1.49M
            return p_node->psz_namespace;
160
53.4M
    }
161
23.0k
    return NULL;
162
1.51M
}
163
164
bool tt_node_Match( const tt_node_t *p_node, const char *psz_name, const char *psz_namespace )
165
6.05M
{
166
    /* compare local part first (should have less chars) */
167
6.05M
    const char *psz_nodelocal = tt_LocalName( p_node->psz_node_name );
168
6.05M
    const char *psz_namelocal = tt_LocalName( psz_name );
169
6.05M
    if( strcmp( psz_namelocal, psz_nodelocal ) )
170
5.20M
        return false;
171
172
846k
    const char *psz_nodens = p_node->psz_namespace;
173
846k
    if( !psz_nodens )
174
803k
        psz_nodens = tt_node_InheritNS( p_node->p_parent );
175
846k
    if( psz_namespace && psz_nodens )
176
845k
        return !strcmp( psz_namespace, psz_nodens );
177
913
    return !!psz_namespace == !!psz_nodens;
178
846k
}
179
180
const char * tt_node_GetAttribute( tt_namespaces_t *p_nss, const tt_node_t *p_node,
181
                                   const char *psz_name, const char *psz_namespace )
182
1.16M
{
183
1.16M
    const void *value;
184
1.16M
    char *alloc = NULL;
185
1.16M
    if( psz_namespace )
186
119k
    {
187
119k
        const char *psz_prefix = tt_namespaces_GetPrefix( p_nss, psz_namespace );
188
119k
        if( psz_prefix == NULL ||
189
119k
            asprintf( &alloc, "%s:%s", psz_prefix, psz_name ) < 1 )
190
26.5k
            return NULL;
191
92.6k
        psz_name = alloc;
192
92.6k
    }
193
1.14M
    value = vlc_dictionary_value_for_key( &p_node->attr_dict, psz_name );
194
1.14M
    free( alloc );
195
1.14M
    return value != kVLCDictionaryNotFound ? (const char *)value : NULL;
196
1.16M
}
197
198
bool tt_node_HasChild( const tt_node_t *p_node )
199
650k
{
200
650k
    return p_node->p_child;
201
650k
}
202
203
static inline bool tt_ScanReset( unsigned *a, unsigned *b, unsigned *c,
204
                                 char *d,  unsigned *e )
205
288k
{
206
288k
    *a = *b = *c = *d = *e = 0;
207
288k
    return false;
208
288k
}
209
210
static tt_time_t tt_ParseTime( const char *s )
211
775k
{
212
775k
    tt_time_t t = {-1, 0};
213
775k
    unsigned h1 = 0, m1 = 0, s1 = 0, d1 = 0;
214
775k
    char c = 0;
215
216
    /* Clock time */
217
775k
    if( sscanf( s, "%u:%2u:%2u%c%u",     &h1, &m1, &s1, &c, &d1 ) == 5 ||
218
775k
                           tt_ScanReset( &h1, &m1, &s1, &c, &d1 )      ||
219
775k
        sscanf( s, "%u:%2u:%2u",         &h1, &m1, &s1          ) == 3 ||
220
775k
                           tt_ScanReset( &h1, &m1, &s1, &c, &d1 ) )
221
663k
    {
222
663k
        t.base = vlc_tick_from_sec(h1 * 3600 + m1 * 60 + s1);
223
663k
        if( c == '.' && d1 > 0 )
224
492k
        {
225
492k
            unsigned i_den = 1;
226
2.26M
            for( const char *p = strchr( s, '.' ) + 1; *p && (i_den < UINT_MAX / 10); p++ )
227
1.77M
                i_den *= 10;
228
492k
            t.base += vlc_tick_from_samples(d1, i_den);
229
492k
        }
230
170k
        else if( c == ':' )
231
67
        {
232
67
            t.frames = d1;
233
67
        }
234
663k
    }
235
112k
    else /* Offset Time */
236
112k
    {
237
112k
        char *psz_end = (char *) s;
238
112k
        double v = vlc_strtod_c( s, &psz_end );
239
112k
        if( psz_end != s && *psz_end )
240
111k
        {
241
111k
            if( *psz_end == 'm' )
242
501
            {
243
501
                if( *(psz_end + 1) == 's' )
244
271
                    t.base = VLC_TICK_FROM_MS(v);
245
230
                else
246
230
                    t.base = vlc_tick_from_sec(60 * v);
247
501
            }
248
110k
            else if( *psz_end == 's' )
249
34.2k
            {
250
34.2k
                t.base = vlc_tick_from_sec(v);
251
34.2k
            }
252
76.5k
            else if( *psz_end == 'h' )
253
723
            {
254
723
                t.base = vlc_tick_from_sec(v * 3600);
255
723
            }
256
75.8k
            else if( *psz_end == 'f' )
257
95
            {
258
95
                t.base = 0;
259
95
                t.frames = v;
260
95
            }
261
            //else if( *psz_end == 't' );
262
111k
        }
263
112k
    }
264
265
775k
    return t;
266
775k
}
267
268
bool tt_timings_Contains( const tt_timings_t *p_range, const tt_time_t *time )
269
987k
{
270
987k
    if( tt_time_Valid( &p_range->end ) &&
271
987k
        tt_time_Compare( &p_range->end, time ) <= 0 )
272
14.0k
        return false;
273
274
973k
    if( tt_time_Valid( &p_range->begin ) &&
275
973k
        tt_time_Compare( &p_range->begin, time ) > 0 )
276
27.4k
        return false;
277
278
946k
    return true;
279
973k
}
280
281
static void tt_textnode_Delete( tt_textnode_t *p_node )
282
56.3k
{
283
56.3k
    free( p_node->psz_text );
284
56.3k
    free( p_node );
285
56.3k
}
286
287
static void tt_node_FreeDictValue( void* p_value, void* p_obj )
288
1.18M
{
289
1.18M
    VLC_UNUSED( p_obj );
290
1.18M
    free( p_value );
291
1.18M
}
292
293
static void tt_node_Delete( tt_node_t *p_node )
294
713k
{
295
713k
    free( p_node->psz_node_name );
296
713k
    free( p_node->psz_namespace );
297
713k
    vlc_dictionary_clear( &p_node->attr_dict, tt_node_FreeDictValue, NULL );
298
713k
    free( p_node );
299
713k
}
300
301
void tt_node_RecursiveDelete( tt_node_t *p_node )
302
713k
{
303
1.46M
    for( ; p_node->p_child ; )
304
748k
    {
305
748k
        tt_basenode_t *p_child = p_node->p_child;
306
748k
        p_node->p_child = p_child->p_next;
307
308
748k
        if( p_child->i_type == TT_NODE_TYPE_TEXT )
309
56.3k
            tt_textnode_Delete( (tt_textnode_t *) p_child );
310
691k
        else
311
691k
            tt_node_RecursiveDelete( (tt_node_t *) p_child );
312
748k
    }
313
713k
    tt_node_Delete( p_node );
314
713k
}
315
316
void tt_node_RemoveAttribute( tt_node_t *p_node, const char *key )
317
0
{
318
0
    vlc_dictionary_remove_value_for_key( &p_node->attr_dict, key,
319
0
                                        tt_node_FreeDictValue, NULL );
320
0
}
321
322
int tt_node_AddAttribute( tt_node_t *p_node, const char *key, const char *value )
323
0
{
324
0
    char *p_dup = strdup( value );
325
0
    if( p_dup )
326
0
        vlc_dictionary_insert( &p_node->attr_dict, key, p_dup );
327
0
    return p_dup ? VLC_SUCCESS : VLC_EGENERIC;
328
0
}
329
330
static void tt_node_ParentAddChild( tt_node_t* p_parent, tt_basenode_t *p_child )
331
748k
{
332
748k
    tt_basenode_t **pp_node = &p_parent->p_child;
333
1.04M
    while( *pp_node != NULL )
334
292k
        pp_node = &((*pp_node)->p_next);
335
748k
    *pp_node = p_child;
336
748k
}
337
338
static tt_textnode_t *tt_textnode_NewImpl( tt_node_t *p_parent, char *psz )
339
56.3k
{
340
56.3k
    if( !psz )
341
0
        return NULL;
342
56.3k
    tt_textnode_t *p_node = calloc( 1, sizeof( *p_node ) );
343
56.3k
    if( !p_node )
344
0
    {
345
0
        free( psz );
346
0
        return NULL;
347
0
    }
348
56.3k
    p_node->psz_text = psz;
349
56.3k
    p_node->i_type = TT_NODE_TYPE_TEXT;
350
56.3k
    p_node->p_parent = p_parent;
351
56.3k
    if( p_parent )
352
56.3k
        tt_node_ParentAddChild( p_parent, (tt_basenode_t *) p_node );
353
56.3k
    return p_node;
354
56.3k
}
355
356
tt_textnode_t *tt_textnode_New( tt_node_t *p_parent, const char *psz_text )
357
56.3k
{
358
56.3k
    return tt_textnode_NewImpl( p_parent, strdup( psz_text ) );
359
56.3k
}
360
361
tt_textnode_t *tt_subtextnode_New( tt_node_t *p_parent, const char *psz_text, size_t len )
362
0
{
363
0
    return tt_textnode_NewImpl( p_parent, strndup( psz_text, len ) );
364
0
}
365
366
tt_node_t * tt_node_New( tt_node_t* p_parent,
367
                         const char* psz_node_name,
368
                         const char *psz_namespace )
369
713k
{
370
713k
    tt_node_t *p_node = calloc( 1, sizeof( *p_node ) );
371
713k
    if( !p_node )
372
0
        return NULL;
373
374
713k
    p_node->i_type = TT_NODE_TYPE_ELEMENT;
375
713k
    p_node->psz_node_name = strdup( psz_node_name );
376
713k
    const char *psz_parent_ns = tt_node_InheritNS( p_parent );
377
    /* set new namespace if not same as parent */
378
713k
    if( psz_namespace &&
379
713k
        (!psz_parent_ns || strcmp( psz_namespace, psz_parent_ns )) )
380
34.0k
        p_node->psz_namespace = strdup( psz_namespace );
381
678k
    else
382
678k
        p_node->psz_namespace = NULL;
383
713k
    if( unlikely( p_node->psz_node_name == NULL ) )
384
0
    {
385
0
        free( p_node );
386
0
        return NULL;
387
0
    }
388
713k
    vlc_dictionary_init( &p_node->attr_dict, 0 );
389
713k
    tt_time_Init( &p_node->timings.begin );
390
713k
    tt_time_Init( &p_node->timings.end );
391
713k
    tt_time_Init( &p_node->timings.dur );
392
713k
    p_node->p_parent = p_parent;
393
713k
    if( p_parent )
394
691k
        tt_node_ParentAddChild( p_parent, (tt_basenode_t *) p_node );
395
396
713k
    return p_node;
397
713k
}
398
399
tt_node_t * tt_node_NewRead( xml_reader_t* reader,
400
                             tt_namespaces_t *p_nss, tt_node_t* p_parent,
401
                             const char* psz_node_name, const char *psz_namespace )
402
713k
{
403
713k
    tt_node_t *p_node = tt_node_New( p_parent, psz_node_name, psz_namespace );
404
713k
    if( !p_node )
405
0
        return NULL;
406
407
713k
    const char* psz_value = NULL, *psz_ns = NULL;
408
713k
    for( const char* psz_key = xml_ReaderNextAttrNS( reader, &psz_value, &psz_ns );
409
1.89M
         psz_key != NULL;
410
1.18M
         psz_key = xml_ReaderNextAttrNS( reader, &psz_value, &psz_ns ) )
411
1.18M
    {
412
1.18M
        if( psz_ns && psz_key )
413
310k
        tt_namespaces_Register( p_nss, psz_key, psz_ns );
414
1.18M
        char *psz_val = strdup( psz_value );
415
1.18M
        if( psz_val )
416
1.18M
        {
417
1.18M
            vlc_dictionary_insert( &p_node->attr_dict, psz_key, psz_val );
418
419
1.18M
            if( !strcasecmp( psz_key, "begin" ) )
420
482k
                p_node->timings.begin = tt_ParseTime( psz_val );
421
703k
            else if( ! strcasecmp( psz_key, "end" ) )
422
292k
                p_node->timings.end = tt_ParseTime( psz_val );
423
411k
            else if( ! strcasecmp( psz_key, "dur" ) )
424
901
                p_node->timings.dur = tt_ParseTime( psz_val );
425
410k
            else if( ! strcasecmp( psz_key, "timeContainer" ) )
426
123
                p_node->timings.i_type = strcmp( psz_val, "seq" ) ? TT_TIMINGS_PARALLEL
427
123
                                                                  : TT_TIMINGS_SEQUENTIAL;
428
1.18M
        }
429
1.18M
    }
430
713k
    return p_node;
431
713k
}
432
#if 0
433
static int tt_node_Skip( xml_reader_t *p_reader, const char *psz_skipped )
434
{
435
    size_t i_depth = 1;
436
    const char *psz_cur;
437
438
    /* need a copy as psz_skipped would point to next node after NextNode */
439
    char *psz_skip = strdup( psz_skipped );
440
    if(!psz_skip)
441
        return VLC_EGENERIC;
442
443
    for( ;; )
444
    {
445
        int i_type = xml_ReaderNextNode( p_reader, &psz_cur );
446
        switch( i_type )
447
        {
448
            case XML_READER_STARTELEM:
449
                if( i_depth == SIZE_MAX )
450
                {
451
                    free( psz_skip );
452
                    return VLC_EGENERIC;
453
                }
454
                if( !xml_ReaderIsEmptyElement( p_reader ) )
455
                    i_depth++;
456
                break;
457
458
            case XML_READER_ENDELEM:
459
                if( !strcmp( psz_cur, psz_skip ) )
460
                {
461
                    free( psz_skip );
462
                    if( i_depth != 1 )
463
                        return VLC_EGENERIC;
464
                    return VLC_SUCCESS;
465
                }
466
                else
467
                {
468
                    if( i_depth == 1 )
469
                    {
470
                        free( psz_skip );
471
                        return VLC_EGENERIC;
472
                    }
473
                    i_depth--;
474
                }
475
                break;
476
477
            default:
478
                if( i_type <= XML_READER_NONE )
479
                {
480
                    free( psz_skip );
481
                    return VLC_EGENERIC;
482
                }
483
                break;
484
        }
485
    }
486
    vlc_assert_unreachable();
487
    return VLC_EGENERIC;
488
}
489
#endif
490
int tt_nodes_Read( xml_reader_t *p_reader, tt_namespaces_t *p_nss, tt_node_t *p_root_node )
491
21.3k
{
492
21.3k
    size_t i_depth = 0;
493
21.3k
    tt_node_t *p_node = p_root_node;
494
495
21.3k
    do
496
1.37M
    {
497
1.37M
        const char *psz_node_name, *psz_node_namespace;
498
1.37M
        int i_type = xml_ReaderNextNodeNS( p_reader, &psz_node_name, &psz_node_namespace );
499
        /* !warn read empty state now as attributes reading will **** it up */
500
1.37M
        bool b_empty = xml_ReaderIsEmptyElement( p_reader );
501
502
1.37M
        if( i_type <= XML_READER_NONE )
503
20.8k
            break;
504
505
1.35M
        switch( i_type )
506
1.35M
        {
507
0
            default:
508
0
                break;
509
510
691k
            case XML_READER_STARTELEM:
511
691k
            {
512
691k
                tt_namespaces_Register( p_nss, psz_node_name, psz_node_namespace );
513
691k
                tt_node_t *p_newnode = tt_node_NewRead( p_reader, p_nss, p_node,
514
691k
                                                        psz_node_name,
515
691k
                                                        psz_node_namespace );
516
691k
                if( !p_newnode )
517
0
                    return VLC_EGENERIC;
518
691k
                if( !b_empty )
519
634k
                {
520
634k
                    p_node = p_newnode;
521
634k
                    i_depth++;
522
634k
                }
523
691k
                break;
524
691k
            }
525
526
56.3k
            case XML_READER_TEXT:
527
56.3k
            {
528
56.3k
                tt_textnode_t *p_textnode = tt_textnode_New( p_node, psz_node_name );
529
56.3k
                VLC_UNUSED(p_textnode);
530
56.3k
            }
531
56.3k
            break;
532
533
603k
            case XML_READER_ENDELEM:
534
603k
            {
535
603k
                if( !tt_node_Match( p_node, psz_node_name, psz_node_namespace ) )
536
471
                    return VLC_EGENERIC;
537
538
602k
                if( i_depth == 0 )
539
17.8k
                {
540
17.8k
                    if( p_node != p_root_node )
541
0
                        return VLC_EGENERIC;
542
17.8k
                    break; /* END */
543
17.8k
                }
544
585k
                i_depth--;
545
585k
                p_node = p_node->p_parent;
546
585k
                break;
547
602k
            }
548
1.35M
        }
549
1.35M
    } while( 1 );
550
551
20.8k
    return VLC_SUCCESS;
552
21.3k
}
553
554
555
/* Timings storage */
556
static int tt_bsearch_searchkey_Compare( const void *key, const void *other )
557
3.61M
{
558
3.61M
    struct tt_searchkey *p_key = (struct tt_searchkey *) key;
559
3.61M
    tt_time_t time = *((tt_time_t *) other);
560
3.61M
    p_key->p_last = (tt_time_t *) other;
561
3.61M
    return tt_time_Compare( &p_key->time, &time );
562
3.61M
}
563
564
size_t tt_timings_FindLowerIndex( const tt_time_t *p_times, size_t i_times, tt_time_t time, bool *pb_found )
565
1.04M
{
566
1.04M
    size_t i_index = 0;
567
1.04M
    if( p_times )
568
1.01M
    {
569
1.01M
        struct tt_searchkey key;
570
1.01M
        key.time = time;
571
1.01M
        key.p_last = NULL;
572
573
1.01M
        tt_time_t *lookup = bsearch( &key, p_times, i_times,
574
1.01M
                                     sizeof(tt_time_t), tt_bsearch_searchkey_Compare );
575
1.01M
        if( lookup )
576
613k
            key.p_last = lookup;
577
1.01M
        *pb_found = !!lookup;
578
579
        /* Compute index from last visited */
580
1.01M
        i_index = (key.p_last - p_times);
581
1.01M
        if( tt_time_Compare( &p_times[i_index], &time ) < 0 )
582
282k
            i_index++;
583
1.01M
    }
584
20.6k
    else *pb_found = false;
585
1.04M
    return i_index;
586
1.04M
}
587
588
static void tt_bsearch_Insert( tt_time_t **pp_times, size_t *pi_times, tt_time_t time )
589
1.04M
{
590
1.04M
    bool b_exists;
591
1.04M
    size_t i_index = tt_timings_FindLowerIndex( *pp_times, *pi_times, time, &b_exists );
592
1.04M
    if( b_exists )
593
613k
        return;
594
595
427k
    if( SIZE_MAX / sizeof(tt_time_t) < (*pi_times + 1) )
596
0
        return;
597
598
427k
    tt_time_t *p_array = realloc( *pp_times, (*pi_times + 1) * sizeof(tt_time_t) );
599
427k
    if( !p_array )
600
0
        return;
601
427k
    *pp_times = p_array;
602
603
427k
    if( *pi_times > 0 )
604
406k
    {
605
406k
        memmove( &p_array[i_index + 1],
606
406k
                 &p_array[i_index],
607
406k
                 (*pi_times - i_index) * sizeof(tt_time_t) );
608
406k
    }
609
610
427k
    p_array[i_index] = time;
611
427k
    *pi_times += 1;
612
427k
}
613
614
615
/* Timings storage */
616
static void tt_timings_MergeParallel( const tt_timings_t *p_ref, tt_timings_t *p_local )
617
694k
{
618
694k
    if( tt_time_Valid( &p_local->begin ) )
619
393k
        p_local->begin = tt_time_Add( p_local->begin, p_ref->begin );
620
300k
    else
621
300k
        p_local->begin = p_ref->begin;
622
623
694k
    if( tt_time_Valid( &p_local->end ) )
624
287k
    {
625
287k
        p_local->end = tt_time_Add( p_local->end, p_ref->begin );
626
287k
    }
627
406k
    else if( tt_time_Valid( &p_local->dur ) && tt_time_Valid( &p_local->begin ) )
628
731
    {
629
731
        p_local->end = tt_time_Add( p_local->begin, p_local->dur );
630
731
    }
631
405k
    else p_local->end = p_ref->end;
632
633
    /* Enforce contained duration */
634
635
694k
    if( tt_time_Valid( &p_ref->end ) && tt_time_Compare( &p_local->end, &p_ref->end ) > 0 )
636
258k
        p_local->end = p_ref->end;
637
638
    /* Just for consistency */
639
694k
    if( tt_time_Valid( &p_local->begin ) && tt_time_Valid( &p_local->end ) )
640
346k
        p_local->dur = tt_time_Sub( p_local->end, p_local->begin );
641
694k
}
642
643
static void tt_timings_MergeSequential( const tt_timings_t *p_restrict,
644
                                       const tt_timings_t *p_prevref, tt_timings_t *p_local )
645
0
{
646
0
    if( tt_time_Valid( &p_local->begin ) )
647
0
        p_local->begin = tt_time_Add( p_local->begin, p_prevref->end );
648
0
    else
649
0
        p_local->begin = p_prevref->end;
650
651
0
    if( tt_time_Valid( &p_local->end ) )
652
0
    {
653
0
        p_local->end = tt_time_Add( p_local->end, p_prevref->end );
654
0
    }
655
0
    else if( tt_time_Valid( &p_local->dur ) && tt_time_Valid( &p_local->begin ) )
656
0
    {
657
0
        p_local->end = tt_time_Add( p_local->begin, p_local->dur );
658
0
    }
659
660
    /* Enforce contained duration */
661
0
    if( tt_time_Valid( &p_restrict->end ) && tt_time_Compare( &p_local->end, &p_restrict->end ) > 0 )
662
0
        p_local->end = p_restrict->end;
663
664
    /* Just for consistency */
665
0
    if( tt_time_Valid( &p_local->begin ) && tt_time_Valid( &p_local->end ) )
666
0
        p_local->dur = tt_time_Sub( p_local->end, p_local->begin );
667
0
}
668
669
void tt_timings_Resolve( tt_basenode_t *p_child, const tt_timings_t *p_container_timings,
670
                         tt_time_t **pp_array, size_t *pi_count )
671
714k
{
672
714k
    const tt_node_t *p_prevnode = NULL;
673
1.46M
    for(  ; p_child; p_child = p_child->p_next )
674
749k
    {
675
749k
        if( p_child->i_type != TT_NODE_TYPE_ELEMENT )
676
55.6k
            continue;
677
678
694k
        tt_node_t *p_childnode = (tt_node_t *) p_child;
679
694k
        if( p_container_timings->i_type == TT_TIMINGS_SEQUENTIAL )
680
0
        {
681
0
            if( p_prevnode == NULL ) /* First */
682
0
                tt_timings_MergeParallel( p_container_timings, &p_childnode->timings );
683
0
            else
684
0
                tt_timings_MergeSequential( p_container_timings,
685
0
                                        &p_prevnode->timings, &p_childnode->timings );
686
0
        }
687
694k
        else
688
694k
        {
689
694k
            tt_timings_MergeParallel( p_container_timings, &p_childnode->timings );
690
694k
        }
691
692
694k
        if( tt_time_Valid( &p_childnode->timings.begin ) )
693
694k
            tt_bsearch_Insert( pp_array, pi_count, p_childnode->timings.begin );
694
695
694k
        if( tt_time_Valid( &p_childnode->timings.end ) )
696
346k
            tt_bsearch_Insert( pp_array, pi_count, p_childnode->timings.end );
697
698
694k
        p_prevnode = p_childnode;
699
700
694k
        tt_timings_Resolve( p_childnode->p_child, &p_childnode->timings,
701
694k
                            pp_array, pi_count );
702
694k
    }
703
714k
}