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