diff options
author | Max Kellermann <max@duempel.org> | 2009-03-09 19:25:26 +0100 |
---|---|---|
committer | Max Kellermann <max@duempel.org> | 2009-03-09 19:25:26 +0100 |
commit | 3291666b570b1d20f59db42936eaa37dbeb9ca65 (patch) | |
tree | c3905d60131c9f440d8ca7d7d1ec45cecce02d85 /src/player_thread.c | |
parent | ab3d7c29dae44f39df28c85e26b108c44fdbc4bf (diff) | |
download | mpd-3291666b570b1d20f59db42936eaa37dbeb9ca65.tar.gz mpd-3291666b570b1d20f59db42936eaa37dbeb9ca65.tar.xz mpd-3291666b570b1d20f59db42936eaa37dbeb9ca65.zip |
output: play from a music_pipe object
Instead of passing individual buffers to audio_output_all_play(), pass
music_chunk objects. Append all those chunks asynchronously to a
music_pipe instance. All output threads may then read chunks from
this pipe. This reduces MPD's internal latency by an order of
magnitude.
Diffstat (limited to 'src/player_thread.c')
-rw-r--r-- | src/player_thread.c | 72 |
1 files changed, 59 insertions, 13 deletions
diff --git a/src/player_thread.c b/src/player_thread.c index dee564ad0..7205822e9 100644 --- a/src/player_thread.c +++ b/src/player_thread.c @@ -177,10 +177,22 @@ player_check_decoder_startup(struct player *player) return false; } else if (!decoder_is_starting()) { /* the decoder is ready and ok */ + + if (audio_format_defined(&player->play_audio_format) && + audio_output_all_check() > 0) { + /* the output devices havn't finished playing + all chunks yet - wait for that */ + + /* XXX synchronize in a better way */ + g_usleep(1000); + return true; + } + player->decoder_starting = false; if (!player->paused && - !audio_output_all_open(&dc.out_audio_format)) { + !audio_output_all_open(&dc.out_audio_format, + player_buffer)) { char *uri = song_get_uri(dc.next_song); g_warning("problems opening audio device " "while playing \"%s\"", uri); @@ -268,7 +280,7 @@ static void player_process_command(struct player *player) audio_output_all_pause(); pc.state = PLAYER_STATE_PAUSE; } else { - if (audio_output_all_open(NULL)) { + if (audio_output_all_open(NULL, player_buffer)) { pc.state = PLAYER_STATE_PLAY; } else { assert(dc.next_song == NULL || dc.next_song->url != NULL); @@ -328,8 +340,6 @@ play_chunk(struct song *song, struct music_chunk *chunk, pc.bit_rate = chunk->bit_rate; if (chunk->tag != NULL) { - audio_output_all_tag(chunk->tag); - if (!song_is_file(song)) { /* always update the tag of remote streams */ struct tag *old_tag = song->tag; @@ -349,9 +359,6 @@ play_chunk(struct song *song, struct music_chunk *chunk, } } - if (chunk->length == 0) - return true; - success = pcm_volume(chunk->data, chunk->length, format, pc.software_volume); if (!success) { @@ -362,7 +369,7 @@ play_chunk(struct song *song, struct music_chunk *chunk, return false; } - if (!audio_output_all_play(chunk->data, chunk->length)) { + if (!audio_output_all_play(chunk)) { pc.errored_song = dc.current_song; pc.error = PLAYER_ERROR_AUDIO; return false; @@ -385,6 +392,15 @@ play_next_chunk(struct player *player) unsigned cross_fade_position; bool success; + if (audio_output_all_check() >= 64) { + /* the output pipe is still large + enough, don't send another chunk */ + + /* XXX synchronize in a better way */ + g_usleep(1000); + return true; + } + if (player->xfade == XFADE_ENABLED && dc.pipe != NULL && dc.pipe != player->pipe && (cross_fade_position = music_pipe_size(player->pipe)) @@ -440,10 +456,11 @@ play_next_chunk(struct player *player) success = play_chunk(player->song, chunk, &player->play_audio_format, player->size_to_time); - music_buffer_return(player_buffer, chunk); - if (!success) + if (!success) { + music_buffer_return(player_buffer, chunk); return false; + } /* this formula should prevent that the decoder gets woken up with each chunk; it @@ -472,7 +489,10 @@ player_song_border(struct player *player) music_pipe_free(player->pipe); player->pipe = dc.pipe; - return player_wait_for_decoder(player); + if (!player_wait_for_decoder(player)) + return false; + + return true; } static void do_play(void) @@ -488,7 +508,6 @@ static void do_play(void) .cross_fade_chunks = 0, .size_to_time = 0.0, }; - static const char silence[CHUNK_SIZE]; player.pipe = music_pipe_new(); @@ -585,6 +604,13 @@ static void do_play(void) if (!play_next_chunk(&player)) break; + } else if (audio_output_all_check() > 0) { + /* not enough data from decoder, but the + output thread is still busy, so it's + okay */ + + /* XXX synchronize in a better way */ + g_usleep(10000); } else if (dc.pipe != NULL && dc.pipe != player.pipe) { /* at the beginning of a new song */ @@ -593,15 +619,32 @@ static void do_play(void) } else if (decoder_is_idle()) { break; } else { + /* the decoder is too busy and hasn't provided + new PCM data in time: send silence (if the + output pipe is empty) */ + struct music_chunk *chunk; size_t frame_size = audio_format_frame_size(&player.play_audio_format); /* this formula ensures that we don't send partial frames */ unsigned num_frames = CHUNK_SIZE / frame_size; + chunk = music_buffer_allocate(player_buffer); + if (chunk == NULL) + continue; + +#ifndef NDEBUG + chunk->audio_format = player.play_audio_format; +#endif + + chunk->length = num_frames * frame_size; + memset(chunk->data, 0, chunk->length); + /*DEBUG("waiting for decoded audio, play silence\n");*/ - if (!audio_output_all_play(silence, num_frames * frame_size)) + if (!audio_output_all_play(chunk)) { + music_buffer_return(player_buffer, chunk); break; + } } } @@ -637,6 +680,9 @@ static gpointer player_task(G_GNUC_UNUSED gpointer arg) break; case PLAYER_COMMAND_STOP: + audio_output_all_cancel(); + /* fall through */ + case PLAYER_COMMAND_SEEK: case PLAYER_COMMAND_PAUSE: pc.next_song = NULL; |