ref: 3cb7da83d454c960f48fbb589a7372873a322c5c
parent: 0703acd9be241cc1121d7f37d5177f67ca11c17f
author: menno <menno>
date: Thu Dec 11 13:38:14 EST 2003
new mp4 library now used in foobar plugin
--- a/plugins/foo_mp4/foo_mp4.cpp
+++ b/plugins/foo_mp4/foo_mp4.cpp
@@ -22,33 +22,20 @@
** 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.75 2003/11/22 18:08:05 ca5e Exp $
+** $Id: foo_mp4.cpp,v 1.76 2003/12/11 18:38:13 menno Exp $
**/
-#include <mp4.h>
-#include <faad.h>
#include "foobar2000/SDK/foobar2000.h"
#include "foobar2000/foo_input_std/id3v2_hacks.h"
-#include "convert.h"
+#define USE_TAGGING
+#include <mp4ff.h>
+#include <faad.h>
//#define DBG_OUT(A) OutputDebugString(A)
#define DBG_OUT(A)
-#if 0
-char *STRIP_REVISION(const char *str)
-{
- char *tmp = strchr(str, ' ');
- int len = lstrlen(tmp)-2;
- if (len > 0 && tmp)
- tmp[len] = '\0';
- else
- tmp = "000";
- return tmp;
-}
-#endif
-
DECLARE_COMPONENT_VERSION ("MPEG-4 AAC decoder",
- "1.69",
+ "2.0",
"Based on FAAD2 v" FAAD2_VERSION "\nCopyright (C) 2002-2003 http://www.audiocoding.com" );
static const char *object_type_string(int type)
@@ -92,620 +79,487 @@
return types[type-1];
}
-class input_mp4 : public input
+static int GetAACTrack(mp4ff_t *infile)
{
-public:
+ /* find AAC track */
+ int i, rc;
+ int numTracks = mp4ff_total_tracks(infile);
- virtual bool test_filename(const char * fn,const char * ext)
+ for (i = 0; i < numTracks; i++)
{
- return (!stricmp(ext,"MP4") || !stricmp(ext,"M4A"));
- }
-
- virtual bool open(reader *r, file_info *info, unsigned flags)
- {
- unsigned __int8 *buffer;
- unsigned __int32 buffer_size;
- unsigned __int8 channels;
- unsigned __int32 samplerate;
-
- faacDecConfigurationPtr config;
+ unsigned char *buff = NULL;
+ unsigned int buff_size = 0;
mp4AudioSpecificConfig mp4ASC;
- m_reader = r;
+ mp4ff_get_decoder_config(infile, i, &buff, &buff_size);
- if (!m_reader->can_seek())
+ if (buff)
{
- console::error("MP4 file needs seeking.");
- return 0;
- }
+ rc = AudioSpecificConfig(buff, buff_size, &mp4ASC);
+ free(buff);
- hDecoder = faacDecOpen();
- if (!hDecoder)
- {
- console::error("Failed to open FAAD2 library.");
- return 0;
+ if (rc < 0)
+ continue;
+ return i;
}
+ }
- config = faacDecGetCurrentConfiguration(hDecoder);
- config->outputFormat = FAAD_FMT_DOUBLE;
- faacDecSetConfiguration(hDecoder, config);
+ /* can't decode this */
+ return -1;
+}
- 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)
- {
- console::error("Failed to open MP4 file.");
- return 0;
- }
+static const char * tech_info_fields[] = {"replaygain_track_gain","replaygain_track_peak","replaygain_album_gain","replaygain_album_peak","tool"};
- track = GetAudioTrack(hFile);
- if (track < 0)
- {
- console::error("No valid audio track found.");
- return 0;
- }
+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;
+}
- unsigned __int8 audioType = MP4GetTrackEsdsObjectTypeId(hFile, track);
- if (!MP4_IS_AAC_AUDIO_TYPE(audioType))
- {
- console::error("No valid AAC track found.");
- return 0;
- }
- buffer = NULL;
- buffer_size = 0;
- MP4GetTrackESConfiguration(hFile, track, &buffer, &buffer_size);
+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;
+ }
+ }
- int ADIF_ASC = 0;
- if ((buffer_size >= 4) && ((buffer[0] == 'A') && (buffer[1] == 'D') &&
- (buffer[2] == 'I') && (buffer[3] == 'F')))
- {
- ADIF_ASC = 1;
- }
+ if (!stricmp_utf8(name,"track")) name = "tracknumber";
- if (buffer_size == 0 || ADIF_ASC == 1 /*MP4_IS_MPEG2_AAC_AUDIO_TYPE(audioType)*/)
- {
- int rc = faacDecInit(hDecoder, (unsigned char*)buffer, buffer_size,
- (unsigned long*)&samplerate, (unsigned char*)&channels);
- if (buffer) free(buffer);
- if (rc < 0)
- {
- console::error("Unable to initialise FAAD2 library.");
- return 0;
- }
+ out->meta_add(name,value);
+}
- mp4ASC.frameLengthFlag = 0;
- mp4ASC.objectTypeIndex = audioType - 0x65;
- } else {
- AudioSpecificConfig((unsigned char*)buffer, buffer_size, &mp4ASC);
- int rc = faacDecInit2(hDecoder, (unsigned char*)buffer, buffer_size,
- (unsigned long*)&samplerate, (unsigned char*)&channels);
- if (buffer) free(buffer);
- if (rc < 0)
- {
- console::error("Unable to initialise FAAD2 library.");
- return 0;
- }
- }
+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;
+ }
+}
- numSamples = MP4GetTrackNumberOfSamples(hFile, track);
- sampleId = 1;
+class input_mp4 : public input
+{
+public:
+ int track;
+ unsigned long m_samplerate;
+ unsigned char channels;
+ void *sample_buffer;
- m_timescale = MP4GetTrackTimeScale(hFile, track);
- m_samplerate = samplerate;
- m_seekto = 0;
- m_framesize = 1024;
- if (mp4ASC.frameLengthFlag == 1) m_framesize = 960;
- useAacLength = false;
+ mp4ff_t *infile;
+ long sampleId, numSamples;
- {
- MP4Timestamp sample_pos;
- MP4Duration sample_dur;
- unsigned char *buf = NULL;
- unsigned __int32 buf_size = 0;
- MP4ReadSample(hFile, track, 1, (unsigned __int8**)&buf,
- &buf_size, &sample_pos, &sample_dur, NULL, NULL);
- if (buf) free(buf);
- m_delaydur = (unsigned int)sample_dur;
- }
- MP4Duration trackDuration = MP4GetTrackDuration(hFile, track) - m_delaydur;
- m_length = (double)(__int64)trackDuration / (double)m_timescale;
- info->set_length(m_length);
- 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);
+ reader * mp4file;
- const char *profile_str = object_type_string(mp4ASC.objectTypeIndex);
- if (profile_str)
- info->info_set("aac_profile", profile_str);
+ faacDecHandle hDecoder;
+ faacDecFrameInfo frameInfo;
+ mp4AudioSpecificConfig mp4ASC;
- if (mp4ASC.sbr_present_flag == 1) {
- info->info_set("codec", "AAC+SBR");
- m_framesize *= 2;
- } else {
- info->info_set("codec", "AAC");
- }
+ unsigned char *buffer;
+ unsigned int buffer_size;
- if (flags & OPEN_FLAG_GET_INFO)
- ReadMP4Tag(info);
- return 1;
- }
+ /* 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()
{
- hFile = MP4_INVALID_FILE_HANDLE;
- hDecoder = NULL;
- m_eof = false;
+ 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()
{
- if (hFile != MP4_INVALID_FILE_HANDLE)
- MP4Close(hFile);
- if (hDecoder)
- faacDecClose(hDecoder);
+ cleanup();
}
- virtual int run(audio_chunk * chunk)
+ virtual bool test_filename(const char * fn,const char * ext)
{
- faacDecFrameInfo frameInfo;
- audio_sample *sample_buffer;
- MP4Timestamp sample_pos;
- MP4Duration sample_dur;
- unsigned __int64 sample_count;
- unsigned __int64 delay = 0;
- bool initial = (sampleId == 1);
+ return (!stricmp(ext,"MP4") || !stricmp(ext,"M4A"));
+ }
- do {
- if (m_eof || (sampleId > numSamples)) return 0;
+ virtual bool open(reader *r, file_info *info, unsigned flags)
+ {
+ cleanup();
- if (sampleId == MP4_INVALID_SAMPLE_ID)
- {
- console::error("Invalid sampleId.");
- return 0;//-1;
- }
+ mp4file = r;
- do {
- unsigned char *buffer = NULL;
- unsigned __int32 buffer_size = 0;
- delay = 0;
+ hDecoder = faacDecOpen();
+ if (hDecoder == 0)
+ {
+ cleanup();
+ console::error("Failed to open FAAD2 library.");
+ return 0;
+ }
- MP4ReadSample(hFile, track, sampleId,
- (unsigned __int8**)&buffer, &buffer_size,
- &sample_pos, &sample_dur, NULL, NULL);
- if (sampleId == 1) sample_dur -= m_delaydur;
- sample_pos -= m_delaydur;
- sampleId++;
+ {
+ faacDecConfigurationPtr config;
+ config = faacDecGetCurrentConfiguration(hDecoder);
+ config->outputFormat = FAAD_FMT_DOUBLE;
+ faacDecSetConfiguration(hDecoder, config);
+ }
- sample_buffer = (audio_sample*)faacDecDecode(hDecoder, &frameInfo, buffer, buffer_size);
- if (buffer) free(buffer);
+ infile = mp4ff_open_read(&mp4cb);
+ if (!infile)
+ {
+ cleanup();
+ console::error("Error parsing MP4 file.");
+ return 0;
+ }
- if (useAacLength || (m_timescale != m_samplerate)) {
- sample_count = frameInfo.channels ? frameInfo.samples/frameInfo.channels : 0;
- } else {
- sample_count = sample_dur;
+ if ((track = GetAACTrack(infile)) < 0)
+ {
+ cleanup();
+ console::error("Unable to find correct AAC sound track in the MP4 file.");
+ return 0;
+ }
- if (!useAacLength && !initial && m_seekto<sample_pos && (sampleId < numSamples/2) && (sample_dur*frameInfo.channels != frameInfo.samples))
- {
- console::info("MP4 seems to have incorrect frame duration, using values from AAC data");
- useAacLength = true;
- }
- }
- if (initial && (sample_count < m_framesize) && frameInfo.channels)
- delay = (frameInfo.samples/frameInfo.channels) - sample_count;
- if (frameInfo.error || !sample_buffer)
- {
- const char *msg;
- if (frameInfo.error)
- msg = faacDecGetErrorMessage(frameInfo.error);
- else
- msg = "faacDecDecode() error";
- console::warning(msg);
- if (sampleId > numSamples) return 0;//-1;
- console::info("Skipping frame");
- sample_count = 0;
- }
- } while (frameInfo.error || frameInfo.samples == 0 || frameInfo.channels == 0 || sample_count == 0);
- unsigned __int64 skip = (sample_pos < m_seekto) ? (m_seekto - sample_pos) : 0;
+ buffer = NULL;
+ buffer_size = 0;
+ mp4ff_get_decoder_config(infile, track, &buffer, &buffer_size);
- if (skip < sample_count)
- {
- 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;
- }
- }
+ if(faacDecInit2(hDecoder, buffer, buffer_size,
+ &m_samplerate,&channels) < 0)
+ {
+ cleanup();
+ console::error("Error initializing decoder library.");
+
+ return 0;
+ }
- unsigned int samples = (unsigned int)(sample_count - skip);
+ m_timescale = mp4ff_time_scale(infile,track);
+ framesize = 1024;
- chunk->set_data((audio_sample*)sample_buffer + (unsigned int)(skip+delay)*frameInfo.channels,
- samples, frameInfo.channels, frameInfo.samplerate);
+ 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);
+ }
- m_seekto = 0;
- }
- } while (sample_pos + sample_dur <= m_seekto);
+ {
+ 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;
- return 1;
- }
+ if ((mp4ASC.sbr_present_flag == 1) || mp4ASC.forceUpSampling) f = f * 2.0;
+ seconds = (float)samples*(float)(f-1.0)/(float)mp4ASC.samplingFrequency;
- virtual set_info_t set_info(reader *r, const file_info * info)
- {
- m_reader = r;
+
+ 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;
+ }
+ }
- 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 SET_INFO_FAILURE;
+ info->set_length(m_length);
- MP4MetadataDelete(hFile);
- MP4Close(hFile);
- 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 SET_INFO_FAILURE;
+ 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]);
- const char *p = info->info_get("TOOL");
- if (p && *p)
- MP4SetMetadataTool(hFile, p);
- p = info->info_get("REPLAYGAIN_TRACK_PEAK");
- if (p && *p)
- MP4SetMetadataFreeForm(hFile, "REPLAYGAIN_TRACK_PEAK", (unsigned __int8*)p, strlen(p));
- p = info->info_get("REPLAYGAIN_TRACK_GAIN");
- if (p && *p)
- MP4SetMetadataFreeForm(hFile, "REPLAYGAIN_TRACK_GAIN", (unsigned __int8*)p, strlen(p));
- p = info->info_get("REPLAYGAIN_ALBUM_PEAK");
- if (p && *p)
- MP4SetMetadataFreeForm(hFile, "REPLAYGAIN_ALBUM_PEAK", (unsigned __int8*)p, strlen(p));
- p = info->info_get("REPLAYGAIN_ALBUM_GAIN");
- if (p && *p)
- MP4SetMetadataFreeForm(hFile, "REPLAYGAIN_ALBUM_GAIN", (unsigned __int8*)p, strlen(p));
+ 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;
+ }
+ }
+ }
+ }
+ }
- int numItems = info->meta_get_count();
- for (int i = 0; i < numItems; i++)
- {
- char *pName = (char *)info->meta_enum_name(i);
- const char *val = info->meta_enum_value(i);
- if (!pName || (pName && !*pName) || !val || (val && !*val)) continue;
+ numSamples = mp4ff_num_samples(infile, track);
- if (!stricmp(pName, "TOTALTRACKS") || !stricmp(pName, "TOTALDISCS")) continue;
+ m_skip_samples = 0;
- if (stricmp(pName, "TITLE") == 0)
- {
- MP4SetMetadataName(hFile, val);
- } else if (stricmp(pName, "ARTIST") == 0) {
- MP4SetMetadataArtist(hFile, val);
- } else if (stricmp(pName, "WRITER") == 0) {
- MP4SetMetadataWriter(hFile, val);
- } else if (stricmp(pName, "ALBUM") == 0) {
- MP4SetMetadataAlbum(hFile, val);
- } else if (stricmp(pName, "YEAR") == 0 || stricmp(pName, "DATE") == 0) {
- MP4SetMetadataYear(hFile, val);
- } else if (stricmp(pName, "COMMENT") == 0) {
- MP4SetMetadataComment(hFile, val);
- } else if (stricmp(pName, "GENRE") == 0) {
- MP4SetMetadataGenre(hFile, val);
- } else if (stricmp(pName, "TRACKNUMBER") == 0 || stricmp(pName, "TRACK") == 0) {
- int t1 = 0, t2 = 0;
- sscanf(val, "%d/%d", &t1, &t2);
- unsigned __int16 trkn = t1, tot = t2;
- const char *t = info->meta_get("TOTALTRACKS");
- if (t && *t) tot = atoi(t);
- MP4SetMetadataTrack(hFile, trkn, tot);
- } else if (stricmp(pName, "DISKNUMBER") == 0 || stricmp(pName, "DISC") == 0) {
- int t1 = 0, t2 = 0;
- sscanf(val, "%d/%d", &t1, &t2);
- unsigned __int16 disk = t1, tot = t2;
- const char *t = info->meta_get("TOTALDISCS");
- if (t && *t) tot = atoi(t);
- MP4SetMetadataDisk(hFile, disk, tot);
- } else if (stricmp(pName, "COMPILATION") == 0) {
- unsigned __int8 cpil = atoi(val);
- MP4SetMetadataCompilation(hFile, cpil);
- } else if (stricmp(pName, "TEMPO") == 0) {
- unsigned __int16 tempo = atoi(val);
- MP4SetMetadataTempo(hFile, tempo);
- } else {
- MP4SetMetadataFreeForm(hFile, pName, (unsigned __int8*)val, strlen(val));
- }
- }
+ sampleId = 0;
- MP4Close(hFile);
- hFile = MP4_INVALID_FILE_HANDLE;
- /* end */
- return SET_INFO_SUCCESS;
+ m_firstframe = true;
+ m_skip_frames = 1;
+
+ return 1;
}
- virtual bool seek(double seconds)
+ virtual int run(audio_chunk * chunk)
{
- if (seconds >= m_length) {
- m_eof = true;
- return true;
- }
+ if (hDecoder==0)
+ {
+ console::error("Attempting to decode while not open.");
+ return -1;
+ }
- unsigned int frame = (unsigned int)((double)m_framesize * ((double)m_timescale / (double)m_samplerate) + 0.5);
- if (frame == 0) frame = 1;
- m_seekto = (unsigned __int64)(seconds * m_timescale + 0.5) + frame;
- MP4Duration target = m_seekto - frame + m_delaydur;
+ if (sampleId >= numSamples) return 0;
- while (1) {
- MP4Duration duration = MP4ConvertToTrackDuration(hFile, track, target, m_timescale);
- sampleId = MP4GetSampleIdFromTime(hFile, track, duration, 0);
- if (sampleId == MP4_INVALID_SAMPLE_ID) return false;
- MP4Timestamp position = MP4GetSampleTime(hFile, track, sampleId);
- if (position <= m_seekto + m_delaydur) break;
- if (target == 0) return false;
- if (target > frame) target -= frame; else target = 0;
- }
+ bool done = false;
- /*
- unsigned int frame = (unsigned int)((double)m_framesize * ((double)m_samplerate / (double)m_timescale) + 0.5);
- if (frame == 0) frame = 1;
+ do
+ {
+ int rc;
+ unsigned int duration;
+ unsigned int aac_sample_count;
+ unsigned int delay = 0;
- m_seekto = (unsigned __int64)(seconds * m_timescale + 0.5) + frame;
- MP4Duration target = m_seekto - frame;
+ /* get acces unit from MP4 file */
+ buffer = NULL;
+ buffer_size = 0;
- while (1) {
- MP4Duration duration = MP4ConvertToTrackDuration(hFile, track, target, m_timescale);
- sampleId = MP4GetSampleIdFromTime(hFile, track, duration, 0);
- if (sampleId == MP4_INVALID_SAMPLE_ID) return false;
- MP4Timestamp position = MP4GetSampleTime(hFile, track, sampleId);
- if (position <= m_seekto) break;
- if (target == 0) return false;
- if (target > frame) target -= frame; else target = 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;
+ }
- faacDecPostSeekReset(hDecoder, -1);
+ sample_buffer = faacDecDecode(hDecoder, &frameInfo, buffer, buffer_size);
- return true;
- }
+ if (buffer) free(buffer);
- virtual bool is_our_content_type(const char *url, const char *type)
- {
- return !stricmp(type, "audio/mp4") || !stricmp(type, "audio/x-mp4");
- }
+ if (frameInfo.channels<=0)
+ {
+ cleanup();
+ console::error("Internal decoder error.");
+ return -1;
+ }
-private:
+ if (frameInfo.error > 0)
+ {
+ console::warning(faacDecGetErrorMessage(frameInfo.error));
+ }
- reader *m_reader;
+ if (m_skip_frames>0) m_skip_frames--;
+ else if (frameInfo.error==0)
+ {
- faacDecHandle hDecoder;
+ aac_sample_count = frameInfo.samples / frameInfo.channels;
+
+ if (m_firstframe && duration < aac_sample_count)
+ delay += aac_sample_count - duration;
- MP4FileHandle hFile;
- MP4SampleId sampleId, numSamples;
- MP4TrackId track;
- unsigned int m_timescale;
- unsigned int m_samplerate;
- unsigned int m_framesize;
- unsigned int m_delaydur;
- unsigned __int64 m_seekto;
- double m_length;
- bool m_eof;
- bool useAacLength;
+ 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;
+ }
+
- int ReadMP4Tag(file_info *info)
- {
- unsigned __int32 valueSize;
- unsigned __int8 *pValue;
- char *pName;
- unsigned int i = 0;
+ 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);
+
- do {
- valueSize = 0;
- pValue = 0;
- pName = 0;
+ return done ? 1 : 0;
+ }
- if (MP4GetMetadataByIndex(hFile, i, (const char **)&pName, &pValue, &valueSize) &&
- valueSize > 0 && pName && pValue)
- {
- char *val = (char *)malloc((valueSize+1)*sizeof(char));
+ 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;
- if (val)
- {
- memcpy(val, pValue, valueSize*sizeof(char));
- val[valueSize] = '\0';
+/* metadata list structure */
+typedef struct
+{
+ mp4ff_tag_t *tags;
+ uint32_t count;
+} mp4ff_metadata_t;
+#endif
- if (pName[0] == '�')
- {
- if (memcmp(pName, "�nam", 4) == 0)
- {
- info->meta_add("TITLE", val);
- } else if (memcmp(pName, "�ART", 4) == 0) {
- info->meta_add("ARTIST", val);
- } else if (memcmp(pName, "�wrt", 4) == 0) {
- info->meta_add("WRITER", val);
- } else if (memcmp(pName, "�alb", 4) == 0) {
- info->meta_add("ALBUM", val);
- } else if (memcmp(pName, "�day", 4) == 0) {
- info->meta_add("DATE", val);
- } else if (memcmp(pName, "�too", 4) == 0) {
- info->info_set("tool", val);
- } else if (memcmp(pName, "�cmt", 4) == 0) {
- info->meta_add("COMMENT", val);
- } else if (memcmp(pName, "�gen", 4) == 0) {
- info->meta_add("GENRE", val);
- } else {
- info->meta_add(pName, val);
- }
- } else if (memcmp(pName, "gnre", 4) == 0) {
- char *t=0;
- if (MP4GetMetadataGenre(hFile, &t) && t)
- info->meta_add("GENRE", t);
- } else if (memcmp(pName, "trkn", 4) == 0) {
- unsigned __int16 trkn = 0, tot = 0;
- if (MP4GetMetadataTrack(hFile, &trkn, &tot))
- {
- char t[64];
- if (tot > 0)
- {
- wsprintf(t, "%d/%d", (int)trkn, (int)tot);
- info->meta_add("TRACKNUMBER", t);
- wsprintf(t, "%d", (int)tot);
- info->meta_add("TOTALTRACKS", t);
- }
- else
- {
- wsprintf(t, "%d", (int)trkn);
- info->meta_add("TRACKNUMBER", t);
- }
- }
- } else if (memcmp(pName, "disk", 4) == 0) {
- unsigned __int16 disk = 0, tot = 0;
- if (MP4GetMetadataDisk(hFile, &disk, &tot))
- {
- char t[64];
- if (tot > 0)
- {
- wsprintf(t, "%d/%d", (int)disk, (int)tot);
- info->meta_add("DISC", t);
- wsprintf(t, "%d", (int)tot);
- info->meta_add("TOTALDISCS", t);
- }
- else
- {
- wsprintf(t, "%d", (int)disk);
- info->meta_add("DISC", t);
- }
- //info->meta_add("DISKNUMBER", t);
- }
- } else if (memcmp(pName, "cpil", 4) == 0) {
- unsigned __int8 cpil = 0;
- if (MP4GetMetadataCompilation(hFile, &cpil))
- {
- char t[64];
- wsprintf(t, "%d", (int)cpil);
- info->meta_add("COMPILATION", t);
- }
- } else if (memcmp(pName, "tmpo", 4) == 0) {
- unsigned __int16 tempo = 0;
- if (MP4GetMetadataTempo(hFile, &tempo))
- {
- char t[64];
- wsprintf(t, "%d BPM", (int)tempo);
- info->meta_add("TEMPO", t);
- }
- } else if (memcmp(pName, "NDFL", 4) == 0) {
- /* Removed */
- } else {
- if (!stricmp(pName, "REPLAYGAIN_TRACK_PEAK"))
- {
- info->info_set_replaygain_track_peak(pfc_string_to_float(val));
- } else if (!stricmp(pName, "REPLAYGAIN_TRACK_GAIN")) {
- info->info_set_replaygain_track_gain(pfc_string_to_float(val));
- } else if (!stricmp(pName, "REPLAYGAIN_ALBUM_PEAK")) {
- info->info_set_replaygain_album_peak(pfc_string_to_float(val));
- } else if (!stricmp(pName, "REPLAYGAIN_ALBUM_GAIN")) {
- info->info_set_replaygain_album_gain(pfc_string_to_float(val));
- } else {
- info->meta_add(pName, val);
- }
- }
- free(val);
- }
- }
- i++;
- } while (valueSize > 0);
+ unsigned rv;
+ ptr_list_t<char> freeme;
+ mem_block_list_t<mp4ff_tag_t> tags;
+ mp4ff_metadata_t mp4meta;
- return 1;
- }
+ {
+ string8_fastalloc name,value;
+ mp4ff_tag_t tag;
- int GetAudioTrack(MP4FileHandle infile)
- {
- /* find AAC track */
- int i;
- int numTracks = MP4GetNumberOfTracks(infile, NULL, 0);
+ 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);
+ }
- for (i = 0; i < numTracks; i++)
- {
- MP4TrackId trackId = MP4FindTrackId(infile, i, NULL, 0);
- const char* trackType = MP4GetTrackType(infile, trackId);
- if (!strcmp(trackType, MP4_AUDIO_TRACK_TYPE))
- {
- return trackId;
- }
- }
+ m = info->info_get_count();
- /* can't decode this */
- return -1;
- }
+ 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);
+ }
+ }
+ }
- /* file callback stuff */
- static unsigned __int32 open_cb(const char *pName,
- const char *mode, void *userData)
- {
- DBG_OUT("open_cb");
- return 1;
- }
+ mp4meta.count = tags.get_count();
+ mp4meta.tags = const_cast<mp4ff_tag_t*>(tags.get_ptr());
- static void close_cb(void *userData)
- {
- DBG_OUT("close_cb");
- return;
- }
+ mp4file = r;
+ rv = mp4ff_meta_update(&mp4cb,&mp4meta);
+ mp4file = 0;
- static unsigned __int32 read_cb(void *pBuffer, unsigned int nBytesToRead,
- void *userData)
- {
- DBG_OUT("read_cb");
- reader *r = (reader*)userData;
- return r->read(pBuffer, nBytesToRead);
- }
+ freeme.free_all();
- static unsigned __int32 write_cb(void *pBuffer, unsigned int nBytesToWrite,
- void *userData)
- {
- DBG_OUT("write_cb");
- reader *r = (reader*)userData;
- return r->write(pBuffer, nBytesToWrite);
- }
+ return rv ? SET_INFO_SUCCESS : SET_INFO_FAILURE;
- static __int64 getpos_cb(void *userData)
- {
- DBG_OUT("getpos_cb");
- reader *r = (reader*)userData;
- return r->get_position();
}
- static __int32 setpos_cb(unsigned __int32 pos, void *userData)
+ virtual bool seek(double seconds)
{
- DBG_OUT("setpos_cb");
- reader *r = (reader*)userData;
- return !(r->seek(pos));
+ 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;
+
}
- static __int64 filesize_cb(void *userData)
+ virtual bool is_our_content_type(const char *url, const char *type)
{
- DBG_OUT("filesize_cb");
- reader *r = (reader*)userData;
- return r->get_length();
+ return !stricmp(type, "audio/mp4") || !stricmp(type, "audio/x-mp4");
}
+
+private:
};
+
+
struct seek_list
{
seek_list *next;
@@ -1240,266 +1094,5 @@
}
};
-class contextmenu_mp4 : public menu_item_context
-{
-private:
- int first_num;
- string8 path;
-
-public:
- virtual int get_num_items() { return 1; }
-
- virtual const char *enum_item(int n)
- {
- if (n == 0) return "Converter (AAC <-> MP4)";
- return 0;
- }
-
- virtual bool context_get_display(int n, const ptr_list_base<metadb_handle> &data, string_base &out, bool is_playlist)
- {
- int count = data.get_count();
- if (count < 1) return false;
- int type = -1;
-
- for (int i = 0; i < count; i++)
- {
- metadb_handle *ptr = data.get_item(i);
- if (!ptr) return false;
- const char *p = ptr->handle_get_path();
- if (!p) return false;
- p = strrchr(p, '.');
- if (!p) return false;
- if (type == -1)
- {
- if (!stricmp(p, ".aac")) type = 0;
- else if (!stricmp(p, ".m4a") || !stricmp(p, ".mp4")) type = 1;
- else return false;
- }
- else
- {
- if (type == 0 && stricmp(p, ".aac")) return false;
- if (type == 1 && (stricmp(p, ".m4a") && stricmp(p, ".mp4"))) return false;
- }
- }
-
- if (type == 0)
- {
- out.set_string("Convert to MP4");
- }
- else
- {
- out.set_string("Extract AAC track");
- if (count > 1) out.add_char('s');
- }
-
- return true;
- }
-
- virtual void context_command(int n, const ptr_list_base<metadb_handle> &data, bool is_playlist)
- {
- const int count = data.get_count();
-
- for (int i = 0; i < count; i++) {
- metadb_handle *ptr = data.get_item(i);
- if (!ptr) return;
-
- file_info_i_full src_info;
-
- ptr->handle_lock();
- bool error = false;
- const file_info *info = ptr->handle_query_locked();
- if (info)
- src_info.copy(info);
- else
- error = true;
- ptr->handle_unlock();
-
- if (!error)
- {
- int type = 1;
- string8 temp = src_info.get_file_path();
- const char *p = strrchr((const char *)temp, '.');
- if (p)
- {
- if (!stricmp(p, ".aac")) type = 0;
- temp.truncate(p-(const char *)temp);
- }
- if (type == 0)
- temp.add_string(".mp4");
- else
- temp.add_string(".aac");
-
- const char *src = (const char *)src_info.get_file_path();
- const char *dst = (const char *)temp;
-
- if (file::g_exists(dst))
- {
- console::info(string_printf("Destination file '%s' already exists", dst));
- console::popup();
- }
- else
- {
- converter *conv = new converter(src, dst, &src_info);
-
- if (conv)
- {
- bool ret;
-
- if (type == 0)
- ret = conv->aac_to_mp4();
- else
- ret = conv->mp4_to_aac();
-
- if (ret)
- console::info(string_printf("'%s' converted to '%s'", src, dst));
- else
- console::error(string_printf("Failed to convert '%s' to '%s'", src, dst));
-
- delete conv;
- }
- else
- {
- console::error("Failed to create new converter");
- }
- }
- }
- else
- {
- console::error("Failed to get file infos");
- }
- }
- }
-};
-
-class contextmenu_mp4o : public menu_item_context
-{
-private:
- int first_num;
- string8 path;
-
-public:
- virtual int get_num_items() { return 1; }
-
- virtual const char *enum_item(int n)
- {
- if (n == 0) return "MP4 Layout Optimiser";
- return 0;
- }
-
- virtual bool context_get_display(int n, const ptr_list_base<metadb_handle> &data, string_base &out, bool is_playlist)
- {
- int count = data.get_count();
- if (count < 1) return false;
- int type = -1;
-
- for (int i = 0; i < count; i++)
- {
- metadb_handle *ptr = data.get_item(i);
- if (!ptr) return false;
- const char *p = ptr->handle_get_path();
- if (!p) return false;
- p = strrchr(p, '.');
- if (!p) return false;
- if (type == -1)
- {
- if (!stricmp(p, ".m4a") || !stricmp(p, ".mp4")) type = 1;
- else return false;
- }
- else
- {
- if (type == 1 && (stricmp(p, ".m4a") && stricmp(p, ".mp4"))) return false;
- }
- }
-
- out.set_string("Optimise MP4 Layout");
-
- return true;
- }
-
- virtual void context_command(int n, const ptr_list_base<metadb_handle> &data, bool is_playlist)
- {
- const int count = data.get_count();
-
- for (int i = 0; i < count; i++) {
- metadb_handle *ptr = data.get_item(i);
- if (!ptr) return;
-
- file_info_i_full src_info;
-
- ptr->handle_lock();
- bool error = false;
- const file_info *info = ptr->handle_query_locked();
- if (info)
- src_info.copy(info);
- else
- error = true;
- ptr->handle_unlock();
-
- if (!error)
- {
- const char *src = (const char *)src_info.get_file_path();
- if (!src || (src && stricmp_utf8_partial(src, "file://")))
- {
- console::error("Unsupported file location");
- return;
- }
-
- string8 name = src + strlen("file://");
- string8 name_short;
-
- if (IsUnicode())
- {
- string_wide_from_utf8 tname(name);
-
- int len = wcslen(tname) + 1;
- WCHAR *wide_fn = (WCHAR *)malloc((len+1) * sizeof(WCHAR));
- if (!wide_fn) return;
-
- int ret = GetShortPathNameW(tname, wide_fn, len+1);
- if (ret == 0)
- {
- wcscpy(wide_fn, tname);
- }
- else if (ret > len)
- {
- len = ret;
- free(wide_fn);
- wide_fn = (WCHAR *)malloc((len+1) * sizeof (WCHAR));
- if (!wide_fn) return;
- if (GetShortPathNameW(tname, wide_fn, (len+1)) == 0)
- wcscpy(wide_fn, tname);
- }
-
- string8 shortname = string_ansi_from_wide(wide_fn);
- name = shortname;
- name_short = string_utf8_from_wide(wide_fn);
- free(wide_fn);
- } else {
- string_ansi_from_utf8 ansi_fn(name);
- name = ansi_fn;
- name_short = string_utf8_from_ansi(ansi_fn);
- }
-
- MP4Optimize((const char*)name);
-
- if (IsUnicode())
- {
- string_wide_from_utf8 short_fn(name_short);
- string_wide_from_utf8 real_fn(name);
- MoveFileW(short_fn, real_fn);
- }
-
- console::info(string_printf("'%s' optimised", src));
- }
- else
- {
- console::error("Failed to get file infos");
- }
- }
- }
-};
-
static service_factory_t<input, input_mp4> foo_mp4;
-static service_factory_t<input, input_aac> foo_aac;
-static service_factory_single_t<menu_item, contextmenu_mp4> foo_mp4_context;
-static service_factory_single_t<menu_item, contextmenu_mp4o> foo_mp4o_context;
+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
@@ -43,7 +43,7 @@
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c
-# ADD CPP /nologo /MD /W3 /GX /O1 /I "../../include" /I "../../common/mp4v2" /I "../../common/mp4av" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O1 /I "../../include" /I "../../common/mp4ff" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x413 /d "NDEBUG"
@@ -90,10 +90,6 @@
# Begin Group "Source Files"
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
-# Begin Source File
-
-SOURCE=.\convert.cpp
-# End Source File
# Begin Source File
SOURCE=.\foo_mp4.cpp
--- a/plugins/foo_mp4/foo_mp4.dsw
+++ b/plugins/foo_mp4/foo_mp4.dsw
@@ -15,13 +15,10 @@
Project_Dep_Name libfaad
End Project Dependency
Begin Project Dependency
- Project_Dep_Name libmp4v2_cb
- End Project Dependency
- Begin Project Dependency
Project_Dep_Name foobar2000_component_client
End Project Dependency
Begin Project Dependency
- Project_Dep_Name libmp4av_st
+ Project_Dep_Name mp4ff
End Project Dependency
}}}
@@ -72,19 +69,7 @@
###############################################################################
-Project: "libmp4av_st"=..\..\common\mp4av\libmp4av_st.dsp - Package Owner=<4>
-
-Package=<5>
-{{{
-}}}
-
-Package=<4>
-{{{
-}}}
-
-###############################################################################
-
-Project: "libmp4v2_cb"=..\..\common\mp4v2\libmp4v2_cb.dsp - Package Owner=<4>
+Project: "mp4ff"=..\..\common\mp4ff\mp4ff.dsp - Package Owner=<4>
Package=<5>
{{{