minidlna: update to 1.1.5
[tomato.git] / release / src / router / minidlna / tagutils / tagutils-aac.c
blobfcdb5731b0903a99de769f1dc3cfe8dbc62fcec7
1 //=========================================================================
2 // FILENAME : tagutils-aac.c
3 // DESCRIPTION : AAC metadata reader
4 //=========================================================================
5 // Copyright (c) 2008- NETGEAR, Inc. All Rights Reserved.
6 //=========================================================================
8 /*
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
24 * This file is derived from mt-daap project.
28 // _aac_findatom:
29 static long
30 _aac_findatom(FILE *fin, long max_offset, char *which_atom, int *atom_size)
32 long current_offset = 0;
33 int size;
34 char atom[4];
36 while(current_offset < max_offset)
38 if(fread((void*)&size, 1, sizeof(int), fin) != sizeof(int))
39 return -1;
41 size = ntohl(size);
43 if(size <= 7)
44 return -1;
46 if(fread(atom, 1, 4, fin) != 4)
47 return -1;
49 if(strncasecmp(atom, which_atom, 4) == 0)
51 *atom_size = size;
52 return current_offset;
55 fseek(fin, size - 8, SEEK_CUR);
56 current_offset += size;
59 return -1;
62 // _get_aactags
63 static int
64 _get_aactags(char *file, struct song_metadata *psong)
66 FILE *fin;
67 long atom_offset;
68 unsigned int atom_length;
70 long current_offset = 0;
71 int current_size;
72 char current_atom[4];
73 char *current_data = NULL;
74 int genre;
75 int len;
77 if(!(fin = fopen(file, "rb")))
79 DPRINTF(E_ERROR, L_SCANNER, "Cannot open file %s for reading\n", file);
80 return -1;
83 fseek(fin, 0, SEEK_SET);
85 atom_offset = _aac_lookforatom(fin, "moov:udta:meta:ilst", &atom_length);
86 if(atom_offset != -1)
88 while(current_offset < atom_length)
90 if(fread((void*)&current_size, 1, sizeof(int), fin) != sizeof(int))
91 break;
93 current_size = ntohl(current_size);
95 if(current_size <= 7 || current_size > 1<<24) // something not right
96 break;
98 if(fread(current_atom, 1, 4, fin) != 4)
99 break;
101 len = current_size - 7; // too short
102 if(len < 22)
103 len = 22;
105 current_data = (char*)malloc(len); // extra byte
107 if(fread(current_data, 1, current_size - 8, fin) != current_size - 8)
108 break;
110 current_data[current_size - 8] = '\0';
111 if(!memcmp(current_atom, "\xA9" "nam", 4))
112 psong->title = strdup((char*)&current_data[16]);
113 else if(!memcmp(current_atom, "\xA9" "ART", 4) ||
114 !memcmp(current_atom, "\xA9" "art", 4))
115 psong->contributor[ROLE_ARTIST] = strdup((char*)&current_data[16]);
116 else if(!memcmp(current_atom, "\xA9" "alb", 4))
117 psong->album = strdup((char*)&current_data[16]);
118 else if(!memcmp(current_atom, "\xA9" "cmt", 4))
119 psong->comment = strdup((char*)&current_data[16]);
120 else if(!memcmp(current_atom, "aART", 4) ||
121 !memcmp(current_atom, "aart", 4))
122 psong->contributor[ROLE_ALBUMARTIST] = strdup((char*)&current_data[16]);
123 else if(!memcmp(current_atom, "\xA9" "dir", 4))
124 psong->contributor[ROLE_CONDUCTOR] = strdup((char*)&current_data[16]);
125 else if(!memcmp(current_atom, "\xA9" "wrt", 4))
126 psong->contributor[ROLE_COMPOSER] = strdup((char*)&current_data[16]);
127 else if(!memcmp(current_atom, "\xA9" "grp", 4))
128 psong->grouping = strdup((char*)&current_data[16]);
129 else if(!memcmp(current_atom, "\xA9" "gen", 4))
130 psong->genre = strdup((char*)&current_data[16]);
131 else if(!memcmp(current_atom, "\xA9" "day", 4))
132 psong->year = atoi((char*)&current_data[16]);
133 else if(!memcmp(current_atom, "tmpo", 4))
134 psong->bpm = (current_data[16] << 8) | current_data[17];
135 else if(!memcmp(current_atom, "trkn", 4))
137 psong->track = (current_data[18] << 8) | current_data[19];
138 psong->total_tracks = (current_data[20] << 8) | current_data[21];
140 else if(!memcmp(current_atom, "disk", 4))
142 psong->disc = (current_data[18] << 8) | current_data[19];
143 psong->total_discs = (current_data[20] << 8) | current_data[21];
145 else if(!memcmp(current_atom, "gnre", 4))
147 genre = current_data[17] - 1;
148 if((genre < 0) || (genre > WINAMP_GENRE_UNKNOWN))
149 genre = WINAMP_GENRE_UNKNOWN;
150 psong->genre = strdup(winamp_genre[genre]);
152 else if(!memcmp(current_atom, "cpil", 4))
154 psong->compilation = current_data[16];
156 else if(!memcmp(current_atom, "covr", 4))
158 psong->image_size = current_size - 8 - 16;
159 if((psong->image = malloc(psong->image_size)))
160 memcpy(psong->image, current_data+16, psong->image_size);
161 else
162 DPRINTF(E_ERROR, L_SCANNER, "Out of memory [%s]\n", file);
165 free(current_data);
166 current_data = NULL;
167 current_offset += current_size;
170 fclose(fin);
171 free(current_data);
173 if(atom_offset == -1)
174 return -1;
176 return 0;
179 // aac_lookforatom
180 static off_t
181 _aac_lookforatom(FILE *aac_fp, char *atom_path, unsigned int *atom_length)
183 long atom_offset;
184 off_t file_size;
185 char *cur_p, *end_p;
186 char atom_name[5];
188 fseek(aac_fp, 0, SEEK_END);
189 file_size = ftell(aac_fp);
190 rewind(aac_fp);
192 end_p = atom_path;
193 while(*end_p != '\0')
195 end_p++;
197 atom_name[4] = '\0';
198 cur_p = atom_path;
200 while(cur_p)
202 if((end_p - cur_p) < 4)
204 return -1;
206 strncpy(atom_name, cur_p, 4);
207 atom_offset = _aac_findatom(aac_fp, file_size, atom_name, (int*)atom_length);
208 if(atom_offset == -1)
210 return -1;
212 cur_p = strchr(cur_p, ':');
213 if(cur_p != NULL)
215 cur_p++;
217 if(!strcmp(atom_name, "meta"))
219 fseek(aac_fp, 4, SEEK_CUR);
221 else if(!strcmp(atom_name, "stsd"))
223 fseek(aac_fp, 8, SEEK_CUR);
225 else if(!strcmp(atom_name, "mp4a"))
227 fseek(aac_fp, 28, SEEK_CUR);
232 // return position of 'size:atom'
233 return ftell(aac_fp) - 8;
237 _aac_check_extended_descriptor(FILE *infile)
239 short int i;
240 unsigned char buf[3];
242 if( fread((void *)&buf, 1, 3, infile) < 3 )
243 return -1;
244 for( i=0; i<3; i++ )
246 if( (buf[i] != 0x80) &&
247 (buf[i] != 0x81) &&
248 (buf[i] != 0xFE) )
250 fseek(infile, -3, SEEK_CUR);
251 return 0;
255 return 0;
258 // _get_aacfileinfo
260 _get_aacfileinfo(char *file, struct song_metadata *psong)
262 FILE *infile;
263 long atom_offset;
264 int atom_length;
265 int sample_size;
266 int samples;
267 unsigned int bitrate;
268 off_t file_size;
269 int ms;
270 unsigned char buffer[2];
271 aac_object_type_t profile_id = 0;
273 psong->vbr_scale = -1;
274 psong->channels = 2; // A "normal" default in case we can't find this information
276 infile = fopen(file, "rb");
277 if(!infile)
279 DPRINTF(E_ERROR, L_SCANNER, "Could not open %s for reading\n", file);
280 return -1;
283 fseek(infile, 0, SEEK_END);
284 file_size = ftell(infile);
285 fseek(infile, 0, SEEK_SET);
287 // move to 'mvhd' atom
288 atom_offset = _aac_lookforatom(infile, "moov:mvhd", (unsigned int*)&atom_length);
289 if(atom_offset != -1)
291 fseek(infile, 12, SEEK_CUR);
292 if(fread((void*)&sample_size, 1, sizeof(int), infile) != sizeof(int) ||
293 fread((void*)&samples, 1, sizeof(int), infile) != sizeof(int))
295 fclose(infile);
296 return -1;
299 sample_size = ntohl(sample_size);
300 samples = ntohl(samples);
302 // avoid overflowing on large sample_sizes (90000)
303 ms = 1000;
304 while((ms > 9) && (!(sample_size % 10)))
306 sample_size /= 10;
307 ms /= 10;
310 // unit = ms
311 psong->song_length = (int)((samples * ms) / sample_size);
314 psong->bitrate = 0;
316 // see if it is aac or alac
317 atom_offset = _aac_lookforatom(infile, "moov:trak:mdia:minf:stbl:stsd:alac", (unsigned int*)&atom_length);
318 if(atom_offset != -1) {
319 fseek(infile, atom_offset + 32, SEEK_SET);
320 if (fread(buffer, sizeof(unsigned char), 2, infile) == 2)
321 psong->samplerate = (buffer[0] << 8) | (buffer[1]);
322 goto bad_esds;
325 // get samplerate from 'mp4a' (not from 'mdhd')
326 atom_offset = _aac_lookforatom(infile, "moov:trak:mdia:minf:stbl:stsd:mp4a", (unsigned int*)&atom_length);
327 if(atom_offset != -1)
329 fseek(infile, atom_offset + 32, SEEK_SET);
330 if(fread(buffer, sizeof(unsigned char), 2, infile) == 2)
331 psong->samplerate = (buffer[0] << 8) | (buffer[1]);
333 fseek(infile, 2, SEEK_CUR);
335 // get bitrate from 'esds'
336 atom_offset = _aac_findatom(infile, atom_length - (ftell(infile) - atom_offset), "esds", &atom_length);
338 if(atom_offset != -1)
340 // skip the version number
341 fseek(infile, atom_offset + 4, SEEK_CUR);
342 // should be 0x03, to signify the descriptor type (section)
343 if( !fread((void *)&buffer, 1, 1, infile) || (buffer[0] != 0x03) || (_aac_check_extended_descriptor(infile) != 0) )
344 goto bad_esds;
345 fseek(infile, 4, SEEK_CUR);
346 if( !fread((void *)&buffer, 1, 1, infile) || (buffer[0] != 0x04) || (_aac_check_extended_descriptor(infile) != 0) )
347 goto bad_esds;
348 fseek(infile, 10, SEEK_CUR); // 10 bytes into section 4 should be average bitrate. max bitrate is 6 bytes in.
349 if(fread((void *)&bitrate, sizeof(unsigned int), 1, infile))
350 psong->bitrate = ntohl(bitrate);
351 if( !fread((void *)&buffer, 1, 1, infile) || (buffer[0] != 0x05) || (_aac_check_extended_descriptor(infile) != 0) )
352 goto bad_esds;
353 fseek(infile, 1, SEEK_CUR); // 1 bytes into section 5 should be the setup data
354 if(fread((void *)&buffer, 2, 1, infile))
356 profile_id = (buffer[0] >> 3); // first 5 bits of setup data is the Audo Profile ID
357 /* Frequency index: (((buffer[0] & 0x7) << 1) | (buffer[1] >> 7))) */
358 samples = ((buffer[1] >> 3) & 0xF);
359 psong->channels = (samples == 7 ? 8 : samples);
363 bad_esds:
365 atom_offset = _aac_lookforatom(infile, "mdat", (unsigned int*)&atom_length);
366 psong->audio_size = atom_length - 8;
367 psong->audio_offset = atom_offset;
369 if(!psong->bitrate)
371 /* Dont' scare people with this for now. Could be Apple Lossless?
372 DPRINTF(E_DEBUG, L_SCANNER, "No 'esds' atom. Guess bitrate. [%s]\n", basename(file)); */
373 if((atom_offset != -1) && (psong->song_length))
375 psong->bitrate = atom_length * 1000 / psong->song_length / 128;
377 /* If this is an obviously wrong bitrate, try something different */
378 if((psong->bitrate < 16000) && (psong->song_length > 1000))
380 psong->bitrate = (file_size * 8) / (psong->song_length / 1000);
384 //DPRINTF(E_DEBUG, L_METADATA, "Profile ID: %u\n", profile_id);
385 switch( profile_id )
387 case AAC_LC:
388 case AAC_LC_ER:
389 if( psong->samplerate < 8000 || psong->samplerate > 48000 )
391 DPRINTF(E_DEBUG, L_METADATA, "Unsupported AAC: sample rate is not 8000 < %d < 48000\n",
392 psong->samplerate);
393 break;
395 /* AAC @ Level 1/2 */
396 if( psong->channels <= 2 && psong->bitrate <= 320000 )
397 xasprintf(&(psong->dlna_pn), "AAC_ISO_320");
398 else if( psong->channels <= 2 && psong->bitrate <= 576000 )
399 xasprintf(&(psong->dlna_pn), "AAC_ISO");
400 else if( psong->channels <= 6 && psong->bitrate <= 1440000 )
401 xasprintf(&(psong->dlna_pn), "AAC_MULT5_ISO");
402 else
403 DPRINTF(E_DEBUG, L_METADATA, "Unhandled AAC: %d channels, %d bitrate\n",
404 psong->channels, psong->bitrate);
405 break;
406 default:
407 DPRINTF(E_DEBUG, L_METADATA, "Unhandled AAC type %d [%s]\n", profile_id, basename(file));
408 break;
411 fclose(infile);
412 return 0;