diff options
Diffstat (limited to 'src/output/pulse_output_plugin.c')
-rw-r--r-- | src/output/pulse_output_plugin.c | 149 |
1 files changed, 110 insertions, 39 deletions
diff --git a/src/output/pulse_output_plugin.c b/src/output/pulse_output_plugin.c index 5fe2f572e..0dc9be0e4 100644 --- a/src/output/pulse_output_plugin.c +++ b/src/output/pulse_output_plugin.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2010 The Music Player Daemon Project + * Copyright (C) 2003-2011 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -31,11 +31,44 @@ #include <pulse/introspect.h> #include <pulse/subscribe.h> #include <pulse/error.h> +#include <pulse/version.h> #include <assert.h> +#include <stddef.h> #define MPD_PULSE_NAME "Music Player Daemon" +#if !defined(PA_CHECK_VERSION) +/** + * This macro was implemented in libpulse 0.9.16. + */ +#define PA_CHECK_VERSION(a,b,c) false +#endif + +struct pulse_output { + struct audio_output base; + + const char *name; + const char *server; + const char *sink; + + struct pulse_mixer *mixer; + + struct pa_threaded_mainloop *mainloop; + struct pa_context *context; + struct pa_stream *stream; + + size_t writable; + +#if !PA_CHECK_VERSION(0,9,11) + /** + * We need this variable because pa_stream_is_corked() wasn't + * added before 0.9.11. + */ + bool pause; +#endif +}; + /** * The quark used for GError.domain. */ @@ -46,6 +79,18 @@ pulse_output_quark(void) } void +pulse_output_lock(struct pulse_output *po) +{ + pa_threaded_mainloop_lock(po->mainloop); +} + +void +pulse_output_unlock(struct pulse_output *po) +{ + pa_threaded_mainloop_unlock(po->mainloop); +} + +void pulse_output_set_mixer(struct pulse_output *po, struct pulse_mixer *pm) { assert(po != NULL); @@ -299,16 +344,19 @@ pulse_output_setup_context(struct pulse_output *po, GError **error_r) return true; } -static void * -pulse_output_init(G_GNUC_UNUSED const struct audio_format *audio_format, - const struct config_param *param, - G_GNUC_UNUSED GError **error_r) +static struct audio_output * +pulse_output_init(const struct config_param *param, GError **error_r) { struct pulse_output *po; g_setenv("PULSE_PROP_media.role", "music", true); po = g_new(struct pulse_output, 1); + if (!ao_base_init(&po->base, &pulse_output_plugin, param, error_r)) { + g_free(po); + return NULL; + } + po->name = config_get_block_string(param, "name", "mpd_pulse"); po->server = config_get_block_string(param, "server", NULL); po->sink = config_get_block_string(param, "sink", NULL); @@ -318,21 +366,22 @@ pulse_output_init(G_GNUC_UNUSED const struct audio_format *audio_format, po->context = NULL; po->stream = NULL; - return po; + return &po->base; } static void -pulse_output_finish(void *data) +pulse_output_finish(struct audio_output *ao) { - struct pulse_output *po = data; + struct pulse_output *po = (struct pulse_output *)ao; + ao_base_finish(&po->base); g_free(po); } static bool -pulse_output_enable(void *data, GError **error_r) +pulse_output_enable(struct audio_output *ao, GError **error_r) { - struct pulse_output *po = data; + struct pulse_output *po = (struct pulse_output *)ao; assert(po->mainloop == NULL); assert(po->context == NULL); @@ -376,9 +425,9 @@ pulse_output_enable(void *data, GError **error_r) } static void -pulse_output_disable(void *data) +pulse_output_disable(struct audio_output *ao) { - struct pulse_output *po = data; + struct pulse_output *po = (struct pulse_output *)ao; assert(po->mainloop != NULL); @@ -494,11 +543,46 @@ pulse_output_stream_write_cb(G_GNUC_UNUSED pa_stream *stream, size_t nbytes, pa_threaded_mainloop_signal(po->mainloop, 0); } +/** + * Create, set up and connect a context. + * + * Caller must lock the main loop. + * + * @return true on success, false on error + */ +static bool +pulse_output_setup_stream(struct pulse_output *po, const pa_sample_spec *ss, + GError **error_r) +{ + assert(po != NULL); + assert(po->context != NULL); + + po->stream = pa_stream_new(po->context, po->name, ss, NULL); + if (po->stream == NULL) { + g_set_error(error_r, pulse_output_quark(), 0, + "pa_stream_new() has failed: %s", + pa_strerror(pa_context_errno(po->context))); + return false; + } + +#if PA_CHECK_VERSION(0,9,8) + pa_stream_set_suspended_callback(po->stream, + pulse_output_stream_suspended_cb, po); +#endif + + pa_stream_set_state_callback(po->stream, + pulse_output_stream_state_cb, po); + pa_stream_set_write_callback(po->stream, + pulse_output_stream_write_cb, po); + + return true; +} + static bool -pulse_output_open(void *data, struct audio_format *audio_format, +pulse_output_open(struct audio_output *ao, struct audio_format *audio_format, GError **error_r) { - struct pulse_output *po = data; + struct pulse_output *po = (struct pulse_output *)ao; pa_sample_spec ss; int error; @@ -540,25 +624,11 @@ pulse_output_open(void *data, struct audio_format *audio_format, /* create a stream .. */ - po->stream = pa_stream_new(po->context, po->name, &ss, NULL); - if (po->stream == NULL) { - g_set_error(error_r, pulse_output_quark(), 0, - "pa_stream_new() has failed: %s", - pa_strerror(pa_context_errno(po->context))); + if (!pulse_output_setup_stream(po, &ss, error_r)) { pa_threaded_mainloop_unlock(po->mainloop); return false; } -#if PA_CHECK_VERSION(0,9,8) - pa_stream_set_suspended_callback(po->stream, - pulse_output_stream_suspended_cb, po); -#endif - - pa_stream_set_state_callback(po->stream, - pulse_output_stream_state_cb, po); - pa_stream_set_write_callback(po->stream, - pulse_output_stream_write_cb, po); - /* .. and connect it (asynchronously) */ error = pa_stream_connect_playback(po->stream, po->sink, @@ -583,9 +653,9 @@ pulse_output_open(void *data, struct audio_format *audio_format, } static void -pulse_output_close(void *data) +pulse_output_close(struct audio_output *ao) { - struct pulse_output *po = data; + struct pulse_output *po = (struct pulse_output *)ao; pa_operation *o; assert(po->mainloop != NULL); @@ -732,9 +802,10 @@ pulse_output_stream_pause(struct pulse_output *po, bool pause, } static size_t -pulse_output_play(void *data, const void *chunk, size_t size, GError **error_r) +pulse_output_play(struct audio_output *ao, const void *chunk, size_t size, + GError **error_r) { - struct pulse_output *po = data; + struct pulse_output *po = (struct pulse_output *)ao; int error; assert(po->mainloop != NULL); @@ -802,9 +873,9 @@ pulse_output_play(void *data, const void *chunk, size_t size, GError **error_r) } static void -pulse_output_cancel(void *data) +pulse_output_cancel(struct audio_output *ao) { - struct pulse_output *po = data; + struct pulse_output *po = (struct pulse_output *)ao; pa_operation *o; assert(po->mainloop != NULL); @@ -834,9 +905,9 @@ pulse_output_cancel(void *data) } static bool -pulse_output_pause(void *data) +pulse_output_pause(struct audio_output *ao) { - struct pulse_output *po = data; + struct pulse_output *po = (struct pulse_output *)ao; GError *error = NULL; assert(po->mainloop != NULL); @@ -881,12 +952,12 @@ pulse_output_test_default_device(void) struct pulse_output *po; bool success; - po = pulse_output_init(NULL, NULL, NULL); + po = (struct pulse_output *)pulse_output_init(NULL, NULL); if (po == NULL) return false; success = pulse_output_wait_connection(po, NULL); - pulse_output_finish(po); + pulse_output_finish(&po->base); return success; } |