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/output_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 '')
-rw-r--r-- | src/output_thread.c | 97 |
1 files changed, 80 insertions, 17 deletions
diff --git a/src/output_thread.c b/src/output_thread.c index a55260785..786b04204 100644 --- a/src/output_thread.c +++ b/src/output_thread.c @@ -19,6 +19,8 @@ #include "output_thread.h" #include "output_api.h" #include "output_internal.h" +#include "chunk.h" +#include "pipe.h" #include <glib.h> @@ -41,6 +43,12 @@ ao_close(struct audio_output *ao) { assert(ao->open); + ao->pipe = NULL; + + g_mutex_lock(ao->mutex); + ao->chunk = NULL; + g_mutex_unlock(ao->mutex); + ao_plugin_close(ao->plugin, ao->data); pcm_convert_deinit(&ao->convert_state); ao->open = false; @@ -48,16 +56,25 @@ ao_close(struct audio_output *ao) g_debug("closed plugin=%s name=\"%s\"", ao->plugin->name, ao->name); } -static void ao_play(struct audio_output *ao) +static bool +ao_play_chunk(struct audio_output *ao, const struct music_chunk *chunk) { - const char *data = ao->args.play.data; - size_t size = ao->args.play.size; + const char *data = chunk->data; + size_t size = chunk->length; GError *error = NULL; - assert(size > 0); + assert(!music_chunk_is_empty(chunk)); + assert(music_chunk_check_format(chunk, &ao->in_audio_format)); assert(size % audio_format_frame_size(&ao->in_audio_format) == 0); - if (!audio_format_equals(&ao->in_audio_format, &ao->out_audio_format)) { + if (chunk->tag != NULL) + ao_plugin_send_tag(ao->plugin, ao->data, chunk->tag); + + if (size == 0) + return true; + + if (!audio_format_equals(&ao->in_audio_format, + &ao->out_audio_format)) { data = pcm_convert(&ao->convert_state, &ao->in_audio_format, data, size, &ao->out_audio_format, &size); @@ -67,7 +84,7 @@ static void ao_play(struct audio_output *ao) investigated further, but for now, do this check as a workaround: */ if (data == NULL) - return; + return true; } while (size > 0) { @@ -87,7 +104,7 @@ static void ao_play(struct audio_output *ao) /* don't automatically reopen this device for 10 seconds */ ao->fail_timer = g_timer_new(); - break; + return false; } assert(nbytes <= size); @@ -97,7 +114,46 @@ static void ao_play(struct audio_output *ao) size -= nbytes; } - ao_command_finished(ao); + return true; +} + +static void ao_play(struct audio_output *ao) +{ + bool success; + const struct music_chunk *chunk; + + assert(ao->pipe != NULL); + + g_mutex_lock(ao->mutex); + chunk = ao->chunk; + if (chunk != NULL) + /* continue the previous play() call */ + chunk = chunk->next; + else + chunk = music_pipe_peek(ao->pipe); + ao->chunk_finished = false; + + while (chunk != NULL && ao->command == AO_COMMAND_NONE) { + assert(!ao->chunk_finished); + + ao->chunk = chunk; + g_mutex_unlock(ao->mutex); + + success = ao_play_chunk(ao, chunk); + + g_mutex_lock(ao->mutex); + + if (!success) { + assert(ao->chunk == NULL); + break; + } + + assert(ao->chunk == chunk); + chunk = chunk->next; + } + + ao->chunk_finished = true; + g_mutex_unlock(ao->mutex); } static void ao_pause(struct audio_output *ao) @@ -130,6 +186,8 @@ static gpointer audio_output_task(gpointer arg) case AO_COMMAND_OPEN: assert(!ao->open); assert(ao->fail_timer == NULL); + assert(ao->pipe != NULL); + assert(ao->chunk == NULL); error = NULL; ret = ao_plugin_open(ao->plugin, ao->data, @@ -170,35 +228,40 @@ static gpointer audio_output_task(gpointer arg) case AO_COMMAND_CLOSE: assert(ao->open); + assert(ao->pipe != NULL); + + ao->pipe = NULL; + ao->chunk = NULL; ao_plugin_cancel(ao->plugin, ao->data); ao_close(ao); ao_command_finished(ao); break; - case AO_COMMAND_PLAY: - ao_play(ao); - break; - case AO_COMMAND_PAUSE: ao_pause(ao); break; case AO_COMMAND_CANCEL: + ao->chunk = NULL; ao_plugin_cancel(ao->plugin, ao->data); ao_command_finished(ao); - break; - case AO_COMMAND_SEND_TAG: - ao_plugin_send_tag(ao->plugin, ao->data, ao->args.tag); - ao_command_finished(ao); - break; + /* the player thread will now clear our music + pipe - wait for a notify, to give it some + time */ + notify_wait(&ao->notify); + continue; case AO_COMMAND_KILL: + ao->chunk = NULL; ao_command_finished(ao); return NULL; } + if (ao->open) + ao_play(ao); + notify_wait(&ao->notify); } } |