diff options
| -rw-r--r-- | src/main.c | 20 | ||||
| -rw-r--r-- | src/player.c | 135 | ||||
| -rw-r--r-- | src/player.h | 13 | ||||
| -rw-r--r-- | src/ui_act.c | 25 | 
4 files changed, 87 insertions, 106 deletions
| @@ -183,7 +183,7 @@ static void BarMainGetPlaylist (BarApp_t *app) {  /*	start new player thread   */ -static void BarMainStartPlayback (BarApp_t *app) { +static void BarMainStartPlayback (BarApp_t *app, pthread_t *playerThread) {  	BarUiPrintSong (&app->settings, app->playlist, app->curStation->isQuickMix ?  			PianoFindStationById (app->ph.stations,  			app->playlist->stationId) : NULL); @@ -216,24 +216,26 @@ static void BarMainStartPlayback (BarApp_t *app) {  		 * thread has been started */  		app->player.mode = PLAYER_STARTING;  		/* start player */ -		pthread_create (&app->player.thread, NULL, BarPlayerThread, +		pthread_create (playerThread, NULL, BarPlayerThread,  				&app->player);  	}  }  /*	player is done, clean up   */ -static void BarMainPlayerCleanup (BarApp_t *app) { +static void BarMainPlayerCleanup (BarApp_t *app, pthread_t *playerThread) { +	void *threadRet; +  	BarUiStartEventCmd (&app->settings, "songfinish", app->curStation,  			app->playlist, &app->player, app->ph.stations, PIANO_RET_OK,  			WAITRESS_RET_OK);  	/* FIXME: pthread_join blocks everything if network connection  	 * is hung up e.g. */ -	pthread_join (app->player.thread, NULL); +	pthread_join (*playerThread, &threadRet);  	/* don't continue playback if thread reports error */ -	if (app->player.ret != PLAYER_RET_OK) { +	if (threadRet != (void *) PLAYER_RET_OK) {  		app->curStation = NULL;  	} @@ -263,6 +265,8 @@ static void BarMainPrintTime (BarApp_t *app) {  /*	main loop   */  static void BarMainLoop (BarApp_t *app) { +	pthread_t playerThread; +  	BarMainGetLoginCredentials (&app->settings, &app->input);  	BarMainLoadProxy (&app->settings, &app->waith); @@ -284,7 +288,7 @@ static void BarMainLoop (BarApp_t *app) {  	while (!app->doQuit) {  		/* song finished playing, clean up things/scrobble song */  		if (app->player.mode == PLAYER_FINISHED_PLAYBACK) { -			BarMainPlayerCleanup (app); +			BarMainPlayerCleanup (app, &playerThread);  		}  		/* check whether player finished playing and start playing new @@ -301,7 +305,7 @@ static void BarMainLoop (BarApp_t *app) {  			}  			/* song ready to play */  			if (app->playlist != NULL) { -				BarMainStartPlayback (app); +				BarMainStartPlayback (app, &playerThread);  			}  		} @@ -315,7 +319,7 @@ static void BarMainLoop (BarApp_t *app) {  	}  	if (app->player.mode != PLAYER_FREED) { -		BarMainPlayerCleanup (app); +		pthread_join (playerThread, NULL);  	}  } diff --git a/src/player.c b/src/player.c index b743ee8..733a315 100644 --- a/src/player.c +++ b/src/player.c @@ -1,5 +1,5 @@  /* -Copyright (c) 2008-2012 +Copyright (c) 2008-2011  	Lars-Dominik Braun <lars@6xq.net>  Permission is hereby granted, free of charge, to any person obtaining a copy @@ -23,17 +23,12 @@ THE SOFTWARE.  /* receive/play audio stream */ -#ifndef __FreeBSD__ -#define _POSIX_C_SOURCE 1 /* sigaction() */ -#endif -  #include <unistd.h>  #include <string.h>  #include <math.h>  #include <stdint.h>  #include <limits.h>  #include <arpa/inet.h> -#include <signal.h>  #include "player.h"  #include "config.h" @@ -42,6 +37,16 @@ THE SOFTWARE.  #define bigToHostEndian32(x) ntohl(x) +/* wait while locked, but don't slow down main thread by keeping + * locks too long */ +#define QUIT_PAUSE_CHECK \ +	pthread_mutex_lock (&player->pauseMutex); \ +	pthread_mutex_unlock (&player->pauseMutex); \ +	if (player->doQuit) { \ +		/* err => abort playback */ \ +		return WAITRESS_CB_RET_ERR; \ +	} +  /* pandora uses float values with 2 digits precision. Scale them by 100 to get   * a "nice" integer */  #define RG_SCALE_FACTOR 100 @@ -74,20 +79,6 @@ static inline signed short int applyReplayGain (const signed short int value,  	}  } -/*	handles bogus signal BAR_PLAYER_SIGCONT - */ -static void BarPlayerNullHandler (int sig) { -} - -/*	handler signal BAR_PLAYER_SIGSTOP and pauses player thread - */ -static void BarPlayerPauseHandler (int sig) { -	/* for a reason I don’t know sigsuspend does not work here, so we use -	 * pause, which should be fine as there are no other (expected) signals -	 * than SIGCONT */ -	pause (); -} -  /*	Refill player's buffer with dataSize of data   *	@param player structure   *	@param new data @@ -133,6 +124,8 @@ static WaitressCbReturn_t BarPlayerAACCb (void *ptr, size_t size,  	const char *data = ptr;  	struct audioPlayer *player = stream; +	QUIT_PAUSE_CHECK; +  	if (!BarPlayerBufferFill (player, data, size)) {  		return WAITRESS_CB_RET_ERR;  	} @@ -166,6 +159,9 @@ static WaitressCbReturn_t BarPlayerAACCb (void *ptr, size_t size,  					(unsigned long long int) player->channels;  			player->bufferRead += frameInfo.bytesconsumed;  			player->sampleSizeCurr++; +			/* going through this loop can take up to a few seconds => +			 * allow earlier thread abort */ +			QUIT_PAUSE_CHECK;  		}  	} else {  		if (player->mode == PLAYER_INITIALIZED) { @@ -209,7 +205,7 @@ static WaitressCbReturn_t BarPlayerAACCb (void *ptr, size_t size,  					if ((player->audioOutDevice = ao_open_live (audioOutDriver,  							&format, NULL)) == NULL) {  						/* we're not interested in the errno */ -						player->ret = PLAYER_RET_ERR; +						player->aoError = 1;  						BarUiMsg (player->settings, MSG_ERR,  								"Cannot open audio device\n");  						return WAITRESS_CB_RET_ERR; @@ -318,6 +314,8 @@ static WaitressCbReturn_t BarPlayerMp3Cb (void *ptr, size_t size,  	struct audioPlayer *player = stream;  	size_t i; +	QUIT_PAUSE_CHECK; +  	if (!BarPlayerBufferFill (player, data, size)) {  		return WAITRESS_CB_RET_ERR;  	} @@ -370,7 +368,7 @@ static WaitressCbReturn_t BarPlayerMp3Cb (void *ptr, size_t size,  			format.byte_format = AO_FMT_NATIVE;  			if ((player->audioOutDevice = ao_open_live (audioOutDriver,  					&format, NULL)) == NULL) { -				player->ret = PLAYER_RET_ERR; +				player->aoError = 1;  				BarUiMsg (player->settings, MSG_ERR,  						"Cannot open audio device\n");  				return WAITRESS_CB_RET_ERR; @@ -397,6 +395,8 @@ static WaitressCbReturn_t BarPlayerMp3Cb (void *ptr, size_t size,  					(unsigned long long int) BAR_PLAYER_MS_TO_S_FACTOR /  					(unsigned long long int) player->samplerate;  		} + +		QUIT_PAUSE_CHECK;  	} while (player->mp3Stream.error != MAD_ERROR_BUFLEN);  	player->bufferRead += player->mp3Stream.next_frame - player->buffer; @@ -407,42 +407,6 @@ static WaitressCbReturn_t BarPlayerMp3Cb (void *ptr, size_t size,  }  #endif /* ENABLE_MAD */ -/*	player cleanup function - * 	@param player structure - */ -static void BarPlayerCleanup (void *data) { -	struct audioPlayer *player = data; - -	switch (player->audioFormat) { -		#ifdef ENABLE_FAAD -		case PIANO_AF_AACPLUS_LO: -		case PIANO_AF_AACPLUS: -			NeAACDecClose(player->aacHandle); -			free (player->sampleSize); -			break; -		#endif /* ENABLE_FAAD */ - -		#ifdef ENABLE_MAD -		case PIANO_AF_MP3: -		case PIANO_AF_MP3_HI: -			mad_synth_finish (&player->mp3Synth); -			mad_frame_finish (&player->mp3Frame); -			mad_stream_finish (&player->mp3Stream); -			break; -		#endif /* ENABLE_MAD */ - -		default: -			/* this should never happen */ -			break; -	} - -	ao_close(player->audioOutDevice); -	WaitressFree (&player->waith); -	free (player->buffer); - -	player->mode = PLAYER_FINISHED_PLAYBACK; -} -  /*	player thread; for every song a new thread is started   *	@param audioPlayer structure   *	@return PLAYER_RET_* @@ -450,28 +414,18 @@ static void BarPlayerCleanup (void *data) {  void *BarPlayerThread (void *data) {  	struct audioPlayer *player = data;  	char extraHeaders[32]; +	void *ret = PLAYER_RET_OK;  	#ifdef ENABLE_FAAD  	NeAACDecConfigurationPtr conf;  	#endif  	WaitressReturn_t wRet = WAITRESS_RET_ERR; -	struct sigaction sa; - -	/* set up pause signals */ -	memset (&sa, 0, sizeof (sa)); -	sa.sa_handler = BarPlayerPauseHandler; -	sigaction (BAR_PLAYER_SIGSTOP, &sa, NULL); -	memset (&sa, 0, sizeof (sa)); -	sa.sa_handler = BarPlayerNullHandler; -	sigaction (BAR_PLAYER_SIGCONT, &sa, NULL); -	/* set up cleanup function */ -	pthread_cleanup_push (BarPlayerCleanup, data);  	/* init handles */ +	pthread_mutex_init (&player->pauseMutex, NULL);  	player->waith.data = (void *) player;  	/* extraHeaders will be initialized later */  	player->waith.extraHeaders = extraHeaders;  	player->buffer = malloc (BAR_PLAYER_BUFSIZE); -	player->ret = PLAYER_RET_OK;  	switch (player->audioFormat) {  		#ifdef ENABLE_FAAD @@ -500,8 +454,9 @@ void *BarPlayerThread (void *data) {  		#endif /* ENABLE_MAD */  		default: +			/* FIXME: leaks memory */  			BarUiMsg (player->settings, MSG_ERR, "Unsupported audio format!\n"); -			pthread_exit (PLAYER_RET_OK); +			return PLAYER_RET_OK;  			break;  	} @@ -516,7 +471,39 @@ void *BarPlayerThread (void *data) {  	} while (wRet == WAITRESS_RET_PARTIAL_FILE || wRet == WAITRESS_RET_TIMEOUT  			|| wRet == WAITRESS_RET_READ_ERR); -	/* cleanup */ -	pthread_cleanup_pop (!0); -	return NULL; +	switch (player->audioFormat) { +		#ifdef ENABLE_FAAD +		case PIANO_AF_AACPLUS_LO: +		case PIANO_AF_AACPLUS: +			NeAACDecClose(player->aacHandle); +			free (player->sampleSize); +			break; +		#endif /* ENABLE_FAAD */ + +		#ifdef ENABLE_MAD +		case PIANO_AF_MP3: +		case PIANO_AF_MP3_HI: +			mad_synth_finish (&player->mp3Synth); +			mad_frame_finish (&player->mp3Frame); +			mad_stream_finish (&player->mp3Stream); +			break; +		#endif /* ENABLE_MAD */ + +		default: +			/* this should never happen: thread is aborted above */ +			break; +	} + +	if (player->aoError) { +		ret = (void *) PLAYER_RET_ERR; +	} + +	ao_close(player->audioOutDevice); +	WaitressFree (&player->waith); +	pthread_mutex_destroy (&player->pauseMutex); +	free (player->buffer); + +	player->mode = PLAYER_FINISHED_PLAYBACK; + +	return ret;  } diff --git a/src/player.h b/src/player.h index 2261d44..ac6853c 100644 --- a/src/player.h +++ b/src/player.h @@ -47,11 +47,11 @@ THE SOFTWARE.  #define BAR_PLAYER_MS_TO_S_FACTOR 1000  #define BAR_PLAYER_BUFSIZE (WAITRESS_BUFFER_SIZE*2) -#define BAR_PLAYER_SIGSTOP SIGRTMIN -#define BAR_PLAYER_SIGCONT SIGRTMIN+1  struct audioPlayer { +	char doQuit;  	unsigned char channels; +	unsigned char aoError;  	enum {  		PLAYER_FREED = 0, /* thread is not running */ @@ -64,10 +64,6 @@ struct audioPlayer {  		PLAYER_RECV_DATA, /* playing track */  		PLAYER_FINISHED_PLAYBACK  	} mode; -	enum { -		PLAYER_RET_OK = 0, -		PLAYER_RET_ERR = 1, -	} ret;  	PianoAudioFormat_t audioFormat;  	unsigned int scale; @@ -105,11 +101,12 @@ struct audioPlayer {  	unsigned char *buffer; -	bool paused; -	pthread_t thread; +	pthread_mutex_t pauseMutex;  	WaitressHandle_t waith;  }; +enum {PLAYER_RET_OK = 0, PLAYER_RET_ERR = 1}; +  void *BarPlayerThread (void *data);  unsigned int BarPlayerCalcScale (float); diff --git a/src/ui_act.c b/src/ui_act.c index 2cc3197..0c9ed1b 100644 --- a/src/ui_act.c +++ b/src/ui_act.c @@ -23,14 +23,10 @@ THE SOFTWARE.  /* functions responding to user's keystrokes */ -#ifndef __FreeBSD__ -#define _POSIX_C_SOURCE 200112L /* pthread_kill() */ -#endif -  #include <string.h>  #include <unistd.h> +#include <pthread.h>  #include <assert.h> -#include <signal.h>  #include "ui.h"  #include "ui_readline.h" @@ -53,9 +49,10 @@ THE SOFTWARE.  static inline void BarUiDoSkipSong (struct audioPlayer *player) {  	assert (player != NULL); -	if (player->mode != PLAYER_FINISHED_PLAYBACK && player->mode != PLAYER_FREED) { -		pthread_cancel (player->thread); -	} +	player->doQuit = 1; +	/* unlocking an unlocked mutex is forbidden by some implementations */ +	pthread_mutex_trylock (&player->pauseMutex); +	pthread_mutex_unlock (&player->pauseMutex);  }  /*	transform station if necessary to allow changes like rename, rate, ... @@ -347,12 +344,9 @@ BarUiActCallback(BarUiActMoveSong) {  /*	pause   */  BarUiActCallback(BarUiActPause) { -	if (app->player.paused) { -		pthread_kill (app->player.thread, BAR_PLAYER_SIGCONT); -		app->player.paused = false; -	} else { -		pthread_kill (app->player.thread, BAR_PLAYER_SIGSTOP); -		app->player.paused = true; +	/* already locked => unlock/unpause */ +	if (pthread_mutex_trylock (&app->player.pauseMutex) == EBUSY) { +		pthread_mutex_unlock (&app->player.pauseMutex);  	}  } @@ -495,9 +489,8 @@ BarUiActCallback(BarUiActSelectQuickMix) {  /*	quit   */  BarUiActCallback(BarUiActQuit) { -	/* cancels player thread */ -	BarUiDoSkipSong (&app->player);  	app->doQuit = 1; +	BarUiDoSkipSong (&app->player);  }  /*	song history | 
