diff options
author | Sean Bartell <wingedtachikoma@gmail.com> | 2011-06-25 21:32:25 -0400 |
---|---|---|
committer | Nils Wallménius <nils@rockbox.org> | 2012-04-25 22:13:20 +0200 |
commit | f40bfc9267b13b54e6379dfe7539447662879d24 (patch) | |
tree | 9b20069d5e62809ff434061ad730096836f916f2 /lib/rbcodec/codecs/libm4a | |
parent | a0009907de7a0107d49040d8a180f140e2eff299 (diff) | |
download | rockbox-f40bfc9267b13b54e6379dfe7539447662879d24.tar.gz rockbox-f40bfc9267b13b54e6379dfe7539447662879d24.zip |
Add codecs to librbcodec.
Change-Id: Id7f4717d51ed02d67cb9f9cb3c0ada4a81843f97
Reviewed-on: http://gerrit.rockbox.org/137
Reviewed-by: Nils Wallménius <nils@rockbox.org>
Tested-by: Nils Wallménius <nils@rockbox.org>
Diffstat (limited to 'lib/rbcodec/codecs/libm4a')
-rw-r--r-- | lib/rbcodec/codecs/libm4a/SOURCES | 2 | ||||
-rw-r--r-- | lib/rbcodec/codecs/libm4a/demux.c | 826 | ||||
-rw-r--r-- | lib/rbcodec/codecs/libm4a/libm4a.make | 18 | ||||
-rw-r--r-- | lib/rbcodec/codecs/libm4a/m4a.c | 267 | ||||
-rw-r--r-- | lib/rbcodec/codecs/libm4a/m4a.h | 138 |
5 files changed, 1251 insertions, 0 deletions
diff --git a/lib/rbcodec/codecs/libm4a/SOURCES b/lib/rbcodec/codecs/libm4a/SOURCES new file mode 100644 index 0000000000..01b93bef01 --- /dev/null +++ b/lib/rbcodec/codecs/libm4a/SOURCES | |||
@@ -0,0 +1,2 @@ | |||
1 | m4a.c | ||
2 | demux.c | ||
diff --git a/lib/rbcodec/codecs/libm4a/demux.c b/lib/rbcodec/codecs/libm4a/demux.c new file mode 100644 index 0000000000..7b09074c52 --- /dev/null +++ b/lib/rbcodec/codecs/libm4a/demux.c | |||
@@ -0,0 +1,826 @@ | |||
1 | /* | ||
2 | * ALAC (Apple Lossless Audio Codec) decoder | ||
3 | * Copyright (c) 2005 David Hammerton | ||
4 | * All rights reserved. | ||
5 | * | ||
6 | * This is the quicktime container demuxer. | ||
7 | * | ||
8 | * http://crazney.net/programs/itunes/alac.html | ||
9 | * | ||
10 | * Permission is hereby granted, free of charge, to any person | ||
11 | * obtaining a copy of this software and associated documentation | ||
12 | * files (the "Software"), to deal in the Software without | ||
13 | * restriction, including without limitation the rights to use, | ||
14 | * copy, modify, merge, publish, distribute, sublicense, and/or | ||
15 | * sell copies of the Software, and to permit persons to whom the | ||
16 | * Software is furnished to do so, subject to the following conditions: | ||
17 | * | ||
18 | * The above copyright notice and this permission notice shall be | ||
19 | * included in all copies or substantial portions of the Software. | ||
20 | * | ||
21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
22 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES | ||
23 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
24 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | ||
25 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||
26 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
27 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||
28 | * OTHER DEALINGS IN THE SOFTWARE. | ||
29 | * | ||
30 | */ | ||
31 | |||
32 | #include <string.h> | ||
33 | #include <inttypes.h> | ||
34 | #include <stdlib.h> | ||
35 | |||
36 | #include "codeclib.h" | ||
37 | |||
38 | #include "m4a.h" | ||
39 | |||
40 | #undef DEBUGF | ||
41 | #if defined(DEBUG) | ||
42 | #define DEBUGF qtmovie->stream->ci->debugf | ||
43 | #else | ||
44 | #define DEBUGF(...) | ||
45 | #endif | ||
46 | |||
47 | typedef struct | ||
48 | { | ||
49 | stream_t *stream; | ||
50 | demux_res_t *res; | ||
51 | } qtmovie_t; | ||
52 | |||
53 | |||
54 | /* chunk handlers */ | ||
55 | static void read_chunk_ftyp(qtmovie_t *qtmovie, size_t chunk_len) | ||
56 | { | ||
57 | fourcc_t type; | ||
58 | size_t size_remaining = chunk_len - 8; | ||
59 | |||
60 | type = stream_read_uint32(qtmovie->stream); | ||
61 | size_remaining-=4; | ||
62 | if ((type != MAKEFOURCC('M','4','A',' ')) && | ||
63 | (type != MAKEFOURCC('m','4','a',' ')) && | ||
64 | (type != MAKEFOURCC('M','4','B',' ')) && | ||
65 | (type != MAKEFOURCC('m','p','4','2')) && | ||
66 | (type != MAKEFOURCC('3','g','p','6')) && | ||
67 | (type != MAKEFOURCC('q','t',' ',' ')) && | ||
68 | (type != MAKEFOURCC('i','s','o','m'))) | ||
69 | { | ||
70 | DEBUGF("not M4A file\n"); | ||
71 | return; | ||
72 | } | ||
73 | /* minor_ver = */ stream_read_uint32(qtmovie->stream); | ||
74 | size_remaining-=4; | ||
75 | |||
76 | /* compatible brands */ | ||
77 | while (size_remaining) | ||
78 | { | ||
79 | /* unused */ | ||
80 | /*fourcc_t cbrand =*/ stream_read_uint32(qtmovie->stream); | ||
81 | size_remaining-=4; | ||
82 | } | ||
83 | } | ||
84 | |||
85 | static uint32_t mp4ff_read_mp4_descr_length(stream_t* stream) | ||
86 | { | ||
87 | uint8_t b; | ||
88 | uint8_t numBytes = 0; | ||
89 | uint32_t length = 0; | ||
90 | |||
91 | do | ||
92 | { | ||
93 | b = stream_read_uint8(stream); | ||
94 | numBytes++; | ||
95 | length = (length << 7) | (b & 0x7F); | ||
96 | } while ((b & 0x80) && numBytes < 4); | ||
97 | |||
98 | return length; | ||
99 | } | ||
100 | |||
101 | /* The following function is based on mp4ff */ | ||
102 | static bool read_chunk_esds(qtmovie_t *qtmovie, size_t chunk_len) | ||
103 | { | ||
104 | uint8_t tag; | ||
105 | uint32_t temp; | ||
106 | |||
107 | (void)chunk_len; | ||
108 | /* version and flags */ | ||
109 | temp=stream_read_uint32(qtmovie->stream); | ||
110 | |||
111 | /* get and verify ES_DescrTag */ | ||
112 | tag = stream_read_uint8(qtmovie->stream); | ||
113 | if (tag == 0x03) | ||
114 | { | ||
115 | /* read length */ | ||
116 | if (mp4ff_read_mp4_descr_length(qtmovie->stream) < 5 + 15) | ||
117 | { | ||
118 | return false; | ||
119 | } | ||
120 | /* skip 3 bytes */ | ||
121 | stream_skip(qtmovie->stream,3); | ||
122 | } else { | ||
123 | /* skip 2 bytes */ | ||
124 | stream_skip(qtmovie->stream,2); | ||
125 | } | ||
126 | |||
127 | /* get and verify DecoderConfigDescrTab */ | ||
128 | if (stream_read_uint8(qtmovie->stream) != 0x04) | ||
129 | { | ||
130 | return false; | ||
131 | } | ||
132 | |||
133 | /* read length */ | ||
134 | temp = mp4ff_read_mp4_descr_length(qtmovie->stream); | ||
135 | if (temp < 13) return false; | ||
136 | |||
137 | /* audioType = */ stream_read_uint8(qtmovie->stream); | ||
138 | /* temp = */ stream_read_int32(qtmovie->stream);//0x15000414 ???? | ||
139 | /* maxBitrate = */ stream_read_int32(qtmovie->stream); | ||
140 | /* avgBitrate = */ stream_read_int32(qtmovie->stream); | ||
141 | |||
142 | /* get and verify DecSpecificInfoTag */ | ||
143 | if (stream_read_uint8(qtmovie->stream) != 0x05) | ||
144 | { | ||
145 | return false; | ||
146 | } | ||
147 | |||
148 | /* read length */ | ||
149 | qtmovie->res->codecdata_len = mp4ff_read_mp4_descr_length(qtmovie->stream); | ||
150 | if (qtmovie->res->codecdata_len > MAX_CODECDATA_SIZE) | ||
151 | { | ||
152 | DEBUGF("codecdata too large (%d) in esds\n", | ||
153 | (int)qtmovie->res->codecdata_len); | ||
154 | return false; | ||
155 | } | ||
156 | |||
157 | stream_read(qtmovie->stream, qtmovie->res->codecdata_len, qtmovie->res->codecdata); | ||
158 | |||
159 | /* will skip the remainder of the atom */ | ||
160 | return true; | ||
161 | } | ||
162 | |||
163 | static bool read_chunk_stsd(qtmovie_t *qtmovie, size_t chunk_len) | ||
164 | { | ||
165 | unsigned int i; | ||
166 | int j; | ||
167 | uint32_t numentries; | ||
168 | size_t size_remaining = chunk_len - 8; | ||
169 | bool got_codec_data = false; | ||
170 | |||
171 | /* version */ | ||
172 | stream_read_uint8(qtmovie->stream); | ||
173 | size_remaining -= 1; | ||
174 | /* flags */ | ||
175 | stream_read_uint8(qtmovie->stream); | ||
176 | stream_read_uint8(qtmovie->stream); | ||
177 | stream_read_uint8(qtmovie->stream); | ||
178 | size_remaining -= 3; | ||
179 | |||
180 | numentries = stream_read_uint32(qtmovie->stream); | ||
181 | size_remaining -= 4; | ||
182 | |||
183 | /* if (numentries != 1) | ||
184 | { | ||
185 | DEBUGF("only expecting one entry in sample description atom!\n"); | ||
186 | return false; | ||
187 | } */ | ||
188 | |||
189 | for (i = 0; i < numentries; i++) | ||
190 | { | ||
191 | uint32_t entry_size; | ||
192 | |||
193 | uint32_t entry_remaining; | ||
194 | |||
195 | entry_size = stream_read_uint32(qtmovie->stream); | ||
196 | qtmovie->res->format = stream_read_uint32(qtmovie->stream); | ||
197 | DEBUGF("format: %c%c%c%c\n",SPLITFOURCC(qtmovie->res->format)); | ||
198 | entry_remaining = entry_size; | ||
199 | entry_remaining -= 8; | ||
200 | |||
201 | /* sound info: */ | ||
202 | |||
203 | /* reserved + data reference index + sound version + reserved */ | ||
204 | stream_skip(qtmovie->stream, 6 + 2 + 2 + 6); | ||
205 | entry_remaining -= 6 + 2 + 2 + 6; | ||
206 | |||
207 | qtmovie->res->num_channels = stream_read_uint16(qtmovie->stream); | ||
208 | qtmovie->res->sound_sample_size = stream_read_uint16(qtmovie->stream); | ||
209 | entry_remaining -= 4; | ||
210 | |||
211 | /* packet size */ | ||
212 | stream_skip(qtmovie->stream, 2); | ||
213 | qtmovie->res->sound_sample_rate = stream_read_uint32(qtmovie->stream); | ||
214 | /* reserved size */ | ||
215 | stream_skip(qtmovie->stream, 2); | ||
216 | entry_remaining -= 8; | ||
217 | |||
218 | /* remaining is codec data */ | ||
219 | |||
220 | if ((qtmovie->res->format==MAKEFOURCC('a','l','a','c'))) { | ||
221 | if (qtmovie->stream->ci->id3->codectype!=AFMT_MP4_ALAC) { | ||
222 | return false; | ||
223 | } | ||
224 | |||
225 | /* 12 = audio format atom, 8 = padding */ | ||
226 | qtmovie->res->codecdata_len = entry_remaining + 12 + 8; | ||
227 | if (qtmovie->res->codecdata_len > MAX_CODECDATA_SIZE) | ||
228 | { | ||
229 | DEBUGF("codecdata too large (%d) in stsd\n", | ||
230 | (int)qtmovie->res->codecdata_len); | ||
231 | return false; | ||
232 | } | ||
233 | |||
234 | memset(qtmovie->res->codecdata, 0, qtmovie->res->codecdata_len); | ||
235 | /* audio format atom */ | ||
236 | #if 0 | ||
237 | /* The ALAC decoder skips these bytes, so there is no need to store them, | ||
238 | and this code isn't endian/alignment safe */ | ||
239 | ((unsigned int*)qtmovie->res->codecdata)[0] = 0x0c000000; | ||
240 | ((unsigned int*)qtmovie->res->codecdata)[1] = MAKEFOURCC('a','m','r','f'); | ||
241 | ((unsigned int*)qtmovie->res->codecdata)[2] = MAKEFOURCC('c','a','l','a'); | ||
242 | #endif | ||
243 | |||
244 | stream_read(qtmovie->stream, | ||
245 | entry_remaining, | ||
246 | ((char*)qtmovie->res->codecdata) + 12); | ||
247 | entry_remaining -= entry_remaining; | ||
248 | got_codec_data = true; | ||
249 | |||
250 | if (entry_remaining) | ||
251 | stream_skip(qtmovie->stream, entry_remaining); | ||
252 | |||
253 | } else if (qtmovie->res->format==MAKEFOURCC('m','p','4','a')) { | ||
254 | if (qtmovie->stream->ci->id3->codectype!=AFMT_MP4_AAC && | ||
255 | qtmovie->stream->ci->id3->codectype!=AFMT_MP4_AAC_HE) { | ||
256 | return false; | ||
257 | } | ||
258 | |||
259 | size_t sub_chunk_len; | ||
260 | fourcc_t sub_chunk_id; | ||
261 | |||
262 | sub_chunk_len = stream_read_uint32(qtmovie->stream); | ||
263 | if (sub_chunk_len <= 1 || sub_chunk_len > entry_remaining) | ||
264 | { | ||
265 | DEBUGF("strange size (%lu) for chunk inside mp4a\n", | ||
266 | (unsigned long)sub_chunk_len); | ||
267 | return false; | ||
268 | } | ||
269 | |||
270 | sub_chunk_id = stream_read_uint32(qtmovie->stream); | ||
271 | |||
272 | if (sub_chunk_id != MAKEFOURCC('e','s','d','s')) | ||
273 | { | ||
274 | DEBUGF("Expected esds chunk inside mp4a, found %c%c%c%c\n",SPLITFOURCC(sub_chunk_id)); | ||
275 | return false; | ||
276 | } | ||
277 | |||
278 | j=qtmovie->stream->ci->curpos+sub_chunk_len-8; | ||
279 | if (read_chunk_esds(qtmovie,sub_chunk_len)) { | ||
280 | if (j!=qtmovie->stream->ci->curpos) { | ||
281 | DEBUGF("curpos=%ld, j=%d - Skipping %ld bytes\n",qtmovie->stream->ci->curpos,j,j-qtmovie->stream->ci->curpos); | ||
282 | stream_skip(qtmovie->stream,j-qtmovie->stream->ci->curpos); | ||
283 | } | ||
284 | got_codec_data = true; | ||
285 | entry_remaining-=sub_chunk_len; | ||
286 | } else { | ||
287 | DEBUGF("Error reading esds\n"); | ||
288 | return false; | ||
289 | } | ||
290 | |||
291 | DEBUGF("entry_remaining=%ld\n",(long)entry_remaining); | ||
292 | stream_skip(qtmovie->stream,entry_remaining); | ||
293 | |||
294 | } else if (qtmovie->res->format==MAKEFOURCC('f','r','e','e')) { | ||
295 | /* Skip "filler" atom */ | ||
296 | stream_skip(qtmovie->stream,entry_remaining); | ||
297 | } else { | ||
298 | DEBUGF("expecting 'alac', 'mp4a' or 'free' data format, got %c%c%c%c\n", | ||
299 | SPLITFOURCC(qtmovie->res->format)); | ||
300 | return false; | ||
301 | } | ||
302 | } | ||
303 | return got_codec_data; | ||
304 | } | ||
305 | |||
306 | static bool read_chunk_stts(qtmovie_t *qtmovie, size_t chunk_len) | ||
307 | { | ||
308 | unsigned int i; | ||
309 | uint32_t numentries; | ||
310 | size_t size_remaining = chunk_len - 8; | ||
311 | |||
312 | /* version */ | ||
313 | stream_read_uint8(qtmovie->stream); | ||
314 | size_remaining -= 1; | ||
315 | /* flags */ | ||
316 | stream_read_uint8(qtmovie->stream); | ||
317 | stream_read_uint8(qtmovie->stream); | ||
318 | stream_read_uint8(qtmovie->stream); | ||
319 | size_remaining -= 3; | ||
320 | |||
321 | numentries = stream_read_uint32(qtmovie->stream); | ||
322 | size_remaining -= 4; | ||
323 | |||
324 | qtmovie->res->num_time_to_samples = numentries; | ||
325 | qtmovie->res->time_to_sample = malloc(numentries * sizeof(*qtmovie->res->time_to_sample)); | ||
326 | |||
327 | if (!qtmovie->res->time_to_sample) | ||
328 | { | ||
329 | DEBUGF("stts too large\n"); | ||
330 | return false; | ||
331 | } | ||
332 | |||
333 | for (i = 0; i < numentries; i++) | ||
334 | { | ||
335 | qtmovie->res->time_to_sample[i].sample_count = stream_read_uint32(qtmovie->stream); | ||
336 | qtmovie->res->time_to_sample[i].sample_duration = stream_read_uint32(qtmovie->stream); | ||
337 | size_remaining -= 8; | ||
338 | } | ||
339 | |||
340 | if (size_remaining) | ||
341 | { | ||
342 | DEBUGF("ehm, size remianing?\n"); | ||
343 | stream_skip(qtmovie->stream, size_remaining); | ||
344 | } | ||
345 | |||
346 | return true; | ||
347 | } | ||
348 | |||
349 | static bool read_chunk_stsz(qtmovie_t *qtmovie, size_t chunk_len) | ||
350 | { | ||
351 | size_t size_remaining = chunk_len - 8; | ||
352 | |||
353 | /* version */ | ||
354 | stream_read_uint8(qtmovie->stream); | ||
355 | size_remaining -= 1; | ||
356 | /* flags */ | ||
357 | stream_read_uint8(qtmovie->stream); | ||
358 | stream_read_uint8(qtmovie->stream); | ||
359 | stream_read_uint8(qtmovie->stream); | ||
360 | size_remaining -= 3; | ||
361 | |||
362 | /* default sample size */ | ||
363 | if (stream_read_uint32(qtmovie->stream) != 0) | ||
364 | { | ||
365 | DEBUGF("i was expecting variable samples sizes\n"); | ||
366 | stream_read_uint32(qtmovie->stream); | ||
367 | size_remaining -= 4; | ||
368 | return true; | ||
369 | } | ||
370 | size_remaining -= 4; | ||
371 | |||
372 | qtmovie->res->num_sample_byte_sizes = stream_read_uint32(qtmovie->stream); | ||
373 | size_remaining -= 4; | ||
374 | |||
375 | if (size_remaining) | ||
376 | { | ||
377 | stream_skip(qtmovie->stream, size_remaining); | ||
378 | } | ||
379 | |||
380 | return true; | ||
381 | } | ||
382 | |||
383 | static bool read_chunk_stsc(qtmovie_t *qtmovie, size_t chunk_len) | ||
384 | { | ||
385 | unsigned int i; | ||
386 | uint32_t numentries; | ||
387 | size_t size_remaining = chunk_len - 8; | ||
388 | |||
389 | /* version + flags */ | ||
390 | stream_read_uint32(qtmovie->stream); | ||
391 | size_remaining -= 4; | ||
392 | |||
393 | numentries = stream_read_uint32(qtmovie->stream); | ||
394 | size_remaining -= 4; | ||
395 | |||
396 | qtmovie->res->num_sample_to_chunks = numentries; | ||
397 | qtmovie->res->sample_to_chunk = malloc(numentries * sizeof(sample_to_chunk_t)); | ||
398 | |||
399 | if (!qtmovie->res->sample_to_chunk) | ||
400 | { | ||
401 | DEBUGF("stsc too large\n"); | ||
402 | return false; | ||
403 | } | ||
404 | |||
405 | for (i = 0; i < numentries; i++) | ||
406 | { | ||
407 | qtmovie->res->sample_to_chunk[i].first_chunk = | ||
408 | stream_read_uint32(qtmovie->stream); | ||
409 | qtmovie->res->sample_to_chunk[i].num_samples = | ||
410 | stream_read_uint32(qtmovie->stream); | ||
411 | stream_read_uint32(qtmovie->stream); | ||
412 | size_remaining -= 12; | ||
413 | } | ||
414 | |||
415 | if (size_remaining) | ||
416 | { | ||
417 | DEBUGF("ehm, size remianing?\n"); | ||
418 | stream_skip(qtmovie->stream, size_remaining); | ||
419 | } | ||
420 | |||
421 | return true; | ||
422 | } | ||
423 | |||
424 | static bool read_chunk_stco(qtmovie_t *qtmovie, size_t chunk_len) | ||
425 | { | ||
426 | uint32_t i, k, old_i; | ||
427 | uint32_t numentries; | ||
428 | uint32_t idx = 0; | ||
429 | uint32_t frame; | ||
430 | uint32_t offset; | ||
431 | uint32_t old_first; | ||
432 | uint32_t new_first; | ||
433 | uint32_t old_frame; | ||
434 | size_t size_remaining = chunk_len - 8; | ||
435 | |||
436 | /* version + flags */ | ||
437 | stream_read_uint32(qtmovie->stream); | ||
438 | size_remaining -= 4; | ||
439 | |||
440 | numentries = stream_read_uint32(qtmovie->stream); | ||
441 | size_remaining -= 4; | ||
442 | |||
443 | qtmovie->res->num_lookup_table = numentries; | ||
444 | qtmovie->res->lookup_table = malloc(numentries * sizeof(*qtmovie->res->lookup_table)); | ||
445 | |||
446 | if (!qtmovie->res->lookup_table) | ||
447 | { | ||
448 | DEBUGF("stco too large to allocate lookup_table[]\n"); | ||
449 | return false; | ||
450 | } | ||
451 | |||
452 | /* read first offset */ | ||
453 | offset = stream_read_uint32(qtmovie->stream); | ||
454 | size_remaining -= 4; | ||
455 | |||
456 | /* Build up lookup table. The lookup table contains the sample index and | ||
457 | * byte position in the file for each chunk. This table is used to seek | ||
458 | * and resume (see m4a_seek() and m4a_seek_raw() in libm4a/m4a.c) and | ||
459 | * to skip empty chunks (see m4a_check_sample_offset() in codecs/aac.c and | ||
460 | * libm4a/m4a.c). | ||
461 | * The seek/resume precision is lower than using sample_byte_size[] and | ||
462 | * depends on numentries. Typically the resolution is ~1/10 of all frames | ||
463 | * which equals about 1/4-1/2 seconds. The loss of seek precision is | ||
464 | * accepted to be able to avoid allocation of the large sample_byte_size[] | ||
465 | * table. This reduces the memory consumption by a factor of 2 or even | ||
466 | * more. */ | ||
467 | i = 1; | ||
468 | old_i = 1; | ||
469 | frame = 0; | ||
470 | old_first = qtmovie->res->sample_to_chunk[0].first_chunk; | ||
471 | old_frame = qtmovie->res->sample_to_chunk[0].num_samples; | ||
472 | new_first = qtmovie->res->sample_to_chunk[1].first_chunk; | ||
473 | for (k = 1; k < numentries; ++k) | ||
474 | { | ||
475 | for (; i < qtmovie->res->num_sample_to_chunks; ++i) | ||
476 | { | ||
477 | if (i > old_i) | ||
478 | { | ||
479 | /* Only access sample_to_chunk[] if new data is required. */ | ||
480 | old_first = qtmovie->res->sample_to_chunk[i-1].first_chunk; | ||
481 | old_frame = qtmovie->res->sample_to_chunk[i-1].num_samples; | ||
482 | new_first = qtmovie->res->sample_to_chunk[i ].first_chunk; | ||
483 | old_i = i; | ||
484 | } | ||
485 | |||
486 | if (new_first > k) | ||
487 | break; | ||
488 | |||
489 | frame += (new_first - old_first) * old_frame; | ||
490 | } | ||
491 | frame += (k - old_first) * old_frame; | ||
492 | |||
493 | qtmovie->res->lookup_table[idx].sample = frame; | ||
494 | qtmovie->res->lookup_table[idx].offset = offset; | ||
495 | idx++; | ||
496 | |||
497 | frame -= (k - old_first) * old_frame; | ||
498 | |||
499 | offset = stream_read_uint32(qtmovie->stream); | ||
500 | size_remaining -= 4; | ||
501 | } | ||
502 | /* zero-terminate the lookup table */ | ||
503 | qtmovie->res->lookup_table[idx].sample = 0; | ||
504 | qtmovie->res->lookup_table[idx].offset = 0; | ||
505 | |||
506 | if (size_remaining) | ||
507 | { | ||
508 | DEBUGF("ehm, size remianing?\n"); | ||
509 | stream_skip(qtmovie->stream, size_remaining); | ||
510 | } | ||
511 | |||
512 | return true; | ||
513 | } | ||
514 | |||
515 | static bool read_chunk_stbl(qtmovie_t *qtmovie, size_t chunk_len) | ||
516 | { | ||
517 | size_t size_remaining = chunk_len - 8; | ||
518 | |||
519 | while (size_remaining) | ||
520 | { | ||
521 | size_t sub_chunk_len; | ||
522 | fourcc_t sub_chunk_id; | ||
523 | |||
524 | sub_chunk_len = stream_read_uint32(qtmovie->stream); | ||
525 | if (sub_chunk_len <= 1 || sub_chunk_len > size_remaining) | ||
526 | { | ||
527 | DEBUGF("strange size (%lu) for chunk inside stbl\n", | ||
528 | (unsigned long)sub_chunk_len); | ||
529 | return false; | ||
530 | } | ||
531 | |||
532 | sub_chunk_id = stream_read_uint32(qtmovie->stream); | ||
533 | |||
534 | switch (sub_chunk_id) | ||
535 | { | ||
536 | case MAKEFOURCC('s','t','s','d'): | ||
537 | if (!read_chunk_stsd(qtmovie, sub_chunk_len)) { | ||
538 | return false; | ||
539 | } | ||
540 | break; | ||
541 | case MAKEFOURCC('s','t','t','s'): | ||
542 | if (!read_chunk_stts(qtmovie, sub_chunk_len)) | ||
543 | { | ||
544 | return false; | ||
545 | } | ||
546 | break; | ||
547 | case MAKEFOURCC('s','t','s','z'): | ||
548 | if (!read_chunk_stsz(qtmovie, sub_chunk_len)) | ||
549 | { | ||
550 | return false; | ||
551 | } | ||
552 | break; | ||
553 | case MAKEFOURCC('s','t','s','c'): | ||
554 | if (!read_chunk_stsc(qtmovie, sub_chunk_len)) | ||
555 | { | ||
556 | return false; | ||
557 | } | ||
558 | break; | ||
559 | case MAKEFOURCC('s','t','c','o'): | ||
560 | if (!read_chunk_stco(qtmovie, sub_chunk_len)) | ||
561 | { | ||
562 | return false; | ||
563 | } | ||
564 | break; | ||
565 | default: | ||
566 | /*DEBUGF("(stbl) unknown chunk id: %c%c%c%c\n", | ||
567 | SPLITFOURCC(sub_chunk_id));*/ | ||
568 | stream_skip(qtmovie->stream, sub_chunk_len - 8); | ||
569 | } | ||
570 | |||
571 | size_remaining -= sub_chunk_len; | ||
572 | } | ||
573 | return true; | ||
574 | } | ||
575 | |||
576 | static bool read_chunk_minf(qtmovie_t *qtmovie, size_t chunk_len) | ||
577 | { | ||
578 | size_t size_remaining = chunk_len - 8; | ||
579 | uint32_t i; | ||
580 | |||
581 | /* Check for smhd, only kind of minf we care about */ | ||
582 | |||
583 | if ((i = stream_read_uint32(qtmovie->stream)) != 16) | ||
584 | { | ||
585 | DEBUGF("unexpected size in media info: %ld\n", (long)i); | ||
586 | stream_skip(qtmovie->stream, size_remaining-4); | ||
587 | return true; | ||
588 | } | ||
589 | |||
590 | if (stream_read_uint32(qtmovie->stream) != MAKEFOURCC('s','m','h','d')) | ||
591 | { | ||
592 | DEBUGF("not a sound header! can't handle this.\n"); | ||
593 | return false; | ||
594 | } | ||
595 | |||
596 | /* now skip the rest of the atom */ | ||
597 | stream_skip(qtmovie->stream, 16 - 8); | ||
598 | size_remaining -= 16; | ||
599 | |||
600 | while (size_remaining) | ||
601 | { | ||
602 | size_t sub_chunk_len; | ||
603 | fourcc_t sub_chunk_id; | ||
604 | |||
605 | sub_chunk_len = stream_read_uint32(qtmovie->stream); | ||
606 | |||
607 | if (sub_chunk_len <= 1 || sub_chunk_len > size_remaining) | ||
608 | { | ||
609 | DEBUGF("strange size (%lu) for chunk inside minf\n", | ||
610 | (unsigned long)sub_chunk_len); | ||
611 | return false; | ||
612 | } | ||
613 | |||
614 | sub_chunk_id = stream_read_uint32(qtmovie->stream); | ||
615 | |||
616 | switch (sub_chunk_id) | ||
617 | { | ||
618 | case MAKEFOURCC('s','t','b','l'): | ||
619 | if (!read_chunk_stbl(qtmovie, sub_chunk_len)) { | ||
620 | return false; | ||
621 | } | ||
622 | break; | ||
623 | default: | ||
624 | /*DEBUGF("(minf) unknown chunk id: %c%c%c%c\n", | ||
625 | SPLITFOURCC(sub_chunk_id));*/ | ||
626 | stream_skip(qtmovie->stream, sub_chunk_len - 8); | ||
627 | break; | ||
628 | } | ||
629 | |||
630 | size_remaining -= sub_chunk_len; | ||
631 | } | ||
632 | return true; | ||
633 | } | ||
634 | |||
635 | static bool read_chunk_mdia(qtmovie_t *qtmovie, size_t chunk_len) | ||
636 | { | ||
637 | size_t size_remaining = chunk_len - 8; | ||
638 | |||
639 | while (size_remaining) | ||
640 | { | ||
641 | size_t sub_chunk_len; | ||
642 | fourcc_t sub_chunk_id; | ||
643 | |||
644 | sub_chunk_len = stream_read_uint32(qtmovie->stream); | ||
645 | if (sub_chunk_len <= 1 || sub_chunk_len > size_remaining) | ||
646 | { | ||
647 | DEBUGF("strange size (%lu) for chunk inside mdia\n", | ||
648 | (unsigned long)sub_chunk_len); | ||
649 | return false; | ||
650 | } | ||
651 | |||
652 | sub_chunk_id = stream_read_uint32(qtmovie->stream); | ||
653 | |||
654 | switch (sub_chunk_id) | ||
655 | { | ||
656 | case MAKEFOURCC('m','i','n','f'): | ||
657 | if (!read_chunk_minf(qtmovie, sub_chunk_len)) { | ||
658 | return false; | ||
659 | } | ||
660 | break; | ||
661 | default: | ||
662 | /*DEBUGF("(mdia) unknown chunk id: %c%c%c%c\n", | ||
663 | SPLITFOURCC(sub_chunk_id));*/ | ||
664 | stream_skip(qtmovie->stream, sub_chunk_len - 8); | ||
665 | break; | ||
666 | } | ||
667 | |||
668 | size_remaining -= sub_chunk_len; | ||
669 | } | ||
670 | return true; | ||
671 | } | ||
672 | |||
673 | /* 'trak' - a movie track - contains other atoms */ | ||
674 | static bool read_chunk_trak(qtmovie_t *qtmovie, size_t chunk_len) | ||
675 | { | ||
676 | size_t size_remaining = chunk_len - 8; | ||
677 | |||
678 | while (size_remaining) | ||
679 | { | ||
680 | size_t sub_chunk_len; | ||
681 | fourcc_t sub_chunk_id; | ||
682 | |||
683 | sub_chunk_len = stream_read_uint32(qtmovie->stream); | ||
684 | if (sub_chunk_len <= 1 || sub_chunk_len > size_remaining) | ||
685 | { | ||
686 | DEBUGF("strange size (%lu) for chunk inside trak\n", | ||
687 | (unsigned long)sub_chunk_len); | ||
688 | return false; | ||
689 | } | ||
690 | |||
691 | sub_chunk_id = stream_read_uint32(qtmovie->stream); | ||
692 | |||
693 | switch (sub_chunk_id) | ||
694 | { | ||
695 | case MAKEFOURCC('m','d','i','a'): | ||
696 | if (!read_chunk_mdia(qtmovie, sub_chunk_len)) { | ||
697 | return false; | ||
698 | } | ||
699 | break; | ||
700 | default: | ||
701 | /*DEBUGF("(trak) unknown chunk id: %c%c%c%c\n", | ||
702 | SPLITFOURCC(sub_chunk_id));*/ | ||
703 | stream_skip(qtmovie->stream, sub_chunk_len - 8); | ||
704 | break; | ||
705 | } | ||
706 | |||
707 | size_remaining -= sub_chunk_len; | ||
708 | } | ||
709 | return true; | ||
710 | } | ||
711 | |||
712 | /* 'moov' movie atom - contains other atoms */ | ||
713 | static bool read_chunk_moov(qtmovie_t *qtmovie, size_t chunk_len) | ||
714 | { | ||
715 | size_t size_remaining = chunk_len - 8; | ||
716 | |||
717 | while (size_remaining) | ||
718 | { | ||
719 | size_t sub_chunk_len; | ||
720 | fourcc_t sub_chunk_id; | ||
721 | |||
722 | sub_chunk_len = stream_read_uint32(qtmovie->stream); | ||
723 | if (sub_chunk_len <= 1 || sub_chunk_len > size_remaining) | ||
724 | { | ||
725 | DEBUGF("strange size (%lu) for chunk inside moov\n", | ||
726 | (unsigned long)sub_chunk_len); | ||
727 | return false; | ||
728 | } | ||
729 | |||
730 | sub_chunk_id = stream_read_uint32(qtmovie->stream); | ||
731 | |||
732 | switch (sub_chunk_id) | ||
733 | { | ||
734 | case MAKEFOURCC('t','r','a','k'): | ||
735 | if (!read_chunk_trak(qtmovie, sub_chunk_len)) { | ||
736 | return false; | ||
737 | } | ||
738 | break; | ||
739 | default: | ||
740 | /*DEBUGF("(moov) unknown chunk id: %c%c%c%c\n", | ||
741 | SPLITFOURCC(sub_chunk_id));*/ | ||
742 | stream_skip(qtmovie->stream, sub_chunk_len - 8); | ||
743 | break; | ||
744 | } | ||
745 | |||
746 | size_remaining -= sub_chunk_len; | ||
747 | } | ||
748 | return true; | ||
749 | } | ||
750 | |||
751 | static void read_chunk_mdat(qtmovie_t *qtmovie, size_t chunk_len) | ||
752 | { | ||
753 | size_t size_remaining = chunk_len - 8; | ||
754 | |||
755 | qtmovie->res->mdat_len = size_remaining; | ||
756 | } | ||
757 | |||
758 | int qtmovie_read(stream_t *file, demux_res_t *demux_res) | ||
759 | { | ||
760 | qtmovie_t qtmovie; | ||
761 | |||
762 | /* construct the stream */ | ||
763 | qtmovie.stream = file; | ||
764 | qtmovie.res = demux_res; | ||
765 | |||
766 | /* read the chunks */ | ||
767 | while (1) | ||
768 | { | ||
769 | size_t chunk_len; | ||
770 | fourcc_t chunk_id; | ||
771 | |||
772 | chunk_len = stream_read_uint32(qtmovie.stream); | ||
773 | if (stream_eof(qtmovie.stream)) | ||
774 | { | ||
775 | if(qtmovie.res->mdat_offset == 0 || qtmovie.res->format == 0) | ||
776 | return 0; | ||
777 | stream_seek(qtmovie.stream, qtmovie.res->mdat_offset); | ||
778 | return 1; | ||
779 | } | ||
780 | |||
781 | if (chunk_len == 1) | ||
782 | { | ||
783 | //DEBUGF("need 64bit support\n"); | ||
784 | return 0; | ||
785 | } | ||
786 | chunk_id = stream_read_uint32(qtmovie.stream); | ||
787 | |||
788 | //DEBUGF("Found a chunk %c%c%c%c, length=%d\n",SPLITFOURCC(chunk_id),chunk_len); | ||
789 | switch (chunk_id) | ||
790 | { | ||
791 | case MAKEFOURCC('f','t','y','p'): | ||
792 | read_chunk_ftyp(&qtmovie, chunk_len); | ||
793 | break; | ||
794 | case MAKEFOURCC('m','o','o','v'): | ||
795 | if (!read_chunk_moov(&qtmovie, chunk_len)) { | ||
796 | return 0; | ||
797 | } | ||
798 | break; | ||
799 | case MAKEFOURCC('m','d','a','t'): | ||
800 | /* There can be empty mdats before the real one. If so, skip them */ | ||
801 | if (chunk_len == 8) | ||
802 | break; | ||
803 | read_chunk_mdat(&qtmovie, chunk_len); | ||
804 | qtmovie.res->mdat_offset=stream_tell(qtmovie.stream); | ||
805 | /* If we've already seen the format, assume there's nothing | ||
806 | interesting after the mdat chunk (the file is "streamable"). | ||
807 | This avoids having to seek, which might cause rebuffering. */ | ||
808 | if(qtmovie.res->format > 0) | ||
809 | return 1; | ||
810 | stream_skip(qtmovie.stream, chunk_len - 8); | ||
811 | break; | ||
812 | |||
813 | /* these following atoms can be skipped !!!! */ | ||
814 | case MAKEFOURCC('f','r','e','e'): | ||
815 | stream_skip(qtmovie.stream, chunk_len - 8); | ||
816 | break; | ||
817 | default: | ||
818 | //DEBUGF("(top) unknown chunk id: %c%c%c%c\n",SPLITFOURCC(chunk_id)); | ||
819 | return 0; | ||
820 | } | ||
821 | |||
822 | } | ||
823 | return 0; | ||
824 | } | ||
825 | |||
826 | |||
diff --git a/lib/rbcodec/codecs/libm4a/libm4a.make b/lib/rbcodec/codecs/libm4a/libm4a.make new file mode 100644 index 0000000000..c25f63fa70 --- /dev/null +++ b/lib/rbcodec/codecs/libm4a/libm4a.make | |||
@@ -0,0 +1,18 @@ | |||
1 | # __________ __ ___. | ||
2 | # Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
3 | # Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
4 | # Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
5 | # Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
6 | # \/ \/ \/ \/ \/ | ||
7 | # $Id$ | ||
8 | # | ||
9 | |||
10 | # libm4a | ||
11 | M4ALIB := $(CODECDIR)/libm4a.a | ||
12 | M4ALIB_SRC := $(call preprocess, $(RBCODECLIB_DIR)/codecs/libm4a/SOURCES) | ||
13 | M4ALIB_OBJ := $(call c2obj, $(M4ALIB_SRC)) | ||
14 | OTHER_SRC += $(M4ALIB_SRC) | ||
15 | |||
16 | $(M4ALIB): $(M4ALIB_OBJ) | ||
17 | $(SILENT)$(shell rm -f $@) | ||
18 | $(call PRINTS,AR $(@F))$(AR) rcs $@ $^ >/dev/null | ||
diff --git a/lib/rbcodec/codecs/libm4a/m4a.c b/lib/rbcodec/codecs/libm4a/m4a.c new file mode 100644 index 0000000000..5fe778ac03 --- /dev/null +++ b/lib/rbcodec/codecs/libm4a/m4a.c | |||
@@ -0,0 +1,267 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2005 Dave Chapman, 2011 Andree Buschmann | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #include <codecs.h> | ||
23 | #include <inttypes.h> | ||
24 | #include "m4a.h" | ||
25 | |||
26 | /* Implementation of the stream.h functions used by libalac */ | ||
27 | |||
28 | #define _Swap32(v) do { \ | ||
29 | v = (((v) & 0x000000FF) << 0x18) | \ | ||
30 | (((v) & 0x0000FF00) << 0x08) | \ | ||
31 | (((v) & 0x00FF0000) >> 0x08) | \ | ||
32 | (((v) & 0xFF000000) >> 0x18); } while(0) | ||
33 | |||
34 | #define _Swap16(v) do { \ | ||
35 | v = (((v) & 0x00FF) << 0x08) | \ | ||
36 | (((v) & 0xFF00) >> 0x08); } while (0) | ||
37 | |||
38 | /* A normal read without any byte-swapping */ | ||
39 | void stream_read(stream_t *stream, size_t size, void *buf) | ||
40 | { | ||
41 | stream->ci->read_filebuf(buf,size); | ||
42 | if (stream->ci->curpos >= stream->ci->filesize) { stream->eof=1; } | ||
43 | } | ||
44 | |||
45 | int32_t stream_read_int32(stream_t *stream) | ||
46 | { | ||
47 | int32_t v; | ||
48 | stream_read(stream, 4, &v); | ||
49 | #ifdef ROCKBOX_LITTLE_ENDIAN | ||
50 | _Swap32(v); | ||
51 | #endif | ||
52 | return v; | ||
53 | } | ||
54 | |||
55 | int32_t stream_tell(stream_t *stream) | ||
56 | { | ||
57 | return stream->ci->curpos; | ||
58 | } | ||
59 | |||
60 | uint32_t stream_read_uint32(stream_t *stream) | ||
61 | { | ||
62 | uint32_t v; | ||
63 | stream_read(stream, 4, &v); | ||
64 | #ifdef ROCKBOX_LITTLE_ENDIAN | ||
65 | _Swap32(v); | ||
66 | #endif | ||
67 | return v; | ||
68 | } | ||
69 | |||
70 | uint16_t stream_read_uint16(stream_t *stream) | ||
71 | { | ||
72 | uint16_t v; | ||
73 | stream_read(stream, 2, &v); | ||
74 | #ifdef ROCKBOX_LITTLE_ENDIAN | ||
75 | _Swap16(v); | ||
76 | #endif | ||
77 | return v; | ||
78 | } | ||
79 | |||
80 | uint8_t stream_read_uint8(stream_t *stream) | ||
81 | { | ||
82 | uint8_t v; | ||
83 | stream_read(stream, 1, &v); | ||
84 | return v; | ||
85 | } | ||
86 | |||
87 | void stream_skip(stream_t *stream, size_t skip) | ||
88 | { | ||
89 | stream->ci->advance_buffer(skip); | ||
90 | } | ||
91 | |||
92 | void stream_seek(stream_t *stream, size_t offset) | ||
93 | { | ||
94 | stream->ci->seek_buffer(offset); | ||
95 | } | ||
96 | |||
97 | int stream_eof(stream_t *stream) | ||
98 | { | ||
99 | return stream->eof; | ||
100 | } | ||
101 | |||
102 | void stream_create(stream_t *stream,struct codec_api* ci) | ||
103 | { | ||
104 | stream->ci=ci; | ||
105 | stream->eof=0; | ||
106 | } | ||
107 | |||
108 | /* Check if there is a dedicated byte position contained for the given frame. | ||
109 | * Return this byte position in case of success or return -1. This allows to | ||
110 | * skip empty samples. | ||
111 | * During standard playback the search result (index i) will always increase. | ||
112 | * Therefor we save this index and let the caller set this value again as start | ||
113 | * index when calling m4a_check_sample_offset() for the next frame. This | ||
114 | * reduces the overall loop count significantly. */ | ||
115 | int m4a_check_sample_offset(demux_res_t *demux_res, uint32_t frame, uint32_t *start) | ||
116 | { | ||
117 | uint32_t i = *start; | ||
118 | for (i=0; i<demux_res->num_lookup_table; ++i) | ||
119 | { | ||
120 | if (demux_res->lookup_table[i].sample > frame || | ||
121 | demux_res->lookup_table[i].offset == 0) | ||
122 | return -1; | ||
123 | if (demux_res->lookup_table[i].sample == frame) | ||
124 | break; | ||
125 | } | ||
126 | *start = i; | ||
127 | return demux_res->lookup_table[i].offset; | ||
128 | } | ||
129 | |||
130 | /* Find the exact or preceding frame in lookup_table[]. Return both frame | ||
131 | * and byte position of this match. */ | ||
132 | static void gather_offset(demux_res_t *demux_res, uint32_t *frame, uint32_t *offset) | ||
133 | { | ||
134 | uint32_t i = 0; | ||
135 | for (i=0; i<demux_res->num_lookup_table; ++i) | ||
136 | { | ||
137 | if (demux_res->lookup_table[i].offset == 0) | ||
138 | break; | ||
139 | if (demux_res->lookup_table[i].sample > *frame) | ||
140 | break; | ||
141 | } | ||
142 | i = (i>0) ? i-1 : 0; /* We want the last chunk _before_ *frame. */ | ||
143 | *frame = demux_res->lookup_table[i].sample; | ||
144 | *offset = demux_res->lookup_table[i].offset; | ||
145 | } | ||
146 | |||
147 | /* Seek to desired sound sample location. Return 1 on success (and modify | ||
148 | * sound_samples_done and current_sample), 0 if failed. | ||
149 | * | ||
150 | * Find the sample (=frame) that contains the given sound sample, find a best | ||
151 | * fit for this sample in the lookup_table[], seek to the byte position. */ | ||
152 | unsigned int m4a_seek(demux_res_t* demux_res, stream_t* stream, | ||
153 | uint32_t sound_sample_loc, uint32_t* sound_samples_done, | ||
154 | int* current_sample) | ||
155 | { | ||
156 | uint32_t i = 0; | ||
157 | uint32_t tmp_var, tmp_cnt, tmp_dur; | ||
158 | uint32_t new_sample = 0; /* Holds the amount of chunks/frames. */ | ||
159 | uint32_t new_sound_sample = 0; /* Sums up total amount of samples. */ | ||
160 | uint32_t new_pos; /* Holds the desired chunk/frame index. */ | ||
161 | |||
162 | /* First check we have the appropriate metadata - we should always | ||
163 | * have it. | ||
164 | */ | ||
165 | if (!demux_res->num_time_to_samples || !demux_res->num_sample_byte_sizes) | ||
166 | { | ||
167 | return 0; | ||
168 | } | ||
169 | |||
170 | /* Find the destination block from time_to_sample array */ | ||
171 | time_to_sample_t *tab = demux_res->time_to_sample; | ||
172 | while (i < demux_res->num_time_to_samples) | ||
173 | { | ||
174 | tmp_cnt = tab[i].sample_count; | ||
175 | tmp_dur = tab[i].sample_duration; | ||
176 | tmp_var = tmp_cnt * tmp_dur; | ||
177 | if (sound_sample_loc <= new_sound_sample + tmp_var) | ||
178 | { | ||
179 | tmp_var = (sound_sample_loc - new_sound_sample); | ||
180 | new_sample += tmp_var / tmp_dur; | ||
181 | new_sound_sample += tmp_var; | ||
182 | break; | ||
183 | } | ||
184 | new_sample += tmp_cnt; | ||
185 | new_sound_sample += tmp_var; | ||
186 | ++i; | ||
187 | } | ||
188 | |||
189 | /* We know the new sample (=frame), now calculate the file position. */ | ||
190 | gather_offset(demux_res, &new_sample, &new_pos); | ||
191 | |||
192 | /* We know the new file position, so let's try to seek to it */ | ||
193 | if (stream->ci->seek_buffer(new_pos)) | ||
194 | { | ||
195 | *sound_samples_done = new_sound_sample; | ||
196 | *current_sample = new_sample; | ||
197 | return 1; | ||
198 | } | ||
199 | |||
200 | return 0; | ||
201 | } | ||
202 | |||
203 | /* Seek to the sample containing file_loc. Return 1 on success (and modify | ||
204 | * sound_samples_done and current_sample), 0 if failed. | ||
205 | * | ||
206 | * Seeking uses the following arrays: | ||
207 | * | ||
208 | * 1) the lookup_table array contains the file offset for the first sample | ||
209 | * of each chunk. | ||
210 | * | ||
211 | * 2) the time_to_sample array contains the duration (in sound samples) | ||
212 | * of each sample of data. | ||
213 | * | ||
214 | * Locate the chunk containing location (using lookup_table), find the first | ||
215 | * sample of that chunk (using lookup_table). Then use time_to_sample to | ||
216 | * calculate the sound_samples_done value. | ||
217 | */ | ||
218 | unsigned int m4a_seek_raw(demux_res_t* demux_res, stream_t* stream, | ||
219 | uint32_t file_loc, uint32_t* sound_samples_done, | ||
220 | int* current_sample) | ||
221 | { | ||
222 | uint32_t i; | ||
223 | uint32_t chunk_sample = 0; | ||
224 | uint32_t total_samples = 0; | ||
225 | uint32_t new_sound_sample = 0; | ||
226 | uint32_t tmp_dur; | ||
227 | uint32_t tmp_cnt; | ||
228 | uint32_t new_pos; | ||
229 | |||
230 | /* We know the desired byte offset, search for the chunk right before. | ||
231 | * Return the associated sample to this chunk as chunk_sample. */ | ||
232 | for (i=0; i < demux_res->num_lookup_table; ++i) | ||
233 | { | ||
234 | if (demux_res->lookup_table[i].offset > file_loc) | ||
235 | break; | ||
236 | } | ||
237 | i = (i>0) ? i-1 : 0; /* We want the last chunk _before_ file_loc. */ | ||
238 | chunk_sample = demux_res->lookup_table[i].sample; | ||
239 | new_pos = demux_res->lookup_table[i].offset; | ||
240 | |||
241 | /* Get sound sample offset. */ | ||
242 | i = 0; | ||
243 | time_to_sample_t *tab2 = demux_res->time_to_sample; | ||
244 | while (i < demux_res->num_time_to_samples) | ||
245 | { | ||
246 | tmp_dur = tab2[i].sample_duration; | ||
247 | tmp_cnt = tab2[i].sample_count; | ||
248 | total_samples += tmp_cnt; | ||
249 | new_sound_sample += tmp_cnt * tmp_dur; | ||
250 | if (chunk_sample <= total_samples) | ||
251 | { | ||
252 | new_sound_sample += (chunk_sample - total_samples) * tmp_dur; | ||
253 | break; | ||
254 | } | ||
255 | ++i; | ||
256 | } | ||
257 | |||
258 | /* Go to the new file position. */ | ||
259 | if (stream->ci->seek_buffer(new_pos)) | ||
260 | { | ||
261 | *sound_samples_done = new_sound_sample; | ||
262 | *current_sample = chunk_sample; | ||
263 | return 1; | ||
264 | } | ||
265 | |||
266 | return 0; | ||
267 | } | ||
diff --git a/lib/rbcodec/codecs/libm4a/m4a.h b/lib/rbcodec/codecs/libm4a/m4a.h new file mode 100644 index 0000000000..aa8e768045 --- /dev/null +++ b/lib/rbcodec/codecs/libm4a/m4a.h | |||
@@ -0,0 +1,138 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2005 Dave Chapman | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #ifndef _M4A_H | ||
23 | #define _M4A_H | ||
24 | |||
25 | #include <codecs.h> | ||
26 | #include <inttypes.h> | ||
27 | |||
28 | /* AAC codecdata appears to always be less than 8 bytes - see | ||
29 | AudioSpecificConfig2 in libfaad/mp4.c | ||
30 | |||
31 | ALAC codecdata appears to always be 44 bytes (see alac_set_info in | ||
32 | libalac/alac.c) but my test file contains 56 bytes. | ||
33 | |||
34 | So we go safe and round up to 64 bytes - if we find more than this, | ||
35 | we give an error (even though we could possibly continue), so we | ||
36 | can increase this buffer. | ||
37 | */ | ||
38 | |||
39 | #define MAX_CODECDATA_SIZE 64 | ||
40 | |||
41 | typedef struct { | ||
42 | struct codec_api* ci; | ||
43 | int eof; | ||
44 | } stream_t; | ||
45 | |||
46 | typedef uint32_t fourcc_t; | ||
47 | |||
48 | typedef struct | ||
49 | { | ||
50 | uint32_t first_chunk; | ||
51 | uint32_t num_samples; | ||
52 | } sample_to_chunk_t; | ||
53 | |||
54 | typedef struct | ||
55 | { | ||
56 | uint32_t sample_count; | ||
57 | uint32_t sample_duration; | ||
58 | } time_to_sample_t; | ||
59 | |||
60 | typedef struct | ||
61 | { | ||
62 | uint32_t sample; | ||
63 | uint32_t offset; | ||
64 | } sample_offset_t; | ||
65 | |||
66 | typedef struct | ||
67 | { | ||
68 | uint16_t num_channels; | ||
69 | uint16_t sound_sample_size; | ||
70 | uint32_t sound_sample_rate; | ||
71 | fourcc_t format; | ||
72 | void *buf; | ||
73 | |||
74 | sample_to_chunk_t *sample_to_chunk; | ||
75 | uint32_t num_sample_to_chunks; | ||
76 | |||
77 | sample_offset_t *lookup_table; | ||
78 | uint32_t num_lookup_table; | ||
79 | |||
80 | time_to_sample_t *time_to_sample; | ||
81 | uint32_t num_time_to_samples; | ||
82 | |||
83 | uint32_t num_sample_byte_sizes; | ||
84 | |||
85 | uint32_t codecdata_len; | ||
86 | uint8_t codecdata[MAX_CODECDATA_SIZE]; | ||
87 | |||
88 | int mdat_offset; | ||
89 | uint32_t mdat_len; | ||
90 | #if 0 | ||
91 | void *mdat; | ||
92 | #endif | ||
93 | } demux_res_t; | ||
94 | |||
95 | int qtmovie_read(stream_t *stream, demux_res_t *demux_res); | ||
96 | |||
97 | #ifndef MAKEFOURCC | ||
98 | #define MAKEFOURCC(ch0, ch1, ch2, ch3) ( \ | ||
99 | ( (int32_t)(char)(ch0) << 24 ) | \ | ||
100 | ( (int32_t)(char)(ch1) << 16 ) | \ | ||
101 | ( (int32_t)(char)(ch2) << 8 ) | \ | ||
102 | ( (int32_t)(char)(ch3) ) ) | ||
103 | #endif | ||
104 | |||
105 | #ifndef SLPITFOURCC | ||
106 | /* splits it into ch0, ch1, ch2, ch3 - use for printf's */ | ||
107 | #define SPLITFOURCC(code) \ | ||
108 | (char)((int32_t)code >> 24), \ | ||
109 | (char)((int32_t)code >> 16), \ | ||
110 | (char)((int32_t)code >> 8), \ | ||
111 | (char)code | ||
112 | #endif | ||
113 | |||
114 | void stream_read(stream_t *stream, size_t len, void *buf); | ||
115 | |||
116 | int32_t stream_tell(stream_t *stream); | ||
117 | int32_t stream_read_int32(stream_t *stream); | ||
118 | uint32_t stream_read_uint32(stream_t *stream); | ||
119 | |||
120 | uint16_t stream_read_uint16(stream_t *stream); | ||
121 | |||
122 | uint8_t stream_read_uint8(stream_t *stream); | ||
123 | |||
124 | void stream_skip(stream_t *stream, size_t skip); | ||
125 | void stream_seek(stream_t *stream, size_t offset); | ||
126 | |||
127 | int stream_eof(stream_t *stream); | ||
128 | |||
129 | void stream_create(stream_t *stream,struct codec_api* ci); | ||
130 | unsigned int get_sample_offset(demux_res_t *demux_res, uint32_t sample); | ||
131 | unsigned int m4a_seek (demux_res_t* demux_res, stream_t* stream, | ||
132 | uint32_t sound_sample_loc, uint32_t* sound_samples_done, | ||
133 | int* current_sample); | ||
134 | unsigned int m4a_seek_raw (demux_res_t* demux_res, stream_t* stream, | ||
135 | uint32_t file_loc, uint32_t* sound_samples_done, int* current_sample); | ||
136 | int m4a_check_sample_offset(demux_res_t *demux_res, uint32_t frame, uint32_t *start); | ||
137 | |||
138 | #endif /* STREAM_H */ | ||