/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 | 404k | { |
42 | 404k | if( !tt_time_Valid( &t ) ) |
43 | 0 | t.base = 0; |
44 | 404k | unsigned f = t.base % CLOCK_FREQ; |
45 | 404k | t.base /= CLOCK_FREQ; |
46 | 404k | unsigned h = t.base / 3600; |
47 | 404k | unsigned m = t.base % 3600 / 60; |
48 | 404k | unsigned s = t.base % 60; |
49 | | |
50 | 404k | int i_ret; |
51 | 404k | char *psz; |
52 | 404k | if( f ) |
53 | 359k | { |
54 | 359k | const char *lz = "000000"; |
55 | 359k | const char *psz_lz = &lz[6]; |
56 | | /* add leading zeroes */ |
57 | 379k | for( unsigned i=10*f; i<CLOCK_FREQ; i *= 10 ) |
58 | 20.1k | psz_lz--; |
59 | | /* strip trailing zeroes */ |
60 | 1.31M | for( ; f > 0 && (f % 10) == 0; f /= 10 ); |
61 | 359k | i_ret = asprintf( &psz, "%02u:%02u:%02u.%s%u", |
62 | 359k | h, m, s, psz_lz, f ); |
63 | 359k | } |
64 | 44.8k | else if( t.frames ) |
65 | 0 | { |
66 | 0 | i_ret = asprintf( &psz, "%02u:%02u:%02u:%s%u", |
67 | 0 | h, m, s, t.frames < 10 ? "0" : "", t.frames ); |
68 | 0 | } |
69 | 44.8k | else |
70 | 44.8k | { |
71 | 44.8k | i_ret = asprintf( &psz, "%02u:%02u:%02u", |
72 | 44.8k | h, m, s ); |
73 | 44.8k | } |
74 | | |
75 | 404k | return i_ret < 0 ? NULL : psz; |
76 | 404k | } |
77 | | |
78 | | static void tt_MemstreamPutEntities( struct vlc_memstream *p_stream, const char *psz ) |
79 | 1.13M | { |
80 | 1.13M | char *psz_entities = vlc_xml_encode( psz ); |
81 | 1.13M | if( psz_entities ) |
82 | 1.13M | { |
83 | 1.13M | vlc_memstream_puts( p_stream, psz_entities ); |
84 | 1.13M | free( psz_entities ); |
85 | 1.13M | } |
86 | 1.13M | } |
87 | | |
88 | | void tt_node_AttributesToText( struct vlc_memstream *p_stream, const tt_node_t* p_node ) |
89 | 424k | { |
90 | 424k | bool b_timed_node = false; |
91 | 424k | const vlc_dictionary_t* p_attr_dict = &p_node->attr_dict; |
92 | 838k | for (size_t i = 0; i < p_attr_dict->i_size; ++i) |
93 | 413k | { |
94 | 413k | for ( vlc_dictionary_entry_t* p_entry = p_attr_dict->p_entries[i]; |
95 | 1.18M | p_entry != NULL; p_entry = p_entry->p_next ) |
96 | 767k | { |
97 | 767k | const char *psz_value = NULL; |
98 | | |
99 | 767k | if( !strcmp(p_entry->psz_key, "begin") || |
100 | 499k | !strcmp(p_entry->psz_key, "end") || |
101 | 287k | !strcmp(p_entry->psz_key, "dur") ) |
102 | 480k | { |
103 | 480k | b_timed_node = true; |
104 | | /* will remove duration */ |
105 | 480k | continue; |
106 | 480k | } |
107 | 287k | else if( !strcmp(p_entry->psz_key, "timeContainer") ) |
108 | 295 | { |
109 | | /* also remove sequential timings info (all abs now) */ |
110 | 295 | continue; |
111 | 295 | } |
112 | 286k | else |
113 | 286k | { |
114 | 286k | psz_value = p_entry->p_value; |
115 | 286k | } |
116 | | |
117 | 286k | if( psz_value == NULL ) |
118 | 0 | continue; |
119 | | |
120 | 286k | vlc_memstream_printf( p_stream, " %s=\"", p_entry->psz_key ); |
121 | 286k | tt_MemstreamPutEntities( p_stream, psz_value ); |
122 | 286k | vlc_memstream_putc( p_stream, '"' ); |
123 | 286k | } |
124 | 413k | } |
125 | | |
126 | 424k | if( b_timed_node ) |
127 | 273k | { |
128 | 273k | if( tt_time_Valid( &p_node->timings.begin ) ) |
129 | 273k | { |
130 | 273k | char *psz = tt_genTiming( p_node->timings.begin ); |
131 | 273k | vlc_memstream_printf( p_stream, " begin=\"%s\"", psz ); |
132 | 273k | free( psz ); |
133 | 273k | } |
134 | | |
135 | 273k | if( tt_time_Valid( &p_node->timings.end ) ) |
136 | 130k | { |
137 | 130k | char *psz = tt_genTiming( p_node->timings.end ); |
138 | 130k | vlc_memstream_printf( p_stream, " end=\"%s\"", psz ); |
139 | 130k | free( psz ); |
140 | 130k | } |
141 | 273k | } |
142 | 424k | } |
143 | | |
144 | | void tt_node_ToText( struct vlc_memstream *p_stream, const tt_basenode_t *p_basenode, |
145 | | const tt_time_t *playbacktime ) |
146 | 486k | { |
147 | 486k | if( p_basenode->i_type == TT_NODE_TYPE_ELEMENT ) |
148 | 449k | { |
149 | 449k | const tt_node_t *p_node = (const tt_node_t *) p_basenode; |
150 | | |
151 | 449k | if( tt_time_Valid( playbacktime ) && |
152 | 449k | !tt_timings_Contains( &p_node->timings, playbacktime ) ) |
153 | 25.1k | return; |
154 | | |
155 | 424k | vlc_memstream_putc( p_stream, '<' ); |
156 | 424k | tt_MemstreamPutEntities( p_stream, p_node->psz_node_name ); |
157 | | |
158 | 424k | tt_node_AttributesToText( p_stream, p_node ); |
159 | | |
160 | 424k | if( tt_node_HasChild( p_node ) ) |
161 | 390k | { |
162 | 390k | 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 | 390k | for( const tt_basenode_t *p_child = p_node->p_child; |
171 | 859k | p_child; p_child = p_child->p_next ) |
172 | 468k | { |
173 | 468k | tt_node_ToText( p_stream, p_child, playbacktime ); |
174 | 468k | } |
175 | | |
176 | 390k | vlc_memstream_puts( p_stream, "</" ); |
177 | 390k | tt_MemstreamPutEntities( p_stream, p_node->psz_node_name ); |
178 | 390k | vlc_memstream_putc( p_stream, '>' ); |
179 | 390k | } |
180 | 33.7k | else |
181 | 33.7k | vlc_memstream_puts( p_stream, "/>" ); |
182 | 424k | } |
183 | 36.8k | else |
184 | 36.8k | { |
185 | 36.8k | const tt_textnode_t *p_textnode = (const tt_textnode_t *) p_basenode; |
186 | 36.8k | tt_MemstreamPutEntities( p_stream, p_textnode->psz_text ); |
187 | 36.8k | } |
188 | 486k | } |