diff options
author | Magnus Holmgren <magnushol@gmail.com> | 2006-10-11 17:42:33 +0000 |
---|---|---|
committer | Magnus Holmgren <magnushol@gmail.com> | 2006-10-11 17:42:33 +0000 |
commit | 406069467d3b7bcbc3a0f923f19ad7aa3551a419 (patch) | |
tree | f6079ecbd94d85526245e9cac3478bf7b29f785b | |
parent | d63d8fedaa9f68c9375ddae6ddf80fb1d045006f (diff) | |
download | rockbox-406069467d3b7bcbc3a0f923f19ad7aa3551a419.tar.gz rockbox-406069467d3b7bcbc3a0f923f19ad7aa3551a419.zip |
More flexible MP4 file metadata parser.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@11189 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r-- | apps/metadata.c | 723 |
1 files changed, 427 insertions, 296 deletions
diff --git a/apps/metadata.c b/apps/metadata.c index 1b2dde3a24..ff0cb76cbe 100644 --- a/apps/metadata.c +++ b/apps/metadata.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <ctype.h> | 22 | #include <ctype.h> |
23 | #include <inttypes.h> | 23 | #include <inttypes.h> |
24 | 24 | ||
25 | #include "errno.h" | ||
25 | #include "metadata.h" | 26 | #include "metadata.h" |
26 | #include "mp3_playback.h" | 27 | #include "mp3_playback.h" |
27 | #include "logf.h" | 28 | #include "logf.h" |
@@ -40,6 +41,37 @@ enum tagtype { TAGTYPE_APE = 1, TAGTYPE_VORBIS }; | |||
40 | #define TAG_NAME_LENGTH 32 | 41 | #define TAG_NAME_LENGTH 32 |
41 | #define TAG_VALUE_LENGTH 128 | 42 | #define TAG_VALUE_LENGTH 128 |
42 | 43 | ||
44 | #define MP4_ID(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) | ||
45 | |||
46 | #define MP4_3gp6 MP4_ID('3', 'g', 'p', '6') | ||
47 | #define MP4_alac MP4_ID('a', 'l', 'a', 'c') | ||
48 | #define MP4_calb MP4_ID(0xa9, 'a', 'l', 'b') | ||
49 | #define MP4_cART MP4_ID(0xa9, 'A', 'R', 'T') | ||
50 | #define MP4_cnam MP4_ID(0xa9, 'n', 'a', 'm') | ||
51 | #define MP4_cwrt MP4_ID(0xa9, 'w', 'r', 't') | ||
52 | #define MP4_ftyp MP4_ID('f', 't', 'y', 'p') | ||
53 | #define MP4_gnre MP4_ID('g', 'n', 'r', 'e') | ||
54 | #define MP4_hdlr MP4_ID('h', 'd', 'l', 'r') | ||
55 | #define MP4_ilst MP4_ID('i', 'l', 's', 't') | ||
56 | #define MP4_M4A MP4_ID('M', '4', 'A', ' ') | ||
57 | #define MP4_mdat MP4_ID('m', 'd', 'a', 't') | ||
58 | #define MP4_mdia MP4_ID('m', 'd', 'i', 'a') | ||
59 | #define MP4_mdir MP4_ID('m', 'd', 'i', 'r') | ||
60 | #define MP4_meta MP4_ID('m', 'e', 't', 'a') | ||
61 | #define MP4_minf MP4_ID('m', 'i', 'n', 'f') | ||
62 | #define MP4_moov MP4_ID('m', 'o', 'o', 'v') | ||
63 | #define MP4_mp4a MP4_ID('m', 'p', '4', 'a') | ||
64 | #define MP4_mp42 MP4_ID('m', 'p', '4', '2') | ||
65 | #define MP4_qt MP4_ID('q', 't', ' ', ' ') | ||
66 | #define MP4_soun MP4_ID('s', 'o', 'u', 'n') | ||
67 | #define MP4_stbl MP4_ID('s', 't', 'b', 'l') | ||
68 | #define MP4_stsd MP4_ID('s', 't', 's', 'd') | ||
69 | #define MP4_stts MP4_ID('s', 't', 't', 's') | ||
70 | #define MP4_trak MP4_ID('t', 'r', 'a', 'k') | ||
71 | #define MP4_trkn MP4_ID('t', 'r', 'k', 'n') | ||
72 | #define MP4_udta MP4_ID('u', 'd', 't', 'a') | ||
73 | #define MP4_extra MP4_ID('-', '-', '-', '-') | ||
74 | |||
43 | struct apetag_header | 75 | struct apetag_header |
44 | { | 76 | { |
45 | char id[8]; | 77 | char id[8]; |
@@ -190,28 +222,31 @@ static void convert_endian(void *data, const char *format) | |||
190 | } | 222 | } |
191 | } | 223 | } |
192 | 224 | ||
193 | /* read_uint32be() - read an unsigned integer from a big-endian | 225 | /* Read an unsigned 16-bit integer from a big-endian file. */ |
194 | (e.g. Quicktime) file. This is used by the .m4a parser | ||
195 | */ | ||
196 | #ifdef ROCKBOX_BIG_ENDIAN | 226 | #ifdef ROCKBOX_BIG_ENDIAN |
197 | #define read_uint32be(fd,buf) read((fd),(buf),4) | 227 | #define read_uint16be(fd, buf) read((fd), (buf), 2) |
198 | #else | 228 | #else |
199 | int read_uint32be(int fd, unsigned int* buf) { | 229 | int read_uint16be(int fd, unsigned short* buf) |
200 | char tmp; | 230 | { |
201 | char* p=(char*)buf; | 231 | size_t n; |
202 | size_t n; | 232 | |
233 | n = read(fd, (char*) buf, 2); | ||
234 | *buf = betoh16(*buf); | ||
235 | return n; | ||
236 | } | ||
237 | #endif | ||
203 | 238 | ||
204 | n=read(fd,p,4); | 239 | /* Read an unsigned 32-bit integer from a big-endian file. */ |
205 | if (n==4) { | 240 | #ifdef ROCKBOX_BIG_ENDIAN |
206 | tmp=p[0]; | 241 | #define read_uint32be(fd,buf) read((fd), (buf), 4) |
207 | p[0]=p[3]; | 242 | #else |
208 | p[3]=tmp; | 243 | int read_uint32be(int fd, unsigned int* buf) |
209 | tmp=p[2]; | 244 | { |
210 | p[2]=p[1]; | 245 | size_t n; |
211 | p[1]=tmp; | ||
212 | } | ||
213 | 246 | ||
214 | return(n); | 247 | n = read(fd, (char*) buf, 4); |
248 | *buf = betoh32(*buf); | ||
249 | return n; | ||
215 | } | 250 | } |
216 | #endif | 251 | #endif |
217 | 252 | ||
@@ -921,292 +956,388 @@ static bool get_wave_metadata(int fd, struct mp3entry* id3) | |||
921 | return true; | 956 | return true; |
922 | } | 957 | } |
923 | 958 | ||
924 | static bool get_m4a_metadata(int fd, struct mp3entry* id3) | 959 | /* Read the tag data from an MP4 file, storing up to buffer_size bytes in |
960 | * buffer. | ||
961 | */ | ||
962 | unsigned long read_mp4_tag(int fd, unsigned int size_left, char* buffer, | ||
963 | unsigned int buffer_left) | ||
925 | { | 964 | { |
926 | unsigned char* buf; | 965 | unsigned int bytes_read = 0; |
927 | unsigned long totalsamples; | 966 | |
928 | int i,j,k; | 967 | if (buffer_left == 0) |
929 | size_t n; | 968 | { |
930 | size_t bytes_remaining; | 969 | lseek(fd, size_left, SEEK_CUR); /* Skip everything */ |
931 | char* id3buf; | 970 | } |
932 | unsigned int compressedsize; | 971 | else |
933 | unsigned int sample_count; | 972 | { |
934 | unsigned int sample_duration; | 973 | /* Skip the data tag header - maybe we should parse it properly? */ |
935 | int numentries; | 974 | lseek(fd, 16, SEEK_CUR); |
936 | int entry_size; | 975 | size_left -= 16; |
937 | int size_remaining; | 976 | |
938 | int chunk_len; | 977 | if (size_left > buffer_left) |
939 | unsigned char chunk_id[4]; | 978 | { |
940 | int sub_chunk_len; | 979 | read(fd, buffer, buffer_left); |
941 | unsigned char sub_chunk_id[4]; | 980 | lseek(fd, size_left - buffer_left, SEEK_CUR); |
942 | 981 | bytes_read = buffer_left; | |
943 | /* A simple parser to read vital metadata from an ALAC file. | 982 | } |
944 | This parser also works for AAC files - they are both stored in | 983 | else |
945 | a Quicktime M4A container. */ | 984 | { |
946 | 985 | read(fd, buffer, size_left); | |
947 | /* Use the trackname part of the id3 structure as a temporary buffer */ | 986 | bytes_read = size_left; |
948 | buf=id3->path; | 987 | } |
949 | 988 | } | |
950 | lseek(fd, 0, SEEK_SET); | 989 | |
951 | 990 | return bytes_read; | |
952 | totalsamples=0; | 991 | } |
953 | compressedsize=0; | 992 | |
954 | /* read the chunks - we stop when we find the mdat chunk and set compressedsize */ | 993 | /* Read a string tag from an MP4 file */ |
955 | while (compressedsize==0) { | 994 | unsigned int read_mp4_tag_string(int fd, int size_left, char** buffer, |
956 | n=read_uint32be(fd,&chunk_len); | 995 | unsigned int* buffer_left, char** dest) |
957 | 996 | { | |
958 | // This means it was a 64-bit file, so we have problems. | 997 | unsigned int bytes_read = read_mp4_tag(fd, size_left, *buffer, |
959 | if (chunk_len == 1) { | 998 | *buffer_left - 1); |
960 | logf("need 64bit support\n"); | 999 | unsigned int length = 0; |
961 | return false; | 1000 | |
962 | } | 1001 | if (bytes_read) |
963 | 1002 | { | |
964 | n=read(fd,&chunk_id,4); | 1003 | (*buffer)[bytes_read] = 0; |
965 | if (n < 4) | 1004 | *dest = *buffer; |
966 | return false; | 1005 | length = strlen(*buffer) + 1; |
967 | 1006 | *buffer_left -= length; | |
968 | if (memcmp(&chunk_id,"ftyp",4)==0) { | 1007 | *buffer += length; |
969 | /* Check for M4A type */ | 1008 | } |
970 | n=read(fd,&chunk_id,4); | 1009 | else |
971 | if ((memcmp(&chunk_id,"M4A ",4)!=0) && | 1010 | { |
972 | (memcmp(&chunk_id,"mp42",4)!=0)) { | 1011 | *dest = NULL; |
973 | logf("Not an M4A file, aborting\n"); | 1012 | } |
974 | return false; | 1013 | |
975 | } | 1014 | return length; |
976 | /* Skip rest of chunk */ | 1015 | } |
977 | lseek(fd, chunk_len - 8 - 4, SEEK_CUR); /* FIXME not 8 */ | 1016 | |
978 | } else if (memcmp(&chunk_id,"moov",4)==0) { | 1017 | static unsigned int read_mp4_atom(int fd, unsigned int* size, |
979 | size_remaining=chunk_len - 8; /* FIXME not 8 */ | 1018 | unsigned int* type, unsigned int size_left) |
980 | 1019 | { | |
981 | while (size_remaining > 0) { | 1020 | read_uint32be(fd, size); |
982 | n=read_uint32be(fd,&sub_chunk_len); | 1021 | read_uint32be(fd, type); |
983 | if ((sub_chunk_len < 1) || (sub_chunk_len > size_remaining)) { | 1022 | |
984 | logf("Strange sub_chunk_len value inside moov: %d (remaining: %d)\n",sub_chunk_len,size_remaining); | 1023 | if (*size == 1) |
985 | return false; | 1024 | { |
1025 | /* FAT32 doesn't support files this big, so something seems to | ||
1026 | * be wrong. (64-bit sizes should only be used when required.) | ||
1027 | */ | ||
1028 | errno = EFBIG; | ||
1029 | *type = 0; | ||
1030 | return 0; | ||
1031 | } | ||
1032 | |||
1033 | if (*size > 0) | ||
1034 | { | ||
1035 | if (*size > size_left) | ||
1036 | { | ||
1037 | size_left = 0; | ||
986 | } | 1038 | } |
987 | n=read(fd,&sub_chunk_id,4); | 1039 | else |
988 | size_remaining-=8; | 1040 | { |
989 | 1041 | size_left -= *size; | |
990 | if (memcmp(&sub_chunk_id,"mvhd",4)==0) { | 1042 | } |
991 | /* We don't need anything from here - skip */ | 1043 | |
992 | lseek(fd, sub_chunk_len - 8, SEEK_CUR); /* FIXME not 8 */ | 1044 | *size -= 8; |
993 | size_remaining-=(sub_chunk_len-8); | 1045 | } |
994 | } else if (memcmp(&sub_chunk_id,"udta",4)==0) { | 1046 | else |
995 | /* The udta chunk contains the metadata - track, artist, album etc. | 1047 | { |
996 | The format appears to be: | 1048 | *size = size_left; |
997 | udta | 1049 | size_left = 0; |
998 | meta | 1050 | } |
999 | hdlr | 1051 | |
1000 | ilst | 1052 | return size_left; |
1001 | .nam | 1053 | } |
1002 | [rest of tags] | 1054 | |
1003 | free | 1055 | static bool read_mp4_tags(int fd, struct mp3entry* id3, |
1004 | 1056 | unsigned int size_left) | |
1005 | NOTE: This code was written by examination of some .m4a files | 1057 | { |
1006 | produced by iTunes v4.9 - it may not therefore be 100% | 1058 | unsigned int size; |
1007 | compliant with all streams. But it should fail gracefully. | 1059 | unsigned int type; |
1008 | */ | 1060 | unsigned int buffer_left = sizeof(id3->id3v2buf) + sizeof(id3->id3v1buf); |
1009 | j=(sub_chunk_len-8); | 1061 | char* buffer = id3->id3v2buf; |
1010 | size_remaining-=j; | 1062 | bool cwrt = false; |
1011 | n=read_uint32be(fd,&sub_chunk_len); | 1063 | |
1012 | n=read(fd,&sub_chunk_id,4); | 1064 | do |
1013 | j-=8; | 1065 | { |
1014 | if (memcmp(&sub_chunk_id,"meta",4)==0) { | 1066 | size_left = read_mp4_atom(fd, &size, &type, size_left); |
1015 | lseek(fd, 4, SEEK_CUR); | 1067 | |
1016 | j-=4; | 1068 | /* DEBUGF("Tag atom: '%c%c%c%c' (%d bytes left)\n", type >> 24 & 0xff, |
1017 | n=read_uint32be(fd,&sub_chunk_len); | 1069 | type >> 16 & 0xff, type >> 8 & 0xff, type & 0xff, size); */ |
1018 | n=read(fd,&sub_chunk_id,4); | 1070 | |
1019 | j-=8; | 1071 | switch (type) |
1020 | if (memcmp(&sub_chunk_id,"hdlr",4)==0) { | 1072 | { |
1021 | lseek(fd, sub_chunk_len - 8, SEEK_CUR); | 1073 | case MP4_cnam: |
1022 | j-=(sub_chunk_len - 8); | 1074 | read_mp4_tag_string(fd, size, &buffer, &buffer_left, |
1023 | n=read_uint32be(fd,&sub_chunk_len); | 1075 | &id3->title); |
1024 | n=read(fd,&sub_chunk_id,4); | 1076 | break; |
1025 | j-=8; | 1077 | |
1026 | if (memcmp(&sub_chunk_id,"ilst",4)==0) { | 1078 | case MP4_cART: |
1027 | /* Here are the actual tags. We use the id3v2 300-byte buffer | 1079 | read_mp4_tag_string(fd, size, &buffer, &buffer_left, |
1028 | to store the string data */ | 1080 | &id3->artist); |
1029 | bytes_remaining=sizeof(id3->id3v2buf); | 1081 | break; |
1030 | id3->genre=255; /* Not every track is the Blues */ | 1082 | |
1031 | id3buf=id3->id3v2buf; | 1083 | case MP4_calb: |
1032 | k=sub_chunk_len-8; | 1084 | read_mp4_tag_string(fd, size, &buffer, &buffer_left, |
1033 | j-=k; | 1085 | &id3->album); |
1034 | while (k > 0) { | 1086 | break; |
1035 | n=read_uint32be(fd,&sub_chunk_len); | 1087 | |
1036 | n=read(fd,&sub_chunk_id,4); | 1088 | case MP4_cwrt: |
1037 | k-=8; | 1089 | read_mp4_tag_string(fd, size, &buffer, &buffer_left, |
1038 | if (memcmp(sub_chunk_id,"\251nam",4)==0) { | 1090 | &id3->composer); |
1039 | read_m4a_tag_string(fd,sub_chunk_len-8,&id3buf,&bytes_remaining,&id3->title); | 1091 | cwrt = false; |
1040 | } else if (memcmp(sub_chunk_id,"\251ART",4)==0) { | 1092 | break; |
1041 | read_m4a_tag_string(fd,sub_chunk_len-8,&id3buf,&bytes_remaining,&id3->artist); | 1093 | |
1042 | } else if (memcmp(sub_chunk_id,"\251alb",4)==0) { | 1094 | case MP4_gnre: |
1043 | read_m4a_tag_string(fd,sub_chunk_len-8,&id3buf,&bytes_remaining,&id3->album); | 1095 | { |
1044 | } else if (memcmp(sub_chunk_id,"\251gen",4)==0) { | 1096 | unsigned short genre; |
1045 | read_m4a_tag_string(fd,sub_chunk_len-8,&id3buf,&bytes_remaining,&id3->genre_string); | 1097 | |
1046 | } else if (memcmp(sub_chunk_id,"\251day",4)==0) { | 1098 | read_mp4_tag(fd, size, (char*) &genre, sizeof(genre)); |
1047 | read_m4a_tag_string(fd,sub_chunk_len-8,&id3buf,&bytes_remaining,&id3->year_string); | 1099 | id3->genre = betoh16(genre); |
1048 | } else if (memcmp(sub_chunk_id,"trkn",4)==0) { | 1100 | } |
1049 | if (sub_chunk_len==0x20) { | 1101 | break; |
1050 | read(fd,buf,sub_chunk_len-8); | 1102 | |
1051 | id3->tracknum=buf[19]; | 1103 | case MP4_trkn: |
1052 | } else { | 1104 | { |
1053 | lseek(fd, sub_chunk_len-8,SEEK_CUR); | 1105 | unsigned short n[2]; |
1106 | |||
1107 | read_mp4_tag(fd, size, (char*) &n, sizeof(n)); | ||
1108 | id3->tracknum = betoh16(n[1]); | ||
1109 | } | ||
1110 | break; | ||
1111 | |||
1112 | case MP4_extra: | ||
1113 | { | ||
1114 | char tag_name[TAG_NAME_LENGTH]; | ||
1115 | unsigned int sub_size; | ||
1116 | |||
1117 | /* "mean" atom */ | ||
1118 | read_uint32be(fd, &sub_size); | ||
1119 | size -= sub_size; | ||
1120 | lseek(fd, sub_size - 4, SEEK_CUR); | ||
1121 | /* "name" atom */ | ||
1122 | read_uint32be(fd, &sub_size); | ||
1123 | size -= sub_size; | ||
1124 | lseek(fd, 8, SEEK_CUR); | ||
1125 | sub_size -= 12; | ||
1126 | |||
1127 | if (sub_size > sizeof(tag_name) - 1) | ||
1128 | { | ||
1129 | read(fd, tag_name, sizeof(tag_name) - 1); | ||
1130 | lseek(fd, sub_size - sizeof(tag_name) - 1, SEEK_CUR); | ||
1131 | tag_name[sizeof(tag_name) - 1] = 0; | ||
1132 | } | ||
1133 | else | ||
1134 | { | ||
1135 | read(fd, tag_name, sub_size); | ||
1136 | tag_name[sub_size] = 0; | ||
1137 | } | ||
1138 | |||
1139 | if ((strcasecmp(tag_name, "composer") == 0) && !cwrt) | ||
1140 | { | ||
1141 | read_mp4_tag_string(fd, size, &buffer, &buffer_left, | ||
1142 | &id3->composer); | ||
1143 | } | ||
1144 | else | ||
1145 | { | ||
1146 | char* any; | ||
1147 | unsigned int length = read_mp4_tag_string(fd, size, | ||
1148 | &buffer, &buffer_left, &any); | ||
1149 | |||
1150 | if (length > 0) | ||
1151 | { | ||
1152 | /* Re-use the read buffer as the dest buffer... */ | ||
1153 | buffer -= length; | ||
1154 | buffer_left += length; | ||
1155 | |||
1156 | parse_replaygain(tag_name, buffer, id3, buffer, | ||
1157 | buffer_left); | ||
1054 | } | 1158 | } |
1055 | } else { | ||
1056 | lseek(fd, sub_chunk_len-8,SEEK_CUR); | ||
1057 | } | ||
1058 | k-=(sub_chunk_len-8); | ||
1059 | } | 1159 | } |
1060 | } | ||
1061 | } | 1160 | } |
1062 | } | 1161 | break; |
1063 | /* Skip any remaining data in udta chunk */ | 1162 | |
1064 | lseek(fd, j, SEEK_CUR); | 1163 | default: |
1065 | } else if (memcmp(&sub_chunk_id,"trak",4)==0) { | 1164 | lseek(fd, size, SEEK_CUR); |
1066 | /* Format of trak chunk: | 1165 | break; |
1067 | tkhd | ||
1068 | mdia | ||
1069 | mdhd | ||
1070 | hdlr | ||
1071 | minf | ||
1072 | smhd | ||
1073 | dinf | ||
1074 | stbl | ||
1075 | stsd - Samplerate, Samplesize, Numchannels | ||
1076 | stts - time_to_sample array - RLE'd table containing duration of each block | ||
1077 | stsz - sample_byte_size array - ?Size in bytes of each compressed block | ||
1078 | stsc - Seek table related? | ||
1079 | stco - Seek table related? | ||
1080 | */ | ||
1081 | |||
1082 | /* Skip tkhd - not needed */ | ||
1083 | n=read_uint32be(fd,&sub_chunk_len); | ||
1084 | n=read(fd,&sub_chunk_id,4); | ||
1085 | if (memcmp(&sub_chunk_id,"tkhd",4)!=0) { | ||
1086 | logf("Expecting tkhd\n"); | ||
1087 | return false; | ||
1088 | } | ||
1089 | lseek(fd, sub_chunk_len - 8, SEEK_CUR); /* FIXME not 8 */ | ||
1090 | size_remaining-=sub_chunk_len; | ||
1091 | |||
1092 | /* Process mdia - skipping possible edts */ | ||
1093 | n=read_uint32be(fd,&sub_chunk_len); | ||
1094 | n=read(fd,&sub_chunk_id,4); | ||
1095 | if (memcmp(&sub_chunk_id,"edts",4)==0) { | ||
1096 | lseek(fd, sub_chunk_len - 8, SEEK_CUR); /* FIXME not 8 */ | ||
1097 | size_remaining-=sub_chunk_len; | ||
1098 | n=read_uint32be(fd,&sub_chunk_len); | ||
1099 | n=read(fd,&sub_chunk_id,4); | ||
1100 | } | ||
1101 | |||
1102 | if (memcmp(&sub_chunk_id,"mdia",4)!=0) { | ||
1103 | logf("Expecting mdia\n"); | ||
1104 | return false; | ||
1105 | } | ||
1106 | size_remaining-=sub_chunk_len; | ||
1107 | j=sub_chunk_len-8; | ||
1108 | |||
1109 | while (j > 0) { | ||
1110 | n=read_uint32be(fd,&sub_chunk_len); | ||
1111 | n=read(fd,&sub_chunk_id,4); | ||
1112 | j-=4; | ||
1113 | if (memcmp(&sub_chunk_id,"minf",4)==0) { | ||
1114 | j=sub_chunk_len-8; | ||
1115 | } else if (memcmp(&sub_chunk_id,"stbl",4)==0) { | ||
1116 | j=sub_chunk_len-8; | ||
1117 | } else if (memcmp(&sub_chunk_id,"stsd",4)==0) { | ||
1118 | n=read(fd,buf,sub_chunk_len-8); | ||
1119 | j-=sub_chunk_len; | ||
1120 | i=0; | ||
1121 | /* Skip version and flags */ | ||
1122 | i+=4; | ||
1123 | |||
1124 | numentries=(buf[i]<<24)|(buf[i+1]<<16)|(buf[i+2]<<8)|buf[i+3]; | ||
1125 | i+=4; | ||
1126 | if (numentries!=1) { | ||
1127 | logf("ERROR: Expecting only one entry in stsd\n"); | ||
1128 | } | ||
1129 | |||
1130 | entry_size=(buf[i]<<24)|(buf[i+1]<<16)|(buf[i+2]<<8)|buf[i+3]; | ||
1131 | i+=4; | ||
1132 | |||
1133 | if (memcmp(&buf[i],"alac",4)==0) { | ||
1134 | id3->codectype=AFMT_ALAC; | ||
1135 | } else if (memcmp(&buf[i],"mp4a",4)==0) { | ||
1136 | id3->codectype=AFMT_AAC; | ||
1137 | } else { | ||
1138 | logf("Not an ALAC or AAC file\n"); | ||
1139 | return false; | ||
1140 | } | ||
1141 | |||
1142 | //numchannels=(buf[i+20]<<8)|buf[i+21]; /* Not used - assume Stereo */ | ||
1143 | //samplesize=(buf[i+22]<<8)|buf[i+23]; /* Not used - assume 16-bit */ | ||
1144 | |||
1145 | /* Samplerate is 32-bit fixed point, but this works for < 65536 Hz */ | ||
1146 | id3->frequency=(buf[i+28]<<8)|buf[i+29]; | ||
1147 | } else if (memcmp(&sub_chunk_id,"stts",4)==0) { | ||
1148 | j-=sub_chunk_len; | ||
1149 | i=8; | ||
1150 | n=read(fd,buf,8); | ||
1151 | i+=8; | ||
1152 | numentries=(buf[4]<<24)|(buf[5]<<16)|(buf[6]<<8)|buf[7]; | ||
1153 | for (k=0;k<numentries;k++) { | ||
1154 | n=read_uint32be(fd,&sample_count); | ||
1155 | n=read_uint32be(fd,&sample_duration); | ||
1156 | totalsamples+=sample_count*sample_duration; | ||
1157 | i+=8; | ||
1158 | } | ||
1159 | if (i > 0) lseek(fd, sub_chunk_len - i, SEEK_CUR); | ||
1160 | } else if (memcmp(&sub_chunk_id,"stsz",4)==0) { | ||
1161 | j-=sub_chunk_len; | ||
1162 | i=8; | ||
1163 | n=read(fd,buf,8); | ||
1164 | i+=8; | ||
1165 | numentries=(buf[4]<<24)|(buf[5]<<16)|(buf[6]<<8)|buf[7]; | ||
1166 | for (k=0;k<numentries;k++) { | ||
1167 | n=read_uint32be(fd,&sample_count); | ||
1168 | n=read_uint32be(fd,&sample_duration); | ||
1169 | totalsamples+=sample_count*sample_duration; | ||
1170 | i+=8; | ||
1171 | } | ||
1172 | if (i > 0) lseek(fd, sub_chunk_len - i, SEEK_CUR); | ||
1173 | } else { | ||
1174 | lseek(fd, sub_chunk_len - 8, SEEK_CUR); /* FIXME not 8 */ | ||
1175 | j-=sub_chunk_len; | ||
1176 | } | ||
1177 | } | ||
1178 | } else { | ||
1179 | logf("Unexpected sub_chunk_id inside moov: %c%c%c%c\n", | ||
1180 | sub_chunk_id[0],sub_chunk_id[1],sub_chunk_id[2],sub_chunk_id[3]); | ||
1181 | return false; | ||
1182 | } | 1166 | } |
1183 | } | ||
1184 | } else if (memcmp(&chunk_id,"mdat",4)==0) { | ||
1185 | /* once we hit mdat we stop reading and return. | ||
1186 | * this is on the assumption that there is no furhter interesting | ||
1187 | * stuff in the stream. if there is stuff will fail (:()). | ||
1188 | * But we need the read pointer to be at the mdat stuff | ||
1189 | * for the decoder. And we don't want to rely on fseek/ftell, | ||
1190 | * as they may not always be avilable */ | ||
1191 | lseek(fd, chunk_len - 8, SEEK_CUR); /* FIXME not 8 */ | ||
1192 | compressedsize=chunk_len-8; | ||
1193 | } else if (memcmp(&chunk_id,"free",4)==0) { | ||
1194 | /* these following atoms can be skipped !!!! */ | ||
1195 | lseek(fd, chunk_len - 8, SEEK_CUR); /* FIXME not 8 */ | ||
1196 | } else { | ||
1197 | logf("(top) unknown chunk id: %c%c%c%c\n", chunk_id[0],chunk_id[1],chunk_id[2],chunk_id[3]); | ||
1198 | return false; | ||
1199 | } | 1167 | } |
1200 | } | 1168 | while ((size_left > 0) && (errno == 0)); |
1201 | 1169 | ||
1202 | id3->vbr=true; /* All ALAC files are VBR */ | 1170 | return true; |
1203 | id3->filesize=filesize(fd); | 1171 | } |
1204 | id3->samples=totalsamples; | ||
1205 | id3->length=(10*totalsamples)/(id3->frequency/100); | ||
1206 | id3->bitrate=(compressedsize*8)/id3->length;; | ||
1207 | 1172 | ||
1208 | return true; | 1173 | static bool read_mp4_container(int fd, struct mp3entry* id3, |
1209 | } | 1174 | unsigned int size_left) |
1175 | { | ||
1176 | unsigned int size; | ||
1177 | unsigned int type; | ||
1178 | unsigned int handler = 0; | ||
1179 | bool rc = true; | ||
1180 | |||
1181 | do | ||
1182 | { | ||
1183 | size_left = read_mp4_atom(fd, &size, &type, size_left); | ||
1184 | |||
1185 | /* DEBUGF("Atom: '%c%c%c%c' (0x%08x, %d bytes left)\n", | ||
1186 | (type >> 24) & 0xff, (type >> 16) & 0xff, (type >> 8) & 0xff, | ||
1187 | type & 0xff, type, size); */ | ||
1188 | |||
1189 | switch (type) | ||
1190 | { | ||
1191 | case MP4_ftyp: | ||
1192 | { | ||
1193 | unsigned int id; | ||
1194 | |||
1195 | read_uint32be(fd, &id); | ||
1196 | size -= 4; | ||
1197 | |||
1198 | if ((id != MP4_M4A) && (id != MP4_mp42) && (id != MP4_qt) | ||
1199 | && (id != MP4_3gp6)) | ||
1200 | { | ||
1201 | DEBUGF("Unknown MP4 file type: '%c%c%c%c'\n", | ||
1202 | id >> 24 & 0xff, id >> 16 & 0xff, id >> 8 & 0xff, | ||
1203 | id & 0xff); | ||
1204 | return false; | ||
1205 | } | ||
1206 | } | ||
1207 | break; | ||
1208 | |||
1209 | case MP4_meta: | ||
1210 | lseek(fd, 4, SEEK_CUR); /* Skip version */ | ||
1211 | size -= 4; | ||
1212 | /* Fall through */ | ||
1213 | |||
1214 | case MP4_moov: | ||
1215 | case MP4_udta: | ||
1216 | case MP4_mdia: | ||
1217 | case MP4_stbl: | ||
1218 | case MP4_trak: | ||
1219 | rc = read_mp4_container(fd, id3, size); | ||
1220 | size = 0; | ||
1221 | break; | ||
1222 | |||
1223 | case MP4_ilst: | ||
1224 | if (handler == MP4_mdir) | ||
1225 | { | ||
1226 | rc = read_mp4_tags(fd, id3, size); | ||
1227 | size = 0; | ||
1228 | } | ||
1229 | break; | ||
1230 | |||
1231 | case MP4_minf: | ||
1232 | if (handler == MP4_soun) | ||
1233 | { | ||
1234 | rc = read_mp4_container(fd, id3, size); | ||
1235 | size = 0; | ||
1236 | } | ||
1237 | break; | ||
1238 | |||
1239 | case MP4_stsd: | ||
1240 | lseek(fd, 8, SEEK_CUR); | ||
1241 | size -= 8; | ||
1242 | rc = read_mp4_container(fd, id3, size); | ||
1243 | size = 0; | ||
1244 | break; | ||
1245 | |||
1246 | case MP4_hdlr: | ||
1247 | lseek(fd, 8, SEEK_CUR); | ||
1248 | read_uint32be(fd, &handler); | ||
1249 | size -= 12; | ||
1250 | /* DEBUGF(" Handler '%c%c%c%c'\n", handler >> 24 & 0xff, | ||
1251 | handler >> 16 & 0xff, handler >> 8 & 0xff,handler & 0xff); */ | ||
1252 | break; | ||
1253 | |||
1254 | case MP4_stts: | ||
1255 | { | ||
1256 | unsigned int entries; | ||
1257 | unsigned int i; | ||
1258 | |||
1259 | lseek(fd, 4, SEEK_CUR); | ||
1260 | read_uint32be(fd, &entries); | ||
1261 | id3->samples = 0; | ||
1262 | |||
1263 | for (i = 0; i < entries; i++) | ||
1264 | { | ||
1265 | unsigned int n; | ||
1266 | unsigned int l; | ||
1267 | |||
1268 | read_uint32be(fd, &n); | ||
1269 | read_uint32be(fd, &l); | ||
1270 | id3->samples += n * l; | ||
1271 | } | ||
1272 | |||
1273 | size = 0; | ||
1274 | } | ||
1275 | break; | ||
1276 | |||
1277 | case MP4_mp4a: | ||
1278 | case MP4_alac: | ||
1279 | { | ||
1280 | unsigned int frequency; | ||
1281 | |||
1282 | id3->codectype = (type == MP4_mp4a) ? AFMT_AAC : AFMT_ALAC; | ||
1283 | lseek(fd, 22, SEEK_CUR); | ||
1284 | read_uint32be(fd, &frequency); | ||
1285 | size -= 26; | ||
1286 | id3->frequency = frequency; | ||
1287 | /* There're some child atoms here, but we don't need them */ | ||
1288 | } | ||
1289 | break; | ||
1290 | |||
1291 | case MP4_mdat: | ||
1292 | id3->filesize = size; | ||
1293 | break; | ||
1294 | |||
1295 | default: | ||
1296 | break; | ||
1297 | } | ||
1298 | |||
1299 | lseek(fd, size, SEEK_CUR); | ||
1300 | } | ||
1301 | while (rc && (size_left > 0) && (errno == 0) && (id3->filesize == 0)); | ||
1302 | /* Break on non-zero filesize, since Rockbox currently doesn't support | ||
1303 | * metadata after the mdat atom (which sets the filesize field). | ||
1304 | */ | ||
1305 | |||
1306 | return rc; | ||
1307 | } | ||
1308 | |||
1309 | static bool get_mp4_metadata(int fd, struct mp3entry* id3) | ||
1310 | { | ||
1311 | id3->codectype = AFMT_UNKNOWN; | ||
1312 | id3->genre = 255; | ||
1313 | id3->filesize = 0; | ||
1314 | errno = 0; | ||
1315 | |||
1316 | if (read_mp4_container(fd, id3, filesize(fd)) && (errno == 0) | ||
1317 | && (id3->samples > 0) && (id3->frequency > 0) | ||
1318 | && (id3->filesize > 0)) | ||
1319 | { | ||
1320 | if (id3->codectype == AFMT_UNKNOWN) | ||
1321 | { | ||
1322 | logf("Not an ALAC or AAC file"); | ||
1323 | return false; | ||
1324 | } | ||
1325 | |||
1326 | id3->length = (id3->samples / id3->frequency) * 1000; | ||
1327 | id3->bitrate = ((int64_t) id3->filesize * 8) / id3->length; | ||
1328 | DEBUGF("MP4 bitrate %d, frequency %d Hz, length %d ms\n", | ||
1329 | id3->bitrate, id3->frequency, id3->length); | ||
1330 | } | ||
1331 | else | ||
1332 | { | ||
1333 | logf("MP4 metadata error"); | ||
1334 | DEBUGF("MP4 metadata error. errno %d, length %d, frequency %d, filesize %d\n", | ||
1335 | errno, id3->length, id3->frequency, id3->filesize); | ||
1336 | return false; | ||
1337 | } | ||
1338 | |||
1339 | return true; | ||
1340 | } | ||
1210 | 1341 | ||
1211 | static bool get_musepack_metadata(int fd, struct mp3entry *id3) | 1342 | static bool get_musepack_metadata(int fd, struct mp3entry *id3) |
1212 | { | 1343 | { |
@@ -1718,7 +1849,7 @@ bool get_metadata(struct track_info* track, int fd, const char* trackname, | |||
1718 | 1849 | ||
1719 | case AFMT_ALAC: | 1850 | case AFMT_ALAC: |
1720 | case AFMT_AAC: | 1851 | case AFMT_AAC: |
1721 | if (!get_m4a_metadata(fd, &(track->id3))) | 1852 | if (!get_mp4_metadata(fd, &(track->id3))) |
1722 | { | 1853 | { |
1723 | return false; | 1854 | return false; |
1724 | } | 1855 | } |