player_control: protect command, state, error with a mutex
Use GMutex/GCond instead of the notify library. Manually lock the player_control object before accessing the protected attributes. Use the GCond object to notify the player thread and the main thread.
This commit is contained in:
		@@ -61,7 +61,7 @@ void decoder_initialized(G_GNUC_UNUSED struct decoder * decoder,
 | 
			
		||||
	dc.state = DECODE_STATE_DECODE;
 | 
			
		||||
	decoder_unlock();
 | 
			
		||||
 | 
			
		||||
	notify_signal(&pc.notify);
 | 
			
		||||
	player_lock_signal();
 | 
			
		||||
 | 
			
		||||
	g_debug("audio_format=%u:%u:%u, seekable=%s",
 | 
			
		||||
		dc.in_audio_format.sample_rate, dc.in_audio_format.bits,
 | 
			
		||||
@@ -112,7 +112,7 @@ void decoder_command_finished(G_GNUC_UNUSED struct decoder * decoder)
 | 
			
		||||
	dc.command = DECODE_COMMAND_NONE;
 | 
			
		||||
	decoder_unlock();
 | 
			
		||||
 | 
			
		||||
	notify_signal(&pc.notify);
 | 
			
		||||
	player_lock_signal();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
double decoder_seek_where(G_GNUC_UNUSED struct decoder * decoder)
 | 
			
		||||
@@ -184,7 +184,7 @@ do_send_tag(struct decoder *decoder, struct input_stream *is,
 | 
			
		||||
		/* there is a partial chunk - flush it, we want the
 | 
			
		||||
		   tag in a new chunk */
 | 
			
		||||
		decoder_flush_chunk(decoder);
 | 
			
		||||
		notify_signal(&pc.notify);
 | 
			
		||||
		player_lock_signal();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	assert(decoder->chunk == NULL);
 | 
			
		||||
@@ -297,7 +297,7 @@ decoder_data(struct decoder *decoder,
 | 
			
		||||
		if (dest == NULL) {
 | 
			
		||||
			/* the chunk is full, flush it */
 | 
			
		||||
			decoder_flush_chunk(decoder);
 | 
			
		||||
			notify_signal(&pc.notify);
 | 
			
		||||
			player_lock_signal();
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -324,7 +324,7 @@ decoder_data(struct decoder *decoder,
 | 
			
		||||
		if (full) {
 | 
			
		||||
			/* the chunk is full, flush it */
 | 
			
		||||
			decoder_flush_chunk(decoder);
 | 
			
		||||
			notify_signal(&pc.notify);
 | 
			
		||||
			player_lock_signal();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		data += nbytes;
 | 
			
		||||
 
 | 
			
		||||
@@ -18,7 +18,7 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "decoder_control.h"
 | 
			
		||||
#include "notify.h"
 | 
			
		||||
#include "player_control.h"
 | 
			
		||||
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
 | 
			
		||||
@@ -40,38 +40,34 @@ void dc_deinit(void)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
dc_command_wait_locked(struct notify *notify)
 | 
			
		||||
dc_command_wait_locked(void)
 | 
			
		||||
{
 | 
			
		||||
	while (dc.command != DECODE_COMMAND_NONE) {
 | 
			
		||||
		decoder_signal();
 | 
			
		||||
		decoder_unlock();
 | 
			
		||||
 | 
			
		||||
		notify_wait(notify);
 | 
			
		||||
 | 
			
		||||
		decoder_lock();
 | 
			
		||||
		player_wait_decoder();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
dc_command_wait(struct notify *notify)
 | 
			
		||||
dc_command_wait(void)
 | 
			
		||||
{
 | 
			
		||||
	decoder_lock();
 | 
			
		||||
	dc_command_wait_locked(notify);
 | 
			
		||||
	dc_command_wait_locked();
 | 
			
		||||
	decoder_unlock();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
dc_command_locked(struct notify *notify, enum decoder_command cmd)
 | 
			
		||||
dc_command_locked(enum decoder_command cmd)
 | 
			
		||||
{
 | 
			
		||||
	dc.command = cmd;
 | 
			
		||||
	dc_command_wait_locked(notify);
 | 
			
		||||
	dc_command_wait_locked();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
dc_command(struct notify *notify, enum decoder_command cmd)
 | 
			
		||||
dc_command(enum decoder_command cmd)
 | 
			
		||||
{
 | 
			
		||||
	decoder_lock();
 | 
			
		||||
	dc_command_locked(notify, cmd);
 | 
			
		||||
	dc_command_locked(cmd);
 | 
			
		||||
	decoder_unlock();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -86,13 +82,13 @@ static void dc_command_async(enum decoder_command cmd)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
dc_start(struct notify *notify, struct song *song)
 | 
			
		||||
dc_start(struct song *song)
 | 
			
		||||
{
 | 
			
		||||
	assert(dc.pipe != NULL);
 | 
			
		||||
	assert(song != NULL);
 | 
			
		||||
 | 
			
		||||
	dc.next_song = song;
 | 
			
		||||
	dc_command(notify, DECODE_COMMAND_START);
 | 
			
		||||
	dc_command(DECODE_COMMAND_START);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
@@ -106,7 +102,7 @@ dc_start_async(struct song *song)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
dc_stop(struct notify *notify)
 | 
			
		||||
dc_stop(void)
 | 
			
		||||
{
 | 
			
		||||
	decoder_lock();
 | 
			
		||||
 | 
			
		||||
@@ -115,16 +111,16 @@ dc_stop(struct notify *notify)
 | 
			
		||||
		   late and the decoder thread is already executing
 | 
			
		||||
		   the old command, we'll call STOP again in this
 | 
			
		||||
		   function (see below). */
 | 
			
		||||
		dc_command_locked(notify, DECODE_COMMAND_STOP);
 | 
			
		||||
		dc_command_locked(DECODE_COMMAND_STOP);
 | 
			
		||||
 | 
			
		||||
	if (dc.state != DECODE_STATE_STOP && dc.state != DECODE_STATE_ERROR)
 | 
			
		||||
		dc_command_locked(notify, DECODE_COMMAND_STOP);
 | 
			
		||||
		dc_command_locked(DECODE_COMMAND_STOP);
 | 
			
		||||
 | 
			
		||||
	decoder_unlock();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool
 | 
			
		||||
dc_seek(struct notify *notify, double where)
 | 
			
		||||
dc_seek(double where)
 | 
			
		||||
{
 | 
			
		||||
	assert(dc.state != DECODE_STATE_START);
 | 
			
		||||
	assert(where >= 0.0);
 | 
			
		||||
@@ -135,7 +131,7 @@ dc_seek(struct notify *notify, double where)
 | 
			
		||||
 | 
			
		||||
	dc.seek_where = where;
 | 
			
		||||
	dc.seek_error = false;
 | 
			
		||||
	dc_command(notify, DECODE_COMMAND_SEEK);
 | 
			
		||||
	dc_command(DECODE_COMMAND_SEEK);
 | 
			
		||||
 | 
			
		||||
	if (dc.seek_error)
 | 
			
		||||
		return false;
 | 
			
		||||
 
 | 
			
		||||
@@ -30,8 +30,6 @@
 | 
			
		||||
#define DECODE_TYPE_FILE	0
 | 
			
		||||
#define DECODE_TYPE_URL		1
 | 
			
		||||
 | 
			
		||||
struct notify;
 | 
			
		||||
 | 
			
		||||
enum decoder_state {
 | 
			
		||||
	DECODE_STATE_STOP = 0,
 | 
			
		||||
	DECODE_STATE_START,
 | 
			
		||||
@@ -205,19 +203,19 @@ decoder_current_song(void)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
dc_command_wait(struct notify *notify);
 | 
			
		||||
dc_command_wait(void);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
dc_start(struct notify *notify, struct song *song);
 | 
			
		||||
dc_start(struct song *song);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
dc_start_async(struct song *song);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
dc_stop(struct notify *notify);
 | 
			
		||||
dc_stop(void);
 | 
			
		||||
 | 
			
		||||
bool
 | 
			
		||||
dc_seek(struct notify *notify, double where);
 | 
			
		||||
dc_seek(double where);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
dc_quit(void);
 | 
			
		||||
 
 | 
			
		||||
@@ -58,10 +58,7 @@ need_chunks(struct input_stream *is, bool do_wait)
 | 
			
		||||
 | 
			
		||||
	if ((is == NULL || decoder_input_buffer(is) <= 0) && do_wait) {
 | 
			
		||||
		decoder_wait();
 | 
			
		||||
 | 
			
		||||
		decoder_unlock();
 | 
			
		||||
		notify_signal(&pc.notify);
 | 
			
		||||
		decoder_lock();
 | 
			
		||||
		player_signal();
 | 
			
		||||
 | 
			
		||||
		return dc.command;
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -112,9 +112,7 @@ static void decoder_run_song(const struct song *song, const char *uri)
 | 
			
		||||
	dc.state = DECODE_STATE_START;
 | 
			
		||||
	dc.command = DECODE_COMMAND_NONE;
 | 
			
		||||
 | 
			
		||||
	decoder_unlock();
 | 
			
		||||
	notify_signal(&pc.notify);
 | 
			
		||||
	decoder_lock();
 | 
			
		||||
	player_signal();
 | 
			
		||||
 | 
			
		||||
	/* wait for the input stream to become ready; its metadata
 | 
			
		||||
	   will be available then */
 | 
			
		||||
@@ -294,17 +292,13 @@ static gpointer decoder_task(G_GNUC_UNUSED gpointer arg)
 | 
			
		||||
 | 
			
		||||
			dc.command = DECODE_COMMAND_NONE;
 | 
			
		||||
 | 
			
		||||
			decoder_unlock();
 | 
			
		||||
			notify_signal(&pc.notify);
 | 
			
		||||
			decoder_lock();
 | 
			
		||||
			player_signal();
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		case DECODE_COMMAND_STOP:
 | 
			
		||||
			dc.command = DECODE_COMMAND_NONE;
 | 
			
		||||
 | 
			
		||||
			decoder_unlock();
 | 
			
		||||
			notify_signal(&pc.notify);
 | 
			
		||||
			decoder_lock();
 | 
			
		||||
			player_signal();
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		case DECODE_COMMAND_NONE:
 | 
			
		||||
 
 | 
			
		||||
@@ -93,7 +93,7 @@ enum {
 | 
			
		||||
GThread *main_task;
 | 
			
		||||
GMainLoop *main_loop;
 | 
			
		||||
 | 
			
		||||
struct notify main_notify;
 | 
			
		||||
GCond *main_cond;
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
glue_daemonize_init(const struct options *options)
 | 
			
		||||
@@ -321,7 +321,7 @@ int main(int argc, char *argv[])
 | 
			
		||||
 | 
			
		||||
	main_task = g_thread_self();
 | 
			
		||||
	main_loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
	notify_init(&main_notify);
 | 
			
		||||
	main_cond = g_cond_new();
 | 
			
		||||
 | 
			
		||||
	event_pipe_init();
 | 
			
		||||
	event_pipe_register(PIPE_EVENT_IDLE, idle_event_emitted);
 | 
			
		||||
@@ -415,7 +415,7 @@ int main(int argc, char *argv[])
 | 
			
		||||
	sticker_global_finish();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	notify_deinit(&main_notify);
 | 
			
		||||
	g_cond_free(main_cond);
 | 
			
		||||
	event_pipe_deinit();
 | 
			
		||||
 | 
			
		||||
	playlist_list_global_finish();
 | 
			
		||||
 
 | 
			
		||||
@@ -26,6 +26,6 @@ extern GThread *main_task;
 | 
			
		||||
 | 
			
		||||
extern GMainLoop *main_loop;
 | 
			
		||||
 | 
			
		||||
extern struct notify main_notify;
 | 
			
		||||
extern GCond *main_cond;
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -445,10 +445,15 @@ audio_output_all_check(void)
 | 
			
		||||
bool
 | 
			
		||||
audio_output_all_wait(unsigned threshold)
 | 
			
		||||
{
 | 
			
		||||
	if (audio_output_all_check() < threshold)
 | 
			
		||||
		return true;
 | 
			
		||||
	player_lock();
 | 
			
		||||
 | 
			
		||||
	notify_wait(&pc.notify);
 | 
			
		||||
	if (audio_output_all_check() < threshold) {
 | 
			
		||||
		player_unlock();
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	player_wait();
 | 
			
		||||
	player_unlock();
 | 
			
		||||
 | 
			
		||||
	return audio_output_all_check() < threshold;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -359,7 +359,7 @@ ao_play(struct audio_output *ao)
 | 
			
		||||
	ao->chunk_finished = true;
 | 
			
		||||
 | 
			
		||||
	g_mutex_unlock(ao->mutex);
 | 
			
		||||
	notify_signal(&pc.notify);
 | 
			
		||||
	player_lock_signal();
 | 
			
		||||
	g_mutex_lock(ao->mutex);
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,7 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "player_control.h"
 | 
			
		||||
#include "decoder_control.h"
 | 
			
		||||
#include "path.h"
 | 
			
		||||
#include "log.h"
 | 
			
		||||
#include "tag.h"
 | 
			
		||||
@@ -35,7 +36,10 @@ void pc_init(unsigned buffer_chunks, unsigned int buffered_before_play)
 | 
			
		||||
{
 | 
			
		||||
	pc.buffer_chunks = buffer_chunks;
 | 
			
		||||
	pc.buffered_before_play = buffered_before_play;
 | 
			
		||||
	notify_init(&pc.notify);
 | 
			
		||||
 | 
			
		||||
	pc.mutex = g_mutex_new();
 | 
			
		||||
	pc.cond = g_cond_new();
 | 
			
		||||
 | 
			
		||||
	pc.command = PLAYER_COMMAND_NONE;
 | 
			
		||||
	pc.error = PLAYER_ERROR_NOERROR;
 | 
			
		||||
	pc.state = PLAYER_STATE_STOP;
 | 
			
		||||
@@ -44,7 +48,16 @@ void pc_init(unsigned buffer_chunks, unsigned int buffered_before_play)
 | 
			
		||||
 | 
			
		||||
void pc_deinit(void)
 | 
			
		||||
{
 | 
			
		||||
	notify_deinit(&pc.notify);
 | 
			
		||||
	g_cond_free(pc.cond);
 | 
			
		||||
	g_mutex_free(pc.mutex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
player_wait_decoder(void)
 | 
			
		||||
{
 | 
			
		||||
	/* during this function, the decoder lock is held, because
 | 
			
		||||
	   we're waiting for the decoder thread */
 | 
			
		||||
	g_cond_wait(pc.cond, dc.mutex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
@@ -56,15 +69,30 @@ pc_song_deleted(const struct song *song)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void player_command(enum player_command cmd)
 | 
			
		||||
static void
 | 
			
		||||
player_command_wait_locked(void)
 | 
			
		||||
{
 | 
			
		||||
	while (pc.command != PLAYER_COMMAND_NONE) {
 | 
			
		||||
		player_signal();
 | 
			
		||||
		g_cond_wait(main_cond, pc.mutex);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
player_command_locked(enum player_command cmd)
 | 
			
		||||
{
 | 
			
		||||
	assert(pc.command == PLAYER_COMMAND_NONE);
 | 
			
		||||
 | 
			
		||||
	pc.command = cmd;
 | 
			
		||||
	while (pc.command != PLAYER_COMMAND_NONE) {
 | 
			
		||||
		notify_signal(&pc.notify);
 | 
			
		||||
		notify_wait(&main_notify);
 | 
			
		||||
	}
 | 
			
		||||
	player_command_wait_locked();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
player_command(enum player_command cmd)
 | 
			
		||||
{
 | 
			
		||||
	player_lock();
 | 
			
		||||
	player_command_locked(cmd);
 | 
			
		||||
	player_unlock();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
 
 | 
			
		||||
@@ -88,10 +88,19 @@ struct player_control {
 | 
			
		||||
	    thread isn't running */
 | 
			
		||||
	GThread *thread;
 | 
			
		||||
 | 
			
		||||
	struct notify notify;
 | 
			
		||||
	volatile enum player_command command;
 | 
			
		||||
	volatile enum player_state state;
 | 
			
		||||
	volatile enum player_error error;
 | 
			
		||||
	/**
 | 
			
		||||
	 * This lock protects #command, #state, #error.
 | 
			
		||||
	 */
 | 
			
		||||
	GMutex *mutex;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Trigger this object after you have modified #command.
 | 
			
		||||
	 */
 | 
			
		||||
	GCond *cond;
 | 
			
		||||
 | 
			
		||||
	enum player_command command;
 | 
			
		||||
	enum player_state state;
 | 
			
		||||
	enum player_error error;
 | 
			
		||||
	uint16_t bit_rate;
 | 
			
		||||
	struct audio_format audio_format;
 | 
			
		||||
	float total_time;
 | 
			
		||||
@@ -109,6 +118,67 @@ void pc_init(unsigned buffer_chunks, unsigned buffered_before_play);
 | 
			
		||||
 | 
			
		||||
void pc_deinit(void);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Locks the #player_control object.
 | 
			
		||||
 */
 | 
			
		||||
static inline void
 | 
			
		||||
player_lock(void)
 | 
			
		||||
{
 | 
			
		||||
	g_mutex_lock(pc.mutex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Unlocks the #player_control object.
 | 
			
		||||
 */
 | 
			
		||||
static inline void
 | 
			
		||||
player_unlock(void)
 | 
			
		||||
{
 | 
			
		||||
	g_mutex_unlock(pc.mutex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Waits for a signal on the #player_control object.  This function is
 | 
			
		||||
 * only valid in the player thread.  The object must be locked prior
 | 
			
		||||
 * to calling this function.
 | 
			
		||||
 */
 | 
			
		||||
static inline void
 | 
			
		||||
player_wait(void)
 | 
			
		||||
{
 | 
			
		||||
	g_cond_wait(pc.cond, pc.mutex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Waits for a signal on the #player_control object.  This function is
 | 
			
		||||
 * only valid in the player thread.  The #decoder_control object must
 | 
			
		||||
 * be locked prior to calling this function.
 | 
			
		||||
 *
 | 
			
		||||
 * Note the small difference to the player_wait() function!
 | 
			
		||||
 */
 | 
			
		||||
void
 | 
			
		||||
player_wait_decoder(void);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Signals the #player_control object.  The object should be locked
 | 
			
		||||
 * prior to calling this function.
 | 
			
		||||
 */
 | 
			
		||||
static inline void
 | 
			
		||||
player_signal(void)
 | 
			
		||||
{
 | 
			
		||||
	g_cond_signal(pc.cond);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Signals the #player_control object.  The object is temporarily
 | 
			
		||||
 * locked by this function.
 | 
			
		||||
 */
 | 
			
		||||
static inline void
 | 
			
		||||
player_lock_signal(void)
 | 
			
		||||
{
 | 
			
		||||
	player_lock();
 | 
			
		||||
	player_signal();
 | 
			
		||||
	player_unlock();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Call this function when the specified song pointer is about to be
 | 
			
		||||
 * invalidated.  This makes sure that player_control.errored_song does
 | 
			
		||||
 
 | 
			
		||||
@@ -106,21 +106,30 @@ struct player {
 | 
			
		||||
 | 
			
		||||
static struct music_buffer *player_buffer;
 | 
			
		||||
 | 
			
		||||
static void player_command_finished(void)
 | 
			
		||||
static void player_command_finished_locked(void)
 | 
			
		||||
{
 | 
			
		||||
	assert(pc.command != PLAYER_COMMAND_NONE);
 | 
			
		||||
 | 
			
		||||
	pc.command = PLAYER_COMMAND_NONE;
 | 
			
		||||
	notify_signal(&main_notify);
 | 
			
		||||
	g_cond_signal(main_cond);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void player_command_finished(void)
 | 
			
		||||
{
 | 
			
		||||
	player_lock();
 | 
			
		||||
	player_command_finished_locked();
 | 
			
		||||
	player_unlock();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Stop the decoder and clears (and frees) its music pipe.
 | 
			
		||||
 *
 | 
			
		||||
 * Player lock is not held.
 | 
			
		||||
 */
 | 
			
		||||
static void
 | 
			
		||||
player_dc_stop(struct player *player)
 | 
			
		||||
{
 | 
			
		||||
	dc_stop(&pc.notify);
 | 
			
		||||
	dc_stop();
 | 
			
		||||
 | 
			
		||||
	if (dc.pipe != NULL) {
 | 
			
		||||
		/* clear and free the decoder pipe */
 | 
			
		||||
@@ -138,17 +147,23 @@ player_dc_stop(struct player *player)
 | 
			
		||||
 * After the decoder has been started asynchronously, wait for the
 | 
			
		||||
 * "START" command to finish.  The decoder may not be initialized yet,
 | 
			
		||||
 * i.e. there is no audio_format information yet.
 | 
			
		||||
 *
 | 
			
		||||
 * The player lock is not held.
 | 
			
		||||
 */
 | 
			
		||||
static bool
 | 
			
		||||
player_wait_for_decoder(struct player *player)
 | 
			
		||||
{
 | 
			
		||||
	dc_command_wait(&pc.notify);
 | 
			
		||||
	dc_command_wait();
 | 
			
		||||
 | 
			
		||||
	if (decoder_lock_has_failed()) {
 | 
			
		||||
		assert(dc.next_song == NULL || dc.next_song->uri != NULL);
 | 
			
		||||
 | 
			
		||||
		player_lock();
 | 
			
		||||
		pc.errored_song = dc.next_song;
 | 
			
		||||
		pc.error = PLAYER_ERROR_FILE;
 | 
			
		||||
		pc.next_song = NULL;
 | 
			
		||||
		player_unlock();
 | 
			
		||||
 | 
			
		||||
		player->queued = false;
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
@@ -177,6 +192,8 @@ player_wait_for_decoder(struct player *player)
 | 
			
		||||
 * The decoder has acknowledged the "START" command (see
 | 
			
		||||
 * player_wait_for_decoder()).  This function checks if the decoder
 | 
			
		||||
 * initialization has completed yet.
 | 
			
		||||
 *
 | 
			
		||||
 * The player lock is not held.
 | 
			
		||||
 */
 | 
			
		||||
static bool
 | 
			
		||||
player_check_decoder_startup(struct player *player)
 | 
			
		||||
@@ -234,8 +251,8 @@ player_check_decoder_startup(struct player *player)
 | 
			
		||||
	} else {
 | 
			
		||||
		/* the decoder is not yet ready; wait
 | 
			
		||||
		   some more */
 | 
			
		||||
		player_wait_decoder();
 | 
			
		||||
		decoder_unlock();
 | 
			
		||||
		notify_wait(&pc.notify);
 | 
			
		||||
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
@@ -245,6 +262,8 @@ player_check_decoder_startup(struct player *player)
 | 
			
		||||
 * Sends a chunk of silence to the audio outputs.  This is called when
 | 
			
		||||
 * there is not enough decoded data in the pipe yet, to prevent
 | 
			
		||||
 * underruns in the hardware buffers.
 | 
			
		||||
 *
 | 
			
		||||
 * The player lock is not held.
 | 
			
		||||
 */
 | 
			
		||||
static bool
 | 
			
		||||
player_send_silence(struct player *player)
 | 
			
		||||
@@ -281,6 +300,8 @@ player_send_silence(struct player *player)
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This is the handler for the #PLAYER_COMMAND_SEEK command.
 | 
			
		||||
 *
 | 
			
		||||
 * The player lock is not held.
 | 
			
		||||
 */
 | 
			
		||||
static bool player_seek_decoder(struct player *player)
 | 
			
		||||
{
 | 
			
		||||
@@ -332,7 +353,7 @@ static bool player_seek_decoder(struct player *player)
 | 
			
		||||
	if (where < 0.0)
 | 
			
		||||
		where = 0.0;
 | 
			
		||||
 | 
			
		||||
	ret = dc_seek(&pc.notify, where);
 | 
			
		||||
	ret = dc_seek(where);
 | 
			
		||||
	if (!ret) {
 | 
			
		||||
		/* decoder failure */
 | 
			
		||||
		player_command_finished();
 | 
			
		||||
@@ -353,6 +374,9 @@ static bool player_seek_decoder(struct player *player)
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Player lock must be held before calling.
 | 
			
		||||
 */
 | 
			
		||||
static void player_process_command(struct player *player)
 | 
			
		||||
{
 | 
			
		||||
	switch (pc.command) {
 | 
			
		||||
@@ -363,8 +387,10 @@ static void player_process_command(struct player *player)
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case PLAYER_COMMAND_UPDATE_AUDIO:
 | 
			
		||||
		player_unlock();
 | 
			
		||||
		audio_output_all_enable_disable();
 | 
			
		||||
		player_command_finished();
 | 
			
		||||
		player_lock();
 | 
			
		||||
		player_command_finished_locked();
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case PLAYER_COMMAND_QUEUE:
 | 
			
		||||
@@ -373,21 +399,28 @@ static void player_process_command(struct player *player)
 | 
			
		||||
		assert(dc.pipe == NULL || dc.pipe == player->pipe);
 | 
			
		||||
 | 
			
		||||
		player->queued = true;
 | 
			
		||||
		player_command_finished();
 | 
			
		||||
		player_command_finished_locked();
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case PLAYER_COMMAND_PAUSE:
 | 
			
		||||
		player_unlock();
 | 
			
		||||
 | 
			
		||||
		player->paused = !player->paused;
 | 
			
		||||
		if (player->paused) {
 | 
			
		||||
			audio_output_all_pause();
 | 
			
		||||
			player_lock();
 | 
			
		||||
 | 
			
		||||
			pc.state = PLAYER_STATE_PAUSE;
 | 
			
		||||
		} else if (!audio_format_defined(&player->play_audio_format)) {
 | 
			
		||||
			/* the decoder hasn't provided an audio format
 | 
			
		||||
			   yet - don't open the audio device yet */
 | 
			
		||||
			player_lock();
 | 
			
		||||
 | 
			
		||||
			pc.state = PLAYER_STATE_PLAY;
 | 
			
		||||
		} else if (audio_output_all_open(&player->play_audio_format, player_buffer)) {
 | 
			
		||||
			/* unpaused, continue playing */
 | 
			
		||||
			player_lock();
 | 
			
		||||
 | 
			
		||||
			pc.state = PLAYER_STATE_PLAY;
 | 
			
		||||
		} else {
 | 
			
		||||
			/* the audio device has failed - rollback to
 | 
			
		||||
@@ -397,13 +430,17 @@ static void player_process_command(struct player *player)
 | 
			
		||||
			pc.error = PLAYER_ERROR_AUDIO;
 | 
			
		||||
 | 
			
		||||
			player->paused = true;
 | 
			
		||||
 | 
			
		||||
			player_lock();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		player_command_finished();
 | 
			
		||||
		player_command_finished_locked();
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case PLAYER_COMMAND_SEEK:
 | 
			
		||||
		player_unlock();
 | 
			
		||||
		player_seek_decoder(player);
 | 
			
		||||
		player_lock();
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case PLAYER_COMMAND_CANCEL:
 | 
			
		||||
@@ -415,26 +452,32 @@ static void player_process_command(struct player *player)
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (dc.pipe != NULL && dc.pipe != player->pipe)
 | 
			
		||||
		if (dc.pipe != NULL && dc.pipe != player->pipe) {
 | 
			
		||||
			/* the decoder is already decoding the song -
 | 
			
		||||
			   stop it and reset the position */
 | 
			
		||||
			player_unlock();
 | 
			
		||||
			player_dc_stop(player);
 | 
			
		||||
			player_lock();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		pc.next_song = NULL;
 | 
			
		||||
		player->queued = false;
 | 
			
		||||
		player_command_finished();
 | 
			
		||||
		player_command_finished_locked();
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case PLAYER_COMMAND_REFRESH:
 | 
			
		||||
		if (audio_format_defined(&player->play_audio_format) &&
 | 
			
		||||
		    !player->paused)
 | 
			
		||||
		    !player->paused) {
 | 
			
		||||
			player_unlock();
 | 
			
		||||
			audio_output_all_check();
 | 
			
		||||
			player_lock();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		pc.elapsed_time = audio_output_all_get_elapsed_time();
 | 
			
		||||
		if (pc.elapsed_time < 0.0)
 | 
			
		||||
			pc.elapsed_time = player->elapsed_time;
 | 
			
		||||
 | 
			
		||||
		player_command_finished();
 | 
			
		||||
		player_command_finished_locked();
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -468,6 +511,8 @@ update_song_tag(struct song *song, const struct tag *new_tag)
 | 
			
		||||
 * Plays a #music_chunk object (after applying software volume).  If
 | 
			
		||||
 * it contains a (stream) tag, copy it to the current song, so MPD's
 | 
			
		||||
 * playlist reflects the new stream tag.
 | 
			
		||||
 *
 | 
			
		||||
 * Player lock is not held.
 | 
			
		||||
 */
 | 
			
		||||
static bool
 | 
			
		||||
play_chunk(struct song *song, struct music_chunk *chunk,
 | 
			
		||||
@@ -553,10 +598,9 @@ play_next_chunk(struct player *player)
 | 
			
		||||
			} else {
 | 
			
		||||
				/* wait for the decoder */
 | 
			
		||||
				decoder_signal();
 | 
			
		||||
				player_wait_decoder();
 | 
			
		||||
				decoder_unlock();
 | 
			
		||||
 | 
			
		||||
				notify_wait(&pc.notify);
 | 
			
		||||
 | 
			
		||||
				return true;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
@@ -574,6 +618,8 @@ play_next_chunk(struct player *player)
 | 
			
		||||
	if (!success) {
 | 
			
		||||
		music_buffer_return(player_buffer, chunk);
 | 
			
		||||
 | 
			
		||||
		player_lock();
 | 
			
		||||
 | 
			
		||||
		pc.error = PLAYER_ERROR_AUDIO;
 | 
			
		||||
 | 
			
		||||
		/* pause: the user may resume playback as soon as an
 | 
			
		||||
@@ -581,6 +627,8 @@ play_next_chunk(struct player *player)
 | 
			
		||||
		pc.state = PLAYER_STATE_PAUSE;
 | 
			
		||||
		player->paused = true;
 | 
			
		||||
 | 
			
		||||
		player_unlock();
 | 
			
		||||
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -602,6 +650,8 @@ play_next_chunk(struct player *player)
 | 
			
		||||
 * has consumed all chunks of the current song, and we should start
 | 
			
		||||
 * sending chunks from the next one.
 | 
			
		||||
 *
 | 
			
		||||
 * The player lock is not held.
 | 
			
		||||
 *
 | 
			
		||||
 * @return true on success, false on error (playback will be stopped)
 | 
			
		||||
 */
 | 
			
		||||
static bool
 | 
			
		||||
@@ -643,31 +693,38 @@ static void do_play(void)
 | 
			
		||||
		.elapsed_time = 0.0,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	player_unlock();
 | 
			
		||||
 | 
			
		||||
	player.pipe = music_pipe_new();
 | 
			
		||||
 | 
			
		||||
	dc.buffer = player_buffer;
 | 
			
		||||
	dc.pipe = player.pipe;
 | 
			
		||||
	dc_start(&pc.notify, pc.next_song);
 | 
			
		||||
	dc_start(pc.next_song);
 | 
			
		||||
	if (!player_wait_for_decoder(&player)) {
 | 
			
		||||
		player_dc_stop(&player);
 | 
			
		||||
		player_command_finished();
 | 
			
		||||
		music_pipe_free(player.pipe);
 | 
			
		||||
		event_pipe_emit(PIPE_EVENT_PLAYLIST);
 | 
			
		||||
		player_lock();
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	player_lock();
 | 
			
		||||
	pc.state = PLAYER_STATE_PLAY;
 | 
			
		||||
	player_command_finished();
 | 
			
		||||
	player_command_finished_locked();
 | 
			
		||||
 | 
			
		||||
	while (true) {
 | 
			
		||||
		player_process_command(&player);
 | 
			
		||||
		if (pc.command == PLAYER_COMMAND_STOP ||
 | 
			
		||||
		    pc.command == PLAYER_COMMAND_EXIT ||
 | 
			
		||||
		    pc.command == PLAYER_COMMAND_CLOSE_AUDIO) {
 | 
			
		||||
			player_unlock();
 | 
			
		||||
			audio_output_all_cancel();
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		player_unlock();
 | 
			
		||||
 | 
			
		||||
		if (player.buffering) {
 | 
			
		||||
			/* buffering at the start of the song - wait
 | 
			
		||||
			   until the buffer is large enough, to
 | 
			
		||||
@@ -683,7 +740,10 @@ static void do_play(void)
 | 
			
		||||
				    !player_send_silence(&player))
 | 
			
		||||
					break;
 | 
			
		||||
 | 
			
		||||
				notify_wait(&pc.notify);
 | 
			
		||||
				decoder_lock();
 | 
			
		||||
				/* XXX race condition: check decoder again */
 | 
			
		||||
				player_wait_decoder();
 | 
			
		||||
				decoder_unlock();
 | 
			
		||||
				continue;
 | 
			
		||||
			} else {
 | 
			
		||||
				/* buffering is complete */
 | 
			
		||||
@@ -698,6 +758,8 @@ static void do_play(void)
 | 
			
		||||
			success = player_check_decoder_startup(&player);
 | 
			
		||||
			if (!success)
 | 
			
		||||
				break;
 | 
			
		||||
 | 
			
		||||
			player_lock();
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -741,9 +803,11 @@ static void do_play(void)
 | 
			
		||||
				player.xfade = XFADE_DISABLED;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (player.paused)
 | 
			
		||||
			notify_wait(&pc.notify);
 | 
			
		||||
		else if (music_pipe_size(player.pipe) > 0) {
 | 
			
		||||
		if (player.paused) {
 | 
			
		||||
			player_lock();
 | 
			
		||||
			player_wait();
 | 
			
		||||
			continue;
 | 
			
		||||
		} else if (music_pipe_size(player.pipe) > 0) {
 | 
			
		||||
			/* at least one music chunk is ready - send it
 | 
			
		||||
			   to the audio output */
 | 
			
		||||
 | 
			
		||||
@@ -773,6 +837,8 @@ static void do_play(void)
 | 
			
		||||
			if (!player_send_silence(&player))
 | 
			
		||||
				break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		player_lock();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	player_dc_stop(&player);
 | 
			
		||||
@@ -783,8 +849,13 @@ static void do_play(void)
 | 
			
		||||
	music_pipe_clear(player.pipe, player_buffer);
 | 
			
		||||
	music_pipe_free(player.pipe);
 | 
			
		||||
 | 
			
		||||
	player_lock();
 | 
			
		||||
	pc.state = PLAYER_STATE_STOP;
 | 
			
		||||
	player_unlock();
 | 
			
		||||
 | 
			
		||||
	event_pipe_emit(PIPE_EVENT_PLAYLIST);
 | 
			
		||||
 | 
			
		||||
	player_lock();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gpointer player_task(G_GNUC_UNUSED gpointer arg)
 | 
			
		||||
@@ -793,6 +864,8 @@ static gpointer player_task(G_GNUC_UNUSED gpointer arg)
 | 
			
		||||
 | 
			
		||||
	player_buffer = music_buffer_new(pc.buffer_chunks);
 | 
			
		||||
 | 
			
		||||
	player_lock();
 | 
			
		||||
 | 
			
		||||
	while (1) {
 | 
			
		||||
		switch (pc.command) {
 | 
			
		||||
		case PLAYER_COMMAND_QUEUE:
 | 
			
		||||
@@ -802,18 +875,25 @@ static gpointer player_task(G_GNUC_UNUSED gpointer arg)
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		case PLAYER_COMMAND_STOP:
 | 
			
		||||
			player_unlock();
 | 
			
		||||
			audio_output_all_cancel();
 | 
			
		||||
			player_lock();
 | 
			
		||||
 | 
			
		||||
			/* fall through */
 | 
			
		||||
 | 
			
		||||
		case PLAYER_COMMAND_SEEK:
 | 
			
		||||
		case PLAYER_COMMAND_PAUSE:
 | 
			
		||||
			pc.next_song = NULL;
 | 
			
		||||
			player_command_finished();
 | 
			
		||||
			player_command_finished_locked();
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		case PLAYER_COMMAND_CLOSE_AUDIO:
 | 
			
		||||
			player_unlock();
 | 
			
		||||
 | 
			
		||||
			audio_output_all_close();
 | 
			
		||||
			player_command_finished();
 | 
			
		||||
 | 
			
		||||
			player_lock();
 | 
			
		||||
			player_command_finished_locked();
 | 
			
		||||
 | 
			
		||||
#ifndef NDEBUG
 | 
			
		||||
			/* in the DEBUG build, check for leaked
 | 
			
		||||
@@ -826,33 +906,39 @@ static gpointer player_task(G_GNUC_UNUSED gpointer arg)
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		case PLAYER_COMMAND_UPDATE_AUDIO:
 | 
			
		||||
			player_unlock();
 | 
			
		||||
			audio_output_all_enable_disable();
 | 
			
		||||
			player_command_finished();
 | 
			
		||||
			player_lock();
 | 
			
		||||
			player_command_finished_locked();
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		case PLAYER_COMMAND_EXIT:
 | 
			
		||||
			player_unlock();
 | 
			
		||||
 | 
			
		||||
			dc_quit();
 | 
			
		||||
			audio_output_all_close();
 | 
			
		||||
			music_buffer_free(player_buffer);
 | 
			
		||||
 | 
			
		||||
			player_command_finished();
 | 
			
		||||
			g_thread_exit(NULL);
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		case PLAYER_COMMAND_CANCEL:
 | 
			
		||||
			pc.next_song = NULL;
 | 
			
		||||
			player_command_finished();
 | 
			
		||||
			player_command_finished_locked();
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		case PLAYER_COMMAND_REFRESH:
 | 
			
		||||
			/* no-op when not playing */
 | 
			
		||||
			player_command_finished();
 | 
			
		||||
			player_command_finished_locked();
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		case PLAYER_COMMAND_NONE:
 | 
			
		||||
			notify_wait(&pc.notify);
 | 
			
		||||
			player_wait();
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user