ref: bafbfab54cc147223da4dd4a3ae9dda60a46216b
dir: /plugins/xmms/src/libmp4.c/
/*
* MP4/AAC decoder for xmms
*
* OPTIONNAL need
* --------------
* libid3 (3.8.x - www.id3.org)
*/
#include <pthread.h>
#include <gtk/gtk.h>
#include "faad.h"
#include "mp4.h"
#include <xmms/plugin.h>
#include <xmms/util.h>
#include <xmms/configfile.h>
#include <xmms/titlestring.h>
#define MP4_DESCRIPTION "MP4 & MPEG2/4-AAC audio player - 1.2.x"
#define MP4_VERSION "ver. 0.4 - 24 November 2003"
#define LIBMP4V2_VERSION "-faad2-version"
#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_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
0, // 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;
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 &&
!strcasecmp(extention, ".mp4") || // official extention
!strcasecmp(extention, ".m4a") || // Apple mp4 extention
!strcasecmp(extention, ".aac") // 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"
"libmp4v2-" LIBMP4V2_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)
{
}
static void mp4_getSongInfo(char *filename)
{
if(mp4cfg.file_type == FILE_MP4)
getMP4info(filename);
else if(mp4cfg.file_type == FILE_AAC)
;
}
static void *mp4Decode(void *args)
{
MP4FileHandle mp4file;
pthread_mutex_lock(&mutex);
seekPosition = -1;
bPlaying = TRUE;
if(!(mp4file = MP4Read(args, 0))){
mp4cfg.file_type = FILE_AAC;
MP4Close(mp4file);
}else{
mp4cfg.file_type = FILE_MP4;
}
if(mp4cfg.file_type == FILE_MP4){
// We are reading a MP4 file
gint mp4track;
if((mp4track = getAACTrack(mp4file)) < 0){
//TODO: check here for others Audio format.....
g_print("Unsupported Audio track type\n");
g_free(args);
MP4Close(mp4file);
bPlaying = FALSE;
pthread_mutex_unlock(&mutex);
pthread_exit(NULL);
}else{
faacDecHandle decoder;
unsigned char *buffer = NULL;
guint bufferSize = 0;
gulong samplerate;
guchar channels;
guint avgBitrate;
MP4Duration duration;
gulong msDuration;
MP4SampleId numSamples;
MP4SampleId sampleID = 1;
decoder = faacDecOpen();
MP4GetTrackESConfiguration(mp4file, mp4track, &buffer, &bufferSize);
if(!buffer){
g_free(args);
faacDecClose(decoder);
MP4Close(mp4file);
bPlaying = FALSE;
pthread_mutex_unlock(&mutex);
pthread_exit(NULL);
}
if(faacDecInit2(decoder, buffer, bufferSize, &samplerate, &channels)<0){
g_free(args);
faacDecClose(decoder);
MP4Close(mp4file);
bPlaying = FALSE;
pthread_mutex_unlock(&mutex);
pthread_exit(NULL);
}
g_free(buffer);
if(channels == 0){
g_print("Number of Channels not supported\n");
g_free(args);
faacDecClose(decoder);
MP4Close(mp4file);
bPlaying = FALSE;
pthread_mutex_unlock(&mutex);
pthread_exit(NULL);
}
duration = MP4GetTrackDuration(mp4file, mp4track);
msDuration = MP4ConvertFromTrackDuration(mp4file, mp4track, duration,
MP4_MSECS_TIME_SCALE);
numSamples = MP4GetTrackNumberOfSamples(mp4file, mp4track);
mp4_ip.output->open_audio(FMT_S16_NE, samplerate, channels);
mp4_ip.output->flush(0);
mp4_ip.set_info(args, msDuration, -1, samplerate/1000, channels);
g_print("MP4 - %d channels @ %d 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);
mp4_ip.output->flush(seekPosition*1000);
seekPosition = -1;
}
buffer=NULL;
bufferSize=0;
if(sampleID > numSamples){
mp4_ip.output->close_audio();
g_free(args);
faacDecClose(decoder);
MP4Close(mp4file);
bPlaying = FALSE;
pthread_mutex_unlock(&mutex);
pthread_exit(NULL);
}
rc = MP4ReadSample(mp4file, mp4track, sampleID++, &buffer, &bufferSize,
NULL, NULL, NULL, NULL);
//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();
mp4_ip.output->close_audio();
g_free(args);
faacDecClose(decoder);
MP4Close(mp4file);
bPlaying = FALSE;
pthread_mutex_unlock(&mutex);
pthread_exit(NULL);
}else{
sampleBuffer = faacDecDecode(decoder, &frameInfo, buffer, bufferSize);
if(frameInfo.error > 0){
g_print("MP4: %s\n",
faacDecGetErrorMessage(frameInfo.error));
mp4_ip.output->close_audio();
g_free(args);
faacDecClose(decoder);
MP4Close(mp4file);
bPlaying = FALSE;
pthread_mutex_unlock(&mutex);
pthread_exit(NULL);
}
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);
}
while(bPlaying && mp4_ip.output->buffer_free()){
xmms_usleep(10000);
}
mp4_ip.output->close_audio();
g_free(args);
faacDecClose(decoder);
MP4Close(mp4file);
bPlaying = FALSE;
pthread_mutex_unlock(&mutex);
pthread_exit(NULL);
}
} else{
// WE ARE READING AN AAC FILE
FILE *file = NULL;
faacDecHandle 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;
faacDecConfigurationPtr 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 = faacDecOpen()) == NULL){
g_print("AAC: Open Decoder Error\n");
fclose(file);
bPlaying = FALSE;
pthread_mutex_unlock(&mutex);
pthread_exit(NULL);
}
config = faacDecGetCurrentConfiguration(decoder);
config->useOldADTSFormat = 0;
faacDecSetConfiguration(decoder, config);
if((buffer = g_malloc(BUFFER_SIZE)) == NULL){
g_print("AAC: error g_malloc\n");
fclose(file);
bPlaying = FALSE;
faacDecClose(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;
faacDecClose(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 = faacDecInit(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){
faacDecFrameInfo 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 = faacDecDecode(decoder, &finfo, buffer, buffervalid);
if(finfo.error){
config = faacDecGetCurrentConfiguration(decoder);
if(config->useOldADTSFormat != 1){
faacDecClose(decoder);
decoder = faacDecOpen();
config = faacDecGetCurrentConfiguration(decoder);
config->useOldADTSFormat = 1;
faacDecSetConfiguration(decoder, config);
finfo.bytesconsumed=0;
finfo.samples = 0;
faacDecInit(decoder,
buffer,
buffervalid,
&samplerate,
&channels);
}else{
g_print("FAAD2 Warning %s\n", faacDecGetErrorMessage(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);
faacDecClose(decoder);
g_free(xmmstitle);
fclose(file);
seekPosition = -1;
/*
if(positionTable){
g_free(positionTable); positionTable=0;
}
*/
bPlaying = FALSE;
pthread_mutex_unlock(&mutex);
pthread_exit(NULL);
}
}