File Coverage

src/aac.c
Criterion Covered Total %
statement 91 115 79.1
branch 56 104 53.8
condition n/a
subroutine n/a
pod n/a
total 147 219 67.1


line stmt bran cond sub pod time code
1             /*
2             * This program is free software; you can redistribute it and/or modify
3             * it under the terms of the GNU General Public License as published by
4             * the Free Software Foundation; either version 2 of the License, or
5             * (at your option) any later version.
6             *
7             * This program is distributed in the hope that it will be useful,
8             * but WITHOUT ANY WARRANTY; without even the implied warranty of
9             * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10             * GNU General Public License for more details.
11             *
12             * You should have received a copy of the GNU General Public License
13             * along with this program; if not, write to the Free Software
14             * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15             */
16              
17             #include "aac.h"
18              
19             static int
20 5           get_aacinfo(PerlIO *infile, char *file, HV *info, HV *tags)
21             {
22             off_t file_size;
23             Buffer buf;
24             unsigned char *bptr;
25 5           int err = 0;
26 5           unsigned int id3_size = 0;
27 5           unsigned int audio_offset = 0;
28              
29 5           buffer_init(&buf, AAC_BLOCK_SIZE);
30              
31 5           file_size = _file_size(infile);
32              
33 5           my_hv_store( info, "file_size", newSVuv(file_size) );
34              
35 5 50         if ( !_check_buf(infile, &buf, 10, AAC_BLOCK_SIZE) ) {
36 0           err = -1;
37 0           goto out;
38             }
39              
40 5           bptr = buffer_ptr(&buf);
41              
42             // Check for ID3 tag
43 5 100         if (
44 2 100         (bptr[0] == 'I' && bptr[1] == 'D' && bptr[2] == '3') &&
    50          
    50          
45 1 50         bptr[3] < 0xff && bptr[4] < 0xff &&
    50          
46 1 50         bptr[6] < 0x80 && bptr[7] < 0x80 && bptr[8] < 0x80 && bptr[9] < 0x80
    50          
    50          
47             ) {
48             /* found an ID3 header... */
49 1           id3_size = 10 + (bptr[6]<<21) + (bptr[7]<<14) + (bptr[8]<<7) + bptr[9];
50              
51 1 50         if (bptr[5] & 0x10) {
52             // footer present
53 0           id3_size += 10;
54             }
55              
56 1           audio_offset += id3_size;
57              
58             DEBUG_TRACE("Found ID3 tag of size %d\n", id3_size);
59              
60             // Seek past ID3 and clear buffer
61 1           buffer_clear(&buf);
62 1           PerlIO_seek(infile, id3_size, SEEK_SET);
63              
64             // Read start of AAC data
65 1 50         if ( !_check_buf(infile, &buf, 10, AAC_BLOCK_SIZE) ) {
66 0           err = -1;
67 0           goto out;
68             }
69             }
70              
71             // Find 0xFF sync
72 669 50         while ( buffer_len(&buf) >= 6 ) {
73 669           bptr = buffer_ptr(&buf);
74              
75 669 100         if ( (bptr[0] == 0xFF) && ((bptr[1] & 0xF6) == 0xF0)
    50          
76 5 50         && aac_parse_adts(infile, file, file_size - audio_offset, &buf, info))
77             {
78             break;
79             }
80             else {
81 664           buffer_consume(&buf, 1);
82 664           audio_offset++;
83             }
84             }
85              
86             /*
87             XXX: need an ADIF test file
88             else if ( memcmp(bptr, "ADIF", 4) == 0 ) {
89             aac_parse_adif(infile, file, &buf, info);
90             }
91             */
92              
93 5           my_hv_store( info, "audio_offset", newSVuv(audio_offset) );
94 5           my_hv_store( info, "audio_size", newSVuv(file_size - audio_offset) );
95              
96             // Parse ID3 at end
97 5 100         if (id3_size) {
98 1           parse_id3(infile, file, info, tags, 0, file_size);
99             }
100              
101             out:
102 5           buffer_free(&buf);
103              
104 5 50         if (err) return err;
105              
106 5           return 0;
107             }
108              
109             // ADTS parser adapted from faad
110              
111             int
112 5           aac_parse_adts(PerlIO *infile, char *file, off_t audio_size, Buffer *buf, HV *info)
113             {
114             int frames, frame_length;
115 5           int t_framelength = 0;
116 5           int samplerate = 0;
117             int bitrate;
118 5           uint8_t profile = 0;
119 5           uint8_t channels = 0;
120             float frames_per_sec, bytes_per_frame, length;
121              
122             unsigned char *bptr;
123              
124             /* Read all frames to ensure correct time and bitrate */
125 5           for (frames = 0; /* */; frames++) {
126 104 50         if ( !_check_buf(infile, buf, audio_size > AAC_BLOCK_SIZE ? AAC_BLOCK_SIZE : audio_size, AAC_BLOCK_SIZE) ) {
127 0 0         if (frames < 1)
128 0           return 0;
129             else
130 0           break;
131             }
132              
133 104           bptr = buffer_ptr(buf);
134              
135             /* check syncword */
136 104 50         if (!((bptr[0] == 0xFF)&&((bptr[1] & 0xF6) == 0xF0)))
    50          
137             break;
138              
139 104 100         if (frames == 0) {
140 5           profile = (bptr[2] & 0xc0) >> 6;
141 5           samplerate = adts_sample_rates[(bptr[2]&0x3c)>>2];
142 5           channels = ((bptr[2] & 0x1) << 2) | ((bptr[3] & 0xc0) >> 6);
143             }
144              
145 208           frame_length = ((((unsigned int)bptr[3] & 0x3)) << 11)
146 104           | (((unsigned int)bptr[4]) << 3) | (bptr[5] >> 5);
147              
148 104 100         if (frames == 0 && _check_buf(infile, buf, frame_length + 10, AAC_BLOCK_SIZE)) {
    50          
149 5           unsigned char *bptr2 = (unsigned char *)buffer_ptr(buf) + frame_length;
150             int frame_length2;
151 5 50         if (!((bptr2[0] == 0xFF)&&((bptr2[1] & 0xF6) == 0xF0))
    50          
152 5 50         || profile != (bptr2[2] & 0xc0) >> 6
153 5 50         || samplerate != adts_sample_rates[(bptr2[2]&0x3c)>>2]
154 5 50         || channels != (((bptr2[2] & 0x1) << 2) | ((bptr2[3] & 0xc0) >> 6)))
155             {
156             DEBUG_TRACE("False sync at frame %d+1\n", frames);
157 0           return 0;
158             }
159              
160 10           frame_length2 = ((((unsigned int)bptr2[3] & 0x3)) << 11)
161 5           | (((unsigned int)bptr2[4]) << 3) | (bptr2[5] >> 5);
162              
163 5 100         if (_check_buf(infile, buf, frame_length + frame_length2 + 10, AAC_BLOCK_SIZE)) {
164 4           bptr2 = (unsigned char *)buffer_ptr(buf) + frame_length + frame_length2;
165 4 50         if (!((bptr2[0] == 0xFF)&&((bptr2[1] & 0xF6) == 0xF0))
    50          
166 4 50         || profile != (bptr2[2] & 0xc0) >> 6
167 4 50         || samplerate != adts_sample_rates[(bptr2[2]&0x3c)>>2]
168 4 50         || channels != (((bptr2[2] & 0x1) << 2) | ((bptr2[3] & 0xc0) >> 6)))
169             {
170             DEBUG_TRACE("False sync at frame %d+2\n", frames);
171 0           return 0;
172             }
173             }
174             }
175              
176 104           t_framelength += frame_length;
177              
178 104 50         if (frame_length > buffer_len(buf))
179 0           break;
180              
181 104           buffer_consume(buf, frame_length);
182 104           audio_size -= frame_length;
183              
184             // Avoid looping again if we have a partial frame header
185 104 100         if (audio_size < 6)
186 5           break;
187 99           }
188              
189 5 50         if (frames < 1) {
190             DEBUG_TRACE("False sync\n");
191 0           return 0;
192             }
193              
194 5           frames_per_sec = (float)samplerate/1024.0f;
195 5 50         if (frames != 0)
196 5           bytes_per_frame = (float)t_framelength/(float)(frames*1000);
197             else
198 0           bytes_per_frame = 0;
199              
200 5           bitrate = (int)(8. * bytes_per_frame * frames_per_sec + 0.5);
201              
202 5 50         if (frames_per_sec != 0)
203 5           length = (float)frames/frames_per_sec;
204             else
205 0           length = 1;
206              
207             // DLNA profile detection
208             // XXX Does not detect HEAAC_L3_ADTS
209 5 50         if (samplerate >= 8000) {
210 5 50         if (profile == 1) { // LC
211 5 50         if (channels <= 2) {
212 5 50         if (bitrate <= 192) {
213 5 100         if (samplerate <= 24000)
214 2           my_hv_store( info, "dlna_profile", newSVpv("HEAAC_L2_ADTS_320", 0) ); // XXX shouldn't really use samplerate for AAC vs AACplus
215             else
216 5           my_hv_store( info, "dlna_profile", newSVpv("AAC_ADTS_192", 0) );
217             }
218 0 0         else if (bitrate <= 320) {
219 0 0         if (samplerate <= 24000)
220 0           my_hv_store( info, "dlna_profile", newSVpv("HEAAC_L2_ADTS_320", 0) );
221             else
222 0           my_hv_store( info, "dlna_profile", newSVpv("AAC_ADTS_320", 0) );
223             }
224             else {
225 0 0         if (samplerate <= 24000)
226 0           my_hv_store( info, "dlna_profile", newSVpv("HEAAC_L2_ADTS", 0) );
227             else
228 5           my_hv_store( info, "dlna_profile", newSVpv("AAC_ADTS", 0) );
229             }
230             }
231 0 0         else if (channels <= 6) {
232 0 0         if (samplerate <= 24000)
233 0           my_hv_store( info, "dlna_profile", newSVpv("HEAAC_MULT5_ADTS", 0) );
234             else
235 0           my_hv_store( info, "dlna_profile", newSVpv("AAC_MULT5_ADTS", 0) );
236             }
237             }
238             }
239              
240             // Samplerate <= 24000 is AACplus and the samplerate is doubled
241 5 100         if (samplerate <= 24000)
242 2           samplerate *= 2;
243              
244 5           my_hv_store( info, "bitrate", newSVuv(bitrate * 1000) );
245 5           my_hv_store( info, "song_length_ms", newSVuv(length * 1000) );
246 5           my_hv_store( info, "samplerate", newSVuv(samplerate) );
247 5           my_hv_store( info, "profile", newSVpv( aac_profiles[profile], 0 ) );
248 5           my_hv_store( info, "channels", newSVuv(channels) );
249              
250 5           return 1;
251             }