summaryrefslogtreecommitdiff
path: root/lib/rbcodec/codecs/libm4a/demux.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rbcodec/codecs/libm4a/demux.c')
-rw-r--r--lib/rbcodec/codecs/libm4a/demux.c826
1 files changed, 826 insertions, 0 deletions
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