summaryrefslogtreecommitdiff
path: root/faad2/src/plugins/xmms
diff options
context:
space:
mode:
Diffstat (limited to 'faad2/src/plugins/xmms')
-rw-r--r--faad2/src/plugins/xmms/AUTHORS3
-rw-r--r--faad2/src/plugins/xmms/ChangeLog43
-rw-r--r--faad2/src/plugins/xmms/INSTALL2
-rw-r--r--faad2/src/plugins/xmms/Makefile.am2
-rw-r--r--faad2/src/plugins/xmms/NEWS21
-rw-r--r--faad2/src/plugins/xmms/README23
-rw-r--r--faad2/src/plugins/xmms/TODO6
-rw-r--r--faad2/src/plugins/xmms/src/Makefile.am14
-rw-r--r--faad2/src/plugins/xmms/src/aac_utils.c104
-rw-r--r--faad2/src/plugins/xmms/src/libmp4.c586
-rw-r--r--faad2/src/plugins/xmms/src/mp4_utils.c469
11 files changed, 1273 insertions, 0 deletions
diff --git a/faad2/src/plugins/xmms/AUTHORS b/faad2/src/plugins/xmms/AUTHORS
new file mode 100644
index 0000000..1def44f
--- /dev/null
+++ b/faad2/src/plugins/xmms/AUTHORS
@@ -0,0 +1,3 @@
+xmms-mp4 plugin for xmms-1.2.x
+
+re-coded by ciberfred from scratch
diff --git a/faad2/src/plugins/xmms/ChangeLog b/faad2/src/plugins/xmms/ChangeLog
new file mode 100644
index 0000000..4a89f46
--- /dev/null
+++ b/faad2/src/plugins/xmms/ChangeLog
@@ -0,0 +1,43 @@
+22 August 2004:
+ * move from libmp4v2 to libmp4ff
+
+1 December 2003:
+ * remove aac plug and merge the aac part with the mp4 part
+ so now 2 plugins in one :)
+
+4 juillet 2003:
+ * package the plugin for faad2
+ * upgrade code to libfaad2-2.0 API
+ * version 0.5
+
+15 juin 2003:
+ * better configuration code
+ (choose automaticaly at compile time)
+ * installation must be set by root
+ * version 0.4
+
+15 mai 2003:
+ * update configure script to be better
+ * version 0.3
+
+01 Novembre 2002:
+ * check automake/autoconf/libtool for plugin, now 'make install' work
+ * handle seeking
+ * configuration box created (thanks glade !)
+ * handle aac informations
+ * modification to a better infobox (thanks angain glade !)
+ * version 0.2 ready to public :)
+
+25 Aout 2002:
+ * gtk-1.2.x info file with some 'static' useful ID3 info
+ * title show in xmms correctly
+ * version 0.1 ready to public :)
+
+20 Aout 2002:
+ * everything...
+ * work with faad2lib-1.1, id3lib-3.8.0 (glibc-2.2.5, gcc-2.95.3)
+ i think the minimum required but need testing...
+ * playlist working
+ * handle id3tag, the plugin work :)
+ * new maintener : ciberfred
+
diff --git a/faad2/src/plugins/xmms/INSTALL b/faad2/src/plugins/xmms/INSTALL
new file mode 100644
index 0000000..3f5cccf
--- /dev/null
+++ b/faad2/src/plugins/xmms/INSTALL
@@ -0,0 +1,2 @@
+the installation of this plugin is provide by the faad2 package.
+add to configure time the option '--with-xmms' and the plugin will be build \ No newline at end of file
diff --git a/faad2/src/plugins/xmms/Makefile.am b/faad2/src/plugins/xmms/Makefile.am
new file mode 100644
index 0000000..5d39788
--- /dev/null
+++ b/faad2/src/plugins/xmms/Makefile.am
@@ -0,0 +1,2 @@
+SUBDIRS = src
+
diff --git a/faad2/src/plugins/xmms/NEWS b/faad2/src/plugins/xmms/NEWS
new file mode 100644
index 0000000..32b85d3
--- /dev/null
+++ b/faad2/src/plugins/xmms/NEWS
@@ -0,0 +1,21 @@
+22 Aout 2004
+------------
+modification du system de compilation, passage de libmp4v2 a libmp4ff
+en static, libfaad en dynamique.
+
+1 Decembre 2003
+---------------
+merge du plugin aac et du plugin mp4. modification des script du projet faad
+le plugin ne doit pas etre construit en meme temps que le projet
+
+4 Juillet 2003
+--------------
+integration du plugin xmms-aac dans le projet faad2 version 2.x
+
+15 aout 2002
+------------
+
+Recodage en entier du plugin aac
+me contacter par mail a :
+
+frederic.fondriest@laposte.net
diff --git a/faad2/src/plugins/xmms/README b/faad2/src/plugins/xmms/README
new file mode 100644
index 0000000..7380891
--- /dev/null
+++ b/faad2/src/plugins/xmms/README
@@ -0,0 +1,23 @@
+ xmms-mp4 plugin v0.5
+ (dynamic version)
+ "a mp4/aac audio player for xmms"
+ coded by ciberfred from france
+ -------------
+
+This source code allow to xmms to read .mp4/.m4a/.aac files
+
+About.
+ This plugin is a merge between aac and mp4 plugin. so now you could read
+all new and old files encoded with different encoder and different format
+(for the aac part). This is possible since the libfaad2 allow to read
+old aac ADTS format.
+
+For any informations about this plugin contact me at :
+
+mail : frederic.fondriest@laposte.net
+ICQ : 17293220
+aac plugin homepage (and more) : http://fondriest.frederic.free.fr/realisations/
+IRC : irc.eu.freenode.net (#lamip)
+
+--
+Frederic Fondriest
diff --git a/faad2/src/plugins/xmms/TODO b/faad2/src/plugins/xmms/TODO
new file mode 100644
index 0000000..e2de779
--- /dev/null
+++ b/faad2/src/plugins/xmms/TODO
@@ -0,0 +1,6 @@
+TODO:
+
+ * handle AAC info such as MPEG-AAC type, header (ADTS/ADIF),...
+ * seeking... but it will certainly an option because it need
+ a reading of whole file....
+ * any suggestions ... send me somes messages about it :)
diff --git a/faad2/src/plugins/xmms/src/Makefile.am b/faad2/src/plugins/xmms/src/Makefile.am
new file mode 100644
index 0000000..33d21fc
--- /dev/null
+++ b/faad2/src/plugins/xmms/src/Makefile.am
@@ -0,0 +1,14 @@
+local_CFLAGS=`$(XMMS_CONFIG) --cflags` -Wall
+local_LDFLAGS=`$(XMMS_CONFIG) --libs`
+libdir = `$(XMMS_CONFIG) --input-plugin-dir`
+lib_LTLIBRARIES = libmp4.la
+
+libmp4_la_CFLAGS = $(local_CFLAGS) -Wall \
+ -I$(top_srcdir)/include -I$(top_srcdir)/common/mp4ff
+
+libmp4_la_LIBADD = $(top_builddir)/libfaad/libfaad.la \
+ $(top_builddir)/common/mp4ff/libmp4ff.a
+
+libmp4_la_LDFLAGS = -module -avoid-version $(local_LDFLAGS) -lpthread
+
+libmp4_la_SOURCES = libmp4.c mp4_utils.c aac_utils.c
diff --git a/faad2/src/plugins/xmms/src/aac_utils.c b/faad2/src/plugins/xmms/src/aac_utils.c
new file mode 100644
index 0000000..45543e6
--- /dev/null
+++ b/faad2/src/plugins/xmms/src/aac_utils.c
@@ -0,0 +1,104 @@
+/*
+ *
+ * utils for AAC informations
+*/
+#include <glib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+#define ADTS_HEADER_SIZE 8
+#define SEEK_TABLE_CHUNK 60
+#define MPEG4_TYPE 0
+#define MPEG2_TYPE 1
+
+// Read ADTS header, the file descriptor must be at
+// the begining of the aac frame not at the id3tag
+
+int getAacInfo(FILE *fd)
+{
+ unsigned char header[ADTS_HEADER_SIZE];
+ unsigned int id;
+ unsigned long originPosition;
+
+ originPosition = ftell(fd);
+ if(fread(header, 1, ADTS_HEADER_SIZE, fd) != ADTS_HEADER_SIZE){
+ fseek(fd, originPosition, SEEK_SET);
+ return(-1);
+ }
+ if(!((header[0]==0xFF)&&((header[1]& 0xF6)==0xF0))){
+ printf("Bad header\n");
+ return(-1);
+ }
+ id = header[1]&0x08;
+ if(id==0){//MPEG-4 AAC
+ fseek(fd, originPosition, SEEK_SET);
+ return(MPEG4_TYPE);
+ }else{
+ fseek(fd, originPosition, SEEK_SET);
+ return(MPEG2_TYPE);
+ }
+ fseek(fd, originPosition, SEEK_SET);
+ return(-1);
+}
+
+// as AAC is VBR we need to check all ADTS header
+// to enable seeking...
+// there is no other solution
+void checkADTSForSeeking(FILE *fd,
+ unsigned long **seekTable,
+ unsigned long *seekTableLength)
+{
+ unsigned long originPosition;
+ unsigned long position;
+ unsigned char header[ADTS_HEADER_SIZE];
+ unsigned int frameCount, frameLength, frameInsec;
+ unsigned int id=0, seconds=0;
+
+ originPosition = ftell(fd);
+
+ for(frameCount=0,frameInsec=0;; frameCount++,frameInsec++){
+ position = ftell(fd);
+ if(fread(header, 1, ADTS_HEADER_SIZE, fd)!=ADTS_HEADER_SIZE){
+ break;
+ }
+ if(!strncmp(header, "ID3", 3)){
+ break;
+ }
+ if(!((header[0]==0xFF)&&((header[1]& 0xF6)==0xF0))){
+ printf("error : Bad 1st header, file may be corrupt !\n");
+ break;
+ }
+ if(!frameCount){
+ id=header[1]&0x08;
+ if(((*seekTable) = malloc(SEEK_TABLE_CHUNK * sizeof(unsigned long)))==0){
+ printf("malloc error\n");
+ return;
+ }
+ (*seekTableLength) = SEEK_TABLE_CHUNK;
+ }
+
+ //if(id==0){//MPEG-4
+ //frameLength = ((unsigned int)header[4]<<5)|((unsigned int)header[5]>>3);
+ //}else{//MPEG-2
+ frameLength = (((unsigned int)header[3]&0x3)<<11)|((unsigned int)header[4]<<3)|(header[5]>>5);
+ //}
+ if(frameInsec==43){//???
+ frameInsec=0;
+ }
+ if(frameInsec==0){
+ if(seconds == (*seekTableLength)){
+ (*seekTable) = realloc((*seekTable), (seconds+SEEK_TABLE_CHUNK)*sizeof(unsigned long));
+ (*seekTableLength) = seconds+SEEK_TABLE_CHUNK;
+ }
+ (*seekTable)[seconds] = position;
+ seconds++;
+ }
+ if(fseek(fd, frameLength-ADTS_HEADER_SIZE, SEEK_CUR)==-1){
+ break;
+ }
+ }
+ (*seekTableLength) = seconds;
+ fseek(fd, originPosition, SEEK_SET);
+}
diff --git a/faad2/src/plugins/xmms/src/libmp4.c b/faad2/src/plugins/xmms/src/libmp4.c
new file mode 100644
index 0000000..117a087
--- /dev/null
+++ b/faad2/src/plugins/xmms/src/libmp4.c
@@ -0,0 +1,586 @@
+/*
+ * MP4/AAC decoder for xmms
+ *
+ * OPTIONNAL need
+ * --------------
+ * libid3 (3.8.x - www.id3.org)
+*/
+
+#include <pthread.h>
+#include <gtk/gtk.h>
+#include <stdio.h>
+#include <string.h>
+
+#if defined(HAVE_BMP)
+#include <bmp/plugin.h>
+#include <bmp/util.h>
+#include <bmp/configfile.h>
+#include <bmp/titlestring.h>
+#else
+#include <xmms/plugin.h>
+#include <xmms/util.h>
+#include <xmms/configfile.h>
+#include <xmms/titlestring.h>
+#endif /*HAVE_BMP*/
+
+#include "neaacdec.h"
+#include "mp4ff.h"
+
+#define MP4_DESCRIPTION "MP4 & MPEG2/4-AAC audio player - 1.2.x"
+#define MP4_VERSION "ver. 0.5-faad2-version - 22 August 2004"
+#define MP4_ABOUT "Written by ciberfred"
+#define BUFFER_SIZE FAAD_MIN_STREAMSIZE*64
+
+static void mp4_init(void);
+static void mp4_about(void);
+static void mp4_play(char *);
+static void mp4_stop(void);
+static void mp4_pause(short);
+static void mp4_seek(int);
+static int mp4_getTime(void);
+static void mp4_cleanup(void);
+static void mp4_getSongTitle(char *, char **, int *);
+static void mp4_getSongInfo(char *);
+static int mp4_isFile(char *);
+static void* mp4Decode(void *);
+
+InputPlugin mp4_ip =
+ {
+ 0, // handle
+ 0, // filename
+ MP4_DESCRIPTION,
+ mp4_init,
+ mp4_about,
+ 0, // configuration
+ mp4_isFile,
+ 0, //scandir
+ mp4_play,
+ mp4_stop,
+ mp4_pause,
+ mp4_seek,
+ 0, // set equalizer
+ mp4_getTime,
+ 0, // get volume
+ 0,
+ mp4_cleanup,
+ 0, // obsolete
+ 0, // send visualisation data
+ 0, // set player window info
+ 0, // set song title text
+ mp4_getSongTitle, // get song title text
+ mp4_getSongInfo, // info box
+ 0, // to output plugin
+ };
+
+typedef struct _mp4cfg{
+ gshort file_type;
+#define FILE_UNKNOW 0
+#define FILE_MP4 1
+#define FILE_AAC 2
+} Mp4Config;
+
+static Mp4Config mp4cfg;
+static gboolean bPlaying = FALSE;
+static pthread_t decodeThread;
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+static int seekPosition = -1;
+
+// Functions from mp4_utils.c
+extern int getAACTrack(mp4ff_t *infile);
+extern mp4ff_callback_t *getMP4FF_cb(FILE *mp4file);
+extern char *getMP4title(mp4ff_t *infile, char *filename);
+extern void getMP4info(char* filename, FILE *mp4file);
+
+InputPlugin *get_iplugin_info(void)
+{
+ return(&mp4_ip);
+}
+
+static void mp4_init(void)
+{
+ memset(&decodeThread, 0, sizeof(pthread_t));
+ mp4cfg.file_type = FILE_UNKNOW;
+ seekPosition = -1;
+ return;
+}
+
+static void mp4_play(char *filename)
+{
+ bPlaying = TRUE;
+ pthread_create(&decodeThread, 0, mp4Decode, g_strdup(filename));
+ return;
+}
+
+static void mp4_stop(void)
+{
+ if(bPlaying){
+ bPlaying = FALSE;
+ pthread_join(decodeThread, NULL);
+ memset(&decodeThread, 0, sizeof(pthread_t));
+ mp4_ip.output->close_audio();
+ }
+}
+
+static int mp4_isFile(char *filename)
+{
+ if(filename){
+ gchar* extention;
+
+ extention = strrchr(filename, '.');
+ if(extention &&
+ (!strncasecmp(extention, ".mp4", 4) || // official extention
+ !strncasecmp(extention, ".m4a", 4) || // Apple mp4 extention
+ !strncasecmp(extention, ".aac", 4) // old MPEG2/4-AAC extention
+ )){
+ return (1);
+ }
+ }
+ return(0);
+}
+
+static void mp4_about(void)
+{
+ static GtkWidget *aboutbox;
+
+ if(aboutbox!=NULL)
+ return;
+ aboutbox = xmms_show_message("About MP4 AAC player plugin",
+ "libfaad2-" FAAD2_VERSION "\n"
+ "plugin version: " MP4_VERSION "\n"
+ MP4_ABOUT,
+ "Ok", FALSE, NULL, NULL);
+ gtk_signal_connect(GTK_OBJECT(aboutbox), "destroy",
+ GTK_SIGNAL_FUNC(gtk_widget_destroyed),
+ &aboutbox);
+}
+
+static void mp4_pause(short flag)
+{
+ mp4_ip.output->pause(flag);
+}
+
+static void mp4_seek(int time)
+{
+ seekPosition = time;
+ while(bPlaying && seekPosition!=-1)
+ xmms_usleep(10000);
+}
+
+static int mp4_getTime(void)
+{
+ if(!bPlaying)
+ return (-1);
+ else
+ return (mp4_ip.output->output_time());
+}
+
+static void mp4_cleanup(void)
+{
+}
+
+void mp4_get_file_type(FILE *mp4file)
+{
+ unsigned char header[10] = {0};
+
+ fseek(mp4file, 0, SEEK_SET);
+ fread(header, 1, 8, mp4file);
+ if(header[4]=='f' &&
+ header[5]=='t' &&
+ header[6]=='y' &&
+ header[7]=='p'){
+ mp4cfg.file_type = FILE_MP4;
+ }else{
+ mp4cfg.file_type = FILE_AAC;
+ }
+}
+
+static void mp4_getSongTitle(char *filename, char **title, int *len) {
+ FILE* mp4file;
+
+ (*title) = NULL;
+ (*len) = -1;
+
+ if((mp4file = fopen(filename, "rb"))){
+ mp4_get_file_type(mp4file);
+ fseek(mp4file, 0, SEEK_SET);
+ if(mp4cfg.file_type == FILE_MP4){
+ mp4ff_callback_t* mp4cb;
+ mp4ff_t* infile;
+ gint mp4track;
+
+ mp4cb = getMP4FF_cb(mp4file);
+ if ((infile = mp4ff_open_read_metaonly(mp4cb)) &&
+ ((mp4track = getAACTrack(infile)) >= 0)){
+ (*title) = getMP4title(infile, filename);
+
+ double track_duration = mp4ff_get_track_duration(infile, mp4track);
+ unsigned long time_scale = mp4ff_time_scale(infile, mp4track);
+ unsigned long length = (track_duration * 1000 / time_scale);
+ (*len) = length;
+ }
+ if(infile) mp4ff_close(infile);
+ if(mp4cb) g_free(mp4cb);
+ }
+ else{
+ // Check AAC ID3 tag...
+ }
+ fclose(mp4file);
+ }
+}
+
+static void mp4_getSongInfo(char *filename)
+{
+ FILE* mp4file;
+ if((mp4file = fopen(filename, "rb"))){
+ if (mp4cfg.file_type == FILE_UNKNOW)
+ mp4_get_file_type(mp4file);
+ fseek(mp4file, 0, SEEK_SET);
+ if(mp4cfg.file_type == FILE_MP4)
+ getMP4info(filename, mp4file);
+ else if(mp4cfg.file_type == FILE_AAC)
+ /*
+ * check the id3tagv2
+ */
+ ;
+ fclose(mp4file);
+ }
+}
+
+static void *mp4Decode(void *args)
+{
+ FILE* mp4file;
+
+ pthread_mutex_lock(&mutex);
+ seekPosition = -1;
+ bPlaying = TRUE;
+
+ if(!(mp4file = fopen(args, "rb"))){
+ g_print("MP4!AAC - Can't open file\n");
+ g_free(args);
+ bPlaying = FALSE;
+ pthread_mutex_unlock(&mutex);
+ pthread_exit(NULL);
+
+ }
+ mp4_get_file_type(mp4file);
+ fseek(mp4file, 0, SEEK_SET);
+ if(mp4cfg.file_type == FILE_MP4){// We are reading a MP4 file
+ mp4ff_callback_t* mp4cb;
+ mp4ff_t* infile;
+ gint mp4track;
+
+ mp4cb = getMP4FF_cb(mp4file);
+ if(!(infile = mp4ff_open_read(mp4cb))){
+ g_print("MP4 - Can't open file\n");
+ goto end;
+ }
+
+ if((mp4track = getAACTrack(infile)) < 0){
+ /*
+ * TODO: check here for others Audio format.....
+ *
+ */
+ g_print("Unsupported Audio track type\n");
+ g_free(args);
+ fclose(mp4file);
+ bPlaying = FALSE;
+ pthread_mutex_unlock(&mutex);
+ pthread_exit(NULL);
+ }else{
+ NeAACDecHandle decoder;
+ unsigned char *buffer = NULL;
+ guint bufferSize = 0;
+ gulong samplerate;
+ guchar channels;
+ //guint avgBitrate;
+ //MP4Duration duration;
+ int msDuration;
+ int numSamples;
+ int sampleID = 0;
+ unsigned int framesize;
+ mp4AudioSpecificConfig mp4ASC;
+ gchar *xmmstitle;
+
+ decoder = NeAACDecOpen();
+ mp4ff_get_decoder_config(infile, mp4track, &buffer, &bufferSize);
+ if(NeAACDecInit2(decoder, buffer, bufferSize, &samplerate, &channels)<0){
+ goto end;
+ }
+ if(buffer){
+ framesize = 1024;
+ if(NeAACDecAudioSpecificConfig(buffer, bufferSize, &mp4ASC) >= 0){
+ if(mp4ASC.frameLengthFlag == 1) framesize = 960;
+ if(mp4ASC.sbr_present_flag == 1) framesize *= 2;
+ }
+ g_free(buffer);
+ }
+ if(channels == 0){
+ g_print("Number of Channels not supported\n");
+ goto end;
+ }
+
+ //duration = MP4GetTrackDuration(mp4file, mp4track);
+ //msDuration = MP4ConvertFromTrackDuration(mp4file, mp4track,
+ // duration,MP4_MSECS_TIME_SCALE);
+
+ //msDuration = mp4ff_get_track_duration(infile, mp4track);
+ //printf("%d\n", msDuration);
+
+ //numSamples = MP4GetTrackNumberOfSamples(mp4file, mp4track);
+ numSamples = mp4ff_num_samples(infile, mp4track);
+ {
+ float f = 1024.0;
+ if(mp4ASC.sbr_present_flag == 1)
+ f = f * 2.0;
+ msDuration = ((float)numSamples*(float)(f-1.0)/
+ (float)samplerate)*1000;
+ }
+ xmmstitle = getMP4title(infile, args);
+ mp4_ip.output->open_audio(FMT_S16_NE, samplerate, channels);
+ mp4_ip.output->flush(0);
+ mp4_ip.set_info(xmmstitle, msDuration, -1, samplerate/1000, channels);
+ g_print("MP4 - %d channels @ %ld Hz\n", channels, samplerate);
+
+ while(bPlaying){
+ void* sampleBuffer;
+ faacDecFrameInfo frameInfo;
+ gint rc;
+
+ if(seekPosition!=-1){
+ /*
+ duration = MP4ConvertToTrackDuration(mp4file,
+ mp4track,
+ seekPosition*1000,
+ MP4_MSECS_TIME_SCALE);
+ sampleID = MP4GetSampleIdFromTime(mp4file, mp4track, duration, 0);
+ */
+ float f = 1024.0;
+ if(mp4ASC.sbr_present_flag == 1)
+ f = f * 2.0;
+ sampleID = (float)seekPosition*(float)samplerate/(float)(f-1.0);
+ mp4_ip.output->flush(seekPosition*1000);
+ seekPosition = -1;
+ }
+ buffer=NULL;
+ bufferSize=0;
+ rc = mp4ff_read_sample(infile, mp4track, sampleID++,
+ &buffer, &bufferSize);
+ //g_print("%d/%d\n", sampleID-1, numSamples);
+ if((rc==0) || (buffer== NULL)){
+ g_print("MP4: read error\n");
+ sampleBuffer = NULL;
+ sampleID=0;
+ mp4_ip.output->buffer_free();
+ goto end;
+ }else{
+ sampleBuffer = NeAACDecDecode(decoder, &frameInfo, buffer, bufferSize);
+ if(frameInfo.error > 0){
+ g_print("MP4: %s\n",
+ faacDecGetErrorMessage(frameInfo.error));
+ goto end;
+ }
+ if(buffer){
+ g_free(buffer); buffer=NULL; bufferSize=0;
+ }
+ while(bPlaying && mp4_ip.output->buffer_free()<frameInfo.samples<<1)
+ xmms_usleep(30000);
+ }
+ mp4_ip.add_vis_pcm(mp4_ip.output->written_time(),
+ FMT_S16_NE,
+ channels,
+ frameInfo.samples<<1,
+ sampleBuffer);
+ mp4_ip.output->write_audio(sampleBuffer, frameInfo.samples<<1);
+ if(sampleID >= numSamples){
+ break;
+ }
+ }
+ while(bPlaying && mp4_ip.output->buffer_playing() && mp4_ip.output->buffer_free()){
+ xmms_usleep(10000);
+ }
+end:
+ mp4_ip.output->close_audio();
+ g_free(args);
+ NeAACDecClose(decoder);
+ if(infile) mp4ff_close(infile);
+ if(mp4cb) g_free(mp4cb);
+ bPlaying = FALSE;
+ fclose(mp4file);
+ pthread_mutex_unlock(&mutex);
+ pthread_exit(NULL);
+ }
+ }else{
+ // WE ARE READING AN AAC FILE
+ FILE *file = NULL;
+ NeAACDecHandle decoder = 0;
+ guchar *buffer = 0;
+ gulong bufferconsumed = 0;
+ gulong samplerate = 0;
+ guchar channels;
+ gulong buffervalid = 0;
+ TitleInput* input;
+ gchar *temp = g_strdup(args);
+ gchar *ext = strrchr(temp, '.');
+ gchar *xmmstitle = NULL;
+ NeAACDecConfigurationPtr config;
+
+ if((file = fopen(args, "rb")) == 0){
+ g_print("AAC: can't find file %s\n", args);
+ bPlaying = FALSE;
+ pthread_mutex_unlock(&mutex);
+ pthread_exit(NULL);
+ }
+ if((decoder = NeAACDecOpen()) == NULL){
+ g_print("AAC: Open Decoder Error\n");
+ fclose(file);
+ bPlaying = FALSE;
+ pthread_mutex_unlock(&mutex);
+ pthread_exit(NULL);
+ }
+ config = NeAACDecGetCurrentConfiguration(decoder);
+ config->useOldADTSFormat = 0;
+ NeAACDecSetConfiguration(decoder, config);
+ if((buffer = g_malloc(BUFFER_SIZE)) == NULL){
+ g_print("AAC: error g_malloc\n");
+ fclose(file);
+ bPlaying = FALSE;
+ NeAACDecClose(decoder);
+ pthread_mutex_unlock(&mutex);
+ pthread_exit(NULL);
+ }
+ if((buffervalid = fread(buffer, 1, BUFFER_SIZE, file))==0){
+ g_print("AAC: Error reading file\n");
+ g_free(buffer);
+ fclose(file);
+ bPlaying = FALSE;
+ NeAACDecClose(decoder);
+ pthread_mutex_unlock(&mutex);
+ pthread_exit(NULL);
+ }
+ XMMS_NEW_TITLEINPUT(input);
+ input->file_name = g_basename(temp);
+ input->file_ext = ext ? ext+1 : NULL;
+ input->file_path = temp;
+ if(!strncmp(buffer, "ID3", 3)){
+ gint size = 0;
+
+ fseek(file, 0, SEEK_SET);
+ size = (buffer[6]<<21) | (buffer[7]<<14) | (buffer[8]<<7) | buffer[9];
+ size+=10;
+ fread(buffer, 1, size, file);
+ buffervalid = fread(buffer, 1, BUFFER_SIZE, file);
+ }
+ xmmstitle = xmms_get_titlestring(xmms_get_gentitle_format(), input);
+ if(xmmstitle == NULL)
+ xmmstitle = g_strdup(input->file_name);
+ if(temp) g_free(temp);
+ if(input->performer) g_free(input->performer);
+ if(input->album_name) g_free(input->album_name);
+ if(input->track_name) g_free(input->track_name);
+ if(input->genre) g_free(input->genre);
+ g_free(input);
+ bufferconsumed = NeAACDecInit(decoder,
+ buffer,
+ buffervalid,
+ &samplerate,
+ &channels);
+ if(mp4_ip.output->open_audio(FMT_S16_NE,samplerate,channels) == FALSE){
+ g_print("AAC: Output Error\n");
+ g_free(buffer); buffer=0;
+ faacDecClose(decoder);
+ fclose(file);
+ mp4_ip.output->close_audio();
+ /*
+ if(positionTable){
+ g_free(positionTable); positionTable=0;
+ }
+ */
+ g_free(xmmstitle);
+ bPlaying = FALSE;
+ pthread_mutex_unlock(&mutex);
+ pthread_exit(NULL);
+ }
+ //if(bSeek){
+ //mp4_ip.set_info(xmmstitle, lenght*1000, -1, samplerate, channels);
+ //}else{
+ mp4_ip.set_info(xmmstitle, -1, -1, samplerate, channels);
+ //}
+ mp4_ip.output->flush(0);
+
+ while(bPlaying && buffervalid > 0){
+ NeAACDecFrameInfo finfo;
+ unsigned long samplesdecoded;
+ char* sample_buffer = NULL;
+ /*
+ if(bSeek && seekPosition!=-1){
+ fseek(file, positionTable[seekPosition], SEEK_SET);
+ bufferconsumed=0;
+ buffervalid = fread(buffer, 1, BUFFER_SIZE, file);
+ aac_ip.output->flush(seekPosition*1000);
+ seekPosition=-1;
+ }
+ */
+ if(bufferconsumed > 0){
+ memmove(buffer, &buffer[bufferconsumed], buffervalid-bufferconsumed);
+ buffervalid -= bufferconsumed;
+ buffervalid += fread(&buffer[buffervalid], 1,
+ BUFFER_SIZE-buffervalid, file);
+ bufferconsumed = 0;
+ }
+ sample_buffer = NeAACDecDecode(decoder, &finfo, buffer, buffervalid);
+ if(finfo.error){
+ config = NeAACDecGetCurrentConfiguration(decoder);
+ if(config->useOldADTSFormat != 1){
+ NeAACDecClose(decoder);
+ decoder = NeAACDecOpen();
+ config = NeAACDecGetCurrentConfiguration(decoder);
+ config->useOldADTSFormat = 1;
+ NeAACDecSetConfiguration(decoder, config);
+ finfo.bytesconsumed=0;
+ finfo.samples = 0;
+ NeAACDecInit(decoder,
+ buffer,
+ buffervalid,
+ &samplerate,
+ &channels);
+ }else{
+ g_print("FAAD2 Warning %s\n", NeAACDecGetErrorMessage(finfo.error));
+ buffervalid = 0;
+ }
+ }
+ bufferconsumed += finfo.bytesconsumed;
+ samplesdecoded = finfo.samples;
+ if((samplesdecoded<=0) && !sample_buffer){
+ g_print("AAC: error sample decoding\n");
+ continue;
+ }
+ while(bPlaying && mp4_ip.output->buffer_free() < (samplesdecoded<<1)){
+ xmms_usleep(10000);
+ }
+ mp4_ip.add_vis_pcm(mp4_ip.output->written_time(),
+ FMT_S16_LE, channels,
+ samplesdecoded<<1, sample_buffer);
+ mp4_ip.output->write_audio(sample_buffer, samplesdecoded<<1);
+ }
+ while(bPlaying && mp4_ip.output->buffer_playing()){
+ xmms_usleep(10000);
+ }
+ mp4_ip.output->buffer_free();
+ mp4_ip.output->close_audio();
+ bPlaying = FALSE;
+ g_free(buffer);
+ NeAACDecClose(decoder);
+ g_free(xmmstitle);
+ fclose(file);
+ seekPosition = -1;
+ /*
+ if(positionTable){
+ g_free(positionTable); positionTable=0;
+ }
+ */
+ bPlaying = FALSE;
+ pthread_mutex_unlock(&mutex);
+ pthread_exit(NULL);
+
+ }
+}
diff --git a/faad2/src/plugins/xmms/src/mp4_utils.c b/faad2/src/plugins/xmms/src/mp4_utils.c
new file mode 100644
index 0000000..a517963
--- /dev/null
+++ b/faad2/src/plugins/xmms/src/mp4_utils.c
@@ -0,0 +1,469 @@
+/*
+ * some functions for MP4 files
+*/
+
+#include "mp4ff.h"
+#include "faad.h"
+
+#include <gtk/gtk.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <xmms/plugin.h>
+#include <xmms/titlestring.h>
+#include <xmms/util.h>
+
+const char *mp4AudioNames[]=
+ {
+ "MPEG-1 Audio Layers 1,2 or 3",
+ "MPEG-2 low biterate (MPEG-1 extension) - MP3",
+ "MPEG-2 AAC Main Profile",
+ "MPEG-2 AAC Low Complexity profile",
+ "MPEG-2 AAC SSR profile",
+ "MPEG-4 audio (MPEG-4 AAC)",
+ 0
+ };
+
+/* MPEG-4 Audio types from 14496-3 Table 1.5.1 (from mp4.h)*/
+const char *mpeg4AudioNames[]=
+ {
+ "!!!!MPEG-4 Audio track Invalid !!!!!!!",
+ "MPEG-4 AAC Main profile",
+ "MPEG-4 AAC Low Complexity profile",
+ "MPEG-4 AAC SSR profile",
+ "MPEG-4 AAC Long Term Prediction profile",
+ "MPEG-4 AAC Scalable",
+ "MPEG-4 CELP",
+ "MPEG-4 HVXC",
+ "MPEG-4 Text To Speech",
+ "MPEG-4 Main Synthetic profile",
+ "MPEG-4 Wavetable Synthesis profile",
+ "MPEG-4 MIDI Profile",
+ "MPEG-4 Algorithmic Synthesis and Audio FX profile"
+ };
+
+static GtkWidget *mp4_info_dialog = NULL;
+
+/*
+ * find AAC track
+*/
+
+int getAACTrack(mp4ff_t *infile)
+{
+ int i, rc;
+ int numTracks = mp4ff_total_tracks(infile);
+
+ printf("total-tracks: %d\n", numTracks);
+ for(i=0; i<numTracks; i++){
+ unsigned char* buff = 0;
+ int buff_size = 0;
+ mp4AudioSpecificConfig mp4ASC;
+
+ printf("testing-track: %d\n", i);
+ mp4ff_get_decoder_config(infile, i, &buff, &buff_size);
+ if(buff){
+ rc = NeAACDecAudioSpecificConfig(buff, buff_size, &mp4ASC);
+ g_free(buff);
+ if(rc < 0)
+ continue;
+ return(i);
+ }
+ }
+ return(-1);
+}
+
+uint32_t read_callback(void *user_data, void *buffer, uint32_t length)
+{
+ return fread(buffer, 1, length, (FILE*)user_data);
+}
+
+uint32_t seek_callback(void *user_data, uint64_t position)
+{
+ return fseek((FILE*)user_data, position, SEEK_SET);
+}
+
+mp4ff_callback_t *getMP4FF_cb(FILE *mp4file)
+{
+ mp4ff_callback_t* mp4cb = malloc(sizeof(mp4ff_callback_t));
+ mp4cb->read = read_callback;
+ mp4cb->seek = seek_callback;
+ mp4cb->user_data = mp4file;
+ return mp4cb;
+}
+
+/*
+ * Function to display an info box for an mp4 file.
+ * This code is based heavily on fileinfo.c from the xmms mpg123
+ * plugin, and the info box layout mimics that plugin.
+*/
+void create_mp4_info_dialog (char *filename, FILE *mp4file, mp4ff_t *infile, gint mp4track)
+{
+ char *window_title, *value, *value2;
+ static GtkWidget *filename_entry, *title_entry, *artist_entry, *album_entry;
+ static GtkWidget *genre_entry, *year_entry, *track_entry, *comment_entry;
+ static GtkWidget *mp4_info_label;
+
+ if (!mp4_info_dialog)
+ {
+ GtkWidget *dialog_vbox1, *vbox1, *hbox2, *hbox3, *hbox4;
+ GtkWidget *frame2, *frame3, *table1, *dialog_action_area1;
+ GtkWidget *filename_label, *title_label, *artist_label, *album_label;
+ GtkWidget *genre_label, *year_label, *track_label, *comment_label;
+ GtkWidget *close_button;
+
+ mp4_info_dialog = gtk_dialog_new ();
+ gtk_object_set_data (GTK_OBJECT (mp4_info_dialog), "mp4_info_dialog", mp4_info_dialog);
+ gtk_window_set_policy (GTK_WINDOW (mp4_info_dialog), TRUE, TRUE, FALSE);
+ gtk_signal_connect(GTK_OBJECT (mp4_info_dialog), "destroy",
+ gtk_widget_destroyed, &mp4_info_dialog);
+
+ dialog_vbox1 = GTK_DIALOG (mp4_info_dialog)->vbox;
+ gtk_object_set_data (GTK_OBJECT (mp4_info_dialog), "dialog_vbox1", dialog_vbox1);
+ gtk_container_set_border_width (GTK_CONTAINER (dialog_vbox1), 3);
+
+ hbox2 = gtk_hbox_new (FALSE, 0);
+ gtk_widget_ref (hbox2);
+ gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "hbox2", hbox2,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_box_pack_start (GTK_BOX (dialog_vbox1), hbox2, FALSE, TRUE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (hbox2), 3);
+
+ filename_label = gtk_label_new ("Filename: ");
+ gtk_widget_ref (filename_label);
+ gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "filename_label", filename_label,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_box_pack_start (GTK_BOX (hbox2), filename_label, FALSE, FALSE, 0);
+
+ filename_entry = gtk_entry_new ();
+ gtk_widget_ref (filename_entry);
+ gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "filename_entry", filename_entry,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_box_pack_start (GTK_BOX (hbox2), filename_entry, TRUE, TRUE, 0);
+ gtk_entry_set_editable (GTK_ENTRY (filename_entry), FALSE);
+
+ hbox3 = gtk_hbox_new (FALSE, 0);
+ gtk_widget_ref (hbox3);
+ gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "hbox3", hbox3,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_box_pack_start (GTK_BOX (dialog_vbox1), hbox3, TRUE, TRUE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (hbox3), 3);
+
+ frame2 = gtk_frame_new ("Tag Info: ");
+ gtk_widget_ref (frame2);
+ gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "frame2", frame2,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_box_pack_start (GTK_BOX (hbox3), frame2, TRUE, TRUE, 0);
+
+ table1 = gtk_table_new (6, 2, FALSE);
+ gtk_widget_ref (table1);
+ gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "table1", table1,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_container_add (GTK_CONTAINER (frame2), table1);
+ gtk_container_set_border_width (GTK_CONTAINER (table1), 5);
+
+ comment_label = gtk_label_new ("Comment: ");
+ gtk_widget_ref (comment_label);
+ gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "comment_label", comment_label,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_table_attach (GTK_TABLE (table1), comment_label, 0, 1, 5, 6,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (GTK_EXPAND), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (comment_label), 1, 0.5);
+
+ genre_label = gtk_label_new ("Genre: ");
+ gtk_widget_ref (genre_label);
+ gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "genre_label", genre_label,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_table_attach (GTK_TABLE (table1), genre_label, 0, 1, 4, 5,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (GTK_EXPAND), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (genre_label), 1, 0.5);
+
+ album_label = gtk_label_new ("Album: ");
+ gtk_widget_ref (album_label);
+ gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "album_label", album_label,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_table_attach (GTK_TABLE (table1), album_label, 0, 1, 2, 3,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (GTK_EXPAND), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (album_label), 1, 0.5);
+
+ title_label = gtk_label_new ("Title: ");
+ gtk_widget_ref (title_label);
+ gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "title_label", title_label,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_table_attach (GTK_TABLE (table1), title_label, 0, 1, 0, 1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (GTK_EXPAND), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (title_label), 1, 0.5);
+
+ artist_label = gtk_label_new ("Artist: ");
+ gtk_widget_ref (artist_label);
+ gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "artist_label", artist_label,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_table_attach (GTK_TABLE (table1), artist_label, 0, 1, 1, 2,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (GTK_EXPAND), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (artist_label), 1, 0.5);
+
+ year_label = gtk_label_new ("Year: ");
+ gtk_widget_ref (year_label);
+ gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "year_label", year_label,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_table_attach (GTK_TABLE (table1), year_label, 0, 1, 3, 4,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (GTK_EXPAND), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (year_label), 1, 0.5);
+
+ hbox4 = gtk_hbox_new (FALSE, 0);
+ gtk_widget_ref (hbox4);
+ gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "hbox4", hbox4,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_table_attach (GTK_TABLE (table1), hbox4, 1, 2, 3, 4,
+ (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions) (GTK_FILL), 0, 0);
+
+ year_entry = gtk_entry_new ();
+ gtk_widget_ref (year_entry);
+ gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "year_entry", year_entry,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_box_pack_start (GTK_BOX (hbox4), year_entry, FALSE, FALSE, 0);
+ gtk_widget_set_usize (year_entry, 60, -2);
+ gtk_entry_set_editable (GTK_ENTRY (year_entry), FALSE);
+
+ track_label = gtk_label_new (" Track: ");
+ gtk_widget_ref (track_label);
+ gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "track_label", track_label,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_box_pack_start (GTK_BOX (hbox4), track_label, FALSE, FALSE, 0);
+ gtk_misc_set_alignment (GTK_MISC (track_label), 1, 0.5);
+
+ track_entry = gtk_entry_new ();
+ gtk_widget_ref (track_entry);
+ gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "track_entry", track_entry,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_box_pack_start (GTK_BOX (hbox4), track_entry, FALSE, FALSE, 0);
+ gtk_widget_set_usize (track_entry, 60, -2);
+ gtk_entry_set_editable (GTK_ENTRY (track_entry), FALSE);
+
+ title_entry = gtk_entry_new ();
+ gtk_widget_ref (title_entry);
+ gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "title_entry", title_entry,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_table_attach (GTK_TABLE (table1), title_entry, 1, 2, 0, 1,
+ (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_entry_set_editable (GTK_ENTRY (title_entry), FALSE);
+
+ artist_entry = gtk_entry_new ();
+ gtk_widget_ref (artist_entry);
+ gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "artist_entry", artist_entry,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_table_attach (GTK_TABLE (table1), artist_entry, 1, 2, 1, 2,
+ (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_entry_set_editable (GTK_ENTRY (artist_entry), FALSE);
+
+ album_entry = gtk_entry_new ();
+ gtk_widget_ref (album_entry);
+ gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "album_entry", album_entry,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_table_attach (GTK_TABLE (table1), album_entry, 1, 2, 2, 3,
+ (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_entry_set_editable (GTK_ENTRY (album_entry), FALSE);
+
+ genre_entry = gtk_entry_new ();
+ gtk_widget_ref (genre_entry);
+ gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "genre_entry", genre_entry,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_table_attach (GTK_TABLE (table1), genre_entry, 1, 2, 4, 5,
+ (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_entry_set_editable (GTK_ENTRY (genre_entry), FALSE);
+
+ comment_entry = gtk_entry_new ();
+ gtk_widget_ref (comment_entry);
+ gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "comment_entry", comment_entry,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_table_attach (GTK_TABLE (table1), comment_entry, 1, 2, 5, 6,
+ (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_entry_set_editable (GTK_ENTRY (comment_entry), FALSE);
+
+ frame3 = gtk_frame_new ("MP4 Info: ");
+ gtk_widget_ref (frame3);
+ gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "frame3", frame3,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_box_pack_start (GTK_BOX (hbox3), frame3, FALSE, TRUE, 0);
+
+ vbox1 = gtk_vbox_new (FALSE, 0);
+ gtk_widget_ref (vbox1);
+ gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "vbox1", vbox1,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_container_add (GTK_CONTAINER (frame3), vbox1);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox1), 5);
+
+ mp4_info_label = gtk_label_new ("");
+ gtk_widget_ref (mp4_info_label);
+ gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "mp4_info_label", mp4_info_label,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_box_pack_start (GTK_BOX (vbox1), mp4_info_label, TRUE, TRUE, 0);
+ gtk_label_set_justify (GTK_LABEL (mp4_info_label), GTK_JUSTIFY_LEFT);
+ gtk_misc_set_alignment (GTK_MISC (mp4_info_label), 0, 0);
+
+ dialog_action_area1 = GTK_DIALOG (mp4_info_dialog)->action_area;
+ gtk_object_set_data (GTK_OBJECT (mp4_info_dialog), "dialog_action_area1", dialog_action_area1);
+ gtk_container_set_border_width (GTK_CONTAINER (dialog_action_area1), 4);
+
+ close_button = gtk_button_new_with_label ("Close");
+ gtk_widget_ref (close_button);
+ gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "close_button", close_button,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_box_pack_start (GTK_BOX (dialog_action_area1), close_button, FALSE, FALSE, 0);
+
+ gtk_signal_connect_object (GTK_OBJECT (close_button), "clicked",
+ GTK_SIGNAL_FUNC (gtk_widget_destroy), GTK_OBJECT (mp4_info_dialog));
+
+ }
+
+ window_title = g_strdup_printf("File Info - %s", g_basename(filename));
+ gtk_window_set_title (GTK_WINDOW (mp4_info_dialog), window_title);
+ g_free(window_title);
+
+ gtk_entry_set_text (GTK_ENTRY (filename_entry), filename);
+
+ gtk_entry_set_text (GTK_ENTRY (title_entry), "");
+ gtk_entry_set_text (GTK_ENTRY (artist_entry), "");
+ gtk_entry_set_text (GTK_ENTRY (album_entry), "");
+ gtk_entry_set_text (GTK_ENTRY (year_entry), "");
+ gtk_entry_set_text (GTK_ENTRY (track_entry), "");
+ gtk_entry_set_text (GTK_ENTRY (genre_entry), "");
+ gtk_entry_set_text (GTK_ENTRY (comment_entry), "");
+
+ if ((mp4ff_meta_get_title(infile, &value)) && value != NULL) {
+ gtk_entry_set_text (GTK_ENTRY(title_entry), value);
+ g_free(value);
+ }
+ if ((mp4ff_meta_get_artist(infile, &value)) && value != NULL) {
+ gtk_entry_set_text (GTK_ENTRY(artist_entry), value);
+ g_free(value);
+ }
+ if ((mp4ff_meta_get_album(infile, &value)) && value != NULL) {
+ gtk_entry_set_text (GTK_ENTRY(album_entry), value);
+ g_free(value);
+ }
+ if ((mp4ff_meta_get_date(infile, &value)) && value != NULL) {
+ gtk_entry_set_text (GTK_ENTRY(year_entry), value);
+ g_free(value);
+ }
+ if ((mp4ff_meta_get_track(infile, &value)) && value != NULL) {
+ if ((mp4ff_meta_get_totaltracks(infile, &value2)) && value2 != NULL) {
+ char *tmp = g_strdup_printf("%s of %s", value, value2);
+ g_free(value2);
+ g_free(value);
+ value = tmp;
+ }
+ gtk_entry_set_text (GTK_ENTRY(track_entry), value);
+ g_free(value);
+ }
+ if ((mp4ff_meta_get_genre(infile, &value)) && value != NULL) {
+ gtk_entry_set_text (GTK_ENTRY(genre_entry), value);
+ g_free(value);
+ }
+ if ((mp4ff_meta_get_comment(infile, &value)) && value != NULL) {
+ gtk_entry_set_text (GTK_ENTRY(comment_entry), value);
+ g_free(value);
+ }
+
+ // Get the length of the track.
+ double track_duration = mp4ff_get_track_duration(infile, mp4track);
+ unsigned long time_scale = mp4ff_time_scale(infile, mp4track);
+ unsigned long length = (track_duration / time_scale);
+ int min = length / 60;
+ int sec = length % 60;
+
+ // Get other info about the track.
+ unsigned long bitrate = mp4ff_get_avg_bitrate(infile, mp4track) / 1000;
+ unsigned long samplerate = mp4ff_get_sample_rate(infile, mp4track);
+ unsigned long channels = mp4ff_get_channel_count(infile, mp4track);
+ unsigned long audio_type = mp4ff_get_audio_type(infile, mp4track);
+ fseek(mp4file, 0, SEEK_END);
+ int filesize = ftell(mp4file) / 1024;
+
+ value = g_strdup_printf("Length: %d:%d\nAvg. Bitrate: %ld kbps\nSample Rate: %ld Hz\nChannels: %ld\nAudio Type: %ld\nFile Size: %d KB", min, sec, bitrate, samplerate, channels, audio_type, filesize);
+ gtk_label_set_text (GTK_LABEL(mp4_info_label), value);
+ g_free(value);
+
+ gtk_widget_show_all(mp4_info_dialog);
+}
+
+
+void getMP4info(char* filename, FILE* mp4file)
+{
+ mp4ff_callback_t* mp4cb;
+ mp4ff_t* infile;
+ gint mp4track;
+
+ mp4cb = getMP4FF_cb(mp4file);
+ if ((infile = mp4ff_open_read_metaonly(mp4cb)) &&
+ ((mp4track = getAACTrack(infile)) >= 0)){
+ create_mp4_info_dialog (filename, mp4file, infile, mp4track);
+ }
+ if(infile) mp4ff_close(infile);
+ if(mp4cb) g_free(mp4cb);
+}
+
+/* Get the xmms titlestring for the file based on metadata.
+The following code was adapted from the gtkpod project, specifically
+mp4file.c (C) Jorg Schuler, but written to use the mp4ff library. The
+mpg123 input plugin for xmms was used as a guide for this function.
+ --Jason Arroyo, 2004 */
+char *getMP4title(mp4ff_t *infile, char *filename) {
+ char *ret=NULL;
+ gchar *value, *path, *temp;
+
+ TitleInput *input;
+ XMMS_NEW_TITLEINPUT(input);
+
+ // Fill in the TitleInput with the relevant data
+ // from the mp4 file that can be used to display the title.
+ mp4ff_meta_get_title(infile, &input->track_name);
+ mp4ff_meta_get_artist(infile, &input->performer);
+ mp4ff_meta_get_album(infile, &input->album_name);
+ if (mp4ff_meta_get_track(infile, &value) && value != NULL) {
+ input->track_number = atoi(value);
+ g_free(value);
+ }
+ if (mp4ff_meta_get_date(infile, &value) && value != NULL) {
+ input->year = atoi(value);
+ g_free(value);
+ }
+ mp4ff_meta_get_genre(infile, &input->genre);
+ mp4ff_meta_get_comment(infile, &input->comment);
+ input->file_name = g_strdup(g_basename(filename));
+ path = g_strdup(filename);
+ temp = strrchr(path, '.');
+ if (temp != NULL) {++temp;}
+ input->file_ext = g_strdup_printf("%s", temp);
+ temp = strrchr(path, '/');
+ if (temp) {*temp = '\0';}
+ input->file_path = g_strdup_printf("%s/", path);
+
+ // Use the default xmms title format to format the
+ // title from the above info.
+ ret = xmms_get_titlestring(xmms_get_gentitle_format(), input);
+
+ g_free(input->track_name);
+ g_free(input->performer);
+ g_free(input->album_name);
+ g_free(input->genre);
+ g_free(input->comment);
+ g_free(input->file_name);
+ g_free(input->file_ext);
+ g_free(input->file_path);
+ g_free(input);
+ g_free(path);
+
+ return ret;
+}