From cc2160de5cc05dc3aa77f3a34358e66f6193c8c9 Mon Sep 17 00:00:00 2001 From: Michał Cichoń Date: Tue, 26 Jun 2012 20:35:30 +0200 Subject: Add support for AAC. --- faad2/src/plugins/xmms/AUTHORS | 3 + faad2/src/plugins/xmms/ChangeLog | 43 +++ faad2/src/plugins/xmms/INSTALL | 2 + faad2/src/plugins/xmms/Makefile.am | 2 + faad2/src/plugins/xmms/NEWS | 21 ++ faad2/src/plugins/xmms/README | 23 ++ faad2/src/plugins/xmms/TODO | 6 + faad2/src/plugins/xmms/src/Makefile.am | 14 + faad2/src/plugins/xmms/src/aac_utils.c | 104 ++++++ faad2/src/plugins/xmms/src/libmp4.c | 586 +++++++++++++++++++++++++++++++++ faad2/src/plugins/xmms/src/mp4_utils.c | 469 ++++++++++++++++++++++++++ 11 files changed, 1273 insertions(+) create mode 100644 faad2/src/plugins/xmms/AUTHORS create mode 100644 faad2/src/plugins/xmms/ChangeLog create mode 100644 faad2/src/plugins/xmms/INSTALL create mode 100644 faad2/src/plugins/xmms/Makefile.am create mode 100644 faad2/src/plugins/xmms/NEWS create mode 100644 faad2/src/plugins/xmms/README create mode 100644 faad2/src/plugins/xmms/TODO create mode 100644 faad2/src/plugins/xmms/src/Makefile.am create mode 100644 faad2/src/plugins/xmms/src/aac_utils.c create mode 100644 faad2/src/plugins/xmms/src/libmp4.c create mode 100644 faad2/src/plugins/xmms/src/mp4_utils.c (limited to 'faad2/src/plugins/xmms') 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 +#include +#include +#include + + +#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 +#include +#include +#include + +#if defined(HAVE_BMP) +#include +#include +#include +#include +#else +#include +#include +#include +#include +#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()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 +#include +#include +#include +#include +#include +#include + +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; iread = 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; +} -- cgit v1.2.3