1 | /* |
2 | Copyright (c) 2012, Broadcom Europe Ltd |
3 | All rights reserved. |
4 | |
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, 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 | |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY |
20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
23 | ON 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 |
25 | SOFTWARE, 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 | /****************************************************************************** |
41 | Defines. |
42 | ******************************************************************************/ |
43 | #define 16 |
44 | #define BLOCK_SIZE (16*1024) |
45 | |
46 | /****************************************************************************** |
47 | GUID list for the different codecs |
48 | ******************************************************************************/ |
49 | static const GUID_T atracx_guid = {0xbfaa23e9, 0x58cb, 0x7144, {0xa1, 0x19, 0xff, 0xfa, 0x01, 0xe4, 0xce, 0x62}}; |
50 | static const GUID_T pcm_guid = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; |
51 | |
52 | /****************************************************************************** |
53 | Type definitions |
54 | ******************************************************************************/ |
55 | typedef 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 [WAV_EXTRADATA_MAX]; |
65 | |
66 | } VC_CONTAINER_MODULE_T; |
67 | |
68 | /****************************************************************************** |
69 | Function prototypes |
70 | ******************************************************************************/ |
71 | VC_CONTAINER_STATUS_T wav_reader_open( VC_CONTAINER_T * ); |
72 | |
73 | /****************************************************************************** |
74 | Local Functions |
75 | ******************************************************************************/ |
76 | |
77 | /***************************************************************************** |
78 | Functions exported as part of the Container Module API |
79 | *****************************************************************************/ |
80 | |
81 | static 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 | /*****************************************************************************/ |
137 | static 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 | /*****************************************************************************/ |
159 | static 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 | /*****************************************************************************/ |
171 | VC_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 | |