1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 2014-2015 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 */
27 #define LSMASH_IMPORTER_INTERNAL
30 /*********************************************************************************
31 Waveform Audio File Format (WAVE) importer
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 )
63 uint32_t nSamplesPerSec
;
64 uint32_t nAvgBytesPerSec
;
66 uint16_t wBitsPerSample
;
68 } waveformat_extended_t
;
72 waveformat_extended_t wfx
;
75 uint16_t wValidBitsPerSample
;
76 uint16_t wSamplesPerBlock
;
79 uint32_t dwChannelMask
;
81 } waveformat_extensible_t
;
85 uint32_t number_of_samples
;
88 waveformat_extensible_t fmt
;
89 isom_portable_chunk_t chunk
;
92 static void remove_wave_importer( wave_importer_t
*wave_imp
)
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
);
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
)
125 if( wave_imp
->number_of_samples
/ summary
->samples_in_frame
> wave_imp
->au_number
)
126 wave_imp
->au_length
= summary
->bytes_per_frame
;
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 )
134 lsmash_sample_t
*sample
= lsmash_create_sample( wave_imp
->au_length
);
136 return LSMASH_ERR_MEMORY_ALLOC
;
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
:
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
;
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
);
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
);
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
;
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
);
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
);
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
;
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
);
244 lsmash_cleanup_summary( (lsmash_summary_t
*)summary
);
248 static int wave_importer_probe( importer_t
*importer
)
250 wave_importer_t
*wave_imp
= create_wave_importer( importer
);
252 return LSMASH_ERR_MEMORY_ALLOC
;
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
;
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
);
272 case LSMASH_4CC( 'f', 'm', 't', ' ' ) :
275 err
= LSMASH_ERR_INVALID_DATA
;
278 if( (err
= wave_parse_fmt_chunk( wave_imp
, bs
)) < 0 )
280 fmt_chunk_present
= 1;
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
;
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;
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
;
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
;
319 * Treat WAVE file format as if it's QuickTime file format. */
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 )
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 )
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
;
344 if( (err
= lsmash_add_entry( importer
->summaries
, summary
)) < 0 )
346 lsmash_cleanup_summary( (lsmash_summary_t
*)summary
);
349 importer
->info
= wave_imp
;
350 importer
->status
= IMPORTER_OK
;
353 lsmash_importer_break_fake_movie( importer
);
354 remove_wave_importer( wave_imp
);
355 importer
->file
->qt_compatible
= 0;
356 importer
->info
= NULL
;
360 static uint32_t wave_importer_get_last_delta( importer_t
*importer
, uint32_t track_number
)
362 debug_if( !importer
|| !importer
->info
)
364 wave_importer_t
*wave_imp
= (wave_importer_t
*)importer
->info
;
365 if( !wave_imp
|| track_number
!= 1 || importer
->status
!= IMPORTER_EOF
)
367 lsmash_audio_summary_t
*summary
= (lsmash_audio_summary_t
*)lsmash_get_entry_data( importer
->summaries
, track_number
);
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
);
379 return LSMASH_ERR_NAMELESS
;
380 isom_timeline_t
*timeline
= isom_timeline_create();
382 return LSMASH_ERR_NAMELESS
;
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
;
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 )
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
)
408 if( wave_imp
->number_of_samples
- samples
>= summary
->samples_in_frame
)
409 duration
= summary
->samples_in_frame
;
411 duration
= wave_imp
->number_of_samples
- samples
;
412 isom_lpcm_bunch_t bunch
=
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 )
427 if( (err
= lsmash_add_entry( file
->timeline
, timeline
)) < 0 )
431 isom_timeline_destroy( timeline
);
432 isom_remove_timelines( file
);
436 const importer_functions wave_importer
=
438 { "WAVE", offsetof( importer_t
, log_level
) },
441 wave_importer_get_accessunit
,
442 wave_importer_get_last_delta
,
443 wave_importer_cleanup
,
444 wave_importer_construct_timeline