aac_plugin.c 14 KB
Newer Older
1
/* the Music Player Daemon (MPD)
2
 * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
 * This project's homepage is: http://www.musicpd.org
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

19
#include "../decoder_api.h"
20 21 22 23 24

#ifdef HAVE_FAAD

#define AAC_MAX_CHANNELS	6

Warren Dukes's avatar
Warren Dukes committed
25 26
#include "../utils.h"
#include "../log.h"
27 28 29 30 31

#include <faad.h>

/* all code here is either based on or copied from FAAD2's frontend code */
typedef struct {
Max Kellermann's avatar
Max Kellermann committed
32
	struct decoder *decoder;
Avuton Olrich's avatar
Avuton Olrich committed
33
	InputStream *inStream;
Max Kellermann's avatar
Max Kellermann committed
34 35 36
	size_t bytesIntoBuffer;
	size_t bytesConsumed;
	off_t fileOffset;
37 38 39 40
	unsigned char *buffer;
	int atEof;
} AacBuffer;

41 42 43 44 45 46 47 48 49 50 51 52 53
static void aac_buffer_shift(AacBuffer * b, size_t length)
{
	assert(length >= b->bytesConsumed);
	assert(length <= b->bytesConsumed + b->bytesIntoBuffer);

	memmove(b->buffer, b->buffer + length,
		b->bytesConsumed + b->bytesIntoBuffer - length);

	length -= b->bytesConsumed;
	b->bytesConsumed = 0;
	b->bytesIntoBuffer -= length;
}

Avuton Olrich's avatar
Avuton Olrich committed
54 55
static void fillAacBuffer(AacBuffer * b)
{
56 57
	size_t bread;

58 59
	if (b->bytesIntoBuffer >= FAAD_MIN_STREAMSIZE * AAC_MAX_CHANNELS)
		/* buffer already full */
60 61
		return;

62
	aac_buffer_shift(b, b->bytesConsumed);
63

64
	if (!b->atEof) {
65 66 67
		size_t rest = FAAD_MIN_STREAMSIZE * AAC_MAX_CHANNELS -
			b->bytesIntoBuffer;

Max Kellermann's avatar
Max Kellermann committed
68 69 70
		bread = decoder_read(b->decoder, b->inStream,
				     (void *)(b->buffer + b->bytesIntoBuffer),
				     rest);
71
		if (bread == 0 && inputStreamAtEOF(b->inStream))
72 73 74 75 76 77 78 79 80
			b->atEof = 1;
		b->bytesIntoBuffer += bread;
	}

	if ((b->bytesIntoBuffer > 3 && memcmp(b->buffer, "TAG", 3) == 0) ||
	    (b->bytesIntoBuffer > 11 &&
	     memcmp(b->buffer, "LYRICSBEGIN", 11) == 0) ||
	    (b->bytesIntoBuffer > 8 && memcmp(b->buffer, "APETAGEX", 8) == 0))
		b->bytesIntoBuffer = 0;
81 82
}

Max Kellermann's avatar
Max Kellermann committed
83
static void advanceAacBuffer(AacBuffer * b, size_t bytes)
Avuton Olrich's avatar
Avuton Olrich committed
84 85
{
	b->fileOffset += bytes;
86
	b->bytesConsumed = bytes;
Avuton Olrich's avatar
Avuton Olrich committed
87
	b->bytesIntoBuffer -= bytes;
88 89
}

Avuton Olrich's avatar
Avuton Olrich committed
90 91 92 93
static int adtsSampleRates[] =
    { 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
	16000, 12000, 11025, 8000, 7350, 0, 0, 0
};
94

95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
/**
 * Check whether the buffer head is an AAC frame, and return the frame
 * length.  Returns 0 if it is not a frame.
 */
static size_t adts_check_frame(AacBuffer * b)
{
	if (b->bytesIntoBuffer <= 7)
		return 0;

	/* check syncword */
	if (!((b->buffer[0] == 0xFF) && ((b->buffer[1] & 0xF6) == 0xF0)))
		return 0;

	return (((unsigned int)b->buffer[3] & 0x3) << 11) |
		(((unsigned int)b->buffer[4]) << 3) |
		(b->buffer[5] >> 5);
}

Max Kellermann's avatar
Max Kellermann committed
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
/**
 * Find the next AAC frame in the buffer.  Returns 0 if no frame is
 * found or if not enough data is available.
 */
static size_t adts_find_frame(AacBuffer * b)
{
	const unsigned char *p;
	size_t frame_length;

	while ((p = memchr(b->buffer, 0xff, b->bytesIntoBuffer)) != NULL) {
		/* discard data before 0xff */
		if (p > b->buffer)
			aac_buffer_shift(b, p - b->buffer);

		if (b->bytesIntoBuffer <= 7)
			/* not enough data yet */
			return 0;

		/* is it a frame? */
		frame_length = adts_check_frame(b);
		if (frame_length > 0)
			/* yes, it is */
			return frame_length;

		/* it's just some random 0xff byte; discard and and
		   continue searching */
		aac_buffer_shift(b, 1);
	}

	/* nothing at all; discard the whole buffer */
	aac_buffer_shift(b, b->bytesIntoBuffer);
	return 0;
}

147
static void adtsParse(AacBuffer * b, float *length)
Avuton Olrich's avatar
Avuton Olrich committed
148
{
Max Kellermann's avatar
Max Kellermann committed
149
	unsigned int frames, frameLength;
150
	int sampleRate = 0;
151
	float framesPerSec;
152 153

	/* Read all frames to ensure correct time and bitrate */
Avuton Olrich's avatar
Avuton Olrich committed
154
	for (frames = 0;; frames++) {
155 156
		fillAacBuffer(b);

Max Kellermann's avatar
Max Kellermann committed
157
		frameLength = adts_find_frame(b);
158
		if (frameLength > 0) {
Avuton Olrich's avatar
Avuton Olrich committed
159 160 161 162
			if (frames == 0) {
				sampleRate = adtsSampleRates[(b->
							      buffer[2] & 0x3c)
							     >> 2];
163 164
			}

Avuton Olrich's avatar
Avuton Olrich committed
165 166
			if (frameLength > b->bytesIntoBuffer)
				break;
167

Avuton Olrich's avatar
Avuton Olrich committed
168 169 170
			advanceAacBuffer(b, frameLength);
		} else
			break;
171 172
	}

Avuton Olrich's avatar
Avuton Olrich committed
173 174 175
	framesPerSec = (float)sampleRate / 1024.0;
	if (framesPerSec != 0)
		*length = (float)frames / framesPerSec;
176 177
}

Max Kellermann's avatar
Max Kellermann committed
178 179
static void initAacBuffer(AacBuffer * b,
			  struct decoder *decoder, InputStream * inStream)
180
{
Avuton Olrich's avatar
Avuton Olrich committed
181
	memset(b, 0, sizeof(AacBuffer));
182

Max Kellermann's avatar
Max Kellermann committed
183
	b->decoder = decoder;
184
	b->inStream = inStream;
185

186
	b->buffer = xmalloc(FAAD_MIN_STREAMSIZE * AAC_MAX_CHANNELS);
Avuton Olrich's avatar
Avuton Olrich committed
187
	memset(b->buffer, 0, FAAD_MIN_STREAMSIZE * AAC_MAX_CHANNELS);
188 189 190 191 192 193 194 195 196 197 198
}

static void aac_parse_header(AacBuffer * b, float *length)
{
	size_t fileread;
	size_t tagsize;

	if (length)
		*length = -1;

	fileread = b->inStream->size;
199

200
	fillAacBuffer(b);
201 202

	tagsize = 0;
203
	if (b->bytesIntoBuffer >= 10 && !memcmp(b->buffer, "ID3", 3)) {
204
		tagsize = (b->buffer[6] << 21) | (b->buffer[7] << 14) |
Avuton Olrich's avatar
Avuton Olrich committed
205
		    (b->buffer[8] << 7) | (b->buffer[9] << 0);
206

Avuton Olrich's avatar
Avuton Olrich committed
207 208
		tagsize += 10;
		advanceAacBuffer(b, tagsize);
209 210 211
		fillAacBuffer(b);
	}

Avuton Olrich's avatar
Avuton Olrich committed
212 213
	if (length == NULL)
		return;
214

215 216
	if (b->bytesIntoBuffer >= 2 &&
	    (b->buffer[0] == 0xFF) && ((b->buffer[1] & 0xF6) == 0xF0)) {
217
		adtsParse(b, length);
218
		seekInputStream(b->inStream, tagsize, SEEK_SET);
219

220
		b->bytesIntoBuffer = 0;
221 222
		b->bytesConsumed = 0;
		b->fileOffset = tagsize;
223 224

		fillAacBuffer(b);
Avuton Olrich's avatar
Avuton Olrich committed
225
	} else if (memcmp(b->buffer, "ADIF", 4) == 0) {
226 227
		int bitRate;
		int skipSize = (b->buffer[4] & 0x80) ? 9 : 0;
Avuton Olrich's avatar
Avuton Olrich committed
228 229 230 231 232 233 234 235 236 237 238 239 240
		bitRate =
		    ((unsigned int)(b->
				    buffer[4 +
					   skipSize] & 0x0F) << 19) | ((unsigned
									int)b->
								       buffer[5
									      +
									      skipSize]
								       << 11) |
		    ((unsigned int)b->
		     buffer[6 + skipSize] << 3) | ((unsigned int)b->buffer[7 +
									   skipSize]
						   & 0xE0);
241

242 243 244 245
		if (fileread != 0 && bitRate != 0)
			*length = fileread * 8.0 / bitRate;
		else
			*length = fileread;
246 247 248
	}
}

Avuton Olrich's avatar
Avuton Olrich committed
249 250
static float getAacFloatTotalTime(char *file)
{
251 252
	AacBuffer b;
	float length;
253 254
	faacDecHandle decoder;
	faacDecConfigurationPtr config;
255
	uint32_t sampleRate;
256
	unsigned char channels;
257
	InputStream inStream;
258
	long bread;
259

Avuton Olrich's avatar
Avuton Olrich committed
260 261
	if (openInputStream(&inStream, file) < 0)
		return -1;
262

Max Kellermann's avatar
Max Kellermann committed
263
	initAacBuffer(&b, NULL, &inStream);
264
	aac_parse_header(&b, &length);
265

Avuton Olrich's avatar
Avuton Olrich committed
266
	if (length < 0) {
267 268 269 270
		decoder = faacDecOpen();

		config = faacDecGetCurrentConfiguration(decoder);
		config->outputFormat = FAAD_FMT_16BIT;
Avuton Olrich's avatar
Avuton Olrich committed
271
		faacDecSetConfiguration(decoder, config);
272 273

		fillAacBuffer(&b);
274
#ifdef HAVE_FAAD_BUFLEN_FUNCS
Avuton Olrich's avatar
Avuton Olrich committed
275 276
		bread = faacDecInit(decoder, b.buffer, b.bytesIntoBuffer,
				    &sampleRate, &channels);
277
#else
Avuton Olrich's avatar
Avuton Olrich committed
278
		bread = faacDecInit(decoder, b.buffer, &sampleRate, &channels);
279
#endif
Avuton Olrich's avatar
Avuton Olrich committed
280 281
		if (bread >= 0 && sampleRate > 0 && channels > 0)
			length = 0;
282 283 284

		faacDecClose(decoder);
	}
285

Avuton Olrich's avatar
Avuton Olrich committed
286 287
	if (b.buffer)
		free(b.buffer);
288
	closeInputStream(&inStream);
289

290 291 292
	return length;
}

Avuton Olrich's avatar
Avuton Olrich committed
293 294
static int getAacTotalTime(char *file)
{
295
	int file_time = -1;
296 297
	float length;

Avuton Olrich's avatar
Avuton Olrich committed
298
	if ((length = getAacFloatTotalTime(file)) >= 0)
299
		file_time = length + 0.5;
300

301
	return file_time;
302 303
}

304 305 306 307 308 309 310 311 312
static int aac_stream_decode(struct decoder * mpd_decoder,
			     InputStream *inStream)
{
	float file_time;
	float totalTime = 0;
	faacDecHandle decoder;
	faacDecFrameInfo frameInfo;
	faacDecConfigurationPtr config;
	long bread;
313
	struct audio_format audio_format;
314 315 316 317 318 319 320 321 322
	uint32_t sampleRate;
	unsigned char channels;
	unsigned int sampleCount;
	char *sampleBuffer;
	size_t sampleBufferLen;
	mpd_uint16 bitRate = 0;
	AacBuffer b;
	int initialized = 0;

Max Kellermann's avatar
Max Kellermann committed
323
	initAacBuffer(&b, mpd_decoder, inStream);
324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438

	decoder = faacDecOpen();

	config = faacDecGetCurrentConfiguration(decoder);
	config->outputFormat = FAAD_FMT_16BIT;
#ifdef HAVE_FAACDECCONFIGURATION_DOWNMATRIX
	config->downMatrix = 1;
#endif
#ifdef HAVE_FAACDECCONFIGURATION_DONTUPSAMPLEIMPLICITSBR
	config->dontUpSampleImplicitSBR = 0;
#endif
	faacDecSetConfiguration(decoder, config);

	while (b.bytesIntoBuffer < FAAD_MIN_STREAMSIZE * AAC_MAX_CHANNELS &&
	       !b.atEof &&
	       decoder_get_command(mpd_decoder) == DECODE_COMMAND_NONE) {
	       	fillAacBuffer(&b);
		adts_find_frame(&b);
		fillAacBuffer(&b);
		my_usleep(10000);
	}

#ifdef HAVE_FAAD_BUFLEN_FUNCS
	bread = faacDecInit(decoder, b.buffer, b.bytesIntoBuffer,
			    &sampleRate, &channels);
#else
	bread = faacDecInit(decoder, b.buffer, &sampleRate, &channels);
#endif
	if (bread < 0) {
		ERROR("Error not a AAC stream.\n");
		faacDecClose(decoder);
		if (b.buffer)
			free(b.buffer);
		return -1;
	}

	audio_format.bits = 16;

	file_time = 0.0;

	advanceAacBuffer(&b, bread);

	while (1) {
		fillAacBuffer(&b);
		adts_find_frame(&b);
		fillAacBuffer(&b);

		if (b.bytesIntoBuffer == 0)
			break;

#ifdef HAVE_FAAD_BUFLEN_FUNCS
		sampleBuffer = faacDecDecode(decoder, &frameInfo, b.buffer,
					     b.bytesIntoBuffer);
#else
		sampleBuffer = faacDecDecode(decoder, &frameInfo, b.buffer);
#endif

		if (frameInfo.error > 0) {
			ERROR("error decoding AAC stream\n");
			ERROR("faad2 error: %s\n",
			      faacDecGetErrorMessage(frameInfo.error));
			break;
		}
#ifdef HAVE_FAACDECFRAMEINFO_SAMPLERATE
		sampleRate = frameInfo.samplerate;
#endif

		if (!initialized) {
			audio_format.channels = frameInfo.channels;
			audio_format.sampleRate = sampleRate;
			decoder_initialized(mpd_decoder, &audio_format, totalTime);
			initialized = 1;
		}

		advanceAacBuffer(&b, frameInfo.bytesconsumed);

		sampleCount = (unsigned long)(frameInfo.samples);

		if (sampleCount > 0) {
			bitRate = frameInfo.bytesconsumed * 8.0 *
			    frameInfo.channels * sampleRate /
			    frameInfo.samples / 1000 + 0.5;
			file_time +=
			    (float)(frameInfo.samples) / frameInfo.channels /
			    sampleRate;
		}

		sampleBufferLen = sampleCount * 2;

		decoder_data(mpd_decoder, NULL, 0, sampleBuffer,
			     sampleBufferLen, file_time,
			     bitRate, NULL);
		if (decoder_get_command(mpd_decoder) == DECODE_COMMAND_SEEK) {
			decoder_seek_error(mpd_decoder);
		} else if (decoder_get_command(mpd_decoder) == DECODE_COMMAND_STOP)
			break;
	}

	decoder_flush(mpd_decoder);

	faacDecClose(decoder);
	if (b.buffer)
		free(b.buffer);

	if (!initialized)
		return -1;

	if (decoder_get_command(mpd_decoder) == DECODE_COMMAND_SEEK) {
		decoder_seek_error(mpd_decoder);
	}

	return 0;
}


439
static int aac_decode(struct decoder * mpd_decoder, char *path)
Avuton Olrich's avatar
Avuton Olrich committed
440
{
441
	float file_time;
Warren Dukes's avatar
Warren Dukes committed
442
	float totalTime;
443 444 445
	faacDecHandle decoder;
	faacDecFrameInfo frameInfo;
	faacDecConfigurationPtr config;
446
	long bread;
447
	struct audio_format audio_format;
448
	uint32_t sampleRate;
449 450
	unsigned char channels;
	unsigned int sampleCount;
Avuton Olrich's avatar
Avuton Olrich committed
451
	char *sampleBuffer;
452
	size_t sampleBufferLen;
Warren Dukes's avatar
Warren Dukes committed
453
	/*float * seekTable;
Avuton Olrich's avatar
Avuton Olrich committed
454 455
	   long seekTableEnd = -1;
	   int seekPositionFound = 0; */
456
	mpd_uint16 bitRate = 0;
Warren Dukes's avatar
Warren Dukes committed
457
	AacBuffer b;
458
	InputStream inStream;
459
	int initialized = 0;
460

Avuton Olrich's avatar
Avuton Olrich committed
461 462
	if ((totalTime = getAacFloatTotalTime(path)) < 0)
		return -1;
463

Avuton Olrich's avatar
Avuton Olrich committed
464 465
	if (openInputStream(&inStream, path) < 0)
		return -1;
466

Max Kellermann's avatar
Max Kellermann committed
467
	initAacBuffer(&b, mpd_decoder, &inStream);
468
	aac_parse_header(&b, NULL);
469 470 471 472 473 474 475 476 477 478 479

	decoder = faacDecOpen();

	config = faacDecGetCurrentConfiguration(decoder);
	config->outputFormat = FAAD_FMT_16BIT;
#ifdef HAVE_FAACDECCONFIGURATION_DOWNMATRIX
	config->downMatrix = 1;
#endif
#ifdef HAVE_FAACDECCONFIGURATION_DONTUPSAMPLEIMPLICITSBR
	config->dontUpSampleImplicitSBR = 0;
#endif
Avuton Olrich's avatar
Avuton Olrich committed
480
	faacDecSetConfiguration(decoder, config);
481

Warren Dukes's avatar
Warren Dukes committed
482
	fillAacBuffer(&b);
483 484

#ifdef HAVE_FAAD_BUFLEN_FUNCS
Avuton Olrich's avatar
Avuton Olrich committed
485 486
	bread = faacDecInit(decoder, b.buffer, b.bytesIntoBuffer,
			    &sampleRate, &channels);
487
#else
Avuton Olrich's avatar
Avuton Olrich committed
488
	bread = faacDecInit(decoder, b.buffer, &sampleRate, &channels);
489
#endif
Avuton Olrich's avatar
Avuton Olrich committed
490
	if (bread < 0) {
Warren Dukes's avatar
Warren Dukes committed
491
		ERROR("Error not a AAC stream.\n");
492
		faacDecClose(decoder);
Avuton Olrich's avatar
Avuton Olrich committed
493 494
		if (b.buffer)
			free(b.buffer);
495 496 497
		return -1;
	}

498
	audio_format.bits = 16;
499

500
	file_time = 0.0;
501

Avuton Olrich's avatar
Avuton Olrich committed
502
	advanceAacBuffer(&b, bread);
Warren Dukes's avatar
Warren Dukes committed
503

504
	while (1) {
505 506
		fillAacBuffer(&b);

507
		if (b.bytesIntoBuffer == 0)
508
			break;
509

510
#ifdef HAVE_FAAD_BUFLEN_FUNCS
Avuton Olrich's avatar
Avuton Olrich committed
511 512
		sampleBuffer = faacDecDecode(decoder, &frameInfo, b.buffer,
					     b.bytesIntoBuffer);
513
#else
Avuton Olrich's avatar
Avuton Olrich committed
514
		sampleBuffer = faacDecDecode(decoder, &frameInfo, b.buffer);
515
#endif
516

Avuton Olrich's avatar
Avuton Olrich committed
517
		if (frameInfo.error > 0) {
518
			ERROR("error decoding AAC file: %s\n", path);
519
			ERROR("faad2 error: %s\n",
Avuton Olrich's avatar
Avuton Olrich committed
520
			      faacDecGetErrorMessage(frameInfo.error));
521 522
			break;
		}
523 524 525 526
#ifdef HAVE_FAACDECFRAMEINFO_SAMPLERATE
		sampleRate = frameInfo.samplerate;
#endif

527
		if (!initialized) {
528 529
			audio_format.channels = frameInfo.channels;
			audio_format.sampleRate = sampleRate;
530 531
			decoder_initialized(mpd_decoder, &audio_format,
					    totalTime);
532
			initialized = 1;
Warren Dukes's avatar
Warren Dukes committed
533 534
		}

Avuton Olrich's avatar
Avuton Olrich committed
535
		advanceAacBuffer(&b, frameInfo.bytesconsumed);
Warren Dukes's avatar
Warren Dukes committed
536

Warren Dukes's avatar
Warren Dukes committed
537
		sampleCount = (unsigned long)(frameInfo.samples);
538

Avuton Olrich's avatar
Avuton Olrich committed
539 540 541 542
		if (sampleCount > 0) {
			bitRate = frameInfo.bytesconsumed * 8.0 *
			    frameInfo.channels * sampleRate /
			    frameInfo.samples / 1000 + 0.5;
543
			file_time +=
Avuton Olrich's avatar
Avuton Olrich committed
544 545
			    (float)(frameInfo.samples) / frameInfo.channels /
			    sampleRate;
546
		}
Avuton Olrich's avatar
Avuton Olrich committed
547 548

		sampleBufferLen = sampleCount * 2;
549

Max Kellermann's avatar
Max Kellermann committed
550 551 552
		decoder_data(mpd_decoder, NULL, 0, sampleBuffer,
			     sampleBufferLen, file_time,
			     bitRate, NULL);
553 554 555
		if (decoder_get_command(mpd_decoder) == DECODE_COMMAND_SEEK) {
			decoder_seek_error(mpd_decoder);
		} else if (decoder_get_command(mpd_decoder) == DECODE_COMMAND_STOP)
556
			break;
Warren Dukes's avatar
Warren Dukes committed
557
	}
558

559
	decoder_flush(mpd_decoder);
560

Warren Dukes's avatar
Warren Dukes committed
561
	faacDecClose(decoder);
Avuton Olrich's avatar
Avuton Olrich committed
562 563
	if (b.buffer)
		free(b.buffer);
Warren Dukes's avatar
Warren Dukes committed
564

565
	if (!initialized)
Avuton Olrich's avatar
Avuton Olrich committed
566
		return -1;
Warren Dukes's avatar
Warren Dukes committed
567

568 569
	if (decoder_get_command(mpd_decoder) == DECODE_COMMAND_SEEK) {
		decoder_seek_error(mpd_decoder);
Avuton Olrich's avatar
Avuton Olrich committed
570
	}
571 572 573 574

	return 0;
}

575
static struct tag *aacTagDup(char *file)
Avuton Olrich's avatar
Avuton Olrich committed
576
{
577
	struct tag *ret = NULL;
578
	int file_time = getAacTotalTime(file);
Warren Dukes's avatar
Warren Dukes committed
579

580
	if (file_time >= 0) {
581 582
		if ((ret = tag_id3_load(file)) == NULL)
			ret = tag_new();
583
		ret->time = file_time;
Avuton Olrich's avatar
Avuton Olrich committed
584 585 586
	} else {
		DEBUG("aacTagDup: Failed to get total song time from: %s\n",
		      file);
587
	}
Warren Dukes's avatar
Warren Dukes committed
588 589 590 591

	return ret;
}

Max Kellermann's avatar
Max Kellermann committed
592 593
static const char *aac_suffixes[] = { "aac", NULL };
static const char *aac_mimeTypes[] = { "audio/aac", "audio/aacp", NULL };
Warren Dukes's avatar
Warren Dukes committed
594

595
struct decoder_plugin aacPlugin = {
Avuton Olrich's avatar
Avuton Olrich committed
596 597
	"aac",
	NULL,
598 599
	NULL,
	NULL,
600
	aac_stream_decode,
Avuton Olrich's avatar
Avuton Olrich committed
601 602
	aac_decode,
	aacTagDup,
603
	INPUT_PLUGIN_STREAM_FILE | INPUT_PLUGIN_STREAM_URL,
604 605
	aac_suffixes,
	aac_mimeTypes
Warren Dukes's avatar
Warren Dukes committed
606 607 608 609
};

#else

610
struct decoder_plugin aacPlugin;
Warren Dukes's avatar
Warren Dukes committed
611

Avuton Olrich's avatar
Avuton Olrich committed
612
#endif /* HAVE_FAAD */