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