Reinstate dropbear 0.54 update
[tomato.git] / release / src / router / minidlna / tagutils / tagutils-aac.c
blob4224d5f7b99dbe53f6c33fdd476fa1a0428b3662
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.
27 // _mac_to_unix_time
28 static time_t
29 _mac_to_unix_time(int t)
31 struct timeval tv;
32 struct timezone tz;
34 gettimeofday(&tv, &tz);
36 return (t - (365L * 66L * 24L * 60L * 60L + 17L * 60L * 60L * 24L) +
37 (tz.tz_minuteswest * 60));
42 // _aac_findatom:
43 static long
44 _aac_findatom(FILE *fin, long max_offset, char *which_atom, int *atom_size)
46 long current_offset = 0;
47 int size;
48 char atom[4];
50 while(current_offset < max_offset)
52 if(fread((void*)&size, 1, sizeof(int), fin) != sizeof(int))
53 return -1;
55 size = ntohl(size);
57 if(size <= 7)
58 return -1;
60 if(fread(atom, 1, 4, fin) != 4)
61 return -1;
63 if(strncasecmp(atom, which_atom, 4) == 0)
65 *atom_size = size;
66 return current_offset;
69 fseek(fin, size - 8, SEEK_CUR);
70 current_offset += size;
73 return -1;
76 // _get_aactags
77 static int
78 _get_aactags(char *file, struct song_metadata *psong)
80 FILE *fin;
81 long atom_offset;
82 unsigned int atom_length;
84 long current_offset = 0;
85 int current_size;
86 char current_atom[4];
87 char *current_data;
88 int genre;
89 int len;
91 if(!(fin = fopen(file, "rb")))
93 DPRINTF(E_ERROR, L_SCANNER, "Cannot open file %s for reading\n", file);
94 return -1;
97 fseek(fin, 0, SEEK_SET);
99 atom_offset = _aac_lookforatom(fin, "moov:udta:meta:ilst", &atom_length);
100 if(atom_offset != -1)
102 while(current_offset < atom_length)
104 if(fread((void*)&current_size, 1, sizeof(int), fin) != sizeof(int))
105 break;
107 current_size = ntohl(current_size);
109 if(current_size <= 7 || current_size > 1<<24) // something not right
110 break;
112 if(fread(current_atom, 1, 4, fin) != 4)
113 break;
115 len = current_size - 7; // too short
116 if(len < 22)
117 len = 22;
119 current_data = (char*)malloc(len); // extra byte
120 memset(current_data, 0x00, len);
122 if(fread(current_data, 1, current_size - 8, fin) != current_size - 8)
123 break;
125 if(!memcmp(current_atom, "\xA9" "nam", 4))
126 psong->title = strdup((char*)&current_data[16]);
127 else if(!memcmp(current_atom, "\xA9" "ART", 4) ||
128 !memcmp(current_atom, "\xA9" "art", 4))
129 psong->contributor[ROLE_ARTIST] = strdup((char*)&current_data[16]);
130 else if(!memcmp(current_atom, "\xA9" "alb", 4))
131 psong->album = strdup((char*)&current_data[16]);
132 else if(!memcmp(current_atom, "\xA9" "cmt", 4))
133 psong->comment = strdup((char*)&current_data[16]);
134 else if(!memcmp(current_atom, "\xA9" "dir", 4))
135 psong->contributor[ROLE_CONDUCTOR] = strdup((char*)&current_data[16]);
136 else if(!memcmp(current_atom, "\xA9" "wrt", 4))
137 psong->contributor[ROLE_COMPOSER] = strdup((char*)&current_data[16]);
138 else if(!memcmp(current_atom, "\xA9" "grp", 4))
139 psong->grouping = strdup((char*)&current_data[16]);
140 else if(!memcmp(current_atom, "\xA9" "gen", 4))
141 psong->genre = strdup((char*)&current_data[16]);
142 else if(!memcmp(current_atom, "\xA9" "day", 4))
143 psong->year = atoi((char*)&current_data[16]);
144 else if(!memcmp(current_atom, "tmpo", 4))
145 psong->bpm = (current_data[16] << 8) | current_data[17];
146 else if(!memcmp(current_atom, "trkn", 4))
148 psong->track = (current_data[18] << 8) | current_data[19];
149 psong->total_tracks = (current_data[20] << 8) | current_data[21];
151 else if(!memcmp(current_atom, "disk", 4))
153 psong->disc = (current_data[18] << 8) | current_data[19];
154 psong->total_discs = (current_data[20] << 8) | current_data[21];
156 else if(!memcmp(current_atom, "gnre", 4))
158 genre = current_data[17] - 1;
159 if((genre < 0) || (genre > WINAMP_GENRE_UNKNOWN))
160 genre = WINAMP_GENRE_UNKNOWN;
161 psong->genre = strdup(winamp_genre[genre]);
163 else if(!memcmp(current_atom, "cpil", 4))
165 psong->compilation = current_data[16];
167 else if(!memcmp(current_atom, "covr", 4))
169 psong->image_size = current_size - 8 - 16;
170 if((psong->image = malloc(psong->image_size)))
171 memcpy(psong->image, current_data+16, psong->image_size);
172 else
173 DPRINTF(E_ERROR, L_SCANNER, "Out of memory [%s]\n", file);
176 free(current_data);
177 current_offset += current_size;
181 fclose(fin);
183 if(atom_offset == -1)
184 return -1;
186 return 0;
189 // aac_lookforatom
190 static off_t
191 _aac_lookforatom(FILE *aac_fp, char *atom_path, unsigned int *atom_length)
193 long atom_offset;
194 off_t file_size;
195 char *cur_p, *end_p;
196 char atom_name[5];
198 fseek(aac_fp, 0, SEEK_END);
199 file_size = ftell(aac_fp);
200 rewind(aac_fp);
202 end_p = atom_path;
203 while(*end_p != '\0')
205 end_p++;
207 atom_name[4] = '\0';
208 cur_p = atom_path;
210 while(cur_p)
212 if((end_p - cur_p) < 4)
214 return -1;
216 strncpy(atom_name, cur_p, 4);
217 atom_offset = _aac_findatom(aac_fp, file_size, atom_name, (int*)atom_length);
218 if(atom_offset == -1)
220 return -1;
222 cur_p = strchr(cur_p, ':');
223 if(cur_p != NULL)
225 cur_p++;
227 if(!strcmp(atom_name, "meta"))
229 fseek(aac_fp, 4, SEEK_CUR);
231 else if(!strcmp(atom_name, "stsd"))
233 fseek(aac_fp, 8, SEEK_CUR);
235 else if(!strcmp(atom_name, "mp4a"))
237 fseek(aac_fp, 28, SEEK_CUR);
242 // return position of 'size:atom'
243 return ftell(aac_fp) - 8;
247 _aac_check_extended_descriptor(FILE *infile)
249 short int i;
250 unsigned char buf[3];
252 if( !fread((void *)&buf, 3, 1, infile) )
253 return -1;
254 for( i=0; i<3; i++ )
256 if( (buf[i] != 0x80) &&
257 (buf[i] != 0x81) &&
258 (buf[i] != 0xFE) )
260 fseek(infile, -3, SEEK_CUR);
261 return 0;
265 return 0;
268 // _get_aacfileinfo
270 _get_aacfileinfo(char *file, struct song_metadata *psong)
272 FILE *infile;
273 long atom_offset;
274 int atom_length;
275 int sample_size;
276 int samples;
277 unsigned int bitrate;
278 off_t file_size;
279 int ms;
280 unsigned char buffer[2];
281 int time = 0;
282 aac_object_type_t profile_id = 0;
284 psong->vbr_scale = -1;
285 psong->channels = 2; // A "normal" default in case we can't find this information
287 if(!(infile = fopen(file, "rb")))
289 DPRINTF(E_ERROR, L_SCANNER, "Could not open %s for reading\n", file);
290 return -1;
293 fseek(infile, 0, SEEK_END);
294 file_size = ftell(infile);
295 fseek(infile, 0, SEEK_SET);
297 // move to 'mvhd' atom
298 atom_offset = _aac_lookforatom(infile, "moov:mvhd", (unsigned int*)&atom_length);
299 if(atom_offset != -1)
301 fseek(infile, 8, SEEK_CUR);
302 fread((void *)&time, sizeof(int), 1, infile);
303 time = ntohl(time);
304 // slimserver prefer to use filesystem time
305 //psong->time_modified = _mac_to_unix_time(time);
306 fread((void*)&sample_size, 1, sizeof(int), infile);
307 fread((void*)&samples, 1, sizeof(int), infile);
309 sample_size = ntohl(sample_size);
310 samples = ntohl(samples);
312 // avoid overflowing on large sample_sizes (90000)
313 ms = 1000;
314 while((ms > 9) && (!(sample_size % 10)))
316 sample_size /= 10;
317 ms /= 10;
320 // unit = ms
321 psong->song_length = (int)((samples * ms) / sample_size);
324 psong->bitrate = 0;
326 // see if it is aac or alac
327 atom_offset = _aac_lookforatom(infile, "moov:trak:mdia:minf:stbl:stsd:alac", (unsigned int*)&atom_length);
328 if(atom_offset != -1) {
329 fseek(infile, atom_offset + 32, SEEK_SET);
330 fread(buffer, sizeof(unsigned char), 2, infile);
332 psong->samplerate = (buffer[0] << 8) | (buffer[1]);
333 goto bad_esds;
336 // get samplerate from 'mp4a' (not from 'mdhd')
337 atom_offset = _aac_lookforatom(infile, "moov:trak:mdia:minf:stbl:stsd:mp4a", (unsigned int*)&atom_length);
338 if(atom_offset != -1)
340 fseek(infile, atom_offset + 32, SEEK_SET);
341 fread(buffer, sizeof(unsigned char), 2, infile);
343 psong->samplerate = (buffer[0] << 8) | (buffer[1]);
345 fseek(infile, 2, SEEK_CUR);
347 // get bitrate from 'esds'
348 atom_offset = _aac_findatom(infile, atom_length - (ftell(infile) - atom_offset), "esds", &atom_length);
350 if(atom_offset != -1)
352 // skip the version number
353 fseek(infile, atom_offset + 4, SEEK_CUR);
354 // should be 0x03, to signify the descriptor type (section)
355 fread((void *)&buffer, 1, 1, infile);
356 if( (buffer[0] != 0x03) || (_aac_check_extended_descriptor(infile) != 0) )
357 goto bad_esds;
358 fseek(infile, 4, SEEK_CUR);
359 fread((void *)&buffer, 1, 1, infile);
360 if( (buffer[0] != 0x04) || (_aac_check_extended_descriptor(infile) != 0) )
361 goto bad_esds;
362 fseek(infile, 10, SEEK_CUR); // 10 bytes into section 4 should be average bitrate. max bitrate is 6 bytes in.
363 fread((void *)&bitrate, sizeof(unsigned int), 1, infile);
364 psong->bitrate = ntohl(bitrate);
365 fread((void *)&buffer, 1, 1, infile);
366 if( (buffer[0] != 0x05) || (_aac_check_extended_descriptor(infile) != 0) )
367 goto bad_esds;
368 fseek(infile, 1, SEEK_CUR); // 1 bytes into section 5 should be the setup data
369 fread((void *)&buffer, 2, 1, infile);
370 profile_id = (buffer[0] >> 3); // first 5 bits of setup data is the Audo Profile ID
371 /* Frequency index: (((buffer[0] & 0x7) << 1) | (buffer[1] >> 7))) */
372 samples = ((buffer[1] >> 3) & 0xF);
373 psong->channels = (samples == 7 ? 8 : samples);
376 bad_esds:
378 atom_offset = _aac_lookforatom(infile, "mdat", (unsigned int*)&atom_length);
379 psong->audio_size = atom_length - 8;
380 psong->audio_offset = atom_offset;
382 if(!psong->bitrate)
384 /* Dont' scare people with this for now. Could be Apple Lossless?
385 DPRINTF(E_DEBUG, L_SCANNER, "No 'esds' atom. Guess bitrate. [%s]\n", basename(file)); */
386 if((atom_offset != -1) && (psong->song_length))
388 psong->bitrate = atom_length * 1000 / psong->song_length / 128;
390 /* If this is an obviously wrong bitrate, try something different */
391 if((psong->bitrate < 16000) && (psong->song_length > 1000))
393 psong->bitrate = (file_size * 8) / (psong->song_length / 1000);
397 //DPRINTF(E_DEBUG, L_METADATA, "Profile ID: %u\n", profile_id);
398 switch( profile_id )
400 case AAC_LC:
401 case AAC_LC_ER:
402 if( psong->samplerate < 8000 || psong->samplerate > 48000 )
404 DPRINTF(E_DEBUG, L_METADATA, "Unsupported AAC: sample rate is not 8000 < %d < 48000\n",
405 psong->samplerate);
406 break;
408 /* AAC @ Level 1/2 */
409 if( psong->channels <= 2 && psong->bitrate <= 320000 )
410 asprintf(&(psong->dlna_pn), "AAC_ISO_320");
411 else if( psong->channels <= 2 && psong->bitrate <= 576000 )
412 asprintf(&(psong->dlna_pn), "AAC_ISO");
413 else if( psong->channels <= 6 && psong->bitrate <= 1440000 )
414 asprintf(&(psong->dlna_pn), "AAC_MULT5_ISO");
415 else
416 DPRINTF(E_DEBUG, L_METADATA, "Unhandled AAC: %d channels, %d bitrate\n",
417 psong->channels, psong->bitrate);
418 break;
419 default:
420 DPRINTF(E_DEBUG, L_METADATA, "Unhandled AAC type %d [%s]\n", profile_id, basename(file));
421 break;
424 fclose(infile);
425 return 0;