diff options
Diffstat (limited to 'apps/codecs/libm4a/m4a.c')
-rw-r--r-- | apps/codecs/libm4a/m4a.c | 380 |
1 files changed, 258 insertions, 122 deletions
diff --git a/apps/codecs/libm4a/m4a.c b/apps/codecs/libm4a/m4a.c index f914f4e4d1..0a87ec35ea 100644 --- a/apps/codecs/libm4a/m4a.c +++ b/apps/codecs/libm4a/m4a.c | |||
@@ -21,6 +21,13 @@ | |||
21 | #include <inttypes.h> | 21 | #include <inttypes.h> |
22 | #include "m4a.h" | 22 | #include "m4a.h" |
23 | 23 | ||
24 | #if defined(DEBUG) || defined(SIMULATOR) | ||
25 | extern struct codec_api* rb; | ||
26 | #define DEBUGF rb->debugf | ||
27 | #else | ||
28 | #define DEBUGF(...) | ||
29 | #endif | ||
30 | |||
24 | /* Implementation of the stream.h functions used by libalac */ | 31 | /* Implementation of the stream.h functions used by libalac */ |
25 | 32 | ||
26 | #define _Swap32(v) do { \ | 33 | #define _Swap32(v) do { \ |
@@ -101,15 +108,7 @@ uint8_t stream_read_uint8(stream_t *stream) | |||
101 | 108 | ||
102 | void stream_skip(stream_t *stream, size_t skip) | 109 | void stream_skip(stream_t *stream, size_t skip) |
103 | { | 110 | { |
104 | (void)stream; | 111 | stream->ci->advance_buffer(skip); |
105 | #if 1 | ||
106 | char buf; | ||
107 | while (skip > 0) { | ||
108 | stream->ci->read_filebuf(&buf,1); | ||
109 | skip--; | ||
110 | } | ||
111 | #endif | ||
112 | //stream->ci->advance_buffer(skip); | ||
113 | } | 112 | } |
114 | 113 | ||
115 | int stream_eof(stream_t *stream) | 114 | int stream_eof(stream_t *stream) |
@@ -158,136 +157,273 @@ int get_sample_info(demux_res_t *demux_res, uint32_t samplenum, | |||
158 | return 1; | 157 | return 1; |
159 | } | 158 | } |
160 | 159 | ||
161 | /* Seek to sample_loc (or close to it). Return 1 on success (and | 160 | unsigned int get_sample_offset(demux_res_t *demux_res, uint32_t sample) |
162 | modify samplesdone and currentblock), 0 if failed | 161 | { |
162 | uint32_t chunk = 1; | ||
163 | uint32_t range_samples = 0; | ||
164 | uint32_t total_samples = 0; | ||
165 | uint32_t chunk_sample; | ||
166 | uint32_t prev_chunk; | ||
167 | uint32_t prev_chunk_samples; | ||
168 | uint32_t file_offset; | ||
169 | uint32_t i; | ||
170 | |||
171 | /* First check we have the appropriate metadata - we should always | ||
172 | * have it. | ||
173 | */ | ||
174 | |||
175 | if (sample >= demux_res->num_sample_byte_sizes || | ||
176 | !demux_res->num_sample_to_chunks || | ||
177 | !demux_res->num_chunk_offsets) | ||
178 | { | ||
179 | return 0; | ||
180 | } | ||
163 | 181 | ||
164 | Seeking uses the following two arrays: | 182 | /* Locate the chunk containing the sample */ |
183 | |||
184 | prev_chunk = demux_res->sample_to_chunk[0].first_chunk; | ||
185 | prev_chunk_samples = demux_res->sample_to_chunk[0].num_samples; | ||
165 | 186 | ||
166 | 1) the sample_byte_size array contains the length in bytes of | 187 | for (i = 1; i < demux_res->num_sample_to_chunks; i++) |
167 | each block ("sample" in Applespeak). | 188 | { |
189 | chunk = demux_res->sample_to_chunk[i].first_chunk; | ||
190 | range_samples = (chunk - prev_chunk) * prev_chunk_samples; | ||
168 | 191 | ||
169 | 2) the time_to_sample array contains the duration (in samples) of | 192 | if (sample < total_samples + range_samples) |
170 | each block of data. | 193 | { |
194 | break; | ||
195 | } | ||
171 | 196 | ||
172 | So we just find the block number we are going to seek to (using | 197 | total_samples += range_samples; |
173 | time_to_sample) and then find the offset in the file (using | 198 | prev_chunk = demux_res->sample_to_chunk[i].first_chunk; |
174 | sample_byte_size). | 199 | prev_chunk_samples = demux_res->sample_to_chunk[i].num_samples; |
200 | } | ||
175 | 201 | ||
176 | Each ALAC block seems to be independent of all the others. | 202 | if (demux_res->num_sample_to_chunks > 1) |
177 | */ | 203 | { |
204 | chunk = prev_chunk + (sample - total_samples) / prev_chunk_samples; | ||
205 | } | ||
206 | else | ||
207 | { | ||
208 | chunk = 1; | ||
209 | } | ||
210 | |||
211 | /* Get sample of the first sample in the chunk */ | ||
212 | |||
213 | chunk_sample = total_samples + (chunk - prev_chunk) * prev_chunk_samples; | ||
214 | |||
215 | /* Get offset in file */ | ||
216 | |||
217 | if (chunk > demux_res->num_chunk_offsets) | ||
218 | { | ||
219 | file_offset = demux_res->chunk_offset[demux_res->num_chunk_offsets - 1]; | ||
220 | } | ||
221 | else | ||
222 | { | ||
223 | file_offset = demux_res->chunk_offset[chunk - 1]; | ||
224 | } | ||
225 | |||
226 | if (chunk_sample > sample) { | ||
227 | return 0; | ||
228 | } | ||
229 | |||
230 | for (i = chunk_sample; i < sample; i++) | ||
231 | { | ||
232 | file_offset += demux_res->sample_byte_size[i]; | ||
233 | } | ||
234 | |||
235 | if (file_offset > demux_res->mdat_offset + demux_res->mdat_len) | ||
236 | { | ||
237 | return 0; | ||
238 | } | ||
239 | |||
240 | return file_offset; | ||
241 | } | ||
178 | 242 | ||
179 | unsigned int alac_seek (demux_res_t* demux_res, | 243 | /* Seek to the sample containing sound_sample_loc. Return 1 on success |
180 | stream_t* stream, | 244 | * (and modify sound_samples_done and current_sample), 0 if failed. |
181 | unsigned int sample_loc, | 245 | * |
182 | uint32_t* samplesdone, int* currentblock) | 246 | * Seeking uses the following arrays: |
247 | * | ||
248 | * 1) the time_to_sample array contains the duration (in sound samples) | ||
249 | * of each sample of data. | ||
250 | * | ||
251 | * 2) the sample_byte_size array contains the length in bytes of each | ||
252 | * sample. | ||
253 | * | ||
254 | * 3) the sample_to_chunk array contains information about which chunk | ||
255 | * of samples each sample belongs to. | ||
256 | * | ||
257 | * 4) the chunk_offset array contains the file offset of each chunk. | ||
258 | * | ||
259 | * So find the sample number we are going to seek to (using time_to_sample) | ||
260 | * and then find the offset in the file (using sample_to_chunk, | ||
261 | * chunk_offset sample_byte_size, in that order.). | ||
262 | * | ||
263 | */ | ||
264 | unsigned int alac_seek(demux_res_t* demux_res, stream_t* stream, | ||
265 | uint32_t sound_sample_loc, uint32_t* sound_samples_done, | ||
266 | int* current_sample) | ||
183 | { | 267 | { |
184 | int flag; | 268 | uint32_t i; |
185 | unsigned int i,j; | 269 | uint32_t j; |
186 | unsigned int newblock; | 270 | uint32_t new_sample; |
187 | unsigned int newsample; | 271 | uint32_t new_sound_sample; |
188 | unsigned int newpos; | 272 | uint32_t new_pos; |
189 | 273 | ||
190 | /* First check we have the appropriate metadata - we should always | 274 | /* First check we have the appropriate metadata - we should always |
191 | have it. */ | 275 | * have it. |
192 | if ((demux_res->num_time_to_samples==0) || | 276 | */ |
193 | (demux_res->num_sample_byte_sizes==0)) { return 0; } | 277 | |
194 | 278 | if ((demux_res->num_time_to_samples==0) || | |
195 | /* Find the destination block from time_to_sample array */ | 279 | (demux_res->num_sample_byte_sizes==0)) |
196 | i=0; | 280 | { |
197 | newblock=0; | 281 | return 0; |
198 | newsample=0; | 282 | } |
199 | flag=0; | 283 | |
200 | 284 | /* Find the destination block from time_to_sample array */ | |
201 | while ((i<demux_res->num_time_to_samples) && (flag==0) && | ||
202 | (newsample < sample_loc)) { | ||
203 | j=(sample_loc-newsample) / | ||
204 | demux_res->time_to_sample[i].sample_duration; | ||
205 | 285 | ||
206 | if (j <= demux_res->time_to_sample[i].sample_count) { | 286 | i = 0; |
207 | newblock+=j; | 287 | new_sample = 0; |
208 | newsample+=j*demux_res->time_to_sample[i].sample_duration; | 288 | new_sound_sample = 0; |
209 | flag=1; | 289 | |
210 | } else { | 290 | while ((i < demux_res->num_time_to_samples) && |
211 | newsample+=(demux_res->time_to_sample[i].sample_duration | 291 | (new_sound_sample < sound_sample_loc)) |
212 | * demux_res->time_to_sample[i].sample_count); | 292 | { |
213 | newblock+=demux_res->time_to_sample[i].sample_count; | 293 | j = (sound_sample_loc - new_sound_sample) / |
214 | i++; | 294 | demux_res->time_to_sample[i].sample_duration; |
295 | |||
296 | if (j <= demux_res->time_to_sample[i].sample_count) | ||
297 | { | ||
298 | new_sample += j; | ||
299 | new_sound_sample += j * | ||
300 | demux_res->time_to_sample[i].sample_duration; | ||
301 | break; | ||
302 | } | ||
303 | else | ||
304 | { | ||
305 | new_sound_sample += (demux_res->time_to_sample[i].sample_duration | ||
306 | * demux_res->time_to_sample[i].sample_count); | ||
307 | new_sample += demux_res->time_to_sample[i].sample_count; | ||
308 | i++; | ||
309 | } | ||
215 | } | 310 | } |
216 | } | 311 | |
217 | 312 | /* We know the new block, now calculate the file position. */ | |
218 | /* We know the new block, now calculate the file position */ | 313 | |
219 | newpos=demux_res->mdat_offset; | 314 | new_pos = get_sample_offset(demux_res, new_sample); |
220 | for (i=0;i<newblock;i++) { | 315 | |
221 | newpos+=demux_res->sample_byte_size[i]; | 316 | /* We know the new file position, so let's try to seek to it */ |
222 | } | 317 | |
223 | 318 | if (stream->ci->seek_buffer(new_pos)) | |
224 | /* We know the new file position, so let's try to seek to it */ | 319 | { |
225 | if (stream->ci->seek_buffer(newpos)) { | 320 | *sound_samples_done = new_sound_sample; |
226 | *samplesdone=newsample; | 321 | *current_sample = new_sample; |
227 | *currentblock=newblock; | 322 | return 1; |
228 | return 1; | 323 | } |
229 | } else { | 324 | |
230 | return 0; | 325 | return 0; |
231 | } | ||
232 | } | 326 | } |
233 | 327 | ||
234 | /* Seek to file_loc (or close to it). Return 1 on success (and | 328 | /* Seek to the sample containing file_loc. Return 1 on success (and modify |
235 | modify samplesdone and currentblock), 0 if failed | 329 | * sound_samples_done and current_sample), 0 if failed. |
236 | 330 | * | |
237 | Seeking uses the following array: | 331 | * Seeking uses the following arrays: |
332 | * | ||
333 | * 1) the chunk_offset array contains the file offset of each chunk. | ||
334 | * | ||
335 | * 2) the sample_to_chunk array contains information about which chunk | ||
336 | * of samples each sample belongs to. | ||
337 | * | ||
338 | * 3) the sample_byte_size array contains the length in bytes of each | ||
339 | * sample. | ||
340 | * | ||
341 | * 4) the time_to_sample array contains the duration (in sound samples) | ||
342 | * of each sample of data. | ||
343 | * | ||
344 | * Locate the chunk containing location (using chunk_offset), find the | ||
345 | * sample of that chunk (using sample_to_chunk) and finally the location | ||
346 | * of that sample (using sample_byte_size). Then use time_to_sample to | ||
347 | * calculate the sound_samples_done value. | ||
348 | */ | ||
349 | unsigned int alac_seek_raw(demux_res_t* demux_res, stream_t* stream, | ||
350 | uint32_t file_loc, uint32_t* sound_samples_done, | ||
351 | int* current_sample) | ||
352 | { | ||
353 | uint32_t chunk_sample = 0; | ||
354 | uint32_t total_samples = 0; | ||
355 | uint32_t new_sound_sample = 0; | ||
356 | uint32_t new_pos; | ||
357 | uint32_t chunk; | ||
358 | uint32_t i; | ||
359 | |||
360 | if (!demux_res->num_chunk_offsets || | ||
361 | !demux_res->num_sample_to_chunks) | ||
362 | { | ||
363 | return 0; | ||
364 | } | ||
238 | 365 | ||
239 | the sample_byte_size array contains the length in bytes of | 366 | /* Locate the chunk containing file_loc. */ |
240 | each block ("sample" in Applespeak). | ||
241 | 367 | ||
242 | So we just find the last block before (or at) the requested position. | 368 | for (i = 0; i < demux_res->num_chunk_offsets && |
369 | file_loc < demux_res->chunk_offset[i]; i++) | ||
370 | { | ||
371 | } | ||
372 | |||
373 | chunk = i + 1; | ||
374 | new_pos = demux_res->chunk_offset[chunk - 1]; | ||
243 | 375 | ||
244 | Each ALAC block seems to be independent of all the others. | 376 | /* Get the first sample of the chunk. */ |
245 | */ | 377 | |
378 | for (i = 1; i < demux_res->num_sample_to_chunks && | ||
379 | chunk < demux_res->sample_to_chunk[i - 1].first_chunk; i++) | ||
380 | { | ||
381 | chunk_sample += demux_res->sample_to_chunk[i - 1].num_samples * | ||
382 | (demux_res->sample_to_chunk[i].first_chunk - | ||
383 | demux_res->sample_to_chunk[i - 1].first_chunk); | ||
384 | } | ||
385 | |||
386 | chunk_sample += (chunk - demux_res->sample_to_chunk[i - 1].first_chunk) * | ||
387 | demux_res->sample_to_chunk[i - 1].num_samples; | ||
246 | 388 | ||
247 | unsigned int alac_seek_raw (demux_res_t* demux_res, | 389 | /* Get the position within the chunk. */ |
248 | stream_t* stream, | 390 | |
249 | unsigned int file_loc, | 391 | for (; chunk_sample < demux_res->num_sample_byte_sizes; chunk_sample++) |
250 | uint32_t* samplesdone, int* currentblock) | 392 | { |
251 | { | 393 | if (file_loc < new_pos + demux_res->sample_byte_size[chunk_sample]) |
252 | unsigned int i; | 394 | { |
253 | unsigned int j; | 395 | break; |
254 | unsigned int newblock; | 396 | } |
255 | unsigned int newsample; | 397 | |
256 | unsigned int newpos; | 398 | new_pos += demux_res->sample_byte_size[chunk_sample]; |
257 | 399 | } | |
258 | /* First check we have the appropriate metadata - we should always | 400 | |
259 | have it. */ | 401 | /* Get sound sample offset. */ |
260 | if ((demux_res->num_time_to_samples==0) || | 402 | |
261 | (demux_res->num_sample_byte_sizes==0)) { return 0; } | 403 | for (i = 0; i < demux_res->num_time_to_samples; i++) |
262 | 404 | { | |
263 | /* Find the destination block from the sample_byte_size array. */ | 405 | if (chunk_sample < |
264 | newpos=demux_res->mdat_offset; | 406 | total_samples + demux_res->time_to_sample[i].sample_count) |
265 | for (i=0;(i<demux_res->num_sample_byte_sizes) && | 407 | { |
266 | (newpos+demux_res->sample_byte_size[i]<=file_loc);i++) { | 408 | break; |
267 | newpos+=demux_res->sample_byte_size[i]; | 409 | } |
268 | } | 410 | |
269 | 411 | total_samples += demux_res->time_to_sample[i].sample_count; | |
270 | newblock=i; | 412 | new_sound_sample += demux_res->time_to_sample[i].sample_count |
271 | newsample=0; | 413 | * demux_res->time_to_sample[i].sample_duration; |
272 | |||
273 | /* Get the sample offset of the block */ | ||
274 | for (i=0,j=0;(i<demux_res->num_time_to_samples) && (j<newblock); | ||
275 | i++,j+=demux_res->time_to_sample[i].sample_count) { | ||
276 | if (newblock-j < demux_res->time_to_sample[i].sample_count) { | ||
277 | newsample+=(newblock-j)*demux_res->time_to_sample[i].sample_duration; | ||
278 | break; | ||
279 | } else { | ||
280 | newsample+=(demux_res->time_to_sample[i].sample_duration | ||
281 | * demux_res->time_to_sample[i].sample_count); | ||
282 | } | 414 | } |
283 | } | 415 | |
416 | new_sound_sample += (chunk_sample - total_samples) | ||
417 | * demux_res->time_to_sample[i].sample_duration; | ||
418 | |||
419 | /* Go to the new file position. */ | ||
420 | |||
421 | if (stream->ci->seek_buffer(new_pos)) | ||
422 | { | ||
423 | *sound_samples_done = new_sound_sample; | ||
424 | *current_sample = chunk_sample; | ||
425 | return 1; | ||
426 | } | ||
284 | 427 | ||
285 | /* We know the new file position, so let's try to seek to it */ | ||
286 | if (stream->ci->seek_buffer(newpos)) { | ||
287 | *samplesdone=newsample; | ||
288 | *currentblock=newblock; | ||
289 | return 1; | ||
290 | } else { | ||
291 | return 0; | 428 | return 0; |
292 | } | ||
293 | } | 429 | } |