summaryrefslogtreecommitdiff
path: root/lib/rbcodec/codecs/libm4a
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rbcodec/codecs/libm4a')
-rw-r--r--lib/rbcodec/codecs/libm4a/SOURCES2
-rw-r--r--lib/rbcodec/codecs/libm4a/demux.c826
-rw-r--r--lib/rbcodec/codecs/libm4a/libm4a.make18
-rw-r--r--lib/rbcodec/codecs/libm4a/m4a.c267
-rw-r--r--lib/rbcodec/codecs/libm4a/m4a.h138
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 @@
1m4a.c
2demux.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
47typedef struct
48{
49 stream_t *stream;
50 demux_res_t *res;
51} qtmovie_t;
52
53
54/* chunk handlers */
55static 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
85static 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 */
102static 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
163static 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
306static 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
349static 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
383static 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
424static 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
515static 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
576static 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
635static 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 */
674static 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 */
713static 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
751static 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
758int 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
11M4ALIB := $(CODECDIR)/libm4a.a
12M4ALIB_SRC := $(call preprocess, $(RBCODECLIB_DIR)/codecs/libm4a/SOURCES)
13M4ALIB_OBJ := $(call c2obj, $(M4ALIB_SRC))
14OTHER_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 */
39void 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
45int32_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
55int32_t stream_tell(stream_t *stream)
56{
57 return stream->ci->curpos;
58}
59
60uint32_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
70uint16_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
80uint8_t stream_read_uint8(stream_t *stream)
81{
82 uint8_t v;
83 stream_read(stream, 1, &v);
84 return v;
85}
86
87void stream_skip(stream_t *stream, size_t skip)
88{
89 stream->ci->advance_buffer(skip);
90}
91
92void stream_seek(stream_t *stream, size_t offset)
93{
94 stream->ci->seek_buffer(offset);
95}
96
97int stream_eof(stream_t *stream)
98{
99 return stream->eof;
100}
101
102void 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. */
115int 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. */
132static 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. */
152unsigned 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 */
218unsigned 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
41typedef struct {
42 struct codec_api* ci;
43 int eof;
44} stream_t;
45
46typedef uint32_t fourcc_t;
47
48typedef struct
49{
50 uint32_t first_chunk;
51 uint32_t num_samples;
52} sample_to_chunk_t;
53
54typedef struct
55{
56 uint32_t sample_count;
57 uint32_t sample_duration;
58} time_to_sample_t;
59
60typedef struct
61{
62 uint32_t sample;
63 uint32_t offset;
64} sample_offset_t;
65
66typedef 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
95int 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
114void stream_read(stream_t *stream, size_t len, void *buf);
115
116int32_t stream_tell(stream_t *stream);
117int32_t stream_read_int32(stream_t *stream);
118uint32_t stream_read_uint32(stream_t *stream);
119
120uint16_t stream_read_uint16(stream_t *stream);
121
122uint8_t stream_read_uint8(stream_t *stream);
123
124void stream_skip(stream_t *stream, size_t skip);
125void stream_seek(stream_t *stream, size_t offset);
126
127int stream_eof(stream_t *stream);
128
129void stream_create(stream_t *stream,struct codec_api* ci);
130unsigned int get_sample_offset(demux_res_t *demux_res, uint32_t sample);
131unsigned 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);
134unsigned 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);
136int m4a_check_sample_offset(demux_res_t *demux_res, uint32_t frame, uint32_t *start);
137
138#endif /* STREAM_H */