summaryrefslogtreecommitdiff
path: root/plugins/aac
diff options
context:
space:
mode:
authorGravatar Viktor Semykin <thesame.ml@gmail.com>2010-07-04 15:09:02 +0300
committerGravatar Viktor Semykin <thesame.ml@gmail.com>2010-07-04 15:09:02 +0300
commitdbca894cb0a4872481e0026b81d2b3ae083d1485 (patch)
treec510fa549a27e761627195a2bca667bd6796e435 /plugins/aac
parent05ae679fd8e6762f98d8582f75f05a09f9a7c71b (diff)
parent3793f58d388dd1e26e6edf7bd548ed17fc588ae1 (diff)
merged with devel
Diffstat (limited to 'plugins/aac')
-rw-r--r--plugins/aac/Makefile.am9
-rw-r--r--plugins/aac/aac.c725
-rw-r--r--plugins/aac/aac_parser.c100
-rw-r--r--plugins/aac/aac_parser.h32
4 files changed, 866 insertions, 0 deletions
diff --git a/plugins/aac/Makefile.am b/plugins/aac/Makefile.am
new file mode 100644
index 00000000..a10ff510
--- /dev/null
+++ b/plugins/aac/Makefile.am
@@ -0,0 +1,9 @@
+if HAVE_AAC
+aacdir = $(libdir)/$(PACKAGE)
+pkglib_LTLIBRARIES = aac.la
+aac_la_SOURCES = aac.c aac_parser.c aac_parser.h
+aac_la_LDFLAGS = -module
+
+aac_la_LIBADD = $(LDADD) $(FAAD2_LIBS)
+AM_CFLAGS = $(CFLAGS) -std=c99
+endif
diff --git a/plugins/aac/aac.c b/plugins/aac/aac.c
new file mode 100644
index 00000000..f59c7308
--- /dev/null
+++ b/plugins/aac/aac.c
@@ -0,0 +1,725 @@
+/*
+ DeaDBeeF - ultimate music player for GNU/Linux systems with X11
+ Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net>
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <string.h>
+#include <stdio.h>
+#include <neaacdec.h>
+#ifdef HAVE_CONFIG_H
+#include "../../config.h"
+#endif
+#include <mp4ff.h>
+#include <stdlib.h>
+#include "../../deadbeef.h"
+#include "aac_parser.h"
+
+#define min(x,y) ((x)<(y)?(x):(y))
+#define max(x,y) ((x)>(y)?(x):(y))
+
+#define trace(...) { fprintf(stderr, __VA_ARGS__); }
+//#define trace(fmt,...)
+
+static DB_decoder_t plugin;
+static DB_functions_t *deadbeef;
+
+#define AAC_BUFFER_SIZE 8192
+#define OUT_BUFFER_SIZE 100000
+
+typedef struct {
+ DB_fileinfo_t info;
+ NeAACDecHandle dec;
+ DB_FILE *file;
+ mp4ff_t *mp4file;
+ mp4ff_callback_t mp4reader;
+ int mp4track;
+ int mp4sample;
+ int mp4framesize;
+ int skipsamples;
+ int startsample;
+ int endsample;
+ int currentsample;
+ char buffer[AAC_BUFFER_SIZE];
+ int remaining;
+ int faad_channels;
+ char out_buffer[OUT_BUFFER_SIZE];
+ int out_remaining;
+} aac_info_t;
+
+// allocate codec control structure
+static DB_fileinfo_t *
+aac_open (void) {
+ DB_fileinfo_t *_info = malloc (sizeof (aac_info_t));
+ aac_info_t *info = (aac_info_t *)_info;
+ memset (info, 0, sizeof (aac_info_t));
+ return _info;
+}
+
+static uint32_t
+aac_fs_read (void *user_data, void *buffer, uint32_t length) {
+// trace ("aac_fs_read\n");
+ DB_FILE *fp = (DB_FILE *)user_data;
+ return deadbeef->fread (buffer, 1, length, fp);
+}
+
+static uint32_t
+aac_fs_seek (void *user_data, uint64_t position) {
+// trace ("aac_fs_seek\n");
+ DB_FILE *fp = (DB_FILE *)user_data;
+ return deadbeef->fseek (fp, position, SEEK_SET);
+}
+
+static int
+parse_aac_stream(DB_FILE *fp, int *samplerate, int *channels, float *duration, int *totalsamples)
+{
+ int skip = deadbeef->junk_get_leading_size (fp);
+ if (skip >= 0) {
+ deadbeef->fseek (fp, skip, SEEK_SET);
+ }
+ int offs = deadbeef->ftell (fp);
+ int fsize = deadbeef->fgetlength (fp);
+ if (skip > 0) {
+ fsize -= skip;
+ }
+
+ uint8_t buf[ADTS_HEADER_SIZE*8];
+
+ int nsamples = 0;
+ int stream_sr = 0;
+ int stream_ch = 0;
+
+ int eof = 0;
+ int bufsize = 0;
+ int remaining = 0;
+
+ int frame = 0;
+
+ do {
+ int size = sizeof (buf) - bufsize;
+ if (deadbeef->fread (buf + bufsize, 1, size, fp) != size) {
+ trace ("parse_aac_stream: eof\n");
+ break;
+ }
+ bufsize = sizeof (buf);
+
+ int channels, samplerate, bitrate, samples;
+ size = aac_sync (buf, &channels, &samplerate, &bitrate, &samples);
+ if (size == 0) {
+ memmove (buf, buf+1, sizeof (buf)-1);
+ bufsize--;
+ continue;
+ }
+ else {
+ //trace ("aac: frame #%d sync: %d %d %d %d %d\n", frame, channels, samplerate, bitrate, samples, size);
+ frame++;
+ nsamples += samples;
+ if (!stream_sr) {
+ stream_sr = samplerate;
+ }
+ if (!stream_ch) {
+ stream_ch = channels;
+ }
+ if (deadbeef->fseek (fp, size-sizeof(buf), SEEK_CUR) == -1) {
+ trace ("parse_aac_stream: invalid seek %d\n", size-sizeof(buf));
+ break;
+ }
+ bufsize = 0;
+ }
+ } while (totalsamples || frame < 1000);
+
+ if (!frame || !stream_sr || !nsamples) {
+ return -1;
+ }
+
+ *samplerate = stream_sr;
+ *channels = stream_ch;
+
+ if (totalsamples) {
+ *totalsamples = nsamples;
+ *duration = nsamples / (float)stream_sr;
+ trace ("aac: duration=%f (%d samples @ %d Hz), fsize=%d, nframes=%d\n", *duration, *totalsamples, stream_sr, fsize, frame);
+ }
+ else {
+ int pos = deadbeef->ftell (fp);
+ int totalsamples = (double)fsize / (pos-offs) * nsamples;
+ *duration = totalsamples / (float)stream_sr;
+ trace ("aac: duration=%f (%d samples @ %d Hz), fsize=%d\n", *duration, totalsamples, stream_sr, fsize);
+ }
+
+ return 0;
+}
+
+// returns -1 for error, 0 for mp4, 1 for aac
+int
+aac_probe (DB_FILE *fp, float *duration, int *samplerate, int *channels, int *totalsamples, int *mp4track) {
+ // try mp4
+
+ mp4ff_callback_t cb = {
+ .read = aac_fs_read,
+ .write = NULL,
+ .seek = aac_fs_seek,
+ .truncate = NULL,
+ .user_data = fp
+ };
+
+ *duration = -1;
+ mp4ff_t *mp4 = mp4ff_open_read (&cb);
+ if (!mp4) {
+ trace ("not an mp4 file\n");
+ return -1;
+ }
+ int ntracks = mp4ff_total_tracks (mp4);
+ if (ntracks > 0) {
+ trace ("m4a container detected, ntracks=%d\n", ntracks);
+ int i = -1;
+ trace ("looking for mp4 data...\n");
+ for (i = 0; i < ntracks; i++) {
+ unsigned char* buff = 0;
+ unsigned int buff_size = 0;
+ mp4AudioSpecificConfig mp4ASC;
+ mp4ff_get_decoder_config(mp4, i, &buff, &buff_size);
+ if(buff){
+ int rc = AudioSpecificConfig(buff, buff_size, &mp4ASC);
+ free(buff);
+ if(rc < 0)
+ continue;
+ break;
+ }
+ }
+
+ if (i != ntracks)
+ {
+ trace ("mp4 track: %d\n", i);
+ *samplerate = mp4ff_get_sample_rate (mp4, i);
+ *channels = mp4ff_get_channel_count (mp4, i);
+ int samples = mp4ff_num_samples(mp4, i);
+ trace ("mp4 nsamples=%d, samplerate=%d\n", samples * 1024, *samplerate);
+ *duration = (float)samples * 1024 / (*samplerate);
+
+ if (totalsamples) {
+ *totalsamples = samples;
+ }
+ if (mp4track) {
+ *mp4track = i;
+ }
+ return 0;
+ }
+ }
+ mp4ff_close (mp4);
+ trace ("mp4 track not found, looking for aac stream...\n");
+
+ // not an mp4, try raw aac
+ deadbeef->rewind (fp);
+ if (parse_aac_stream (fp, samplerate, channels, duration, totalsamples) == -1) {
+ trace ("aac stream not found\n");
+ return -1;
+ }
+ trace ("found aac stream\n");
+ return 1;
+}
+
+
+static int
+aac_init (DB_fileinfo_t *_info, DB_playItem_t *it) {
+ aac_info_t *info = (aac_info_t *)_info;
+
+ info->file = deadbeef->fopen (it->fname);
+ if (!info->file) {
+ return -1;
+ }
+
+ // probe
+ float duration = -1;
+ int samplerate = -1;
+ int channels = -1;
+ int totalsamples = -1;
+
+ int skip = deadbeef->junk_get_leading_size (info->file);
+ if (skip >= 0) {
+ deadbeef->fseek (info->file, skip, SEEK_SET);
+ }
+ int offs = deadbeef->ftell (info->file);
+
+ info->mp4track = -1;
+// int res = aac_probe (info->file, &duration, &samplerate, &channels, &totalsamples, &mp4track);
+// if (res == -1) {
+// return -1;
+// }
+ info->mp4reader.read = aac_fs_read;
+ info->mp4reader.write = NULL;
+ info->mp4reader.seek = aac_fs_seek;
+ info->mp4reader.truncate = NULL;
+ info->mp4reader.user_data = info->file;
+
+ info->mp4file = mp4ff_open_read (&info->mp4reader);
+ if (info->mp4file) {
+ int ntracks = mp4ff_total_tracks (info->mp4file);
+ if (ntracks > 0) {
+ trace ("m4a container detected, ntracks=%d\n", ntracks);
+ int i = -1;
+ trace ("looking for mp4 data...\n");
+ unsigned char* buff = 0;
+ unsigned int buff_size = 0;
+ for (i = 0; i < ntracks; i++) {
+ mp4AudioSpecificConfig mp4ASC;
+ mp4ff_get_decoder_config (info->mp4file, i, &buff, &buff_size);
+ if(buff){
+ int rc = AudioSpecificConfig(buff, buff_size, &mp4ASC);
+ if(rc < 0)
+ continue;
+ break;
+ }
+ }
+
+ if (i != ntracks && buff)
+ {
+ trace ("mp4 track: %d\n", i);
+ samplerate = mp4ff_get_sample_rate (info->mp4file, i);
+ channels = mp4ff_get_channel_count (info->mp4file, i);
+ int samples = mp4ff_num_samples(info->mp4file, i);
+ trace ("mp4 nsamples=%d, samplerate=%d\n", samples * 1024, samplerate);
+ duration = (float)samples * 1024 / samplerate;
+ totalsamples = samples * 1024;
+ info->mp4track = i;
+
+ // init mp4 decoding
+ info->dec = NeAACDecOpen ();
+ unsigned long srate;
+ unsigned char ch;
+ if (NeAACDecInit2(info->dec, buff, buff_size, &srate, &ch) < 0) {
+ trace ("NeAACDecInit2 returned error\n");
+ return -1;
+ }
+ info->faad_channels = ch;
+ mp4AudioSpecificConfig mp4ASC;
+ if (NeAACDecAudioSpecificConfig(buff, buff_size, &mp4ASC) >= 0)
+ {
+ info->mp4framesize = 1024;
+ if (mp4ASC.frameLengthFlag == 1) {
+ info->mp4framesize = 960;
+ }
+ if (mp4ASC.sbr_present_flag == 1) {
+ info->mp4framesize *= 2;
+ }
+ }
+ free (buff);
+ }
+ else {
+ mp4ff_close (info->mp4file);
+ info->mp4file = NULL;
+ }
+ }
+ else {
+ mp4ff_close (info->mp4file);
+ info->mp4file = NULL;
+ }
+ }
+ if (!info->mp4file) {
+ trace ("mp4 track not found, looking for aac stream...\n");
+
+ // not an mp4, try raw aac
+ deadbeef->rewind (info->file);
+ if (parse_aac_stream (info->file, &samplerate, &channels, &duration, &totalsamples) == -1) {
+ trace ("aac stream not found\n");
+ return -1;
+ }
+ trace ("found aac stream\n");
+ }
+
+// duration = (float)totalsamples / samplerate;
+// deadbeef->pl_set_item_duration (it, duration);
+
+ _info->bps = 16;
+ _info->channels = channels;
+ _info->samplerate = samplerate;
+ _info->plugin = &plugin;
+
+ deadbeef->fseek (info->file, offs, SEEK_SET);
+
+ if (!info->mp4file) {
+ info->dec = NeAACDecOpen ();
+
+ info->remaining = deadbeef->fread (info->buffer, 1, AAC_BUFFER_SIZE, info->file);
+
+ NeAACDecConfigurationPtr conf = NeAACDecGetCurrentConfiguration (info->dec);
+ conf->dontUpSampleImplicitSBR = 1;
+ NeAACDecSetConfiguration (info->dec, conf);
+ unsigned long srate;
+ unsigned char ch;
+ int consumed = NeAACDecInit (info->dec, info->buffer, info->remaining, &srate, &ch);
+ if (consumed < 0) {
+ trace ("NeAACDecInit returned %d\n", consumed);
+ return -1;
+ }
+ if (consumed > info->remaining) {
+ trace ("NeAACDecInit consumed more than available! wtf?\n");
+ return -1;
+ }
+ if (consumed == info->remaining) {
+ info->remaining = 0;
+ }
+ else if (consumed > 0) {
+ memmove (info->buffer, info->buffer + consumed, info->remaining - consumed);
+ info->remaining -= consumed;
+ }
+ info->faad_channels = ch;
+ }
+
+ if (it->endsample > 0) {
+ info->startsample = it->startsample;
+ info->endsample = it->endsample;
+ plugin.seek_sample (_info, 0);
+ }
+ else {
+ info->startsample = 0;
+ info->endsample = totalsamples-1;
+ }
+
+
+// _info->samplerate = srate;
+
+ // recalculate duration
+// trace ("duration=%f, totalsamples = %d\n", duration, totalsamples);
+// trace ("NeAACDecInit returned samplerate=%d, channels=%d\n", (int)srate, (int)ch);
+
+ return 0;
+}
+
+static void
+aac_free (DB_fileinfo_t *_info) {
+ aac_info_t *info = (aac_info_t *)_info;
+ if (info) {
+ if (info->file) {
+ deadbeef->fclose (info->file);
+ }
+ if (info->mp4file) {
+ mp4ff_close (info->mp4file);
+ }
+ if (info->dec) {
+ NeAACDecClose (info->dec);
+ }
+ free (info);
+ }
+}
+
+static int
+aac_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) {
+ aac_info_t *info = (aac_info_t *)_info;
+ if (info->currentsample + size / (2 * _info->channels) > info->endsample) {
+ size = (info->endsample - info->currentsample + 1) * 2 * _info->channels;
+ if (size <= 0) {
+ return 0;
+ }
+ }
+
+ int initsize = size;
+ int eof = 0;
+ int ch = min (_info->channels, 2);
+ int sample_size = ch * (_info->bps >> 3);
+
+ while (size > 0) {
+ if (info->skipsamples > 0 && info->out_remaining > 0) {
+ int skip = min (info->out_remaining, info->skipsamples);
+ if (skip < info->out_remaining) {
+ memmove (info->out_buffer, info->out_buffer + skip * 2 * info->faad_channels, (info->out_remaining - skip) * 2 * info->faad_channels);
+ }
+ info->out_remaining -= skip;
+ info->skipsamples -= skip;
+ }
+ if (info->out_remaining > 0) {
+ int n = size / sample_size;
+ n = min (info->out_remaining, n);
+
+ char *src = info->out_buffer;
+ for (int i = 0; i < n; i++) {
+ memcpy (bytes, src, sample_size);
+ bytes += sample_size;
+ src += info->faad_channels * 2;
+ }
+
+ size -= n * sample_size;
+ if (n == info->out_remaining) {
+ info->out_remaining = 0;
+ }
+ else {
+ memmove (info->out_buffer, src, (info->out_remaining - n) * info->faad_channels * 2);
+ info->out_remaining -= n;
+ }
+ continue;
+ }
+
+ char *samples = NULL;
+ NeAACDecFrameInfo frame_info;
+
+ if (info->mp4file) {
+ unsigned char *buffer = NULL;
+ int buffer_size = 0;
+ int rc = mp4ff_read_sample (info->mp4file, info->mp4track, info->mp4sample, &buffer, &buffer_size);
+ if (rc == 0) {
+ break;
+ }
+ info->mp4sample++;
+ samples = NeAACDecDecode(info->dec, &frame_info, buffer, buffer_size);
+ free (buffer);
+ if (!samples) {
+ break;
+ }
+ }
+ else {
+ if (info->remaining < AAC_BUFFER_SIZE) {
+ size_t res = deadbeef->fread (info->buffer + info->remaining, 1, AAC_BUFFER_SIZE-info->remaining, info->file);
+ if (res == 0) {
+ eof = 1;
+ }
+ info->remaining += res;
+ }
+
+ //trace ("NeAACDecDecode %d bytes %d offs\n", info->remaining, deadbeef->ftell (info->file));
+ samples = NeAACDecDecode (info->dec, &frame_info, info->buffer, info->remaining);
+ if (!samples) {
+ trace ("NeAACDecDecode failed\n");
+ break;
+ }
+ int consumed = frame_info.bytesconsumed;
+ if (consumed > info->remaining) {
+ trace ("NeAACDecDecode consumed more than available! wtf?\n");
+ break;
+ }
+ if (consumed == info->remaining) {
+ info->remaining = 0;
+ }
+ else if (consumed > 0) {
+ memmove (info->buffer, info->buffer + consumed, info->remaining - consumed);
+ info->remaining -= consumed;
+ }
+ }
+
+ if (frame_info.samples > 0) {
+ memcpy (info->out_buffer, samples, frame_info.samples * 2);
+ info->out_remaining = frame_info.samples / frame_info.channels;
+ }
+ }
+
+ info->currentsample += (initsize-size) / sample_size;
+ if (size != 0) {
+ trace ("aac_read_int16 eof\n");
+ }
+ return initsize-size;
+}
+
+// returns -1 on error, 0 on success
+int
+seek_raw_aac (aac_info_t *info, int sample) {
+ deadbeef->rewind (info->file);
+ int skip = deadbeef->junk_get_leading_size (info->file);
+ if (skip >= 0) {
+ deadbeef->fseek (info->file, skip, SEEK_SET);
+ }
+
+ int offs = deadbeef->ftell (info->file);
+ uint8_t buf[ADTS_HEADER_SIZE*8];
+
+ int nsamples = 0;
+ int stream_sr = 0;
+ int stream_ch = 0;
+
+ int eof = 0;
+ int bufsize = 0;
+ int remaining = 0;
+
+ int frame = 0;
+
+ int frame_samples = 0;
+ int curr_sample = 0;
+
+ do {
+ curr_sample += frame_samples;
+ int size = sizeof (buf) - bufsize;
+ if (deadbeef->fread (buf + bufsize, 1, size, info->file) != size) {
+ trace ("seek_raw_aac: eof\n");
+ break;
+ }
+ bufsize = sizeof (buf);
+
+ int channels, samplerate, bitrate;
+ size = aac_sync (buf, &channels, &samplerate, &bitrate, &frame_samples);
+ if (size == 0) {
+ memmove (buf, buf+1, sizeof (buf)-1);
+ bufsize--;
+ continue;
+ }
+ else {
+ //trace ("aac: frame #%d sync: %d %d %d %d %d\n", frame, channels, samplerate, bitrate, samples, size);
+ frame++;
+ if (deadbeef->fseek (info->file, size-sizeof(buf), SEEK_CUR) == -1) {
+ trace ("seek_raw_aac: invalid seek %d\n", size-sizeof(buf));
+ break;
+ }
+ bufsize = 0;
+ }
+ } while (curr_sample + frame_samples < sample);
+
+ if (curr_sample + frame_samples < sample) {
+ return -1;
+ }
+
+ return sample - curr_sample;
+}
+
+static int
+aac_seek_sample (DB_fileinfo_t *_info, int sample) {
+ aac_info_t *info = (aac_info_t *)_info;
+
+ sample += info->startsample;
+ if (info->mp4file) {
+ info->mp4sample = sample / (info->mp4framesize-1);
+ info->skipsamples = sample - info->mp4sample * (info->mp4framesize-1);
+ }
+ else {
+ int res = seek_raw_aac (info, sample);
+ if (res < 0) {
+ return -1;
+ }
+ info->skipsamples = res;
+ }
+ info->remaining = 0;
+ info->out_remaining = 0;
+ info->currentsample = sample;
+ _info->readpos = (float)(info->currentsample - info->startsample) / _info->samplerate;
+ return 0;
+}
+
+static int
+aac_seek (DB_fileinfo_t *_info, float t) {
+ return aac_seek_sample (_info, t * _info->samplerate);
+}
+
+static DB_playItem_t *
+aac_insert (DB_playItem_t *after, const char *fname) {
+ trace ("adding %s\n", fname);
+ DB_FILE *fp = deadbeef->fopen (fname);
+ if (!fp) {
+ trace ("not found\n");
+ return NULL;
+ }
+ if (fp->vfs->streaming) {
+ trace ("no streaming aac yet (%s)\n", fname);
+ deadbeef->fclose (fp);
+ return NULL;
+ }
+
+ int skip = deadbeef->junk_get_leading_size (fp);
+ if (skip >= 0) {
+ deadbeef->fseek (fp, skip, SEEK_SET);
+ }
+
+ const char *ftype = NULL;
+ float duration;
+ int samplerate;
+ int channels;
+ int totalsamples;
+
+ // slowwww!
+ int res = aac_probe (fp, &duration, &samplerate, &channels, &totalsamples, NULL);
+ if (res == -1) {
+ deadbeef->fclose (fp);
+ return NULL;
+ }
+ else if (res == 0) {
+ ftype = plugin.filetypes[1];
+ }
+ else if (res == 1) {
+ ftype = plugin.filetypes[0];
+ }
+
+ DB_playItem_t *it = deadbeef->pl_item_alloc ();
+ it->decoder_id = deadbeef->plug_get_decoder_id (plugin.plugin.id);
+ it->fname = strdup (fname);
+ it->filetype = ftype;
+ deadbeef->pl_set_item_duration (it, duration);
+// trace ("duration: %f sec\n", duration);
+
+ // read tags
+ if (ftype == "aac") {
+ int apeerr = deadbeef->junk_apev2_read (it, fp);
+ int v2err = deadbeef->junk_id3v2_read (it, fp);
+ int v1err = deadbeef->junk_id3v1_read (it, fp);
+ }
+
+ deadbeef->fclose (fp);
+
+ // embedded cue
+ deadbeef->pl_lock ();
+ const char *cuesheet = deadbeef->pl_find_meta (it, "cuesheet");
+ DB_playItem_t *cue = NULL;
+
+ if (cuesheet) {
+ cue = deadbeef->pl_insert_cue_from_buffer (after, it, cuesheet, strlen (cuesheet), totalsamples, samplerate);
+ if (cue) {
+ deadbeef->pl_item_unref (it);
+ deadbeef->pl_item_unref (cue);
+ deadbeef->pl_unlock ();
+ return cue;
+ }
+ }
+ deadbeef->pl_unlock ();
+
+ cue = deadbeef->pl_insert_cue (after, it, totalsamples, samplerate);
+ if (cue) {
+ deadbeef->pl_item_unref (it);
+ deadbeef->pl_item_unref (cue);
+ return cue;
+ }
+
+ deadbeef->pl_add_meta (it, "title", NULL);
+
+ after = deadbeef->pl_insert_item (after, it);
+ deadbeef->pl_item_unref (it);
+ return after;
+}
+
+static const char * exts[] = { "aac", "mp4", "m4a", NULL };
+static const char *filetypes[] = { "RAW AAC", "MP4 AAC", NULL };
+
+// define plugin interface
+static DB_decoder_t plugin = {
+ DB_PLUGIN_SET_API_VERSION
+ .plugin.version_major = 0,
+ .plugin.version_minor = 1,
+ .plugin.type = DB_PLUGIN_DECODER,
+ .plugin.id = "aac",
+ .plugin.name = "AAC decoder based on FAAD2",
+ .plugin.descr = "aac (m4a, mp4, ...) player",
+ .plugin.author = "Alexey Yakovenko",
+ .plugin.email = "waker@users.sourceforge.net",
+ .plugin.website = "http://deadbeef.sf.net",
+ .open = aac_open,
+ .init = aac_init,
+ .free = aac_free,
+ .read_int16 = aac_read_int16,
+ .seek = aac_seek,
+ .seek_sample = aac_seek_sample,
+ .insert = aac_insert,
+ .exts = exts,
+ .filetypes = filetypes
+};
+
+DB_plugin_t *
+aac_load (DB_functions_t *api) {
+ deadbeef = api;
+ return DB_PLUGIN (&plugin);
+}
diff --git a/plugins/aac/aac_parser.c b/plugins/aac/aac_parser.c
new file mode 100644
index 00000000..32571aff
--- /dev/null
+++ b/plugins/aac/aac_parser.c
@@ -0,0 +1,100 @@
+/*
+ DeaDBeeF - ultimate music player for GNU/Linux systems with X11
+ Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net>
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+#include <stdio.h>
+#include "aac_parser.h"
+
+#define min(x,y) ((x)<(y)?(x):(y))
+#define max(x,y) ((x)>(y)?(x):(y))
+
+#define trace(...) { fprintf(stderr, __VA_ARGS__); }
+//#define trace(fmt,...)
+
+static const int aac_sample_rates[16] = {
+ 96000, 88200, 64000, 48000, 44100, 32000,
+ 24000, 22050, 16000, 12000, 11025, 8000, 7350
+};
+
+static const int aac_channels[8] = {
+ 0, 1, 2, 3, 4, 5, 6, 8
+};
+
+int
+aac_sync(const uint8_t *buf, int *channels, int *sample_rate, int *bit_rate, int *samples)
+{
+ int size, rdb;
+
+ // 12 sync bits
+ if (buf[0] != 0xff || (buf[1]&0xf0) != 0xf0) {
+ //trace ("unsync\n");
+ return 0;
+ }
+
+ int id = (buf[1] & 0x08) >> 7;
+
+ int version = id == 0 ? 4 : 2;
+
+ int layer = (buf[1] & 0x06) >> 5;
+ int protection_abs = (buf[1] & 0x01);
+
+ int header_size = protection_abs > 0 ? 7 : 9;
+
+ int profile_objecttype = (buf[2] & 0xC0) >> 6;
+
+ //const char *profiles[4] = {
+ // "0 Main profile AAC MAIN",
+ // "1 Low Complexity profile (LC)AAC LC",
+ // "2 Scalable Sample Rate profile (SSR)AAC SSR",
+ // "3 (reserved)AAC LTP"
+ //};
+ //trace ("profile: %s\n", profiles[profile_objecttype]);
+
+ int sample_freq_index = (buf[2] & 0x3C) >> 2;
+ if (!aac_sample_rates[sample_freq_index]) {
+ //trace ("invalid samplerate\n");
+ return 0;
+ }
+ //trace ("samplerate %d (#%d)\n", aac_sample_rates[sample_freq_index], sample_freq_index);
+ int private_bit = (buf[2] & 0x02) >> 1;
+
+ int channel_conf = ((buf[2] & 0x01) << 2) | ((buf[3] & 0xC0) >> 6);
+ if (!aac_channels[channel_conf]) {
+ //trace ("invalid channels\n");
+ return 0;
+ }
+ //trace ("channels %d\n", aac_channels[channel_conf]);
+ int orig_copy = (buf[3] & 0x20) >> 5;
+ int home = (buf[3] & 0x10) >> 4;
+ int copyright_ident_bit = (buf[3] & 0x08) >> 3;
+ int copyright_ident_start = (buf[3] & 0x04) >> 2;
+ size = ((buf[3] & 0x03) << 11) | (buf[4] << 3) | ((buf[5] & 0xE0) >> 5);
+ if(size < ADTS_HEADER_SIZE) {
+ //trace ("invalid size\n");
+ return 0;
+ }
+ int adts_buffer_fullness = ((buf[5] & 0x1F) << 3) | ((buf[6] & 0xFC) >> 2);
+ rdb = buf[7] & 0x03;
+
+ *channels = aac_channels[channel_conf];
+ *sample_rate = aac_sample_rates[sample_freq_index];
+ *samples = rdb * 1024;
+ *bit_rate = size * 8 * *sample_rate / *samples;
+
+ return size;
+}
+
diff --git a/plugins/aac/aac_parser.h b/plugins/aac/aac_parser.h
new file mode 100644
index 00000000..53e7916d
--- /dev/null
+++ b/plugins/aac/aac_parser.h
@@ -0,0 +1,32 @@
+/*
+ DeaDBeeF - ultimate music player for GNU/Linux systems with X11
+ Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net>
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __AAC_PARSER_H
+#define __AAC_PARSER_H
+
+#include <stdint.h>
+
+#define ADTS_HEADER_SIZE 7
+
+// buf size must be at least ADTS_HEADER_SIZE*8
+// returns frame size
+int
+aac_sync(const uint8_t *buf, int *channels, int *sample_rate, int *bit_rate, int *samples);
+
+#endif