aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/decoder_api.c2
-rw-r--r--src/decoder_control.c20
-rw-r--r--src/decoder_control.h38
-rw-r--r--src/decoder_thread.c24
-rw-r--r--src/player_control.c3
-rw-r--r--src/player_control.h2
-rw-r--r--src/player_thread.c108
7 files changed, 122 insertions, 75 deletions
diff --git a/src/decoder_api.c b/src/decoder_api.c
index 7681b7b85..2350396a9 100644
--- a/src/decoder_api.c
+++ b/src/decoder_api.c
@@ -86,7 +86,7 @@ char *decoder_get_uri(G_GNUC_UNUSED struct decoder *decoder)
assert(dc->pipe != NULL);
- return song_get_uri(dc->current_song);
+ return song_get_uri(dc->song);
}
enum decoder_command decoder_get_command(G_GNUC_UNUSED struct decoder * decoder)
diff --git a/src/decoder_control.c b/src/decoder_control.c
index 9844b6918..7fe18088b 100644
--- a/src/decoder_control.c
+++ b/src/decoder_control.c
@@ -84,26 +84,20 @@ dc_command_async(struct decoder_control *dc, enum decoder_command cmd)
}
void
-dc_start(struct decoder_control *dc, struct song *song)
+dc_start(struct decoder_control *dc, struct song *song,
+ struct music_buffer *buffer, struct music_pipe *pipe)
{
- assert(dc->pipe != NULL);
assert(song != NULL);
+ assert(buffer != NULL);
+ assert(pipe != NULL);
- dc->next_song = song;
+ dc->song = song;
+ dc->buffer = buffer;
+ dc->pipe = pipe;
dc_command(dc, DECODE_COMMAND_START);
}
void
-dc_start_async(struct decoder_control *dc, struct song *song)
-{
- assert(dc->pipe != NULL);
- assert(song != NULL);
-
- dc->next_song = song;
- dc_command_async(dc, DECODE_COMMAND_START);
-}
-
-void
dc_stop(struct decoder_control *dc)
{
decoder_lock(dc);
diff --git a/src/decoder_control.h b/src/decoder_control.h
index d03c7a805..38c4f0d83 100644
--- a/src/decoder_control.h
+++ b/src/decoder_control.h
@@ -72,14 +72,22 @@ struct decoder_control {
/** the format being sent to the music pipe */
struct audio_format out_audio_format;
- struct song *current_song;
- struct song *next_song;
+ /**
+ * The song currently being decoded. This attribute is set by
+ * the player thread, when it sends the #DECODE_COMMAND_START
+ * command.
+ */
+ const struct song *song;
+
float total_time;
/** the #music_chunk allocator */
struct music_buffer *buffer;
- /** the destination pipe for decoded chunks */
+ /**
+ * The destination pipe for decoded chunks. The caller thread
+ * owns this object, and is responsible for freeing it.
+ */
struct music_pipe *pipe;
};
@@ -132,16 +140,14 @@ decoder_signal(struct decoder_control *dc)
static inline bool
decoder_is_idle(const struct decoder_control *dc)
{
- return (dc->state == DECODE_STATE_STOP ||
- dc->state == DECODE_STATE_ERROR) &&
- dc->command != DECODE_COMMAND_START;
+ return dc->state == DECODE_STATE_STOP ||
+ dc->state == DECODE_STATE_ERROR;
}
static inline bool
decoder_is_starting(const struct decoder_control *dc)
{
- return dc->command == DECODE_COMMAND_START ||
- dc->state == DECODE_STATE_START;
+ return dc->state == DECODE_STATE_START;
}
static inline bool
@@ -198,7 +204,7 @@ decoder_current_song(const struct decoder_control *dc)
case DECODE_STATE_START:
case DECODE_STATE_DECODE:
- return dc->current_song;
+ return dc->song;
}
assert(false);
@@ -208,11 +214,17 @@ decoder_current_song(const struct decoder_control *dc)
void
dc_command_wait(struct decoder_control *dc);
+/**
+ * Start the decoder.
+ *
+ * @param the decoder
+ * @param song the song to be decoded
+ * @param pipe the pipe which receives the decoded chunks (owned by
+ * the caller)
+ */
void
-dc_start(struct decoder_control *dc, struct song *song);
-
-void
-dc_start_async(struct decoder_control *dc, struct song *song);
+dc_start(struct decoder_control *dc, struct song *song,
+ struct music_buffer *buffer, struct music_pipe *pipe);
void
dc_stop(struct decoder_control *dc);
diff --git a/src/decoder_thread.c b/src/decoder_thread.c
index 7308795c3..175f343e0 100644
--- a/src/decoder_thread.c
+++ b/src/decoder_thread.c
@@ -101,11 +101,6 @@ decoder_run_song(struct decoder_control *dc,
struct input_stream input_stream;
const struct decoder_plugin *plugin;
- if (!input_stream_open(&input_stream, uri)) {
- dc->state = DECODE_STATE_ERROR;
- return;
- }
-
decoder.seeking = false;
decoder.song_tag = song->tag != NULL && song_is_file(song)
? tag_dup(song->tag) : NULL;
@@ -118,10 +113,19 @@ decoder_run_song(struct decoder_control *dc,
player_signal();
+ decoder_unlock(dc);
+
+ if (!input_stream_open(&input_stream, uri)) {
+ decoder_lock(dc);
+ dc->state = DECODE_STATE_ERROR;
+ return;
+ }
+
/* wait for the input stream to become ready; its metadata
will be available then */
while (!input_stream.ready) {
+ decoder_lock(dc);
if (dc->command == DECODE_COMMAND_STOP) {
decoder_unlock(dc);
input_stream_close(&input_stream);
@@ -131,6 +135,7 @@ decoder_run_song(struct decoder_control *dc,
}
decoder_unlock(dc);
+
ret = input_stream_buffer(&input_stream);
if (ret < 0) {
input_stream_close(&input_stream);
@@ -138,10 +143,10 @@ decoder_run_song(struct decoder_control *dc,
dc->state = DECODE_STATE_ERROR;
return;
}
-
- decoder_lock(dc);
}
+ decoder_lock(dc);
+
if (dc->command == DECODE_COMMAND_STOP) {
decoder_unlock(dc);
input_stream_close(&input_stream);
@@ -263,9 +268,11 @@ decoder_run_song(struct decoder_control *dc,
static void
decoder_run(struct decoder_control *dc)
{
- struct song *song = dc->next_song;
+ const struct song *song = dc->song;
char *uri;
+ assert(song != NULL);
+
if (song_is_file(song))
uri = map_song_fs(song);
else
@@ -276,7 +283,6 @@ decoder_run(struct decoder_control *dc)
return;
}
- dc->current_song = dc->next_song; /* NEED LOCK */
decoder_run_song(dc, song, uri);
g_free(uri);
diff --git a/src/player_control.c b/src/player_control.c
index 1828a1ca0..220d39a22 100644
--- a/src/player_control.c
+++ b/src/player_control.c
@@ -255,9 +255,10 @@ void
pc_enqueue_song(struct song *song)
{
assert(song != NULL);
- assert(pc.next_song == NULL);
player_lock();
+ assert(pc.next_song == NULL);
+
pc.next_song = song;
player_command_locked(PLAYER_COMMAND_QUEUE);
player_unlock();
diff --git a/src/player_control.h b/src/player_control.h
index c1fd52d81..02c04fda8 100644
--- a/src/player_control.h
+++ b/src/player_control.h
@@ -108,7 +108,7 @@ struct player_control {
float total_time;
float elapsed_time;
struct song *volatile next_song;
- struct song *errored_song;
+ const struct song *errored_song;
volatile double seek_where;
float cross_fade_seconds;
double total_play_time;
diff --git a/src/player_thread.c b/src/player_thread.c
index 32d35f309..5cb73a3ee 100644
--- a/src/player_thread.c
+++ b/src/player_thread.c
@@ -124,6 +124,22 @@ static void player_command_finished(void)
}
/**
+ * Start the decoder.
+ *
+ * Player lock is not held.
+ */
+static void
+player_dc_start(struct player *player, struct music_pipe *pipe)
+{
+ struct decoder_control *dc = player->dc;
+
+ assert(player->queued);
+ assert(pc.next_song != NULL);
+
+ dc_start(dc, pc.next_song, player_buffer, pipe);
+}
+
+/**
* Stop the decoder and clears (and frees) its music pipe.
*
* Player lock is not held.
@@ -148,6 +164,17 @@ player_dc_stop(struct player *player)
}
/**
+ * Returns true if the decoder is decoding the next song (or has begun
+ * decoding it, or has finished doing it), and the player hasn't
+ * switched to that song yet.
+ */
+static bool
+decoding_next_song(const struct player *player)
+{
+ return player->dc->pipe != NULL && player->dc->pipe != player->pipe;
+}
+
+/**
* 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.
@@ -159,34 +186,40 @@ player_wait_for_decoder(struct player *player)
{
struct decoder_control *dc = player->dc;
- dc_command_wait(dc);
+ assert(player->queued);
+ assert(pc.next_song != NULL);
- if (decoder_lock_has_failed(dc)) {
- assert(dc->next_song == NULL || dc->next_song->uri != NULL);
+ player->queued = false;
+ if (decoder_lock_has_failed(dc)) {
player_lock();
- pc.errored_song = dc->next_song;
+ pc.errored_song = dc->song;
pc.error = PLAYER_ERROR_FILE;
pc.next_song = NULL;
player_unlock();
- player->queued = false;
return false;
}
+ player->song = pc.next_song;
+ player->elapsed_time = 0.0;
+
+ /* set the "starting" flag, which will be cleared by
+ player_check_decoder_startup() */
+ player->decoder_starting = true;
+
+ player_lock();
+
+ /* update player_control's song information */
pc.total_time = pc.next_song->tag != NULL
? pc.next_song->tag->time : 0;
pc.bit_rate = 0;
audio_format_clear(&pc.audio_format);
- player->song = pc.next_song;
+ /* clear the queued song */
pc.next_song = NULL;
- player->queued = false;
- player->elapsed_time = 0.0;
- /* set the "starting" flag, which will be cleared by
- player_check_decoder_startup() */
- player->decoder_starting = true;
+ player_unlock();
/* call syncPlaylistWithQueue() in the main thread */
event_pipe_emit(PIPE_EVENT_PLAYLIST);
@@ -212,12 +245,12 @@ player_check_decoder_startup(struct player *player)
if (decoder_has_failed(dc)) {
/* the decoder failed */
- assert(dc->next_song == NULL || dc->next_song->uri != NULL);
-
decoder_unlock(dc);
- pc.errored_song = dc->next_song;
+ player_lock();
+ pc.errored_song = dc->song;
pc.error = PLAYER_ERROR_FILE;
+ player_unlock();
return false;
} else if (!decoder_is_starting(dc)) {
@@ -231,26 +264,30 @@ player_check_decoder_startup(struct player *player)
all chunks yet - wait for that */
return true;
+ player_lock();
pc.total_time = dc->total_time;
pc.audio_format = dc->in_audio_format;
+ player_unlock();
+
player->play_audio_format = dc->out_audio_format;
player->decoder_starting = false;
if (!player->paused &&
!audio_output_all_open(&dc->out_audio_format,
player_buffer)) {
- char *uri = song_get_uri(dc->next_song);
+ char *uri = song_get_uri(dc->song);
g_warning("problems opening audio device "
"while playing \"%s\"", uri);
g_free(uri);
- assert(dc->next_song == NULL || dc->next_song->uri != NULL);
- pc.errored_song = dc->next_song;
+ player_lock();
pc.error = PLAYER_ERROR_AUDIO;
/* pause: the user may resume playback as soon
as an audio output becomes available */
pc.state = PLAYER_STATE_PAUSE;
+ player_unlock();
+
player->paused = true;
return true;
}
@@ -328,10 +365,9 @@ static bool player_seek_decoder(struct player *player)
/* clear music chunks which might still reside in the
pipe */
music_pipe_clear(player->pipe, player_buffer);
- dc->pipe = player->pipe;
/* re-start the decoder */
- dc_start_async(dc, pc.next_song);
+ player_dc_start(player, player->pipe);
ret = player_wait_for_decoder(player);
if (!ret) {
/* decoder failure */
@@ -436,8 +472,6 @@ static void player_process_command(struct player *player)
} else {
/* the audio device has failed - rollback to
pause mode */
- assert(dc->next_song == NULL || dc->next_song->uri != NULL);
- pc.errored_song = dc->next_song;
pc.error = PLAYER_ERROR_AUDIO;
player->paused = true;
@@ -463,7 +497,7 @@ static void player_process_command(struct player *player)
return;
}
- if (dc->pipe != NULL && dc->pipe != player->pipe) {
+ if (decoding_next_song(player)) {
/* the decoder is already decoding the song -
stop it and reset the position */
player_unlock();
@@ -571,7 +605,7 @@ play_next_chunk(struct player *player)
return true;
if (player->xfade == XFADE_ENABLED &&
- dc->pipe != NULL && dc->pipe != player->pipe &&
+ decoding_next_song(player) &&
(cross_fade_position = music_pipe_size(player->pipe))
<= player->cross_fade_chunks) {
/* perform cross fade */
@@ -698,7 +732,7 @@ static void do_play(struct decoder_control *dc)
.buffering = true,
.decoder_starting = false,
.paused = false,
- .queued = false,
+ .queued = true,
.song = NULL,
.xfade = XFADE_UNKNOWN,
.cross_fading = false,
@@ -710,9 +744,7 @@ static void do_play(struct decoder_control *dc)
player.pipe = music_pipe_new();
- dc->buffer = player_buffer;
- dc->pipe = player.pipe;
- dc_start(dc, pc.next_song);
+ player_dc_start(&player, player.pipe);
if (!player_wait_for_decoder(&player)) {
player_dc_stop(&player);
player_command_finished();
@@ -785,18 +817,16 @@ static void do_play(struct decoder_control *dc)
*/
#endif
- if (decoder_lock_is_idle(dc) && player.queued) {
+ if (decoder_lock_is_idle(dc) && player.queued &&
+ dc->pipe == player.pipe) {
/* the decoder has finished the current song;
make it decode the next song */
- assert(pc.next_song != NULL);
assert(dc->pipe == NULL || dc->pipe == player.pipe);
- player.queued = false;
- dc->pipe = music_pipe_new();
- dc_start_async(dc, pc.next_song);
+ player_dc_start(&player, music_pipe_new());
}
- if (dc->pipe != NULL && dc->pipe != player.pipe &&
+ if (decoding_next_song(&player) &&
player.xfade == XFADE_UNKNOWN &&
!decoder_lock_is_starting(dc)) {
/* enable cross fading in this song? if yes,
@@ -835,7 +865,7 @@ static void do_play(struct decoder_control *dc)
/* XXX synchronize in a better way */
g_usleep(10000);
- } else if (dc->pipe != NULL && dc->pipe != player.pipe) {
+ } else if (decoding_next_song(&player)) {
/* at the beginning of a new song */
if (!player_song_border(&player))
@@ -859,14 +889,18 @@ static void do_play(struct decoder_control *dc)
player_dc_stop(&player);
- assert(!player.queued || pc.next_song != NULL);
- pc.next_song = NULL;
-
music_pipe_clear(player.pipe, player_buffer);
music_pipe_free(player.pipe);
player_lock();
+
+ if (player.queued) {
+ assert(pc.next_song != NULL);
+ pc.next_song = NULL;
+ }
+
pc.state = PLAYER_STATE_STOP;
+
player_unlock();
event_pipe_emit(PIPE_EVENT_PLAYLIST);