/src/vlc/modules/demux/wav.c
Line | Count | Source |
1 | | /***************************************************************************** |
2 | | * wav.c : wav file input module for vlc |
3 | | ***************************************************************************** |
4 | | * Copyright (C) 2001-2008 VLC authors and VideoLAN |
5 | | * |
6 | | * Authors: Laurent Aimar <fenrir@via.ecp.fr> |
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 | | |
27 | | #ifdef HAVE_CONFIG_H |
28 | | # include "config.h" |
29 | | #endif |
30 | | |
31 | | #include <assert.h> |
32 | | #include <limits.h> |
33 | | |
34 | | #include <vlc_common.h> |
35 | | #include <vlc_plugin.h> |
36 | | #include <vlc_demux.h> |
37 | | #include <vlc_aout.h> |
38 | | #include <vlc_codecs.h> |
39 | | |
40 | | #include "windows_audio_commons.h" |
41 | | |
42 | 778 | #define WAV_CHAN_MAX 32 |
43 | | static_assert( INPUT_CHAN_MAX >= WAV_CHAN_MAX, "channel count mismatch" ); |
44 | | |
45 | | typedef struct |
46 | | { |
47 | | es_format_t fmt; |
48 | | es_out_id_t *p_es; |
49 | | |
50 | | uint64_t i_data_pos; |
51 | | uint64_t i_data_size; |
52 | | |
53 | | unsigned int i_frame_size; |
54 | | unsigned int i_frame_samples; |
55 | | |
56 | | date_t pts; |
57 | | |
58 | | uint32_t i_channel_mask; |
59 | | uint8_t i_chans_to_reorder; /* do we need channel reordering */ |
60 | | uint8_t pi_chan_table[AOUT_CHAN_MAX]; |
61 | | } demux_sys_t; |
62 | | |
63 | | enum wav_chunk_id { |
64 | | wav_chunk_id_data, |
65 | | wav_chunk_id_ds64, |
66 | | wav_chunk_id_fmt, |
67 | | }; |
68 | | |
69 | | static const struct wav_chunk_id_key |
70 | | { |
71 | | enum wav_chunk_id id; |
72 | | char key[5]; |
73 | | } wav_chunk_id_key_list[] = { |
74 | | /* Alphabetical order */ |
75 | | { wav_chunk_id_data, "data" }, |
76 | | { wav_chunk_id_ds64, "ds64" }, |
77 | | { wav_chunk_id_fmt, "fmt " }, |
78 | | }; |
79 | | static const size_t wav_chunk_id_key_count = ARRAY_SIZE(wav_chunk_id_key_list); |
80 | | |
81 | | static int |
82 | | wav_chunk_CompareCb(const void *a, const void *b) |
83 | 288k | { |
84 | 288k | const struct wav_chunk_id_key *id = b; |
85 | 288k | return memcmp(a, id->key, 4); |
86 | 288k | } |
87 | | |
88 | | static int Demux( demux_t *p_demux ) |
89 | 5.81M | { |
90 | 5.81M | demux_sys_t *p_sys = p_demux->p_sys; |
91 | 5.81M | block_t *p_block; |
92 | 5.81M | const int64_t i_pos = vlc_stream_Tell( p_demux->s ); |
93 | 5.81M | unsigned int i_read_size = p_sys->i_frame_size; |
94 | 5.81M | uint32_t i_read_samples = p_sys->i_frame_samples; |
95 | | |
96 | | |
97 | 5.81M | if( p_sys->i_data_size > 0 ) |
98 | 5.81M | { |
99 | 5.81M | int64_t i_end = p_sys->i_data_pos + p_sys->i_data_size; |
100 | 5.81M | if ( i_pos >= i_end ) |
101 | 725 | return VLC_DEMUXER_EOF; /* EOF */ |
102 | | |
103 | | /* Don't read past data chunk boundary */ |
104 | 5.81M | if ( i_end < i_pos + i_read_size ) |
105 | 535 | { |
106 | 535 | i_read_size = i_end - i_pos; |
107 | 535 | i_read_samples = ( p_sys->i_frame_size - i_read_size ) |
108 | 535 | * p_sys->i_frame_samples / p_sys->i_frame_size; |
109 | 535 | } |
110 | 5.81M | } |
111 | | |
112 | 5.81M | if( ( p_block = vlc_stream_Block( p_demux->s, i_read_size ) ) == NULL ) |
113 | 530 | { |
114 | 530 | msg_Warn( p_demux, "cannot read data" ); |
115 | 530 | return VLC_DEMUXER_EOF; |
116 | 530 | } |
117 | | |
118 | 5.80M | p_block->i_dts = |
119 | 5.80M | p_block->i_pts = date_Get( &p_sys->pts ); |
120 | | |
121 | | /* set PCR */ |
122 | 5.80M | es_out_SetPCR( p_demux->out, p_block->i_pts ); |
123 | | |
124 | | /* Do the channel reordering */ |
125 | 5.80M | if( p_sys->i_chans_to_reorder ) |
126 | 2.43k | aout_ChannelReorder( p_block->p_buffer, p_block->i_buffer, |
127 | 2.43k | p_sys->i_chans_to_reorder, |
128 | 2.43k | p_sys->pi_chan_table, p_sys->fmt.i_codec ); |
129 | | |
130 | 5.80M | es_out_Send( p_demux->out, p_sys->p_es, p_block ); |
131 | | |
132 | 5.80M | date_Increment( &p_sys->pts, i_read_samples ); |
133 | | |
134 | 5.80M | return VLC_DEMUXER_SUCCESS; |
135 | 5.81M | } |
136 | | |
137 | | static int Control( demux_t *p_demux, int i_query, va_list args ) |
138 | 0 | { |
139 | 0 | demux_sys_t *p_sys = p_demux->p_sys; |
140 | 0 | int64_t i_end = -1; |
141 | |
|
142 | 0 | if( p_sys->i_data_size > 0 ) |
143 | 0 | i_end = p_sys->i_data_pos + p_sys->i_data_size; |
144 | |
|
145 | 0 | int ret = demux_vaControlHelper( p_demux->s, p_sys->i_data_pos, i_end, |
146 | 0 | p_sys->fmt.i_bitrate, |
147 | 0 | p_sys->fmt.audio.i_blockalign, |
148 | 0 | i_query, args ); |
149 | 0 | if( ret != VLC_SUCCESS ) |
150 | 0 | return ret; |
151 | | |
152 | | /* Update the date to the new seek point */ |
153 | 0 | switch( i_query ) |
154 | 0 | { |
155 | 0 | case DEMUX_SET_POSITION: |
156 | 0 | case DEMUX_SET_TIME: |
157 | 0 | { |
158 | 0 | uint64_t ofs = vlc_stream_Tell( p_demux->s ); |
159 | 0 | if( unlikely( ofs < p_sys->i_data_pos ) ) |
160 | 0 | return VLC_SUCCESS; |
161 | | |
162 | 0 | ofs -= p_sys->i_data_pos; |
163 | 0 | vlc_tick_t pts = VLC_TICK_0 + |
164 | 0 | vlc_tick_from_samples( ofs * 8, p_sys->fmt.i_bitrate ); |
165 | 0 | date_Set( &p_sys->pts, pts ); |
166 | 0 | break; |
167 | 0 | } |
168 | 0 | default: |
169 | 0 | break; |
170 | 0 | } |
171 | 0 | return VLC_SUCCESS; |
172 | 0 | } |
173 | | |
174 | | static int ChunkSkip( demux_t *p_demux, uint32_t i_size ) |
175 | 142k | { |
176 | 142k | i_size += i_size & 1; |
177 | | |
178 | 142k | if( unlikely( i_size >= 65536 ) ) |
179 | 381 | { |
180 | | /* Arbitrary size where a seek should be performed instead of skipping |
181 | | * by reading NULL. Non data chunks are generally smaller than this. |
182 | | * This seek may be used to skip the data chunk if there is an other |
183 | | * chunk after it (unlikely). */ |
184 | 381 | return vlc_stream_Seek( p_demux->s, |
185 | 381 | vlc_stream_Tell( p_demux->s ) + i_size ); |
186 | 381 | } |
187 | | |
188 | 142k | return vlc_stream_Read( p_demux->s, NULL, i_size ) != i_size ? VLC_EGENERIC : VLC_SUCCESS; |
189 | 142k | } |
190 | | |
191 | | static int ChunkGetNext( demux_t *p_demux, enum wav_chunk_id *p_id, |
192 | | uint32_t *pi_size ) |
193 | 7.70k | { |
194 | 7.70k | #ifndef NDEBUG |
195 | | /* assert that keys are in alphabetical order */ |
196 | 23.1k | for( size_t i = 0; i < wav_chunk_id_key_count - 1; ++i ) |
197 | 15.4k | assert( strcmp( wav_chunk_id_key_list[i].key, |
198 | 7.70k | wav_chunk_id_key_list[i + 1].key ) < 0 ); |
199 | 7.70k | #endif |
200 | | |
201 | 7.70k | for( ;; ) |
202 | 146k | { |
203 | 146k | const uint8_t *p_peek; |
204 | 146k | if( vlc_stream_Peek( p_demux->s, &p_peek, 8 ) < 8 ) |
205 | 568 | return VLC_EGENERIC; |
206 | | |
207 | 145k | const struct wav_chunk_id_key *id = |
208 | 145k | bsearch( p_peek, wav_chunk_id_key_list, wav_chunk_id_key_count, |
209 | 145k | sizeof(*wav_chunk_id_key_list), wav_chunk_CompareCb ); |
210 | 145k | uint32_t i_size = GetDWLE( p_peek + 4 ); |
211 | | |
212 | 145k | if( id == NULL ) |
213 | 138k | { |
214 | 138k | msg_Warn( p_demux, "unknown chunk '%4.4s' of size: %u", |
215 | 138k | p_peek, i_size ); |
216 | | |
217 | 138k | if( vlc_stream_Read( p_demux->s, NULL, 8 ) != 8 ) |
218 | 0 | return VLC_EGENERIC; |
219 | | |
220 | 138k | if( ChunkSkip( p_demux, i_size ) != VLC_SUCCESS ) |
221 | 91 | return VLC_EGENERIC; |
222 | 138k | continue; |
223 | 138k | } |
224 | | |
225 | 7.04k | if( vlc_stream_Read( p_demux->s, NULL, 8 ) != 8 ) |
226 | 0 | return VLC_EGENERIC; |
227 | | |
228 | 7.04k | *p_id = id->id; |
229 | 7.04k | *pi_size = i_size; |
230 | | |
231 | 7.04k | return VLC_SUCCESS; |
232 | 7.04k | } |
233 | 7.70k | } |
234 | | |
235 | | static int FrameInfo_PCM( unsigned int *pi_size, unsigned *pi_samples, |
236 | | const es_format_t *p_fmt ) |
237 | 824 | { |
238 | 824 | int i_bytes; |
239 | | |
240 | 824 | if( p_fmt->audio.i_rate > 352800 |
241 | 794 | || p_fmt->audio.i_bitspersample > 64 |
242 | 778 | || p_fmt->audio.i_channels > WAV_CHAN_MAX ) |
243 | 48 | return VLC_EGENERIC; |
244 | | |
245 | | /* read samples for 50ms of */ |
246 | 776 | *pi_samples = __MAX( p_fmt->audio.i_rate / 20, 1 ); |
247 | | |
248 | 776 | i_bytes = *pi_samples * p_fmt->audio.i_channels * |
249 | 776 | ( (p_fmt->audio.i_bitspersample + 7) / 8 ); |
250 | | |
251 | 776 | if( p_fmt->audio.i_blockalign > 0 ) |
252 | 495 | { |
253 | 495 | const int i_modulo = i_bytes % p_fmt->audio.i_blockalign; |
254 | 495 | if( i_modulo > 0 ) |
255 | 390 | i_bytes += p_fmt->audio.i_blockalign - i_modulo; |
256 | 495 | } |
257 | | |
258 | 776 | *pi_size = i_bytes; |
259 | 776 | return VLC_SUCCESS; |
260 | 824 | } |
261 | | |
262 | | static int FrameInfo_MS_ADPCM( unsigned int *pi_size, unsigned *pi_samples, |
263 | | const es_format_t *p_fmt ) |
264 | 247 | { |
265 | 247 | if( p_fmt->audio.i_channels == 0 ) |
266 | 3 | return VLC_EGENERIC; |
267 | | |
268 | 244 | if( p_fmt->audio.i_blockalign < 7 * p_fmt->audio.i_channels ) |
269 | 4 | return VLC_EGENERIC; |
270 | | |
271 | 240 | *pi_samples = 2 + 2 * ( p_fmt->audio.i_blockalign - |
272 | 240 | 7 * p_fmt->audio.i_channels ) / p_fmt->audio.i_channels; |
273 | 240 | *pi_size = p_fmt->audio.i_blockalign; |
274 | | |
275 | 240 | return VLC_SUCCESS; |
276 | 244 | } |
277 | | |
278 | | static int FrameInfo_IMA_ADPCM( unsigned int *pi_size, unsigned *pi_samples, |
279 | | const es_format_t *p_fmt ) |
280 | 243 | { |
281 | 243 | if( p_fmt->audio.i_channels == 0 ) |
282 | 2 | return VLC_EGENERIC; |
283 | | |
284 | 241 | if( p_fmt->audio.i_blockalign < 4 * p_fmt->audio.i_channels ) |
285 | 2 | return VLC_EGENERIC; |
286 | | |
287 | 239 | *pi_samples = 2 * ( p_fmt->audio.i_blockalign - |
288 | 239 | 4 * p_fmt->audio.i_channels ) / p_fmt->audio.i_channels; |
289 | 239 | *pi_size = p_fmt->audio.i_blockalign; |
290 | | |
291 | 239 | return VLC_SUCCESS; |
292 | 241 | } |
293 | | |
294 | | static int FrameInfo_Creative_ADPCM( unsigned int *pi_size, unsigned *pi_samples, |
295 | | const es_format_t *p_fmt ) |
296 | 25 | { |
297 | 25 | if( p_fmt->audio.i_channels == 0 ) |
298 | 1 | return VLC_EGENERIC; |
299 | | |
300 | | /* 4 bits / sample */ |
301 | 24 | *pi_samples = p_fmt->audio.i_blockalign * 2 / p_fmt->audio.i_channels; |
302 | 24 | *pi_size = p_fmt->audio.i_blockalign; |
303 | | |
304 | 24 | return VLC_SUCCESS; |
305 | 25 | } |
306 | | |
307 | | static int FrameInfo_MSGSM( unsigned int *pi_size, unsigned *pi_samples, |
308 | | const es_format_t *p_fmt ) |
309 | 250 | { |
310 | 250 | if( p_fmt->i_bitrate <= 0 ) |
311 | 1 | return VLC_EGENERIC; |
312 | | |
313 | 249 | *pi_samples = ( p_fmt->audio.i_blockalign * p_fmt->audio.i_rate * 8) |
314 | 249 | / p_fmt->i_bitrate; |
315 | 249 | *pi_size = p_fmt->audio.i_blockalign; |
316 | | |
317 | 249 | return VLC_SUCCESS; |
318 | 250 | } |
319 | | static void Close ( vlc_object_t * p_this ) |
320 | 1.25k | { |
321 | 1.25k | demux_t *p_demux = (demux_t*)p_this; |
322 | 1.25k | demux_sys_t *p_sys = p_demux->p_sys; |
323 | | |
324 | 1.25k | es_format_Clean( &p_sys->fmt ); |
325 | 1.25k | free( p_sys ); |
326 | 1.25k | } |
327 | | |
328 | | static int ChunkParseDS64( demux_t *p_demux, uint32_t i_size ) |
329 | 2.35k | { |
330 | 2.35k | demux_sys_t *p_sys = p_demux->p_sys; |
331 | 2.35k | const uint8_t *p_peek; |
332 | | |
333 | 2.35k | if( i_size < 24 ) |
334 | 5 | { |
335 | 5 | msg_Err( p_demux, "invalid 'ds64' chunk" ); |
336 | 5 | return VLC_EGENERIC; |
337 | 5 | } |
338 | | |
339 | 2.34k | if( vlc_stream_Peek( p_demux->s, &p_peek, 24 ) < 24 ) |
340 | 70 | return VLC_EGENERIC; |
341 | | |
342 | 2.27k | p_sys->i_data_size = GetQWLE( &p_peek[8] ); |
343 | | |
344 | 2.27k | return ChunkSkip( p_demux, i_size ); |
345 | 2.34k | } |
346 | | |
347 | | static void InitFmt( demux_t *p_demux ) |
348 | 2.92k | { |
349 | 2.92k | demux_sys_t *p_sys = p_demux->p_sys; |
350 | | |
351 | 2.92k | es_format_Init( &p_sys->fmt, AUDIO_ES, 0 ); |
352 | 2.92k | p_sys->i_frame_size = 0; |
353 | 2.92k | p_sys->i_frame_samples = 0; |
354 | 2.92k | p_sys->i_chans_to_reorder = 0; |
355 | 2.92k | p_sys->i_channel_mask = 0; |
356 | 2.92k | } |
357 | | |
358 | | static int ChunkParseFmt( demux_t *p_demux, uint32_t i_size ) |
359 | 1.93k | { |
360 | 1.93k | demux_sys_t *p_sys = p_demux->p_sys; |
361 | 1.93k | WAVEFORMATEXTENSIBLE *p_wf_ext = NULL; |
362 | 1.93k | WAVEFORMATEX *p_wf = NULL; |
363 | 1.93k | const char *psz_name; |
364 | 1.93k | unsigned int i_extended; |
365 | | |
366 | 1.93k | i_size += 2; |
367 | 1.93k | if( i_size < sizeof( WAVEFORMATEX ) || i_size > (sizeof( WAVEFORMATEX ) + UINT16_MAX ) ) |
368 | 30 | { |
369 | 30 | msg_Err( p_demux, "invalid 'fmt ' chunk of size %" PRIu32, i_size ); |
370 | 30 | goto error; |
371 | 30 | } |
372 | | |
373 | | /* load waveformatex */ |
374 | 1.90k | p_wf_ext = malloc( i_size ); |
375 | 1.90k | if( unlikely( !p_wf_ext ) ) |
376 | 0 | goto error; |
377 | | |
378 | 1.90k | p_wf = &p_wf_ext->Format; |
379 | 1.90k | p_wf->cbSize = 0; |
380 | 1.90k | i_size -= 2; |
381 | 1.90k | if( vlc_stream_Read( p_demux->s, p_wf, i_size ) != (int)i_size || |
382 | 1.86k | ( ( i_size & 1 ) && vlc_stream_Read( p_demux->s, NULL, 1 ) != 1 ) ) |
383 | 46 | { |
384 | 46 | msg_Err( p_demux, "cannot load 'fmt ' chunk" ); |
385 | 46 | goto error; |
386 | 46 | } |
387 | | |
388 | 1.86k | wf_tag_to_fourcc( GetWLE( &p_wf->wFormatTag ), &p_sys->fmt.i_codec, |
389 | 1.86k | &psz_name ); |
390 | 1.86k | p_sys->fmt.audio.i_channels = GetWLE ( &p_wf->nChannels ); |
391 | 1.86k | p_sys->fmt.audio.i_rate = GetDWLE( &p_wf->nSamplesPerSec ); |
392 | 1.86k | p_sys->fmt.audio.i_blockalign = GetWLE( &p_wf->nBlockAlign ); |
393 | 1.86k | p_sys->fmt.i_bitrate = GetDWLE( &p_wf->nAvgBytesPerSec ) * 8; |
394 | 1.86k | p_sys->fmt.audio.i_bitspersample = GetWLE( &p_wf->wBitsPerSample ); |
395 | 1.86k | if( i_size >= sizeof(WAVEFORMATEX) ) |
396 | 319 | p_sys->fmt.i_extra = __MIN( GetWLE( &p_wf->cbSize ), i_size - sizeof(WAVEFORMATEX) ); |
397 | 1.86k | i_extended = 0; |
398 | | |
399 | | /* Handle new WAVE_FORMAT_EXTENSIBLE wav files */ |
400 | | /* see the following link for more information: |
401 | | * http://www.microsoft.com/whdc/device/audio/multichaud.mspx#EFAA */ |
402 | 1.86k | if( GetWLE( &p_wf->wFormatTag ) == WAVE_FORMAT_EXTENSIBLE && |
403 | 252 | i_size >= sizeof( WAVEFORMATEXTENSIBLE ) && |
404 | 240 | ( p_sys->fmt.i_extra + sizeof( WAVEFORMATEX ) |
405 | 240 | >= sizeof( WAVEFORMATEXTENSIBLE ) ) ) |
406 | 234 | { |
407 | 234 | unsigned i_channel_mask; |
408 | 234 | GUID guid_subformat; |
409 | | |
410 | 234 | guid_subformat = p_wf_ext->SubFormat; |
411 | 234 | guid_subformat.Data1 = GetDWLE( &p_wf_ext->SubFormat.Data1 ); |
412 | 234 | guid_subformat.Data2 = GetWLE( &p_wf_ext->SubFormat.Data2 ); |
413 | 234 | guid_subformat.Data3 = GetWLE( &p_wf_ext->SubFormat.Data3 ); |
414 | | |
415 | 234 | sf_tag_to_fourcc( &guid_subformat, &p_sys->fmt.i_codec, &psz_name ); |
416 | | |
417 | 234 | msg_Dbg( p_demux, "extensible format guid " GUID_FMT, GUID_PRINT(guid_subformat) ); |
418 | | |
419 | 234 | i_extended = sizeof( WAVEFORMATEXTENSIBLE ) - sizeof( WAVEFORMATEX ); |
420 | 234 | p_sys->fmt.i_extra -= i_extended; |
421 | | |
422 | 234 | i_channel_mask = GetDWLE( &p_wf_ext->dwChannelMask ); |
423 | 234 | if( i_channel_mask ) |
424 | 232 | { |
425 | 232 | int i_match = 0; |
426 | 232 | p_sys->i_channel_mask = getChannelMask( &i_channel_mask, p_sys->fmt.audio.i_channels, &i_match ); |
427 | 232 | if( i_channel_mask ) |
428 | 232 | msg_Warn( p_demux, "Some channels are unrecognized or uselessly specified (0x%x)", i_channel_mask ); |
429 | 232 | if( i_match < p_sys->fmt.audio.i_channels ) |
430 | 113 | { |
431 | 113 | int i_missing = p_sys->fmt.audio.i_channels - i_match; |
432 | 113 | msg_Warn( p_demux, "Trying to fill up unspecified position for %d channels", p_sys->fmt.audio.i_channels - i_match ); |
433 | | |
434 | 113 | static const uint32_t pi_pair[] = { AOUT_CHAN_REARLEFT|AOUT_CHAN_REARRIGHT, |
435 | 113 | AOUT_CHAN_MIDDLELEFT|AOUT_CHAN_MIDDLERIGHT, |
436 | 113 | AOUT_CHAN_LEFT|AOUT_CHAN_RIGHT }; |
437 | | /* FIXME: Unused yet |
438 | | static const uint32_t pi_center[] = { AOUT_CHAN_REARCENTER, |
439 | | 0, |
440 | | AOUT_CHAN_CENTER }; */ |
441 | | |
442 | | /* Try to complete with pair */ |
443 | 452 | for( unsigned i = 0; i < ARRAY_SIZE(pi_pair); i++ ) |
444 | 339 | { |
445 | 339 | if( i_missing >= 2 && !(p_sys->i_channel_mask & pi_pair[i] ) ) |
446 | 101 | { |
447 | 101 | i_missing -= 2; |
448 | 101 | p_sys->i_channel_mask |= pi_pair[i]; |
449 | 101 | } |
450 | 339 | } |
451 | | /* Well fill up with what we can */ |
452 | 833 | for( unsigned i = 0; i < ARRAY_SIZE(pi_channels_aout) && i_missing > 0; i++ ) |
453 | 753 | { |
454 | 753 | if( !( p_sys->i_channel_mask & pi_channels_aout[i] ) ) |
455 | 297 | { |
456 | 297 | p_sys->i_channel_mask |= pi_channels_aout[i]; |
457 | 297 | i_missing--; |
458 | | |
459 | 297 | if( i_missing <= 0 ) |
460 | 33 | break; |
461 | 297 | } |
462 | 753 | } |
463 | | |
464 | 113 | i_match = p_sys->fmt.audio.i_channels - i_missing; |
465 | 113 | } |
466 | 232 | if( i_match < p_sys->fmt.audio.i_channels ) |
467 | 67 | { |
468 | 67 | msg_Err( p_demux, "Invalid/unsupported channel mask" ); |
469 | 67 | p_sys->i_channel_mask = 0; |
470 | 67 | } |
471 | 232 | } |
472 | 234 | } |
473 | 1.86k | if( p_sys->i_channel_mask == 0 && p_sys->fmt.audio.i_channels > 2 |
474 | 1.03k | && p_sys->fmt.audio.i_channels <= AOUT_CHAN_MAX ) |
475 | 554 | { |
476 | | /* A dwChannelMask of 0 tells the audio device to render the first |
477 | | * channel to the first port on the device, the second channel to the |
478 | | * second port on the device, and so on. pi_default_channels is |
479 | | * different than pi_channels_aout. Indeed FLC/FRC must be treated a |
480 | | * SL/SR in that case. See "Default Channel Ordering" and "Details |
481 | | * about dwChannelMask" from msdn */ |
482 | | |
483 | 554 | static const uint32_t pi_default_channels[] = { |
484 | 554 | AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, AOUT_CHAN_CENTER, |
485 | 554 | AOUT_CHAN_LFE, AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, |
486 | 554 | AOUT_CHAN_MIDDLELEFT, AOUT_CHAN_MIDDLERIGHT, AOUT_CHAN_REARCENTER }; |
487 | | |
488 | 4.12k | for( unsigned i = 0; i < p_sys->fmt.audio.i_channels && |
489 | 3.57k | i < ARRAY_SIZE(pi_default_channels); |
490 | 3.57k | i++ ) |
491 | 3.57k | p_sys->i_channel_mask |= pi_default_channels[i]; |
492 | 554 | } |
493 | | |
494 | 1.86k | if( p_sys->i_channel_mask ) |
495 | 711 | { |
496 | 711 | if( p_sys->fmt.i_codec == VLC_FOURCC('a','r','a','w') || |
497 | 318 | p_sys->fmt.i_codec == VLC_FOURCC('a','f','l','t') ) |
498 | 513 | p_sys->i_chans_to_reorder = |
499 | 513 | aout_CheckChannelReorder( pi_channels_aout, NULL, |
500 | 513 | p_sys->i_channel_mask, |
501 | 513 | p_sys->pi_chan_table ); |
502 | | |
503 | 711 | msg_Dbg( p_demux, "channel mask: %x, reordering: %u", |
504 | 711 | p_sys->i_channel_mask, p_sys->i_chans_to_reorder ); |
505 | 711 | } |
506 | | |
507 | 1.86k | p_sys->fmt.audio.i_physical_channels = p_sys->i_channel_mask; |
508 | | |
509 | 1.86k | if( p_sys->fmt.i_extra > 0 ) |
510 | 73 | { |
511 | 73 | p_sys->fmt.p_extra = malloc( p_sys->fmt.i_extra ); |
512 | 73 | if( unlikely(!p_sys->fmt.p_extra) ) |
513 | 0 | { |
514 | 0 | p_sys->fmt.i_extra = 0; |
515 | 0 | goto error; |
516 | 0 | } |
517 | 73 | memcpy( p_sys->fmt.p_extra, (uint8_t *)p_wf + sizeof( WAVEFORMATEX ) + i_extended, |
518 | 73 | p_sys->fmt.i_extra ); |
519 | 73 | } |
520 | | |
521 | 1.86k | msg_Dbg( p_demux, "format: 0x%4.4x, fourcc: %4.4s, channels: %d, " |
522 | 1.86k | "freq: %u Hz, bitrate: %uKo/s, blockalign: %d, bits/samples: %d, " |
523 | 1.86k | "extra size: %zu", |
524 | 1.86k | GetWLE( &p_wf->wFormatTag ), (char *)&p_sys->fmt.i_codec, |
525 | 1.86k | p_sys->fmt.audio.i_channels, p_sys->fmt.audio.i_rate, |
526 | 1.86k | p_sys->fmt.i_bitrate / 8 / 1024, p_sys->fmt.audio.i_blockalign, |
527 | 1.86k | p_sys->fmt.audio.i_bitspersample, p_sys->fmt.i_extra ); |
528 | | |
529 | 1.86k | free( p_wf ); |
530 | 1.86k | p_wf = NULL; |
531 | | |
532 | 1.86k | switch( p_sys->fmt.i_codec ) |
533 | 1.86k | { |
534 | 506 | case VLC_FOURCC( 'a', 'r', 'a', 'w' ): |
535 | 646 | case VLC_FOURCC( 'a', 'f', 'l', 't' ): |
536 | 646 | case VLC_FOURCC( 'u', 'l', 'a', 'w' ): |
537 | 758 | case VLC_CODEC_ALAW: |
538 | 824 | case VLC_CODEC_MULAW: |
539 | 824 | if( FrameInfo_PCM( &p_sys->i_frame_size, &p_sys->i_frame_samples, |
540 | 824 | &p_sys->fmt ) ) |
541 | 48 | goto error; |
542 | 776 | p_sys->fmt.i_codec = |
543 | 776 | vlc_fourcc_GetCodecAudio( p_sys->fmt.i_codec, |
544 | 776 | p_sys->fmt.audio.i_bitspersample ); |
545 | 776 | if( p_sys->fmt.i_codec == 0 ) { |
546 | 27 | msg_Err( p_demux, "Unrecognized codec" ); |
547 | 27 | goto error; |
548 | 27 | } |
549 | 749 | break; |
550 | 749 | case VLC_CODEC_ADPCM_MS: |
551 | | /* FIXME not sure at all FIXME */ |
552 | 193 | case VLC_FOURCC( 'm', 's', 0x00, 0x61 ): |
553 | 247 | case VLC_FOURCC( 'm', 's', 0x00, 0x62 ): |
554 | 247 | if( FrameInfo_MS_ADPCM( &p_sys->i_frame_size, &p_sys->i_frame_samples, |
555 | 247 | &p_sys->fmt ) ) |
556 | 7 | goto error; |
557 | 240 | break; |
558 | 243 | case VLC_CODEC_ADPCM_IMA_WAV: |
559 | 243 | if( FrameInfo_IMA_ADPCM( &p_sys->i_frame_size, &p_sys->i_frame_samples, |
560 | 243 | &p_sys->fmt ) ) |
561 | 4 | goto error; |
562 | 239 | break; |
563 | 239 | case VLC_CODEC_ADPCM_CREATIVE: |
564 | 25 | if( FrameInfo_Creative_ADPCM( &p_sys->i_frame_size, &p_sys->i_frame_samples, |
565 | 25 | &p_sys->fmt ) ) |
566 | 1 | goto error; |
567 | 24 | break; |
568 | 24 | case VLC_CODEC_MPGA: |
569 | 6 | case VLC_CODEC_A52: |
570 | | /* FIXME set end of area FIXME */ |
571 | 6 | goto error; |
572 | 55 | case VLC_CODEC_GSM_MS: |
573 | 65 | case VLC_CODEC_ADPCM_G726: |
574 | 109 | case VLC_CODEC_TRUESPEECH: |
575 | 111 | case VLC_CODEC_ATRAC3P: |
576 | 118 | case VLC_CODEC_ATRAC3: |
577 | 150 | case VLC_CODEC_G723_1: |
578 | 250 | case VLC_CODEC_WMA2: |
579 | 250 | if( FrameInfo_MSGSM( &p_sys->i_frame_size, &p_sys->i_frame_samples, |
580 | 250 | &p_sys->fmt ) ) |
581 | 1 | goto error; |
582 | 249 | break; |
583 | 265 | default: |
584 | 265 | msg_Err( p_demux, "unsupported codec (%4.4s)", |
585 | 265 | (char*)&p_sys->fmt.i_codec ); |
586 | 265 | goto error; |
587 | 1.86k | } |
588 | | |
589 | 1.50k | if( p_sys->i_frame_size == 0 || p_sys->i_frame_samples == 0 ) |
590 | 44 | { |
591 | 44 | msg_Dbg( p_demux, "invalid frame size: 0 0" ); |
592 | 44 | goto error; |
593 | 44 | } |
594 | 1.45k | if( p_sys->fmt.audio.i_rate == 0 ) |
595 | 7 | { |
596 | 7 | msg_Dbg( p_demux, "invalid sample rate: 0" ); |
597 | 7 | goto error; |
598 | 7 | } |
599 | | |
600 | 1.45k | msg_Dbg( p_demux, "found %s audio format", psz_name ); |
601 | | |
602 | 1.45k | return VLC_SUCCESS; |
603 | | |
604 | 486 | error: |
605 | 486 | es_format_Clean( &p_sys->fmt ); |
606 | 486 | InitFmt( p_demux ); |
607 | 486 | free( p_wf ); |
608 | 486 | return VLC_EGENERIC; |
609 | 1.45k | } |
610 | | |
611 | | static int Open( vlc_object_t * p_this ) |
612 | 3.60k | { |
613 | 3.60k | demux_t *p_demux = (demux_t*)p_this; |
614 | 3.60k | demux_sys_t *p_sys; |
615 | | |
616 | 3.60k | const uint8_t *p_peek; |
617 | 3.60k | bool b_is_rf64; |
618 | 3.60k | uint32_t i_size; |
619 | | |
620 | | /* Is it a wav file ? */ |
621 | 3.60k | if( vlc_stream_Peek( p_demux->s, &p_peek, 12 ) < 12 ) |
622 | 8 | return VLC_EGENERIC; |
623 | | |
624 | 3.59k | b_is_rf64 = ( memcmp( p_peek, "RF64", 4 ) == 0 ); |
625 | 3.59k | if( ( !b_is_rf64 && memcmp( p_peek, "RIFF", 4 ) ) || |
626 | 2.45k | memcmp( &p_peek[8], "WAVE", 4 ) ) |
627 | 1.15k | { |
628 | 1.15k | return VLC_EGENERIC; |
629 | 1.15k | } |
630 | | |
631 | 2.43k | p_demux->pf_demux = Demux; |
632 | 2.43k | p_demux->pf_control = Control; |
633 | 2.43k | p_demux->p_sys = p_sys = malloc( sizeof( *p_sys ) ); |
634 | 2.43k | if( unlikely(!p_sys) ) |
635 | 0 | return VLC_ENOMEM; |
636 | | |
637 | 2.43k | p_sys->p_es = NULL; |
638 | 2.43k | p_sys->i_data_pos = p_sys->i_data_size = 0; |
639 | 2.43k | InitFmt( p_demux ); |
640 | | |
641 | | /* skip riff header */ |
642 | 2.43k | if( vlc_stream_Read( p_demux->s, NULL, 12 ) != 12 ) |
643 | 0 | goto error; |
644 | | |
645 | 2.43k | bool eof = false; |
646 | 2.43k | enum wav_chunk_id id; |
647 | 8.84k | while( !eof && ( ChunkGetNext( p_demux, &id, &i_size ) ) == VLC_SUCCESS ) |
648 | 7.04k | { |
649 | 7.04k | if( i_size == 0 ) |
650 | 2 | { |
651 | 2 | msg_Err( p_demux, "invalid chunk with a size 0"); |
652 | 2 | goto error; |
653 | 2 | } |
654 | | |
655 | 7.04k | switch( id ) |
656 | 7.04k | { |
657 | 2.49k | case wav_chunk_id_data: |
658 | 2.49k | { |
659 | 2.49k | uint64_t i_stream_size; |
660 | 2.49k | if( vlc_stream_GetSize( p_demux->s, &i_stream_size ) != VLC_SUCCESS ) |
661 | 0 | goto error; |
662 | 2.49k | p_sys->i_data_pos = vlc_stream_Tell( p_demux->s ); |
663 | | |
664 | 2.49k | if( !b_is_rf64 && i_stream_size >= i_size + p_sys->i_data_pos ) |
665 | 1.07k | p_sys->i_data_size = i_size; |
666 | | |
667 | 2.49k | if( likely( b_is_rf64 |
668 | 2.49k | || p_sys->i_data_pos + i_size == i_stream_size ) ) |
669 | 1.14k | { |
670 | | /* Bypass the final ChunkGetNext() to avoid a read+seek |
671 | | * since this chunk is the last one */ |
672 | 1.14k | eof = true; |
673 | 1.14k | } /* Unlikely case where there is a chunk after 'data' */ |
674 | 1.35k | else if( ChunkSkip( p_demux, i_size ) != VLC_SUCCESS ) |
675 | 41 | goto error; |
676 | 2.45k | break; |
677 | 2.49k | } |
678 | 2.45k | case wav_chunk_id_ds64: |
679 | 2.35k | if( b_is_rf64 ) |
680 | 2.35k | { |
681 | 2.35k | if( ChunkParseDS64( p_demux, i_size ) != VLC_SUCCESS ) |
682 | 102 | goto error; |
683 | 2.35k | } |
684 | 4 | else |
685 | 4 | { |
686 | 4 | msg_Err( p_demux, "'ds64' chunk found but format not RF64" ); |
687 | 4 | goto error; |
688 | 4 | } |
689 | 2.24k | break; |
690 | 2.24k | case wav_chunk_id_fmt: |
691 | 2.19k | if( p_sys->i_frame_samples != 0 ) |
692 | 255 | { |
693 | 255 | ChunkSkip( p_demux, i_size ); |
694 | 255 | msg_Warn( p_demux, "fmt chunk already parsed" ); |
695 | 255 | break; |
696 | 255 | } |
697 | 1.93k | if( ChunkParseFmt( p_demux, i_size ) != VLC_SUCCESS ) |
698 | 486 | goto error; |
699 | 1.45k | break; |
700 | 7.04k | } |
701 | 7.04k | } |
702 | | |
703 | 1.80k | if( p_sys->i_data_pos == 0 || p_sys->i_data_size == 0 |
704 | 1.31k | || p_sys->i_frame_samples == 0 ) |
705 | 547 | { |
706 | 547 | msg_Err( p_demux, "'%s' chunk not found", |
707 | 547 | p_sys->i_data_pos == 0 ? "data" : |
708 | 547 | p_sys->i_frame_samples == 0 ? "fmt " : |
709 | 547 | b_is_rf64 ? "ds64" : "data" ); |
710 | 547 | goto error; |
711 | 547 | } |
712 | | |
713 | | /* Seek back to data position if needed */ |
714 | 1.25k | if( unlikely( vlc_stream_Tell( p_demux->s ) != p_sys->i_data_pos ) |
715 | 158 | && vlc_stream_Seek( p_demux->s, p_sys->i_data_pos ) != VLC_SUCCESS ) |
716 | 0 | goto error; |
717 | | |
718 | 1.25k | if( p_sys->fmt.i_bitrate <= 0 ) |
719 | 119 | { |
720 | 119 | p_sys->fmt.i_bitrate = (int64_t)p_sys->i_frame_size * |
721 | 119 | p_sys->fmt.audio.i_rate * 8 / p_sys->i_frame_samples; |
722 | 119 | } |
723 | | |
724 | 1.25k | p_sys->fmt.i_id = 0; |
725 | 1.25k | p_sys->p_es = es_out_Add( p_demux->out, &p_sys->fmt ); |
726 | 1.25k | if( unlikely(p_sys->p_es == NULL) ) |
727 | 0 | goto error; |
728 | | |
729 | 1.25k | date_Init( &p_sys->pts, p_sys->fmt.audio.i_rate, 1 ); |
730 | 1.25k | date_Set( &p_sys->pts, VLC_TICK_0 ); |
731 | | |
732 | 1.25k | return VLC_SUCCESS; |
733 | | |
734 | 1.18k | error: |
735 | 1.18k | es_format_Clean( &p_sys->fmt ); |
736 | 1.18k | free( p_sys ); |
737 | 1.18k | return VLC_EGENERIC; |
738 | 1.25k | } |
739 | | |
740 | 104 | vlc_module_begin () |
741 | 52 | set_description( N_("WAV demuxer") ) |
742 | 52 | set_subcategory( SUBCAT_INPUT_DEMUX ) |
743 | 52 | set_capability( "demux", 142 ) |
744 | 104 | set_callbacks( Open, Close ) |
745 | 52 | vlc_module_end () |