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 Mpeg4AAClicense@nero.com.
-**
-** $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 Mpeg4AAClicense@nero.com.
+**
+** $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