source: titan/minidlna-1.0.22/tagutils/tagutils-aac.c @ 13567

Last change on this file since 13567 was 13567, checked in by obi, 11 years ago

[titan] add minidlna-1.0.22 first step

File size: 11.3 KB
Line 
1//=========================================================================
2// FILENAME     : tagutils-aac.c
3// DESCRIPTION  : AAC metadata reader
4//=========================================================================
5// Copyright (c) 2008- NETGEAR, Inc. All Rights Reserved.
6//=========================================================================
7
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.
13 *
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.
18 *
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/>.
21 */
22
23/*
24 * This file is derived from mt-daap project.
25 */
26
27// _mac_to_unix_time
28static time_t
29_mac_to_unix_time(int t)
30{
31        struct timeval tv;
32        struct timezone tz;
33
34        gettimeofday(&tv, &tz);
35
36        return (t - (365L * 66L * 24L * 60L * 60L + 17L * 60L * 60L * 24L) +
37                (tz.tz_minuteswest * 60));
38}
39
40
41
42// _aac_findatom:
43static long
44_aac_findatom(FILE *fin, long max_offset, char *which_atom, int *atom_size)
45{
46        long current_offset = 0;
47        int size;
48        char atom[4];
49
50        while(current_offset < max_offset)
51        {
52                if(fread((void*)&size, 1, sizeof(int), fin) != sizeof(int))
53                        return -1;
54
55                size = ntohl(size);
56
57                if(size <= 7)
58                        return -1;
59
60                if(fread(atom, 1, 4, fin) != 4)
61                        return -1;
62
63                if(strncasecmp(atom, which_atom, 4) == 0)
64                {
65                        *atom_size = size;
66                        return current_offset;
67                }
68
69                fseek(fin, size - 8, SEEK_CUR);
70                current_offset += size;
71        }
72
73        return -1;
74}
75
76// _get_aactags
77static int
78_get_aactags(char *file, struct song_metadata *psong)
79{
80        FILE *fin;
81        long atom_offset;
82        unsigned int atom_length;
83
84        long current_offset = 0;
85        int current_size;
86        char current_atom[4];
87        char *current_data;
88        int genre;
89        int len;
90
91        if(!(fin = fopen(file, "rb")))
92        {
93                DPRINTF(E_ERROR, L_SCANNER, "Cannot open file %s for reading\n", file);
94                return -1;
95        }
96
97        fseek(fin, 0, SEEK_SET);
98
99        atom_offset = _aac_lookforatom(fin, "moov:udta:meta:ilst", &atom_length);
100        if(atom_offset != -1)
101        {
102                while(current_offset < atom_length)
103                {
104                        if(fread((void*)&current_size, 1, sizeof(int), fin) != sizeof(int))
105                                break;
106
107                        current_size = ntohl(current_size);
108
109                        if(current_size <= 7 || current_size > 1<<24)  // something not right
110                                break;
111
112                        if(fread(current_atom, 1, 4, fin) != 4)
113                                break;
114
115                        len = current_size - 7; // too short
116                        if(len < 22)
117                                len = 22;
118
119                        current_data = (char*)malloc(len); // extra byte
120                        memset(current_data, 0x00, len);
121
122                        if(fread(current_data, 1, current_size - 8, fin) != current_size - 8)
123                                break;
124
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))
147                        {
148                                psong->track = (current_data[18] << 8) | current_data[19];
149                                psong->total_tracks = (current_data[20] << 8) | current_data[21];
150                        }
151                        else if(!memcmp(current_atom, "disk", 4))
152                        {
153                                psong->disc = (current_data[18] << 8) | current_data[19];
154                                psong->total_discs = (current_data[20] << 8) | current_data[21];
155                        }
156                        else if(!memcmp(current_atom, "gnre", 4))
157                        {
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]);
162                        }
163                        else if(!memcmp(current_atom, "cpil", 4))
164                        {
165                                psong->compilation = current_data[16];
166                        }
167                        else if(!memcmp(current_atom, "covr", 4))
168                        {
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);
174                        }
175
176                        free(current_data);
177                        current_offset += current_size;
178                }
179        }
180
181        fclose(fin);
182
183        if(atom_offset == -1)
184                return -1;
185
186        return 0;
187}
188
189// aac_lookforatom
190static off_t
191_aac_lookforatom(FILE *aac_fp, char *atom_path, unsigned int *atom_length)
192{
193        long atom_offset;
194        off_t file_size;
195        char *cur_p, *end_p;
196        char atom_name[5];
197
198        fseek(aac_fp, 0, SEEK_END);
199        file_size = ftell(aac_fp);
200        rewind(aac_fp);
201
202        end_p = atom_path;
203        while(*end_p != '\0')
204        {
205                end_p++;
206        }
207        atom_name[4] = '\0';
208        cur_p = atom_path;
209
210        while(cur_p)
211        {
212                if((end_p - cur_p) < 4)
213                {
214                        return -1;
215                }
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)
219                {
220                        return -1;
221                }
222                cur_p = strchr(cur_p, ':');
223                if(cur_p != NULL)
224                {
225                        cur_p++;
226
227                        if(!strcmp(atom_name, "meta"))
228                        {
229                                fseek(aac_fp, 4, SEEK_CUR);
230                        }
231                        else if(!strcmp(atom_name, "stsd"))
232                        {
233                                fseek(aac_fp, 8, SEEK_CUR);
234                        }
235                        else if(!strcmp(atom_name, "mp4a"))
236                        {
237                                fseek(aac_fp, 28, SEEK_CUR);
238                        }
239                }
240        }
241
242        // return position of 'size:atom'
243        return ftell(aac_fp) - 8;
244}
245
246int
247_aac_check_extended_descriptor(FILE *infile)
248{
249        short int i;
250        unsigned char buf[3];
251
252        if( !fread((void *)&buf, 3, 1, infile) )
253                return -1;
254        for( i=0; i<3; i++ )
255        {
256                if( (buf[i] != 0x80) &&
257                    (buf[i] != 0x81) &&
258                    (buf[i] != 0xFE) )
259                {
260                        fseek(infile, -3, SEEK_CUR);
261                        return 0;
262                }
263        }
264
265        return 0;
266}
267
268// _get_aacfileinfo
269int
270_get_aacfileinfo(char *file, struct song_metadata *psong)
271{
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;
283
284        psong->vbr_scale = -1;
285        psong->channels = 2; // A "normal" default in case we can't find this information
286
287        if(!(infile = fopen(file, "rb")))
288        {
289                DPRINTF(E_ERROR, L_SCANNER, "Could not open %s for reading\n", file);
290                return -1;
291        }
292
293        fseek(infile, 0, SEEK_END);
294        file_size = ftell(infile);
295        fseek(infile, 0, SEEK_SET);
296
297        // move to 'mvhd' atom
298        atom_offset = _aac_lookforatom(infile, "moov:mvhd", (unsigned int*)&atom_length);
299        if(atom_offset != -1)
300        {
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);
308
309                sample_size = ntohl(sample_size);
310                samples = ntohl(samples);
311
312                // avoid overflowing on large sample_sizes (90000)
313                ms = 1000;
314                while((ms > 9) && (!(sample_size % 10)))
315                {
316                        sample_size /= 10;
317                        ms /= 10;
318                }
319
320                // unit = ms
321                psong->song_length = (int)((samples * ms) / sample_size);
322        }
323
324        psong->bitrate = 0;
325
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);
331
332                psong->samplerate = (buffer[0] << 8) | (buffer[1]);
333                goto bad_esds;
334        }
335
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)
339        {
340                fseek(infile, atom_offset + 32, SEEK_SET);
341                fread(buffer, sizeof(unsigned char), 2, infile);
342
343                psong->samplerate = (buffer[0] << 8) | (buffer[1]);
344
345                fseek(infile, 2, SEEK_CUR);
346
347                // get bitrate from 'esds'
348                atom_offset = _aac_findatom(infile, atom_length - (ftell(infile) - atom_offset), "esds", &atom_length);
349
350                if(atom_offset != -1)
351                {
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);
374                }
375        }
376bad_esds:
377
378        atom_offset = _aac_lookforatom(infile, "mdat", (unsigned int*)&atom_length);
379        psong->audio_size = atom_length - 8;
380        psong->audio_offset = atom_offset;
381
382        if(!psong->bitrate)
383        {
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))
387                {
388                        psong->bitrate = atom_length * 1000 / psong->song_length / 128;
389                }
390                /* If this is an obviously wrong bitrate, try something different */
391                if((psong->bitrate < 16000) && (psong->song_length > 1000))
392                {
393                        psong->bitrate = (file_size * 8) / (psong->song_length / 1000);
394                }
395        }
396
397        //DPRINTF(E_DEBUG, L_METADATA, "Profile ID: %u\n", profile_id);
398        switch( profile_id )
399        {
400                case AAC_LC:
401                case AAC_LC_ER:
402                        if( psong->samplerate < 8000 || psong->samplerate > 48000 )
403                        {
404                                DPRINTF(E_DEBUG, L_METADATA, "Unsupported AAC: sample rate is not 8000 < %d < 48000\n",
405                                                              psong->samplerate);
406                                break;
407                        }
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;
422        }
423
424        fclose(infile);
425        return 0;
426}
Note: See TracBrowser for help on using the repository browser.