shithub: aacdec

Download patch

ref: 428cc5c278aca667ae975a6476f8dda92a496b76
parent: d0d358260cb40bacb324cdc37c426a24a858e6be
author: menno <menno>
date: Sat Dec 13 17:27:58 EST 2003

foo_mp4: new gapless support, mp3 in mp4 decoding support

--- a/plugins/foo_mp4/convert.h
+++ b/plugins/foo_mp4/convert.h
@@ -30,7 +30,7 @@
 #include <mp4.h>
 #include <mp4av.h>
 #include <faad.h>
-#include "foobar2000/SDK/foobar2000.h"
+#include "../SDK/foobar2000.h"
 
 #define ADTS_HEADER_MAX_SIZE 10 // bytes
 
--- a/plugins/foo_mp4/foo_mp4.cpp
+++ /dev/null
@@ -1,1098 +1,0 @@
-/*
-** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding
-** Copyright (C) 2003 M. Bakker, Ahead Software AG, http://www.nero.com
-**
-** 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.
-**
-** Any non-GPL usage of this software or parts of this software is strictly
-** forbidden.
-**
-** Commercial non-GPL licensing of this software is possible.
-** For more info contact Ahead Software through [email protected].
-**
-** $Id: foo_mp4.cpp,v 1.76 2003/12/11 18:38:13 menno Exp $
-**/
-
-#include "foobar2000/SDK/foobar2000.h"
-#include "foobar2000/foo_input_std/id3v2_hacks.h"
-#define USE_TAGGING
-#include <mp4ff.h>
-#include <faad.h>
-
-//#define DBG_OUT(A) OutputDebugString(A)
-#define DBG_OUT(A)
-
-DECLARE_COMPONENT_VERSION ("MPEG-4 AAC decoder",
-                           "2.0",
-                           "Based on FAAD2 v" FAAD2_VERSION "\nCopyright (C) 2002-2003 http://www.audiocoding.com" );
-
-static const char *object_type_string(int type)
-{
-    static const char *types[31] = {
-        "AAC Main",
-        "AAC LC",
-        "AAC SSR",
-        "AAC LTP",
-        "AAC HE",
-        "AAC Scalable",
-        "TwinVQ",
-        "CELP",
-        "HVXC",
-        "Reserved",
-        "Reserved",
-        "TTSI",
-        "Main synthetic",
-        "Wavetable synthesis",
-        "General MIDI",
-        "Algorithmic Synthesis and Audio FX",
-        "ER AAC LC",
-        "Reserved",
-        "ER AAC LTP",
-        "ER AAC scalable",
-        "ER TwinVQ",
-        "ER BSAC",
-        "ER AAC LD",
-        "ER CELP",
-        "ER HVXC",
-        "ER HILN",
-        "ER Parametric",
-        "Reserved",
-        "Reserved",
-        "Reserved",
-        "Reserved",
-    };
-
-    if (type<1 || type>31) return NULL;
-
-    return types[type-1];
-}
-
-static int GetAACTrack(mp4ff_t *infile)
-{
-    /* find AAC track */
-    int i, rc;
-    int numTracks = mp4ff_total_tracks(infile);
-
-    for (i = 0; i < numTracks; i++)
-    {
-        unsigned char *buff = NULL;
-        unsigned int buff_size = 0;
-        mp4AudioSpecificConfig mp4ASC;
-
-        mp4ff_get_decoder_config(infile, i, &buff, &buff_size);
-
-        if (buff)
-        {
-            rc = AudioSpecificConfig(buff, buff_size, &mp4ASC);
-            free(buff);
-
-            if (rc < 0)
-                continue;
-            return i;
-        }
-    }
-
-    /* can't decode this */
-    return -1;
-}
-
-static const char * tech_info_fields[] = {"replaygain_track_gain","replaygain_track_peak","replaygain_album_gain","replaygain_album_peak","tool"};
-
-const char * check_tech_info(const char * name)
-{
-	unsigned n;
-	for(n=0;n<tabsize(tech_info_fields);n++)
-	{
-		if (!stricmp_utf8(name,tech_info_fields[n])) return tech_info_fields[n];
-	}
-	return 0;
-}
-
-
-static void meta_mp4_to_fb2k(const char * name,const char * value,file_info * out)
-{
-	{
-		const char * p_tech_info = check_tech_info(name);
-		if (p_tech_info)
-		{
-			out->info_set(p_tech_info,value);
-			return;
-		}
-	}
-
-	if (!stricmp_utf8(name,"track")) name = "tracknumber";
-
-	out->meta_add(name,value);
-}
-
-
-static void meta_fb2k_to_mp4(const char * name,const char * value,string_base & out_name,string_base & out_value)
-{
-	if (!stricmp_utf8(name,"tracknumber"))
-	{
-		out_name = "track";
-		out_value = value;
-	}
-	else
-	{
-		out_name = name;
-		out_value = value;
-	}
-}
-
-class input_mp4 : public input
-{
-public:
-    int track;
-    unsigned long m_samplerate;
-    unsigned char channels;
-    void *sample_buffer;
-
-    mp4ff_t *infile;
-    long sampleId, numSamples;
-
-	reader * mp4file;
-
-    faacDecHandle hDecoder;
-    faacDecFrameInfo frameInfo;
-    mp4AudioSpecificConfig mp4ASC;
-
-    unsigned char *buffer;
-    unsigned int buffer_size;
-
-
-    /* for gapless decoding */
-    unsigned int framesize;
-	mp4ff_callback_t mp4cb;
-	
-	double m_length;
-	uint32_t m_timescale;
-	uint32_t m_skip_samples;
-	uint32_t m_skip_frames;
-	bool m_firstframe;
-
-    static uint32_t callback_read(void *udata, void *buffer, uint32_t length)
-	{
-		return reinterpret_cast<input_mp4*>(udata)->mp4file->read(buffer,length);
-	}
-
-    static uint32_t callback_write(void *udata, void *buffer, uint32_t length)
-	{
-		return reinterpret_cast<input_mp4*>(udata)->mp4file->write(buffer,length);
-	}
-
-    static uint32_t callback_seek(void *udata, uint64_t position)
-	{
-		return reinterpret_cast<input_mp4*>(udata)->mp4file->seek(position) ? 1 : 0;
-	}
-	
-	static uint32_t callback_truncate(void *udata)
-	{
-		return reinterpret_cast<input_mp4*>(udata)->mp4file->set_eof() ? 1 : 0;
-	}
-	
-	void cleanup()
-	{
-		if (infile) {mp4ff_close(infile);infile=0;}
-		if (hDecoder) {faacDecClose(hDecoder);hDecoder=0;}
-	}
-
-    input_mp4()
-    {
-		mp4cb.user_data = reinterpret_cast<void*>(this);
-		mp4cb.read = callback_read;
-		mp4cb.write = callback_write;
-		mp4cb.seek = callback_seek;
-		mp4cb.truncate = callback_truncate;
-
-		m_skip_frames = 0;
-		hDecoder = 0;
-		infile = 0;
-    }
-
-    ~input_mp4()
-    {
-		cleanup();
-    }
-
-    virtual bool test_filename(const char * fn,const char * ext)
-    {
-        return (!stricmp(ext,"MP4") || !stricmp(ext,"M4A"));
-    }
-
-    virtual bool open(reader *r, file_info *info, unsigned flags)
-    {
-		cleanup();
-
-		mp4file = r;
-
-		hDecoder = faacDecOpen();
-		if (hDecoder == 0)
-		{
-			cleanup();
-			console::error("Failed to open FAAD2 library.");
-			return 0;
-		}
-
-		{
-			faacDecConfigurationPtr config;
-            config = faacDecGetCurrentConfiguration(hDecoder);
-            config->outputFormat = FAAD_FMT_DOUBLE;
-            faacDecSetConfiguration(hDecoder, config);
-		}
-
-
-		infile = mp4ff_open_read(&mp4cb);
-		if (!infile)
-		{
-			cleanup();
-			console::error("Error parsing MP4 file.");
-			return 0;
-		}
-
-		if ((track = GetAACTrack(infile)) < 0)
-		{
-			cleanup();
-			console::error("Unable to find correct AAC sound track in the MP4 file.");
-			return 0;
-		}
-
-
-
-
-		buffer = NULL;
-		buffer_size = 0;
-		mp4ff_get_decoder_config(infile, track, &buffer, &buffer_size);
-
-		if(faacDecInit2(hDecoder, buffer, buffer_size,
-						&m_samplerate,&channels) < 0)
-		{
-			cleanup();
-			console::error("Error initializing decoder library.");
-			
-			return 0;
-		}
-
-		m_timescale = mp4ff_time_scale(infile,track);
-		framesize = 1024;
-
-		if (buffer)
-		{
-			if (AudioSpecificConfig(buffer, buffer_size, &mp4ASC) >= 0)
-			{
-				if (mp4ASC.frameLengthFlag == 1) framesize = 960;
-				if (mp4ASC.sbr_present_flag == 1) framesize *= 2;
-			}
-			free(buffer);
-		}
-
-		{
-			char *tag = NULL, *item = NULL;
-			int k, j;
-			static const char *ot[6] = { "NULL", "MAIN AAC", "LC AAC", "SSR AAC", "LTP AAC", "HE AAC" };
-			const long samples = mp4ff_num_samples(infile, track);
-			double f = 1024.0;
-			double seconds;
-
-			if ((mp4ASC.sbr_present_flag == 1) || mp4ASC.forceUpSampling) f = f * 2.0;
-			seconds = (float)samples*(float)(f-1.0)/(float)mp4ASC.samplingFrequency;
-
-			
-			if (m_timescale<=0)
-				m_length = -1.0;
-			else
-			{
-				int64_t duration = mp4ff_get_track_duration(infile,track);
-				if (duration == -1)
-					m_length = -1.0;
-				else
-				{
-					m_length = (double)duration / (double)m_timescale;
-				}
-			}
-
-			info->set_length(m_length);
-
-
-			if (flags & OPEN_FLAG_GET_INFO)
-			{
-				info->info_set_int("bitrate",(mp4ff_get_avg_bitrate(infile,track) + 500) / 1000);
-				info->info_set_int("channels",mp4ASC.channelsConfiguration);
-				info->info_set_int("samplerate",mp4ASC.samplingFrequency);
-				info->info_set("codec","AAC");
-				info->info_set("aac_profile",ot[(mp4ASC.objectTypeIndex > 5)?0:mp4ASC.objectTypeIndex]);
-
-				j = mp4ff_meta_get_num_items(infile);
-				for (k = 0; k < j; k++)
-				{
-					if (mp4ff_meta_get_by_index(infile, k, &item, &tag))
-					{
-						if (item != NULL && tag != NULL)
-						{
-							meta_mp4_to_fb2k(item,tag,info);
-							free(item); item = NULL;
-							free(tag); tag = NULL;
-						}
-					}
-				}
-			}
-		}
-
-		numSamples = mp4ff_num_samples(infile, track);
-
-		m_skip_samples = 0;
-
-		sampleId = 0;
-
-		m_firstframe = true;
-		m_skip_frames = 1;
-
-        return 1;
-    }
-
-    virtual int run(audio_chunk * chunk)
-    {
-		if (hDecoder==0)
-		{
-			console::error("Attempting to decode while not open.");
-			return -1;
-		}
-
-		if (sampleId >= numSamples) return 0;
-
-		bool done = false;
-
-		do		
-		{
-			int rc;
-			unsigned int duration;
-			unsigned int aac_sample_count;
-			unsigned int delay = 0;
-
-			/* get acces unit from MP4 file */
-			buffer = NULL;
-			buffer_size = 0;
-
-			duration = mp4ff_get_sample_duration(infile, track, sampleId);
-			rc = mp4ff_read_sample(infile, track, sampleId, &buffer,  &buffer_size);
-			if (rc == 0)
-			{
-				cleanup();
-				console::error("Reading from MP4 file failed.");
-				return -1;
-			}
-
-			sample_buffer = faacDecDecode(hDecoder, &frameInfo, buffer, buffer_size);
-
-			if (buffer) free(buffer);
-
-			if (frameInfo.channels<=0)
-			{
-				cleanup();
-				console::error("Internal decoder error.");
-				return -1;
-			}
-
-			if (frameInfo.error > 0)
-			{
-				console::warning(faacDecGetErrorMessage(frameInfo.error));
-			}
-
-			if (m_skip_frames>0) m_skip_frames--;
-			else if (frameInfo.error==0)
-			{
-
-				aac_sample_count = frameInfo.samples / frameInfo.channels;
-				
-				if (m_firstframe && duration < aac_sample_count)
-					delay += aac_sample_count - duration;
-
-				if (m_skip_samples)//for sample-accurate seeking
-				{
-					unsigned int delta = m_skip_samples;
-					if (delta > duration) delta = duration;
-					delay += delta;
-					duration -= delta;
-					m_skip_samples -= delta;
-				}
-				
-
-				if (duration > 0)
-				{
-					chunk->set_data_64((const double*)sample_buffer + delay * frameInfo.channels,duration,frameInfo.channels,m_samplerate);
-					done = true;
-					m_firstframe = false;
-				}
-			}
-			sampleId++;
-		}
-		while(!done && sampleId < numSamples);
-		
-
-        return done ? 1 : 0;
-    }
-
-    virtual set_info_t set_info(reader *r, const file_info * info)
-    {
-#if 0
-/* metadata tag structure */
-typedef struct
-{
-    char *item;
-    char *value;
-} mp4ff_tag_t;
-
-/* metadata list structure */
-typedef struct
-{
-    mp4ff_tag_t *tags;
-    uint32_t count;
-} mp4ff_metadata_t;
-#endif
-
-
-
-		unsigned rv;
-		ptr_list_t<char> freeme;
-		mem_block_list_t<mp4ff_tag_t> tags;
-		mp4ff_metadata_t mp4meta;
-
-		{
-			string8_fastalloc name,value;
-			mp4ff_tag_t tag;
-
-			unsigned n, m;
-			
-			m = info->meta_get_count();
-			for(n=0;n<m;n++)
-			{
-				meta_fb2k_to_mp4(info->meta_enum_name(n),info->meta_enum_value(n),name,value);
-				freeme.add_item(tag.item = strdup(name));
-				freeme.add_item(tag.value = strdup(value));
-				tags.add_item(tag);
-			}
-
-
-			m = info->info_get_count();
-
-			for(n=0;n<m;n++)
-			{
-				const char * p_name = check_tech_info(info->info_enum_name(n));
-				if (p_name)
-				{
-					tag.item = const_cast<char*>(p_name);
-					tag.value = const_cast<char*>(info->info_enum_value(n));
-					tags.add_item(tag);
-				}
-			}
-		}
-
-		mp4meta.count = tags.get_count();
-		mp4meta.tags = const_cast<mp4ff_tag_t*>(tags.get_ptr());
-
-		mp4file = r;
-		rv = mp4ff_meta_update(&mp4cb,&mp4meta);
-		mp4file = 0;
-
-		freeme.free_all();
-
-		return rv ? SET_INFO_SUCCESS : SET_INFO_FAILURE;
-
-    }
-
-    virtual bool seek(double seconds)
-    {
-		if (hDecoder == 0)
-		{
-			console::error("Attempting to seek while not open.");
-			return false;
-		}
-        if (seconds >= m_length) {
-            sampleId = numSamples;
-            return true;
-        }
-		
-		int64_t offset = (int64_t)(seconds * m_timescale);
-		int32_t skip_samples;
-		int32_t dest_sample = mp4ff_find_sample(infile,track,offset,&skip_samples);
-		if (dest_sample == (-1)) return false;
-		if (dest_sample>0)
-		{
-			dest_sample--;
-			m_skip_frames = 1;
-		}
-		else
-		{//should never happen
-			m_skip_frames = 1;
-		}
-
-		sampleId = dest_sample;
-		
-		m_skip_samples = MulDiv(skip_samples,m_samplerate,m_timescale);
-
-		m_firstframe = sampleId < numSamples / 2;
-
-        faacDecPostSeekReset(hDecoder, -1);
-
-        return true;
-
-    }
-
-    virtual bool is_our_content_type(const char *url, const char *type)
-    {
-        return !stricmp(type, "audio/mp4") || !stricmp(type, "audio/x-mp4");
-    }
-
-private:
-};
-
-
-
-struct seek_list
-{
-    seek_list *next;
-    __int64 offset;
-};
-
-class input_aac : public input
-{
-public:
-
-    virtual bool test_filename(const char * fn,const char * ext)
-    {
-        return !stricmp(ext,"AAC");
-    }
-
-    virtual bool open(reader *r, file_info *info, unsigned flags)
-    {
-        int tagsize = 0, tmp = 0;
-        int bread = 0;
-        double length = 1.;
-        __int64 bitrate = 128;
-        unsigned char channels = 0;
-        unsigned long samplerate = 0;
-        int sbr = 0;
-        int header_type = 0;
-        int profile = 0;
-
-        m_reader = r;
-        tagsize = (int)id3v2_calc_size(m_reader);
-        if (tagsize<0) return 0;
-
-        if (!(m_aac_buffer = (unsigned char*)malloc(768*6)))
-        {
-            console::error("Memory allocation error.");
-            return 0;
-        }
-
-        for (int init=0; init<2; init++)
-        {
-            faacDecConfigurationPtr config;
-
-            hDecoder = faacDecOpen();
-            if (!hDecoder)
-            {
-                console::error("Failed to open FAAD2 library.");
-                return 0;
-            }
-
-            config = faacDecGetCurrentConfiguration(hDecoder);
-            config->outputFormat = FAAD_FMT_DOUBLE;
-            faacDecSetConfiguration(hDecoder, config);
-
-            memset(m_aac_buffer, 0, 768*6);
-            bread = m_reader->read(m_aac_buffer, 768*6);
-            m_aac_bytes_into_buffer = bread;
-            m_aac_bytes_consumed = 0;
-            m_file_offset = 0;
-            m_last_offset = -1;
-            m_at_eof = (bread != 768*6) ? 1 : 0;
-
-            if (init==0)
-            {
-                faacDecFrameInfo frameInfo;
-
-                fill_buffer();
-                if ((m_aac_bytes_consumed = faacDecInit(hDecoder,
-                    m_aac_buffer, m_aac_bytes_into_buffer,
-                    &samplerate, &channels)) < 0)
-                {
-                    console::error("Can't initialize decoder library.");
-                    return 0;
-                }
-                advance_buffer(m_aac_bytes_consumed);
-
-                do {
-                    memset(&frameInfo, 0, sizeof(faacDecFrameInfo));
-                    fill_buffer();
-                    faacDecDecode(hDecoder, &frameInfo, m_aac_buffer, m_aac_bytes_into_buffer);
-                } while (!frameInfo.samples && !frameInfo.error);
-
-                if (frameInfo.error)
-                {
-                    console::error(faacDecGetErrorMessage(frameInfo.error));
-                    return 0;
-                }
-
-                m_samplerate = frameInfo.samplerate;
-                m_framesize = (frameInfo.channels != 0) ? frameInfo.samples/frameInfo.channels : 0;
-                sbr = frameInfo.sbr;
-                profile = frameInfo.object_type;
-                header_type = frameInfo.header_type;
-
-                faacDecClose(hDecoder);
-                m_reader->seek(tagsize);
-            }
-        }
-
-        m_head = (struct seek_list*)malloc(sizeof(struct seek_list));
-        m_tail = m_head;
-        m_tail->next = NULL;
-
-        m_header_type = 0;
-        if ((m_aac_buffer[0] == 0xFF) && ((m_aac_buffer[1] & 0xF6) == 0xF0))
-        {
-            if (m_reader->can_seek())
-            {
-                adts_parse(&bitrate, &length);
-                m_reader->seek(tagsize);
-
-                bread = m_reader->read(m_aac_buffer, 768*6);
-                if (bread != 768*6)
-                    m_at_eof = 1;
-                else
-                    m_at_eof = 0;
-                m_aac_bytes_into_buffer = bread;
-                m_aac_bytes_consumed = 0;
-
-                m_header_type = 1;
-            }
-        } else if (memcmp(m_aac_buffer, "ADIF", 4) == 0) {
-            int skip_size = (m_aac_buffer[4] & 0x80) ? 9 : 0;
-            bitrate = ((unsigned int)(m_aac_buffer[4 + skip_size] & 0x0F)<<19) |
-                ((unsigned int)m_aac_buffer[5 + skip_size]<<11) |
-                ((unsigned int)m_aac_buffer[6 + skip_size]<<3) |
-                ((unsigned int)m_aac_buffer[7 + skip_size] & 0xE0);
-
-            length = (double)m_reader->get_length();
-            if (length == -1.)
-            {
-                length = 1;
-            } else {
-                length = ((double)length*8.)/((double)bitrate) + 0.5;
-            }
-
-            bitrate = (__int64)((double)bitrate/1000.0 + 0.5);
-
-            m_header_type = 2;
-        }
-
-        if (!m_reader->can_seek())
-        {
-            length = 0;
-        }
-
-        fill_buffer();
-        if ((m_aac_bytes_consumed = faacDecInit(hDecoder,
-            m_aac_buffer, m_aac_bytes_into_buffer,
-            &samplerate, &channels)) < 0)
-        {
-            console::error("Can't initialize decoder library.");
-            return 0;
-        }
-        advance_buffer(m_aac_bytes_consumed);
-
-        m_length = length;
-        info->set_length(m_length);
-
-        if (flags & OPEN_FLAG_GET_INFO) {
-            const char *profile_str = object_type_string(profile);
-            const char *header_str = NULL;
-
-            info->info_set_int("bitrate", bitrate);
-            info->info_set_int("channels", (__int64)channels);
-            info->info_set_int("samplerate", (__int64)m_samplerate);
-
-            if (profile_str)
-                info->info_set("aac_profile", profile_str);
-
-            if (header_type == RAW)
-                header_str = "RAW";
-            else if (header_type == ADIF)
-                header_str = "ADIF";
-            else if (header_type == ADTS)
-                header_str = "ADTS";
-
-            if (header_str)
-                info->info_set("aac_header_type", header_str);
-
-            if (sbr == 1 || sbr == 2) /* SBR: 0: off, 1: on; upsample, 2: on; downsampled, 3: off; upsampled */
-                info->info_set("codec", "AAC+SBR");
-            else
-                info->info_set("codec", "AAC");
-
-            tag_reader::g_run_multi(m_reader, info, "ape|id3v2|lyrics3|id3v1");
-        }
-
-        return 1;
-    }
-
-    input_aac()
-    {
-        m_head = NULL;
-        m_tail = NULL;
-        m_samplerate = 0;
-        hDecoder = NULL;
-        m_aac_buffer = NULL;
-        m_samples = 0;
-        m_samplepos = 0;
-        m_seekskip = 0;
-        m_eof = false;
-    }
-
-    ~input_aac()
-    {
-        struct seek_list *target = m_head;
-
-        if (hDecoder)
-            faacDecClose(hDecoder);
-        if (m_aac_buffer)
-            free(m_aac_buffer);
-
-        while (target)
-        {
-            struct seek_list *tmp = target;
-            target = target->next;
-            if (tmp) free(tmp);
-        }
-    }
-
-    virtual int run(audio_chunk * chunk)
-    {
-        while (1) {
-            if (m_eof || (m_samples > 0 && m_samplepos >= m_samples)) return 0; // gapless playback
-
-            if (m_aac_bytes_into_buffer == 0) return 0;
-
-            faacDecFrameInfo frameInfo;
-            audio_sample *sample_buffer = 0;
-
-            memset(&frameInfo, 0, sizeof(faacDecFrameInfo));
-
-            do
-            {
-                fill_buffer();
-
-                if (m_aac_bytes_into_buffer != 0)
-                {
-                    sample_buffer = (audio_sample*)faacDecDecode(hDecoder, &frameInfo,
-                        m_aac_buffer, m_aac_bytes_into_buffer);
-
-                    if (m_header_type != 1)
-                    {
-                        if (m_last_offset < m_file_offset)
-                        {
-                            m_tail->offset = m_file_offset;
-                            m_tail->next = (struct seek_list*)malloc(sizeof(struct seek_list));
-                            m_tail = m_tail->next;
-                            m_tail->next = NULL;
-                            m_last_offset = m_file_offset;
-                        }
-                    }
-
-                    advance_buffer(frameInfo.bytesconsumed);
-                }
-
-                if (frameInfo.error || !sample_buffer)
-                {
-                    if (!frameInfo.error) return 0; // EOF
-                    const char *msg = faacDecGetErrorMessage(frameInfo.error);
-                    if (msg) console::error(msg);
-                    return 0; //-1;
-                }
-
-                if (m_aac_bytes_into_buffer == 0) break;
-            } while (!frameInfo.samples || !frameInfo.channels);
-
-            if (!frameInfo.samples || !frameInfo.channels) return 0;
-
-            unsigned int samples = frameInfo.samples/frameInfo.channels;
-
-            m_samplerate = frameInfo.samplerate;
-            m_framesize = samples;
-
-            if (m_samples > 0) { // gapless playback
-                if (m_samplepos + samples > m_samples) samples = (unsigned int)(m_samples - m_samplepos);
-            }
-
-            m_samplepos += samples;
-
-            if ((unsigned)m_seekskip < samples) {
-                if (frameInfo.channels == 6 && frameInfo.num_lfe_channels)
-                {
-                    //channel order for 5.1: L/R/C/LF/BL/BR
-                    audio_sample r1, r2, r3, r4, r5, r6;
-                    for (unsigned int i = 0; i < frameInfo.samples; i += frameInfo.channels)
-                    {
-                        r1 = sample_buffer[i];
-                        r2 = sample_buffer[i+1];
-                        r3 = sample_buffer[i+2];
-                        r4 = sample_buffer[i+3];
-                        r5 = sample_buffer[i+4];
-                        r6 = sample_buffer[i+5];
-                        sample_buffer[i] = r2;
-                        sample_buffer[i+1] = r3;
-                        sample_buffer[i+2] = r1;
-                        sample_buffer[i+3] = r6;
-                        sample_buffer[i+4] = r4;
-                        sample_buffer[i+5] = r5;
-                    }
-                }
-
-                samples -= m_seekskip;
-                if (chunk)
-                {
-                    chunk->set_data((audio_sample*)sample_buffer + m_seekskip*frameInfo.channels,
-                        samples, frameInfo.channels, frameInfo.samplerate);
-                }
-                m_seekskip = 0;
-                break;
-            } else {
-                m_seekskip -= samples;
-            }
-        }
-
-        return 1;
-    }
-
-    virtual set_info_t set_info(reader *r,const file_info *info)
-    {
-        tag_remover::g_run(r);
-        return tag_writer::g_run(r,info,"ape") ? SET_INFO_SUCCESS : SET_INFO_FAILURE;
-    }
-
-    virtual bool seek(double seconds)
-    {
-        unsigned int i, frames;
-        int bread;
-        struct seek_list *target = m_head;
-
-        if (seconds >= m_length) {
-            m_eof = true;
-            return true;
-        }
-
-        double cur_pos_sec = (double)(__int64)m_samplepos / (double)(__int64)m_samplerate;
-
-        if (m_reader->can_seek() && ((m_header_type == 1) || (seconds < cur_pos_sec)))
-        {
-            frames = (unsigned int)(seconds*((double)m_samplerate/(double)m_framesize));
-            if (frames > 1) frames--;
-
-            for (i = 0; i < frames; i++)
-            {
-                if (target->next)
-                    target = target->next;
-                else
-                    return false;
-            }
-            if (target->offset == 0 && frames > 0)
-                return false;
-            m_file_offset = target->offset;
-            m_reader->seek(m_file_offset);
-
-            bread = m_reader->read(m_aac_buffer, 768*6);
-            if (bread != 768*6)
-                m_at_eof = 1;
-            else
-                m_at_eof = 0;
-            m_aac_bytes_into_buffer = bread;
-            m_aac_bytes_consumed = 0;
-            m_file_offset += bread;
-            m_samplepos =(frames > 1) ? (unsigned __int64)(frames-1) * m_framesize : 0;
-            m_seekskip = (int)((unsigned __int64)(seconds * m_samplerate + 0.5) - m_samplepos);// + m_framesize;
-            if (m_seekskip < 0) return false; // should never happen
-            faacDecPostSeekReset(hDecoder, -1);
-
-            faacDecFrameInfo frameInfo;
-            memset(&frameInfo, 0, sizeof(faacDecFrameInfo));
-            fill_buffer();
-            faacDecDecode(hDecoder, &frameInfo, m_aac_buffer, m_aac_bytes_into_buffer);
-
-            return true;
-        } else {
-            if (seconds > cur_pos_sec)
-            {
-                frames = (unsigned int)((seconds - cur_pos_sec)*((double)m_samplerate/(double)m_framesize));
-
-                if (frames > 0)
-                {
-                    for (i = 0; i < frames; i++)
-                    {
-                        if (!run(NULL))
-                            return false;
-                    }
-                }
-
-                m_seekskip = (int)((unsigned __int64)(seconds * m_samplerate + 0.5) - m_samplepos);
-                if (m_seekskip < 0) return false; // should never happen
-                faacDecPostSeekReset(hDecoder, -1);
-            }
-            return true;
-        }
-        return false;
-    }
-
-    virtual bool is_our_content_type(const char *url, const char *type)
-    {
-        return !stricmp(type, "audio/aac") || !stricmp(type, "audio/x-aac");
-    }
-
-private:
-
-    reader *m_reader;
-
-    faacDecHandle hDecoder;
-
-    long m_aac_bytes_into_buffer;
-    long m_aac_bytes_consumed;
-    __int64 m_file_offset;
-    __int64 m_last_offset;
-    unsigned char *m_aac_buffer;
-    int m_at_eof;
-
-    unsigned long m_samplerate;
-    int m_header_type;
-
-    struct seek_list *m_head;
-    struct seek_list *m_tail;
-
-    unsigned __int64 m_samples;
-    unsigned int m_framesize;
-    unsigned __int64 m_samplepos;
-    int m_seekskip;
-    double m_length;
-    bool m_eof;
-
-    int fill_buffer()
-    {
-        int bread;
-
-        if (m_aac_bytes_consumed > 0)
-        {
-            if (m_aac_bytes_into_buffer)
-            {
-                memmove((void*)m_aac_buffer, (void*)(m_aac_buffer + m_aac_bytes_consumed),
-                    m_aac_bytes_into_buffer*sizeof(unsigned char));
-            }
-
-            if (!m_at_eof)
-            {
-                bread = m_reader->read((void*)(m_aac_buffer + m_aac_bytes_into_buffer),
-                    m_aac_bytes_consumed);
-
-                if (bread != m_aac_bytes_consumed)
-                    m_at_eof = 1;
-
-                m_aac_bytes_into_buffer += bread;
-            }
-
-            m_aac_bytes_consumed = 0;
-
-            if (m_aac_bytes_into_buffer > 3)
-            {
-                if (memcmp(m_aac_buffer, "TAG", 3) == 0)
-                    m_aac_bytes_into_buffer = 0;
-            }
-            if (m_aac_bytes_into_buffer > 11)
-            {
-                if (memcmp(m_aac_buffer, "LYRICSBEGIN", 11) == 0)
-                    m_aac_bytes_into_buffer = 0;
-            }
-            if (m_aac_bytes_into_buffer > 8)
-            {
-                if (memcmp(m_aac_buffer, "APETAGEX", 8) == 0)
-                    m_aac_bytes_into_buffer = 0;
-            }
-        }
-
-        return 1;
-    }
-
-    void advance_buffer(int bytes)
-    {
-        m_file_offset += bytes;
-        m_aac_bytes_consumed = bytes;
-        m_aac_bytes_into_buffer -= bytes;
-    }
-
-    int adts_parse(__int64 *bitrate, double *length)
-    {
-        static int sample_rates[] = {96000,88200,64000,48000,44100,32000,24000,22050,16000,12000,11025,8000};
-        int frames, frame_length;
-        int t_framelength = 0;
-        int samplerate;
-        double frames_per_sec, bytes_per_frame;
-
-        /* Read all frames to ensure correct time and bitrate */
-        for (frames = 0; /* */; frames++)
-        {
-            fill_buffer();
-
-            if (m_aac_bytes_into_buffer > 7)
-            {
-                /* check syncword */
-                if (!((m_aac_buffer[0] == 0xFF)&&((m_aac_buffer[1] & 0xF6) == 0xF0)))
-                    break;
-
-                m_tail->offset = m_file_offset;
-                m_tail->next = (struct seek_list*)malloc(sizeof(struct seek_list));
-                m_tail = m_tail->next;
-                m_tail->next = NULL;
-
-                if (frames == 0)
-                    samplerate = sample_rates[(m_aac_buffer[2]&0x3c)>>2];
-
-                frame_length = ((((unsigned int)m_aac_buffer[3] & 0x3)) << 11)
-                    | (((unsigned int)m_aac_buffer[4]) << 3) | (m_aac_buffer[5] >> 5);
-
-                t_framelength += frame_length;
-
-                if (frame_length > m_aac_bytes_into_buffer)
-                    break;
-
-                advance_buffer(frame_length);
-            } else {
-                break;
-            }
-        }
-
-        frames_per_sec = (double)samplerate/1024.0;
-        if (frames != 0)
-            bytes_per_frame = (double)t_framelength/(double)(frames*1000);
-        else
-            bytes_per_frame = 0;
-        *bitrate = (__int64)(8. * bytes_per_frame * frames_per_sec + 0.5);
-        if (frames_per_sec != 0)
-            *length = (double)frames/frames_per_sec;
-        else
-            *length = 1;
-
-        return 1;
-    }
-};
-
-static service_factory_t<input, input_mp4> foo_mp4;
-static service_factory_t<input, input_aac> foo_aac;
\ No newline at end of file
--- a/plugins/foo_mp4/foo_mp4.dsp
+++ b/plugins/foo_mp4/foo_mp4.dsp
@@ -69,7 +69,7 @@
 # PROP Ignore_Export_Lib 0
 # PROP Target_Dir ""
 # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /GZ /c
-# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /I "../../common/mp4v2" /I "../../common/mp4av" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /Gm /ZI /Od /I "../../include" /I "../../common/mp4ff" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /GZ /c
 # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
 # ADD BASE RSC /l 0x413 /d "_DEBUG"
@@ -92,7 +92,20 @@
 # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
 # Begin Source File
 
-SOURCE=.\foo_mp4.cpp
+SOURCE=.\convert.cpp
+# PROP Exclude_From_Build 1
+# End Source File
+# Begin Source File
+
+SOURCE=.\input_aac.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\mp4_parser.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\packet_decoder_aac.cpp
 # End Source File
 # End Group
 # Begin Group "Header Files"
--- /dev/null
+++ b/plugins/foo_mp4/input_aac.cpp
@@ -1,0 +1,616 @@
+/*
+** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding
+** Copyright (C) 2003 M. Bakker, Ahead Software AG, http://www.nero.com
+**
+** 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.
+**
+** Any non-GPL usage of this software or parts of this software is strictly
+** forbidden.
+**
+** Commercial non-GPL licensing of this software is possible.
+** For more info contact Ahead Software through [email protected].
+**
+** $Id: input_aac.cpp,v 1.2 2003/12/14 13:50:50 menno Exp $
+**/
+
+#include "foobar2000/SDK/foobar2000.h"
+#include "foobar2000/foo_input_std/id3v2_hacks.h"
+#include <faad.h>
+
+//#define DBG_OUT(A) OutputDebugString(A)
+#define DBG_OUT(A)
+
+static const char *object_type_string(int type)
+{
+    static const char *types[31] = {
+        "AAC Main",
+        "AAC LC",
+        "AAC SSR",
+        "AAC LTP",
+        "AAC HE",
+        "AAC Scalable",
+        "TwinVQ",
+        "CELP",
+        "HVXC",
+        "Reserved",
+        "Reserved",
+        "TTSI",
+        "Main synthetic",
+        "Wavetable synthesis",
+        "General MIDI",
+        "Algorithmic Synthesis and Audio FX",
+        "ER AAC LC",
+        "Reserved",
+        "ER AAC LTP",
+        "ER AAC scalable",
+        "ER TwinVQ",
+        "ER BSAC",
+        "ER AAC LD",
+        "ER CELP",
+        "ER HVXC",
+        "ER HILN",
+        "ER Parametric",
+        "Reserved",
+        "Reserved",
+        "Reserved",
+        "Reserved",
+    };
+
+    if (type<1 || type>31) return NULL;
+
+    return types[type-1];
+}
+
+
+struct seek_list
+{
+    seek_list *next;
+    __int64 offset;
+};
+
+class input_aac : public input
+{
+public:
+
+    virtual bool test_filename(const char * fn,const char * ext)
+    {
+        return !stricmp(ext,"AAC");
+    }
+
+    virtual bool open(reader *r, file_info *info, unsigned flags)
+    {
+        int tagsize = 0, tmp = 0;
+        int bread = 0;
+        double length = 1.;
+        __int64 bitrate = 128;
+        unsigned char channels = 0;
+        unsigned long samplerate = 0;
+        int sbr = 0;
+        int header_type = 0;
+        int profile = 0;
+
+        m_reader = r;
+        tagsize = (int)id3v2_calc_size(m_reader);
+        if (tagsize<0) return 0;
+
+        if (!(m_aac_buffer = (unsigned char*)malloc(768*6)))
+        {
+            console::error("Memory allocation error.");
+            return 0;
+        }
+
+        for (int init=0; init<2; init++)
+        {
+            faacDecConfigurationPtr config;
+
+            hDecoder = faacDecOpen();
+            if (!hDecoder)
+            {
+                console::error("Failed to open FAAD2 library.");
+                return 0;
+            }
+
+            config = faacDecGetCurrentConfiguration(hDecoder);
+            config->outputFormat = FAAD_FMT_DOUBLE;
+            faacDecSetConfiguration(hDecoder, config);
+
+            memset(m_aac_buffer, 0, 768*6);
+            bread = m_reader->read(m_aac_buffer, 768*6);
+            m_aac_bytes_into_buffer = bread;
+            m_aac_bytes_consumed = 0;
+            m_file_offset = 0;
+            m_last_offset = -1;
+            m_at_eof = (bread != 768*6) ? 1 : 0;
+
+            if (init==0)
+            {
+                faacDecFrameInfo frameInfo;
+
+                fill_buffer();
+                if ((m_aac_bytes_consumed = faacDecInit(hDecoder,
+                    m_aac_buffer, m_aac_bytes_into_buffer,
+                    &samplerate, &channels)) < 0)
+                {
+                    console::error("Can't initialize decoder library.");
+                    return 0;
+                }
+                advance_buffer(m_aac_bytes_consumed);
+
+                do {
+                    memset(&frameInfo, 0, sizeof(faacDecFrameInfo));
+                    fill_buffer();
+                    faacDecDecode(hDecoder, &frameInfo, m_aac_buffer, m_aac_bytes_into_buffer);
+                } while (!frameInfo.samples && !frameInfo.error);
+
+                if (frameInfo.error)
+                {
+                    console::error(faacDecGetErrorMessage(frameInfo.error));
+                    return 0;
+                }
+
+                m_samplerate = frameInfo.samplerate;
+                m_framesize = (frameInfo.channels != 0) ? frameInfo.samples/frameInfo.channels : 0;
+                sbr = frameInfo.sbr;
+                profile = frameInfo.object_type;
+                header_type = frameInfo.header_type;
+
+                faacDecClose(hDecoder);
+                m_reader->seek(tagsize);
+            }
+        }
+
+        m_head = (struct seek_list*)malloc(sizeof(struct seek_list));
+        m_tail = m_head;
+        m_tail->next = NULL;
+
+        m_header_type = 0;
+        if ((m_aac_buffer[0] == 0xFF) && ((m_aac_buffer[1] & 0xF6) == 0xF0))
+        {
+            if (m_reader->can_seek())
+            {
+                adts_parse(&bitrate, &length);
+                m_reader->seek(tagsize);
+
+                bread = m_reader->read(m_aac_buffer, 768*6);
+                if (bread != 768*6)
+                    m_at_eof = 1;
+                else
+                    m_at_eof = 0;
+                m_aac_bytes_into_buffer = bread;
+                m_aac_bytes_consumed = 0;
+
+                m_header_type = 1;
+            }
+        } else if (memcmp(m_aac_buffer, "ADIF", 4) == 0) {
+            int skip_size = (m_aac_buffer[4] & 0x80) ? 9 : 0;
+            bitrate = ((unsigned int)(m_aac_buffer[4 + skip_size] & 0x0F)<<19) |
+                ((unsigned int)m_aac_buffer[5 + skip_size]<<11) |
+                ((unsigned int)m_aac_buffer[6 + skip_size]<<3) |
+                ((unsigned int)m_aac_buffer[7 + skip_size] & 0xE0);
+
+            length = (double)m_reader->get_length();
+            if (length == -1.)
+            {
+                length = 1;
+            } else {
+                length = ((double)length*8.)/((double)bitrate) + 0.5;
+            }
+
+            bitrate = (__int64)((double)bitrate/1000.0 + 0.5);
+
+            m_header_type = 2;
+        }
+
+        if (!m_reader->can_seek())
+        {
+            length = 0;
+        }
+
+        fill_buffer();
+        if ((m_aac_bytes_consumed = faacDecInit(hDecoder,
+            m_aac_buffer, m_aac_bytes_into_buffer,
+            &samplerate, &channels)) < 0)
+        {
+            console::error("Can't initialize decoder library.");
+            return 0;
+        }
+        advance_buffer(m_aac_bytes_consumed);
+
+        m_length = length;
+        info->set_length(m_length);
+
+        if (flags & OPEN_FLAG_GET_INFO) {
+            const char *profile_str = object_type_string(profile);
+            const char *header_str = NULL;
+
+            info->info_set_int("bitrate", bitrate);
+            info->info_set_int("channels", (__int64)channels);
+            info->info_set_int("samplerate", (__int64)m_samplerate);
+
+            if (profile_str)
+                info->info_set("aac_profile", profile_str);
+
+            if (header_type == RAW)
+                header_str = "RAW";
+            else if (header_type == ADIF)
+                header_str = "ADIF";
+            else if (header_type == ADTS)
+                header_str = "ADTS";
+
+            if (header_str)
+                info->info_set("aac_header_type", header_str);
+
+            if (sbr == 1 || sbr == 2) /* SBR: 0: off, 1: on; upsample, 2: on; downsampled, 3: off; upsampled */
+                info->info_set("codec", "AAC+SBR");
+            else
+                info->info_set("codec", "AAC");
+
+            tag_reader::g_run_multi(m_reader, info, "ape|id3v2|lyrics3|id3v1");
+        }
+
+        return 1;
+    }
+
+    input_aac()
+    {
+        m_head = NULL;
+        m_tail = NULL;
+        m_samplerate = 0;
+        hDecoder = NULL;
+        m_aac_buffer = NULL;
+        m_samples = 0;
+        m_samplepos = 0;
+        m_seekskip = 0;
+        m_eof = false;
+    }
+
+    ~input_aac()
+    {
+        struct seek_list *target = m_head;
+
+        if (hDecoder)
+            faacDecClose(hDecoder);
+        if (m_aac_buffer)
+            free(m_aac_buffer);
+
+        while (target)
+        {
+            struct seek_list *tmp = target;
+            target = target->next;
+            if (tmp) free(tmp);
+        }
+    }
+
+    virtual int run(audio_chunk * chunk)
+    {
+        while (1) {
+            if (m_eof || (m_samples > 0 && m_samplepos >= m_samples)) return 0; // gapless playback
+
+            if (m_aac_bytes_into_buffer == 0) return 0;
+
+            faacDecFrameInfo frameInfo;
+            audio_sample *sample_buffer = 0;
+
+            memset(&frameInfo, 0, sizeof(faacDecFrameInfo));
+
+            do
+            {
+                fill_buffer();
+
+                if (m_aac_bytes_into_buffer != 0)
+                {
+                    sample_buffer = (audio_sample*)faacDecDecode(hDecoder, &frameInfo,
+                        m_aac_buffer, m_aac_bytes_into_buffer);
+
+                    if (m_header_type != 1)
+                    {
+                        if (m_last_offset < m_file_offset)
+                        {
+                            m_tail->offset = m_file_offset;
+                            m_tail->next = (struct seek_list*)malloc(sizeof(struct seek_list));
+                            m_tail = m_tail->next;
+                            m_tail->next = NULL;
+                            m_last_offset = m_file_offset;
+                        }
+                    }
+
+                    advance_buffer(frameInfo.bytesconsumed);
+                }
+
+                if (frameInfo.error || !sample_buffer)
+                {
+                    if (!frameInfo.error) return 0; // EOF
+                    const char *msg = faacDecGetErrorMessage(frameInfo.error);
+                    if (msg) console::error(msg);
+                    return 0; //-1;
+                }
+
+                if (m_aac_bytes_into_buffer == 0) break;
+            } while (!frameInfo.samples || !frameInfo.channels);
+
+            if (!frameInfo.samples || !frameInfo.channels) return 0;
+
+            unsigned int samples = frameInfo.samples/frameInfo.channels;
+
+            m_samplerate = frameInfo.samplerate;
+            m_framesize = samples;
+
+            if (m_samples > 0) { // gapless playback
+                if (m_samplepos + samples > m_samples) samples = (unsigned int)(m_samples - m_samplepos);
+            }
+
+            m_samplepos += samples;
+
+            if ((unsigned)m_seekskip < samples) {
+                if (frameInfo.channels == 6 && frameInfo.num_lfe_channels)
+                {
+                    //channel order for 5.1: L/R/C/LF/BL/BR
+                    audio_sample r1, r2, r3, r4, r5, r6;
+                    for (unsigned int i = 0; i < frameInfo.samples; i += frameInfo.channels)
+                    {
+                        r1 = sample_buffer[i];
+                        r2 = sample_buffer[i+1];
+                        r3 = sample_buffer[i+2];
+                        r4 = sample_buffer[i+3];
+                        r5 = sample_buffer[i+4];
+                        r6 = sample_buffer[i+5];
+                        sample_buffer[i] = r2;
+                        sample_buffer[i+1] = r3;
+                        sample_buffer[i+2] = r1;
+                        sample_buffer[i+3] = r6;
+                        sample_buffer[i+4] = r4;
+                        sample_buffer[i+5] = r5;
+                    }
+                }
+
+                samples -= m_seekskip;
+                if (chunk)
+                {
+                    chunk->set_data((audio_sample*)sample_buffer + m_seekskip*frameInfo.channels,
+                        samples, frameInfo.channels, frameInfo.samplerate);
+                }
+                m_seekskip = 0;
+                break;
+            } else {
+                m_seekskip -= samples;
+            }
+        }
+
+        return 1;
+    }
+
+    virtual set_info_t set_info(reader *r,const file_info *info)
+    {
+        tag_remover::g_run(r);
+        return tag_writer::g_run(r,info,"ape") ? SET_INFO_SUCCESS : SET_INFO_FAILURE;
+    }
+
+    virtual bool seek(double seconds)
+    {
+        unsigned int i, frames;
+        int bread;
+        struct seek_list *target = m_head;
+
+        if (seconds >= m_length) {
+            m_eof = true;
+            return true;
+        }
+
+        double cur_pos_sec = (double)(__int64)m_samplepos / (double)(__int64)m_samplerate;
+
+        if (m_reader->can_seek() && ((m_header_type == 1) || (seconds < cur_pos_sec)))
+        {
+            frames = (unsigned int)(seconds*((double)m_samplerate/(double)m_framesize));
+            if (frames > 1) frames--;
+
+            for (i = 0; i < frames; i++)
+            {
+                if (target->next)
+                    target = target->next;
+                else
+                    return false;
+            }
+            if (target->offset == 0 && frames > 0)
+                return false;
+            m_file_offset = target->offset;
+            m_reader->seek(m_file_offset);
+
+            bread = m_reader->read(m_aac_buffer, 768*6);
+            if (bread != 768*6)
+                m_at_eof = 1;
+            else
+                m_at_eof = 0;
+            m_aac_bytes_into_buffer = bread;
+            m_aac_bytes_consumed = 0;
+            m_file_offset += bread;
+            m_samplepos =(frames > 1) ? (unsigned __int64)(frames-1) * m_framesize : 0;
+            m_seekskip = (int)((unsigned __int64)(seconds * m_samplerate + 0.5) - m_samplepos);// + m_framesize;
+            if (m_seekskip < 0) return false; // should never happen
+            faacDecPostSeekReset(hDecoder, -1);
+
+            faacDecFrameInfo frameInfo;
+            memset(&frameInfo, 0, sizeof(faacDecFrameInfo));
+            fill_buffer();
+            faacDecDecode(hDecoder, &frameInfo, m_aac_buffer, m_aac_bytes_into_buffer);
+
+            return true;
+        } else {
+            if (seconds > cur_pos_sec)
+            {
+                frames = (unsigned int)((seconds - cur_pos_sec)*((double)m_samplerate/(double)m_framesize));
+
+                if (frames > 0)
+                {
+                    for (i = 0; i < frames; i++)
+                    {
+                        if (!run(NULL))
+                            return false;
+                    }
+                }
+
+                m_seekskip = (int)((unsigned __int64)(seconds * m_samplerate + 0.5) - m_samplepos);
+                if (m_seekskip < 0) return false; // should never happen
+                faacDecPostSeekReset(hDecoder, -1);
+            }
+            return true;
+        }
+        return false;
+    }
+
+    virtual bool is_our_content_type(const char *url, const char *type)
+    {
+        return !stricmp(type, "audio/aac") || !stricmp(type, "audio/x-aac");
+    }
+
+private:
+
+    reader *m_reader;
+
+    faacDecHandle hDecoder;
+
+    long m_aac_bytes_into_buffer;
+    long m_aac_bytes_consumed;
+    __int64 m_file_offset;
+    __int64 m_last_offset;
+    unsigned char *m_aac_buffer;
+    int m_at_eof;
+
+    unsigned long m_samplerate;
+    int m_header_type;
+
+    struct seek_list *m_head;
+    struct seek_list *m_tail;
+
+    unsigned __int64 m_samples;
+    unsigned int m_framesize;
+    unsigned __int64 m_samplepos;
+    int m_seekskip;
+    double m_length;
+    bool m_eof;
+
+    int fill_buffer()
+    {
+        int bread;
+
+        if (m_aac_bytes_consumed > 0)
+        {
+            if (m_aac_bytes_into_buffer)
+            {
+                memmove((void*)m_aac_buffer, (void*)(m_aac_buffer + m_aac_bytes_consumed),
+                    m_aac_bytes_into_buffer*sizeof(unsigned char));
+            }
+
+            if (!m_at_eof)
+            {
+                bread = m_reader->read((void*)(m_aac_buffer + m_aac_bytes_into_buffer),
+                    m_aac_bytes_consumed);
+
+                if (bread != m_aac_bytes_consumed)
+                    m_at_eof = 1;
+
+                m_aac_bytes_into_buffer += bread;
+            }
+
+            m_aac_bytes_consumed = 0;
+
+            if (m_aac_bytes_into_buffer > 3)
+            {
+                if (memcmp(m_aac_buffer, "TAG", 3) == 0)
+                    m_aac_bytes_into_buffer = 0;
+            }
+            if (m_aac_bytes_into_buffer > 11)
+            {
+                if (memcmp(m_aac_buffer, "LYRICSBEGIN", 11) == 0)
+                    m_aac_bytes_into_buffer = 0;
+            }
+            if (m_aac_bytes_into_buffer > 8)
+            {
+                if (memcmp(m_aac_buffer, "APETAGEX", 8) == 0)
+                    m_aac_bytes_into_buffer = 0;
+            }
+        }
+
+        return 1;
+    }
+
+    void advance_buffer(int bytes)
+    {
+        m_file_offset += bytes;
+        m_aac_bytes_consumed = bytes;
+        m_aac_bytes_into_buffer -= bytes;
+    }
+
+    int adts_parse(__int64 *bitrate, double *length)
+    {
+        static int sample_rates[] = {96000,88200,64000,48000,44100,32000,24000,22050,16000,12000,11025,8000};
+        int frames, frame_length;
+        int t_framelength = 0;
+        int samplerate;
+        double frames_per_sec, bytes_per_frame;
+
+        /* Read all frames to ensure correct time and bitrate */
+        for (frames = 0; /* */; frames++)
+        {
+            fill_buffer();
+
+            if (m_aac_bytes_into_buffer > 7)
+            {
+                /* check syncword */
+                if (!((m_aac_buffer[0] == 0xFF)&&((m_aac_buffer[1] & 0xF6) == 0xF0)))
+                    break;
+
+                m_tail->offset = m_file_offset;
+                m_tail->next = (struct seek_list*)malloc(sizeof(struct seek_list));
+                m_tail = m_tail->next;
+                m_tail->next = NULL;
+
+                if (frames == 0)
+                    samplerate = sample_rates[(m_aac_buffer[2]&0x3c)>>2];
+
+                frame_length = ((((unsigned int)m_aac_buffer[3] & 0x3)) << 11)
+                    | (((unsigned int)m_aac_buffer[4]) << 3) | (m_aac_buffer[5] >> 5);
+
+                t_framelength += frame_length;
+
+                if (frame_length > m_aac_bytes_into_buffer)
+                    break;
+
+                advance_buffer(frame_length);
+            } else {
+                break;
+            }
+        }
+
+        frames_per_sec = (double)samplerate/1024.0;
+        if (frames != 0)
+            bytes_per_frame = (double)t_framelength/(double)(frames*1000);
+        else
+            bytes_per_frame = 0;
+        *bitrate = (__int64)(8. * bytes_per_frame * frames_per_sec + 0.5);
+        if (frames_per_sec != 0)
+            *length = (double)frames/frames_per_sec;
+        else
+            *length = 1;
+
+        return 1;
+    }
+};
+
+static service_factory_t<input, input_aac> foo_aac;
+
+
+DECLARE_COMPONENT_VERSION ("MPEG-4 AAC decoder",
+                           "2.0",
+                           "Based on FAAD2 v" FAAD2_VERSION "\nCopyright (C) 2002-2003 http://www.audiocoding.com" );
--- /dev/null
+++ b/plugins/foo_mp4/mp4_parser.cpp
@@ -1,0 +1,535 @@
+#include "foobar2000/SDK/foobar2000.h"
+#define USE_TAGGING
+#include <mp4ff.h>
+
+bool is_valid_aac_decoder_config(const void * data,unsigned bytes);
+
+static int find_track_to_decode(mp4ff_t *infile,string_base & codec)
+{
+    /* find AAC track */
+    int i;
+    int numTracks = mp4ff_total_tracks(infile);
+
+    for (i = 0; i < numTracks; i++)
+    {
+		if (mp4ff_get_track_type(infile,i)==1)
+		{
+			switch(mp4ff_get_audio_type(infile,i))
+			{
+			case 0x40:
+			case 0x66:
+			case 0x67:
+			case 0x68:
+				codec = "AAC";
+				return i;
+			case 0x6B:
+			case 0x69:
+				codec = "MP3";
+				return i;
+/*			case 0xE1:
+				codec = "Vorbis";
+				return i;
+			case 0xE2:
+				codec = "AC3";
+				return i;*/
+			}
+		}
+    }
+	return -1;
+}
+
+static const char * tech_info_fields[] = {"replaygain_track_gain","replaygain_track_peak","replaygain_album_gain","replaygain_album_peak","tool"};
+
+const char * check_tech_info(const char * name)
+{
+	unsigned n;
+	for(n=0;n<tabsize(tech_info_fields);n++)
+	{
+		if (!stricmp_utf8(name,tech_info_fields[n])) return tech_info_fields[n];
+	}
+	return 0;
+}
+
+
+static void meta_mp4_to_fb2k(const char * name,const char * value,file_info * out)
+{
+	{
+		const char * p_tech_info = check_tech_info(name);
+		if (p_tech_info)
+		{
+			out->info_set(p_tech_info,value);
+			return;
+		}
+	}
+
+	if (!stricmp_utf8(name,"track")) name = "tracknumber";
+
+	out->meta_add(name,value);
+}
+
+
+static void meta_fb2k_to_mp4(const char * name,const char * value,string_base & out_name,string_base & out_value)
+{
+	if (!stricmp_utf8(name,"tracknumber"))
+	{
+		out_name = "track";
+		out_value = value;
+	}
+	else
+	{
+		out_name = name;
+		out_value = value;
+	}
+}
+
+class input_mp4 : public input
+{
+public:
+    int track;
+    void *sample_buffer;
+
+    mp4ff_t *infile;
+    unsigned sampleId, numSamples;
+	unsigned m_offset;
+
+	reader * mp4file;
+
+	packet_decoder * p_decoder;
+
+
+    unsigned char *buffer;
+    unsigned int buffer_size;
+
+
+    /* for gapless decoding */
+
+	mp4ff_callback_t mp4cb;
+	
+	double m_length;
+	uint32_t m_timescale;
+	int m_skip_samples;
+	int m_extra_duration;
+	uint32_t m_skip_frames;
+
+	unsigned m_expected_sample_rate,m_expected_channels;
+
+	audio_chunk_i m_tempchunk;
+
+    static uint32_t callback_read(void *udata, void *buffer, uint32_t length)
+	{
+		return reinterpret_cast<input_mp4*>(udata)->mp4file->read(buffer,length);
+	}
+
+    static uint32_t callback_write(void *udata, void *buffer, uint32_t length)
+	{
+		return reinterpret_cast<input_mp4*>(udata)->mp4file->write(buffer,length);
+	}
+
+    static uint32_t callback_seek(void *udata, uint64_t position)
+	{
+		return reinterpret_cast<input_mp4*>(udata)->mp4file->seek(position) ? 1 : 0;
+	}
+	
+	static uint32_t callback_truncate(void *udata)
+	{
+		return reinterpret_cast<input_mp4*>(udata)->mp4file->set_eof() ? 1 : 0;
+	}
+	
+	void cleanup()
+	{
+		if (infile) {mp4ff_close(infile);infile=0;}
+		if (p_decoder)
+		{
+			p_decoder->service_release();
+			p_decoder = 0;
+		}
+	}
+
+    input_mp4()
+    {
+		mp4cb.user_data = reinterpret_cast<void*>(this);
+		mp4cb.read = callback_read;
+		mp4cb.write = callback_write;
+		mp4cb.seek = callback_seek;
+		mp4cb.truncate = callback_truncate;
+
+		m_skip_frames = 0;
+		p_decoder = 0;
+		infile = 0;
+		m_offset = 0;
+		m_skip_samples = 0;
+		m_extra_duration = 0;
+    }
+
+    ~input_mp4()
+    {
+		cleanup();
+    }
+
+    virtual bool test_filename(const char * fn,const char * ext)
+    {
+        return (!stricmp(ext,"MP4") || !stricmp(ext,"M4A"));
+    }
+
+    virtual bool open(reader *r, file_info *info, unsigned flags)
+    {
+		if (!r->can_seek())
+		{
+			console::error("Unseekable streams not supported.");
+			return false;
+		}
+		string8 codecname;
+
+		cleanup();
+
+		mp4file = r;
+
+
+
+		infile = mp4ff_open_read(&mp4cb);
+		if (!infile)
+		{
+			cleanup();
+			console::error("Error parsing MP4 file.");
+			return 0;
+		}
+
+		if ((track = find_track_to_decode(infile,codecname)) < 0)
+		{
+			cleanup();
+			console::error("Unable to find correct sound track in the MP4 file.");
+			return 0;
+		}
+
+		p_decoder = packet_decoder::create(codecname);
+		if (p_decoder == 0)
+		{
+			cleanup();
+			console::error("Unable to find correct packet decoder object.");
+			return 0;
+		}
+
+		info->info_set("codec",codecname);
+
+		buffer = NULL;
+		buffer_size = 0;
+		mp4ff_get_decoder_config(infile, track, &buffer, &buffer_size);
+
+		if (!p_decoder->init(buffer,buffer_size,info))
+		{
+			if (buffer) free(buffer);
+			cleanup();
+			console::error("Error initializing decoder.");
+			
+			return 0;
+		}
+
+		m_timescale = mp4ff_time_scale(infile,track);
+
+		if (buffer)
+		{
+			free(buffer);
+		}
+
+		{
+			char *tag = NULL, *item = NULL;
+			int k, j;
+			
+			
+			if (m_timescale<=0)
+				m_length = -1.0;
+			else
+			{
+				int64_t duration = mp4ff_get_track_duration(infile,track);
+				if (duration == -1)
+					m_length = -1.0;
+				else
+				{
+					m_length = (double)duration / (double)m_timescale;
+				}
+			}
+
+			info->set_length(m_length);
+
+			m_expected_sample_rate = mp4ff_get_sample_rate(infile,track);
+			m_expected_channels = mp4ff_get_channel_count(infile,track);
+
+			if (flags & OPEN_FLAG_GET_INFO)
+			{
+				info->info_set_int("bitrate",(mp4ff_get_avg_bitrate(infile,track) + 500) / 1000);
+				info->info_set_int("channels",m_expected_channels);
+				info->info_set_int("samplerate",m_expected_sample_rate);
+
+				j = mp4ff_meta_get_num_items(infile);
+				for (k = 0; k < j; k++)
+				{
+					if (mp4ff_meta_get_by_index(infile, k, &item, &tag))
+					{
+						if (item != NULL && tag != NULL)
+						{
+							meta_mp4_to_fb2k(item,tag,info);
+							free(item); item = NULL;
+							free(tag); tag = NULL;
+						}
+					}
+				}
+			}
+		}
+
+		numSamples = mp4ff_num_samples(infile, track);
+
+		m_skip_samples = 0;
+		m_extra_duration = 0;
+
+		m_offset = 0;
+
+		sampleId = 0;
+
+        return 1;
+    }
+
+    virtual int run(audio_chunk * chunk)
+    {
+		if (p_decoder==0)
+		{
+			console::error("Attempting to decode while not open.");
+			return -1;
+		}
+
+		if (sampleId >= numSamples) return 0;
+
+		bool done = false;
+
+		do		
+		{
+			/* get acces unit from MP4 file */
+			buffer = NULL;
+			buffer_size = 0;
+
+			{
+				unsigned offset = mp4ff_get_sample_offset(infile,track,sampleId);
+				int delta = offset - m_offset;
+				m_skip_samples += delta;
+				m_extra_duration += delta;
+				m_offset = offset;
+			}
+			
+			unsigned duration = mp4ff_get_sample_duration(infile, track, sampleId);
+//			console::info(uStringPrintf("duration: %u",duration));
+
+			if (mp4ff_read_sample(infile, track, sampleId, &buffer,  &buffer_size) == 0)
+			{
+				cleanup();
+				console::error("Reading from MP4 file failed.");
+				return -1;
+			}
+
+			m_tempchunk.reset();
+			if (!p_decoder->decode(buffer,buffer_size,&m_tempchunk))
+			{
+				cleanup();
+				console::error("Decode error.");
+				return -1;
+			}
+
+			if (buffer) free(buffer);
+
+			if (m_skip_frames>0) m_skip_frames--;
+			else
+			{
+				if (m_tempchunk.is_empty())
+				{
+					if (duration > 0)
+					{
+						m_tempchunk.set_srate(m_expected_sample_rate);
+						m_tempchunk.set_channels(m_expected_channels);
+						m_tempchunk.pad_with_silence(duration);
+					//	console::warning("Decoder returned empty chunk from a non-empty MP4 frame.");
+					}
+				}
+				else
+				{
+					if (m_tempchunk.get_srate() != m_expected_sample_rate)
+					{
+						cleanup();
+						console::error(uStringPrintf("Expected sample rate: %u, got: %u.",m_expected_sample_rate,m_tempchunk.get_srate()));
+						return -1;
+					}
+					if (m_tempchunk.get_channels() != m_expected_channels)
+					{
+						cleanup();
+						console::error(uStringPrintf("Expected channels: %u, got: %u.",m_expected_channels,m_tempchunk.get_channels()));
+						return -1;
+					}
+				}
+				unsigned samplerate,channels,decoded_sample_count;
+
+				samplerate = m_tempchunk.get_srate();
+				channels = m_tempchunk.get_channels();
+				decoded_sample_count = m_tempchunk.get_sample_count();
+
+				if (decoded_sample_count < duration)
+				{
+					//console::warning("Decoded MP4 frame smaller than expected.");
+					decoded_sample_count = duration;
+					m_tempchunk.pad_with_silence(decoded_sample_count);
+				}
+				else if (decoded_sample_count > duration && m_extra_duration>0)
+				{
+					unsigned duration2 = duration + m_extra_duration;
+					if (duration2 > decoded_sample_count) duration2 = decoded_sample_count;
+					m_extra_duration -= duration2 - duration;
+//					console::info(uStringPrintf("duration: %u => %u",duration,duration2));
+					duration = duration2;
+				}
+
+				unsigned offset = 0;
+				if (m_skip_samples>0)
+				{
+					unsigned int delta = (unsigned)m_skip_samples;
+					if (delta > duration) delta = duration;
+					offset += delta;
+					duration -= delta;
+					m_skip_samples -= delta;
+				}
+				else if (m_skip_samples<0)
+				{
+					//blah....
+					m_tempchunk.insert_silence_fromstart(-m_skip_samples);
+					m_skip_samples = 0;
+				}
+
+
+				if (duration > 0)
+				{
+					chunk->set_data_64(m_tempchunk.get_data() + offset * channels,duration,channels,samplerate);
+					done = true;
+				}
+			}
+			sampleId++;
+		}
+		while(!done && sampleId < numSamples);
+		
+
+        return done ? 1 : 0;
+    }
+
+    virtual set_info_t set_info(reader *r, const file_info * info)
+    {
+#if 0
+/* metadata tag structure */
+typedef struct
+{
+    char *item;
+    char *value;
+} mp4ff_tag_t;
+
+/* metadata list structure */
+typedef struct
+{
+    mp4ff_tag_t *tags;
+    uint32_t count;
+} mp4ff_metadata_t;
+#endif
+
+
+
+		unsigned rv;
+		ptr_list_t<char> freeme;
+		mem_block_list_t<mp4ff_tag_t> tags;
+		mp4ff_metadata_t mp4meta;
+
+		{
+			string8_fastalloc name,value;
+			mp4ff_tag_t tag;
+
+			unsigned n, m;
+			
+			m = info->meta_get_count();
+			for(n=0;n<m;n++)
+			{
+				meta_fb2k_to_mp4(info->meta_enum_name(n),info->meta_enum_value(n),name,value);
+				freeme.add_item(tag.item = strdup(name));
+				freeme.add_item(tag.value = strdup(value));
+				tags.add_item(tag);
+			}
+
+
+			m = info->info_get_count();
+
+			for(n=0;n<m;n++)
+			{
+				const char * p_name = check_tech_info(info->info_enum_name(n));
+				if (p_name)
+				{
+					tag.item = const_cast<char*>(p_name);
+					tag.value = const_cast<char*>(info->info_enum_value(n));
+					tags.add_item(tag);
+				}
+			}
+		}
+
+		mp4meta.count = tags.get_count();
+		mp4meta.tags = const_cast<mp4ff_tag_t*>(tags.get_ptr());
+
+		mp4file = r;
+		rv = mp4ff_meta_update(&mp4cb,&mp4meta);
+		mp4file = 0;
+
+		freeme.free_all();
+
+		return rv ? SET_INFO_SUCCESS : SET_INFO_FAILURE;
+
+    }
+
+    virtual bool seek(double seconds)
+    {
+		if (p_decoder == 0)
+		{
+			console::error("Attempting to seek while not open.");
+			return false;
+		}
+        if (seconds >= m_length) {
+            sampleId = numSamples;
+            return true;
+        }
+		
+		unsigned max_frame_dependency = p_decoder->get_max_frame_dependency();
+		int64_t offset = (int64_t)(seconds * m_timescale + 0.5);
+		int32_t skip_samples = 0;
+		unsigned dest_sample = mp4ff_find_sample(infile,track,offset,&skip_samples);
+//		console::info(uStringPrintf("%u %u %u",(unsigned)offset,dest_sample,skip_samples));
+
+		if (dest_sample == (-1)) return false;
+
+		if (dest_sample < max_frame_dependency)
+		{
+			m_skip_frames = dest_sample;
+			dest_sample = 0;
+		}
+		else
+		{
+			m_skip_frames = max_frame_dependency;
+			dest_sample -= max_frame_dependency;
+		}
+
+		sampleId = dest_sample;
+		
+		m_skip_samples = skip_samples;
+
+		m_offset = 0;
+		m_extra_duration = 0;
+
+		p_decoder->reset_after_seek();
+
+        return true;
+
+    }
+
+    virtual bool is_our_content_type(const char *url, const char *type)
+    {
+        return !stricmp(type, "audio/mp4") || !stricmp(type, "audio/x-mp4");
+    }
+};
+
+static service_factory_t<input, input_mp4> foo_mp4;
\ No newline at end of file
--- /dev/null
+++ b/plugins/foo_mp4/packet_decoder_aac.cpp
@@ -1,0 +1,126 @@
+#include "foobar2000/SDK/foobar2000.h"
+#include <faad.h>
+
+
+class packet_decoder_aac : public packet_decoder
+{
+    faacDecHandle hDecoder;
+    faacDecFrameInfo frameInfo;
+	void cleanup()
+	{
+		if (hDecoder) {faacDecClose(hDecoder);hDecoder=0;}
+	}
+public:
+	packet_decoder_aac()
+	{
+		hDecoder = 0;
+	}
+
+	~packet_decoder_aac()
+	{
+		cleanup();
+	}
+	
+	virtual bool is_our_type(const char * name) {return !stricmp_utf8(name,"AAC");}
+	
+	virtual unsigned get_max_frame_dependency() {return 1;}
+	
+	virtual bool init(const void * data,unsigned bytes,file_info * info)
+	{
+		if (hDecoder) faacDecClose(hDecoder);
+		if (data==0) return false;
+		hDecoder = faacDecOpen();
+		if (hDecoder == 0)
+		{
+			cleanup();
+			console::error("Failed to open FAAD2 library.");
+			return false;
+		}
+
+		{
+			faacDecConfigurationPtr config;
+            config = faacDecGetCurrentConfiguration(hDecoder);
+            config->outputFormat = 
+#if audio_sample_size == 64
+				FAAD_FMT_DOUBLE
+#else
+				FAAD_FMT_FLOAT
+#endif
+				;
+            faacDecSetConfiguration(hDecoder, config);
+		}
+
+		unsigned long t_samplerate;
+		unsigned char t_channels;
+
+		if(faacDecInit2(hDecoder, (unsigned char*)data, bytes,
+						&t_samplerate,&t_channels) < 0)
+		{
+			faacDecClose(hDecoder);
+			hDecoder = 0;
+			return false;
+		}
+
+		{
+			mp4AudioSpecificConfig mp4ASC;
+			if (AudioSpecificConfig((unsigned char*)data, bytes, &mp4ASC) >= 0)
+			{
+				static const char *ot[6] = { "NULL", "MAIN AAC", "LC AAC", "SSR AAC", "LTP AAC", "HE AAC" };
+				info->info_set("aac_profile",ot[(mp4ASC.objectTypeIndex > 5)?0:mp4ASC.objectTypeIndex]);
+			}
+		}
+
+
+
+		return true;
+
+
+	}
+	
+	virtual void reset_after_seek()
+	{
+		if (hDecoder)
+		{
+			faacDecPostSeekReset(hDecoder, -1);
+		}
+	}
+
+	virtual bool decode(const void * buffer,unsigned bytes,audio_chunk * out)
+	{
+		const audio_sample * sample_buffer = (const audio_sample*)faacDecDecode(hDecoder, &frameInfo, (unsigned char*)buffer, bytes);
+
+		if (frameInfo.error > 0)
+		{
+			cleanup();
+			console::error(faacDecGetErrorMessage(frameInfo.error));
+			return false;
+		}
+
+		if (frameInfo.channels<=0)
+		{
+			cleanup();
+			console::error("Internal decoder error.");
+			return false;
+		}
+
+		if (frameInfo.samples)
+		{
+			return out->set_data(sample_buffer,frameInfo.samples / frameInfo.channels,frameInfo.channels,frameInfo.samplerate);
+		}
+		else
+		{
+			out->reset();
+			return true;
+		}
+	}
+	virtual const char * get_name() {return "MPEG-4 AAC";}
+};
+
+static packet_decoder_factory<packet_decoder_aac> AHAHAHAHA;
+
+
+bool is_valid_aac_decoder_config(const void * data,unsigned bytes)
+{
+	mp4AudioSpecificConfig mp4ASC;
+	return AudioSpecificConfig((unsigned char*)data, bytes, &mp4ASC)>=0;
+}
\ No newline at end of file