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/demux.c | |
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/demux.c')
-rw-r--r-- | lib/rbcodec/codecs/libm4a/demux.c | 826 |
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 | |||
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 | |||