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 6           get_aacinfo(PerlIO *infile, char *file, HV *info, HV *tags)
21             {
22             off_t file_size;
23             Buffer buf;
24             unsigned char *bptr;
25 6           int err = 0;
26 6           unsigned int id3_size = 0;
27 6           unsigned int audio_offset = 0;
28              
29 6           buffer_init(&buf, AAC_BLOCK_SIZE);
30              
31 6           file_size = _file_size(infile);
32              
33 6           my_hv_store( info, "file_size", newSVuv(file_size) );
34              
35 6 50         if ( !_check_buf(infile, &buf, 10, AAC_BLOCK_SIZE) ) {
36 0           err = -1;
37 0           goto out;
38             }
39              
40 6           bptr = buffer_ptr(&buf);
41              
42             // Check for ID3 tag
43 6 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 670 50         while ( buffer_len(&buf) >= 6 ) {
73 670           bptr = buffer_ptr(&buf);
74              
75 670 100         if ( (bptr[0] == 0xFF) && ((bptr[1] & 0xF6) == 0xF0)
    50          
76 6 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 6           my_hv_store( info, "audio_offset", newSVuv(audio_offset) );
94 6           my_hv_store( info, "audio_size", newSVuv(file_size - audio_offset) );
95              
96             // Parse ID3 at end
97 6 100         if (id3_size) {
98 1           parse_id3(infile, file, info, tags, 0, file_size);
99             }
100              
101             out:
102 6           buffer_free(&buf);
103              
104 6 50         if (err) return err;
105              
106 6           return 0;
107             }
108              
109             // ADTS parser adapted from faad
110              
111             int
112 6           aac_parse_adts(PerlIO *infile, char *file, off_t audio_size, Buffer *buf, HV *info)
113             {
114             int frames, frame_length;
115 6           int t_framelength = 0;
116 6           int samplerate = 0;
117             int bitrate;
118 6           uint8_t profile = 0;
119 6           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 6           for (frames = 1; /* */; frames++) {
126 107 50         if ( !_check_buf(infile, buf, audio_size > AAC_BLOCK_SIZE ? AAC_BLOCK_SIZE : audio_size, AAC_BLOCK_SIZE) ) {
127 0 0         if (frames < 2)
128 0           return 0;
129             else
130 0           break;
131             }
132              
133 107           bptr = buffer_ptr(buf);
134              
135             /* check syncword */
136 107 50         if (!((bptr[0] == 0xFF)&&((bptr[1] & 0xF6) == 0xF0)))
    50          
137             break;
138              
139 107 100         if (frames == 1) {
140 6           profile = (bptr[2] & 0xc0) >> 6;
141 6           samplerate = adts_sample_rates[(bptr[2]&0x3c)>>2];
142 6           channels = ((bptr[2] & 0x1) << 2) | ((bptr[3] & 0xc0) >> 6);
143             }
144              
145 214           frame_length = ((((unsigned int)bptr[3] & 0x3)) << 11)
146 107           | (((unsigned int)bptr[4]) << 3) | (bptr[5] >> 5);
147              
148 107 100         if (frames == 1 && _check_buf(infile, buf, frame_length + 10, AAC_BLOCK_SIZE)) {
    50          
149 6           unsigned char *bptr2 = (unsigned char *)buffer_ptr(buf) + frame_length;
150             int frame_length2;
151 6 50         if (!((bptr2[0] == 0xFF)&&((bptr2[1] & 0xF6) == 0xF0))
    50          
152 6 50         || profile != (bptr2[2] & 0xc0) >> 6
153 6 50         || samplerate != adts_sample_rates[(bptr2[2]&0x3c)>>2]
154 6 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 12           frame_length2 = ((((unsigned int)bptr2[3] & 0x3)) << 11)
161 6           | (((unsigned int)bptr2[4]) << 3) | (bptr2[5] >> 5);
162              
163 6 100         if (_check_buf(infile, buf, frame_length + frame_length2 + 10, AAC_BLOCK_SIZE)) {
164 5           bptr2 = (unsigned char *)buffer_ptr(buf) + frame_length + frame_length2;
165 5 50         if (!((bptr2[0] == 0xFF)&&((bptr2[1] & 0xF6) == 0xF0))
    50          
166 5 50         || profile != (bptr2[2] & 0xc0) >> 6
167 5 50         || samplerate != adts_sample_rates[(bptr2[2]&0x3c)>>2]
168 5 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 107           t_framelength += frame_length;
177              
178 107 50         if (frame_length > buffer_len(buf))
179 0           break;
180              
181 107           buffer_consume(buf, frame_length);
182 107           audio_size -= frame_length;
183              
184             // Avoid looping again if we have a partial frame header
185 107 100         if (audio_size < 6)
186 6           break;
187 101           }
188              
189 6 50         if (frames < 2) {
190             DEBUG_TRACE("False sync\n");
191 0           return 0;
192             }
193              
194 6           frames_per_sec = (float)samplerate/1024.0f;
195 6 50         if (frames != 0)
196 6           bytes_per_frame = (float)t_framelength/(float)(frames*1000);
197             else
198 0           bytes_per_frame = 0;
199              
200 6           bitrate = (int)(8. * bytes_per_frame * frames_per_sec + 0.5);
201              
202 6 50         if (frames_per_sec != 0)
203 6           length = (float)frames/frames_per_sec;
204             else
205 0           length = 1;
206              
207             DEBUG_TRACE("ADTS frames=%d, frames_per_sec=%f, bytes_per_frame=%f, length=%f\n",
208             frames, frames_per_sec, bytes_per_frame, length);
209              
210             // DLNA profile detection
211             // XXX Does not detect HEAAC_L3_ADTS
212 6 50         if (samplerate >= 8000) {
213 6 50         if (profile == 1) { // LC
214 6 50         if (channels <= 2) {
215 6 50         if (bitrate <= 192) {
216 6 100         if (samplerate <= 24000)
217 3           my_hv_store( info, "dlna_profile", newSVpv("HEAAC_L2_ADTS_320", 0) ); // XXX shouldn't really use samplerate for AAC vs AACplus
218             else
219 6           my_hv_store( info, "dlna_profile", newSVpv("AAC_ADTS_192", 0) );
220             }
221 0 0         else if (bitrate <= 320) {
222 0 0         if (samplerate <= 24000)
223 0           my_hv_store( info, "dlna_profile", newSVpv("HEAAC_L2_ADTS_320", 0) );
224             else
225 0           my_hv_store( info, "dlna_profile", newSVpv("AAC_ADTS_320", 0) );
226             }
227             else {
228 0 0         if (samplerate <= 24000)
229 0           my_hv_store( info, "dlna_profile", newSVpv("HEAAC_L2_ADTS", 0) );
230             else
231 6           my_hv_store( info, "dlna_profile", newSVpv("AAC_ADTS", 0) );
232             }
233             }
234 0 0         else if (channels <= 6) {
235 0 0         if (samplerate <= 24000)
236 0           my_hv_store( info, "dlna_profile", newSVpv("HEAAC_MULT5_ADTS", 0) );
237             else
238 0           my_hv_store( info, "dlna_profile", newSVpv("AAC_MULT5_ADTS", 0) );
239             }
240             }
241             }
242              
243             // Samplerate <= 24000 is AACplus and the samplerate is doubled
244 6 100         if (samplerate <= 24000)
245 3           samplerate *= 2;
246              
247 6           my_hv_store( info, "bitrate", newSVuv(bitrate * 1000) );
248 6           my_hv_store( info, "song_length_ms", newSVuv(length * 1000) );
249 6           my_hv_store( info, "samplerate", newSVuv(samplerate) );
250 6           my_hv_store( info, "profile", newSVpv( aac_profiles[profile], 0 ) );
251 6           my_hv_store( info, "channels", newSVuv(channels) );
252              
253 6           return 1;
254             }