1 //=========================================================================
2 // FILENAME : tagutils-aac.c
3 // DESCRIPTION : AAC metadata reader
4 //=========================================================================
5 // Copyright (c) 2008- NETGEAR, Inc. All Rights Reserved.
6 //=========================================================================
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.
30 _aac_findatom(FILE *fin
, long max_offset
, char *which_atom
, int *atom_size
)
32 long current_offset
= 0;
36 while(current_offset
< max_offset
)
38 if(fread((void*)&size
, 1, sizeof(int), fin
) != sizeof(int))
46 if(fread(atom
, 1, 4, fin
) != 4)
49 if(strncasecmp(atom
, which_atom
, 4) == 0)
52 return current_offset
;
55 fseek(fin
, size
- 8, SEEK_CUR
);
56 current_offset
+= size
;
64 _get_aactags(char *file
, struct song_metadata
*psong
)
68 unsigned int atom_length
;
70 long current_offset
= 0;
73 char *current_data
= NULL
;
77 if(!(fin
= fopen(file
, "rb")))
79 DPRINTF(E_ERROR
, L_SCANNER
, "Cannot open file %s for reading\n", file
);
83 fseek(fin
, 0, SEEK_SET
);
85 atom_offset
= _aac_lookforatom(fin
, "moov:udta:meta:ilst", &atom_length
);
88 while(current_offset
< atom_length
)
90 if(fread((void*)¤t_size
, 1, sizeof(int), fin
) != sizeof(int))
93 current_size
= ntohl(current_size
);
95 if(current_size
<= 7 || current_size
> 1<<24) // something not right
98 if(fread(current_atom
, 1, 4, fin
) != 4)
101 len
= current_size
- 7; // too short
105 current_data
= (char*)malloc(len
); // extra byte
107 if(fread(current_data
, 1, current_size
- 8, fin
) != current_size
- 8)
110 current_data
[current_size
- 8] = '\0';
111 if(!memcmp(current_atom
, "\xA9" "nam", 4))
112 psong
->title
= strdup((char*)¤t_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*)¤t_data
[16]);
116 else if(!memcmp(current_atom
, "\xA9" "alb", 4))
117 psong
->album
= strdup((char*)¤t_data
[16]);
118 else if(!memcmp(current_atom
, "\xA9" "cmt", 4))
119 psong
->comment
= strdup((char*)¤t_data
[16]);
120 else if(!memcmp(current_atom
, "aART", 4) ||
121 !memcmp(current_atom
, "aart", 4))
122 psong
->contributor
[ROLE_ALBUMARTIST
] = strdup((char*)¤t_data
[16]);
123 else if(!memcmp(current_atom
, "\xA9" "dir", 4))
124 psong
->contributor
[ROLE_CONDUCTOR
] = strdup((char*)¤t_data
[16]);
125 else if(!memcmp(current_atom
, "\xA9" "wrt", 4))
126 psong
->contributor
[ROLE_COMPOSER
] = strdup((char*)¤t_data
[16]);
127 else if(!memcmp(current_atom
, "\xA9" "grp", 4))
128 psong
->grouping
= strdup((char*)¤t_data
[16]);
129 else if(!memcmp(current_atom
, "\xA9" "gen", 4))
130 psong
->genre
= strdup((char*)¤t_data
[16]);
131 else if(!memcmp(current_atom
, "\xA9" "day", 4))
132 psong
->year
= atoi((char*)¤t_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
);
162 DPRINTF(E_ERROR
, L_SCANNER
, "Out of memory [%s]\n", file
);
167 current_offset
+= current_size
;
173 if(atom_offset
== -1)
181 _aac_lookforatom(FILE *aac_fp
, char *atom_path
, unsigned int *atom_length
)
188 fseek(aac_fp
, 0, SEEK_END
);
189 file_size
= ftell(aac_fp
);
193 while(*end_p
!= '\0')
202 if((end_p
- cur_p
) < 4)
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)
212 cur_p
= strchr(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
)
240 unsigned char buf
[3];
242 if( fread((void *)&buf
, 1, 3, infile
) < 3 )
246 if( (buf
[i
] != 0x80) &&
250 fseek(infile
, -3, SEEK_CUR
);
260 _get_aacfileinfo(char *file
, struct song_metadata
*psong
)
267 unsigned int bitrate
;
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");
279 DPRINTF(E_ERROR
, L_SCANNER
, "Could not open %s for reading\n", file
);
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))
299 sample_size
= ntohl(sample_size
);
300 samples
= ntohl(samples
);
302 // avoid overflowing on large sample_sizes (90000)
304 while((ms
> 9) && (!(sample_size
% 10)))
311 psong
->song_length
= (int)((samples
* ms
) / sample_size
);
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]);
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) )
345 fseek(infile
, 4, SEEK_CUR
);
346 if( !fread((void *)&buffer
, 1, 1, infile
) || (buffer
[0] != 0x04) || (_aac_check_extended_descriptor(infile
) != 0) )
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) )
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
);
365 atom_offset
= _aac_lookforatom(infile
, "mdat", (unsigned int*)&atom_length
);
366 psong
->audio_size
= atom_length
- 8;
367 psong
->audio_offset
= atom_offset
;
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);
389 if( psong
->samplerate
< 8000 || psong
->samplerate
> 48000 )
391 DPRINTF(E_DEBUG
, L_METADATA
, "Unsupported AAC: sample rate is not 8000 < %d < 48000\n",
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");
403 DPRINTF(E_DEBUG
, L_METADATA
, "Unhandled AAC: %d channels, %d bitrate\n",
404 psong
->channels
, psong
->bitrate
);
407 DPRINTF(E_DEBUG
, L_METADATA
, "Unhandled AAC type %d [%s]\n", profile_id
, basename(file
));