mirror of
https://github.com/ElvishArtisan/rivendell.git
synced 2025-04-15 09:09:36 +02:00
MP4 support v2 (undebugged). This replaces callouts to faad with use of libfaad (aka neaacdec) and loads all components dynamically, much like existing MP1/2/3 support.
This commit is contained in:
parent
aae6763f4f
commit
f63b14cca9
13
configure.ac
13
configure.ac
@ -216,18 +216,17 @@ fi
|
||||
if test -z $MP4V2_DISABLED ; then
|
||||
AC_CHECK_HEADER(mp4v2/mp4v2.h,[MP4V2_HEADER_FOUND=yes],[])
|
||||
if test $MP4V2_HEADER_FOUND ; then
|
||||
AC_CHECK_LIB(mp4v2,MP4Read,[MP4V2_FOUND=yes],[])
|
||||
fi
|
||||
if test $MP4V2_FOUND ; then
|
||||
MP4V2_LIBS="-lmp4v2"
|
||||
AC_DEFINE(HAVE_MP4V2)
|
||||
fi
|
||||
AC_CHECK_HEADER(neaacdec.h,[MP4V2_FOUND=yes],[])
|
||||
if test $MP4V2_FOUND ; then
|
||||
AC_DEFINE(HAVE_MP4_LIBS)
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
#
|
||||
# Set Hard Library Dependencies
|
||||
#
|
||||
AC_SUBST(LIB_RDLIBS,"-lm -lpthread -lqui -lrd -lcurl -lid3 $FLAC_LIBS $MP4V2_LIBS -lsndfile -lsamplerate -lcdda_interface -lcdda_paranoia -lcrypt -ldl -lpam -lSoundTouch")
|
||||
AC_SUBST(LIB_RDLIBS,"-lm -lpthread -lqui -lrd -lcurl -lid3 $FLAC_LIBS -lsndfile -lsamplerate -lcdda_interface -lcdda_paranoia -lcrypt -ldl -lpam -lSoundTouch")
|
||||
|
||||
#
|
||||
# Setup MPEG Dependencies
|
||||
|
@ -176,6 +176,7 @@ dist_librd_la_SOURCES = dbversion.h\
|
||||
rdmeteraverage.cpp rdmeteraverage.h\
|
||||
rdmixer.cpp rdmixer.h\
|
||||
rdmonitor_config.cpp rdmonitor_config.h\
|
||||
rdmp4.cpp rdmp4.h\
|
||||
rdnownext.cpp rdnownext.h\
|
||||
rdoneshot.cpp rdoneshot.h\
|
||||
rdpam.cpp rdpam.h\
|
||||
|
@ -42,6 +42,10 @@
|
||||
#include <FLAC++/encoder.h>
|
||||
#include <rdflacdecode.h>
|
||||
#endif // HAVE_FLAC
|
||||
#ifdef HAVE_MP4_LIBS
|
||||
#include <mp4v2/mp4v2.h>
|
||||
#include <neaacdec.h>
|
||||
#endif // HAVE_MP4_LIBS
|
||||
#include <id3/tag.h>
|
||||
#include <qfile.h>
|
||||
|
||||
@ -680,72 +684,160 @@ RDAudioConvert::ErrorCode RDAudioConvert::Stage1Mpeg(const QString &dstfile,
|
||||
#endif // HAVE_MAD
|
||||
}
|
||||
|
||||
// Based on libfaad's frontend/main.c, but using libmp4v2 for MP4 access.
|
||||
RDAudioConvert::ErrorCode RDAudioConvert::Stage1M4A(const QString &dstfile,
|
||||
RDWaveFile *wave) {
|
||||
|
||||
const char* args[7];
|
||||
int childstatus = 0;
|
||||
|
||||
pid_t child = fork();
|
||||
|
||||
QString tmpname = dstfile + ".m4a_temp.wav";
|
||||
|
||||
if(child == 0) {
|
||||
|
||||
freopen("/dev/null", "w", stdout);
|
||||
freopen("/dev/null", "w", stderr);
|
||||
|
||||
args[0] = "faad";
|
||||
args[1] = wave->getName();
|
||||
args[2] = "-o";
|
||||
args[3] = tmpname;
|
||||
args[4] = "-b";
|
||||
args[5] = "4"; // Emit a Float32 format wave file, like the other stage 1s.
|
||||
args[6] = 0;
|
||||
execvp("faad", (char* const*)args);
|
||||
exit(109);
|
||||
RDWaveFile *wave)
|
||||
{
|
||||
#ifdef HAVE_MP4_LIBS
|
||||
SNDFILE *sf_dst=NULL;
|
||||
SF_INFO sf_dst_info;
|
||||
int ret = RDAudioConvert::ErrorOk;
|
||||
|
||||
if(!LoadMP4Libs()) {
|
||||
return RDAudioConvert::ErrorFormatNotSupported;
|
||||
}
|
||||
else {
|
||||
|
||||
waitpid(child, &childstatus, 0);
|
||||
|
||||
// Killed by a signal (e.g. OOM?)
|
||||
if(!WIFEXITED(childstatus)) {
|
||||
unlink(tmpname);
|
||||
return RDAudioConvert::ErrorInternal;
|
||||
}
|
||||
|
||||
// Returned 109, probably because we could't find faad?
|
||||
if(WEXITSTATUS(childstatus) == 109)
|
||||
return RDAudioConvert::ErrorFormatNotSupported;
|
||||
|
||||
// Two elements are missing:
|
||||
// 1. need to measure the peak amplitude;
|
||||
// 2. need to trim if a subrange was requested.
|
||||
// For now just use the sndfile import path to accomplish both tasks.
|
||||
|
||||
{
|
||||
|
||||
SF_INFO sf_src_info;
|
||||
SNDFILE* sf_src;
|
||||
|
||||
memset(&sf_src_info, 0, sizeof(sf_src_info));
|
||||
// If this fails it is likely because faad could not decode.
|
||||
if((sf_src = sf_open(tmpname, SFM_READ, &sf_src_info)) == NULL)
|
||||
return RDAudioConvert::ErrorFormatError;
|
||||
|
||||
RDAudioConvert::ErrorCode err = Stage1SndFile(dstfile, sf_src, &sf_src_info);
|
||||
|
||||
sf_close(sf_src);
|
||||
unlink(tmpname);
|
||||
|
||||
return err;
|
||||
//
|
||||
// Open source
|
||||
//
|
||||
MP4FileHandle f = dlmp4.MP4Read(getName());
|
||||
if(f == MP4_INVALID_FILE_HANDLE)
|
||||
return RDAudioConvert::ErrorNoSource;
|
||||
|
||||
MP4TrackId audioTrack = dlmp4.getMP4AACTrack(f);
|
||||
MP4SampleId firstSample = 0;
|
||||
MP4SampleId lastSample = dlmp4.MP4GetTrackNumberOfSamples(f, audioTrack) - 1;
|
||||
if(conv_start_point > 0) {
|
||||
|
||||
double startsecs = ((double)conv_start_point) / 1000;
|
||||
MP4Timestamp startts = (MP4Timestamp)(startsecs * wave->getSamplesPerSec());
|
||||
firstSample = dlmp4.MP4GetSampleIdFromTime(f, audioTrack, startts, /*need_sync=*/false);
|
||||
if(firstSample == MP4_INVALID_SAMPLE_ID) {
|
||||
ret = RDAudioConvert::ErrorInvalidSource;
|
||||
goto out_mp4;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(conv_end_point > 0) {
|
||||
|
||||
double stopsecs = ((double)conv_end_point) / 1000;
|
||||
MP4Timestamp stopts = (MP4Timestamp)(stopsecs * wave->getSamplesPerSec());
|
||||
lastSample = dlmp4.MP4GetSampleIdFromTime(f, audioTrack, stopts, /*need_sync=*/false);
|
||||
if(lastSample == MP4_INVALID_SAMPLE_ID) {
|
||||
ret = RDAudioConvert::ErrorInvalidSource;
|
||||
goto out_mp4;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
uint32_t aacBufSize = dlmp4.MP4GetTrackMaxSampleSize(f, audioTrack);
|
||||
uint8_t* aacBuf = malloc(aacBufSize);
|
||||
if(!aacBufSize || !aacBuf) {
|
||||
// Probably the source's fault for specifying a massive buffer.
|
||||
ret = RDAudioConvert::ErrorInvalidSource;
|
||||
goto out_mp4;
|
||||
}
|
||||
|
||||
uint8_t* aacConfigBuffer;
|
||||
uint32_t* aacConfigSize;
|
||||
|
||||
dlmp4.MP4GetTrackESConfiguration(f, audioTrack, &aacConfigBuffer, &aacConfigSize);
|
||||
if(!aacConfigBuffer) {
|
||||
ret = RDAudioConvert::ErrorInvalidSource;
|
||||
goto out_mp4_buf;
|
||||
}
|
||||
|
||||
//
|
||||
// Open Destination
|
||||
//
|
||||
|
||||
memset(&sf_dst_info,0,sizeof(sf_dst_info));
|
||||
sf_dst_info.format=SF_FORMAT_WAV|SF_FORMAT_FLOAT;
|
||||
sf_dst_info.channels=wave->getChannels();
|
||||
sf_dst_info.samplerate=wave->getSamplesPerSec();
|
||||
if((sf_dst=sf_open(dstfile,SFM_WRITE,&sf_dst_info))==NULL) {
|
||||
ret = RDAudioConvert::ErrorNoDestination;
|
||||
goto out_mp4_configbuf;
|
||||
}
|
||||
sf_command(sf_dst,SFC_SET_NORM_DOUBLE,NULL,SF_FALSE);
|
||||
|
||||
//
|
||||
// Initialize Decoder
|
||||
//
|
||||
NeAACDecHandle hDecoder = dlmp4.NeAACDecOpen();
|
||||
|
||||
NeAACDecConfigurationPtr config = dlmp4.NeAACDecGetCurrentConfiguration(hDecoder);
|
||||
config->outputFormat = FAAD_FMT_FLOAT;
|
||||
config->downMatrix = 1; // Downmix >2 channels to stereo.
|
||||
if(!dlmp4.NeAACDecSetConfiguration(hDecoder, config)) {
|
||||
ret = RDAudioConvert::ErrorInvalidSource;
|
||||
goto out_decoder;
|
||||
}
|
||||
|
||||
unsigned long foundSampleRate;
|
||||
unsigned char foundChannels;
|
||||
if(dlmp4.NeAACDecInit2(hDecoder, aacConfigBuffer, aacConfigSize, &foundSampleRate, &foundChannels) < 0) {
|
||||
ret = RDAudioConvert::ErrorInvalidSource;
|
||||
goto out_decoder;
|
||||
}
|
||||
|
||||
if(foundSampleRate != wave->getSamplesPerSec() || foundChannels != wave->getChannels()) {
|
||||
fprintf(stderr, "M4A header information inconsistent with actual file? Header: %lu/%u; file: %lu/%u\n",
|
||||
wave->getSamplesPerSec(), (unsigned)wave->getChannels(), foundSampleRate, (unsigned)foundChannels);
|
||||
ret = RDAudioConvert::ErrorInvalidSource;
|
||||
goto out_decoder;
|
||||
}
|
||||
|
||||
//
|
||||
// Decode
|
||||
//
|
||||
for(MP4SampleId i = firstSample; i <= lastSample; ++i) {
|
||||
|
||||
uint32_t aacBytes = aacBufSize;
|
||||
if(!dlmp4.MP4ReadSample(f, audioTrack, i, &aacBuf, &aacBytes, 0, 0, 0, 0)) {
|
||||
ret = RDAudioConvert::ErrorInvalidSource;
|
||||
break;
|
||||
}
|
||||
|
||||
NeAACDecFrameInfo frameInfo;
|
||||
// The library docs are not clear about the lifetime or cleanup of sample_buffer.
|
||||
// I hope it lives until the next NeAACDecDecode call, and is cleaned up by NeAACDecClose
|
||||
void* sample_buffer = dlmp4.NeAACDecDecode(hDecoder, &frameInfo, aacBuf, aacBytes);
|
||||
if(!sample_buffer) {
|
||||
ret = RDAudioConvert::ErrorInvalidSource;
|
||||
break;
|
||||
}
|
||||
|
||||
UpdatePeak(sample_buffer, frameInfo.samples);
|
||||
|
||||
if(sf_write_float(dst_sf, sample_buffer, frameInfo.samples) != frameInfo.samples) {
|
||||
ret = RDAudioConvert::ErrorInternal;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
// Cleanup
|
||||
//
|
||||
|
||||
out_decoder:
|
||||
dlmp4.NeAACDecClose(hDecoder);
|
||||
out_sf:
|
||||
sf_close(sf_dst);
|
||||
out_mp4_configbuf:
|
||||
free(aacConfigBuffer);
|
||||
out_mp4_buf:
|
||||
free(aacBuf);
|
||||
out_mp4:
|
||||
dlmp4.MP4Close(f, 0);
|
||||
|
||||
return ret;
|
||||
|
||||
#else
|
||||
return RDAudioConvert::ErrorFormatNotSupported;
|
||||
#endif
|
||||
}
|
||||
|
||||
RDAudioConvert::ErrorCode RDAudioConvert::Stage1SndFile(const QString &dstfile,
|
||||
|
@ -152,6 +152,9 @@ class RDAudioConvert : public QObject
|
||||
int (*lame_encode_flush)(lame_global_flags *,unsigned char *,int);
|
||||
int (*lame_set_bWriteVbrTag)(lame_global_flags *, int);
|
||||
#endif // HAVE_LAME
|
||||
#ifdef HAVE_MP4_LIBS
|
||||
DLMP4 dlmp4;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
|
106
lib/rdmp4.cpp
Normal file
106
lib/rdmp4.cpp
Normal file
@ -0,0 +1,106 @@
|
||||
// rdmp4.cpp
|
||||
//
|
||||
// Helpers for dealing with MP4 files.
|
||||
//
|
||||
// (C) Copyright 2014 Christopher Smowton <chris@smowton.net>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Library General Public License
|
||||
// version 2 as published by the Free Software Foundation.
|
||||
//
|
||||
// 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
//
|
||||
//
|
||||
|
||||
#ifdef HAVE_MP4_LIBS
|
||||
|
||||
#include <rdmp4.h>
|
||||
#include <string.h>
|
||||
|
||||
MP4TrackId DLMP4::getMP4AACTrack(MP4FileHandle f)
|
||||
{
|
||||
|
||||
uint32_t nTracks = this->MP4GetNumberOfTracks(f);
|
||||
|
||||
for(uint32_t trackIndex = 0; trackIndex < nTracks; ++trackIndex) {
|
||||
|
||||
MP4TrackId thisTrack = this->MP4FindTrackId(f, trackIndex);
|
||||
const char* trackType = this->MP4GetTrackType(f, thisTrack);
|
||||
if(trackType && !strcmp(trackType, MP4_AUDIO_TRACK_TYPE)) {
|
||||
|
||||
const char* dataName = this->MP4GetTrackMediaDataName(f, thisTrack);
|
||||
// The M4A format is only currently useful for AAC in an M4A container:
|
||||
if(dataName &&
|
||||
(!strcasecmp(dataName, "mp4a")) &&
|
||||
this->MP4GetTrackEsdsObjectTypeId(f, thisTrack) == MP4_MPEG4_AUDIO_TYPE) {
|
||||
|
||||
return thisTrack;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return MP4_INVALID_TRACK_ID;
|
||||
|
||||
}
|
||||
|
||||
#define check_dlsym(lval, libhandle, symname) \
|
||||
*(void**)(&lval) = dlsym(libhandle, symname); \
|
||||
if(!lval) return false;
|
||||
|
||||
bool DLMP4::load()
|
||||
{
|
||||
|
||||
if(loadSuccess)
|
||||
return true;
|
||||
|
||||
neaac_handle = dlopen("libfaad.so",RTLD_LAZY);
|
||||
mp4_handle = dlopen("libmp4v2.so",RTLD_LAZY);
|
||||
|
||||
if(!neaac_handle || !mp4_handle)
|
||||
return false;
|
||||
|
||||
check_dlsym(this->MP4Read, mp4_handle, "MP4Read");
|
||||
check_dlsym(this->MP4GetTrackNumberOfSamples, mp4_handle, "MP4GetTrackNumberOfSamples");
|
||||
check_dlsym(this->MP4GetSampleIdFromTime, mp4_handle, "MP4GetSampleIdFromTime");
|
||||
check_dlsym(this->MP4GetTrackMaxSampleSize, mp4_handle, "MP4GetTrackMaxSampleSize");
|
||||
check_dlsym(this->MP4GetTrackESConfiguration, mp4_handle, "MP4GetTrackESConfiguration");
|
||||
check_dlsym(this->MP4ReadSample, mp4_handle, "MP4ReadSample");
|
||||
check_dlsym(this->MP4GetTrackBitRate, mp4_handle, "MP4GetTrackBitRate");
|
||||
check_dlsym(this->MP4GetTrackAudioChannels, mp4_handle, "MP4GetTrackAudioChannels");
|
||||
check_dlsym(this->MP4GetTrackDuration, mp4_handle, "MP4GetTrackDuration");
|
||||
check_dlsym(this->MP4ConvertFromTrackDuration, mp4_handle, "MP4ConvertFromTrackDuration");
|
||||
check_dlsym(this->MP4GetTrackTimeScale, mp4_handle, "MP4GetTrackTimeScale");
|
||||
check_dlsym(this->MP4GetNumberOfTracks, mp4_handle, "MP4GetNumberOfTracks");
|
||||
check_dlsym(this->MP4FindTrackId, mp4_handle, "MP4FindTrackId");
|
||||
check_dlsym(this->MP4GetTrackType, mp4_handle, "MP4GetTrackType");
|
||||
check_dlsym(this->MP4GetTrackMediaDataName, mp4_handle, "MP4GetTrackMediaDataName");
|
||||
check_dlsym(this->MP4GetTrackEsdsObjectTypeId, mp4_handle, "MP4GetTrackEsdsObjectTypeId");
|
||||
check_dlsym(this->MP4TagsAlloc, mp4_handle, "MP4TagsAlloc");
|
||||
check_dlsym(this->MP4TagsFetch, mp4_handle, "MP4TagsFetch");
|
||||
check_dlsym(this->MP4TagsFree, mp4_handle, "MP4TagsFree");
|
||||
check_dlsym(this->MP4Close, mp4_handle, "MP4Close");
|
||||
|
||||
check_dlsym(this->NeAACDecOpen, neaac_handle, "NeAACDecOpen");
|
||||
check_dlsym(this->NeAACDecGetCurrentConfiguration, neaac_handle, "NeAACDecGetCurrentConfiguration");
|
||||
check_dlsym(this->NeAACDecSetConfiguration, neaac_handle, "NeAACDecSetConfiguration");
|
||||
check_dlsym(this->NeAACDecInit2, neaac_handle, "NeAACDecInit2");
|
||||
check_dlsym(this->NeAACDecDecode, neaac_handle, "NeAACDecDecode");
|
||||
check_dlsym(this->NeAACDecClose, neaac_handle, "NeAACDecClose");
|
||||
|
||||
loadSuccess = true;
|
||||
|
||||
}
|
||||
|
||||
#undef check_dlsym
|
||||
|
||||
#endif // HAVE_MP4_LIBS
|
95
lib/rdmp4.h
Normal file
95
lib/rdmp4.h
Normal file
@ -0,0 +1,95 @@
|
||||
// rdmp4.h
|
||||
//
|
||||
// Helpers for dealing with MP4 files.
|
||||
//
|
||||
// (C) Copyright 2014 Christopher Smowton <chris@smowton.net>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Library General Public License
|
||||
// version 2 as published by the Free Software Foundation.
|
||||
//
|
||||
// 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
//
|
||||
//
|
||||
|
||||
#ifndef RDMP4_H
|
||||
#define RDMP4_H
|
||||
#ifdef HAVE_MP4_LIBS
|
||||
|
||||
#include <mp4v2/mp4v2.h>
|
||||
#include <neaacdec.h>
|
||||
|
||||
struct DLMP4 {
|
||||
|
||||
DLMP4() : loadSuccess(false) {}
|
||||
|
||||
void *conv_neaac_handle;
|
||||
void *conv_mp4_handle;
|
||||
bool loadSuccess;
|
||||
|
||||
// MP4v2 Functions
|
||||
MP4FileHandle (*MP4Read) (const char* fileName);
|
||||
MP4SampleId (*MP4GetTrackNumberOfSamples) (MP4FileHandle hFile, MP4TrackId trackId);
|
||||
MP4SampleId (*MP4GetSampleIdFromTime) (MP4FileHandle hFile, MP4TrackId trackId, MP4Timestamp when, bool wantSyncSample);
|
||||
uint32_t (*MP4GetTrackMaxSampleSize) (MP4FileHandle hFile, MP4TrackId trackId);
|
||||
bool (*MP4GetTrackESConfiguration) (MP4FileHandle hFile, MP4TrackId trackId, uint8_t** ppConfig, uint32_t* pConfigSize);
|
||||
bool (*MP4ReadSample) (
|
||||
MP4FileHandle hFile,
|
||||
MP4TrackId trackId,
|
||||
MP4SampleId sampleId,
|
||||
uint8_t** ppBytes,
|
||||
uint32_t* pNumBytes,
|
||||
MP4Timestamp* pStartTime,
|
||||
MP4Duration* pDuration,
|
||||
MP4Duration* pRenderingOffset,
|
||||
bool* pIsSyncSample);
|
||||
uint32_t (*MP4GetTrackBitRate) (MP4FileHandle hFile, MP4TrackId trackId);
|
||||
int (*MP4GetTrackAudioChannels) (MP4FileHandle hFile, MP4TrackId trackId);
|
||||
MP4Duration (*MP4GetTrackDuration) (MP4FileHandle hFile, MP4TrackId trackId);
|
||||
uint64_t (*MP4ConvertFromTrackDuration) (
|
||||
MP4FileHandle hFile,
|
||||
MP4TrackId trackId,
|
||||
MP4Duration duration,
|
||||
uint32_t timeScale);
|
||||
uint32_t (*MP4GetTrackTimeScale) (MP4FileHandle hFile, MP4TrackId trackId);
|
||||
uint32_t (*MP4GetNumberOfTracks) (MP4FileHandle hFile);
|
||||
MP4TrackId (*MP4FindTrackId) (MP4FileHandle hFile, uint32_t trackIdx);
|
||||
const char* (*MP4GetTrackType) (MP4FileHandle hFile, MP4TrackId);
|
||||
const char* (*MP4GetTrackMediaDataName) (MP4FileHandle hFile, MP4TrackId);
|
||||
uint8_t (*MP4GetTrackEsdsObjectTypeId) (MP4FileHandle hFile, MP4TrackId);
|
||||
const MP4Tags* (*MP4TagsAlloc) (void);
|
||||
bool (*MP4TagsFetch) (const MP4Tags* tags, MP4FileHandle hFile);
|
||||
void (*MP4TagsFree) (const MP4Tags* tags);
|
||||
void (*MP4Close) (MP4FileHandle hFile, uint32_t flags);
|
||||
|
||||
// libfaad / NeAACDec functions
|
||||
NeAACDecHandle (*NeAACDecOpen) (void);
|
||||
NeAACDecConfigurationPtr (*NeAACDecGetCurrentConfiguration) (NeAACDecHandle hDecoder);
|
||||
unsigned char (*NeAACDecSetConfiguration) (NeAACDecHandle hDecoder, NeAACDecConfigurationPtr config);
|
||||
char (*NeAACDecInit2)(NeAACDecHandle hDecoder,
|
||||
unsigned char *pBuffer,
|
||||
unsigned long SizeOfDecoderSpecificInfo,
|
||||
unsigned long *samplerate,
|
||||
unsigned char *channels);
|
||||
void* (*NeAACDecDecode) (NeAACDecHandle hDecoder,
|
||||
NeAACDecFrameInfo *hInfo,
|
||||
unsigned char *buffer,
|
||||
unsigned long buffer_size);
|
||||
void (*NeAACDecClose) (NeAACDecHandle hDecoder);
|
||||
|
||||
// Helper functions:
|
||||
MP4TrackId getMP4AACTrack(MP4FileHandle f);
|
||||
|
||||
bool load();
|
||||
|
||||
};
|
||||
|
||||
#endif // HAVE_MP4_LIBS
|
||||
#endif // RDMP4_H
|
@ -47,8 +47,9 @@
|
||||
#include <rd.h>
|
||||
#include <rdwavefile.h>
|
||||
#include <rdconf.h>
|
||||
#include <rdmp4.h>
|
||||
|
||||
#ifdef HAVE_MP4V2
|
||||
#ifdef HAVE_MP4_LIBS
|
||||
#include <mp4v2/mp4v2.h>
|
||||
#endif
|
||||
|
||||
@ -328,55 +329,36 @@ bool RDWaveFile::openWave(RDWaveData *data)
|
||||
|
||||
case RDWaveFile::M4A:
|
||||
{
|
||||
#ifdef HAVE_MP4V2
|
||||
#ifdef HAVE_MP4_LIBS
|
||||
|
||||
// MP4 libs must already be loaded by now for file to have that type.
|
||||
assert(dlmp4.load());
|
||||
format_tag=WAVE_FORMAT_M4A;
|
||||
|
||||
MP4FileHandle f = MP4Read(getName());
|
||||
MP4FileHandle f = dlmp4.MP4Read(getName());
|
||||
if(f == MP4_INVALID_FILE_HANDLE)
|
||||
return false;
|
||||
|
||||
// Find an audio track, and populate sample rate, bits/sample etc.
|
||||
uint32_t nTracks = MP4GetNumberOfTracks(f);
|
||||
|
||||
MP4TrackId audioTrack = MP4_INVALID_TRACK_ID;
|
||||
for(uint32_t trackIndex = 0; trackIndex < nTracks && audioTrack == MP4_INVALID_TRACK_ID; ++trackIndex) {
|
||||
|
||||
MP4TrackId thisTrack = MP4FindTrackId(f, trackIndex);
|
||||
const char* trackType = MP4GetTrackType(f, thisTrack);
|
||||
if(trackType && !strcmp(trackType, MP4_AUDIO_TRACK_TYPE)) {
|
||||
|
||||
const char* dataName = MP4GetTrackMediaDataName(f, thisTrack);
|
||||
// The M4A format is only currently useful for AAC in an M4A container:
|
||||
if(dataName &&
|
||||
(!strcasecmp(dataName, "mp4a")) &&
|
||||
MP4GetTrackEsdsObjectTypeId(f, thisTrack) == MP4_MPEG4_AUDIO_TYPE) {
|
||||
|
||||
audioTrack = thisTrack;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
MP4TrackId audioTrack = dlmp4.getMP4AACTrack(f);
|
||||
|
||||
if(audioTrack == MP4_INVALID_TRACK_ID) {
|
||||
MP4Close(f);
|
||||
dlmp4.MP4Close(f, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Found audio track. Get audio data:
|
||||
avg_bytes_per_sec = MP4GetTrackBitRate(f, audioTrack);
|
||||
channels = MP4GetTrackAudioChannels(f, audioTrack);
|
||||
avg_bytes_per_sec = dlmp4.MP4GetTrackBitRate(f, audioTrack);
|
||||
channels = dlmp4.MP4GetTrackAudioChannels(f, audioTrack);
|
||||
|
||||
MP4Duration trackDuration = MP4GetTrackDuration(f, audioTrack);
|
||||
ext_time_length = (unsigned)MP4ConvertFromTrackDuration(f, audioTrack, trackDuration,
|
||||
MP4_MSECS_TIME_SCALE);
|
||||
MP4Duration trackDuration = dlmp4.MP4GetTrackDuration(f, audioTrack);
|
||||
ext_time_length = (unsigned)dlmp4.MP4ConvertFromTrackDuration(f, audioTrack, trackDuration,
|
||||
MP4_MSECS_TIME_SCALE);
|
||||
time_length = ext_time_length / 1000;
|
||||
samples_per_sec = MP4GetTrackTimeScale(f, audioTrack);
|
||||
samples_per_sec = dlmp4.MP4GetTrackTimeScale(f, audioTrack);
|
||||
bits_per_sample = 16;
|
||||
data_start = 0;
|
||||
sample_length = MP4GetTrackNumberOfSamples(f, audioTrack);
|
||||
sample_length = dlmp4.MP4GetTrackNumberOfSamples(f, audioTrack);
|
||||
data_length = sample_length * 2 * channels;
|
||||
data_chunk = true;
|
||||
format_chunk = true;
|
||||
@ -386,8 +368,8 @@ bool RDWaveFile::openWave(RDWaveData *data)
|
||||
|
||||
if(wave_data) {
|
||||
|
||||
const MP4Tags* tags = MP4TagsAlloc();
|
||||
MP4TagsFetch(tags, f);
|
||||
const MP4Tags* tags = dlmp4.MP4TagsAlloc();
|
||||
dlmp4.MP4TagsFetch(tags, f);
|
||||
|
||||
wave_data->setMetadataFound(true);
|
||||
|
||||
@ -400,11 +382,11 @@ bool RDWaveFile::openWave(RDWaveData *data)
|
||||
if(tags->album)
|
||||
wave_data->setAlbum(tags->album);
|
||||
|
||||
MP4TagsFree(tags);
|
||||
dlmp4.MP4TagsFree(tags);
|
||||
|
||||
}
|
||||
|
||||
MP4Close(f);
|
||||
dlmp4.MP4Close(f, 0);
|
||||
|
||||
return true;
|
||||
|
||||
@ -2308,11 +2290,13 @@ bool RDWaveFile::IsAiff(int fd)
|
||||
|
||||
bool RDWaveFile::IsM4A(int fd)
|
||||
{
|
||||
#ifdef HAVE_MP4V2
|
||||
MP4FileHandle f = MP4Read(getName());
|
||||
#ifdef HAVE_MP4_LIBS
|
||||
if(!dlmp4.load())
|
||||
return false;
|
||||
MP4FileHandle f = dlmp4.MP4Read(getName());
|
||||
bool ret = f != MP4_INVALID_FILE_HANDLE;
|
||||
if(ret)
|
||||
MP4Close(f);
|
||||
dlmp4.MP4Close(f, 0);
|
||||
return ret;
|
||||
#else
|
||||
return false;
|
||||
|
@ -1241,6 +1241,10 @@ class RDWaveFile
|
||||
ogg_page ogg_pg;
|
||||
ogg_packet ogg_pack;
|
||||
#endif // HAVE_VORBIS
|
||||
#ifdef HAVE_MP4_LIBS
|
||||
DLMP4 dlmp4;
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user