/src/vlc/modules/demux/flac.c
Line | Count | Source (jump to first uncovered line) |
1 | | /***************************************************************************** |
2 | | * flac.c : FLAC demux module for vlc |
3 | | ***************************************************************************** |
4 | | * Copyright (C) 2001-2008 VLC authors and VideoLAN |
5 | | * |
6 | | * Authors: Gildas Bazin <gbazin@netcourrier.com> |
7 | | * Laurent Aimar <fenrir@via.ecp.fr> |
8 | | * |
9 | | * This program is free software; you can redistribute it and/or modify it |
10 | | * under the terms of the GNU Lesser General Public License as published by |
11 | | * the Free Software Foundation; either version 2.1 of the License, or |
12 | | * (at your option) any later version. |
13 | | * |
14 | | * This program is distributed in the hope that it will be useful, |
15 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | | * GNU Lesser General Public License for more details. |
18 | | * |
19 | | * You should have received a copy of the GNU Lesser General Public License |
20 | | * along with this program; if not, write to the Free Software Foundation, |
21 | | * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. |
22 | | *****************************************************************************/ |
23 | | |
24 | | /***************************************************************************** |
25 | | * Preamble |
26 | | *****************************************************************************/ |
27 | | #ifdef HAVE_CONFIG_H |
28 | | # include "config.h" |
29 | | #endif |
30 | | |
31 | | #include <vlc_common.h> |
32 | | #include <vlc_plugin.h> |
33 | | #include <vlc_demux.h> |
34 | | #include <vlc_meta.h> /* vlc_meta_* */ |
35 | | #include <vlc_input.h> /* vlc_input_attachment, vlc_seekpoint */ |
36 | | #include <vlc_codec.h> /* decoder_t */ |
37 | | #include <vlc_charset.h> /* EnsureUTF8 */ |
38 | | |
39 | | #include <assert.h> |
40 | | #include <limits.h> |
41 | | #include "xiph_metadata.h" /* vorbis comments */ |
42 | | #include "../packetizer/flac.h" |
43 | | |
44 | | /***************************************************************************** |
45 | | * Module descriptor |
46 | | *****************************************************************************/ |
47 | | static int Open ( vlc_object_t * ); |
48 | | static void Close ( vlc_object_t * ); |
49 | | |
50 | 4 | vlc_module_begin () |
51 | 2 | set_description( N_("FLAC demuxer") ) |
52 | 2 | set_capability( "demux", 155 ) |
53 | 2 | set_subcategory( SUBCAT_INPUT_DEMUX ) |
54 | 2 | set_callbacks( Open, Close ) |
55 | 2 | add_shortcut( "flac" ) |
56 | 2 | add_file_extension("flac") |
57 | 2 | vlc_module_end () |
58 | | |
59 | | /***************************************************************************** |
60 | | * Local prototypes |
61 | | *****************************************************************************/ |
62 | | static int Demux ( demux_t * ); |
63 | | static int Control( demux_t *, int, va_list ); |
64 | | |
65 | | static int ParseHeaders( demux_t *, es_format_t * ); |
66 | | |
67 | | typedef struct |
68 | | { |
69 | | vlc_tick_t i_time_offset; |
70 | | uint64_t i_byte_offset; |
71 | | } flac_seekpoint_t; |
72 | | |
73 | | typedef struct |
74 | | { |
75 | | bool b_start; |
76 | | int i_next_block_flags; |
77 | | es_out_id_t *p_es; |
78 | | block_t *p_current_block; |
79 | | |
80 | | /* Packetizer */ |
81 | | decoder_t *p_packetizer; |
82 | | |
83 | | vlc_meta_t *p_meta; |
84 | | |
85 | | vlc_tick_t i_pts; |
86 | | struct flac_stream_info stream_info; |
87 | | bool b_stream_info; |
88 | | |
89 | | vlc_tick_t i_length; /* Length from stream info */ |
90 | | uint64_t i_data_pos; |
91 | | |
92 | | /* */ |
93 | | int i_seekpoint; |
94 | | flac_seekpoint_t **seekpoint; |
95 | | |
96 | | /* title/chapters seekpoints */ |
97 | | int i_title_seekpoints; |
98 | | seekpoint_t **pp_title_seekpoints; |
99 | | |
100 | | /* */ |
101 | | int i_attachments; |
102 | | input_attachment_t **attachments; |
103 | | int i_cover_idx; |
104 | | int i_cover_score; |
105 | | } demux_sys_t; |
106 | | |
107 | 646 | #define FLAC_PACKET_SIZE 16384 |
108 | 0 | #define FLAC_MAX_PREROLL VLC_TICK_FROM_SEC(4) |
109 | 0 | #define FLAC_MAX_SLOW_PREROLL VLC_TICK_FROM_SEC(45) |
110 | | |
111 | | /***************************************************************************** |
112 | | * Open: initializes ES structures |
113 | | *****************************************************************************/ |
114 | | static int Open( vlc_object_t * p_this ) |
115 | 61 | { |
116 | 61 | demux_t *p_demux = (demux_t*)p_this; |
117 | 61 | demux_sys_t *p_sys; |
118 | 61 | const uint8_t *p_peek; |
119 | 61 | es_format_t fmt; |
120 | | |
121 | | /* Have a peep at the show. */ |
122 | 61 | if( vlc_stream_Peek( p_demux->s, &p_peek, 4 ) < 4 ) return VLC_EGENERIC; |
123 | | |
124 | 61 | if( p_peek[0]!='f' || p_peek[1]!='L' || p_peek[2]!='a' || p_peek[3]!='C' ) |
125 | 42 | { |
126 | 42 | if( !p_demux->obj.force |
127 | 42 | && !demux_IsContentType( p_demux, "audio/flac" ) ) |
128 | 42 | return VLC_EGENERIC; |
129 | | |
130 | | /* User forced */ |
131 | 0 | msg_Err( p_demux, "this doesn't look like a flac stream, " |
132 | 0 | "continuing anyway" ); |
133 | 0 | } |
134 | | |
135 | 19 | p_sys = malloc( sizeof( demux_sys_t ) ); |
136 | 19 | if( unlikely(p_sys == NULL) ) |
137 | 0 | return VLC_ENOMEM; |
138 | | |
139 | 19 | p_demux->pf_demux = Demux; |
140 | 19 | p_demux->pf_control = Control; |
141 | 19 | p_demux->p_sys = p_sys; |
142 | 19 | p_sys->b_start = true; |
143 | 19 | p_sys->i_next_block_flags = 0; |
144 | 19 | p_sys->p_packetizer = NULL; |
145 | 19 | p_sys->p_meta = NULL; |
146 | 19 | p_sys->i_length = 0; |
147 | 19 | p_sys->i_pts = VLC_TICK_INVALID; |
148 | 19 | p_sys->b_stream_info = false; |
149 | 19 | p_sys->p_es = NULL; |
150 | 19 | p_sys->p_current_block = NULL; |
151 | 19 | TAB_INIT( p_sys->i_seekpoint, p_sys->seekpoint ); |
152 | 19 | TAB_INIT( p_sys->i_attachments, p_sys->attachments); |
153 | 19 | TAB_INIT( p_sys->i_title_seekpoints, p_sys->pp_title_seekpoints ); |
154 | 19 | p_sys->i_cover_idx = 0; |
155 | 19 | p_sys->i_cover_score = 0; |
156 | | |
157 | 19 | es_format_Init( &fmt, AUDIO_ES, VLC_CODEC_FLAC ); |
158 | 19 | fmt.i_id = 0; |
159 | | |
160 | | /* We need to read and store the STREAMINFO metadata into fmt extra */ |
161 | 19 | if( ParseHeaders( p_demux, &fmt ) ) |
162 | 0 | goto error; |
163 | | |
164 | | /* Load the FLAC packetizer */ |
165 | 19 | p_sys->p_packetizer = demux_PacketizerNew( VLC_OBJECT(p_demux), &fmt, "flac" ); |
166 | 19 | if( !p_sys->p_packetizer ) |
167 | 0 | goto error; |
168 | | |
169 | 19 | if( p_sys->i_cover_idx < p_sys->i_attachments ) |
170 | 0 | { |
171 | 0 | char psz_url[128]; |
172 | 0 | if( !p_sys->p_meta ) |
173 | 0 | p_sys->p_meta = vlc_meta_New(); |
174 | 0 | snprintf( psz_url, sizeof(psz_url), "attachment://%s", |
175 | 0 | p_sys->attachments[p_sys->i_cover_idx]->psz_name ); |
176 | 0 | vlc_meta_Set( p_sys->p_meta, vlc_meta_ArtworkURL, psz_url ); |
177 | 0 | } |
178 | | |
179 | 19 | p_sys->p_es = es_out_Add( p_demux->out, &fmt ); |
180 | 19 | if( !p_sys->p_es ) |
181 | 0 | goto error; |
182 | | |
183 | 19 | return VLC_SUCCESS; |
184 | 0 | error: |
185 | 0 | Close( p_this ); |
186 | 0 | return VLC_EGENERIC; |
187 | 19 | } |
188 | | |
189 | | /***************************************************************************** |
190 | | * Close: frees unused data |
191 | | *****************************************************************************/ |
192 | | static void Close( vlc_object_t * p_this ) |
193 | 19 | { |
194 | 19 | demux_t *p_demux = (demux_t*)p_this; |
195 | 19 | demux_sys_t *p_sys = p_demux->p_sys; |
196 | | |
197 | 19 | if( p_sys->p_current_block ) |
198 | 0 | block_Release( p_sys->p_current_block ); |
199 | | |
200 | 38 | for( int i = 0; i < p_sys->i_seekpoint; i++ ) |
201 | 19 | free(p_sys->seekpoint[i]); |
202 | 19 | TAB_CLEAN( p_sys->i_seekpoint, p_sys->seekpoint ); |
203 | | |
204 | 19 | for( int i = 0; i < p_sys->i_attachments; i++ ) |
205 | 0 | vlc_input_attachment_Release( p_sys->attachments[i] ); |
206 | 19 | TAB_CLEAN( p_sys->i_attachments, p_sys->attachments); |
207 | | |
208 | 19 | for( int i = 0; i < p_sys->i_title_seekpoints; i++ ) |
209 | 0 | vlc_seekpoint_Delete( p_sys->pp_title_seekpoints[i] ); |
210 | 19 | TAB_CLEAN( p_sys->i_title_seekpoints, p_sys->pp_title_seekpoints ); |
211 | | |
212 | | /* Delete the decoder */ |
213 | 19 | if( p_sys->p_packetizer ) |
214 | 19 | demux_PacketizerDestroy( p_sys->p_packetizer ); |
215 | | |
216 | 19 | if( p_sys->p_meta ) |
217 | 0 | vlc_meta_Delete( p_sys->p_meta ); |
218 | 19 | free( p_sys ); |
219 | 19 | } |
220 | | |
221 | | static block_t *GetPacketizedBlock( decoder_t *p_packetizer, |
222 | | const struct flac_stream_info *streaminfo, |
223 | | block_t **pp_current_block ) |
224 | 768 | { |
225 | 768 | block_t *p_block = p_packetizer->pf_packetize( p_packetizer, pp_current_block ); |
226 | 768 | if( p_block ) |
227 | 125 | { |
228 | 125 | if( p_block->i_buffer >= FLAC_HEADER_SIZE_MIN && p_block->i_buffer < INT_MAX ) |
229 | 125 | { |
230 | 125 | struct flac_header_info headerinfo = { .i_pts = VLC_TICK_INVALID }; |
231 | 125 | int i_ret = FLAC_ParseSyncInfo( p_block->p_buffer, p_block->i_buffer, |
232 | 125 | streaminfo, NULL, &headerinfo ); |
233 | 125 | assert( i_ret != 0 ); /* Same as packetizer */ |
234 | | /* Use Frame PTS, not the interpolated one */ |
235 | 125 | p_block->i_dts = p_block->i_pts = headerinfo.i_pts; |
236 | 125 | } |
237 | 125 | } |
238 | 768 | return p_block; |
239 | 768 | } |
240 | | |
241 | | static void FlushPacketizer( decoder_t *p_packetizer ) |
242 | 0 | { |
243 | 0 | if( p_packetizer->pf_flush ) |
244 | 0 | p_packetizer->pf_flush( p_packetizer ); |
245 | 0 | else |
246 | 0 | { |
247 | 0 | block_t *p_block_out; |
248 | 0 | while( (p_block_out = p_packetizer->pf_packetize( p_packetizer, NULL )) ) |
249 | 0 | block_Release( p_block_out ); |
250 | 0 | } |
251 | 0 | } |
252 | | |
253 | | static void Reset( demux_sys_t *p_sys ) |
254 | 0 | { |
255 | 0 | p_sys->i_pts = VLC_TICK_INVALID; |
256 | |
|
257 | 0 | FlushPacketizer( p_sys->p_packetizer ); |
258 | 0 | if( p_sys->p_current_block ) |
259 | 0 | { |
260 | 0 | block_Release( p_sys->p_current_block ); |
261 | 0 | p_sys->p_current_block = NULL; |
262 | 0 | } |
263 | 0 | } |
264 | | |
265 | | static int RefineSeek( demux_t *p_demux, vlc_tick_t i_time, double i_bytemicrorate, |
266 | | uint64_t i_lowpos, uint64_t i_highpos ) |
267 | 0 | { |
268 | 0 | demux_sys_t *p_sys = p_demux->p_sys; |
269 | 0 | bool b_found = false; |
270 | 0 | block_t *p_block_out; |
271 | 0 | block_t *p_block_in; |
272 | |
|
273 | 0 | unsigned i_frame_size = FLAC_FRAME_SIZE_MIN; |
274 | |
|
275 | 0 | bool b_canfastseek = false; |
276 | 0 | vlc_stream_Control( p_demux->s, STREAM_CAN_FASTSEEK, &b_canfastseek ); |
277 | |
|
278 | 0 | uint64_t i_start_pos = vlc_stream_Tell( p_demux->s ); |
279 | |
|
280 | 0 | while( !b_found ) |
281 | 0 | { |
282 | 0 | FlushPacketizer( p_sys->p_packetizer ); |
283 | |
|
284 | 0 | p_block_out = NULL; |
285 | 0 | p_block_in = NULL; |
286 | |
|
287 | 0 | while( !p_block_out ) |
288 | 0 | { |
289 | 0 | if( !p_block_in ) |
290 | 0 | { |
291 | 0 | if( !(p_block_in = vlc_stream_Block( p_demux->s, i_frame_size )) ) |
292 | 0 | break; |
293 | 0 | } |
294 | | |
295 | 0 | p_block_out = GetPacketizedBlock( p_sys->p_packetizer, |
296 | 0 | p_sys->b_stream_info ? &p_sys->stream_info : NULL, |
297 | 0 | &p_block_in ); |
298 | 0 | } |
299 | |
|
300 | 0 | if( !p_block_out ) |
301 | 0 | { |
302 | 0 | if( p_block_in ) |
303 | 0 | block_Release( p_block_in ); |
304 | 0 | break; |
305 | 0 | } |
306 | | |
307 | 0 | if( p_block_out->i_buffer > i_frame_size ) |
308 | 0 | i_frame_size = p_block_out->i_buffer; |
309 | | |
310 | | /* If we are further than wanted block */ |
311 | 0 | if( p_block_out->i_dts >= i_time ) |
312 | 0 | { |
313 | 0 | vlc_tick_t i_diff = p_block_out->i_dts - i_time; |
314 | | /* Not in acceptable approximation range */ |
315 | 0 | if( i_diff > VLC_TICK_FROM_MS(100) && i_diff / i_bytemicrorate > i_frame_size ) |
316 | 0 | { |
317 | 0 | i_highpos = i_start_pos; |
318 | 0 | i_start_pos -= ( i_diff / i_bytemicrorate ); |
319 | 0 | i_start_pos = __MAX(i_start_pos, i_lowpos + i_frame_size); |
320 | 0 | } |
321 | 0 | else b_found = true; |
322 | 0 | } |
323 | 0 | else |
324 | 0 | { |
325 | 0 | vlc_tick_t i_diff = i_time - p_block_out->i_dts; |
326 | | /* Not in acceptable NEXT_TIME demux range */ |
327 | 0 | if( i_diff >= ((b_canfastseek) ? FLAC_MAX_PREROLL : FLAC_MAX_SLOW_PREROLL) && |
328 | 0 | i_diff / i_bytemicrorate > i_frame_size ) |
329 | 0 | { |
330 | 0 | i_lowpos = i_start_pos; |
331 | 0 | i_start_pos += ( i_diff / i_bytemicrorate ); |
332 | 0 | i_start_pos = __MIN(i_start_pos, i_highpos - i_frame_size); |
333 | 0 | } |
334 | 0 | else b_found = true; |
335 | 0 | } |
336 | |
|
337 | 0 | if( p_block_out ) |
338 | 0 | block_Release( p_block_out ); |
339 | 0 | if( p_block_in ) |
340 | 0 | block_Release( p_block_in ); |
341 | |
|
342 | 0 | if( !b_found ) |
343 | 0 | { |
344 | 0 | if( i_highpos < i_lowpos || i_highpos - i_lowpos < i_frame_size ) |
345 | 0 | break; |
346 | | |
347 | 0 | if( VLC_SUCCESS != vlc_stream_Seek( p_demux->s, i_start_pos ) ) |
348 | 0 | break; |
349 | 0 | } |
350 | 0 | } |
351 | |
|
352 | 0 | return b_found ? VLC_SUCCESS : VLC_EGENERIC; |
353 | 0 | } |
354 | | |
355 | | /***************************************************************************** |
356 | | * Demux: reads and demuxes data packets |
357 | | ***************************************************************************** |
358 | | * Returns -1 in case of error, 0 in case of EOF, 1 otherwise |
359 | | *****************************************************************************/ |
360 | | static int Demux( demux_t *p_demux ) |
361 | 768 | { |
362 | 768 | demux_sys_t *p_sys = p_demux->p_sys; |
363 | 768 | block_t *p_block_out; |
364 | | |
365 | 768 | if( p_sys->p_current_block == NULL ) |
366 | 646 | p_sys->p_current_block = vlc_stream_Block( p_demux->s, FLAC_PACKET_SIZE ); |
367 | | |
368 | 768 | bool b_eof = (p_sys->p_current_block == NULL); |
369 | | |
370 | 768 | if ( !b_eof ) |
371 | 749 | { |
372 | 749 | p_sys->p_current_block->i_flags = p_sys->i_next_block_flags; |
373 | 749 | p_sys->i_next_block_flags = 0; |
374 | 749 | p_sys->p_current_block->i_pts = |
375 | 749 | p_sys->p_current_block->i_dts = p_sys->b_start ? VLC_TICK_0 : VLC_TICK_INVALID; |
376 | 749 | } |
377 | | |
378 | 768 | while( (p_block_out = GetPacketizedBlock( p_sys->p_packetizer, |
379 | 768 | p_sys->b_stream_info ? &p_sys->stream_info : NULL, |
380 | 768 | p_sys->p_current_block ? &p_sys->p_current_block : NULL ) ) ) |
381 | 125 | { |
382 | | /* Only clear on output when packet is accepted as sync #17111 */ |
383 | 125 | p_sys->b_start = false; |
384 | 250 | while( p_block_out ) |
385 | 125 | { |
386 | 125 | block_t *p_next = p_block_out->p_next; |
387 | | |
388 | 125 | p_block_out->p_next = NULL; |
389 | | |
390 | | /* set PCR */ |
391 | 125 | if( unlikely(p_sys->i_pts == VLC_TICK_INVALID) ) |
392 | 7 | es_out_SetPCR( p_demux->out, __MAX(p_block_out->i_dts - 1, VLC_TICK_0) ); |
393 | | |
394 | 125 | if(p_block_out->i_dts != VLC_TICK_INVALID) |
395 | 125 | p_sys->i_pts = p_block_out->i_dts; |
396 | | |
397 | 125 | es_out_Send( p_demux->out, p_sys->p_es, p_block_out ); |
398 | | |
399 | 125 | es_out_SetPCR( p_demux->out, p_sys->i_pts ); |
400 | | |
401 | 125 | p_block_out = p_next; |
402 | 125 | } |
403 | 125 | break; |
404 | 125 | } |
405 | | |
406 | 768 | return b_eof ? VLC_DEMUXER_EOF : VLC_DEMUXER_SUCCESS; |
407 | 768 | } |
408 | | |
409 | | /***************************************************************************** |
410 | | * Control: |
411 | | *****************************************************************************/ |
412 | | static vlc_tick_t ControlGetLength( demux_t *p_demux ) |
413 | 0 | { |
414 | 0 | demux_sys_t *p_sys = p_demux->p_sys; |
415 | 0 | const uint64_t i_size = stream_Size(p_demux->s) - p_sys->i_data_pos; |
416 | 0 | vlc_tick_t i_length = p_sys->i_length; |
417 | 0 | int i; |
418 | | |
419 | | /* Try to fix length using seekpoint and current size for truncated file */ |
420 | 0 | for( i = p_sys->i_seekpoint-1; i >= 0; i-- ) |
421 | 0 | { |
422 | 0 | flac_seekpoint_t *s = p_sys->seekpoint[i]; |
423 | 0 | if( s->i_byte_offset <= i_size ) |
424 | 0 | { |
425 | 0 | if( i+1 < p_sys->i_seekpoint ) |
426 | 0 | { |
427 | | /* Broken file */ |
428 | 0 | flac_seekpoint_t *n = p_sys->seekpoint[i+1]; |
429 | 0 | assert( n->i_byte_offset != s->i_byte_offset); /* Should be ensured by ParseSeekTable */ |
430 | 0 | i_length = s->i_time_offset + (n->i_time_offset-s->i_time_offset) * (i_size-s->i_byte_offset) / (n->i_byte_offset-s->i_byte_offset); |
431 | 0 | } |
432 | 0 | break; |
433 | 0 | } |
434 | 0 | } |
435 | 0 | return i_length; |
436 | 0 | } |
437 | | |
438 | | static vlc_tick_t ControlGetTime( demux_t *p_demux ) |
439 | 0 | { |
440 | 0 | demux_sys_t *p_sys = p_demux->p_sys; |
441 | 0 | return p_sys->i_pts; |
442 | 0 | } |
443 | | |
444 | | static int ControlSetTime( demux_t *p_demux, vlc_tick_t i_time ) |
445 | 0 | { |
446 | 0 | demux_sys_t *p_sys = p_demux->p_sys; |
447 | 0 | bool b_seekable; |
448 | 0 | int i; |
449 | | |
450 | | /* */ |
451 | 0 | vlc_stream_Control( p_demux->s, STREAM_CAN_SEEK, &b_seekable ); |
452 | 0 | if( !b_seekable ) |
453 | 0 | return VLC_EGENERIC; |
454 | | |
455 | 0 | const vlc_tick_t i_length = ControlGetLength( p_demux ); |
456 | 0 | if( i_length <= 0 ) |
457 | 0 | return VLC_EGENERIC; |
458 | | |
459 | 0 | const uint64_t i_stream_size = stream_Size( p_demux->s ); |
460 | 0 | if( i_stream_size <= p_sys->i_data_pos ) |
461 | 0 | return VLC_EGENERIC; |
462 | | |
463 | 0 | const double i_bytemicrorate = (double) i_length / (i_stream_size - p_sys->i_data_pos); |
464 | 0 | if( i_bytemicrorate == 0 ) |
465 | 0 | return VLC_EGENERIC; |
466 | | |
467 | 0 | uint64_t i_lower = p_sys->i_data_pos; |
468 | 0 | uint64_t i_upper = i_stream_size; |
469 | 0 | uint64_t i_start_pos; |
470 | |
|
471 | 0 | assert( p_sys->i_seekpoint > 0 ); /* ReadMeta ensure at least (0,0) */ |
472 | 0 | if( p_sys->i_seekpoint > 1 ) |
473 | 0 | { |
474 | | /* lookup base offset */ |
475 | 0 | for( i = p_sys->i_seekpoint-1; i >= 0; i-- ) |
476 | 0 | { |
477 | 0 | if( p_sys->seekpoint[i]->i_time_offset <= i_time ) |
478 | 0 | break; |
479 | 0 | } |
480 | |
|
481 | 0 | i_lower = p_sys->seekpoint[0]->i_byte_offset + p_sys->i_data_pos; |
482 | 0 | if( i+1 < p_sys->i_seekpoint ) |
483 | 0 | i_upper = p_sys->seekpoint[i+1]->i_byte_offset + p_sys->i_data_pos; |
484 | |
|
485 | 0 | i_start_pos = i_lower; |
486 | 0 | } |
487 | 0 | else |
488 | 0 | { |
489 | 0 | i_start_pos = i_time / i_bytemicrorate; |
490 | 0 | } |
491 | |
|
492 | 0 | if( VLC_SUCCESS != vlc_stream_Seek( p_demux->s, i_start_pos ) ) |
493 | 0 | return VLC_EGENERIC; |
494 | | |
495 | 0 | int i_ret = RefineSeek( p_demux, i_time, i_bytemicrorate, i_lower, i_upper ); |
496 | 0 | if( i_ret == VLC_SUCCESS ) |
497 | 0 | { |
498 | 0 | p_sys->i_next_block_flags |= BLOCK_FLAG_DISCONTINUITY; |
499 | 0 | Reset( p_sys ); |
500 | 0 | es_out_Control( p_demux->out, ES_OUT_SET_NEXT_DISPLAY_TIME, i_time ); |
501 | 0 | } |
502 | |
|
503 | 0 | return i_ret; |
504 | 0 | } |
505 | | |
506 | | static int Control( demux_t *p_demux, int i_query, va_list args ) |
507 | 0 | { |
508 | 0 | demux_sys_t *p_sys = p_demux->p_sys; |
509 | |
|
510 | 0 | if( i_query == DEMUX_GET_META ) |
511 | 0 | { |
512 | 0 | vlc_meta_t *p_meta = va_arg( args, vlc_meta_t * ); |
513 | 0 | if( p_sys->p_meta ) |
514 | 0 | vlc_meta_Merge( p_meta, p_sys->p_meta ); |
515 | 0 | return VLC_SUCCESS; |
516 | 0 | } |
517 | 0 | else if( i_query == DEMUX_HAS_UNSUPPORTED_META ) |
518 | 0 | { |
519 | 0 | bool *pb_bool = va_arg( args, bool* ); |
520 | 0 | *pb_bool = true; |
521 | 0 | return VLC_SUCCESS; |
522 | 0 | } |
523 | 0 | else if( i_query == DEMUX_GET_LENGTH ) |
524 | 0 | { |
525 | 0 | *va_arg( args, vlc_tick_t * ) = ControlGetLength( p_demux ); |
526 | 0 | return VLC_SUCCESS; |
527 | 0 | } |
528 | 0 | else if( i_query == DEMUX_SET_TIME ) |
529 | 0 | { |
530 | 0 | return ControlSetTime( p_demux, va_arg( args, vlc_tick_t ) ); |
531 | 0 | } |
532 | 0 | else if( i_query == DEMUX_SET_POSITION ) |
533 | 0 | { |
534 | 0 | const double f = va_arg( args, double ); |
535 | 0 | vlc_tick_t i_length = ControlGetLength( p_demux ); |
536 | 0 | int i_ret; |
537 | 0 | if( i_length > 0 ) |
538 | 0 | { |
539 | 0 | i_ret = ControlSetTime( p_demux, i_length * f ); |
540 | 0 | if( i_ret == VLC_SUCCESS ) |
541 | 0 | return i_ret; |
542 | 0 | } |
543 | | /* just byte pos seek */ |
544 | 0 | i_ret = vlc_stream_Seek( p_demux->s, (int64_t) (f * stream_Size( p_demux->s )) ); |
545 | 0 | if( i_ret == VLC_SUCCESS ) |
546 | 0 | { |
547 | 0 | p_sys->i_next_block_flags |= BLOCK_FLAG_DISCONTINUITY; |
548 | 0 | Reset( p_sys ); |
549 | 0 | } |
550 | 0 | return i_ret; |
551 | 0 | } |
552 | 0 | else if( i_query == DEMUX_GET_TIME ) |
553 | 0 | { |
554 | 0 | *va_arg( args, vlc_tick_t * ) = ControlGetTime( p_demux ); |
555 | 0 | return VLC_SUCCESS; |
556 | 0 | } |
557 | 0 | else if( i_query == DEMUX_GET_POSITION ) |
558 | 0 | { |
559 | 0 | const vlc_tick_t i_length = ControlGetLength(p_demux); |
560 | 0 | if( i_length > 0 ) |
561 | 0 | { |
562 | 0 | vlc_tick_t current = ControlGetTime(p_demux); |
563 | 0 | if( current <= i_length ) |
564 | 0 | { |
565 | 0 | *(va_arg( args, double * )) = (double)current / (double)i_length; |
566 | 0 | return VLC_SUCCESS; |
567 | 0 | } |
568 | 0 | } |
569 | | /* Else fallback on byte position */ |
570 | 0 | } |
571 | 0 | else if( i_query == DEMUX_GET_ATTACHMENTS ) |
572 | 0 | { |
573 | 0 | input_attachment_t ***ppp_attach = |
574 | 0 | va_arg( args, input_attachment_t *** ); |
575 | 0 | int *pi_int = va_arg( args, int * ); |
576 | |
|
577 | 0 | if( p_sys->i_attachments <= 0 ) |
578 | 0 | return VLC_EGENERIC; |
579 | | |
580 | 0 | *ppp_attach = vlc_alloc( p_sys->i_attachments, sizeof(input_attachment_t*) ); |
581 | 0 | if( !*ppp_attach ) |
582 | 0 | return VLC_EGENERIC; |
583 | 0 | *pi_int = p_sys->i_attachments; |
584 | 0 | for( int i = 0; i < p_sys->i_attachments; i++ ) |
585 | 0 | (*ppp_attach)[i] = vlc_input_attachment_Hold( p_sys->attachments[i] ); |
586 | 0 | return VLC_SUCCESS; |
587 | 0 | } |
588 | 0 | else if( i_query == DEMUX_GET_TITLE_INFO ) |
589 | 0 | { |
590 | 0 | input_title_t ***ppp_title = va_arg( args, input_title_t *** ); |
591 | 0 | int *pi_int = va_arg( args, int * ); |
592 | 0 | int *pi_title_offset = va_arg( args, int * ); |
593 | 0 | int *pi_seekpoint_offset = va_arg( args, int * ); |
594 | |
|
595 | 0 | if( !p_sys->i_title_seekpoints ) |
596 | 0 | return VLC_EGENERIC; |
597 | | |
598 | 0 | *pi_int = 1; |
599 | 0 | *ppp_title = malloc( sizeof(input_title_t*) ); |
600 | 0 | if(!*ppp_title) |
601 | 0 | return VLC_EGENERIC; |
602 | | |
603 | 0 | input_title_t *p_title = (*ppp_title)[0] = vlc_input_title_New(); |
604 | 0 | if(!p_title) |
605 | 0 | { |
606 | 0 | free(*ppp_title); |
607 | 0 | return VLC_EGENERIC; |
608 | 0 | } |
609 | | |
610 | 0 | p_title->seekpoint = vlc_alloc( p_sys->i_title_seekpoints, sizeof(seekpoint_t*) ); |
611 | 0 | if(!p_title->seekpoint) |
612 | 0 | { |
613 | 0 | vlc_input_title_Delete(p_title); |
614 | 0 | free(*ppp_title); |
615 | 0 | return VLC_EGENERIC; |
616 | 0 | } |
617 | | |
618 | 0 | p_title->i_seekpoint = p_sys->i_title_seekpoints; |
619 | 0 | for( int i = 0; i < p_title->i_seekpoint; i++ ) |
620 | 0 | p_title->seekpoint[i] = vlc_seekpoint_Duplicate( p_sys->pp_title_seekpoints[i] ); |
621 | |
|
622 | 0 | *pi_title_offset = 0; |
623 | 0 | *pi_seekpoint_offset = 0; |
624 | |
|
625 | 0 | return VLC_SUCCESS; |
626 | 0 | } |
627 | 0 | else if( i_query == DEMUX_SET_TITLE ) |
628 | 0 | { |
629 | 0 | const int i_title = va_arg( args, int ); |
630 | 0 | if( i_title != 0 ) |
631 | 0 | return VLC_EGENERIC; |
632 | 0 | return VLC_SUCCESS; |
633 | 0 | } |
634 | 0 | else if( i_query == DEMUX_SET_SEEKPOINT ) |
635 | 0 | { |
636 | 0 | const int i_seekpoint = va_arg( args, int ); |
637 | 0 | if( !p_sys->i_title_seekpoints || i_seekpoint >= p_sys->i_title_seekpoints ) |
638 | 0 | return VLC_EGENERIC; |
639 | 0 | return ControlSetTime( p_demux, p_sys->pp_title_seekpoints[i_seekpoint]->i_time_offset ); |
640 | 0 | } |
641 | | |
642 | 0 | return demux_vaControlHelper( p_demux->s, p_sys->i_data_pos, -1, |
643 | 0 | 8*0, 1, i_query, args ); |
644 | 0 | } |
645 | | |
646 | | enum |
647 | | { |
648 | | META_STREAMINFO = 0, |
649 | | META_SEEKTABLE = 3, |
650 | | META_COMMENT = 4, |
651 | | META_PICTURE = 6, |
652 | | }; |
653 | | |
654 | | static inline int Get24bBE( const uint8_t *p ) |
655 | 23 | { |
656 | 23 | return (p[0] << 16)|(p[1] << 8)|(p[2]); |
657 | 23 | } |
658 | | |
659 | | static void ParseSeekTable( demux_t *p_demux, const uint8_t *p_data, size_t i_data, |
660 | | unsigned i_sample_rate ); |
661 | | static void ParseComment( demux_t *, const uint8_t *p_data, size_t i_data ); |
662 | | static void ParsePicture( demux_t *, const uint8_t *p_data, size_t i_data ); |
663 | | |
664 | | static int ParseHeaders( demux_t *p_demux, es_format_t *p_fmt ) |
665 | 19 | { |
666 | 19 | demux_sys_t *p_sys = p_demux->p_sys; |
667 | 19 | ssize_t i_peek; |
668 | 19 | const uint8_t *p_peek; |
669 | 19 | bool b_last; |
670 | | |
671 | | /* Be sure we have seekpoint 0 */ |
672 | 19 | flac_seekpoint_t *s = xmalloc( sizeof (*s) ); |
673 | 19 | s->i_time_offset = 0; |
674 | 19 | s->i_byte_offset = 0; |
675 | 19 | TAB_APPEND( p_sys->i_seekpoint, p_sys->seekpoint, s ); |
676 | | |
677 | 19 | uint8_t header[4]; |
678 | 19 | if( vlc_stream_Read( p_demux->s, header, 4) < 4) |
679 | 0 | return VLC_EGENERIC; |
680 | | |
681 | 19 | if (memcmp(header, "fLaC", 4)) |
682 | 0 | return VLC_EGENERIC; |
683 | | |
684 | 19 | b_last = 0; |
685 | 42 | while( !b_last ) |
686 | 23 | { |
687 | 23 | int i_len; |
688 | 23 | int i_type; |
689 | | |
690 | 23 | i_peek = vlc_stream_Peek( p_demux->s, &p_peek, 4 ); |
691 | 23 | if( i_peek < 4 ) |
692 | 0 | break; |
693 | 23 | b_last = p_peek[0]&0x80; |
694 | 23 | i_type = p_peek[0]&0x7f; |
695 | 23 | i_len = Get24bBE( &p_peek[1] ); |
696 | | |
697 | 23 | if( i_type == META_STREAMINFO && p_fmt->p_extra == NULL ) |
698 | 19 | { |
699 | 19 | if( i_len != FLAC_STREAMINFO_SIZE ) { |
700 | 0 | msg_Err( p_demux, "invalid size %d for a STREAMINFO metadata block", i_len ); |
701 | 0 | return VLC_EGENERIC; |
702 | 0 | } |
703 | | |
704 | 19 | p_fmt->p_extra = malloc( FLAC_STREAMINFO_SIZE ); |
705 | 19 | if( p_fmt->p_extra == NULL ) |
706 | 0 | return VLC_EGENERIC; |
707 | | |
708 | 19 | if( vlc_stream_Read( p_demux->s, NULL, 4 ) != 4 ) |
709 | 0 | { |
710 | 0 | FREENULL( p_fmt->p_extra ); |
711 | 0 | return VLC_EGENERIC; |
712 | 0 | } |
713 | 19 | if( vlc_stream_Read( p_demux->s, p_fmt->p_extra, |
714 | 19 | FLAC_STREAMINFO_SIZE ) != FLAC_STREAMINFO_SIZE ) |
715 | 0 | { |
716 | 0 | msg_Err( p_demux, "failed to read STREAMINFO metadata block" ); |
717 | 0 | FREENULL( p_fmt->p_extra ); |
718 | 0 | return VLC_EGENERIC; |
719 | 0 | } |
720 | 19 | p_fmt->i_extra = FLAC_STREAMINFO_SIZE; |
721 | | |
722 | | /* */ |
723 | 19 | p_sys->b_stream_info = true; |
724 | 19 | FLAC_ParseStreamInfo( (uint8_t *) p_fmt->p_extra, &p_sys->stream_info ); |
725 | | |
726 | 19 | p_fmt->audio.i_rate = p_sys->stream_info.sample_rate; |
727 | 19 | p_fmt->audio.i_channels = p_sys->stream_info.channels; |
728 | 19 | p_fmt->audio.i_bitspersample = p_sys->stream_info.bits_per_sample; |
729 | 19 | if( p_sys->stream_info.sample_rate > 0 ) |
730 | 17 | p_sys->i_length = vlc_tick_from_samples(p_sys->stream_info.total_samples, |
731 | 17 | p_sys->stream_info.sample_rate); |
732 | | |
733 | 19 | continue; |
734 | 19 | } |
735 | 4 | else if( i_type == META_SEEKTABLE ) |
736 | 1 | { |
737 | 1 | i_peek = vlc_stream_Peek( p_demux->s, &p_peek, 4+i_len ); |
738 | 1 | if( i_peek == 4+i_len ) |
739 | 1 | ParseSeekTable( p_demux, p_peek, i_peek, p_fmt->audio.i_rate ); |
740 | 1 | } |
741 | 3 | else if( i_type == META_COMMENT ) |
742 | 1 | { |
743 | 1 | i_peek = vlc_stream_Peek( p_demux->s, &p_peek, 4+i_len ); |
744 | 1 | if( i_peek == 4+i_len ) |
745 | 1 | ParseComment( p_demux, p_peek, i_peek ); |
746 | 1 | } |
747 | 2 | else if( i_type == META_PICTURE ) |
748 | 0 | { |
749 | 0 | i_peek = vlc_stream_Peek( p_demux->s, &p_peek, 4+i_len ); |
750 | 0 | if( i_peek == 4+i_len ) |
751 | 0 | ParsePicture( p_demux, p_peek, i_peek ); |
752 | 0 | } |
753 | | |
754 | 4 | if( vlc_stream_Read( p_demux->s, NULL, 4+i_len ) != (4+i_len) ) |
755 | 0 | break; |
756 | 4 | } |
757 | | |
758 | | /* */ |
759 | 19 | p_sys->i_data_pos = vlc_stream_Tell( p_demux->s ); |
760 | | |
761 | 19 | if ( p_fmt->p_extra == NULL ) |
762 | 0 | return VLC_EGENERIC; |
763 | | |
764 | 19 | return VLC_SUCCESS; |
765 | 19 | } |
766 | | |
767 | | static void ParseSeekTable( demux_t *p_demux, const uint8_t *p_data, size_t i_data, |
768 | | unsigned i_sample_rate ) |
769 | 1 | { |
770 | 1 | demux_sys_t *p_sys = p_demux->p_sys; |
771 | 1 | flac_seekpoint_t *s; |
772 | 1 | size_t i; |
773 | | |
774 | 1 | if( i_sample_rate == 0 ) |
775 | 0 | return; |
776 | | |
777 | | /* */ |
778 | 1 | for( i = 0; i < (i_data-4)/18; i++ ) |
779 | 1 | { |
780 | 1 | const int64_t i_sample = GetQWBE( &p_data[4+18*i+0] ); |
781 | 1 | int j; |
782 | | |
783 | 1 | if( i_sample < 0 || i_sample >= INT64_MAX || |
784 | 1 | GetQWBE( &p_data[4+18*i+8] ) < FLAC_STREAMINFO_SIZE ) |
785 | 1 | break; |
786 | | |
787 | 0 | s = xmalloc( sizeof (*s) ); |
788 | 0 | s->i_time_offset = vlc_tick_from_samples(i_sample, i_sample_rate); |
789 | 0 | s->i_byte_offset = GetQWBE( &p_data[4+18*i+8] ); |
790 | | |
791 | | /* Check for duplicate entry */ |
792 | 0 | for( j = 0; j < p_sys->i_seekpoint; j++ ) |
793 | 0 | { |
794 | 0 | if( p_sys->seekpoint[j]->i_time_offset == s->i_time_offset || |
795 | 0 | p_sys->seekpoint[j]->i_byte_offset == s->i_byte_offset ) |
796 | 0 | { |
797 | 0 | free( s ); |
798 | 0 | s = NULL; |
799 | 0 | break; |
800 | 0 | } |
801 | 0 | } |
802 | 0 | if( s ) |
803 | 0 | { |
804 | 0 | TAB_APPEND( p_sys->i_seekpoint, p_sys->seekpoint, s ); |
805 | 0 | } |
806 | 0 | } |
807 | | /* TODO sort it by size and remove wrong seek entry (time not increasing) */ |
808 | 1 | } |
809 | | |
810 | | static void ParseComment( demux_t *p_demux, const uint8_t *p_data, size_t i_data ) |
811 | 1 | { |
812 | 1 | demux_sys_t *p_sys = p_demux->p_sys; |
813 | | |
814 | 1 | if( i_data < 4 ) |
815 | 0 | return; |
816 | | |
817 | 1 | vorbis_ParseComment( NULL, &p_sys->p_meta, &p_data[4], i_data - 4, |
818 | 1 | &p_sys->i_attachments, &p_sys->attachments, |
819 | 1 | &p_sys->i_cover_score, &p_sys->i_cover_idx, |
820 | 1 | &p_sys->i_title_seekpoints, &p_sys->pp_title_seekpoints, NULL, NULL ); |
821 | 1 | } |
822 | | |
823 | | static void ParsePicture( demux_t *p_demux, const uint8_t *p_data, size_t i_data ) |
824 | 0 | { |
825 | 0 | demux_sys_t *p_sys = p_demux->p_sys; |
826 | |
|
827 | 0 | i_data -= 4; p_data += 4; |
828 | |
|
829 | 0 | input_attachment_t *p_attachment = ParseFlacPicture( p_data, i_data, |
830 | 0 | p_sys->i_attachments, &p_sys->i_cover_score, &p_sys->i_cover_idx ); |
831 | 0 | if( p_attachment == NULL ) |
832 | 0 | return; |
833 | | |
834 | 0 | TAB_APPEND( p_sys->i_attachments, p_sys->attachments, p_attachment ); |
835 | 0 | } |