/src/vlc/modules/demux/stl.c
Line | Count | Source |
1 | | /***************************************************************************** |
2 | | * stl.c: EBU STL demuxer |
3 | | ***************************************************************************** |
4 | | * Copyright (C) 2010 Laurent Aimar |
5 | | * |
6 | | * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org> |
7 | | * |
8 | | * This program is free software; you can redistribute it and/or modify it |
9 | | * under the terms of the GNU Lesser General Public License as published by |
10 | | * the Free Software Foundation; either version 2.1 of the License, or |
11 | | * (at your option) any later version. |
12 | | * |
13 | | * This program is distributed in the hope that it will be useful, |
14 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | | * GNU Lesser General Public License for more details. |
17 | | * |
18 | | * You should have received a copy of the GNU Lesser General Public License |
19 | | * along with this program; if not, write to the Free Software Foundation, |
20 | | * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. |
21 | | *****************************************************************************/ |
22 | | |
23 | | /***************************************************************************** |
24 | | * Preamble |
25 | | *****************************************************************************/ |
26 | | #ifdef HAVE_CONFIG_H |
27 | | # include "config.h" |
28 | | #endif |
29 | | #include <assert.h> |
30 | | |
31 | | #include <vlc_common.h> |
32 | | #include <vlc_plugin.h> |
33 | | #include <vlc_demux.h> |
34 | | |
35 | | /***************************************************************************** |
36 | | * Module descriptor |
37 | | *****************************************************************************/ |
38 | | static int Open (vlc_object_t *); |
39 | | static void Close(vlc_object_t *); |
40 | | |
41 | 168 | vlc_module_begin() |
42 | 84 | set_description(N_("EBU STL subtitles parser")) |
43 | 84 | set_subcategory(SUBCAT_INPUT_DEMUX) |
44 | 84 | set_capability("demux", 4) |
45 | 168 | set_callbacks(Open, Close) |
46 | 84 | add_shortcut("stl", "subtitle") |
47 | 84 | vlc_module_end() |
48 | | |
49 | | /***************************************************************************** |
50 | | * Local definitions/prototypes |
51 | | *****************************************************************************/ |
52 | | typedef struct { |
53 | | vlc_tick_t start; |
54 | | vlc_tick_t stop; |
55 | | size_t blocknumber; |
56 | | size_t count; |
57 | | } stl_entry_t; |
58 | | |
59 | | typedef struct |
60 | | { |
61 | | size_t count; |
62 | | stl_entry_t *index; |
63 | | |
64 | | es_out_id_t *es; |
65 | | |
66 | | size_t current; |
67 | | vlc_tick_t next_date; |
68 | | bool b_slave; |
69 | | bool b_first_time; |
70 | | } demux_sys_t; |
71 | | |
72 | | static size_t ParseInteger(uint8_t *data, size_t size) |
73 | 8.11k | { |
74 | 8.11k | char tmp[16]; |
75 | 8.11k | assert(size < sizeof(tmp)); |
76 | 8.11k | memcpy(tmp, data, size); |
77 | 8.11k | tmp[size] = '\0'; |
78 | | |
79 | 8.11k | return strtol(tmp, NULL, 10); |
80 | 8.11k | } |
81 | | static vlc_tick_t ParseTimeCode(uint8_t *data, double fps) |
82 | 214k | { |
83 | 214k | return CLOCK_FREQ * (data[0] * 3600 + |
84 | 214k | data[1] * 60 + |
85 | 214k | data[2] * 1 + |
86 | 214k | data[3] / fps); |
87 | 214k | } |
88 | | static vlc_tick_t ParseTextTimeCode(uint8_t *data, double fps) |
89 | 1.35k | { |
90 | 1.35k | uint8_t tmp[4]; |
91 | 6.76k | for (int i = 0; i < 4; i++) |
92 | 5.41k | tmp[i] = ParseInteger(&data[2 * i], 2); |
93 | 1.35k | return ParseTimeCode(tmp, fps); |
94 | 1.35k | } |
95 | | |
96 | | static int Control(demux_t *demux, int query, va_list args) |
97 | 0 | { |
98 | 0 | demux_sys_t *sys = demux->p_sys; |
99 | 0 | switch(query) { |
100 | 0 | case DEMUX_CAN_SEEK: |
101 | 0 | return vlc_stream_vaControl(demux->s, query, args); |
102 | 0 | case DEMUX_GET_LENGTH: { |
103 | 0 | *va_arg(args, vlc_tick_t *) = |
104 | 0 | sys->count > 0 ? sys->index[sys->count-1].stop : 0; |
105 | 0 | return VLC_SUCCESS; |
106 | 0 | } |
107 | 0 | case DEMUX_GET_TIME: { |
108 | 0 | vlc_tick_t *t = va_arg(args, vlc_tick_t *); |
109 | 0 | *t = sys->next_date - var_GetInteger(vlc_object_parent(demux), |
110 | 0 | "spu-delay"); |
111 | 0 | if( *t < 0 ) |
112 | 0 | *t = sys->next_date; |
113 | 0 | return VLC_SUCCESS; |
114 | 0 | } |
115 | 0 | case DEMUX_SET_NEXT_DEMUX_TIME: { |
116 | 0 | sys->b_slave = true; |
117 | 0 | sys->next_date = va_arg(args, vlc_tick_t); |
118 | 0 | return VLC_SUCCESS; |
119 | 0 | } |
120 | 0 | case DEMUX_SET_TIME: { |
121 | 0 | vlc_tick_t t = va_arg(args, vlc_tick_t); |
122 | 0 | for( size_t i = 0; i + 1< sys->count; i++ ) |
123 | 0 | { |
124 | 0 | if( sys->index[i + 1].start >= t && |
125 | 0 | vlc_stream_Seek(demux->s, 1024 + 128LL * sys->index[i].blocknumber) == VLC_SUCCESS ) |
126 | 0 | { |
127 | 0 | sys->current = i; |
128 | 0 | sys->next_date = t; |
129 | 0 | sys->b_first_time = true; |
130 | 0 | return VLC_SUCCESS; |
131 | 0 | } |
132 | 0 | } |
133 | 0 | break; |
134 | 0 | } |
135 | 0 | case DEMUX_SET_POSITION: |
136 | 0 | { |
137 | 0 | double f = va_arg( args, double ); |
138 | 0 | if(sys->count && sys->index[sys->count-1].stop > 0) |
139 | 0 | { |
140 | 0 | vlc_tick_t i64 = f * sys->index[sys->count-1].stop; |
141 | 0 | return demux_Control(demux, DEMUX_SET_TIME, i64); |
142 | 0 | } |
143 | 0 | break; |
144 | 0 | } |
145 | 0 | case DEMUX_GET_POSITION: |
146 | 0 | { |
147 | 0 | double *pf = va_arg(args, double *); |
148 | 0 | if(sys->current >= sys->count) |
149 | 0 | { |
150 | 0 | *pf = 1.0; |
151 | 0 | } |
152 | 0 | else if(sys->count > 0 && sys->index[sys->count-1].stop > 0) |
153 | 0 | { |
154 | 0 | *pf = sys->next_date - var_GetInteger(vlc_object_parent(demux), |
155 | 0 | "spu-delay"); |
156 | 0 | if(*pf < 0) |
157 | 0 | *pf = sys->next_date; |
158 | 0 | *pf /= sys->index[sys->count-1].stop; |
159 | 0 | } |
160 | 0 | else |
161 | 0 | { |
162 | 0 | *pf = 0.0; |
163 | 0 | } |
164 | 0 | return VLC_SUCCESS; |
165 | 0 | } |
166 | 0 | case DEMUX_CAN_PAUSE: |
167 | 0 | case DEMUX_SET_PAUSE_STATE: |
168 | 0 | case DEMUX_CAN_CONTROL_PACE: |
169 | 0 | case DEMUX_GET_PTS_DELAY: { |
170 | 0 | return demux_vaControlHelper( demux->s, 0, -1, 0, 1, query, args ); |
171 | 0 | } |
172 | 0 | default: |
173 | 0 | break; |
174 | 0 | } |
175 | 0 | return VLC_EGENERIC; |
176 | 0 | } |
177 | | |
178 | | static int Demux(demux_t *demux) |
179 | 2.83G | { |
180 | 2.83G | demux_sys_t *sys = demux->p_sys; |
181 | | |
182 | 2.83G | vlc_tick_t i_barrier = sys->next_date |
183 | 2.83G | - var_GetInteger(vlc_object_parent(demux), "spu-delay"); |
184 | 2.83G | if(i_barrier < 0) |
185 | 0 | i_barrier = sys->next_date; |
186 | | |
187 | 2.83G | while(sys->current < sys->count && |
188 | 2.83G | sys->index[sys->current].start <= i_barrier) |
189 | 38.2k | { |
190 | 38.2k | stl_entry_t *s = &sys->index[sys->current]; |
191 | | |
192 | 38.2k | if (!sys->b_slave && sys->b_first_time) |
193 | 1.13k | { |
194 | 1.13k | es_out_SetPCR(demux->out, VLC_TICK_0 + i_barrier); |
195 | 1.13k | sys->b_first_time = false; |
196 | 1.13k | } |
197 | | |
198 | | /* Might be a gap in block # */ |
199 | 38.2k | const uint64_t i_pos = 1024 + 128LL * s->blocknumber; |
200 | 38.2k | if(i_pos != vlc_stream_Tell(demux->s) && |
201 | 26.7k | vlc_stream_Seek( demux->s, i_pos ) != VLC_SUCCESS ) |
202 | 0 | return VLC_DEMUXER_EOF; |
203 | | |
204 | 38.2k | block_t *b = vlc_stream_Block(demux->s, 128); |
205 | 38.2k | if (b && b->i_buffer == 128) |
206 | 38.2k | { |
207 | 38.2k | b->i_dts = |
208 | 38.2k | b->i_pts = VLC_TICK_0 + s->start; |
209 | 38.2k | if (s->stop > s->start) |
210 | 12.3k | b->i_length = s->stop - s->start; |
211 | 38.2k | es_out_Send(demux->out, sys->es, b); |
212 | 38.2k | } |
213 | 0 | else |
214 | 0 | { |
215 | 0 | if(b) |
216 | 0 | block_Release(b); |
217 | 0 | return VLC_DEMUXER_EOF; |
218 | 0 | } |
219 | 38.2k | sys->current++; |
220 | 38.2k | } |
221 | | |
222 | 2.83G | if (!sys->b_slave) |
223 | 2.83G | { |
224 | 2.83G | es_out_SetPCR(demux->out, VLC_TICK_0 + i_barrier); |
225 | 2.83G | sys->next_date += VLC_TICK_FROM_MS(125); |
226 | 2.83G | } |
227 | | |
228 | 2.83G | return sys->current < sys->count ? VLC_DEMUXER_SUCCESS : VLC_DEMUXER_EOF; |
229 | 2.83G | } |
230 | | |
231 | | static int Open(vlc_object_t *object) |
232 | 4.82k | { |
233 | 4.82k | demux_t *demux = (demux_t*)object; |
234 | | |
235 | 4.82k | const uint8_t *peek; |
236 | 4.82k | if (vlc_stream_Peek(demux->s, &peek, 11) != 11) |
237 | 8 | return VLC_EGENERIC; |
238 | | |
239 | 4.82k | bool is_stl_25 = !memcmp(&peek[3], "STL25.01", 8); |
240 | 4.81k | bool is_stl_30 = !memcmp(&peek[3], "STL30.01", 8); |
241 | 4.81k | if (!is_stl_25 && !is_stl_30) |
242 | 3.40k | return VLC_EGENERIC; |
243 | 1.40k | const double fps = is_stl_25 ? 25 : 30; |
244 | | |
245 | 1.40k | uint8_t header[1024]; |
246 | 1.40k | if (vlc_stream_Read(demux->s, header, sizeof(header)) != sizeof(header)) { |
247 | 53 | msg_Err(demux, "Incomplete EBU STL header"); |
248 | 53 | return VLC_EGENERIC; |
249 | 53 | } |
250 | 1.35k | const int cct = ParseInteger(&header[12], 2); |
251 | 1.35k | const vlc_tick_t program_start = ParseTextTimeCode(&header[256], fps); |
252 | 1.35k | const size_t tti_count = ParseInteger(&header[238], 5); |
253 | 1.35k | if (!tti_count) |
254 | 10 | return VLC_EGENERIC; |
255 | 1.34k | msg_Dbg(demux, "Detected EBU STL : CCT=%d TTI=%zu start=%8.8s %"PRId64, cct, tti_count, &header[256], program_start); |
256 | | |
257 | 1.34k | demux_sys_t *sys = malloc(sizeof(*sys)); |
258 | 1.34k | if(!sys) |
259 | 0 | return VLC_EGENERIC; |
260 | | |
261 | 1.34k | sys->b_slave = false; |
262 | 1.34k | sys->b_first_time = true; |
263 | 1.34k | sys->next_date = 0; |
264 | 1.34k | sys->current = 0; |
265 | 1.34k | sys->count = 0; |
266 | 1.34k | sys->index = calloc(tti_count, sizeof(*sys->index)); |
267 | 1.34k | if(!sys->index) |
268 | 28 | { |
269 | 28 | free(sys); |
270 | 28 | return VLC_EGENERIC; |
271 | 28 | } |
272 | | |
273 | 1.34k | bool comment = false; |
274 | 1.31k | stl_entry_t *s = &sys->index[0]; |
275 | 1.31k | s->count = 0; |
276 | | |
277 | 155k | for (size_t i = 0; i < tti_count; i++) { |
278 | 155k | uint8_t tti[16]; |
279 | 155k | if (vlc_stream_Read(demux->s, tti, 16) != 16 || |
280 | 154k | vlc_stream_Read(demux->s, NULL, 112) != 112) { |
281 | 1.20k | msg_Warn(demux, "Incomplete EBU STL file"); |
282 | 1.20k | break; |
283 | 1.20k | } |
284 | 153k | const int ebn = tti[3]; |
285 | 153k | if (ebn >= 0xf0 && ebn <= 0xfd) |
286 | 1.06k | continue; |
287 | 152k | if (ebn == 0xfe) |
288 | 355 | continue; |
289 | | |
290 | 152k | if (s->count <= 0) { |
291 | 68.9k | comment = tti[15] != 0; |
292 | 68.9k | s->start = ParseTimeCode(&tti[5], fps) - program_start; |
293 | 68.9k | s->stop = ParseTimeCode(&tti[9], fps) - program_start; |
294 | 68.9k | s->blocknumber = i; |
295 | 68.9k | } |
296 | 152k | s->count++; |
297 | 152k | if (ebn == 0xff && !comment) |
298 | 38.2k | s = &sys->index[++sys->count]; |
299 | 152k | if (ebn == 0xff && sys->count < tti_count) |
300 | 68.5k | s->count = 0; |
301 | 152k | } |
302 | | |
303 | 1.31k | demux->p_sys = sys; |
304 | 1.31k | if (sys->count == 0 || |
305 | 1.13k | vlc_stream_Seek(demux->s, 1024 + 128LL * sys->index[0].blocknumber) != VLC_SUCCESS) |
306 | 178 | { |
307 | 178 | Close(object); |
308 | 178 | return VLC_EGENERIC; |
309 | 178 | } |
310 | | |
311 | 1.13k | es_format_t fmt; |
312 | 1.13k | es_format_Init(&fmt, SPU_ES, VLC_CODEC_EBU_STL); |
313 | 1.13k | fmt.i_extra = sizeof(header); |
314 | 1.13k | fmt.p_extra = header; |
315 | | |
316 | 1.13k | fmt.i_id = 0; |
317 | 1.13k | sys->es = es_out_Add(demux->out, &fmt); |
318 | 1.13k | fmt.i_extra = 0; |
319 | 1.13k | fmt.p_extra = NULL; |
320 | 1.13k | es_format_Clean(&fmt); |
321 | | |
322 | 1.13k | if(sys->es == NULL) |
323 | 0 | { |
324 | 0 | Close(object); |
325 | 0 | return VLC_EGENERIC; |
326 | 0 | } |
327 | | |
328 | 1.13k | demux->p_sys = sys; |
329 | 1.13k | demux->pf_demux = Demux; |
330 | 1.13k | demux->pf_control = Control; |
331 | 1.13k | return VLC_SUCCESS; |
332 | 1.13k | } |
333 | | |
334 | | static void Close(vlc_object_t *object) |
335 | 2.35k | { |
336 | 2.35k | demux_t *demux = (demux_t*)object; |
337 | 2.35k | demux_sys_t *sys = demux->p_sys; |
338 | | |
339 | 2.35k | free(sys->index); |
340 | 2.35k | free(sys); |
341 | 2.35k | } |
342 | | |