diff options
Diffstat (limited to 'apps/metadata/mp4.c')
-rw-r--r-- | apps/metadata/mp4.c | 669 |
1 files changed, 669 insertions, 0 deletions
diff --git a/apps/metadata/mp4.c b/apps/metadata/mp4.c new file mode 100644 index 0000000000..6523bb6551 --- /dev/null +++ b/apps/metadata/mp4.c | |||
@@ -0,0 +1,669 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2005 Dave Chapman | ||
11 | * | ||
12 | * All files in this archive are subject to the GNU General Public License. | ||
13 | * See the file COPYING in the source tree root for full license agreement. | ||
14 | * | ||
15 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
16 | * KIND, either express or implied. | ||
17 | * | ||
18 | ****************************************************************************/ | ||
19 | #include <stdio.h> | ||
20 | #include <string.h> | ||
21 | #include <stdlib.h> | ||
22 | #include <ctype.h> | ||
23 | #include <inttypes.h> | ||
24 | |||
25 | #include "system.h" | ||
26 | #include "errno.h" | ||
27 | #include "id3.h" | ||
28 | #include "metadata_common.h" | ||
29 | #include "logf.h" | ||
30 | #include "debug.h" | ||
31 | #include "replaygain.h" | ||
32 | |||
33 | #define MP4_ID(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) | ||
34 | |||
35 | #define MP4_3gp6 MP4_ID('3', 'g', 'p', '6') | ||
36 | #define MP4_alac MP4_ID('a', 'l', 'a', 'c') | ||
37 | #define MP4_calb MP4_ID(0xa9, 'a', 'l', 'b') | ||
38 | #define MP4_cART MP4_ID(0xa9, 'A', 'R', 'T') | ||
39 | #define MP4_cnam MP4_ID(0xa9, 'n', 'a', 'm') | ||
40 | #define MP4_cwrt MP4_ID(0xa9, 'w', 'r', 't') | ||
41 | #define MP4_esds MP4_ID('e', 's', 'd', 's') | ||
42 | #define MP4_ftyp MP4_ID('f', 't', 'y', 'p') | ||
43 | #define MP4_gnre MP4_ID('g', 'n', 'r', 'e') | ||
44 | #define MP4_hdlr MP4_ID('h', 'd', 'l', 'r') | ||
45 | #define MP4_ilst MP4_ID('i', 'l', 's', 't') | ||
46 | #define MP4_M4A MP4_ID('M', '4', 'A', ' ') | ||
47 | #define MP4_M4B MP4_ID('M', '4', 'B', ' ') | ||
48 | #define MP4_mdat MP4_ID('m', 'd', 'a', 't') | ||
49 | #define MP4_mdia MP4_ID('m', 'd', 'i', 'a') | ||
50 | #define MP4_mdir MP4_ID('m', 'd', 'i', 'r') | ||
51 | #define MP4_meta MP4_ID('m', 'e', 't', 'a') | ||
52 | #define MP4_minf MP4_ID('m', 'i', 'n', 'f') | ||
53 | #define MP4_moov MP4_ID('m', 'o', 'o', 'v') | ||
54 | #define MP4_mp4a MP4_ID('m', 'p', '4', 'a') | ||
55 | #define MP4_mp42 MP4_ID('m', 'p', '4', '2') | ||
56 | #define MP4_qt MP4_ID('q', 't', ' ', ' ') | ||
57 | #define MP4_soun MP4_ID('s', 'o', 'u', 'n') | ||
58 | #define MP4_stbl MP4_ID('s', 't', 'b', 'l') | ||
59 | #define MP4_stsd MP4_ID('s', 't', 's', 'd') | ||
60 | #define MP4_stts MP4_ID('s', 't', 't', 's') | ||
61 | #define MP4_trak MP4_ID('t', 'r', 'a', 'k') | ||
62 | #define MP4_trkn MP4_ID('t', 'r', 'k', 'n') | ||
63 | #define MP4_udta MP4_ID('u', 'd', 't', 'a') | ||
64 | #define MP4_extra MP4_ID('-', '-', '-', '-') | ||
65 | |||
66 | /* Read the tag data from an MP4 file, storing up to buffer_size bytes in | ||
67 | * buffer. | ||
68 | */ | ||
69 | static unsigned long read_mp4_tag(int fd, unsigned int size_left, char* buffer, | ||
70 | unsigned int buffer_left) | ||
71 | { | ||
72 | unsigned int bytes_read = 0; | ||
73 | |||
74 | if (buffer_left == 0) | ||
75 | { | ||
76 | lseek(fd, size_left, SEEK_CUR); /* Skip everything */ | ||
77 | } | ||
78 | else | ||
79 | { | ||
80 | /* Skip the data tag header - maybe we should parse it properly? */ | ||
81 | lseek(fd, 16, SEEK_CUR); | ||
82 | size_left -= 16; | ||
83 | |||
84 | if (size_left > buffer_left) | ||
85 | { | ||
86 | read(fd, buffer, buffer_left); | ||
87 | lseek(fd, size_left - buffer_left, SEEK_CUR); | ||
88 | bytes_read = buffer_left; | ||
89 | } | ||
90 | else | ||
91 | { | ||
92 | read(fd, buffer, size_left); | ||
93 | bytes_read = size_left; | ||
94 | } | ||
95 | } | ||
96 | |||
97 | return bytes_read; | ||
98 | } | ||
99 | |||
100 | /* Read a string tag from an MP4 file */ | ||
101 | static unsigned int read_mp4_tag_string(int fd, int size_left, char** buffer, | ||
102 | unsigned int* buffer_left, char** dest) | ||
103 | { | ||
104 | unsigned int bytes_read = read_mp4_tag(fd, size_left, *buffer, | ||
105 | *buffer_left - 1); | ||
106 | unsigned int length = 0; | ||
107 | |||
108 | if (bytes_read) | ||
109 | { | ||
110 | (*buffer)[bytes_read] = 0; | ||
111 | *dest = *buffer; | ||
112 | length = strlen(*buffer) + 1; | ||
113 | *buffer_left -= length; | ||
114 | *buffer += length; | ||
115 | } | ||
116 | else | ||
117 | { | ||
118 | *dest = NULL; | ||
119 | } | ||
120 | |||
121 | return length; | ||
122 | } | ||
123 | |||
124 | static unsigned int read_mp4_atom(int fd, unsigned int* size, | ||
125 | unsigned int* type, unsigned int size_left) | ||
126 | { | ||
127 | read_uint32be(fd, size); | ||
128 | read_uint32be(fd, type); | ||
129 | |||
130 | if (*size == 1) | ||
131 | { | ||
132 | /* FAT32 doesn't support files this big, so something seems to | ||
133 | * be wrong. (64-bit sizes should only be used when required.) | ||
134 | */ | ||
135 | errno = EFBIG; | ||
136 | *type = 0; | ||
137 | return 0; | ||
138 | } | ||
139 | |||
140 | if (*size > 0) | ||
141 | { | ||
142 | if (*size > size_left) | ||
143 | { | ||
144 | size_left = 0; | ||
145 | } | ||
146 | else | ||
147 | { | ||
148 | size_left -= *size; | ||
149 | } | ||
150 | |||
151 | *size -= 8; | ||
152 | } | ||
153 | else | ||
154 | { | ||
155 | *size = size_left; | ||
156 | size_left = 0; | ||
157 | } | ||
158 | |||
159 | return size_left; | ||
160 | } | ||
161 | |||
162 | static unsigned int read_mp4_length(int fd, unsigned int* size) | ||
163 | { | ||
164 | unsigned int length = 0; | ||
165 | int bytes = 0; | ||
166 | unsigned char c; | ||
167 | |||
168 | do | ||
169 | { | ||
170 | read(fd, &c, 1); | ||
171 | bytes++; | ||
172 | (*size)--; | ||
173 | length = (length << 7) | (c & 0x7F); | ||
174 | } | ||
175 | while ((c & 0x80) && (bytes < 4) && (*size > 0)); | ||
176 | |||
177 | return length; | ||
178 | } | ||
179 | |||
180 | static bool read_mp4_esds(int fd, struct mp3entry* id3, | ||
181 | unsigned int* size) | ||
182 | { | ||
183 | unsigned char buf[8]; | ||
184 | bool sbr = false; | ||
185 | |||
186 | lseek(fd, 4, SEEK_CUR); /* Version and flags. */ | ||
187 | read(fd, buf, 1); /* Verify ES_DescrTag. */ | ||
188 | *size -= 5; | ||
189 | |||
190 | if (*buf == 3) | ||
191 | { | ||
192 | /* read length */ | ||
193 | if (read_mp4_length(fd, size) < 20) | ||
194 | { | ||
195 | return sbr; | ||
196 | } | ||
197 | |||
198 | lseek(fd, 3, SEEK_CUR); | ||
199 | *size -= 3; | ||
200 | } | ||
201 | else | ||
202 | { | ||
203 | lseek(fd, 2, SEEK_CUR); | ||
204 | *size -= 2; | ||
205 | } | ||
206 | |||
207 | read(fd, buf, 1); /* Verify DecoderConfigDescrTab. */ | ||
208 | *size -= 1; | ||
209 | |||
210 | if (*buf != 4) | ||
211 | { | ||
212 | return sbr; | ||
213 | } | ||
214 | |||
215 | if (read_mp4_length(fd, size) < 13) | ||
216 | { | ||
217 | return sbr; | ||
218 | } | ||
219 | |||
220 | lseek(fd, 13, SEEK_CUR); /* Skip audio type, bit rates, etc. */ | ||
221 | read(fd, buf, 1); | ||
222 | *size -= 14; | ||
223 | |||
224 | if (*buf != 5) /* Verify DecSpecificInfoTag. */ | ||
225 | { | ||
226 | return sbr; | ||
227 | } | ||
228 | |||
229 | { | ||
230 | static const int sample_rates[] = | ||
231 | { | ||
232 | 96000, 88200, 64000, 48000, 44100, 32000, | ||
233 | 24000, 22050, 16000, 12000, 11025, 8000 | ||
234 | }; | ||
235 | unsigned long bits; | ||
236 | unsigned int length; | ||
237 | unsigned int index; | ||
238 | unsigned int type; | ||
239 | |||
240 | /* Read the (leading part of the) decoder config. */ | ||
241 | length = read_mp4_length(fd, size); | ||
242 | length = MIN(length, *size); | ||
243 | length = MIN(length, sizeof(buf)); | ||
244 | memset(buf, 0, sizeof(buf)); | ||
245 | read(fd, buf, length); | ||
246 | *size -= length; | ||
247 | |||
248 | /* Maybe time to write a simple read_bits function... */ | ||
249 | |||
250 | /* Decoder config format: | ||
251 | * Object type - 5 bits | ||
252 | * Frequency index - 4 bits | ||
253 | * Channel configuration - 4 bits | ||
254 | */ | ||
255 | bits = get_long_be(buf); | ||
256 | type = bits >> 27; /* Object type - 5 bits */ | ||
257 | index = (bits >> 23) & 0xf; /* Frequency index - 4 bits */ | ||
258 | |||
259 | if (index < (sizeof(sample_rates) / sizeof(*sample_rates))) | ||
260 | { | ||
261 | id3->frequency = sample_rates[index]; | ||
262 | } | ||
263 | |||
264 | if (type == 5) | ||
265 | { | ||
266 | DEBUGF("MP4: SBR\n"); | ||
267 | unsigned int old_index = index; | ||
268 | |||
269 | sbr = true; | ||
270 | index = (bits >> 15) & 0xf; /* Frequency index - 4 bits */ | ||
271 | |||
272 | if (index == 15) | ||
273 | { | ||
274 | /* 17 bits read so far... */ | ||
275 | bits = get_long_be(&buf[2]); | ||
276 | id3->frequency = (bits >> 7) & 0x00ffffff; | ||
277 | } | ||
278 | else if (index < (sizeof(sample_rates) / sizeof(*sample_rates))) | ||
279 | { | ||
280 | id3->frequency = sample_rates[index]; | ||
281 | } | ||
282 | |||
283 | if (old_index == index) | ||
284 | { | ||
285 | /* Downsampled SBR */ | ||
286 | id3->frequency *= 2; | ||
287 | } | ||
288 | } | ||
289 | /* Skip 13 bits from above, plus 3 bits, then read 11 bits */ | ||
290 | else if ((length >= 4) && (((bits >> 5) & 0x7ff) == 0x2b7)) | ||
291 | { | ||
292 | /* extensionAudioObjectType */ | ||
293 | DEBUGF("MP4: extensionAudioType\n"); | ||
294 | type = bits & 0x1f; /* Object type - 5 bits*/ | ||
295 | bits = get_long_be(&buf[4]); | ||
296 | |||
297 | if (type == 5) | ||
298 | { | ||
299 | sbr = bits >> 31; | ||
300 | |||
301 | if (sbr) | ||
302 | { | ||
303 | unsigned int old_index = index; | ||
304 | |||
305 | /* 1 bit read so far */ | ||
306 | index = (bits >> 27) & 0xf; /* Frequency index - 4 bits */ | ||
307 | |||
308 | if (index == 15) | ||
309 | { | ||
310 | /* 5 bits read so far */ | ||
311 | id3->frequency = (bits >> 3) & 0x00ffffff; | ||
312 | } | ||
313 | else if (index < (sizeof(sample_rates) / sizeof(*sample_rates))) | ||
314 | { | ||
315 | id3->frequency = sample_rates[index]; | ||
316 | } | ||
317 | |||
318 | if (old_index == index) | ||
319 | { | ||
320 | /* Downsampled SBR */ | ||
321 | id3->frequency *= 2; | ||
322 | } | ||
323 | } | ||
324 | } | ||
325 | } | ||
326 | |||
327 | if (!sbr && (id3->frequency <= 24000) && (length <= 2)) | ||
328 | { | ||
329 | /* Double the frequency for low-frequency files without a "long" | ||
330 | * DecSpecificConfig header. The file may or may not contain SBR, | ||
331 | * but here we guess it does if the header is short. This can | ||
332 | * fail on some files, but it's the best we can do, short of | ||
333 | * decoding (parts of) the file. | ||
334 | */ | ||
335 | id3->frequency *= 2; | ||
336 | } | ||
337 | } | ||
338 | |||
339 | return sbr; | ||
340 | } | ||
341 | |||
342 | static bool read_mp4_tags(int fd, struct mp3entry* id3, | ||
343 | unsigned int size_left) | ||
344 | { | ||
345 | unsigned int size; | ||
346 | unsigned int type; | ||
347 | unsigned int buffer_left = sizeof(id3->id3v2buf) + sizeof(id3->id3v1buf); | ||
348 | char* buffer = id3->id3v2buf; | ||
349 | bool cwrt = false; | ||
350 | |||
351 | do | ||
352 | { | ||
353 | size_left = read_mp4_atom(fd, &size, &type, size_left); | ||
354 | |||
355 | /* DEBUGF("Tag atom: '%c%c%c%c' (%d bytes left)\n", type >> 24 & 0xff, | ||
356 | type >> 16 & 0xff, type >> 8 & 0xff, type & 0xff, size); */ | ||
357 | |||
358 | switch (type) | ||
359 | { | ||
360 | case MP4_cnam: | ||
361 | read_mp4_tag_string(fd, size, &buffer, &buffer_left, | ||
362 | &id3->title); | ||
363 | break; | ||
364 | |||
365 | case MP4_cART: | ||
366 | read_mp4_tag_string(fd, size, &buffer, &buffer_left, | ||
367 | &id3->artist); | ||
368 | break; | ||
369 | |||
370 | case MP4_calb: | ||
371 | read_mp4_tag_string(fd, size, &buffer, &buffer_left, | ||
372 | &id3->album); | ||
373 | break; | ||
374 | |||
375 | case MP4_cwrt: | ||
376 | read_mp4_tag_string(fd, size, &buffer, &buffer_left, | ||
377 | &id3->composer); | ||
378 | cwrt = false; | ||
379 | break; | ||
380 | |||
381 | case MP4_gnre: | ||
382 | { | ||
383 | unsigned short genre; | ||
384 | |||
385 | read_mp4_tag(fd, size, (char*) &genre, sizeof(genre)); | ||
386 | id3->genre_string = id3_get_num_genre(betoh16(genre) - 1); | ||
387 | } | ||
388 | break; | ||
389 | |||
390 | case MP4_trkn: | ||
391 | { | ||
392 | unsigned short n[2]; | ||
393 | |||
394 | read_mp4_tag(fd, size, (char*) &n, sizeof(n)); | ||
395 | id3->tracknum = betoh16(n[1]); | ||
396 | } | ||
397 | break; | ||
398 | |||
399 | case MP4_extra: | ||
400 | { | ||
401 | char tag_name[TAG_NAME_LENGTH]; | ||
402 | unsigned int sub_size; | ||
403 | |||
404 | /* "mean" atom */ | ||
405 | read_uint32be(fd, &sub_size); | ||
406 | size -= sub_size; | ||
407 | lseek(fd, sub_size - 4, SEEK_CUR); | ||
408 | /* "name" atom */ | ||
409 | read_uint32be(fd, &sub_size); | ||
410 | size -= sub_size; | ||
411 | lseek(fd, 8, SEEK_CUR); | ||
412 | sub_size -= 12; | ||
413 | |||
414 | if (sub_size > sizeof(tag_name) - 1) | ||
415 | { | ||
416 | read(fd, tag_name, sizeof(tag_name) - 1); | ||
417 | lseek(fd, sub_size - sizeof(tag_name) - 1, SEEK_CUR); | ||
418 | tag_name[sizeof(tag_name) - 1] = 0; | ||
419 | } | ||
420 | else | ||
421 | { | ||
422 | read(fd, tag_name, sub_size); | ||
423 | tag_name[sub_size] = 0; | ||
424 | } | ||
425 | |||
426 | if ((strcasecmp(tag_name, "composer") == 0) && !cwrt) | ||
427 | { | ||
428 | read_mp4_tag_string(fd, size, &buffer, &buffer_left, | ||
429 | &id3->composer); | ||
430 | } | ||
431 | else if (strcasecmp(tag_name, "iTunSMPB") == 0) | ||
432 | { | ||
433 | char value[TAG_VALUE_LENGTH]; | ||
434 | char* value_p = value; | ||
435 | char* any; | ||
436 | unsigned int length = sizeof(value); | ||
437 | |||
438 | read_mp4_tag_string(fd, size, &value_p, &length, &any); | ||
439 | id3->lead_trim = get_itunes_int32(value, 1); | ||
440 | id3->tail_trim = get_itunes_int32(value, 2); | ||
441 | DEBUGF("AAC: lead_trim %d, tail_trim %d\n", | ||
442 | id3->lead_trim, id3->tail_trim); | ||
443 | } | ||
444 | else | ||
445 | { | ||
446 | char* any; | ||
447 | unsigned int length = read_mp4_tag_string(fd, size, | ||
448 | &buffer, &buffer_left, &any); | ||
449 | |||
450 | if (length > 0) | ||
451 | { | ||
452 | /* Re-use the read buffer as the dest buffer... */ | ||
453 | buffer -= length; | ||
454 | buffer_left += length; | ||
455 | |||
456 | if (parse_replaygain(tag_name, buffer, id3, | ||
457 | buffer, buffer_left) > 0) | ||
458 | { | ||
459 | /* Data used, keep it. */ | ||
460 | buffer += length; | ||
461 | buffer_left -= length; | ||
462 | } | ||
463 | } | ||
464 | } | ||
465 | } | ||
466 | break; | ||
467 | |||
468 | default: | ||
469 | lseek(fd, size, SEEK_CUR); | ||
470 | break; | ||
471 | } | ||
472 | } | ||
473 | while ((size_left > 0) && (errno == 0)); | ||
474 | |||
475 | return true; | ||
476 | } | ||
477 | |||
478 | static bool read_mp4_container(int fd, struct mp3entry* id3, | ||
479 | unsigned int size_left) | ||
480 | { | ||
481 | unsigned int size; | ||
482 | unsigned int type; | ||
483 | unsigned int handler = 0; | ||
484 | bool rc = true; | ||
485 | |||
486 | do | ||
487 | { | ||
488 | size_left = read_mp4_atom(fd, &size, &type, size_left); | ||
489 | |||
490 | /* DEBUGF("Atom: '%c%c%c%c' (0x%08x, %d bytes left)\n", | ||
491 | (type >> 24) & 0xff, (type >> 16) & 0xff, (type >> 8) & 0xff, | ||
492 | type & 0xff, type, size); */ | ||
493 | |||
494 | switch (type) | ||
495 | { | ||
496 | case MP4_ftyp: | ||
497 | { | ||
498 | unsigned int id; | ||
499 | |||
500 | read_uint32be(fd, &id); | ||
501 | size -= 4; | ||
502 | |||
503 | if ((id != MP4_M4A) && (id != MP4_M4B) && (id != MP4_mp42) | ||
504 | && (id != MP4_qt) && (id != MP4_3gp6)) | ||
505 | { | ||
506 | DEBUGF("Unknown MP4 file type: '%c%c%c%c'\n", | ||
507 | id >> 24 & 0xff, id >> 16 & 0xff, id >> 8 & 0xff, | ||
508 | id & 0xff); | ||
509 | return false; | ||
510 | } | ||
511 | } | ||
512 | break; | ||
513 | |||
514 | case MP4_meta: | ||
515 | lseek(fd, 4, SEEK_CUR); /* Skip version */ | ||
516 | size -= 4; | ||
517 | /* Fall through */ | ||
518 | |||
519 | case MP4_moov: | ||
520 | case MP4_udta: | ||
521 | case MP4_mdia: | ||
522 | case MP4_stbl: | ||
523 | case MP4_trak: | ||
524 | rc = read_mp4_container(fd, id3, size); | ||
525 | size = 0; | ||
526 | break; | ||
527 | |||
528 | case MP4_ilst: | ||
529 | if (handler == MP4_mdir) | ||
530 | { | ||
531 | rc = read_mp4_tags(fd, id3, size); | ||
532 | size = 0; | ||
533 | } | ||
534 | break; | ||
535 | |||
536 | case MP4_minf: | ||
537 | if (handler == MP4_soun) | ||
538 | { | ||
539 | rc = read_mp4_container(fd, id3, size); | ||
540 | size = 0; | ||
541 | } | ||
542 | break; | ||
543 | |||
544 | case MP4_stsd: | ||
545 | lseek(fd, 8, SEEK_CUR); | ||
546 | size -= 8; | ||
547 | rc = read_mp4_container(fd, id3, size); | ||
548 | size = 0; | ||
549 | break; | ||
550 | |||
551 | case MP4_hdlr: | ||
552 | lseek(fd, 8, SEEK_CUR); | ||
553 | read_uint32be(fd, &handler); | ||
554 | size -= 12; | ||
555 | /* DEBUGF(" Handler '%c%c%c%c'\n", handler >> 24 & 0xff, | ||
556 | handler >> 16 & 0xff, handler >> 8 & 0xff,handler & 0xff); */ | ||
557 | break; | ||
558 | |||
559 | case MP4_stts: | ||
560 | { | ||
561 | unsigned int entries; | ||
562 | unsigned int i; | ||
563 | |||
564 | lseek(fd, 4, SEEK_CUR); | ||
565 | read_uint32be(fd, &entries); | ||
566 | id3->samples = 0; | ||
567 | |||
568 | for (i = 0; i < entries; i++) | ||
569 | { | ||
570 | unsigned int n; | ||
571 | unsigned int l; | ||
572 | |||
573 | read_uint32be(fd, &n); | ||
574 | read_uint32be(fd, &l); | ||
575 | id3->samples += n * l; | ||
576 | } | ||
577 | |||
578 | size = 0; | ||
579 | } | ||
580 | break; | ||
581 | |||
582 | case MP4_mp4a: | ||
583 | case MP4_alac: | ||
584 | { | ||
585 | unsigned int frequency; | ||
586 | |||
587 | id3->codectype = (type == MP4_mp4a) ? AFMT_AAC : AFMT_ALAC; | ||
588 | lseek(fd, 22, SEEK_CUR); | ||
589 | read_uint32be(fd, &frequency); | ||
590 | size -= 26; | ||
591 | id3->frequency = frequency; | ||
592 | |||
593 | if (type == MP4_mp4a) | ||
594 | { | ||
595 | unsigned int subsize; | ||
596 | unsigned int subtype; | ||
597 | |||
598 | /* Get frequency from the decoder info tag, if possible. */ | ||
599 | lseek(fd, 2, SEEK_CUR); | ||
600 | /* The esds atom is a part of the mp4a atom, so ignore | ||
601 | * the returned size (it's already accounted for). | ||
602 | */ | ||
603 | read_mp4_atom(fd, &subsize, &subtype, size); | ||
604 | size -= 10; | ||
605 | |||
606 | if (subtype == MP4_esds) | ||
607 | { | ||
608 | read_mp4_esds(fd, id3, &size); | ||
609 | } | ||
610 | } | ||
611 | } | ||
612 | break; | ||
613 | |||
614 | case MP4_mdat: | ||
615 | id3->filesize = size; | ||
616 | break; | ||
617 | |||
618 | default: | ||
619 | break; | ||
620 | } | ||
621 | |||
622 | lseek(fd, size, SEEK_CUR); | ||
623 | } | ||
624 | while (rc && (size_left > 0) && (errno == 0) && (id3->filesize == 0)); | ||
625 | /* Break on non-zero filesize, since Rockbox currently doesn't support | ||
626 | * metadata after the mdat atom (which sets the filesize field). | ||
627 | */ | ||
628 | |||
629 | return rc; | ||
630 | } | ||
631 | |||
632 | bool get_mp4_metadata(int fd, struct mp3entry* id3) | ||
633 | { | ||
634 | id3->codectype = AFMT_UNKNOWN; | ||
635 | id3->filesize = 0; | ||
636 | errno = 0; | ||
637 | |||
638 | if (read_mp4_container(fd, id3, filesize(fd)) && (errno == 0) | ||
639 | && (id3->samples > 0) && (id3->frequency > 0) | ||
640 | && (id3->filesize > 0)) | ||
641 | { | ||
642 | if (id3->codectype == AFMT_UNKNOWN) | ||
643 | { | ||
644 | logf("Not an ALAC or AAC file"); | ||
645 | return false; | ||
646 | } | ||
647 | |||
648 | id3->length = ((int64_t) id3->samples * 1000) / id3->frequency; | ||
649 | |||
650 | if (id3->length <= 0) | ||
651 | { | ||
652 | logf("mp4 length invalid!"); | ||
653 | return false; | ||
654 | } | ||
655 | |||
656 | id3->bitrate = ((int64_t) id3->filesize * 8) / id3->length; | ||
657 | DEBUGF("MP4 bitrate %d, frequency %ld Hz, length %ld ms\n", | ||
658 | id3->bitrate, id3->frequency, id3->length); | ||
659 | } | ||
660 | else | ||
661 | { | ||
662 | logf("MP4 metadata error"); | ||
663 | DEBUGF("MP4 metadata error. errno %d, length %ld, frequency %ld, filesize %ld\n", | ||
664 | errno, id3->length, id3->frequency, id3->filesize); | ||
665 | return false; | ||
666 | } | ||
667 | |||
668 | return true; | ||
669 | } | ||