Update copyright dates.
[L-SMASH.git] / importer / wave_imp.c
bloba5bad0bbbdb3843681896eddaf05b903fb006517
1 /*****************************************************************************
2 * wave_imp.c
3 *****************************************************************************
4 * Copyright (C) 2014-2017 L-SMASH project
6 * Authors: Yusuke Nakamura <muken.the.vfrmaniac@gmail.com>
8 * Permission to use, copy, modify, and/or distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 *****************************************************************************/
21 /* This file is available under an ISC license. */
23 #include "common/internal.h" /* must be placed first */
25 #include <string.h>
27 #define LSMASH_IMPORTER_INTERNAL
28 #include "importer.h"
30 /*********************************************************************************
31 Waveform Audio File Format (WAVE) importer
33 References
34 Multimedia Programming Interface and Data Specifications 1.0
35 New Multimedia Data Types and Data Techniques April 15, 1994 Revision: 3.0
36 Multiple channel audio data and WAVE files March 7, 2007
37 Microsoft Windows SDK MMReg.h
38 **********************************************************************************/
39 #include "core/timeline.h"
41 #define WAVE_MIN_FILESIZE 45
43 #define WAVE_FORMAT_TYPE_ID_PCM 0x0001 /* WAVE_FORMAT_PCM */
44 #define WAVE_FORMAT_TYPE_ID_EXTENSIBLE 0xFFFE /* WAVE_FORMAT_EXTENSIBLE */
46 #define PASS_GUID( _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15 ) \
47 { _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15 }
48 #define DEFINE_WAVEFORMAT_EXTENSIBLE_SUBTYPE_GUID( name, value ) \
49 static const uint8_t name[16] = value
51 /* KSDATAFORMAT_SUBTYPE_PCM := 00000001-0000-0010-8000-00aa00389b71 */
52 DEFINE_WAVEFORMAT_EXTENSIBLE_SUBTYPE_GUID
54 WAVEFORMAT_EXTENSIBLE_SUBTYPE_GUID_PCM,
55 PASS_GUID( 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
56 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 )
59 typedef struct
61 uint16_t wFormatTag;
62 uint16_t nChannels;
63 uint32_t nSamplesPerSec;
64 uint32_t nAvgBytesPerSec;
65 uint16_t nBlockAlign;
66 uint16_t wBitsPerSample;
67 uint16_t cbSize;
68 } waveformat_extended_t;
70 typedef struct
72 waveformat_extended_t wfx;
73 union
75 uint16_t wValidBitsPerSample;
76 uint16_t wSamplesPerBlock;
77 uint16_t wReserved;
78 } Samples;
79 uint32_t dwChannelMask;
80 uint8_t guid[16];
81 } waveformat_extensible_t;
83 typedef struct
85 uint32_t number_of_samples;
86 uint32_t au_length;
87 uint32_t au_number;
88 waveformat_extensible_t fmt;
89 isom_portable_chunk_t chunk;
90 } wave_importer_t;
92 static void remove_wave_importer( wave_importer_t *wave_imp )
94 if( !wave_imp )
95 return;
96 lsmash_free( wave_imp );
99 static wave_importer_t *create_wave_importer( importer_t *importer )
101 return (wave_importer_t *)lsmash_malloc_zero( sizeof(wave_importer_t) );
104 static void wave_importer_cleanup( importer_t *importer )
106 debug_if( importer && importer->info )
107 remove_wave_importer( importer->info );
110 static int wave_importer_get_accessunit( importer_t *importer, uint32_t track_number, lsmash_sample_t **p_sample )
112 if( !importer->info )
113 return LSMASH_ERR_NAMELESS;
114 if( track_number != 1 )
115 return LSMASH_ERR_FUNCTION_PARAM;
116 lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_get_entry_data( importer->summaries, track_number );
117 if( !summary )
118 return LSMASH_ERR_NAMELESS;
119 wave_importer_t *wave_imp = (wave_importer_t *)importer->info;
120 importer_status current_status = importer->status;
121 if( current_status == IMPORTER_ERROR )
122 return LSMASH_ERR_NAMELESS;
123 if( current_status == IMPORTER_EOF )
124 return IMPORTER_EOF;
125 if( wave_imp->number_of_samples / summary->samples_in_frame > wave_imp->au_number )
126 wave_imp->au_length = summary->bytes_per_frame;
127 else
129 wave_imp->au_length = wave_imp->fmt.wfx.nBlockAlign * (wave_imp->number_of_samples % summary->samples_in_frame);
130 importer->status = IMPORTER_EOF;
131 if( wave_imp->au_length == 0 )
132 return IMPORTER_EOF;
134 lsmash_sample_t *sample = lsmash_create_sample( wave_imp->au_length );
135 if( !sample )
136 return LSMASH_ERR_MEMORY_ALLOC;
137 *p_sample = sample;
138 if( lsmash_bs_get_bytes_ex( importer->bs, wave_imp->au_length, sample->data ) != wave_imp->au_length )
140 importer->status = IMPORTER_ERROR;
141 return LSMASH_ERR_INVALID_DATA;
143 sample->length = wave_imp->au_length;
144 sample->dts = wave_imp->au_number ++ * summary->samples_in_frame;
145 sample->cts = sample->dts;
146 sample->prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
147 return current_status;
150 static inline int wave_fmt_subtype_cmp( const waveformat_extensible_t *fmt, const uint8_t guid[16] )
152 return memcmp( fmt->guid, guid, 16 );
155 static int wave_parse_fmt_chunk( wave_importer_t *wave_imp, lsmash_bs_t *bs )
157 waveformat_extensible_t *fmt = &wave_imp->fmt;
158 waveformat_extended_t *wfx = &fmt->wfx;
159 wfx->wFormatTag = lsmash_bs_get_le16( bs );
160 wfx->nChannels = lsmash_bs_get_le16( bs );
161 wfx->nSamplesPerSec = lsmash_bs_get_le32( bs );
162 wfx->nAvgBytesPerSec = lsmash_bs_get_le32( bs );
163 wfx->nBlockAlign = lsmash_bs_get_le16( bs );
164 wfx->wBitsPerSample = lsmash_bs_get_le16( bs );
165 switch( wfx->wFormatTag )
167 case WAVE_FORMAT_TYPE_ID_PCM :
168 return 0;
169 case WAVE_FORMAT_TYPE_ID_EXTENSIBLE :
170 wfx->cbSize = lsmash_bs_get_le16( bs );
171 if( wfx->cbSize < 22 )
172 return LSMASH_ERR_INVALID_DATA;
173 fmt->Samples.wValidBitsPerSample = lsmash_bs_get_le16( bs );
174 fmt->dwChannelMask = lsmash_bs_get_le32( bs );
175 if( lsmash_bs_get_bytes_ex( bs, 16, fmt->guid ) != 16 )
176 return LSMASH_ERR_NAMELESS;
177 /* We support only PCM audio currently. */
178 if( wave_fmt_subtype_cmp( fmt, WAVEFORMAT_EXTENSIBLE_SUBTYPE_GUID_PCM ) )
179 return LSMASH_ERR_INVALID_DATA;
180 return 0;
181 default :
182 return LSMASH_ERR_NAMELESS;
186 static lsmash_audio_summary_t *wave_create_summary( waveformat_extensible_t *fmt )
188 lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_create_summary( LSMASH_SUMMARY_TYPE_AUDIO );
189 if( !summary )
190 return NULL;
191 waveformat_extended_t *wfx = &fmt->wfx;
192 summary->sample_type = QT_CODEC_TYPE_LPCM_AUDIO;
193 summary->aot = MP4A_AUDIO_OBJECT_TYPE_NULL;
194 summary->frequency = wfx->nSamplesPerSec;
195 summary->channels = wfx->nChannels;
196 summary->sample_size = wfx->wFormatTag == WAVE_FORMAT_TYPE_ID_EXTENSIBLE
197 ? fmt->Samples.wValidBitsPerSample
198 : wfx->wBitsPerSample;
199 summary->samples_in_frame = 1000; /* arbitrary */
200 summary->sbr_mode = MP4A_AAC_SBR_NOT_SPECIFIED;
201 summary->bytes_per_frame = wfx->nBlockAlign * summary->samples_in_frame;
202 summary->max_au_length = summary->bytes_per_frame;
203 lsmash_codec_specific_t *cs = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_FORMAT_SPECIFIC_FLAGS,
204 LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED );
205 if( !cs )
206 goto fail;
207 lsmash_qt_audio_format_specific_flags_t *lpcm = (lsmash_qt_audio_format_specific_flags_t *)cs->data.structured;
208 if( (summary->sample_size & 7) == 0 )
209 lpcm->format_flags |= QT_AUDIO_FORMAT_FLAG_PACKED;
210 else
211 lpcm->format_flags |= QT_AUDIO_FORMAT_FLAG_ALIGNED_HIGH;
212 if( summary->sample_size > 8 )
213 lpcm->format_flags |= QT_AUDIO_FORMAT_FLAG_SIGNED_INTEGER;
214 if( lsmash_add_entry( &summary->opaque->list, cs ) < 0 )
216 lsmash_destroy_codec_specific_data( cs );
217 goto fail;
219 if( wfx->wFormatTag == WAVE_FORMAT_TYPE_ID_EXTENSIBLE || wfx->nChannels > 2 )
221 cs = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_CHANNEL_LAYOUT,
222 LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED );
223 if( !cs )
224 goto fail;
225 lsmash_qt_audio_channel_layout_t *layout = (lsmash_qt_audio_channel_layout_t *)cs->data.structured;
226 if( wfx->wFormatTag == WAVE_FORMAT_TYPE_ID_EXTENSIBLE )
228 layout->channelLayoutTag = QT_CHANNEL_LAYOUT_USE_CHANNEL_BITMAP;
229 layout->channelBitmap = fmt->dwChannelMask;
231 else
233 layout->channelLayoutTag = QT_CHANNEL_LAYOUT_UNKNOWN | wfx->nChannels;
234 layout->channelBitmap = 0;
236 if( lsmash_add_entry( &summary->opaque->list, cs ) < 0 )
238 lsmash_destroy_codec_specific_data( cs );
239 goto fail;
242 return summary;
243 fail:
244 lsmash_cleanup_summary( (lsmash_summary_t *)summary );
245 return NULL;
248 static int wave_importer_probe( importer_t *importer )
250 wave_importer_t *wave_imp = create_wave_importer( importer );
251 if( !wave_imp )
252 return LSMASH_ERR_MEMORY_ALLOC;
253 int err = 0;
254 uint32_t filesize;
255 lsmash_bs_t *bs = importer->bs;
256 if( lsmash_bs_get_be32( bs ) != LSMASH_4CC( 'R', 'I', 'F', 'F' )
257 || ((filesize = lsmash_bs_get_le32( bs ) + 8) < WAVE_MIN_FILESIZE && filesize > 8)
258 || lsmash_bs_get_be32( bs ) != LSMASH_4CC( 'W', 'A', 'V', 'E' ) )
260 err = LSMASH_ERR_INVALID_DATA;
261 goto fail;
263 int fmt_chunk_present = 0;
264 int data_chunk_present = 0;
265 while( !bs->eob && !(fmt_chunk_present && data_chunk_present) )
267 uint32_t ckID = lsmash_bs_get_be32( bs );
268 uint32_t ckSize = lsmash_bs_get_le32( bs );
269 lsmash_bs_reset_counter( bs );
270 switch( ckID )
272 case LSMASH_4CC( 'f', 'm', 't', ' ' ) :
273 if( ckSize < 16 )
275 err = LSMASH_ERR_INVALID_DATA;
276 goto fail;
278 if( (err = wave_parse_fmt_chunk( wave_imp, bs )) < 0 )
279 goto fail;
280 fmt_chunk_present = 1;
281 break;
282 case LSMASH_4CC( 'd', 'a', 't', 'a' ) :
283 if( !fmt_chunk_present )
285 /* The 'fmt ' chunk must be present before the 'data' chunk. */
286 err = LSMASH_ERR_INVALID_DATA;
287 goto fail;
289 wave_imp->chunk.data_offset = lsmash_bs_get_stream_pos( bs );
290 wave_imp->chunk.length = ckSize;
291 wave_imp->chunk.number = 1;
292 wave_imp->chunk.file = importer->file;
293 wave_imp->number_of_samples = ckSize / wave_imp->fmt.wfx.nBlockAlign;
294 data_chunk_present = 1;
295 break;
296 default :
297 break;
299 if( !data_chunk_present )
301 /* Skip the rest of this chunk.
302 * Note that ckData is word-aligned even if ckSize is an odd number. */
303 uint32_t skip_size = ckSize;
304 if( skip_size & 1 )
305 skip_size++;
306 if( skip_size > lsmash_bs_count( bs ) )
308 skip_size -= lsmash_bs_count( bs );
309 lsmash_bs_read_seek( bs, skip_size, SEEK_CUR );
313 if( !(fmt_chunk_present && data_chunk_present) )
315 err = LSMASH_ERR_INVALID_DATA;
316 goto fail;
318 /* Make fake movie.
319 * Treat WAVE file format as if it's QuickTime file format. */
320 uint32_t track_ID;
321 lsmash_movie_parameters_t movie_param = { 0 };
322 lsmash_track_parameters_t track_param = { 0 };
323 lsmash_media_parameters_t media_param = { 0 };
324 importer->file->qt_compatible = 1;
325 if( (err = lsmash_importer_make_fake_movie( importer )) < 0
326 || (err = lsmash_importer_make_fake_track( importer, ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK, &track_ID )) < 0
327 || (err = lsmash_get_movie_parameters( importer->root, &movie_param )) < 0
328 || (err = lsmash_get_track_parameters( importer->root, track_ID, &track_param )) < 0
329 || (err = lsmash_get_media_parameters( importer->root, track_ID, &media_param )) < 0 )
330 goto fail;
331 movie_param.timescale = wave_imp->fmt.wfx.nSamplesPerSec;
332 media_param.timescale = wave_imp->fmt.wfx.nSamplesPerSec;
333 if( (err = lsmash_set_movie_parameters( importer->root, &movie_param )) < 0
334 || (err = lsmash_set_track_parameters( importer->root, track_ID, &track_param )) < 0
335 || (err = lsmash_set_media_parameters( importer->root, track_ID, &media_param )) < 0 )
336 goto fail;
337 lsmash_audio_summary_t *summary = wave_create_summary( &wave_imp->fmt );
338 if( !summary || lsmash_add_sample_entry( importer->root, track_ID, summary ) != 1 )
340 lsmash_cleanup_summary( (lsmash_summary_t *)summary );
341 err = LSMASH_ERR_NAMELESS;
342 goto fail;
344 if( (err = lsmash_add_entry( importer->summaries, summary )) < 0 )
346 lsmash_cleanup_summary( (lsmash_summary_t *)summary );
347 goto fail;
349 importer->info = wave_imp;
350 importer->status = IMPORTER_OK;
351 return 0;
352 fail:
353 lsmash_importer_break_fake_movie( importer );
354 remove_wave_importer( wave_imp );
355 importer->file->qt_compatible = 0;
356 importer->info = NULL;
357 return err;
360 static uint32_t wave_importer_get_last_delta( importer_t *importer, uint32_t track_number )
362 debug_if( !importer || !importer->info )
363 return 0;
364 wave_importer_t *wave_imp = (wave_importer_t *)importer->info;
365 if( !wave_imp || track_number != 1 || importer->status != IMPORTER_EOF )
366 return 0;
367 lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_get_entry_data( importer->summaries, track_number );
368 if( !summary )
369 return 0;
370 return wave_imp->number_of_samples / summary->samples_in_frame >= wave_imp->au_number
371 ? summary->samples_in_frame
372 : (wave_imp->number_of_samples % summary->samples_in_frame);
375 static int wave_importer_construct_timeline( importer_t *importer, uint32_t track_number )
377 lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_get_entry_data( importer->summaries, track_number );
378 if( !summary )
379 return LSMASH_ERR_NAMELESS;
380 isom_timeline_t *timeline = isom_timeline_create();
381 if( !timeline )
382 return LSMASH_ERR_NAMELESS;
383 int err;
384 lsmash_file_t *file = importer->file;
385 if( !file->timeline )
387 file->timeline = lsmash_create_entry_list();
388 if( !file->timeline )
390 err = LSMASH_ERR_MEMORY_ALLOC;
391 goto fail;
394 wave_importer_t *wave_imp = (wave_importer_t *)importer->info;
395 if( (err = isom_timeline_set_track_ID( timeline, 1 )) < 0
396 || (err = isom_timeline_set_movie_timescale( timeline, wave_imp->fmt.wfx.nSamplesPerSec )) < 0
397 || (err = isom_timeline_set_media_timescale( timeline, wave_imp->fmt.wfx.nSamplesPerSec )) < 0
398 || (err = isom_timeline_set_sample_count( timeline, wave_imp->number_of_samples )) < 0
399 || (err = isom_timeline_set_max_sample_size( timeline, summary->max_au_length )) < 0
400 || (err = isom_timeline_set_media_duration( timeline, wave_imp->number_of_samples )) < 0
401 || (err = isom_timeline_set_track_duration( timeline, wave_imp->number_of_samples )) < 0 )
402 goto fail;
403 isom_timeline_set_lpcm_sample_getter_funcs( timeline );
404 uint64_t data_offset = wave_imp->chunk.data_offset;
405 for( uint32_t samples = 0; samples < wave_imp->number_of_samples; samples += summary->samples_in_frame )
407 uint32_t duration;
408 if( wave_imp->number_of_samples - samples >= summary->samples_in_frame )
409 duration = summary->samples_in_frame;
410 else
411 duration = wave_imp->number_of_samples - samples;
412 isom_lpcm_bunch_t bunch =
414 .pos = data_offset,
415 .duration = 1,
416 .offset = 0,
417 .length = wave_imp->fmt.wfx.nBlockAlign,
418 .index = 1, /* no changes */
419 .chunk = &wave_imp->chunk,
420 .prop = (lsmash_sample_property_t){ .ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC },
421 .sample_count = duration
423 data_offset += duration * wave_imp->fmt.wfx.nBlockAlign;
424 if( (err = isom_add_lpcm_bunch_entry( timeline, &bunch )) < 0 )
425 goto fail;
427 if( (err = lsmash_add_entry( file->timeline, timeline )) < 0 )
428 goto fail;
429 return 0;
430 fail:
431 isom_timeline_destroy( timeline );
432 isom_remove_timelines( file );
433 return err;
436 const importer_functions wave_importer =
438 { "WAVE", offsetof( importer_t, log_level ) },
440 wave_importer_probe,
441 wave_importer_get_accessunit,
442 wave_importer_get_last_delta,
443 wave_importer_cleanup,
444 wave_importer_construct_timeline