ref: 2b65b584656d94459a29da0ebfef673e5c5d9ca1
dir: /plugins/foo_mp4/foo_mp4.cpp/
/*
** FAAD - Freeware Advanced Audio Decoder
** Copyright (C) 2002 M. Bakker
**
** 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.
**
** $Id: foo_mp4.cpp,v 1.15 2003/04/02 20:07:46 menno Exp $
**/
#include <mp4.h>
#include <faad.h>
#include "pfc/pfc.h"
#include "foobar2000/SDK/input.h"
class input_mp4 : public input
{
public:
virtual int test_filename(const char * fn,const char * ext)
{
return !stricmp(ext,"MP4");
}
virtual int open(reader *r, file_info *info, int full_open)
{
unsigned __int8 *buffer;
unsigned __int32 buffer_size;
unsigned __int8 channels;
unsigned __int32 samplerate;
faacDecConfigurationPtr config;
m_reader = r;
hDecoder = faacDecOpen();
if (!hDecoder) return 0;
config = faacDecGetCurrentConfiguration(hDecoder);
config->outputFormat = FAAD_FMT_DOUBLE;
faacDecSetConfiguration(hDecoder, config);
hFile = MP4ReadCb(0, open_cb, close_cb, read_cb, write_cb,
setpos_cb, getpos_cb, filesize_cb, (void*)m_reader);
if (hFile == MP4_INVALID_FILE_HANDLE) return 0;
track = GetAACTrack(hFile);
if (track < 1) return 0;
buffer = NULL;
buffer_size = 0;
MP4GetTrackESConfiguration(hFile, track, &buffer, &buffer_size);
if (!buffer) return 0;
int rc = faacDecInit2(hDecoder, (unsigned char*)buffer, buffer_size,
(unsigned long*)&samplerate, (unsigned char*)&channels);
if (buffer) free(buffer);
if (rc < 0) return 0;
numSamples = MP4GetTrackNumberOfSamples(hFile, track);
sampleId = 1;
unsigned __int64 length = MP4GetTrackDuration(hFile, track);
__int64 msDuration = MP4ConvertFromTrackDuration(hFile, track,
length, MP4_MSECS_TIME_SCALE);
info->set_length((double)msDuration/1000.0);
info->info_set_int("bitrate",(__int64)(1.0/1000.0 *
(double)(__int64)MP4GetTrackIntegerProperty(hFile,
track, "mdia.minf.stbl.stsd.mp4a.esds.decConfigDescr.avgBitrate")) + 0.5);
info->info_set_int("channels", (__int64)channels);
info->info_set_int("samplerate", (__int64)samplerate);
ReadMP4Tag(info);
return 1;
}
input_mp4()
{
hFile = MP4_INVALID_FILE_HANDLE;
hDecoder = NULL;
}
~input_mp4()
{
if (hFile != MP4_INVALID_FILE_HANDLE)
MP4Close(hFile);
if (hDecoder)
faacDecClose(hDecoder);
}
virtual int run(audio_chunk * chunk)
{
faacDecFrameInfo frameInfo;
unsigned char *buffer;
unsigned __int32 buffer_size;
void *sample_buffer;
if (sampleId == MP4_INVALID_SAMPLE_ID)
return 0;
do {
buffer = NULL;
buffer_size = 0;
MP4ReadSample(hFile, track, sampleId,
(unsigned __int8**)&buffer, &buffer_size,
NULL, NULL, NULL, NULL);
sampleId++;
sample_buffer = faacDecDecode(hDecoder, &frameInfo, buffer, buffer_size);
if (buffer) free(buffer);
} while ((frameInfo.error == 0) && (frameInfo.samples == 0));
if (frameInfo.error || (sampleId > numSamples))
return 0;
chunk->data = (audio_sample*)sample_buffer;
chunk->samples = frameInfo.samples/frameInfo.channels;
chunk->nch = frameInfo.channels;
chunk->srate = frameInfo.samplerate;
return 1;
}
virtual int set_info(reader *r,const file_info * info)
{
m_reader = r;
hFile = MP4ModifyCb(0, 0, open_cb, close_cb, read_cb, write_cb,
setpos_cb, getpos_cb, filesize_cb, (void*)m_reader);
if (hFile == MP4_INVALID_FILE_HANDLE) return 0;
track = GetAACTrack(hFile);
if (track < 1) return 0;
MP4TagDelete(hFile, track);
/* replay gain writing */
const char *p = NULL;
p = info->info_get("REPLAYGAIN_TRACK_PEAK");
if (p)
MP4TagAddEntry(hFile, track, "REPLAYGAIN_TRACK_PEAK", p);
p = info->info_get("REPLAYGAIN_TRACK_GAIN");
if (p)
MP4TagAddEntry(hFile, track, "REPLAYGAIN_TRACK_GAIN", p);
p = info->info_get("REPLAYGAIN_ALBUM_PEAK");
if (p)
MP4TagAddEntry(hFile, track, "REPLAYGAIN_ALBUM_PEAK", p);
p = info->info_get("REPLAYGAIN_ALBUM_GAIN");
if (p)
MP4TagAddEntry(hFile, track, "REPLAYGAIN_ALBUM_GAIN", p);
int numItems = info->meta_get_count();
if (numItems > 0)
{
for (int i = 0; i < numItems; i++)
{
const char *n = info->meta_enum_name(i);
const char *v = info->meta_enum_value(i);
MP4TagAddEntry(hFile, track, n, v);
}
}
numItems = MP4TagGetNumEntries(hFile, track);
if (numItems == 0)
MP4TagDelete(hFile, track);
/* end */
return 1;
}
virtual int seek(double seconds)
{
MP4Duration duration;
duration = MP4ConvertToTrackDuration(hFile,
track, seconds, MP4_SECS_TIME_SCALE);
sampleId = MP4GetSampleIdFromTime(hFile,
track, duration, 0);
return 1;
}
private:
reader *m_reader;
MP4FileHandle hFile;
MP4SampleId sampleId, numSamples;
MP4TrackId track;
faacDecHandle hDecoder;
int ReadMP4Tag(file_info *info)
{
int numItems = MP4TagGetNumEntries(hFile, track);
for (int i = 0; i < numItems; i++)
{
float f = 0.0;
const char *n = NULL, *v = NULL;
MP4TagGetEntry(hFile, track, i, &n, &v);
if (!strcmp(n, "REPLAYGAIN_TRACK_PEAK"))
{
sscanf(v, "%f", &f);
info->info_set_replaygain_track_peak((double)f);
} else if (!strcmp(n, "REPLAYGAIN_TRACK_GAIN")) {
sscanf(v, "%f", &f);
info->info_set_replaygain_track_gain((double)f);
} else if (!strcmp(n, "REPLAYGAIN_ALBUM_PEAK")) {
sscanf(v, "%f", &f);
info->info_set_replaygain_album_peak((double)f);
} else if (!strcmp(n, "REPLAYGAIN_ALBUM_GAIN")) {
sscanf(v, "%f", &f);
info->info_set_replaygain_album_gain((double)f);
} else {
info->meta_add(n, v);
}
}
return 1;
}
int GetAACTrack(MP4FileHandle infile)
{
/* find AAC track */
int i, rc;
int numTracks = MP4GetNumberOfTracks(infile, NULL, /* subType */ 0);
for (i = 0; i < numTracks; i++)
{
MP4TrackId trackId = MP4FindTrackId(infile, i, NULL, /* subType */ 0);
const char* trackType = MP4GetTrackType(infile, trackId);
if (!strcmp(trackType, MP4_AUDIO_TRACK_TYPE))
{
unsigned char *buff = NULL;
int buff_size = 0;
mp4AudioSpecificConfig mp4ASC;
MP4GetTrackESConfiguration(infile, trackId,
(unsigned __int8**)&buff, (unsigned __int32*)&buff_size);
if (buff)
{
rc = AudioSpecificConfig(buff, buff_size, &mp4ASC);
free(buff);
if (rc < 0)
return -1;
return trackId;
}
}
}
/* can't decode this */
return -1;
}
/* file callback stuff */
static unsigned __int32 open_cb(const char *pName,
const char *mode, void *userData)
{
return 1;
}
static void close_cb(void *userData)
{
return;
}
static unsigned __int32 read_cb(void *pBuffer, unsigned int nBytesToRead,
void *userData)
{
reader *r = (reader*)userData;
return r->read(pBuffer, nBytesToRead);
}
static unsigned __int32 write_cb(void *pBuffer, unsigned int nBytesToWrite,
void *userData)
{
reader *r = (reader*)userData;
return r->write(pBuffer, nBytesToWrite);
}
static __int64 getpos_cb(void *userData)
{
reader *r = (reader*)userData;
return r->get_position();
}
static __int32 setpos_cb(unsigned __int32 pos, void *userData)
{
reader *r = (reader*)userData;
return !(r->seek(pos));
}
static __int64 filesize_cb(void *userData)
{
reader *r = (reader*)userData;
return r->get_length();
}
};
static service_factory_t<input,input_mp4> foo;