Commit 43731984 authored by Rémi Denis-Courmont's avatar Rémi Denis-Courmont

Revert back to one PulseAudio mainloop per context, kill libvlcpulse

This fixes deadlocks between input and output.
parent ed17149b
...@@ -124,10 +124,12 @@ if HAVE_ALSA ...@@ -124,10 +124,12 @@ if HAVE_ALSA
libvlc_LTLIBRARIES += libaccess_alsa_plugin.la libvlc_LTLIBRARIES += libaccess_alsa_plugin.la
endif endif
libpulsesrc_plugin_la_SOURCES = pulse.c libpulsesrc_plugin_la_SOURCES = \
../audio_output/vlcpulse.c \
../audio_output/vlcpulse.h \
pulse.c
libpulsesrc_plugin_la_CFLAGS= $(AM_CFLAGS) $(PULSE_CFLAGS) libpulsesrc_plugin_la_CFLAGS= $(AM_CFLAGS) $(PULSE_CFLAGS)
libpulsesrc_plugin_la_LIBADD = $(AM_LIBADD) $(PULSE_LIBS) \ libpulsesrc_plugin_la_LIBADD = $(AM_LIBADD) $(PULSE_LIBS)
../../src/libvlcpulse.la
libpulsesrc_plugin_la_DEPENDENCIES = libpulsesrc_plugin_la_DEPENDENCIES =
if HAVE_PULSE if HAVE_PULSE
libvlc_LTLIBRARIES += libpulsesrc_plugin.la libvlc_LTLIBRARIES += libpulsesrc_plugin.la
......
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
#include <vlc_demux.h> #include <vlc_demux.h>
#include <vlc_plugin.h> #include <vlc_plugin.h>
#include <pulse/pulseaudio.h> #include <pulse/pulseaudio.h>
#include <vlc_pulse.h> #include "../audio_output/vlcpulse.h"
static int Open(vlc_object_t *); static int Open(vlc_object_t *);
static void Close(vlc_object_t *); static void Close(vlc_object_t *);
...@@ -47,6 +47,7 @@ struct demux_sys_t ...@@ -47,6 +47,7 @@ struct demux_sys_t
{ {
pa_stream *stream; /**< PulseAudio playback stream object */ pa_stream *stream; /**< PulseAudio playback stream object */
pa_context *context; /**< PulseAudio connection context */ pa_context *context; /**< PulseAudio connection context */
pa_threaded_mainloop *mainloop; /**< PulseAudio thread */
es_out_id_t *es; es_out_id_t *es;
bool discontinuity; /**< The next block will not follow the last one */ bool discontinuity; /**< The next block will not follow the last one */
...@@ -57,15 +58,16 @@ struct demux_sys_t ...@@ -57,15 +58,16 @@ struct demux_sys_t
/* Stream helpers */ /* Stream helpers */
static void stream_state_cb(pa_stream *s, void *userdata) static void stream_state_cb(pa_stream *s, void *userdata)
{ {
pa_threaded_mainloop *mainloop = userdata;
switch (pa_stream_get_state(s)) { switch (pa_stream_get_state(s)) {
case PA_STREAM_READY: case PA_STREAM_READY:
case PA_STREAM_FAILED: case PA_STREAM_FAILED:
case PA_STREAM_TERMINATED: case PA_STREAM_TERMINATED:
vlc_pa_signal(0); pa_threaded_mainloop_signal(mainloop, 0);
default: default:
break; break;
} }
(void) userdata;
} }
static void stream_moved_cb(pa_stream *s, void *userdata) static void stream_moved_cb(pa_stream *s, void *userdata)
...@@ -109,14 +111,14 @@ static void stream_underflow_cb(pa_stream *s, void *userdata) ...@@ -109,14 +111,14 @@ static void stream_underflow_cb(pa_stream *s, void *userdata)
(void) s; (void) s;
} }
static int stream_wait(pa_stream *stream) static int stream_wait(pa_stream *stream, pa_threaded_mainloop *mainloop)
{ {
pa_stream_state_t state; pa_stream_state_t state;
while ((state = pa_stream_get_state(stream)) != PA_STREAM_READY) { while ((state = pa_stream_get_state(stream)) != PA_STREAM_READY) {
if (state == PA_STREAM_FAILED || state == PA_STREAM_TERMINATED) if (state == PA_STREAM_FAILED || state == PA_STREAM_TERMINATED)
return -1; return -1;
vlc_pa_wait(); pa_threaded_mainloop_wait(mainloop);
} }
return 0; return 0;
} }
...@@ -210,17 +212,17 @@ static int Open(vlc_object_t *obj) ...@@ -210,17 +212,17 @@ static int Open(vlc_object_t *obj)
{ {
demux_t *demux = (demux_t *)obj; demux_t *demux = (demux_t *)obj;
pa_context *ctx = vlc_pa_connect(obj);
if (ctx == NULL)
return VLC_EGENERIC;
demux_sys_t *sys = malloc(sizeof (*sys)); demux_sys_t *sys = malloc(sizeof (*sys));
if (unlikely(sys == NULL)) { if (unlikely(sys == NULL))
vlc_pa_disconnect (obj, ctx);
return VLC_ENOMEM; return VLC_ENOMEM;
sys->context = vlc_pa_connect(obj, &sys->mainloop);
if (sys->context == NULL) {
free(sys);
return VLC_EGENERIC;
} }
sys->stream = NULL; sys->stream = NULL;
sys->context = ctx;
sys->es = NULL; sys->es = NULL;
sys->discontinuity = false; sys->discontinuity = false;
sys->caching = INT64_C(1000) * var_InheritInteger(obj, "live-caching"); sys->caching = INT64_C(1000) * var_InheritInteger(obj, "live-caching");
...@@ -254,13 +256,13 @@ static int Open(vlc_object_t *obj) ...@@ -254,13 +256,13 @@ static int Open(vlc_object_t *obj)
/* Create record stream */ /* Create record stream */
pa_stream *s; pa_stream *s;
vlc_pa_lock(); pa_threaded_mainloop_lock(sys->mainloop);
s = pa_stream_new(ctx, "audio stream", &ss, &map); s = pa_stream_new(sys->context, "audio stream", &ss, &map);
if (s == NULL) if (s == NULL)
goto error; goto error;
sys->stream = s; sys->stream = s;
pa_stream_set_state_callback(s, stream_state_cb, NULL); pa_stream_set_state_callback(s, stream_state_cb, sys->mainloop);
pa_stream_set_read_callback(s, stream_read_cb, demux); pa_stream_set_read_callback(s, stream_read_cb, demux);
pa_stream_set_moved_callback(s, stream_moved_cb, demux); pa_stream_set_moved_callback(s, stream_moved_cb, demux);
pa_stream_set_overflow_callback(s, stream_overflow_cb, demux); pa_stream_set_overflow_callback(s, stream_overflow_cb, demux);
...@@ -269,8 +271,8 @@ static int Open(vlc_object_t *obj) ...@@ -269,8 +271,8 @@ static int Open(vlc_object_t *obj)
pa_stream_set_underflow_callback(s, stream_underflow_cb, demux); pa_stream_set_underflow_callback(s, stream_underflow_cb, demux);
if (pa_stream_connect_record(s, NULL, &attr, flags) < 0 if (pa_stream_connect_record(s, NULL, &attr, flags) < 0
|| stream_wait(s)) { || stream_wait(s, sys->mainloop)) {
vlc_pa_error(obj, "cannot connect record stream", ctx); vlc_pa_error(obj, "cannot connect record stream", sys->context);
goto error; goto error;
} }
...@@ -289,14 +291,14 @@ static int Open(vlc_object_t *obj) ...@@ -289,14 +291,14 @@ static int Open(vlc_object_t *obj)
const struct pa_buffer_attr *pba = pa_stream_get_buffer_attr(s); const struct pa_buffer_attr *pba = pa_stream_get_buffer_attr(s);
msg_Dbg(obj, "using buffer metrics: maxlength=%"PRIu32", fragsize=%"PRIu32, msg_Dbg(obj, "using buffer metrics: maxlength=%"PRIu32", fragsize=%"PRIu32,
pba->maxlength, pba->fragsize); pba->maxlength, pba->fragsize);
vlc_pa_unlock(); pa_threaded_mainloop_unlock(sys->mainloop);
demux->pf_demux = NULL; demux->pf_demux = NULL;
demux->pf_control = Control; demux->pf_control = Control;
return VLC_SUCCESS; return VLC_SUCCESS;
error: error:
vlc_pa_unlock(); pa_threaded_mainloop_unlock(sys->mainloop);
Close(obj); Close(obj);
return VLC_EGENERIC; return VLC_EGENERIC;
} }
...@@ -305,11 +307,10 @@ static void Close (vlc_object_t *obj) ...@@ -305,11 +307,10 @@ static void Close (vlc_object_t *obj)
{ {
demux_t *demux = (demux_t *)obj; demux_t *demux = (demux_t *)obj;
demux_sys_t *sys = demux->p_sys; demux_sys_t *sys = demux->p_sys;
pa_context *ctx = sys->context;
pa_stream *s = sys->stream; pa_stream *s = sys->stream;
if (likely(s != NULL)) { if (likely(s != NULL)) {
vlc_pa_lock(); pa_threaded_mainloop_lock(sys->mainloop);
pa_stream_disconnect(s); pa_stream_disconnect(s);
pa_stream_set_state_callback(s, NULL, NULL); pa_stream_set_state_callback(s, NULL, NULL);
pa_stream_set_read_callback(s, NULL, NULL); pa_stream_set_read_callback(s, NULL, NULL);
...@@ -319,9 +320,9 @@ static void Close (vlc_object_t *obj) ...@@ -319,9 +320,9 @@ static void Close (vlc_object_t *obj)
pa_stream_set_suspended_callback(s, NULL, NULL); pa_stream_set_suspended_callback(s, NULL, NULL);
pa_stream_set_underflow_callback(s, NULL, NULL); pa_stream_set_underflow_callback(s, NULL, NULL);
pa_stream_unref(s); pa_stream_unref(s);
vlc_pa_unlock(); pa_threaded_mainloop_unlock(sys->mainloop);
} }
vlc_pa_disconnect(obj, ctx); vlc_pa_disconnect(obj, sys->context, sys->mainloop);
free(sys); free(sys);
} }
...@@ -38,10 +38,9 @@ if HAVE_ALSA ...@@ -38,10 +38,9 @@ if HAVE_ALSA
libvlc_LTLIBRARIES += libalsa_plugin.la libvlc_LTLIBRARIES += libalsa_plugin.la
endif endif
libpulse_plugin_la_SOURCES = pulse.c libpulse_plugin_la_SOURCES = vlcpulse.c vlcpulse.h pulse.c
libpulse_plugin_la_CFLAGS = $(AM_CFLAGS) $(PULSE_CFLAGS) libpulse_plugin_la_CFLAGS = $(AM_CFLAGS) $(PULSE_CFLAGS)
libpulse_plugin_la_LIBADD = $(AM_LIBADD) $(PULSE_LIBS) \ libpulse_plugin_la_LIBADD = $(AM_LIBADD) $(PULSE_LIBS) $(LIBM)
$(LIBM) ../../src/libvlcpulse.la
libpulse_plugin_la_DEPENDENCIES = libpulse_plugin_la_DEPENDENCIES =
if HAVE_PULSE if HAVE_PULSE
libvlc_LTLIBRARIES += libpulse_plugin.la libvlc_LTLIBRARIES += libpulse_plugin.la
......
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
#include <vlc_cpu.h> #include <vlc_cpu.h>
#include <pulse/pulseaudio.h> #include <pulse/pulseaudio.h>
#include <vlc_pulse.h> #include "vlcpulse.h"
#if !PA_CHECK_VERSION(0,9,22) #if !PA_CHECK_VERSION(0,9,22)
# include <vlc_xlib.h> # include <vlc_xlib.h>
#endif #endif
...@@ -60,7 +60,7 @@ vlc_module_end () ...@@ -60,7 +60,7 @@ vlc_module_end ()
/* NOTE: /* NOTE:
* Be careful what you do when the PulseAudio mainloop is held, which is to say * Be careful what you do when the PulseAudio mainloop is held, which is to say
* within PulseAudio callbacks, or after vlc_pa_lock(). * within PulseAudio callbacks, or after pa_threaded_mainloop_lock().
* In particular, a VLC variable callback cannot be triggered nor deleted with * In particular, a VLC variable callback cannot be triggered nor deleted with
* the PulseAudio mainloop lock held, if the callback acquires the lock. */ * the PulseAudio mainloop lock held, if the callback acquires the lock. */
...@@ -68,6 +68,7 @@ struct aout_sys_t ...@@ -68,6 +68,7 @@ struct aout_sys_t
{ {
pa_stream *stream; /**< PulseAudio playback stream object */ pa_stream *stream; /**< PulseAudio playback stream object */
pa_context *context; /**< PulseAudio connection context */ pa_context *context; /**< PulseAudio connection context */
pa_threaded_mainloop *mainloop; /**< PulseAudio thread */
pa_time_event *trigger; /**< Deferred stream trigger */ pa_time_event *trigger; /**< Deferred stream trigger */
pa_volume_t base_volume; /**< 0dB reference volume */ pa_volume_t base_volume; /**< 0dB reference volume */
pa_cvolume cvolume; /**< actual sink input volume */ pa_cvolume cvolume; /**< actual sink input volume */
...@@ -177,20 +178,6 @@ static void sink_info_cb(pa_context *c, const pa_sink_info *i, int eol, ...@@ -177,20 +178,6 @@ static void sink_info_cb(pa_context *c, const pa_sink_info *i, int eol,
/*** Latency management and lip synchronization ***/ /*** Latency management and lip synchronization ***/
static mtime_t vlc_pa_get_latency(audio_output_t *aout,
pa_context *ctx, pa_stream *s)
{
pa_usec_t latency;
int negative;
if (pa_stream_get_latency(s, &latency, &negative)) {
if (pa_context_errno (ctx) != PA_ERR_NODATA)
vlc_pa_error(aout, "unknown latency", ctx);
return VLC_TS_INVALID;
}
return negative ? -latency : +latency;
}
static void stream_reset_sync(pa_stream *s, audio_output_t *aout) static void stream_reset_sync(pa_stream *s, audio_output_t *aout)
{ {
aout_sys_t *sys = aout->sys; aout_sys_t *sys = aout->sys;
...@@ -211,7 +198,7 @@ static void stream_start(pa_stream *s, audio_output_t *aout) ...@@ -211,7 +198,7 @@ static void stream_start(pa_stream *s, audio_output_t *aout)
pa_operation *op; pa_operation *op;
if (sys->trigger != NULL) { if (sys->trigger != NULL) {
vlc_pa_rttime_free(sys->trigger); vlc_pa_rttime_free(sys->mainloop, sys->trigger);
sys->trigger = NULL; sys->trigger = NULL;
} }
...@@ -229,7 +216,7 @@ static void stream_stop(pa_stream *s, audio_output_t *aout) ...@@ -229,7 +216,7 @@ static void stream_stop(pa_stream *s, audio_output_t *aout)
pa_operation *op; pa_operation *op;
if (sys->trigger != NULL) { if (sys->trigger != NULL) {
vlc_pa_rttime_free(sys->trigger); vlc_pa_rttime_free(sys->mainloop, sys->trigger);
sys->trigger = NULL; sys->trigger = NULL;
} }
...@@ -363,15 +350,16 @@ static void stream_latency_cb(pa_stream *s, void *userdata) ...@@ -363,15 +350,16 @@ static void stream_latency_cb(pa_stream *s, void *userdata)
/*** Stream helpers ***/ /*** Stream helpers ***/
static void stream_state_cb(pa_stream *s, void *userdata) static void stream_state_cb(pa_stream *s, void *userdata)
{ {
pa_threaded_mainloop *mainloop = userdata;
switch (pa_stream_get_state(s)) { switch (pa_stream_get_state(s)) {
case PA_STREAM_READY: case PA_STREAM_READY:
case PA_STREAM_FAILED: case PA_STREAM_FAILED:
case PA_STREAM_TERMINATED: case PA_STREAM_TERMINATED:
vlc_pa_signal(0); pa_threaded_mainloop_signal(mainloop, 0);
default: default:
break; break;
} }
(void) userdata;
} }
static void stream_event_cb(pa_stream *s, const char *name, pa_proplist *pl, static void stream_event_cb(pa_stream *s, const char *name, pa_proplist *pl,
...@@ -456,14 +444,14 @@ static void stream_underflow_cb(pa_stream *s, void *userdata) ...@@ -456,14 +444,14 @@ static void stream_underflow_cb(pa_stream *s, void *userdata)
stream_reset_sync(s, aout); stream_reset_sync(s, aout);
} }
static int stream_wait(pa_stream *stream) static int stream_wait(pa_stream *stream, pa_threaded_mainloop *mainloop)
{ {
pa_stream_state_t state; pa_stream_state_t state;
while ((state = pa_stream_get_state(stream)) != PA_STREAM_READY) { while ((state = pa_stream_get_state(stream)) != PA_STREAM_READY) {
if (state == PA_STREAM_FAILED || state == PA_STREAM_TERMINATED) if (state == PA_STREAM_FAILED || state == PA_STREAM_TERMINATED)
return -1; return -1;
vlc_pa_wait(); pa_threaded_mainloop_wait(mainloop);
} }
return 0; return 0;
} }
...@@ -533,7 +521,7 @@ static void Play(audio_output_t *aout, block_t *block) ...@@ -533,7 +521,7 @@ static void Play(audio_output_t *aout, block_t *block)
* output FIFO lock while the PulseAudio threaded main loop lock is held * output FIFO lock while the PulseAudio threaded main loop lock is held
* (including from PulseAudio stream callbacks). Otherwise lock inversion * (including from PulseAudio stream callbacks). Otherwise lock inversion
* will take place, and sooner or later a deadlock. */ * will take place, and sooner or later a deadlock. */
vlc_pa_lock(); pa_threaded_mainloop_lock(sys->mainloop);
sys->pts = pts; sys->pts = pts;
if (pa_stream_is_corked(s) > 0) if (pa_stream_is_corked(s) > 0)
...@@ -552,7 +540,7 @@ static void Play(audio_output_t *aout, block_t *block) ...@@ -552,7 +540,7 @@ static void Play(audio_output_t *aout, block_t *block)
block_Release(block); block_Release(block);
} }
vlc_pa_unlock(); pa_threaded_mainloop_unlock(sys->mainloop);
} }
/** /**
...@@ -563,7 +551,7 @@ static void Pause(audio_output_t *aout, bool paused, mtime_t date) ...@@ -563,7 +551,7 @@ static void Pause(audio_output_t *aout, bool paused, mtime_t date)
aout_sys_t *sys = aout->sys; aout_sys_t *sys = aout->sys;
pa_stream *s = sys->stream; pa_stream *s = sys->stream;
vlc_pa_lock(); pa_threaded_mainloop_lock(sys->mainloop);
if (paused) { if (paused) {
sys->paused = date; sys->paused = date;
...@@ -577,7 +565,7 @@ static void Pause(audio_output_t *aout, bool paused, mtime_t date) ...@@ -577,7 +565,7 @@ static void Pause(audio_output_t *aout, bool paused, mtime_t date)
stream_resync(aout, s); stream_resync(aout, s);
} }
vlc_pa_unlock(); pa_threaded_mainloop_unlock(sys->mainloop);
} }
/** /**
...@@ -589,7 +577,7 @@ static void Flush(audio_output_t *aout, bool wait) ...@@ -589,7 +577,7 @@ static void Flush(audio_output_t *aout, bool wait)
pa_stream *s = sys->stream; pa_stream *s = sys->stream;
pa_operation *op; pa_operation *op;
vlc_pa_lock(); pa_threaded_mainloop_lock(sys->mainloop);
if (wait) if (wait)
op = pa_stream_drain(s, NULL, NULL); op = pa_stream_drain(s, NULL, NULL);
...@@ -598,7 +586,7 @@ static void Flush(audio_output_t *aout, bool wait) ...@@ -598,7 +586,7 @@ static void Flush(audio_output_t *aout, bool wait)
op = pa_stream_flush(s, NULL, NULL); op = pa_stream_flush(s, NULL, NULL);
if (op != NULL) if (op != NULL)
pa_operation_unref(op); pa_operation_unref(op);
vlc_pa_unlock(); pa_threaded_mainloop_unlock(sys->mainloop);
} }
static int VolumeSet(audio_output_t *aout, float vol, bool mute) static int VolumeSet(audio_output_t *aout, float vol, bool mute)
...@@ -623,14 +611,14 @@ static int VolumeSet(audio_output_t *aout, float vol, bool mute) ...@@ -623,14 +611,14 @@ static int VolumeSet(audio_output_t *aout, float vol, bool mute)
assert(pa_cvolume_valid(&cvolume)); assert(pa_cvolume_valid(&cvolume));
vlc_pa_lock(); pa_threaded_mainloop_lock(sys->mainloop);
op = pa_context_set_sink_input_volume(sys->context, idx, &cvolume, NULL, NULL); op = pa_context_set_sink_input_volume(sys->context, idx, &cvolume, NULL, NULL);
if (likely(op != NULL)) if (likely(op != NULL))
pa_operation_unref(op); pa_operation_unref(op);
op = pa_context_set_sink_input_mute(sys->context, idx, mute, NULL, NULL); op = pa_context_set_sink_input_mute(sys->context, idx, mute, NULL, NULL);
if (likely(op != NULL)) if (likely(op != NULL))
pa_operation_unref(op); pa_operation_unref(op);
vlc_pa_unlock(); pa_threaded_mainloop_unlock(sys->mainloop);
return 0; return 0;
} }
...@@ -647,7 +635,7 @@ static int StreamMove(vlc_object_t *obj, const char *varname, vlc_value_t old, ...@@ -647,7 +635,7 @@ static int StreamMove(vlc_object_t *obj, const char *varname, vlc_value_t old,
(void) varname; (void) old; (void) varname; (void) old;
vlc_pa_lock(); pa_threaded_mainloop_lock(sys->mainloop);
op = pa_context_move_sink_input_by_index(sys->context, idx, sink_idx, op = pa_context_move_sink_input_by_index(sys->context, idx, sink_idx,
NULL, NULL); NULL, NULL);
if (likely(op != NULL)) { if (likely(op != NULL)) {
...@@ -655,7 +643,7 @@ static int StreamMove(vlc_object_t *obj, const char *varname, vlc_value_t old, ...@@ -655,7 +643,7 @@ static int StreamMove(vlc_object_t *obj, const char *varname, vlc_value_t old,
msg_Dbg(aout, "moving to sink %"PRIu32, sink_idx); msg_Dbg(aout, "moving to sink %"PRIu32, sink_idx);
} else } else
vlc_pa_error(obj, "cannot move sink", sys->context); vlc_pa_error(obj, "cannot move sink", sys->context);
vlc_pa_unlock(); pa_threaded_mainloop_unlock(sys->mainloop);
return (op != NULL) ? VLC_SUCCESS : VLC_EGENERIC; return (op != NULL) ? VLC_SUCCESS : VLC_EGENERIC;
} }
...@@ -828,7 +816,7 @@ static int Open(vlc_object_t *obj) ...@@ -828,7 +816,7 @@ static int Open(vlc_object_t *obj)
if (unlikely(sys == NULL)) if (unlikely(sys == NULL))
return VLC_ENOMEM; return VLC_ENOMEM;
pa_context *ctx = vlc_pa_connect (obj); pa_context *ctx = vlc_pa_connect(obj, &sys->mainloop);
if (ctx == NULL) if (ctx == NULL)
{ {
free (sys); free (sys);
...@@ -881,13 +869,13 @@ static int Open(vlc_object_t *obj) ...@@ -881,13 +869,13 @@ static int Open(vlc_object_t *obj)
/* Create a playback stream */ /* Create a playback stream */
pa_stream *s; pa_stream *s;
vlc_pa_lock(); pa_threaded_mainloop_lock(sys->mainloop);
s = pa_stream_new_extended(ctx, "audio stream", formatv, formatc, NULL); s = pa_stream_new_extended(ctx, "audio stream", formatv, formatc, NULL);
for (unsigned i = 0; i < formatc; i++) for (unsigned i = 0; i < formatc; i++)
pa_format_info_free(formatv[i]); pa_format_info_free(formatv[i]);
#else #else
vlc_pa_lock(); pa_threaded_mainloop_lock(sys->mainloop);
pa_stream *s = pa_stream_new(ctx, "audio stream", &ss, &map); pa_stream *s = pa_stream_new(ctx, "audio stream", &ss, &map);
#endif #endif
if (s == NULL) { if (s == NULL) {
...@@ -895,7 +883,7 @@ static int Open(vlc_object_t *obj) ...@@ -895,7 +883,7 @@ static int Open(vlc_object_t *obj)
goto fail; goto fail;
} }
sys->stream = s; sys->stream = s;
pa_stream_set_state_callback(s, stream_state_cb, NULL); pa_stream_set_state_callback(s, stream_state_cb, sys->mainloop);
pa_stream_set_event_callback(s, stream_event_cb, aout); pa_stream_set_event_callback(s, stream_event_cb, aout);
pa_stream_set_latency_update_callback(s, stream_latency_cb, aout); pa_stream_set_latency_update_callback(s, stream_latency_cb, aout);
pa_stream_set_moved_callback(s, stream_moved_cb, aout); pa_stream_set_moved_callback(s, stream_moved_cb, aout);
...@@ -905,7 +893,7 @@ static int Open(vlc_object_t *obj) ...@@ -905,7 +893,7 @@ static int Open(vlc_object_t *obj)
pa_stream_set_underflow_callback(s, stream_underflow_cb, aout); pa_stream_set_underflow_callback(s, stream_underflow_cb, aout);
if (pa_stream_connect_playback(s, NULL, &attr, flags, NULL, NULL) < 0 if (pa_stream_connect_playback(s, NULL, &attr, flags, NULL, NULL) < 0
|| stream_wait(s)) { || stream_wait(s, sys->mainloop)) {
vlc_pa_error(obj, "stream connection failure", ctx); vlc_pa_error(obj, "stream connection failure", ctx);
goto fail; goto fail;
} }
...@@ -940,7 +928,7 @@ static int Open(vlc_object_t *obj) ...@@ -940,7 +928,7 @@ static int Open(vlc_object_t *obj)
if (op != NULL) if (op != NULL)
pa_operation_unref(op); pa_operation_unref(op);
stream_moved_cb(s, aout); stream_moved_cb(s, aout);
vlc_pa_unlock(); pa_threaded_mainloop_unlock(sys->mainloop);
aout->format.i_format = format; aout->format.i_format = format;
aout->pf_play = Play; aout->pf_play = Play;
...@@ -950,7 +938,7 @@ static int Open(vlc_object_t *obj) ...@@ -950,7 +938,7 @@ static int Open(vlc_object_t *obj)
return VLC_SUCCESS; return VLC_SUCCESS;
fail: fail:
vlc_pa_unlock(); pa_threaded_mainloop_unlock(sys->mainloop);
Close(obj); Close(obj);
return VLC_EGENERIC; return VLC_EGENERIC;
} }
...@@ -970,9 +958,9 @@ static void Close (vlc_object_t *obj) ...@@ -970,9 +958,9 @@ static void Close (vlc_object_t *obj)
var_DelCallback (aout, "audio-device", StreamMove, s); var_DelCallback (aout, "audio-device", StreamMove, s);
var_Destroy (aout, "audio-device"); var_Destroy (aout, "audio-device");
vlc_pa_lock (); pa_threaded_mainloop_lock(sys->mainloop);
if (unlikely(sys->trigger != NULL)) if (unlikely(sys->trigger != NULL))
vlc_pa_rttime_free(sys->trigger); vlc_pa_rttime_free(sys->mainloop, sys->trigger);
pa_stream_disconnect(s); pa_stream_disconnect(s);
/* Clear all callbacks */ /* Clear all callbacks */
...@@ -986,9 +974,9 @@ static void Close (vlc_object_t *obj) ...@@ -986,9 +974,9 @@ static void Close (vlc_object_t *obj)
pa_stream_set_underflow_callback(s, NULL, NULL); pa_stream_set_underflow_callback(s, NULL, NULL);
pa_stream_unref(s); pa_stream_unref(s);
vlc_pa_unlock (); pa_threaded_mainloop_unlock(sys->mainloop);
} }
vlc_pa_disconnect(obj, ctx); vlc_pa_disconnect(obj, ctx, sys->mainloop);
free(sys); free(sys);
} }
...@@ -23,11 +23,10 @@ ...@@ -23,11 +23,10 @@
# include "config.h" # include "config.h"
#endif #endif
#define MODULE_STRING "pulse"
#include <vlc_common.h> #include <vlc_common.h>
#include <pulse/pulseaudio.h> #include <pulse/pulseaudio.h>
#include <vlc_pulse.h> #include "vlcpulse.h"
#include <assert.h> #include <assert.h>
#include <stdlib.h> #include <stdlib.h>
#include <locale.h> #include <locale.h>
...@@ -40,105 +39,22 @@ void vlc_pa_error (vlc_object_t *obj, const char *msg, pa_context *ctx) ...@@ -40,105 +39,22 @@ void vlc_pa_error (vlc_object_t *obj, const char *msg, pa_context *ctx)
msg_Err (obj, "%s: %s", msg, pa_strerror (pa_context_errno (ctx))); msg_Err (obj, "%s: %s", msg, pa_strerror (pa_context_errno (ctx)));
} }
static pa_threaded_mainloop *vlc_pa_mainloop;
static unsigned refs = 0;
static vlc_mutex_t lock = VLC_STATIC_MUTEX;
/**
* Creates and references the VLC PulseAudio threaded main loop.
* @return 0 on success, -1 on failure
*/
static int vlc_pa_mainloop_init (void)
{
vlc_mutex_lock (&lock);
if (refs == 0)
{
vlc_pa_mainloop = pa_threaded_mainloop_new ();
if (unlikely(vlc_pa_mainloop == NULL))
goto err;
if (pa_threaded_mainloop_start (vlc_pa_mainloop) < 0)
{
pa_threaded_mainloop_free (vlc_pa_mainloop);
goto err;
}
}
else
{
if (unlikely(refs >= UINT_MAX))
goto err;
}
refs++;
vlc_mutex_unlock (&lock);
return 0;
err:
vlc_mutex_unlock (&lock);
return -1;
}
/**
* Releases a reference to the VLC PulseAudio main loop.
*/
static void vlc_pa_mainloop_deinit (void)
{
vlc_mutex_lock (&lock);
assert (refs > 0);
if (--refs == 0)
{
pa_threaded_mainloop_stop (vlc_pa_mainloop);
pa_threaded_mainloop_free (vlc_pa_mainloop);
}
vlc_mutex_unlock (&lock);
}
/**
* Acquires the main loop lock.
*/
void vlc_pa_lock (void)
{
pa_threaded_mainloop_lock (vlc_pa_mainloop);
}
/**
* Releases the main loop lock.
*/
void vlc_pa_unlock (void)
{
pa_threaded_mainloop_unlock (vlc_pa_mainloop);
}
/**
* Signals the main loop.
*/
void vlc_pa_signal (int do_wait)
{
pa_threaded_mainloop_signal (vlc_pa_mainloop, do_wait);
}
/**
* Waits for the main loop to be signaled.
*/
void vlc_pa_wait (void)
{
pa_threaded_mainloop_wait (vlc_pa_mainloop);
}
static void context_state_cb (pa_context *ctx, void *userdata) static void context_state_cb (pa_context *ctx, void *userdata)
{ {
pa_threaded_mainloop *mainloop = userdata;
switch (pa_context_get_state(ctx)) switch (pa_context_get_state(ctx))
{ {
case PA_CONTEXT_READY: case PA_CONTEXT_READY:
case PA_CONTEXT_FAILED: case PA_CONTEXT_FAILED:
case PA_CONTEXT_TERMINATED: case PA_CONTEXT_TERMINATED:
vlc_pa_signal (0); pa_threaded_mainloop_signal (mainloop, 0);
default: default:
break; break;
} }
(void) userdata;
} }
static bool context_wait (pa_context *ctx) static bool context_wait (pa_context *ctx, pa_threaded_mainloop *mainloop)
{ {
pa_context_state_t state; pa_context_state_t state;
...@@ -146,7 +62,7 @@ static bool context_wait (pa_context *ctx) ...@@ -146,7 +62,7 @@ static bool context_wait (pa_context *ctx)
{ {
if (state == PA_CONTEXT_FAILED || state == PA_CONTEXT_TERMINATED) if (state == PA_CONTEXT_FAILED || state == PA_CONTEXT_TERMINATED)
return -1; return -1;
vlc_pa_wait (); pa_threaded_mainloop_wait (mainloop);
} }
return 0; return 0;
} }
...@@ -155,19 +71,25 @@ static bool context_wait (pa_context *ctx) ...@@ -155,19 +71,25 @@ static bool context_wait (pa_context *ctx)
* Initializes the PulseAudio main loop and connects to the PulseAudio server. * Initializes the PulseAudio main loop and connects to the PulseAudio server.
* @return a PulseAudio context on success, or NULL on error * @return a PulseAudio context on success, or NULL on error
*/ */
pa_context *vlc_pa_connect (vlc_object_t *obj) pa_context *vlc_pa_connect (vlc_object_t *obj, pa_threaded_mainloop **mlp)
{ {
if (unlikely(vlc_pa_mainloop_init ()))
return NULL;
msg_Dbg (obj, "using library version %s", pa_get_library_version ()); msg_Dbg (obj, "using library version %s", pa_get_library_version ());
msg_Dbg (obj, " (compiled with version %s, protocol %u)", msg_Dbg (obj, " (compiled with version %s, protocol %u)",
pa_get_headers_version (), PA_PROTOCOL_VERSION); pa_get_headers_version (), PA_PROTOCOL_VERSION);
char *ua = var_InheritString (obj, "user-agent"); /* Initialize main loop */
pa_context *ctx; pa_threaded_mainloop *mainloop = pa_threaded_mainloop_new ();
if (unlikely(mainloop == NULL))
return NULL;
if (pa_threaded_mainloop_start (mainloop) < 0)
{
pa_threaded_mainloop_free (mainloop);
return NULL;
}
/* Fill in context (client) properties */ /* Fill in context (client) properties */
char *ua = var_InheritString (obj, "user-agent");
pa_proplist *props = pa_proplist_new (); pa_proplist *props = pa_proplist_new ();
if (likely(props != NULL)) if (likely(props != NULL))
{ {
...@@ -208,18 +130,21 @@ pa_context *vlc_pa_connect (vlc_object_t *obj) ...@@ -208,18 +130,21 @@ pa_context *vlc_pa_connect (vlc_object_t *obj)
} }
/* Connect to PulseAudio daemon */ /* Connect to PulseAudio daemon */
vlc_pa_lock (); pa_context *ctx;
pa_mainloop_api *api;
ctx = pa_context_new_with_proplist (pa_threaded_mainloop_get_api (vlc_pa_mainloop), ua, props); pa_threaded_mainloop_lock (mainloop);
api = pa_threaded_mainloop_get_api (mainloop);
ctx = pa_context_new_with_proplist (api, ua, props);
free (ua); free (ua);
if (props != NULL) if (props != NULL)
pa_proplist_free(props); pa_proplist_free (props);
if (unlikely(ctx == NULL)) if (unlikely(ctx == NULL))
goto fail; goto fail;
pa_context_set_state_callback (ctx, context_state_cb, NULL); pa_context_set_state_callback (ctx, context_state_cb, mainloop);
if (pa_context_connect (ctx, NULL, 0, NULL) < 0 if (pa_context_connect (ctx, NULL, 0, NULL) < 0
|| context_wait (ctx)) || context_wait (ctx, mainloop))
{ {
vlc_pa_error (obj, "PulseAudio server connection failure", ctx); vlc_pa_error (obj, "PulseAudio server connection failure", ctx);
pa_context_unref (ctx); pa_context_unref (ctx);
...@@ -232,27 +157,31 @@ pa_context *vlc_pa_connect (vlc_object_t *obj) ...@@ -232,27 +157,31 @@ pa_context *vlc_pa_connect (vlc_object_t *obj)
pa_context_get_protocol_version (ctx), pa_context_get_protocol_version (ctx),
pa_context_get_server_protocol_version (ctx)); pa_context_get_server_protocol_version (ctx));
vlc_pa_unlock (); pa_threaded_mainloop_unlock (mainloop);
*mlp = mainloop;
return ctx; return ctx;
fail: fail:
vlc_pa_unlock (); pa_threaded_mainloop_unlock (mainloop);
vlc_pa_mainloop_deinit (); pa_threaded_mainloop_stop (mainloop);
pa_threaded_mainloop_free (mainloop);
return NULL; return NULL;
} }
/** /**
* Closes a connection to PulseAudio. * Closes a connection to PulseAudio.
*/ */
void vlc_pa_disconnect (vlc_object_t *obj, pa_context *ctx) void vlc_pa_disconnect (vlc_object_t *obj, pa_context *ctx,
pa_threaded_mainloop *mainloop)
{ {
vlc_pa_lock (); pa_threaded_mainloop_lock (mainloop);
pa_context_disconnect (ctx); pa_context_disconnect (ctx);
pa_context_set_state_callback (ctx, NULL, NULL); pa_context_set_state_callback (ctx, NULL, NULL);
pa_context_unref (ctx); pa_context_unref (ctx);
vlc_pa_unlock (); pa_threaded_mainloop_unlock (mainloop);
vlc_pa_mainloop_deinit (); pa_threaded_mainloop_stop (mainloop);
pa_threaded_mainloop_free (mainloop);
(void) obj; (void) obj;
} }
...@@ -262,7 +191,27 @@ void vlc_pa_disconnect (vlc_object_t *obj, pa_context *ctx) ...@@ -262,7 +191,27 @@ void vlc_pa_disconnect (vlc_object_t *obj, pa_context *ctx)
* \warning This function must be called from the mainloop, * \warning This function must be called from the mainloop,
* or with the mainloop lock held. * or with the mainloop lock held.
*/ */
void vlc_pa_rttime_free(pa_time_event *e) void vlc_pa_rttime_free (pa_threaded_mainloop *mainloop, pa_time_event *e)
{ {
(pa_threaded_mainloop_get_api (vlc_pa_mainloop))->time_free (e); pa_mainloop_api *api = pa_threaded_mainloop_get_api (mainloop);
api->time_free (e);
}
#undef vlc_pa_get_latency
/**
* Gets latency of a PulseAudio stream.
* \return the latency or VLC_TS_INVALID on error.
*/
mtime_t vlc_pa_get_latency(vlc_object_t *obj, pa_context *ctx, pa_stream *s)
{
pa_usec_t latency;
int negative;
if (pa_stream_get_latency(s, &latency, &negative)) {
if (pa_context_errno (ctx) != PA_ERR_NODATA)
vlc_pa_error(obj, "unknown latency", ctx);
return VLC_TS_INVALID;
}
return negative ? -latency : +latency;
} }
...@@ -24,18 +24,18 @@ ...@@ -24,18 +24,18 @@
extern "C" { extern "C" {
# endif # endif
VLC_API void vlc_pa_lock (void); VLC_API pa_context *vlc_pa_connect (vlc_object_t *obj,
VLC_API void vlc_pa_unlock (void); pa_threaded_mainloop **);
VLC_API void vlc_pa_signal (int); VLC_API void vlc_pa_disconnect (vlc_object_t *obj, pa_context *ctx,
VLC_API void vlc_pa_wait (void); pa_threaded_mainloop *);
VLC_API pa_context *vlc_pa_connect (vlc_object_t *obj);
VLC_API void vlc_pa_disconnect (vlc_object_t *obj, pa_context *ctx);
VLC_API void vlc_pa_error (vlc_object_t *, const char *msg, pa_context *); VLC_API void vlc_pa_error (vlc_object_t *, const char *msg, pa_context *);
#define vlc_pa_error(o, m, c) vlc_pa_error(VLC_OBJECT(o), m, c) #define vlc_pa_error(o, m, c) vlc_pa_error(VLC_OBJECT(o), m, c)
VLC_API void vlc_pa_rttime_free (pa_time_event *); VLC_API mtime_t vlc_pa_get_latency (vlc_object_t *, pa_context *, pa_stream *);
#define vlc_pa_get_latency(o, c, s) vlc_pa_get_latency(VLC_OBJECT(o), c, s)
VLC_API void vlc_pa_rttime_free (pa_threaded_mainloop *, pa_time_event *);
# ifdef __cplusplus # ifdef __cplusplus
} }
......
...@@ -466,19 +466,6 @@ SOURCES_libvlc = \ ...@@ -466,19 +466,6 @@ SOURCES_libvlc = \
$(SOURCES_libvlc_common) \ $(SOURCES_libvlc_common) \
$(NULL) $(NULL)
###############################################################################
# libvlc pulse
###############################################################################
if HAVE_PULSE
pkglib_LTLIBRARIES = libvlcpulse.la
endif
libvlcpulse_la_SOURCES = pulse/mainloop.c ../include/vlc_pulse.h
libvlcpulse_la_CPPFLAGS = $(PULSE_CFLAGS)
libvlcpulse_la_LIBADD = $(PULSE_LIBS) libvlccore.la
libvlcpulse_la_LDFLAGS = -export-symbols-regex ^vlc_pa_ -no-undefined
############################################################################### ###############################################################################
# GIT revision # GIT revision
############################################################################### ###############################################################################
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment