1 /* the Music Player Daemon (MPD)
2 * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
3 * This project's homepage is: http://www.musicpd.org
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include "../inputPlugin.h"
23 #define AAC_MAX_CHANNELS 6
28 #include "../inputStream.h"
29 #include "../outputBuffer.h"
37 /* all code here is either based on or copied from FAAD2's frontend code */
39 InputStream *inStream;
43 unsigned char *buffer;
47 static void fillAacBuffer(AacBuffer * b)
49 if (b->bytesConsumed > 0) {
52 if (b->bytesIntoBuffer) {
53 memmove((void *)b->buffer, (void *)(b->buffer +
59 bread = readFromInputStream(b->inStream,
64 if (bread != b->bytesConsumed)
66 b->bytesIntoBuffer += bread;
71 if (b->bytesIntoBuffer > 3) {
72 if (memcmp(b->buffer, "TAG", 3) == 0)
73 b->bytesIntoBuffer = 0;
75 if (b->bytesIntoBuffer > 11) {
76 if (memcmp(b->buffer, "LYRICSBEGIN", 11) == 0) {
77 b->bytesIntoBuffer = 0;
80 if (b->bytesIntoBuffer > 8) {
81 if (memcmp(b->buffer, "APETAGEX", 8) == 0) {
82 b->bytesIntoBuffer = 0;
88 static void advanceAacBuffer(AacBuffer * b, int bytes)
90 b->fileOffset += bytes;
91 b->bytesConsumed = bytes;
92 b->bytesIntoBuffer -= bytes;
95 static int adtsSampleRates[] =
96 { 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
97 16000, 12000, 11025, 8000, 7350, 0, 0, 0
100 static int adtsParse(AacBuffer * b, float *length)
102 int frames, frameLength;
103 int tFrameLength = 0;
105 float framesPerSec, bytesPerFrame;
107 /* Read all frames to ensure correct time and bitrate */
108 for (frames = 0;; frames++) {
111 if (b->bytesIntoBuffer > 7) {
113 if (!((b->buffer[0] == 0xFF) &&
114 ((b->buffer[1] & 0xF6) == 0xF0))) {
119 sampleRate = adtsSampleRates[(b->
124 frameLength = ((((unsigned int)b->buffer[3] & 0x3))
125 << 11) | (((unsigned int)b->buffer[4])
126 << 3) | (b->buffer[5] >> 5);
128 tFrameLength += frameLength;
130 if (frameLength > b->bytesIntoBuffer)
133 advanceAacBuffer(b, frameLength);
138 framesPerSec = (float)sampleRate / 1024.0;
140 bytesPerFrame = (float)tFrameLength / (float)(frames * 1000);
143 if (framesPerSec != 0)
144 *length = (float)frames / framesPerSec;
149 static void initAacBuffer(InputStream * inStream, AacBuffer * b, float *length,
150 size_t * retFileread, size_t * retTagsize)
159 memset(b, 0, sizeof(AacBuffer));
161 b->inStream = inStream;
163 fileread = inStream->size;
165 b->buffer = xmalloc(FAAD_MIN_STREAMSIZE * AAC_MAX_CHANNELS);
166 memset(b->buffer, 0, FAAD_MIN_STREAMSIZE * AAC_MAX_CHANNELS);
168 bread = readFromInputStream(inStream, b->buffer, 1,
169 FAAD_MIN_STREAMSIZE * AAC_MAX_CHANNELS);
170 b->bytesIntoBuffer = bread;
171 b->bytesConsumed = 0;
174 if (bread != FAAD_MIN_STREAMSIZE * AAC_MAX_CHANNELS)
178 if (!memcmp(b->buffer, "ID3", 3)) {
179 tagsize = (b->buffer[6] << 21) | (b->buffer[7] << 14) |
180 (b->buffer[8] << 7) | (b->buffer[9] << 0);
183 advanceAacBuffer(b, tagsize);
188 *retFileread = fileread;
190 *retTagsize = tagsize;
195 if ((b->buffer[0] == 0xFF) && ((b->buffer[1] & 0xF6) == 0xF0)) {
196 adtsParse(b, length);
197 seekInputStream(b->inStream, tagsize, SEEK_SET);
199 bread = readFromInputStream(b->inStream, b->buffer, 1,
200 FAAD_MIN_STREAMSIZE *
202 if (bread != FAAD_MIN_STREAMSIZE * AAC_MAX_CHANNELS)
206 b->bytesIntoBuffer = bread;
207 b->bytesConsumed = 0;
208 b->fileOffset = tagsize;
209 } else if (memcmp(b->buffer, "ADIF", 4) == 0) {
211 int skipSize = (b->buffer[4] & 0x80) ? 9 : 0;
215 skipSize] & 0x0F) << 19) | ((unsigned
222 buffer[6 + skipSize] << 3) | ((unsigned int)b->buffer[7 +
226 if (fileread != 0 && bitRate != 0)
227 *length = fileread * 8.0 / bitRate;
233 static float getAacFloatTotalTime(char *file)
237 size_t fileread, tagsize;
238 faacDecHandle decoder;
239 faacDecConfigurationPtr config;
240 unsigned long sampleRate;
241 unsigned char channels;
242 InputStream inStream;
245 if (openInputStream(&inStream, file) < 0)
248 initAacBuffer(&inStream, &b, &length, &fileread, &tagsize);
251 decoder = faacDecOpen();
253 config = faacDecGetCurrentConfiguration(decoder);
254 config->outputFormat = FAAD_FMT_16BIT;
255 faacDecSetConfiguration(decoder, config);
258 #ifdef HAVE_FAAD_BUFLEN_FUNCS
259 bread = faacDecInit(decoder, b.buffer, b.bytesIntoBuffer,
260 &sampleRate, &channels);
262 bread = faacDecInit(decoder, b.buffer, &sampleRate, &channels);
264 if (bread >= 0 && sampleRate > 0 && channels > 0)
267 faacDecClose(decoder);
272 closeInputStream(&inStream);
277 static int getAacTotalTime(char *file)
282 if ((length = getAacFloatTotalTime(file)) >= 0)
288 static int aac_decode(OutputBuffer * cb, DecoderControl * dc, char *path)
292 faacDecHandle decoder;
293 faacDecFrameInfo frameInfo;
294 faacDecConfigurationPtr config;
296 unsigned long sampleRate;
297 unsigned char channels;
299 unsigned int sampleCount;
301 size_t sampleBufferLen;
303 long seekTableEnd = -1;
304 int seekPositionFound = 0; */
305 mpd_uint16 bitRate = 0;
307 InputStream inStream;
309 if ((totalTime = getAacFloatTotalTime(path)) < 0)
312 if (openInputStream(&inStream, path) < 0)
315 initAacBuffer(&inStream, &b, NULL, NULL, NULL);
317 decoder = faacDecOpen();
319 config = faacDecGetCurrentConfiguration(decoder);
320 config->outputFormat = FAAD_FMT_16BIT;
321 #ifdef HAVE_FAACDECCONFIGURATION_DOWNMATRIX
322 config->downMatrix = 1;
324 #ifdef HAVE_FAACDECCONFIGURATION_DONTUPSAMPLEIMPLICITSBR
325 config->dontUpSampleImplicitSBR = 0;
327 faacDecSetConfiguration(decoder, config);
331 #ifdef HAVE_FAAD_BUFLEN_FUNCS
332 bread = faacDecInit(decoder, b.buffer, b.bytesIntoBuffer,
333 &sampleRate, &channels);
335 bread = faacDecInit(decoder, b.buffer, &sampleRate, &channels);
338 ERROR("Error not a AAC stream.\n");
339 faacDecClose(decoder);
340 closeInputStream(b.inStream);
346 dc->audioFormat.bits = 16;
348 dc->totalTime = totalTime;
352 advanceAacBuffer(&b, bread);
357 if (b.bytesIntoBuffer == 0) {
361 #ifdef HAVE_FAAD_BUFLEN_FUNCS
362 sampleBuffer = faacDecDecode(decoder, &frameInfo, b.buffer,
365 sampleBuffer = faacDecDecode(decoder, &frameInfo, b.buffer);
368 if (frameInfo.error > 0) {
369 ERROR("error decoding AAC file: %s\n", path);
370 ERROR("faad2 error: %s\n",
371 faacDecGetErrorMessage(frameInfo.error));
375 #ifdef HAVE_FAACDECFRAMEINFO_SAMPLERATE
376 sampleRate = frameInfo.samplerate;
379 if (dc->state != DECODE_STATE_DECODE) {
380 dc->audioFormat.channels = frameInfo.channels;
381 dc->audioFormat.sampleRate = sampleRate;
382 getOutputAudioFormat(&(dc->audioFormat),
384 dc->state = DECODE_STATE_DECODE;
387 advanceAacBuffer(&b, frameInfo.bytesconsumed);
389 sampleCount = (unsigned long)(frameInfo.samples);
391 if (sampleCount > 0) {
392 bitRate = frameInfo.bytesconsumed * 8.0 *
393 frameInfo.channels * sampleRate /
394 frameInfo.samples / 1000 + 0.5;
396 (float)(frameInfo.samples) / frameInfo.channels /
400 sampleBufferLen = sampleCount * 2;
402 sendDataToOutputBuffer(cb, NULL, dc, 0, sampleBuffer,
403 sampleBufferLen, time, bitRate, NULL);
407 } else if (dc->stop) {
413 flushOutputBuffer(cb);
415 faacDecClose(decoder);
416 closeInputStream(b.inStream);
420 if (dc->state != DECODE_STATE_DECODE)
429 dc->state = DECODE_STATE_STOP;
432 dc->state = DECODE_STATE_STOP;
437 static MpdTag *aacTagDup(char *file)
442 time = getAacTotalTime(file);
445 if ((ret = id3Dup(file)) == NULL)
449 DEBUG("aacTagDup: Failed to get total song time from: %s\n",
456 static char *aac_suffixes[] = { "aac", NULL };
457 static char *aac_mimeTypes[] = { "audio/aac", "audio/aacp", NULL };
459 InputPlugin aacPlugin = {
467 INPUT_PLUGIN_STREAM_FILE,
474 InputPlugin aacPlugin;
476 #endif /* HAVE_FAAD */