shithub: aacdec

ref: 9831e451d4dffcfe05f7d8d60e3c96c8f04d7eef
dir: /plugins/xmms/src/libaac.c/

View raw version
/*
**			  AAC plugin for XMMS 1.2.7
**				by ciberfred
**		------------------------------------------------
**
** need libfaad2-2.0  package from http://www.audiocoding.com
** and id3lib-3.8.x   package from http://www.id3lib.org
**
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <glib.h>
#include <gtk/gtk.h>

#include "faad.h"
#include "xmms/plugin.h"
#include "xmms/util.h"
#include "xmms/configfile.h"
#include "xmms/titlestring.h"
#include "aac_utils.h"

#define AAC_DESCRIPTION	"MPEG2/4 AAC player - 1.2.7"
#define AAC_VERSION	"AAC player - 5 July 2003 (v0.4.1)"
#define AAC_ABOUT	"Writen from scratch by ciberfred from France\n"
#define PATH2CONFIGFILE "/.xmms/Plugins/aacConfig.txt"
#define BUFFER_SIZE	FAAD_MIN_STREAMSIZE*64

static void	aac_init(void);
static void	aac_play(char*);
static void	aac_stop(void);
static void	aac_pause(short);
static int	aac_getTime(void);
static void	aac_seek(int);
static void	aac_cleanup(void);
static void	aac_about(void);
static void	aac_configuration(void);
static void	*aac_decode(void*);

static void	aac_getSongInfo(char*);
static int	aac_isFile(char*);

extern void	readID3tag(char*);
extern GtkWidget *createDialogInfo(void);
extern void	clearWindowDatas(void);

static GtkWidget *infoBoxWindow = NULL;
extern char *title, *artist, *album, *track, *genre;


/*****************************************************************************/
/*
** struct need by xmms for Library Interface
*/

InputPlugin aac_ip =
  {
    0,		// handle
    0,		// filename
    AAC_DESCRIPTION,// description
    aac_init,	// init_func
    aac_about,	// aboutbox
    aac_configuration,		// configuration
    aac_isFile,	// ???
    0,		// scan dir
    aac_play,	// when play button
    aac_stop,	// when stop
    aac_pause,	// when pause
    aac_seek,	// when seek
    0,		// set equalizer
    aac_getTime,	// ???
    0,		// get volume
    0,		// set volume
    aac_cleanup,	// the cleanup function :)
    0,		// obsolete (???)
    0,		// send visualisation data
    0,		// set player window info
    0,		// set song title text
    0,	// get song title text to show on Playlist
    aac_getSongInfo,// file info box
    0		// pointer to outputPlugin
  };
static gboolean 	bPlaying = FALSE;
static gboolean		bOutputOpen = FALSE;
static pthread_t	decodeThread;
static gboolean		bSeek = FALSE;
static gint		seekPosition = -1; // track position
static pthread_mutex_t	mutex = PTHREAD_MUTEX_INITIALIZER;
static unsigned long	*positionTable = 0;
int			aacType;
/*****************************************************************************/

InputPlugin *get_iplugin_info(void)
{
  return (&aac_ip);
}


static void aac_init(void)
{
  ConfigFile*	cfg;

  memset(&decodeThread, 0, sizeof(pthread_t));
  cfg = xmms_cfg_open_default_file();
  xmms_cfg_read_boolean(cfg, "AAC", "seeking", &bSeek);
  xmms_cfg_free(cfg);
}

static void aac_cleanup(void)
{
  memset(&decodeThread, 0, sizeof(pthread_t));
  if(positionTable){
    free(positionTable);
  }

}

static void aac_play(char *filename)
{
  bPlaying = TRUE;
  if(pthread_create(&decodeThread, 0, aac_decode, g_strdup(filename))!=0){
    printf("Error creating pthread, can't play file\n");
  }

  return;
}

static void aac_stop(void)
{
  if (bPlaying){
    bPlaying = FALSE;
    pthread_join(decodeThread, NULL);
    memset(&decodeThread, 0, sizeof(pthread_t));
    aac_ip.output->close_audio();
    clearWindowDatas();
  }
}

static void aac_pause(short paused)
{
  if(bOutputOpen){
    aac_ip.output->pause(paused);
  }
}

static int aac_getTime(void)
{
  if (!bPlaying){
    return (-1);
  }
  else{
    return (aac_ip.output->output_time());
  }
}

static void aac_seek(int time)
{
  seekPosition=time;
  while(bPlaying && seekPosition!=-1) xmms_usleep(10000);
}

static void aac_getSongInfo(char *filename)
{
  infoBoxWindow = createDialogInfo();
  gtk_widget_show(infoBoxWindow);
}

static void *aac_decode(void *args)
{
  char 			*filename = args;
  char			*xmmstitle=NULL;
  FILE 			*file = NULL;
  faacDecHandle		decoder = 0;
  unsigned char		*buffer = 0;
  unsigned long		bufferconsumed = 0;
  unsigned long		samplerate = 0;
  unsigned long		lenght=0;
  char			channels;
  unsigned long		buffervalid = 0;
  TitleInput		*input;
  char 			*temp = g_strdup(filename);
  char			*ext  = strrchr(temp, '.');

  pthread_mutex_lock(&mutex);
  seekPosition=-1;
  clearWindowDatas();
  if((file = fopen(filename, "rb")) == 0){
    printf("can't find file %s\n", filename);
    pthread_mutex_unlock(&mutex);
    pthread_exit(NULL);
  }
  if(bSeek){
    checkADTSForSeeking(file, &positionTable, &lenght);
    if((aacType = getAacInfo(file)) ==-1){
      g_print("erreur getAAC\n");
      fclose(file);
      if(positionTable){
	free(positionTable); positionTable=0;
      }
      pthread_mutex_unlock(&mutex);
      pthread_exit(NULL);
    }
  }
  if((decoder = faacDecOpen()) == NULL){
    printf("Open Decoder Error\n");
    fclose(file);
    pthread_mutex_unlock(&mutex);
    pthread_exit(NULL);
  }
  if((buffer = g_malloc(BUFFER_SIZE)) == NULL){
    printf("error g_malloc\n");
    fclose(file);
    pthread_mutex_unlock(&mutex);
    pthread_exit(NULL);
  }

  if((buffervalid = fread(buffer, 1, BUFFER_SIZE, file))==0){
    printf("Error file NULL\n");
    g_free(buffer);
    fclose(file);
    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)){
    int size = 0;

    readID3tag(filename);
    if(title)
      input->track_name = g_strdup(title);
    if(artist)
      input->performer = g_strdup(artist);
    if(genre)
      input->genre = g_strdup(genre);
    if(track)
      input->track_number = atoi(track);
    fseek(file, 0, SEEK_SET);
/*
** hum .. horrible hack taken from the winamp plugin to jump
** the tag, is there any id3 function to do this ???? hum... seems not :(
*/
    size = (buffer[6]<<21) | (buffer[7]<<14) | (buffer[8]<<7) | buffer[9];
    size+=10;
    fread(buffer, 1, size, file);
    if(bSeek){
      checkADTSForSeeking(file, &positionTable, &lenght);
      if((aacType = getAacInfo(file)) ==-1){
	printf("erreur getAAC\n");
	g_free(buffer); buffer=0;
	faacDecClose(decoder);
	fclose(file);
	aac_ip.output->close_audio();
	if(positionTable){
	  free(positionTable); positionTable=0;
	}
	pthread_mutex_unlock(&mutex);
	pthread_exit(NULL);
      }
      printf("AAC-%s Type\n", aacType?"MPEG2":"MPEG4");
    }
    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);
  g_free(temp);
  g_free(input->performer);
  g_free(input->album_name);
  g_free(input->track_name);
  g_free(input->genre);
  g_free(input);

  bufferconsumed = faacDecInit(decoder, buffer, BUFFER_SIZE, &samplerate, &channels);
  if((bOutputOpen = aac_ip.output->open_audio(FMT_S16_NE, samplerate, channels)) == FALSE){
    printf("Output Error\n");
    g_free(buffer); buffer=0;
    faacDecClose(decoder);
    fclose(file);
    aac_ip.output->close_audio();
    if(positionTable){
      free(positionTable); positionTable=0;
    }
    pthread_mutex_unlock(&mutex);
    pthread_exit(NULL);
  }
  if(bSeek){
    aac_ip.set_info(xmmstitle, lenght*1000, -1, samplerate, channels);
  }else{
    aac_ip.set_info(xmmstitle, -1, -1, samplerate, channels);
  }
  aac_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){
      buffervalid = 0;
      printf("FAAD2 Error %s\n", faacDecGetErrorMessage(finfo.error));
      printf("---Use Psystrip.exe on the file to avoid the ADTS error---\n");
    }
    bufferconsumed += finfo.bytesconsumed;
    samplesdecoded = finfo.samples;
    if((samplesdecoded<=0) && !sample_buffer){
      printf("error\n");
    }
    while(bPlaying && aac_ip.output->buffer_free() < (samplesdecoded<<1)){
      xmms_usleep(10000);
    }
    aac_ip.add_vis_pcm(aac_ip.output->written_time(), FMT_S16_LE, channels, samplesdecoded<<1, sample_buffer);
    aac_ip.output->write_audio(sample_buffer, samplesdecoded<<1);
  }
  while(bPlaying && aac_ip.output->buffer_playing()){
    xmms_usleep(10000);
  }
  aac_ip.output->buffer_free();
  aac_ip.output->close_audio();
  bPlaying = FALSE;
  bOutputOpen = FALSE;
  g_free(buffer);
  faacDecClose(decoder);
  g_free(xmmstitle);
  fclose(file);
  seekPosition = -1;
  if(positionTable){
    free(positionTable); positionTable=0;
  }
  pthread_mutex_unlock(&mutex);
  pthread_exit(NULL);
}

static int aac_isFile(char *filename)
{
  char *extention = strrchr(filename, '.');
  if (extention && !strcasecmp(extention, ".aac")){
    return (1);
  }
  return(0);
}

static void aac_about(void)
{
  static GtkWidget	*aboutbox;

  if(aboutbox!=NULL)
    return;

  aboutbox = xmms_show_message(
		 "About MPEG2/4-AAC plugin",
		 "decoding engine : FAAD2 team\n"
		 "Plugin coder : ciberfred",
 	         "Ok", FALSE, NULL, NULL);
  gtk_signal_connect(GTK_OBJECT(aboutbox), "destroy",
		     GTK_SIGNAL_FUNC(gtk_widget_destroyed),
		     &aboutbox);
}


static  GtkWidget *checkbutton;
static  GtkWidget *window;
static void aac_configuration_save(GtkWidget *widget, gpointer data)
{
  ConfigFile	*cfg;
  bSeek = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbutton));
  cfg = xmms_cfg_open_default_file();
  xmms_cfg_write_boolean(cfg, "AAC", "seeking", bSeek);
  xmms_cfg_free(cfg);
  gtk_widget_destroy(window);
}

static void	aac_configuration(void)
{
  GtkWidget *vbox, *hbox;
  GtkWidget *NotaBene;
  GtkWidget *button2;

  window = gtk_window_new(GTK_WINDOW_DIALOG);
  gtk_signal_connect(GTK_OBJECT(window), "destroy",
		     GTK_SIGNAL_FUNC(gtk_widget_destroyed),
		     &window);
  gtk_window_set_title(GTK_WINDOW(window), "AAC Plugin Configuration");
  gtk_widget_set_usize(window, 220, 200);

  vbox = gtk_vbox_new(FALSE, 2);
  gtk_container_add(GTK_CONTAINER(window), vbox);

  NotaBene = gtk_text_new(NULL, NULL);
  GTK_WIDGET_UNSET_FLAGS (NotaBene, GTK_CAN_FOCUS);
  gtk_text_insert(GTK_TEXT(NotaBene), NULL, NULL, NULL,
                   "Remember that unable seeking"
		   " is not suitable for playing"
		   " file from network.\n"
		   "Seeking must read first all aac file before playing.",-1);
  gtk_box_pack_start(GTK_BOX(vbox), NotaBene, FALSE, FALSE, 0);

  checkbutton = gtk_check_button_new_with_label("Unable Seeking");
  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton), bSeek);
  gtk_box_pack_start(GTK_BOX(vbox), checkbutton, FALSE, FALSE, 0);

  hbox = gtk_hbutton_box_new();
  gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);

  button2 = gtk_button_new_with_label ("Save");
  gtk_signal_connect_object(GTK_OBJECT(button2), "clicked",
			    GTK_SIGNAL_FUNC(aac_configuration_save),
			    GTK_OBJECT(window));
  gtk_box_pack_start(GTK_BOX(hbox), button2, FALSE, FALSE, 0);
  button2 = gtk_button_new_with_label ("Close");
  gtk_signal_connect_object(GTK_OBJECT(button2), "clicked",
			    GTK_SIGNAL_FUNC(gtk_widget_destroy),
			    GTK_OBJECT(window));
  gtk_box_pack_start(GTK_BOX(hbox), button2, FALSE, FALSE, 0);
  gtk_widget_show_all(window);
}