I've updated the patch to mirror the audio output changes in
flacdecoder.cpp. I've run it with cvs from a few days ago with no
problems. This may fix your compile problem (although I have no idea
what the error was; if it was from compiling globalsettings.cpp, you
likely have to `make install` mythtv from current cvs).
In any case, I think the patch should be updated.
I don't know what needs to be changed with respect to the UTF updates,
though.
I'd like very much to see this in CVS.
Thanks
Kevin Smyth
On Mon, 2005-01-31 at 23:21 +0100, Peter Martens wrote:
> I really like the idea being able to play aac-files in mythmusic. But
> ... Is the patch broken against current cvs? I am getting compile
> errors. I checked the files and in my opinion the patch applied well.
>
> peter
> _______________________________________________
> mythtv-dev mailing list
> [email protected]
> http://mythtv.org/cgi-bin/mailman/listinfo/mythtv-dev
? aac-new.patch
? mythmusic/1.18-1.20.patch
? mythmusic/1.19-1.20.patch
? mythmusic/aacdecoder.cpp
? mythmusic/aacdecoder.h
Index: configure
===================================================================
RCS file: /var/lib/mythcvs/mythmusic/configure,v
retrieving revision 1.7
diff -u -r1.7 configure
--- configure 8 Aug 2003 17:26:09 -0000 1.7
+++ configure 30 Jan 2005 20:54:58 -0000
@@ -7,6 +7,7 @@
opengl="no"
fftw_lib="no"
sdl="no"
+aac="no"
for opt do
case "$opt" in
@@ -16,7 +17,9 @@
;;
--enable-sdl) sdl="yes"
;;
- --enable-all) sdl="yes"; opengl="yes"; fftw_lib="yes";
+ --enable-aac) aac="yes"
+ ;;
+ --enable-all) sdl="yes"; opengl="yes"; fftw_lib="yes"; aac="yes";
;;
esac
done
@@ -32,6 +35,7 @@
--enable-fftw enable fftw visualizers [default=no]
--enable-opengl enable OpenGL visualizers [default=no]
--enable-sdl use SDL for the synaesthesia output [default=no]
+ --enable-aac enable AAC/MP4 audio file decompression [default=no]
--enable-all Enable all of the above.
EOF
exit 1
@@ -78,9 +82,19 @@
echo "QMAKE_CXXFLAGS_RELEASE += `sdl-config --cflags`" >> ./mythmusic/config.pro
echo "QMAKE_CXXFLAGS_DEBUG += `sdl-config --cflags`" >> ./mythmusic/config.pro
fi
-
if test "$sdl" = "no" ; then
echo " SDL support will not be included"
fi
+
+if test "$aac" = "yes" ; then
+ echo " AAC support will be included"
+ echo "#define AAC_SUPPORT 1" >> ./mythmusic/config.h
+ echo "LIBS += -lfaad -lmp4ff" >> ./mythmusic/config.pro
+ echo "HEADERS += aacdecoder.h" >> ./mythmusic/config.pro
+ echo "SOURCES += aacdecoder.cpp" >> ./mythmusic/config.pro
+fi
+if test "$aac" = "no" ; then
+ echo " AAC support will not be included"
+fi
echo ""
Index: mythmusic/decoder.cpp
===================================================================
RCS file: /var/lib/mythcvs/mythmusic/mythmusic/decoder.cpp,v
retrieving revision 1.11
diff -u -r1.11 decoder.cpp
--- mythmusic/decoder.cpp 26 Jan 2005 06:01:01 -0000 1.11
+++ mythmusic/decoder.cpp 30 Jan 2005 20:54:58 -0000
@@ -118,6 +118,9 @@
Decoder::registerFactory(new FlacDecoderFactory);
Decoder::registerFactory(new CdDecoderFactory);
Decoder::registerFactory(new avfDecoderFactory);
+#ifdef AAC_SUPPORT
+ Decoder::registerFactory(new aacDecoderFactory);
+#endif
}
}
Index: mythmusic/decoder.h
===================================================================
RCS file: /var/lib/mythcvs/mythmusic/mythmusic/decoder.h,v
retrieving revision 1.10
diff -u -r1.10 decoder.h
--- mythmusic/decoder.h 26 Jan 2005 06:01:01 -0000 1.10
+++ mythmusic/decoder.h 30 Jan 2005 20:54:58 -0000
@@ -1,6 +1,7 @@
#ifndef DECODER_H_
#define DECODER_H_
+#include "config.h"
#include <qstring.h>
#include <qevent.h>
#include <qthread.h>
@@ -166,4 +167,15 @@
Decoder *create(const QString &, QIODevice *, AudioOutput *, bool);
};
+#ifdef AAC_SUPPORT
+class aacDecoderFactory : public DecoderFactory
+{
+public:
+ bool supports(const QString &) const;
+ const QString &extension() const;
+ const QString &description() const;
+ Decoder *create(const QString &, QIODevice *, AudioOutput *, bool);
+};
+#endif
+
#endif
/*
aacdecoder.cpp
(c) 2003, 2004 Thor Sigvaldason and Isaac Richards
Part of the mythTV project
aac decoder methods
*/
#include <iostream>
#include <string>
#include <qobject.h>
#include <qiodevice.h>
#include <qfile.h>
#include <faad.h>
#include "aacdecoder.h"
#include "constants.h"
#include <mythtv/audiooutput.h>
#include "metadata.h"
#include <mythtv/mythcontext.h>
//
// C style callbacks (jump right back into the aacDecoder object)
//
extern "C" uint32_t read_callback(void *user_data, void *buffer, uint32_t length)
{
aacDecoder *the_decoder_object = (aacDecoder*) user_data;
if(the_decoder_object)
{
return the_decoder_object->aacRead((char *)buffer, length);
}
cerr << "read_callback called with no aacDecoder object assigned" << endl;
return 0;
}
uint32_t seek_callback(void *user_data, uint64_t position)
{
aacDecoder *the_decoder_object = (aacDecoder*) user_data;
if (the_decoder_object)
{
return the_decoder_object->aacSeek(position);
}
cerr << "seek_callback called with no aacDecoder object assigned" << endl;
return 0;
}
/*
---------------------------------------------------------------------
*/
aacDecoder::aacDecoder(const QString &file, DecoderFactory *d, QIODevice *i,
AudioOutput *o)
: Decoder(d, i, o)
{
filename = file;
inited = FALSE;
user_stop = FALSE;
stat = 0;
bks = 0;
done = FALSE;
finish = FALSE;
len = 0;
sample_rate = 0;
bitrate = 0;
seekTime = -1.0;
totalTime = 0.0;
channels = 0;
output_buf = 0;
output_bytes = 0;
output_at = 0;
mp4_file_flag = false;
mp4_callback = NULL;
timescale = 0;
framesize = 0;
}
aacDecoder::~aacDecoder(void)
{
if (inited)
{
deinit();
}
if (output_buf)
delete [] output_buf;
output_buf = 0;
}
void aacDecoder::stop()
{
user_stop = TRUE;
}
void aacDecoder::flush(bool final)
{
ulong min = final ? 0 : bks;
while ((!done && !finish && seekTime <= 0) && output_bytes > min)
{
if (user_stop || finish)
{
inited = FALSE;
done = TRUE;
}
else
{
ulong sz = output_bytes < bks ? output_bytes : bks;
int samples = (sz * 8) / (channels * 16);
if (output()->AddSamples(output_buf, samples, -1))
{
output_bytes -= sz;
memmove(output_buf, output_buf + sz, output_bytes);
output_at = output_bytes;
} else {
mutex()->unlock();
usleep(500);
mutex()->lock();
done = user_stop;
}
}
}
}
bool aacDecoder::initialize()
{
bks = blockSize();
inited = user_stop = done = finish = FALSE;
len = sample_rate = bitrate = 0;
stat = channels = 0;
timescale = 0;
framesize = 0;
seekTime = -1.0;
totalTime = 0.0;
mp4_file_flag = false;
if (! input())
{
error("aacDecoder: cannot initialize as it has no input");
return false;
}
if (!output_buf) {
output_buf = new char[globalBufferSize];
}
output_at = 0;
output_bytes = 0;
if (! input()->isOpen())
{
if (! input()->open(IO_ReadOnly))
{
error("aacDecoder: failed to open input");
return FALSE;
}
}
//
// See if we can seek
//
if (!input()->at(0))
{
error("couldn't seek in input");
return false;
}
//
// figure out if it's an mp4 file (aac in a mp4 wrapper a la Apple) or
// just a pure aac file.
//
mp4_file_flag = false;
char header_buffer[8];
input()->readBlock(header_buffer, 8);
//
// Seek back to the begining, otherwise the decoder gets totally confused.
//
input()->at(0);
if (
header_buffer[4] == 'f' &&
header_buffer[5] == 't' &&
header_buffer[6] == 'y' &&
header_buffer[7] == 'p'
)
{
//
// It's an mp4/m4a (iTunes file) ...
//
mp4_file_flag = true;
return initializeMP4();
}
else
{
mp4_file_flag = false;
error("aacDecoder: stream is not mp4 ... not yet supported");
input()->close();
inited = false;
return false;
}
input()->close();
inited = false;
return false;
}
bool aacDecoder::initializeMP4()
{
//
// Open the callback structure for seeking and reading
//
mp4_callback = (mp4ff_callback_t*) malloc(sizeof(mp4ff_callback_t));
mp4_callback->read = read_callback;
mp4_callback->seek = seek_callback;
mp4_callback->user_data = this;
//
// Open decoder library (?)
//
decoder_handle = faacDecOpen();
//
// Set configuration
//
faacDecConfigurationPtr config = faacDecGetCurrentConfiguration(decoder_handle);
config->outputFormat = FAAD_FMT_16BIT;
config->downMatrix = 0; // if 1, we could downmix 5.1 to 2 ... apparently
config->dontUpSampleImplicitSBR = 1;
faacDecSetConfiguration(decoder_handle, config);
//
// Open the mp4 input file
//
mp4_input_file = mp4ff_open_read(mp4_callback);
if (!mp4_input_file)
{
error("could not open input as mp4 input file");
faacDecClose(decoder_handle);
free(mp4_callback);
return false;
}
//
// Find the AAC track inside this mp4 container
//
if ( (aac_track_number = getAACTrack(mp4_input_file)) < 0)
{
error("could not find aac track inside mp4 input file");
faacDecClose(decoder_handle);
mp4ff_close(mp4_input_file);
free(mp4_callback);
return false;
}
//
// Get configuration in the right format and use it to do second level
// intialization of the faad lib
//
unsigned char *buffer = NULL;
uint buffer_size;
mp4ff_get_decoder_config(
mp4_input_file,
aac_track_number,
&buffer,
&buffer_size
);
if (faacDecInit2(decoder_handle, buffer, buffer_size,
&sample_rate, &channels) < 0)
{
error("aacDecoder: error in second stage initialization");
faacDecClose(decoder_handle);
mp4ff_close(mp4_input_file);
free(mp4_callback);
if (buffer)
{
free(buffer);
}
return 1;
}
timescale = mp4ff_time_scale(mp4_input_file, aac_track_number);
framesize = 1024;
//
// Fiddle with the default frame size if the data appears to need it
//
mp4AudioSpecificConfig mp4ASC;
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);
}
//
// Extract some information about the content
//
long samples = mp4ff_num_samples(mp4_input_file, aac_track_number);
float f = 1024.0;
float seconds;
if (mp4ASC.sbr_present_flag == 1)
{
f = f * 2.0;
}
seconds = (float)samples*(float)(f-1.0)/(float)mp4ASC.samplingFrequency;
totalTime = seconds;
//
// Check we got same answers from mp4ASC
//
if (channels != mp4ASC.channelsConfiguration)
{
error("accDecoder: possible confusion on number of channels");
}
if (sample_rate != mp4ASC.samplingFrequency)
{
error("accDecoder: possible confusion on frequency");
}
//
// Setup the output
//
if (output())
{
output()->Reconfigure(16, channels, sample_rate);
}
inited = TRUE;
return TRUE;
}
int aacDecoder::getAACTrack(mp4ff_t *infile)
{
//
// Find an AAC track inside an mp4 container
//
int i, rc;
int numTracks = mp4ff_total_tracks(infile);
for (i = 0; i < numTracks; i++)
{
unsigned char *buff = NULL;
uint 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;
}
}
//
// No AAC tracks
//
return -1;
}
void aacDecoder::seek(double pos)
{
seekTime = pos;
}
void aacDecoder::deinit()
{
faacDecClose(decoder_handle);
mp4ff_close(mp4_input_file);
if (mp4_callback)
{
free(mp4_callback);
}
inited = user_stop = done = finish = FALSE;
len = sample_rate = bitrate = 0;
stat = channels = 0;
timescale = 0;
framesize = 0;
setInput(0);
setOutput(0);
}
void aacDecoder::run()
{
mutex()->lock();
if (!inited)
{
error("aacDecoder: run() called without being init'd");
mutex()->unlock();
return;
}
//cerr << "aacDecoder: starting decoding of " << filename << endl;
stat = DecoderEvent::Decoding;
mutex()->unlock();
{
DecoderEvent e((DecoderEvent::Type) stat);
dispatch(e);
}
long current_sample, total_numb_samples;
total_numb_samples = mp4ff_num_samples(mp4_input_file, aac_track_number);
current_sample = -1;
uchar *buffer;
uint buffer_size;
while (!done && !finish && !user_stop)
{
mutex()->lock();
++current_sample;
if (seekTime >= 0.0)
{
//
// Crap ... seek ... well, this is approximately correct
//
current_sample = (long int) (((double)(seekTime / totalTime)) * total_numb_samples);
seekTime = -1.0;
}
if (current_sample >= total_numb_samples)
{
//
// We're done ... make sure we play all the remaining output
//
flush(TRUE);
if (output())
{
output()->Drain();
}
done = TRUE;
if (! user_stop)
{
finish = TRUE;
}
}
else
{
unsigned int sample_count;
buffer = NULL;
buffer_size = 0;
int rc = mp4ff_read_sample(
mp4_input_file,
aac_track_number,
current_sample,
&buffer,
&buffer_size
);
if (rc == 0)
{
error("decoder error reading sample");
done = TRUE;
}
else
{
faacDecFrameInfo frame_info;
void *sample_buffer = faacDecDecode(
decoder_handle,
&frame_info,
buffer,
buffer_size
);
sample_count = frame_info.samples;
//
// Munge the samples into the "right" format and send them
// to the output (after checking we're not going to exceed
// the output buffer size)
//
if (((sample_count * 2) + output_at ) >= globalBufferSize)
{
error("aacDecoder: gloablBufferSize too small, "
"truncating output (this is going to "
"sound like crap)");
sample_count = ((globalBufferSize - output_at) / 2) - 100;
}
char *char_buffer = (char *)sample_buffer;
short *sample_buffer16 = (short*)char_buffer;
for(uint i = 0; i < sample_count; i++)
{
output_buf[output_at + (i*2)] = (char)(sample_buffer16[i] & 0xFF);
output_buf[output_at + (i*2) + 1] = (char)((sample_buffer16[i] >> 8) & 0xFF);
}
if (sample_count > 0)
{
output_at += sample_count * 2;
output_bytes += sample_count * 2;
if (output())
{
flush();
}
}
if (buffer)
{
free(buffer);
}
}
}
mutex()->unlock();
}
flush(TRUE);
mutex()->lock();
//cerr << "aacDecoder: completed decoding of " << filename << endl;
if (finish)
{
//cerr << "decoder finish" << endl;
stat = DecoderEvent::Finished;
}
else if (user_stop) {
stat = DecoderEvent::Stopped;
}
mutex()->unlock();
{
DecoderEvent e((DecoderEvent::Type) stat);
dispatch(e);
}
deinit();
}
//
// Extern C callbacks for metadata reading
//
uint32_t md_read_callback(void *user_data, void *buffer, uint32_t length)
{
return fread(buffer, 1, length, (FILE*)user_data);
}
uint32_t md_seek_callback(void *user_data, uint64_t position)
{
return fseek((FILE*)user_data, position, SEEK_SET);
}
void aacDecoder::metadataSanityCheck(QString *artist, QString *album, QString *title, QString *genre)
{
if (artist->length() < 1)
{
artist->append("Unknown Artist");
}
if (album->length() < 1)
{
album->append("Unknown Album");
}
if (title->length() < 1)
{
title->append("Unknown Title");
}
if (genre->length() < 1)
{
genre->append("Unknown Genre");
}
}
Metadata* aacDecoder::getMetadata(QSqlDatabase *db)
{
Metadata *retdata = new Metadata(filename);
if (retdata->isInDatabase(db, musiclocation))
{
return retdata;
}
delete retdata;
QString artist = "", album = "", title = "", genre = "";
QString writer = "", comment = "";
int year = 0, tracknum = 0, length = 0;
FILE *input = fopen(filename.ascii(), "r");
if (!input)
{
error(QString("could not open \"%1\" to read metadata").arg(filename.ascii()));
return NULL;
}
//
// Create the callback structure
//
mp4ff_callback_t *mp4_cb = (mp4ff_callback_t*) malloc(sizeof(mp4ff_callback_t));
mp4_cb->read = md_read_callback;
mp4_cb->seek = md_seek_callback;
mp4_cb->user_data = input;
//
// Open the mp4 input file
//
mp4ff_t *mp4_ifile = mp4ff_open_read(mp4_cb);
if (!mp4_ifile)
{
error(QString("could not open \"%1\" as mp4 for metadata").arg(filename.ascii()));
free(mp4_cb);
fclose(input);
return NULL;
}
char *char_storage = NULL;
//
// Look for metadata
//
if (mp4ff_meta_get_title(mp4_ifile, &char_storage))
{
title = QString::fromUtf8(char_storage);
free(char_storage);
}
if (mp4ff_meta_get_artist(mp4_ifile, &char_storage))
{
artist = QString::fromUtf8(char_storage);
free(char_storage);
}
if (mp4ff_meta_get_writer(mp4_ifile, &char_storage))
{
writer = QString::fromUtf8(char_storage);
free(char_storage);
}
if (mp4ff_meta_get_album(mp4_ifile, &char_storage))
{
album = QString::fromUtf8(char_storage);
free(char_storage);
}
if (mp4ff_meta_get_date(mp4_ifile, &char_storage))
{
year = QString(char_storage).toUInt();
free(char_storage);
}
if (mp4ff_meta_get_comment(mp4_ifile, &char_storage))
{
comment = QString::fromUtf8(char_storage);
free(char_storage);
}
if (mp4ff_meta_get_genre(mp4_ifile, &char_storage))
{
genre = QString::fromUtf8(char_storage);
free(char_storage);
}
if (mp4ff_meta_get_track(mp4_ifile, &char_storage))
{
tracknum = QString(char_storage).toUInt();
free(char_storage);
}
//
// Find the AAC track inside this mp4 which we need to do to find the
// length
//
int track_num;
if ( (track_num = getAACTrack(mp4_ifile)) < 0)
{
error("could not find aac track to calculate metadata length");
mp4ff_close(mp4_ifile);
free(mp4_callback);
fclose(input);
return NULL;
}
unsigned char *buffer = NULL;
uint buffer_size;
mp4ff_get_decoder_config(
mp4_ifile,
track_num,
&buffer,
&buffer_size
);
if (!buffer)
{
error("could not get decoder config to calculate metadata length");
mp4ff_close(mp4_input_file);
free(mp4_callback);
fclose(input);
return NULL;
}
mp4AudioSpecificConfig mp4ASC;
if (AudioSpecificConfig(buffer, buffer_size, &mp4ASC) < 0)
{
error("could not get audio specifics to calculate metadata length");
mp4ff_close(mp4_input_file);
free(mp4_callback);
fclose(input);
return NULL;
}
long samples = mp4ff_num_samples(mp4_ifile, track_num);
float f = 1024.0;
if (mp4ASC.sbr_present_flag == 1)
{
f = f * 2.0;
}
float numb_seconds = (float)samples*(float)(f-1.0)/(float)mp4ASC.samplingFrequency;
length = (int) (numb_seconds * 1000);
mp4ff_close(mp4_ifile);
free(mp4_cb);
fclose(input);
metadataSanityCheck(&artist, &album, &title, &genre);
retdata = new Metadata(
filename,
artist,
album,
title,
genre,
year,
tracknum,
length
);
//retdata->setComposer(writer);
//retdata->setComment(comment);
//retdata->setBitrate(mp4ASC.samplingFrequency);
retdata->dumpToDatabase(db, musiclocation);
return retdata;
}
void aacDecoder::commitMetadata(Metadata *mdata)
{
error("ignoring request to commit AAC meta data");
}
uint32_t aacDecoder::aacRead(char *buffer, uint32_t length)
{
if (input())
{
Q_LONG read_result = input()->readBlock(buffer, length);
if (read_result < 1)
{
return 0;
}
return read_result;
}
error("aacDecoder: aacRead() was called, but there is no input");
return 0;
}
uint32_t aacDecoder::aacSeek(uint64_t position)
{
if (input())
{
return input()->at(position);
}
error("aacDecoder: aacSeek() was called, but there is no input");
return 0;
}
bool aacDecoderFactory::supports(const QString &source) const
{
bool res = false;
QStringList list = QStringList::split("|", extension());
for (QStringList::Iterator it = list.begin(); it != list.end(); ++it)
{
if (*it == source.right((*it).length()).lower())
{
res = true;
break;
}
}
return res;
}
const QString &aacDecoderFactory::extension() const
{
static QString ext(".m4a");
return ext;
}
const QString &aacDecoderFactory::description() const
{
static QString desc(QObject::tr("Windows Media Audio"));
return desc;
}
Decoder *aacDecoderFactory::create(const QString &file, QIODevice *input,
AudioOutput *output, bool deletable)
{
if (deletable)
return new aacDecoder(file, this, input, output);
static aacDecoder *decoder = 0;
if (!decoder)
{
decoder = new aacDecoder(file, this, input, output);
}
else
{
decoder->setInput(input);
decoder->setOutput(output);
}
return decoder;
}
#ifndef AVFECODER_H_
#define AVFECODER_H_
/*
aacdecoder.h
(c) 2003 Thor Sigvaldason and Isaac Richards
Part of the mythTV project
Header for aac (m4a) decoder
*/
#include <qsqldatabase.h>
#include "decoder.h"
#include <mp4ff.h>
class Metadata;
class aacDecoder : public Decoder
{
public:
aacDecoder(const QString &file, DecoderFactory *, QIODevice *, AudioOutput *);
virtual ~aacDecoder(void);
bool initialize();
//double lengthInSeconds();
void seek(double);
void stop();
Metadata *getMetadata(QSqlDatabase *db);
void commitMetadata(Metadata *mdata);
bool initializeMP4();
int getAACTrack(mp4ff_t *infile);
uint32_t aacRead(char *buffer, uint32_t length);
uint32_t aacSeek(uint64_t position);
private:
void run();
void flush(bool = FALSE);
void deinit();
bool inited, user_stop;
int stat;
char *output_buf;
ulong output_bytes, output_at;
unsigned int bks;
bool done, finish;
long len, bitrate;
uchar channels;
unsigned long sample_rate;
unsigned long output_size;
double totalTime, seekTime;
bool mp4_file_flag;
mp4ff_callback_t *mp4_callback;
faacDecHandle decoder_handle;
mp4ff_t *mp4_input_file;
int aac_track_number;
unsigned long timescale;
unsigned int framesize;
void metadataSanityCheck(QString *artist, QString *album, QString *title, QString *genre);
};
#endif
_______________________________________________
mythtv-dev mailing list
[email protected]
http://mythtv.org/cgi-bin/mailman/listinfo/mythtv-dev