/src/vlc/modules/codec/ttml/genttml.c
Line | Count | Source |
1 | | /***************************************************************************** |
2 | | * genttml.c : TTML formatter |
3 | | ***************************************************************************** |
4 | | * Copyright (C) 2015-2018 VLC authors and VideoLAN |
5 | | * Copyright (C) 2017-2018 VideoLabs |
6 | | * |
7 | | * Authors: Hugo Beauzée-Luyssen <hugo@beauzee.fr> |
8 | | * Sushma Reddy <sushma.reddy@research.iiit.ac.in> |
9 | | * |
10 | | * This program is free software; you can redistribute it and/or modify it |
11 | | * under the terms of the GNU Lesser General Public License as published by |
12 | | * the Free Software Foundation; either version 2.1 of the License, or |
13 | | * (at your option) any later version. |
14 | | * |
15 | | * This program is distributed in the hope that it will be useful, |
16 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 | | * GNU Lesser General Public License for more details. |
19 | | * |
20 | | * You should have received a copy of the GNU Lesser General Public License |
21 | | * along with this program; if not, write to the Free Software Foundation, |
22 | | * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. |
23 | | *****************************************************************************/ |
24 | | |
25 | | #ifdef HAVE_CONFIG_H |
26 | | # include "config.h" |
27 | | #endif |
28 | | |
29 | | //#define TTML_GEN_DEBUG |
30 | | |
31 | | #include <vlc_common.h> |
32 | | #include <vlc_strings.h> |
33 | | |
34 | | #include <assert.h> |
35 | | #include <stdlib.h> |
36 | | #include <ctype.h> |
37 | | |
38 | | #include "ttml.h" |
39 | | |
40 | | char *tt_genTiming( tt_time_t t ) |
41 | 517k | { |
42 | 517k | if( !tt_time_Valid( &t ) ) |
43 | 0 | t.base = 0; |
44 | 517k | unsigned f = t.base % CLOCK_FREQ; |
45 | 517k | t.base /= CLOCK_FREQ; |
46 | 517k | unsigned h = t.base / 3600; |
47 | 517k | unsigned m = t.base % 3600 / 60; |
48 | 517k | unsigned s = t.base % 60; |
49 | | |
50 | 517k | int i_ret; |
51 | 517k | char *psz; |
52 | 517k | if( f ) |
53 | 395k | { |
54 | 395k | const char *lz = "000000"; |
55 | 395k | const char *psz_lz = &lz[6]; |
56 | | /* add leading zeroes */ |
57 | 455k | for( unsigned i=10*f; i<CLOCK_FREQ; i *= 10 ) |
58 | 60.5k | psz_lz--; |
59 | | /* strip trailing zeroes */ |
60 | 1.75M | for( ; f > 0 && (f % 10) == 0; f /= 10 ); |
61 | 395k | i_ret = asprintf( &psz, "%02u:%02u:%02u.%s%u", |
62 | 395k | h, m, s, psz_lz, f ); |
63 | 395k | } |
64 | 121k | else if( t.frames ) |
65 | 31.7k | { |
66 | 31.7k | i_ret = asprintf( &psz, "%02u:%02u:%02u:%s%u", |
67 | 31.7k | h, m, s, t.frames < 10 ? "0" : "", t.frames ); |
68 | 31.7k | } |
69 | 90.1k | else |
70 | 90.1k | { |
71 | 90.1k | i_ret = asprintf( &psz, "%02u:%02u:%02u", |
72 | 90.1k | h, m, s ); |
73 | 90.1k | } |
74 | | |
75 | 517k | return i_ret < 0 ? NULL : psz; |
76 | 517k | } |
77 | | |
78 | | static void tt_MemstreamPutEntities( struct vlc_memstream *p_stream, const char *psz ) |
79 | 1.60M | { |
80 | 1.60M | char *psz_entities = vlc_xml_encode( psz ); |
81 | 1.60M | if( psz_entities ) |
82 | 1.60M | { |
83 | 1.60M | vlc_memstream_puts( p_stream, psz_entities ); |
84 | 1.60M | free( psz_entities ); |
85 | 1.60M | } |
86 | 1.60M | } |
87 | | |
88 | | void tt_node_AttributesToText( struct vlc_memstream *p_stream, const tt_node_t* p_node ) |
89 | 564k | { |
90 | 564k | bool b_timed_node = false; |
91 | 564k | const vlc_dictionary_t* p_attr_dict = &p_node->attr_dict; |
92 | 1.29M | for (size_t i = 0; i < p_attr_dict->i_size; ++i) |
93 | 726k | { |
94 | 726k | for ( vlc_dictionary_entry_t* p_entry = p_attr_dict->p_entries[i]; |
95 | 1.56M | p_entry != NULL; p_entry = p_entry->p_next ) |
96 | 840k | { |
97 | 840k | const char *psz_value = NULL; |
98 | | |
99 | 840k | if( !strcmp(p_entry->psz_key, "begin") || |
100 | 591k | !strcmp(p_entry->psz_key, "end") || |
101 | 437k | !strcmp(p_entry->psz_key, "dur") ) |
102 | 403k | { |
103 | 403k | b_timed_node = true; |
104 | | /* will remove duration */ |
105 | 403k | continue; |
106 | 403k | } |
107 | 436k | else if( !strcmp(p_entry->psz_key, "timeContainer") ) |
108 | 388 | { |
109 | | /* also remove sequential timings info (all abs now) */ |
110 | 388 | continue; |
111 | 388 | } |
112 | 436k | else |
113 | 436k | { |
114 | 436k | psz_value = p_entry->p_value; |
115 | 436k | } |
116 | | |
117 | 436k | if( psz_value == NULL ) |
118 | 0 | continue; |
119 | | |
120 | 436k | vlc_memstream_printf( p_stream, " %s=\"", p_entry->psz_key ); |
121 | 436k | tt_MemstreamPutEntities( p_stream, psz_value ); |
122 | 436k | vlc_memstream_putc( p_stream, '"' ); |
123 | 436k | } |
124 | 726k | } |
125 | | |
126 | 564k | if( b_timed_node ) |
127 | 290k | { |
128 | 290k | if( tt_time_Valid( &p_node->timings.begin ) ) |
129 | 289k | { |
130 | 289k | char *psz = tt_genTiming( p_node->timings.begin ); |
131 | 289k | vlc_memstream_printf( p_stream, " begin=\"%s\"", psz ); |
132 | 289k | free( psz ); |
133 | 289k | } |
134 | | |
135 | 290k | if( tt_time_Valid( &p_node->timings.end ) ) |
136 | 227k | { |
137 | 227k | char *psz = tt_genTiming( p_node->timings.end ); |
138 | 227k | vlc_memstream_printf( p_stream, " end=\"%s\"", psz ); |
139 | 227k | free( psz ); |
140 | 227k | } |
141 | 290k | } |
142 | 564k | } |
143 | | |
144 | | void tt_node_ToText( struct vlc_memstream *p_stream, const tt_basenode_t *p_basenode, |
145 | | const tt_time_t *playbacktime ) |
146 | 652k | { |
147 | 652k | if( p_basenode->i_type == TT_NODE_TYPE_ELEMENT ) |
148 | 586k | { |
149 | 586k | const tt_node_t *p_node = (const tt_node_t *) p_basenode; |
150 | | |
151 | 586k | if( tt_time_Valid( playbacktime ) && |
152 | 586k | !tt_timings_Contains( &p_node->timings, playbacktime ) ) |
153 | 22.7k | return; |
154 | | |
155 | 564k | vlc_memstream_putc( p_stream, '<' ); |
156 | 564k | tt_MemstreamPutEntities( p_stream, p_node->psz_node_name ); |
157 | | |
158 | 564k | tt_node_AttributesToText( p_stream, p_node ); |
159 | | |
160 | 564k | if( tt_node_HasChild( p_node ) ) |
161 | 536k | { |
162 | 536k | vlc_memstream_putc( p_stream, '>' ); |
163 | | |
164 | | #ifdef TTML_DEMUX_DEBUG |
165 | | vlc_memstream_printf( p_stream, "<!-- starts %ld ends %ld -->", |
166 | | tt_time_Convert( &p_node->timings.begin ), |
167 | | tt_time_Convert( &p_node->timings.end ) ); |
168 | | #endif |
169 | | |
170 | 536k | for( const tt_basenode_t *p_child = p_node->p_child; |
171 | 1.16M | p_child; p_child = p_child->p_next ) |
172 | 629k | { |
173 | 629k | tt_node_ToText( p_stream, p_child, playbacktime ); |
174 | 629k | } |
175 | | |
176 | 536k | vlc_memstream_puts( p_stream, "</" ); |
177 | 536k | tt_MemstreamPutEntities( p_stream, p_node->psz_node_name ); |
178 | 536k | vlc_memstream_putc( p_stream, '>' ); |
179 | 536k | } |
180 | 28.0k | else |
181 | 28.0k | vlc_memstream_puts( p_stream, "/>" ); |
182 | 564k | } |
183 | 65.1k | else |
184 | 65.1k | { |
185 | 65.1k | const tt_textnode_t *p_textnode = (const tt_textnode_t *) p_basenode; |
186 | 65.1k | tt_MemstreamPutEntities( p_stream, p_textnode->psz_text ); |
187 | 65.1k | } |
188 | 652k | } |