This fixes deadlocks between input and output.
libvlc_LTLIBRARIES += libaccess_alsa_plugin.la
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_LIBADD = $(AM_LIBADD) $(PULSE_LIBS) \
- ../../src/libvlcpulse.la
+libpulsesrc_plugin_la_LIBADD = $(AM_LIBADD) $(PULSE_LIBS)
libpulsesrc_plugin_la_DEPENDENCIES =
if HAVE_PULSE
libvlc_LTLIBRARIES += libpulsesrc_plugin.la
#include <vlc_demux.h>
#include <vlc_plugin.h>
#include <pulse/pulseaudio.h>
-#include <vlc_pulse.h>
+#include "../audio_output/vlcpulse.h"
static int Open(vlc_object_t *);
static void Close(vlc_object_t *);
{
pa_stream *stream; /**< PulseAudio playback stream object */
pa_context *context; /**< PulseAudio connection context */
+ pa_threaded_mainloop *mainloop; /**< PulseAudio thread */
es_out_id_t *es;
bool discontinuity; /**< The next block will not follow the last one */
/* Stream helpers */
static void stream_state_cb(pa_stream *s, void *userdata)
{
+ pa_threaded_mainloop *mainloop = userdata;
+
switch (pa_stream_get_state(s)) {
case PA_STREAM_READY:
case PA_STREAM_FAILED:
case PA_STREAM_TERMINATED:
- vlc_pa_signal(0);
+ pa_threaded_mainloop_signal(mainloop, 0);
default:
break;
}
- (void) userdata;
}
static void stream_moved_cb(pa_stream *s, void *userdata)
(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;
while ((state = pa_stream_get_state(stream)) != PA_STREAM_READY) {
if (state == PA_STREAM_FAILED || state == PA_STREAM_TERMINATED)
return -1;
- vlc_pa_wait();
+ pa_threaded_mainloop_wait(mainloop);
}
return 0;
}
{
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));
- if (unlikely(sys == NULL)) {
- vlc_pa_disconnect (obj, ctx);
+ if (unlikely(sys == NULL))
return VLC_ENOMEM;
+
+ sys->context = vlc_pa_connect(obj, &sys->mainloop);
+ if (sys->context == NULL) {
+ free(sys);
+ return VLC_EGENERIC;
}
+
sys->stream = NULL;
- sys->context = ctx;
sys->es = NULL;
sys->discontinuity = false;
sys->caching = INT64_C(1000) * var_InheritInteger(obj, "live-caching");
/* Create record stream */
pa_stream *s;
- vlc_pa_lock();
- s = pa_stream_new(ctx, "audio stream", &ss, &map);
+ pa_threaded_mainloop_lock(sys->mainloop);
+ s = pa_stream_new(sys->context, "audio stream", &ss, &map);
if (s == NULL)
goto error;
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_moved_callback(s, stream_moved_cb, demux);
pa_stream_set_overflow_callback(s, stream_overflow_cb, demux);
pa_stream_set_underflow_callback(s, stream_underflow_cb, demux);
if (pa_stream_connect_record(s, NULL, &attr, flags) < 0
- || stream_wait(s)) {
- vlc_pa_error(obj, "cannot connect record stream", ctx);
+ || stream_wait(s, sys->mainloop)) {
+ vlc_pa_error(obj, "cannot connect record stream", sys->context);
goto error;
}
const struct pa_buffer_attr *pba = pa_stream_get_buffer_attr(s);
msg_Dbg(obj, "using buffer metrics: maxlength=%"PRIu32", fragsize=%"PRIu32,
pba->maxlength, pba->fragsize);
- vlc_pa_unlock();
+ pa_threaded_mainloop_unlock(sys->mainloop);
demux->pf_demux = NULL;
demux->pf_control = Control;
return VLC_SUCCESS;
error:
- vlc_pa_unlock();
+ pa_threaded_mainloop_unlock(sys->mainloop);
Close(obj);
return VLC_EGENERIC;
}
{
demux_t *demux = (demux_t *)obj;
demux_sys_t *sys = demux->p_sys;
- pa_context *ctx = sys->context;
pa_stream *s = sys->stream;
if (likely(s != NULL)) {
- vlc_pa_lock();
+ pa_threaded_mainloop_lock(sys->mainloop);
pa_stream_disconnect(s);
pa_stream_set_state_callback(s, NULL, NULL);
pa_stream_set_read_callback(s, NULL, NULL);
pa_stream_set_suspended_callback(s, NULL, NULL);
pa_stream_set_underflow_callback(s, NULL, NULL);
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);
}
libvlc_LTLIBRARIES += libalsa_plugin.la
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_LIBADD = $(AM_LIBADD) $(PULSE_LIBS) \
- $(LIBM) ../../src/libvlcpulse.la
+libpulse_plugin_la_LIBADD = $(AM_LIBADD) $(PULSE_LIBS) $(LIBM)
libpulse_plugin_la_DEPENDENCIES =
if HAVE_PULSE
libvlc_LTLIBRARIES += libpulse_plugin.la
#include <vlc_cpu.h>
#include <pulse/pulseaudio.h>
-#include <vlc_pulse.h>
+#include "vlcpulse.h"
#if !PA_CHECK_VERSION(0,9,22)
# include <vlc_xlib.h>
#endif
/* NOTE:
* 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
* the PulseAudio mainloop lock held, if the callback acquires the lock. */
{
pa_stream *stream; /**< PulseAudio playback stream object */
pa_context *context; /**< PulseAudio connection context */
+ pa_threaded_mainloop *mainloop; /**< PulseAudio thread */
pa_time_event *trigger; /**< Deferred stream trigger */
pa_volume_t base_volume; /**< 0dB reference volume */
pa_cvolume cvolume; /**< actual sink input volume */
/*** 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)
{
aout_sys_t *sys = aout->sys;
pa_operation *op;
if (sys->trigger != NULL) {
- vlc_pa_rttime_free(sys->trigger);
+ vlc_pa_rttime_free(sys->mainloop, sys->trigger);
sys->trigger = NULL;
}
pa_operation *op;
if (sys->trigger != NULL) {
- vlc_pa_rttime_free(sys->trigger);
+ vlc_pa_rttime_free(sys->mainloop, sys->trigger);
sys->trigger = NULL;
}
/*** Stream helpers ***/
static void stream_state_cb(pa_stream *s, void *userdata)
{
+ pa_threaded_mainloop *mainloop = userdata;
+
switch (pa_stream_get_state(s)) {
case PA_STREAM_READY:
case PA_STREAM_FAILED:
case PA_STREAM_TERMINATED:
- vlc_pa_signal(0);
+ pa_threaded_mainloop_signal(mainloop, 0);
default:
break;
}
- (void) userdata;
}
static void stream_event_cb(pa_stream *s, const char *name, pa_proplist *pl,
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;
while ((state = pa_stream_get_state(stream)) != PA_STREAM_READY) {
if (state == PA_STREAM_FAILED || state == PA_STREAM_TERMINATED)
return -1;
- vlc_pa_wait();
+ pa_threaded_mainloop_wait(mainloop);
}
return 0;
}
* output FIFO lock while the PulseAudio threaded main loop lock is held
* (including from PulseAudio stream callbacks). Otherwise lock inversion
* will take place, and sooner or later a deadlock. */
- vlc_pa_lock();
+ pa_threaded_mainloop_lock(sys->mainloop);
sys->pts = pts;
if (pa_stream_is_corked(s) > 0)
block_Release(block);
}
- vlc_pa_unlock();
+ pa_threaded_mainloop_unlock(sys->mainloop);
}
/**
aout_sys_t *sys = aout->sys;
pa_stream *s = sys->stream;
- vlc_pa_lock();
+ pa_threaded_mainloop_lock(sys->mainloop);
if (paused) {
sys->paused = date;
stream_resync(aout, s);
}
- vlc_pa_unlock();
+ pa_threaded_mainloop_unlock(sys->mainloop);
}
/**
pa_stream *s = sys->stream;
pa_operation *op;
- vlc_pa_lock();
+ pa_threaded_mainloop_lock(sys->mainloop);
if (wait)
op = pa_stream_drain(s, NULL, NULL);
op = pa_stream_flush(s, NULL, NULL);
if (op != NULL)
pa_operation_unref(op);
- vlc_pa_unlock();
+ pa_threaded_mainloop_unlock(sys->mainloop);
}
static int VolumeSet(audio_output_t *aout, float vol, bool mute)
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);
if (likely(op != NULL))
pa_operation_unref(op);
op = pa_context_set_sink_input_mute(sys->context, idx, mute, NULL, NULL);
if (likely(op != NULL))
pa_operation_unref(op);
- vlc_pa_unlock();
+ pa_threaded_mainloop_unlock(sys->mainloop);
return 0;
}
(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,
NULL, NULL);
if (likely(op != NULL)) {
msg_Dbg(aout, "moving to sink %"PRIu32, sink_idx);
} else
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;
}
if (unlikely(sys == NULL))
return VLC_ENOMEM;
- pa_context *ctx = vlc_pa_connect (obj);
+ pa_context *ctx = vlc_pa_connect(obj, &sys->mainloop);
if (ctx == NULL)
{
free (sys);
/* Create a playback stream */
pa_stream *s;
- vlc_pa_lock();
+ pa_threaded_mainloop_lock(sys->mainloop);
s = pa_stream_new_extended(ctx, "audio stream", formatv, formatc, NULL);
for (unsigned i = 0; i < formatc; i++)
pa_format_info_free(formatv[i]);
#else
- vlc_pa_lock();
+ pa_threaded_mainloop_lock(sys->mainloop);
pa_stream *s = pa_stream_new(ctx, "audio stream", &ss, &map);
#endif
if (s == NULL) {
goto fail;
}
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_latency_update_callback(s, stream_latency_cb, aout);
pa_stream_set_moved_callback(s, stream_moved_cb, aout);
pa_stream_set_underflow_callback(s, stream_underflow_cb, aout);
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);
goto fail;
}
if (op != NULL)
pa_operation_unref(op);
stream_moved_cb(s, aout);
- vlc_pa_unlock();
+ pa_threaded_mainloop_unlock(sys->mainloop);
aout->format.i_format = format;
aout->pf_play = Play;
return VLC_SUCCESS;
fail:
- vlc_pa_unlock();
+ pa_threaded_mainloop_unlock(sys->mainloop);
Close(obj);
return VLC_EGENERIC;
}
var_DelCallback (aout, "audio-device", StreamMove, s);
var_Destroy (aout, "audio-device");
- vlc_pa_lock ();
+ pa_threaded_mainloop_lock(sys->mainloop);
if (unlikely(sys->trigger != NULL))
- vlc_pa_rttime_free(sys->trigger);
+ vlc_pa_rttime_free(sys->mainloop, sys->trigger);
pa_stream_disconnect(s);
/* Clear all callbacks */
pa_stream_set_underflow_callback(s, NULL, NULL);
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);
}
# include "config.h"
#endif
-#define MODULE_STRING "pulse"
#include <vlc_common.h>
#include <pulse/pulseaudio.h>
-#include <vlc_pulse.h>
+#include "vlcpulse.h"
#include <assert.h>
#include <stdlib.h>
#include <locale.h>
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)
{
+ pa_threaded_mainloop *mainloop = userdata;
+
switch (pa_context_get_state(ctx))
{
case PA_CONTEXT_READY:
case PA_CONTEXT_FAILED:
case PA_CONTEXT_TERMINATED:
- vlc_pa_signal (0);
+ pa_threaded_mainloop_signal (mainloop, 0);
default:
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;
{
if (state == PA_CONTEXT_FAILED || state == PA_CONTEXT_TERMINATED)
return -1;
- vlc_pa_wait ();
+ pa_threaded_mainloop_wait (mainloop);
}
return 0;
}
* Initializes the PulseAudio main loop and connects to the PulseAudio server.
* @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, " (compiled with version %s, protocol %u)",
pa_get_headers_version (), PA_PROTOCOL_VERSION);
- char *ua = var_InheritString (obj, "user-agent");
- pa_context *ctx;
+ /* Initialize main loop */
+ 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 */
+ char *ua = var_InheritString (obj, "user-agent");
pa_proplist *props = pa_proplist_new ();
if (likely(props != NULL))
{
}
/* 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);
if (props != NULL)
- pa_proplist_free(props);
+ pa_proplist_free (props);
if (unlikely(ctx == NULL))
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
- || context_wait (ctx))
+ || context_wait (ctx, mainloop))
{
vlc_pa_error (obj, "PulseAudio server connection failure", ctx);
pa_context_unref (ctx);
pa_context_get_protocol_version (ctx),
pa_context_get_server_protocol_version (ctx));
- vlc_pa_unlock ();
+ pa_threaded_mainloop_unlock (mainloop);
+ *mlp = mainloop;
return ctx;
fail:
- vlc_pa_unlock ();
- vlc_pa_mainloop_deinit ();
+ pa_threaded_mainloop_unlock (mainloop);
+ pa_threaded_mainloop_stop (mainloop);
+ pa_threaded_mainloop_free (mainloop);
return NULL;
}
/**
* 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_set_state_callback (ctx, NULL, NULL);
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;
}
* \warning This function must be called from the mainloop,
* 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;
}
extern "C" {
# endif
-VLC_API void vlc_pa_lock (void);
-VLC_API void vlc_pa_unlock (void);
-VLC_API void vlc_pa_signal (int);
-VLC_API void vlc_pa_wait (void);
-
-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 pa_context *vlc_pa_connect (vlc_object_t *obj,
+ pa_threaded_mainloop **);
+VLC_API void vlc_pa_disconnect (vlc_object_t *obj, pa_context *ctx,
+ pa_threaded_mainloop *);
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)
-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
}
$(SOURCES_libvlc_common) \
$(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
###############################################################################