diff options
Diffstat (limited to 'src/audio_out/audio_pulse_out.c')
-rw-r--r-- | src/audio_out/audio_pulse_out.c | 481 |
1 files changed, 296 insertions, 185 deletions
diff --git a/src/audio_out/audio_pulse_out.c b/src/audio_out/audio_pulse_out.c index 2cef1992d..9e6089730 100644 --- a/src/audio_out/audio_pulse_out.c +++ b/src/audio_out/audio_pulse_out.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2000-2006 the xine project + * Copyright (C) 2000-2007 the xine project * * This file is part of xine, a free video player. * @@ -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.5 2006/11/10 12:10: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/ @@ -40,12 +40,9 @@ #include <math.h> #include <unistd.h> #include <inttypes.h> -#include <assert.h> #include <pthread.h> #include <pulse/pulseaudio.h> -#include <pulse/error.h> -#include <pulse/mainloop.h> #include "xine_internal.h" #include "xineutils.h" @@ -57,78 +54,146 @@ /* CHECKME: should this be conditional on autotools? */ extern const char *__progname; -typedef struct pulse_driver_s { +typedef struct { + audio_driver_class_t driver_class; + xine_t *xine; - ao_driver_t ao_driver; + pthread_mutex_t pa_mutex; /*< Mutex controlling PulseAudio access. */ + struct pa_context *context; /*< Pulseaudio connection context */ + struct pa_threaded_mainloop *mainloop; /*< Main event loop object */ +} pulse_class_t; - xine_t *xine; +typedef struct pulse_driver_s { + ao_driver_t ao_driver; + xine_t *xine; - /** The host to connect to */ - char *host; + pulse_class_t *pa_class; - /** The sink to connect to */ - char *sink; + char *host; /*< The host to connect to */ + char *sink; /*< The sink to connect to */ + struct pa_stream *stream; /*< Pulseaudio playback stream object */ - /** Pulseaudio playback stream object */ - struct pa_stream *stream; + pa_volume_t swvolume; + pa_cvolume cvolume; - /** Pulseaudio connection context */ - struct pa_context *context; + int capabilities; + int mode; - /** Main event loop object */ - struct pa_mainloop *mainloop; + int32_t sample_rate; + uint32_t num_channels; + uint32_t bits_per_sample; + uint32_t bytes_per_frame; - pa_volume_t swvolume; - pa_cvolume cvolume; + uint32_t frames_written; - int capabilities; - int mode; +} pulse_driver_t; - int32_t sample_rate; - uint32_t num_channels; - uint32_t bits_per_sample; - uint32_t bytes_per_frame; +/** + * @brief Callback function called when a stream operation succeed + * @param stream Stream which operation has succeeded + * @param success The success value for the operation (ignored) + * @param this_Gen pulse_driver_t pointer for the PulseAudio output + * instance. + */ +static void __xine_pa_stream_success_callback(pa_stream *const stream, const int success, + void *const this_gen) +{ + pulse_driver_t *const this = (pulse_driver_t*)this_gen; - uint32_t frames_written; + _x_assert(stream); _x_assert(this); + _x_assert(stream == this->stream); - pthread_mutex_t lock; + pa_threaded_mainloop_signal(this->pa_class->mainloop, 0); +} -} pulse_driver_t; +/** + * @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; -typedef struct { - audio_driver_class_t driver_class; + _x_assert(ctx); _x_assert(this); + _x_assert(ctx == this->pa_class->context); - xine_t *xine; -} pulse_class_t; + 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) + * @param this_gen pulse_driver_t pointer for the PulseAudio output + * instance. + */ +static void __xine_pa_context_success_callback(pa_context *const ctx, const int success, + void *const this_gen) +{ + pulse_driver_t *const this = (pulse_driver_t*)this_gen; -/** Make sure that the connection context doesn't starve to death */ -static void keep_alive(pulse_driver_t *this) { - assert(this->context && this->mainloop); + _x_assert(ctx); _x_assert(this); + _x_assert(ctx == this->pa_class->context); - while (pa_mainloop_iterate(this->mainloop, 0, NULL) > 0); + pa_threaded_mainloop_signal(this->pa_class->mainloop, 0); } -/** Wait until no further actions are pending on the connection context */ -static void wait_for_completion(pulse_driver_t *this) { - assert(this->context && this->mainloop); +/** + * @brief Callback function called when the information on the + * context's sink is retrieved. + * @param ctx Context which operation has succeeded + * @param info Structure containing the sink's information + * @param this_gen pulse_driver_t pointer for the PulseAudio output + * instance. + * + * This function saves the volume field of the passed structure to the + * @c cvolume variable of the output instance. + */ +static void __xine_pa_sink_info_callback(pa_context *const ctx, const pa_sink_input_info *const info, + const int is_last, void *const userdata) { + + pulse_driver_t *const this = (pulse_driver_t *) userdata; - while (pa_context_is_pending(this->context)) { - int r = pa_mainloop_iterate(this->mainloop, 1, NULL); - assert(r >= 0); + if (is_last < 0) { + xprintf (this->xine, XINE_VERBOSITY_DEBUG, "audio_pulse_out: Failed to get sink input info: %s\n", + pa_strerror(pa_context_errno(this->pa_class->context))); + return; } + + if (!info) + return; + + this->cvolume = info->volume; + + __xine_pa_context_success_callback(ctx, 0, this); } -/** Wait until the specified operation completes */ -static void wait_for_operation(pulse_driver_t *this, struct pa_operation *o) { - assert(o && this->context && this->mainloop); +static int wait_for_operation(pulse_driver_t *this, pa_operation *o) +{ + _x_assert(this && o && this->pa_class->mainloop); - while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) { - int r = pa_mainloop_iterate(this->mainloop, 1, NULL); - assert(r >= 0); - } + pa_threaded_mainloop_lock(this->pa_class->mainloop); + + while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) + pa_threaded_mainloop_wait(this->pa_class->mainloop); + + pa_threaded_mainloop_unlock(this->pa_class->mainloop); - pa_operation_unref(o); + return 0; } /* @@ -140,6 +205,7 @@ static int ao_pulse_open(ao_driver_t *this_gen, pulse_driver_t *this = (pulse_driver_t *) this_gen; struct pa_sample_spec ss; struct pa_buffer_attr a; + pa_stream_state_t streamstate; xprintf (this->xine, XINE_VERBOSITY_DEBUG, "audio_pulse_out: ao_open bits=%d rate=%d, mode=%d\n", bits, rate, mode); @@ -171,68 +237,85 @@ static int ao_pulse_open(ao_driver_t *this_gen, ss.format = PA_SAMPLE_U8; break; case 16: -#ifdef WORDS_BIGENDIAN - ss.format = PA_SAMPLE_S16BE; -#else - ss.format = PA_SAMPLE_S16LE; -#endif + ss.format = PA_SAMPLE_S16NE; break; case 32: - ss.format = PA_SAMPLE_FLOAT32; + ss.format = PA_SAMPLE_FLOAT32NE; break; } - pthread_mutex_lock(&this->lock); - if (!pa_sample_spec_valid(&ss)) { xprintf (this->xine, XINE_VERBOSITY_DEBUG, "audio_pulse_out: Invalid sample spec\n"); goto fail; } - this->mainloop = pa_mainloop_new(); - assert(this->mainloop); + 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; - this->context = pa_context_new(pa_mainloop_get_api(this->mainloop), __progname); - assert(this->context); + 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_connect(this->context, this->host, 1, NULL); + pa_context_set_state_callback(this->pa_class->context, __xine_pa_context_status_callback, this); - wait_for_completion(this); + pa_threaded_mainloop_wait(this->pa_class->mainloop); + pa_threaded_mainloop_unlock(this->pa_class->mainloop); + } - if (pa_context_get_state(this->context) != PA_CONTEXT_READY) { + 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->context))); + pa_strerror(pa_context_errno(this->pa_class->context))); goto fail; } - this->stream = pa_stream_new(this->context, "audio stream", &ss, NULL); - assert(this->stream); + this->stream = pa_stream_new(this->pa_class->context, "audio stream", &ss, NULL); + _x_assert(this->stream); a.maxlength = pa_bytes_per_second(&ss)*1; a.tlength = a.maxlength*9/10; a.prebuf = a.tlength/2; a.minreq = a.tlength/10; - pa_cvolume_set(&this->cvolume, pa_stream_get_sample_spec(this->stream)->channels, this->swvolume); pa_stream_connect_playback(this->stream, this->sink, &a, PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE, - &this->cvolume, NULL); + NULL, NULL); - wait_for_completion(this); + do { + xine_usec_sleep (100); - if (pa_stream_get_state(this->stream) != PA_STREAM_READY) { - xprintf (this->xine, XINE_VERBOSITY_DEBUG, "audio_pulse_out: Failed to connect to server: %s\n", - pa_strerror(pa_context_errno(this->context))); + streamstate = pa_stream_get_state(this->stream); + } while (streamstate < PA_STREAM_READY); + + pthread_mutex_unlock(&this->pa_class->pa_mutex); + + if (streamstate != PA_STREAM_READY) { + xprintf (this->xine, XINE_VERBOSITY_LOG, "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->lock); - this->frames_written = 0; + this->ao_driver.set_property(this, AO_PROP_PCM_VOL, 100); + return this->sample_rate; fail: - pthread_mutex_unlock(&this->lock); + pa_threaded_mainloop_unlock(this->pa_class->mainloop); + pthread_mutex_unlock(&this->pa_class->pa_mutex); this_gen->close(this_gen); return 0; } @@ -262,22 +345,16 @@ static int ao_pulse_write(ao_driver_t *this_gen, int16_t *data, int size = num_frames * this->bytes_per_frame; int ret = 0; - assert(this->stream && this->context); - - pthread_mutex_lock(&this->lock); - - 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; - keep_alive(this); - while (!(l = pa_stream_writable_size(this->stream))) { - pthread_mutex_unlock(&this->lock); xine_usec_sleep (10000); - pthread_mutex_lock(&this->lock); - keep_alive(this); } if (l > size) @@ -286,16 +363,15 @@ static int ao_pulse_write(ao_driver_t *this_gen, int16_t *data, pa_stream_write(this->stream, data, l, NULL, 0, PA_SEEK_RELATIVE); data = (int16_t *) ((uint8_t*) data + l); size -= l; - - wait_for_completion(this); } this->frames_written += num_frames; if (pa_stream_get_state(this->stream) == PA_STREAM_READY) ret = 1; + + break; } - pthread_mutex_unlock(&this->lock); return ret; } @@ -307,20 +383,25 @@ static int ao_pulse_delay (ao_driver_t *this_gen) pa_usec_t latency = 0; int delay_frames; - pthread_mutex_lock(&this->lock); + if ( ! this->stream ) return this->frames_written; - for (;;) { - if (pa_stream_get_latency(this->stream, &latency, NULL) >= 0) - break; + pthread_mutex_lock(&this->pa_class->pa_mutex); - if (pa_context_errno(this->context) != PA_ERR_NODATA) { - /* error */ - } - keep_alive(this); - } + if (pa_stream_get_latency(this->stream, &latency, NULL) < 0) { + pa_context_unref(this->pa_class->context); + this->pa_class->context = NULL; - pthread_mutex_unlock(&this->lock); + 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); @@ -334,26 +415,19 @@ static void ao_pulse_close(ao_driver_t *this_gen) { pulse_driver_t *this = (pulse_driver_t *) this_gen; - pthread_mutex_lock(&this->lock); if (this->stream) { + pthread_mutex_lock(&this->pa_class->pa_mutex); + if (pa_stream_get_state(this->stream) == PA_STREAM_READY) - wait_for_operation(this, pa_stream_drain(this->stream, NULL, NULL)); + wait_for_operation(this, pa_stream_drain(this->stream, __xine_pa_stream_success_callback, this)); pa_stream_disconnect(this->stream); pa_stream_unref(this->stream); this->stream = NULL; - } - if (this->context) { - pa_context_disconnect(this->context); - pa_context_unref(this->context); - this->context = NULL; - } + pa_context_unref(this->pa_class->context); - if (this->mainloop) { - pa_mainloop_free(this->mainloop); - this->mainloop = NULL; + pthread_mutex_unlock(&this->pa_class->pa_mutex); } - pthread_mutex_unlock(&this->lock); } static uint32_t ao_pulse_get_capabilities (ao_driver_t *this_gen) { @@ -368,101 +442,124 @@ static void ao_pulse_exit(ao_driver_t *this_gen) free (this); } -/** A callback function that is called when the - * pa_context_get_sink_input_info() operation completes. Saves the - * volume field of the specified structure to the global variable volume. */ -static void info_func(struct pa_context *c, const struct pa_sink_input_info *i, int is_last, void *userdata) { - - pulse_driver_t *this = (pulse_driver_t *) userdata; - if (is_last < 0) { - xprintf (this->xine, XINE_VERBOSITY_DEBUG, "audio_pulse_out: Failed to get sink input info: %s\n", - pa_strerror(pa_context_errno(this->context))); - return; - } - - if (!i) - return; - - this->cvolume = i->volume; -} - - static int ao_pulse_get_property (ao_driver_t *this_gen, int property) { pulse_driver_t *this = (pulse_driver_t *) this_gen; + int result = 0; + + if ( ! this->stream || ! this->pa_class->context ) + return 0; + pthread_mutex_lock(&this->pa_class->pa_mutex); + switch(property) { case AO_PROP_PCM_VOL: case AO_PROP_MIXER_VOL: - pthread_mutex_lock(&this->lock); - if( this->stream && this->context ) - wait_for_operation(this, - pa_context_get_sink_input_info(this->context, pa_stream_get_index(this->stream), info_func, this)); - pthread_mutex_unlock(&this->lock); - return (int) (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: + result = pa_cvolume_is_muted(&this->cvolume); break; } - return 0; + pthread_mutex_unlock(&this->pa_class->pa_mutex); + + return result; } static int ao_pulse_set_property (ao_driver_t *this_gen, int property, int value) { pulse_driver_t *this = (pulse_driver_t *) this_gen; + int result = ~value; + + if ( ! this->stream || ! this->pa_class->context ) + return result; + + pthread_mutex_lock(&this->pa_class->pa_mutex); switch(property) { case AO_PROP_PCM_VOL: case AO_PROP_MIXER_VOL: - pthread_mutex_lock(&this->lock); this->swvolume = pa_sw_volume_from_linear((double)value/100.0); - if( this->stream && this->context ) { - pa_cvolume_set(&this->cvolume, pa_stream_get_sample_spec(this->stream)->channels, this->swvolume); - wait_for_operation(this, - pa_context_set_sink_input_volume(this->context, pa_stream_get_index(this->stream), - &this->cvolume, NULL, NULL)); - } - pthread_mutex_unlock(&this->lock); + pa_cvolume_set(&this->cvolume, pa_stream_get_sample_spec(this->stream)->channels, this->swvolume); + wait_for_operation(this, + pa_context_set_sink_input_volume(this->pa_class->context, pa_stream_get_index(this->stream), + &this->cvolume, __xine_pa_context_success_callback, this)); + + result = value; break; + case AO_PROP_MUTE_VOL: + if ( value ) + pa_cvolume_mute(&this->cvolume, pa_stream_get_sample_spec(this->stream)->channels); + else + pa_cvolume_set(&this->cvolume, pa_stream_get_sample_spec(this->stream)->channels, this->swvolume); + + wait_for_operation(this, + pa_context_set_sink_input_volume(this->pa_class->context, pa_stream_get_index(this->stream), + &this->cvolume, __xine_pa_context_success_callback, this)); + + result = value; break; } - return 0; + pthread_mutex_unlock(&this->pa_class->pa_mutex); + + return result; } static int ao_pulse_ctrl(ao_driver_t *this_gen, int cmd, ...) { pulse_driver_t *this = (pulse_driver_t *) this_gen; - pthread_mutex_lock(&this->lock); + if ( ! this->stream ) return 0; + switch (cmd) { case AO_CTRL_PLAY_PAUSE: - assert(this->stream && this->context ); + _x_assert(this->stream && this->pa_class->context ); + + pthread_mutex_lock(&this->pa_class->pa_mutex); if(pa_stream_get_state(this->stream) == PA_STREAM_READY) - wait_for_operation(this,pa_stream_cork(this->stream, 1, NULL, NULL)); + wait_for_operation(this,pa_stream_cork(this->stream, 1, __xine_pa_stream_success_callback, this)); + pthread_mutex_unlock(&this->pa_class->pa_mutex); + break; case AO_CTRL_PLAY_RESUME: - assert(this->stream && this->context); + _x_assert(this->stream && this->pa_class->context); + + pthread_mutex_lock(&this->pa_class->pa_mutex); if(pa_stream_get_state(this->stream) == PA_STREAM_READY) { struct pa_operation *o2, *o1; - o1 = pa_stream_prebuf(this->stream, NULL, NULL); - o2 = pa_stream_cork(this->stream, 0, NULL, NULL); - assert(o1 && o2); - wait_for_operation(this,o1); - wait_for_operation(this,o2); - wait_for_completion(this); + o1 = pa_stream_prebuf(this->stream, __xine_pa_stream_success_callback, this); + _x_assert(o1); wait_for_operation(this, o1); + + o2 = pa_stream_cork(this->stream, 0, __xine_pa_stream_success_callback, this); + _x_assert(o2); wait_for_operation(this,o2); } + pthread_mutex_unlock(&this->pa_class->pa_mutex); + break; case AO_CTRL_FLUSH_BUFFERS: - assert(this->stream && this->context); + _x_assert(this->stream && this->pa_class->context); + + pthread_mutex_lock(&this->pa_class->pa_mutex); if(pa_stream_get_state(this->stream) == PA_STREAM_READY) - wait_for_operation(this,pa_stream_flush(this->stream, NULL, NULL)); + wait_for_operation(this,pa_stream_flush(this->stream, __xine_pa_stream_success_callback, this)); + pthread_mutex_unlock(&this->pa_class->pa_mutex); + this->frames_written = 0; + break; } - pthread_mutex_unlock(&this->lock); return 0; } @@ -470,7 +567,6 @@ 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; lprintf ("audio_pulse_out: open_plugin called\n"); @@ -490,7 +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->swvolume = PA_VOLUME_NORM; + this->host = NULL; + this->sink = NULL; this->ao_driver.get_capabilities = ao_pulse_get_capabilities; this->ao_driver.get_property = ao_pulse_get_property; @@ -514,35 +611,27 @@ 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", this->host ? this->host : "(null)", this->sink ? this->sink : "(null)"); - pthread_mutex_init (&this->lock, NULL); - - /* test pulseaudio connection */ - if( this->ao_driver.open(&this->ao_driver, 16, 44100, AO_CAP_MODE_STEREO) != 0 ) { - this->ao_driver.close(&this->ao_driver); - } else { - free(this); - xprintf (class->xine, XINE_VERBOSITY_DEBUG, "audio_pulse_out: open_plugin failed.\n"); - return NULL; - } + this->pa_class = class; return &this->ao_driver; + + fail: + 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; } /* @@ -561,6 +650,15 @@ static void dispose_class (audio_driver_class_t *this_gen) { pulse_class_t *this = (pulse_class_t *) this_gen; + pthread_mutex_lock(&this->pa_mutex); + + if ( this->context ) + pa_context_unref(this->context); + + pa_threaded_mainloop_stop(this->mainloop); + pa_threaded_mainloop_free(this->mainloop); + + pthread_mutex_destroy(&this->pa_mutex); free (this); } @@ -581,6 +679,18 @@ static void *init_class (xine_t *xine, void *data) { this->xine = xine; + pthread_mutex_init(&this->pa_mutex, NULL); + pthread_mutex_lock(&this->pa_mutex); + + this->mainloop = pa_threaded_mainloop_new(); + _x_assert(this->mainloop); + + pa_threaded_mainloop_start(this->mainloop); + + this->context = NULL; + + pthread_mutex_unlock(&this->pa_mutex); + return this; } @@ -598,3 +708,4 @@ const plugin_info_t xine_plugin_info[] EXPORTED = { { PLUGIN_NONE, 0, "", 0, NULL, NULL } }; + |