diff options
author | Diego 'Flameeyes' Pettenò <flameeyes@gmail.com> | 2007-04-01 00:32:29 +0000 |
---|---|---|
committer | Diego 'Flameeyes' Pettenò <flameeyes@gmail.com> | 2007-04-01 00:32:29 +0000 |
commit | b622467479eebe6efc02374f0f7e08fcc2caaae5 (patch) | |
tree | 8478af7376328959ad3c939f3e3c889b00dcfaaa | |
parent | fc47d546f3ddf86f117a331ff870316511e5505d (diff) | |
download | xine-lib-b622467479eebe6efc02374f0f7e08fcc2caaae5.tar.gz xine-lib-b622467479eebe6efc02374f0f7e08fcc2caaae5.tar.bz2 |
Try to improve response when the plugin can't connect to PulseAudio. Unfortunately if the server dies during playback xine will be too messed up to recover.
This is the same issue as unplugged USB devices, it requires a proper generic handling of these cases.
CVS patchset: 8779
CVS date: 2007/04/01 00:32:29
-rw-r--r-- | src/audio_out/audio_pulse_out.c | 165 |
1 files changed, 113 insertions, 52 deletions
diff --git a/src/audio_out/audio_pulse_out.c b/src/audio_out/audio_pulse_out.c index dbfc4b9f0..9e6089730 100644 --- a/src/audio_out/audio_pulse_out.c +++ b/src/audio_out/audio_pulse_out.c @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $Id: audio_pulse_out.c,v 1.16 2007/03/31 22:06:54 dgp85 Exp $ + * $Id: audio_pulse_out.c,v 1.17 2007/04/01 00:32:29 dgp85 Exp $ * * ao plugin for pulseaudio (rename of polypaudio): * http://0pointer.de/lennart/projects/pulsaudio/ @@ -107,6 +107,34 @@ static void __xine_pa_stream_success_callback(pa_stream *const stream, const int } /** + * @brief Callback function called when the state of the context is changed + * @param ctx Context which operation has succeeded + * @param this_gen pulse_driver_t pointer for the PulseAudio output + * instance. + */ +static void __xine_pa_context_status_callback(pa_context *const ctx, void *const this_gen) +{ + pulse_driver_t *const this = (pulse_driver_t*)this_gen; + + _x_assert(ctx); _x_assert(this); + _x_assert(ctx == this->pa_class->context); + + switch (pa_context_get_state(ctx)) { + case PA_CONTEXT_READY: + case PA_CONTEXT_TERMINATED: + case PA_CONTEXT_FAILED: + pa_threaded_mainloop_signal(this->pa_class->mainloop, 0); + break; + + case PA_CONTEXT_CONNECTING: + case PA_CONTEXT_UNCONNECTED: + case PA_CONTEXT_AUTHORIZING: + case PA_CONTEXT_SETTING_NAME: + break; + } +} + +/** * @brief Callback function called when a context operation succeed * @param ctx Context which operation has succeeded * @param success The success value for the operation (ignored) @@ -221,6 +249,39 @@ static int ao_pulse_open(ao_driver_t *this_gen, goto fail; } + pthread_mutex_lock(&this->pa_class->pa_mutex); + if ( this->pa_class->context && pa_context_get_state(this->pa_class->context) > PA_CONTEXT_READY ) { + pa_context_unref(this->pa_class->context); + this->pa_class->context = NULL; + } + + if ( this->pa_class->context == NULL ) { + this->pa_class->context = pa_context_new(pa_threaded_mainloop_get_api(this->pa_class->mainloop), + __progname); + } + + pa_context_ref(this->pa_class->context); + + if ( pa_context_get_state(this->pa_class->context) == PA_CONTEXT_UNCONNECTED ) { + int ret; + + pa_threaded_mainloop_lock(this->pa_class->mainloop); + ret = pa_context_connect(this->pa_class->context, this->host, 1, NULL); + if ( ret < 0 ) + goto fail; + + pa_context_set_state_callback(this->pa_class->context, __xine_pa_context_status_callback, this); + + pa_threaded_mainloop_wait(this->pa_class->mainloop); + pa_threaded_mainloop_unlock(this->pa_class->mainloop); + } + + if (pa_context_get_state(this->pa_class->context) != PA_CONTEXT_READY) { + xprintf (this->xine, XINE_VERBOSITY_DEBUG, "audio_pulse_out: Failed to connect to server: %s\n", + pa_strerror(pa_context_errno(this->pa_class->context))); + goto fail; + } + this->stream = pa_stream_new(this->pa_class->context, "audio stream", &ss, NULL); _x_assert(this->stream); @@ -229,8 +290,6 @@ static int ao_pulse_open(ao_driver_t *this_gen, a.prebuf = a.tlength/2; a.minreq = a.tlength/10; - pthread_mutex_lock(&this->pa_class->pa_mutex); - pa_stream_connect_playback(this->stream, this->sink, &a, PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL); @@ -255,6 +314,8 @@ static int ao_pulse_open(ao_driver_t *this_gen, return this->sample_rate; fail: + pa_threaded_mainloop_unlock(this->pa_class->mainloop); + pthread_mutex_unlock(&this->pa_class->pa_mutex); this_gen->close(this_gen); return 0; } @@ -284,10 +345,11 @@ static int ao_pulse_write(ao_driver_t *this_gen, int16_t *data, int size = num_frames * this->bytes_per_frame; int ret = 0; - _x_assert(this->stream && this->pa_class->context); - - if (pa_stream_get_state(this->stream) == PA_STREAM_READY) { + if ( !this->stream || !this->pa_class->context) + return -1; + switch( pa_stream_get_state(this->stream) ) { + case PA_STREAM_READY: while (size > 0) { size_t l; @@ -307,6 +369,8 @@ static int ao_pulse_write(ao_driver_t *this_gen, int16_t *data, if (pa_stream_get_state(this->stream) == PA_STREAM_READY) ret = 1; + + break; } return ret; @@ -319,15 +383,25 @@ static int ao_pulse_delay (ao_driver_t *this_gen) pa_usec_t latency = 0; int delay_frames; - for (;;) { - if (pa_stream_get_latency(this->stream, &latency, NULL) >= 0) - break; + if ( ! this->stream ) return this->frames_written; - if (pa_context_errno(this->pa_class->context) != PA_ERR_NODATA) { - /* error */ - } + pthread_mutex_lock(&this->pa_class->pa_mutex); + + if (pa_stream_get_latency(this->stream, &latency, NULL) < 0) { + pa_context_unref(this->pa_class->context); + this->pa_class->context = NULL; + + pa_stream_disconnect(this->stream); + pa_stream_unref(this->stream); + this->stream = NULL; + + pthread_mutex_unlock(&this->pa_class->pa_mutex); + + return 0; } + pthread_mutex_unlock(&this->pa_class->pa_mutex); + /* convert latency (us) to frame units. */ delay_frames = (int)(latency * this->sample_rate / 1000000); @@ -350,6 +424,8 @@ static void ao_pulse_close(ao_driver_t *this_gen) pa_stream_unref(this->stream); this->stream = NULL; + pa_context_unref(this->pa_class->context); + pthread_mutex_unlock(&this->pa_class->pa_mutex); } } @@ -378,10 +454,15 @@ static int ao_pulse_get_property (ao_driver_t *this_gen, int property) { switch(property) { case AO_PROP_PCM_VOL: case AO_PROP_MIXER_VOL: - wait_for_operation(this, - pa_context_get_sink_input_info(this->pa_class->context, pa_stream_get_index(this->stream), - __xine_pa_sink_info_callback, this)); - result = (pa_sw_volume_to_linear(this->swvolume)*100); + { + pa_operation *o = pa_context_get_sink_input_info(this->pa_class->context, + pa_stream_get_index(this->stream), + __xine_pa_sink_info_callback, this); + if ( ! o ) return 0; + wait_for_operation(this, o); + + result = (pa_sw_volume_to_linear(this->swvolume)*100); + } break; case AO_PROP_MUTE_VOL: @@ -437,6 +518,8 @@ static int ao_pulse_set_property (ao_driver_t *this_gen, int property, int value static int ao_pulse_ctrl(ao_driver_t *this_gen, int cmd, ...) { pulse_driver_t *this = (pulse_driver_t *) this_gen; + if ( ! this->stream ) return 0; + switch (cmd) { case AO_CTRL_PLAY_PAUSE: @@ -484,9 +567,7 @@ static int ao_pulse_ctrl(ao_driver_t *this_gen, int cmd, ...) { static ao_driver_t *open_plugin (audio_driver_class_t *class_gen, const void *data) { pulse_class_t *class = (pulse_class_t *) class_gen; pulse_driver_t *this; - char hn[128]; char *device; - pa_context_state_t ctxstate; lprintf ("audio_pulse_out: open_plugin called\n"); @@ -505,6 +586,8 @@ static ao_driver_t *open_plugin (audio_driver_class_t *class_gen, const void *da AO_CAP_16BITS | AO_CAP_FLOAT32; this->sample_rate = 0; + this->host = NULL; + this->sink = NULL; this->ao_driver.get_capabilities = ao_pulse_get_capabilities; this->ao_driver.get_property = ao_pulse_get_property; @@ -528,18 +611,13 @@ static ao_driver_t *open_plugin (audio_driver_class_t *class_gen, const void *da 10, NULL, NULL); - if (device && strlen(device)) { - int i = strcspn(device, ":"); - if (i >= sizeof(hn)) - i = sizeof(hn)-1; - - if (i > 0) { - strncpy(this->host = hn, device, i); - hn[i] = 0; - } - - if (device[i] == ':') - this->sink = device+i+1; + if (device && *device) { + char *sep = strchr(device, ':'); + if ( sep ) { + this->host = strndup(device, sep-device); + this->sink = strdup(&sep[1]); + } else + this->host = strdup(device); } xprintf (class->xine, XINE_VERBOSITY_DEBUG, "audio_pulse_out: host %s sink %s\n", @@ -547,28 +625,10 @@ static ao_driver_t *open_plugin (audio_driver_class_t *class_gen, const void *da this->pa_class = class; - pthread_mutex_lock(&this->pa_class->pa_mutex); - if ( pa_context_get_state(this->pa_class->context) != PA_CONTEXT_READY ){ - pa_context_connect(this->pa_class->context, this->host, 1, NULL); - - do { - xine_usec_sleep (100); - - ctxstate = pa_context_get_state(this->pa_class->context); - } while (ctxstate < PA_CONTEXT_READY); - - if (ctxstate != PA_CONTEXT_READY) { - xprintf (this->xine, XINE_VERBOSITY_DEBUG, "audio_pulse_out: Failed to connect to server: %s\n", - pa_strerror(pa_context_errno(this->pa_class->context))); - goto fail; - } - } - - pthread_mutex_unlock(&this->pa_class->pa_mutex); return &this->ao_driver; fail: - pthread_mutex_lock(&this->pa_class->pa_mutex); + pthread_mutex_unlock(&this->pa_class->pa_mutex); free(this); xprintf (class->xine, XINE_VERBOSITY_DEBUG, "audio_pulse_out: open_plugin failed.\n"); return NULL; @@ -592,7 +652,8 @@ static void dispose_class (audio_driver_class_t *this_gen) { pthread_mutex_lock(&this->pa_mutex); - pa_context_unref(this->context); + if ( this->context ) + pa_context_unref(this->context); pa_threaded_mainloop_stop(this->mainloop); pa_threaded_mainloop_free(this->mainloop); @@ -626,8 +687,7 @@ static void *init_class (xine_t *xine, void *data) { pa_threaded_mainloop_start(this->mainloop); - this->context = pa_context_new(pa_threaded_mainloop_get_api(this->mainloop), __progname); - _x_assert(this->context); + this->context = NULL; pthread_mutex_unlock(&this->pa_mutex); @@ -648,3 +708,4 @@ const plugin_info_t xine_plugin_info[] EXPORTED = { { PLUGIN_NONE, 0, "", 0, NULL, NULL } }; + |