1/*
2Copyright (c) 2012, Broadcom Europe Ltd
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are met:
7 * Redistributions of source code must retain the above copyright
8 notice, this list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above copyright
10 notice, this list of conditions and the following disclaimer in the
11 documentation and/or other materials provided with the distribution.
12 * Neither the name of the copyright holder nor the
13 names of its contributors may be used to endorse or promote products
14 derived from this software without specific prior written permission.
15
16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27#include <stdlib.h>
28#include <string.h>
29
30#define CONTAINER_IS_LITTLE_ENDIAN
31//#define ENABLE_CONTAINERS_LOG_FORMAT
32//#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE
33#define CONTAINER_HELPER_LOG_INDENT(a) 0
34#include "containers/core/containers_private.h"
35#include "containers/core/containers_io_helpers.h"
36#include "containers/core/containers_utils.h"
37#include "containers/core/containers_logging.h"
38#include "containers/core/containers_waveformat.h"
39
40/******************************************************************************
41Defines.
42******************************************************************************/
43#define WAV_EXTRADATA_MAX 16
44#define BLOCK_SIZE (16*1024)
45
46/******************************************************************************
47GUID list for the different codecs
48******************************************************************************/
49static const GUID_T atracx_guid = {0xbfaa23e9, 0x58cb, 0x7144, {0xa1, 0x19, 0xff, 0xfa, 0x01, 0xe4, 0xce, 0x62}};
50static const GUID_T pcm_guid = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
51
52/******************************************************************************
53Type definitions
54******************************************************************************/
55typedef struct VC_CONTAINER_MODULE_T
56{
57 uint64_t data_offset; /**< Offset to the start of the data packets */
58 int64_t data_size; /**< Size of the data contained in the data element */
59 uint32_t block_size; /**< Size of a block of audio data */
60 int64_t position;
61 uint64_t frame_data_left;
62
63 VC_CONTAINER_TRACK_T *track;
64 uint8_t extradata[WAV_EXTRADATA_MAX];
65
66} VC_CONTAINER_MODULE_T;
67
68/******************************************************************************
69Function prototypes
70******************************************************************************/
71VC_CONTAINER_STATUS_T wav_reader_open( VC_CONTAINER_T * );
72
73/******************************************************************************
74Local Functions
75******************************************************************************/
76
77/*****************************************************************************
78Functions exported as part of the Container Module API
79 *****************************************************************************/
80
81static VC_CONTAINER_STATUS_T wav_reader_read( VC_CONTAINER_T *p_ctx,
82 VC_CONTAINER_PACKET_T *p_packet, uint32_t flags )
83{
84 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
85 uint32_t packet_flags = 0, size, data_size;
86 int64_t pts;
87
88 pts = module->position * 8000000 / p_ctx->tracks[0]->format->bitrate;
89 data_size = module->frame_data_left;
90 if(!data_size)
91 {
92 data_size = module->block_size;
93 packet_flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START;
94 }
95 module->frame_data_left = 0;
96
97 if(module->position + data_size > module->data_size)
98 data_size = module->data_size - module->position;
99 if(data_size == 0) return VC_CONTAINER_ERROR_EOS;
100
101 if((flags & VC_CONTAINER_READ_FLAG_SKIP) && !(flags & VC_CONTAINER_READ_FLAG_INFO)) /* Skip packet */
102 {
103 size = SKIP_BYTES(p_ctx, data_size);
104 module->frame_data_left = data_size - size;
105 module->position += size;
106 return STREAM_STATUS(p_ctx);
107 }
108
109 p_packet->flags = packet_flags;
110 p_packet->dts = p_packet->pts = pts;
111 p_packet->track = 0;
112
113 if(flags & VC_CONTAINER_READ_FLAG_SKIP)
114 {
115 size = SKIP_BYTES(p_ctx, data_size);
116 module->frame_data_left = data_size - size;
117 if(!module->frame_data_left) p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END;
118 module->position += size;
119 p_packet->size += size;
120 return STREAM_STATUS(p_ctx);
121 }
122
123 if(flags & VC_CONTAINER_READ_FLAG_INFO)
124 return VC_CONTAINER_SUCCESS;
125
126 size = MIN(data_size, p_packet->buffer_size - p_packet->size);
127 size = READ_BYTES(p_ctx, p_packet->data, size);
128 module->frame_data_left = data_size - size;
129 if(!module->frame_data_left) p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END;
130 module->position += size;
131 p_packet->size += size;
132
133 return STREAM_STATUS(p_ctx);
134}
135
136/*****************************************************************************/
137static VC_CONTAINER_STATUS_T wav_reader_seek( VC_CONTAINER_T *p_ctx, int64_t *p_offset,
138 VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags)
139{
140 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
141 int64_t position;
142 VC_CONTAINER_PARAM_UNUSED(mode);
143 VC_CONTAINER_PARAM_UNUSED(flags);
144
145 position = *p_offset * p_ctx->tracks[0]->format->bitrate / 8000000;
146 if(p_ctx->tracks[0]->format->type->audio.block_align)
147 position = position / p_ctx->tracks[0]->format->type->audio.block_align *
148 p_ctx->tracks[0]->format->type->audio.block_align;
149 if(position > module->data_size) position = module->data_size;
150
151 module->position = position;
152 module->frame_data_left = 0;
153
154 if(position >= module->data_size) return VC_CONTAINER_ERROR_EOS;
155 return SEEK(p_ctx, module->data_offset + position);
156}
157
158/*****************************************************************************/
159static VC_CONTAINER_STATUS_T wav_reader_close( VC_CONTAINER_T *p_ctx )
160{
161 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
162 unsigned int i;
163
164 for(i = 0; i < p_ctx->tracks_num; i++)
165 vc_container_free_track(p_ctx, p_ctx->tracks[i]);
166 free(module);
167 return VC_CONTAINER_SUCCESS;
168}
169
170/*****************************************************************************/
171VC_CONTAINER_STATUS_T wav_reader_open( VC_CONTAINER_T *p_ctx )
172{
173 VC_CONTAINER_MODULE_T *module = 0;
174 VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
175 VC_CONTAINER_FOURCC_T codec;
176 int64_t chunk_size, chunk_pos;
177 uint32_t format, channels, samplerate, bitrate, block_align, bps, cbsize = 0;
178 uint8_t buffer[12];
179
180 /* Check the RIFF chunk descriptor */
181 if( PEEK_BYTES(p_ctx, buffer, 12) != 12 )
182 return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
183 if( VC_FOURCC(buffer[0], buffer[1], buffer[2], buffer[3]) !=
184 VC_FOURCC('R','I','F','F') )
185 return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
186 if( VC_FOURCC(buffer[8], buffer[9], buffer[10], buffer[11]) !=
187 VC_FOURCC('W','A','V','E') )
188 return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
189
190 /*
191 * We are dealing with a WAV file
192 */
193 SKIP_FOURCC(p_ctx, "Chunk ID");
194 SKIP_U32(p_ctx, "Chunk size");
195 SKIP_FOURCC(p_ctx, "WAVE ID");
196
197 /* We're looking for the 'fmt' sub-chunk */
198 do {
199 chunk_pos = STREAM_POSITION(p_ctx) + 8;
200 if( READ_FOURCC(p_ctx, "Chunk ID") == VC_FOURCC('f','m','t',' ') ) break;
201
202 /* Not interested in this chunk. Skip it. */
203 chunk_size = READ_U32(p_ctx, "Chunk size");
204 SKIP_BYTES(p_ctx, chunk_size);
205 } while(STREAM_STATUS(p_ctx) == VC_CONTAINER_SUCCESS);
206
207 if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS)
208 return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; /* 'fmt' not found */
209
210 /* Parse the 'fmt' sub-chunk */
211 chunk_size = READ_U32(p_ctx, "Chunk size");
212 format = READ_U16(p_ctx, "wFormatTag");
213 channels = READ_U16(p_ctx, "nChannels");
214 samplerate = READ_U32(p_ctx, "nSamplesPerSec");
215 bitrate = READ_U32(p_ctx, "nAvgBytesPerSec") * 8;
216 block_align = READ_U16(p_ctx, "nBlockAlign");
217 bps = READ_U16(p_ctx, "wBitsPerSample");
218
219 if(STREAM_POSITION(p_ctx) - chunk_pos <= chunk_size - 2)
220 cbsize = READ_U16(p_ctx, "cbSize");
221
222 if(format == WAVE_FORMAT_EXTENSIBLE &&
223 chunk_size >= 18 + 22 && cbsize >= 22)
224 {
225 GUID_T guid;
226 codec = VC_CONTAINER_CODEC_UNKNOWN;
227
228 SKIP_U16(p_ctx, "wValidBitsPerSample");
229 SKIP_U32(p_ctx, "dwChannelMask");
230 READ_GUID(p_ctx, &guid, "SubFormat");
231
232 if(!memcmp(&guid, &pcm_guid, sizeof(guid)))
233 codec = VC_CONTAINER_CODEC_PCM_SIGNED_LE;
234 else if(!memcmp(&guid, &atracx_guid, sizeof(guid)))
235 codec = VC_CONTAINER_CODEC_ATRAC3;
236
237 cbsize -= 22;
238
239 /* TODO: deal with channel mapping */
240 }
241 else
242 {
243 codec = waveformat_to_codec(format);
244 }
245
246 /* Bail out if we don't recognise the codec */
247 if(codec == VC_CONTAINER_CODEC_UNKNOWN)
248 return VC_CONTAINER_ERROR_FORMAT_FEATURE_NOT_SUPPORTED;
249
250 /* Do some sanity checking on the info we got */
251 if(!channels || !samplerate || !block_align || !bitrate)
252 return VC_CONTAINER_ERROR_FORMAT_INVALID;
253 if(codec == VC_CONTAINER_CODEC_ATRAC3 && channels > 2)
254 return VC_CONTAINER_ERROR_FORMAT_INVALID;
255
256 /* Allocate our context */
257 module = malloc(sizeof(*module));
258 if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
259 memset(module, 0, sizeof(*module));
260 p_ctx->priv->module = module;
261 p_ctx->tracks_num = 1;
262 p_ctx->tracks = &module->track;
263 p_ctx->tracks[0] = vc_container_allocate_track(p_ctx, 0);
264 if(!p_ctx->tracks[0]) return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
265
266 p_ctx->tracks[0]->format->es_type = VC_CONTAINER_ES_TYPE_AUDIO;
267 p_ctx->tracks[0]->format->codec = codec;
268 p_ctx->tracks[0]->format->type->audio.channels = channels;
269 p_ctx->tracks[0]->format->type->audio.sample_rate = samplerate;
270 p_ctx->tracks[0]->format->type->audio.block_align = block_align;
271 p_ctx->tracks[0]->format->type->audio.bits_per_sample = bps;
272 p_ctx->tracks[0]->format->bitrate = bitrate;
273 p_ctx->tracks[0]->is_enabled = true;
274 p_ctx->tracks[0]->format->extradata_size = 0;
275 p_ctx->tracks[0]->format->extradata = module->extradata;
276 module->block_size = block_align;
277
278 /* Prepare the codec extradata */
279 if(codec == VC_CONTAINER_CODEC_ATRAC3)
280 {
281 uint16_t h, mode;
282
283 SKIP_U16(p_ctx, "len");
284 SKIP_U16(p_ctx, "layer");
285 SKIP_U32(p_ctx, "bytes_per_frame");
286 mode = READ_U16(p_ctx, "mode");
287 SKIP_U16(p_ctx, "mode_ext");
288 SKIP_U16(p_ctx, "num_subframes");
289 SKIP_U16(p_ctx, "flags");
290
291 h = (1 << 15);
292 if(channels == 2)
293 {
294 h |= (1 << 13);
295 if(mode == 1) h |= (1 << 14);
296 }
297 h |= block_align & 0x7ff;
298
299 p_ctx->tracks[0]->format->extradata[0] = h >> 8;
300 p_ctx->tracks[0]->format->extradata[1] = h & 255;
301 p_ctx->tracks[0]->format->extradata_size = 2;
302 }
303 else if(codec == VC_CONTAINER_CODEC_ATRACX && cbsize >= 6)
304 {
305 SKIP_BYTES(p_ctx, 2);
306 p_ctx->tracks[0]->format->extradata_size =
307 READ_BYTES(p_ctx, p_ctx->tracks[0]->format->extradata, 6);
308 }
309 else if(codec == VC_CONTAINER_CODEC_PCM_SIGNED_LE)
310 {
311 /* Audioplus can no longer be given anything other than a multiple-of-16 number of samples */
312 block_align *= 16;
313 module->block_size = (BLOCK_SIZE / block_align) * block_align;
314 }
315
316 /* Skip the rest of the 'fmt' sub-chunk */
317 SKIP_BYTES(p_ctx, chunk_pos + chunk_size - STREAM_POSITION(p_ctx));
318
319 /* We also need the 'data' sub-chunk */
320 do {
321 chunk_pos = STREAM_POSITION(p_ctx) + 8;
322 if( READ_FOURCC(p_ctx, "Chunk ID") == VC_FOURCC('d','a','t','a') ) break;
323
324 /* Not interested in this chunk. Skip it. */
325 chunk_size = READ_U32(p_ctx, "Chunk size");
326 SKIP_BYTES(p_ctx, chunk_size);
327 } while(STREAM_STATUS(p_ctx) == VC_CONTAINER_SUCCESS);
328
329 if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS)
330 {
331 status = VC_CONTAINER_ERROR_FORMAT_INVALID; /* 'data' not found */;
332 goto error;
333 }
334
335 module->data_offset = chunk_pos;
336 module->data_size = READ_U32(p_ctx, "Chunk size");
337 p_ctx->duration = module->data_size * 8000000 / bitrate;
338 if(STREAM_SEEKABLE(p_ctx))
339 p_ctx->capabilities |= VC_CONTAINER_CAPS_CAN_SEEK;
340
341 /*
342 * We now have all the information we really need to start playing the stream
343 */
344
345 p_ctx->priv->pf_close = wav_reader_close;
346 p_ctx->priv->pf_read = wav_reader_read;
347 p_ctx->priv->pf_seek = wav_reader_seek;
348
349 /* Seek back to the start of the data */
350 status = SEEK(p_ctx, module->data_offset);
351 if(status != VC_CONTAINER_SUCCESS) goto error;
352 return VC_CONTAINER_SUCCESS;
353
354 error:
355 LOG_DEBUG(p_ctx, "wav: error opening stream (%i)", status);
356 if(module) wav_reader_close(p_ctx);
357 return status;
358}
359
360/********************************************************************************
361 Entrypoint function
362 ********************************************************************************/
363
364#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__)
365# pragma weak reader_open wav_reader_open
366#endif
367