void tt_timings_Resolve( tt_basenode_t *p_child, const tt_timings_t *p_container_timings, tt_time_t **pp_array, size_t *pi_count ) { const tt_node_t *p_prevnode = NULL; for( ; p_child; p_child = p_child->p_next ) { if( p_child->i_type != TT_NODE_TYPE_ELEMENT ) continue; tt_node_t *p_childnode = (tt_node_t *) p_child; if( p_container_timings->i_type == TT_TIMINGS_SEQUENTIAL ) { if( p_prevnode == NULL ) /* First */ tt_timings_MergeParallel( p_container_timings, &p_childnode->timings ); else tt_timings_MergeSequential( p_container_timings, &p_prevnode->timings, &p_childnode->timings ); } else { tt_timings_MergeParallel( p_container_timings, &p_childnode->timings ); } if( tt_time_Valid( &p_childnode->timings.begin ) ) tt_bsearch_Insert( pp_array, pi_count, p_childnode->timings.begin ); if( tt_time_Valid( &p_childnode->timings.end ) ) tt_bsearch_Insert( pp_array, pi_count, p_childnode->timings.end ); p_prevnode = p_childnode; tt_timings_Resolve( p_childnode->p_child, &p_childnode->timings, pp_array, pi_count ); } }
int OpenDemux( vlc_object_t* p_this ) { demux_t *p_demux = (demux_t*)p_this; demux_sys_t *p_sys; const uint8_t *p_peek; ssize_t i_peek = vlc_stream_Peek( p_demux->s, &p_peek, 2048 ); if( unlikely( i_peek <= 32 ) ) return VLC_EGENERIC; const char *psz_xml = (const char *) p_peek; size_t i_xml = i_peek; /* Try to probe without xml module/loading the full document */ char *psz_alloc = NULL; switch( GetQWBE(p_peek) ) { /* See RFC 3023 Part 4 */ case UINT64_C(0xFFFE3C003F007800): /* UTF16 BOM<? */ case UINT64_C(0xFFFE3C003F007400): /* UTF16 BOM<t */ case UINT64_C(0xFEFF003C003F0078): /* UTF16 BOM<? */ case UINT64_C(0xFEFF003C003F0074): /* UTF16 BOM<t */ psz_alloc = FromCharset( "UTF-16", p_peek, i_peek ); break; case UINT64_C(0x3C003F0078006D00): /* UTF16-LE <?xm */ case UINT64_C(0x3C003F0074007400): /* UTF16-LE <tt */ psz_alloc = FromCharset( "UTF-16LE", p_peek, i_peek ); break; case UINT64_C(0x003C003F0078006D): /* UTF16-BE <?xm */ case UINT64_C(0x003C003F00740074): /* UTF16-BE <tt */ psz_alloc = FromCharset( "UTF-16BE", p_peek, i_peek ); break; case UINT64_C(0xEFBBBF3C3F786D6C): /* UTF8 BOM<?xml */ case UINT64_C(0x3C3F786D6C207665): /* UTF8 <?xml ve */ case UINT64_C(0xEFBBBF3C74742078): /* UTF8 BOM<tt x*/ break; default: if(GetDWBE(p_peek) != UINT32_C(0x3C747420)) /* tt node without xml document marker */ return VLC_EGENERIC; } if( psz_alloc ) { psz_xml = psz_alloc; i_xml = strlen( psz_alloc ); } /* Simplified probing. Valid TTML must have a namespace declaration */ const char *psz_tt = strnstr( psz_xml, "tt ", i_xml ); if( !psz_tt || psz_tt == psz_xml || (psz_tt[-1] != ':' && psz_tt[-1] != '<') ) { free( psz_alloc ); return VLC_EGENERIC; } else { const char * const rgsz[] = { "=\"http://www.w3.org/ns/ttml\"", "=\"http://www.w3.org/2004/11/ttaf1\"", "=\"http://www.w3.org/2006/04/ttaf1\"", "=\"http://www.w3.org/2006/10/ttaf1\"", }; const char *psz_ns = NULL; for( size_t i=0; i<ARRAY_SIZE(rgsz) && !psz_ns; i++ ) { psz_ns = strnstr( psz_xml, rgsz[i], i_xml - (psz_tt - psz_xml) ); } free( psz_alloc ); if( !psz_ns ) return VLC_EGENERIC; } p_demux->p_sys = p_sys = calloc( 1, sizeof( *p_sys ) ); if( unlikely( p_sys == NULL ) ) return VLC_ENOMEM; p_sys->b_first_time = true; p_sys->temporal_extent.i_type = TT_TIMINGS_PARALLEL; tt_time_Init( &p_sys->temporal_extent.begin ); tt_time_Init( &p_sys->temporal_extent.end ); tt_time_Init( &p_sys->temporal_extent.dur ); p_sys->temporal_extent.begin.base = 0; p_sys->p_xml = xml_Create( p_demux ); if( !p_sys->p_xml ) goto error; p_sys->p_reader = xml_ReaderCreate( p_sys->p_xml, p_demux->s ); if( !p_sys->p_reader ) goto error; #ifndef TTML_DEMUX_DEBUG p_sys->p_reader->obj.flags |= OBJECT_FLAGS_QUIET; #endif if( ReadTTML( p_demux ) != VLC_SUCCESS ) goto error; tt_timings_Resolve( (tt_basenode_t *) p_sys->p_rootnode, &p_sys->temporal_extent, &p_sys->times.p_array, &p_sys->times.i_count ); #ifdef TTML_DEMUX_DEBUG { struct vlc_memstream stream; if( vlc_memstream_open( &stream ) ) goto error; tt_time_t t; tt_time_Init( &t ); tt_node_ToText( &stream, (tt_basenode_t*)p_sys->p_rootnode, &t /* invalid */ ); vlc_memstream_putc( &stream, '\0' ); if( vlc_memstream_close( &stream ) == VLC_SUCCESS ) { msg_Dbg( p_demux, "%s", stream.ptr ); free( stream.ptr ); } } #endif p_demux->pf_demux = Demux; p_demux->pf_control = Control; es_format_t fmt; es_format_Init( &fmt, SPU_ES, VLC_CODEC_TTML ); p_sys->p_es = es_out_Add( p_demux->out, &fmt ); if( !p_sys->p_es ) goto error; es_format_Clean( &fmt ); return VLC_SUCCESS; error: CloseDemux( p_demux ); return VLC_EGENERIC; }