aboutsummaryrefslogtreecommitdiffstats
path: root/src/player_thread.c
diff options
context:
space:
mode:
authorMax Kellermann <max@duempel.org>2009-03-09 19:25:26 +0100
committerMax Kellermann <max@duempel.org>2009-03-09 19:25:26 +0100
commit3291666b570b1d20f59db42936eaa37dbeb9ca65 (patch)
treec3905d60131c9f440d8ca7d7d1ec45cecce02d85 /src/player_thread.c
parentab3d7c29dae44f39df28c85e26b108c44fdbc4bf (diff)
downloadmpd-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 '')
-rw-r--r--src/player_thread.c72
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;