Coverage Report

Created: 2026-02-26 08:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/vlc/modules/codec/ttml/ttml.c
Line
Count
Source
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
29.2k
{
77
29.2k
    struct tt_namespace_s *ns;
78
29.2k
    vlc_list_foreach( ns, &nss->nodes, links )
79
50.4k
    {
80
50.4k
        free( ns->psz_prefix );
81
50.4k
        free( ns->psz_uri );
82
50.4k
        free( ns );
83
50.4k
    }
84
29.2k
}
85
86
void tt_namespaces_Init( tt_namespaces_t *nss )
87
29.2k
{
88
29.2k
    vlc_list_init( &nss->nodes );
89
29.2k
}
90
91
const char * tt_namespaces_GetURI( const tt_namespaces_t *nss,
92
                                   const char *psz_qn )
93
583k
{
94
583k
    const struct tt_namespace_s *ns;
95
583k
    vlc_list_foreach_const( ns, &nss->nodes, links )
96
2.08M
    {
97
        /* compares prefixed name against raw prefix */
98
2.08M
        for( size_t i=0; ; i++ )
99
3.67M
        {
100
3.67M
            if( ns->psz_prefix[i] == psz_qn[i] )
101
1.60M
            {
102
1.60M
                if( psz_qn[i] == '\0' )
103
13.3k
                    return ns->psz_uri;
104
1.60M
            }
105
2.07M
            else
106
2.07M
            {
107
2.07M
                if( ns->psz_prefix[i] == '\0' && psz_qn[i] == ':' )
108
280k
                    return ns->psz_uri;
109
1.79M
                else
110
1.79M
                    break;
111
2.07M
            }
112
3.67M
        }
113
2.08M
    }
114
289k
    return NULL;
115
583k
}
116
117
const char * tt_namespaces_GetPrefix( const tt_namespaces_t *nss,
118
                                      const char *psz_uri )
119
684k
{
120
684k
    const struct tt_namespace_s *ns;
121
684k
    vlc_list_foreach_const( ns, &nss->nodes, links )
122
2.11M
    {
123
2.11M
        if( !strcmp( ns->psz_uri, psz_uri ) )
124
605k
            return ns->psz_prefix;
125
2.11M
    }
126
79.0k
    return NULL;
127
684k
}
128
129
void tt_namespaces_Register( tt_namespaces_t *nss, const char *psz_prefix,
130
                             const char *psz_uri )
131
603k
{
132
603k
    if( !psz_uri || tt_namespaces_GetPrefix( nss, psz_uri ) )
133
552k
        return;
134
50.4k
    struct tt_namespace_s *ns = malloc(sizeof(*ns));
135
50.4k
    if( ns )
136
50.4k
    {
137
50.4k
        const char *sep = strchr( psz_prefix, ':' );
138
50.4k
        if( sep )
139
27.6k
            ns->psz_prefix = strndup( psz_prefix, sep - psz_prefix );
140
22.7k
        else
141
22.7k
            ns->psz_prefix = strdup("");
142
50.4k
        ns->psz_uri = strdup( psz_uri );
143
50.4k
        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
50.4k
        vlc_list_append( &ns->links, &nss->nodes );
151
50.4k
    }
152
50.4k
}
153
154
static const char * tt_node_InheritNS( const tt_node_t *p_node )
155
1.05M
{
156
32.0M
    for( ; p_node ; p_node = p_node->p_parent )
157
32.0M
    {
158
32.0M
        if( p_node->psz_namespace )
159
966k
            return p_node->psz_namespace;
160
32.0M
    }
161
87.6k
    return NULL;
162
1.05M
}
163
164
bool tt_node_Match( const tt_node_t *p_node, const char *psz_name, const char *psz_namespace )
165
9.29M
{
166
    /* compare local part first (should have less chars) */
167
9.29M
    const char *psz_nodelocal = tt_LocalName( p_node->psz_node_name );
168
9.29M
    const char *psz_namelocal = tt_LocalName( psz_name );
169
9.29M
    if( strcmp( psz_namelocal, psz_nodelocal ) )
170
8.71M
        return false;
171
172
581k
    const char *psz_nodens = p_node->psz_namespace;
173
581k
    if( !psz_nodens )
174
547k
        psz_nodens = tt_node_InheritNS( p_node->p_parent );
175
581k
    if( psz_namespace && psz_nodens )
176
548k
        return !strcmp( psz_namespace, psz_nodens );
177
33.0k
    return !!psz_namespace == !!psz_nodens;
178
581k
}
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
2.37M
{
183
2.37M
    const void *value;
184
2.37M
    char *alloc = NULL;
185
2.37M
    if( psz_namespace )
186
127k
    {
187
127k
        const char *psz_prefix = tt_namespaces_GetPrefix( p_nss, psz_namespace );
188
127k
        if( psz_prefix == NULL ||
189
127k
            asprintf( &alloc, "%s:%s", psz_prefix, psz_name ) < 1 )
190
28.6k
            return NULL;
191
99.2k
        psz_name = alloc;
192
99.2k
    }
193
2.35M
    value = vlc_dictionary_value_for_key( &p_node->attr_dict, psz_name );
194
2.35M
    free( alloc );
195
2.35M
    return value != kVLCDictionaryNotFound ? (const char *)value : NULL;
196
2.37M
}
197
198
bool tt_node_HasChild( const tt_node_t *p_node )
199
454k
{
200
454k
    return p_node->p_child;
201
454k
}
202
203
static inline bool tt_ScanReset( unsigned *a, unsigned *b, unsigned *c,
204
                                 char *d,  unsigned *e )
205
381k
{
206
381k
    *a = *b = *c = *d = *e = 0;
207
381k
    return false;
208
381k
}
209
210
static tt_time_t tt_ParseTime( const char *s )
211
453k
{
212
453k
    tt_time_t t = {-1, 0};
213
453k
    unsigned h1 = 0, m1 = 0, s1 = 0, d1 = 0;
214
453k
    char c = 0;
215
216
    /* Clock time */
217
453k
    if( sscanf( s, "%u:%2u:%2u%c%u",     &h1, &m1, &s1, &c, &d1 ) == 5 ||
218
217k
                           tt_ScanReset( &h1, &m1, &s1, &c, &d1 )      ||
219
217k
        sscanf( s, "%u:%2u:%2u",         &h1, &m1, &s1          ) == 3 ||
220
163k
                           tt_ScanReset( &h1, &m1, &s1, &c, &d1 ) )
221
290k
    {
222
290k
        t.base = vlc_tick_from_sec(h1 * 3600 + m1 * 60 + s1);
223
290k
        if( c == '.' && d1 > 0 )
224
214k
        {
225
214k
            unsigned i_den = 1;
226
812k
            for( const char *p = strchr( s, '.' ) + 1; *p && (i_den < UINT_MAX / 10); p++ )
227
597k
                i_den *= 10;
228
214k
            t.base += vlc_tick_from_samples(d1, i_den);
229
214k
        }
230
75.7k
        else if( c == ':' )
231
13.7k
        {
232
13.7k
            t.frames = d1;
233
13.7k
        }
234
290k
    }
235
163k
    else /* Offset Time */
236
163k
    {
237
163k
        char *psz_end = (char *) s;
238
163k
        double v = vlc_strtod_c( s, &psz_end );
239
163k
        if( psz_end != s && *psz_end )
240
156k
        {
241
156k
            if( *psz_end == 'm' )
242
3.45k
            {
243
3.45k
                if( *(psz_end + 1) == 's' )
244
2.68k
                    t.base = VLC_TICK_FROM_MS(v);
245
775
                else
246
775
                    t.base = vlc_tick_from_sec(60 * v);
247
3.45k
            }
248
152k
            else if( *psz_end == 's' )
249
131k
            {
250
131k
                t.base = vlc_tick_from_sec(v);
251
131k
            }
252
21.5k
            else if( *psz_end == 'h' )
253
1.23k
            {
254
1.23k
                t.base = vlc_tick_from_sec(v * 3600);
255
1.23k
            }
256
20.3k
            else if( *psz_end == 'f' )
257
12.1k
            {
258
12.1k
                t.base = 0;
259
12.1k
                t.frames = v;
260
12.1k
            }
261
            //else if( *psz_end == 't' );
262
156k
        }
263
163k
    }
264
265
453k
    return t;
266
453k
}
267
268
bool tt_timings_Contains( const tt_timings_t *p_range, const tt_time_t *time )
269
851k
{
270
851k
    if( tt_time_Valid( &p_range->end ) &&
271
575k
        tt_time_Compare( &p_range->end, time ) <= 0 )
272
2.84k
        return false;
273
274
848k
    if( tt_time_Valid( &p_range->begin ) &&
275
843k
        tt_time_Compare( &p_range->begin, time ) > 0 )
276
27.4k
        return false;
277
278
820k
    return true;
279
848k
}
280
281
static void tt_textnode_Delete( tt_textnode_t *p_node )
282
68.1k
{
283
68.1k
    free( p_node->psz_text );
284
68.1k
    free( p_node );
285
68.1k
}
286
287
static void tt_node_FreeDictValue( void* p_value, void* p_obj )
288
643k
{
289
643k
    VLC_UNUSED( p_obj );
290
643k
    free( p_value );
291
643k
}
292
293
static void tt_node_Delete( tt_node_t *p_node )
294
507k
{
295
507k
    free( p_node->psz_node_name );
296
507k
    free( p_node->psz_namespace );
297
507k
    vlc_dictionary_clear( &p_node->attr_dict, tt_node_FreeDictValue, NULL );
298
507k
    free( p_node );
299
507k
}
300
301
void tt_node_RecursiveDelete( tt_node_t *p_node )
302
507k
{
303
1.06M
    for( ; p_node->p_child ; )
304
560k
    {
305
560k
        tt_basenode_t *p_child = p_node->p_child;
306
560k
        p_node->p_child = p_child->p_next;
307
308
560k
        if( p_child->i_type == TT_NODE_TYPE_TEXT )
309
68.1k
            tt_textnode_Delete( (tt_textnode_t *) p_child );
310
492k
        else
311
492k
            tt_node_RecursiveDelete( (tt_node_t *) p_child );
312
560k
    }
313
507k
    tt_node_Delete( p_node );
314
507k
}
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
560k
{
332
560k
    tt_basenode_t **pp_node = &p_parent->p_child;
333
718k
    while( *pp_node != NULL )
334
157k
        pp_node = &((*pp_node)->p_next);
335
560k
    *pp_node = p_child;
336
560k
}
337
338
static tt_textnode_t *tt_textnode_NewImpl( tt_node_t *p_parent, char *psz )
339
68.1k
{
340
68.1k
    if( !psz )
341
0
        return NULL;
342
68.1k
    tt_textnode_t *p_node = calloc( 1, sizeof( *p_node ) );
343
68.1k
    if( !p_node )
344
0
    {
345
0
        free( psz );
346
0
        return NULL;
347
0
    }
348
68.1k
    p_node->psz_text = psz;
349
68.1k
    p_node->i_type = TT_NODE_TYPE_TEXT;
350
68.1k
    p_node->p_parent = p_parent;
351
68.1k
    if( p_parent )
352
68.1k
        tt_node_ParentAddChild( p_parent, (tt_basenode_t *) p_node );
353
68.1k
    return p_node;
354
68.1k
}
355
356
tt_textnode_t *tt_textnode_New( tt_node_t *p_parent, const char *psz_text )
357
68.1k
{
358
68.1k
    return tt_textnode_NewImpl( p_parent, strdup( psz_text ) );
359
68.1k
}
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
507k
{
370
507k
    tt_node_t *p_node = calloc( 1, sizeof( *p_node ) );
371
507k
    if( !p_node )
372
0
        return NULL;
373
374
507k
    p_node->i_type = TT_NODE_TYPE_ELEMENT;
375
507k
    p_node->psz_node_name = strdup( psz_node_name );
376
507k
    const char *psz_parent_ns = tt_node_InheritNS( p_parent );
377
    /* set new namespace if not same as parent */
378
507k
    if( psz_namespace &&
379
458k
        (!psz_parent_ns || strcmp( psz_namespace, psz_parent_ns )) )
380
22.8k
        p_node->psz_namespace = strdup( psz_namespace );
381
484k
    else
382
484k
        p_node->psz_namespace = NULL;
383
507k
    if( unlikely( p_node->psz_node_name == NULL ) )
384
0
    {
385
0
        free( p_node );
386
0
        return NULL;
387
0
    }
388
507k
    vlc_dictionary_init( &p_node->attr_dict, 0 );
389
507k
    tt_time_Init( &p_node->timings.begin );
390
507k
    tt_time_Init( &p_node->timings.end );
391
507k
    tt_time_Init( &p_node->timings.dur );
392
507k
    p_node->p_parent = p_parent;
393
507k
    if( p_parent )
394
492k
        tt_node_ParentAddChild( p_parent, (tt_basenode_t *) p_node );
395
396
507k
    return p_node;
397
507k
}
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
507k
{
403
507k
    tt_node_t *p_node = tt_node_New( p_parent, psz_node_name, psz_namespace );
404
507k
    if( !p_node )
405
0
        return NULL;
406
407
507k
    const char* psz_value = NULL, *psz_ns = NULL;
408
507k
    for( const char* psz_key = xml_ReaderNextAttrNS( reader, &psz_value, &psz_ns );
409
1.15M
         psz_key != NULL;
410
643k
         psz_key = xml_ReaderNextAttrNS( reader, &psz_value, &psz_ns ) )
411
643k
    {
412
643k
        if( psz_ns && psz_key )
413
110k
        tt_namespaces_Register( p_nss, psz_key, psz_ns );
414
643k
        char *psz_val = strdup( psz_value );
415
643k
        if( psz_val )
416
643k
        {
417
643k
            vlc_dictionary_insert( &p_node->attr_dict, psz_key, psz_val );
418
419
643k
            if( !strcasecmp( psz_key, "begin" ) )
420
333k
                p_node->timings.begin = tt_ParseTime( psz_val );
421
309k
            else if( ! strcasecmp( psz_key, "end" ) )
422
118k
                p_node->timings.end = tt_ParseTime( psz_val );
423
191k
            else if( ! strcasecmp( psz_key, "dur" ) )
424
1.78k
                p_node->timings.dur = tt_ParseTime( psz_val );
425
189k
            else if( ! strcasecmp( psz_key, "timeContainer" ) )
426
1.77k
                p_node->timings.i_type = strcmp( psz_val, "seq" ) ? TT_TIMINGS_PARALLEL
427
1.77k
                                                                  : TT_TIMINGS_SEQUENTIAL;
428
643k
        }
429
643k
    }
430
507k
    return p_node;
431
507k
}
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
14.2k
{
492
14.2k
    size_t i_depth = 0;
493
14.2k
    tt_node_t *p_node = p_root_node;
494
495
14.2k
    do
496
1.00M
    {
497
1.00M
        const char *psz_node_name, *psz_node_namespace;
498
1.00M
        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.00M
        bool b_empty = xml_ReaderIsEmptyElement( p_reader );
501
502
1.00M
        if( i_type <= XML_READER_NONE )
503
13.7k
            break;
504
505
990k
        switch( i_type )
506
990k
        {
507
0
            default:
508
0
                break;
509
510
492k
            case XML_READER_STARTELEM:
511
492k
            {
512
492k
                tt_namespaces_Register( p_nss, psz_node_name, psz_node_namespace );
513
492k
                tt_node_t *p_newnode = tt_node_NewRead( p_reader, p_nss, p_node,
514
492k
                                                        psz_node_name,
515
492k
                                                        psz_node_namespace );
516
492k
                if( !p_newnode )
517
0
                    return VLC_EGENERIC;
518
492k
                if( !b_empty )
519
461k
                {
520
461k
                    p_node = p_newnode;
521
461k
                    i_depth++;
522
461k
                }
523
492k
                break;
524
492k
            }
525
526
68.1k
            case XML_READER_TEXT:
527
68.1k
            {
528
68.1k
                tt_textnode_t *p_textnode = tt_textnode_New( p_node, psz_node_name );
529
68.1k
                VLC_UNUSED(p_textnode);
530
68.1k
            }
531
68.1k
            break;
532
533
429k
            case XML_READER_ENDELEM:
534
429k
            {
535
429k
                if( !tt_node_Match( p_node, psz_node_name, psz_node_namespace ) )
536
520
                    return VLC_EGENERIC;
537
538
429k
                if( i_depth == 0 )
539
12.0k
                {
540
12.0k
                    if( p_node != p_root_node )
541
0
                        return VLC_EGENERIC;
542
12.0k
                    break; /* END */
543
12.0k
                }
544
417k
                i_depth--;
545
417k
                p_node = p_node->p_parent;
546
417k
                break;
547
429k
            }
548
990k
        }
549
990k
    } while( 1 );
550
551
13.7k
    return VLC_SUCCESS;
552
14.2k
}
553
554
555
/* Timings storage */
556
static int tt_bsearch_searchkey_Compare( const void *key, const void *other )
557
2.75M
{
558
2.75M
    struct tt_searchkey *p_key = (struct tt_searchkey *) key;
559
2.75M
    tt_time_t time = *((tt_time_t *) other);
560
2.75M
    p_key->p_last = (tt_time_t *) other;
561
2.75M
    return tt_time_Compare( &p_key->time, &time );
562
2.75M
}
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
772k
{
566
772k
    size_t i_index = 0;
567
772k
    if( p_times )
568
759k
    {
569
759k
        struct tt_searchkey key;
570
759k
        key.time = time;
571
759k
        key.p_last = NULL;
572
573
759k
        tt_time_t *lookup = bsearch( &key, p_times, i_times,
574
759k
                                     sizeof(tt_time_t), tt_bsearch_searchkey_Compare );
575
759k
        if( lookup )
576
446k
            key.p_last = lookup;
577
759k
        *pb_found = !!lookup;
578
579
        /* Compute index from last visited */
580
759k
        i_index = (key.p_last - p_times);
581
759k
        if( tt_time_Compare( &p_times[i_index], &time ) < 0 )
582
194k
            i_index++;
583
759k
    }
584
13.7k
    else *pb_found = false;
585
772k
    return i_index;
586
772k
}
587
588
static void tt_bsearch_Insert( tt_time_t **pp_times, size_t *pi_times, tt_time_t time )
589
772k
{
590
772k
    bool b_exists;
591
772k
    size_t i_index = tt_timings_FindLowerIndex( *pp_times, *pi_times, time, &b_exists );
592
772k
    if( b_exists )
593
446k
        return;
594
595
326k
    if( SIZE_MAX / sizeof(tt_time_t) < (*pi_times + 1) )
596
0
        return;
597
598
326k
    tt_time_t *p_array = realloc( *pp_times, (*pi_times + 1) * sizeof(tt_time_t) );
599
326k
    if( !p_array )
600
0
        return;
601
326k
    *pp_times = p_array;
602
603
326k
    if( *pi_times > 0 )
604
312k
    {
605
312k
        memmove( &p_array[i_index + 1],
606
312k
                 &p_array[i_index],
607
312k
                 (*pi_times - i_index) * sizeof(tt_time_t) );
608
312k
    }
609
610
326k
    p_array[i_index] = time;
611
326k
    *pi_times += 1;
612
326k
}
613
614
615
/* Timings storage */
616
static void tt_timings_MergeParallel( const tt_timings_t *p_ref, tt_timings_t *p_local )
617
484k
{
618
484k
    if( tt_time_Valid( &p_local->begin ) )
619
305k
        p_local->begin = tt_time_Add( p_local->begin, p_ref->begin );
620
178k
    else
621
178k
        p_local->begin = p_ref->begin;
622
623
484k
    if( tt_time_Valid( &p_local->end ) )
624
112k
    {
625
112k
        p_local->end = tt_time_Add( p_local->end, p_ref->begin );
626
112k
    }
627
371k
    else if( tt_time_Valid( &p_local->dur ) && tt_time_Valid( &p_local->begin ) )
628
1.20k
    {
629
1.20k
        p_local->end = tt_time_Add( p_local->begin, p_local->dur );
630
1.20k
    }
631
370k
    else p_local->end = p_ref->end;
632
633
    /* Enforce contained duration */
634
635
484k
    if( tt_time_Valid( &p_ref->end ) && tt_time_Compare( &p_local->end, &p_ref->end ) > 0 )
636
93.9k
        p_local->end = p_ref->end;
637
638
    /* Just for consistency */
639
484k
    if( tt_time_Valid( &p_local->begin ) && tt_time_Valid( &p_local->end ) )
640
287k
        p_local->dur = tt_time_Sub( p_local->end, p_local->begin );
641
484k
}
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
4.46k
{
646
4.46k
    if( tt_time_Valid( &p_local->begin ) )
647
653
        p_local->begin = tt_time_Add( p_local->begin, p_prevref->end );
648
3.81k
    else
649
3.81k
        p_local->begin = p_prevref->end;
650
651
4.46k
    if( tt_time_Valid( &p_local->end ) )
652
413
    {
653
413
        p_local->end = tt_time_Add( p_local->end, p_prevref->end );
654
413
    }
655
4.05k
    else if( tt_time_Valid( &p_local->dur ) && tt_time_Valid( &p_local->begin ) )
656
109
    {
657
109
        p_local->end = tt_time_Add( p_local->begin, p_local->dur );
658
109
    }
659
660
    /* Enforce contained duration */
661
4.46k
    if( tt_time_Valid( &p_restrict->end ) && tt_time_Compare( &p_local->end, &p_restrict->end ) > 0 )
662
1.41k
        p_local->end = p_restrict->end;
663
664
    /* Just for consistency */
665
4.46k
    if( tt_time_Valid( &p_local->begin ) && tt_time_Valid( &p_local->end ) )
666
1.47k
        p_local->dur = tt_time_Sub( p_local->end, p_local->begin );
667
4.46k
}
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
502k
{
672
502k
    const tt_node_t *p_prevnode = NULL;
673
1.05M
    for(  ; p_child; p_child = p_child->p_next )
674
555k
    {
675
555k
        if( p_child->i_type != TT_NODE_TYPE_ELEMENT )
676
66.6k
            continue;
677
678
488k
        tt_node_t *p_childnode = (tt_node_t *) p_child;
679
488k
        if( p_container_timings->i_type == TT_TIMINGS_SEQUENTIAL )
680
5.93k
        {
681
5.93k
            if( p_prevnode == NULL ) /* First */
682
1.46k
                tt_timings_MergeParallel( p_container_timings, &p_childnode->timings );
683
4.46k
            else
684
4.46k
                tt_timings_MergeSequential( p_container_timings,
685
4.46k
                                        &p_prevnode->timings, &p_childnode->timings );
686
5.93k
        }
687
482k
        else
688
482k
        {
689
482k
            tt_timings_MergeParallel( p_container_timings, &p_childnode->timings );
690
482k
        }
691
692
488k
        if( tt_time_Valid( &p_childnode->timings.begin ) )
693
484k
            tt_bsearch_Insert( pp_array, pi_count, p_childnode->timings.begin );
694
695
488k
        if( tt_time_Valid( &p_childnode->timings.end ) )
696
288k
            tt_bsearch_Insert( pp_array, pi_count, p_childnode->timings.end );
697
698
488k
        p_prevnode = p_childnode;
699
700
488k
        tt_timings_Resolve( p_childnode->p_child, &p_childnode->timings,
701
488k
                            pp_array, pi_count );
702
488k
    }
703
502k
}