diff options
-rw-r--r-- | src/decoder_api.c | 2 | ||||
-rw-r--r-- | src/decoder_control.c | 20 | ||||
-rw-r--r-- | src/decoder_control.h | 38 | ||||
-rw-r--r-- | src/decoder_thread.c | 24 | ||||
-rw-r--r-- | src/player_control.c | 3 | ||||
-rw-r--r-- | src/player_control.h | 2 | ||||
-rw-r--r-- | src/player_thread.c | 108 |
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); |