Commit 0037635b authored by Jan Schmidt's avatar Jan Schmidt
Browse files

Rewrite the pulse plugin, conditionally enabling new behaviour with


newer pulseaudio.

Fixes: #567794

* Hook pulsesink's volume property up with the stream volume -- not the
  sink volume in PA.

* Read the device description directly from the sink instead of going
  via the mixer.

* Properly implement _reset() methods for both sink and source to avoid
  deadlocks when shutting down a pipeline.

* Replace all simple pa_threaded_mainloop_wait() by proper loops to
  guarantee that we wait for the right event in case multiple events are
  fired.  While this is not strictly necessary in many cases it
  certainly is more correct and makes me sleep better at night.

* Replace CHECK_DEAD_GOTO macros with proper functions

* Extend the number of supported channels to 32 since that is the actual
  limit in PA.

* Get rid of _dispose() methods since we don't need them.

* Increase the volume property upper limit of the sink to 1000.

* Reset function pointers after we disconnect a stream/context. Better
  fix for bug 556986.

* Reset the state of the element properly if open/prepare fails

* Cork the PA stream when the pipeline is paused. This allows the PA
* daemon to
  close audio device on pause and thus save a bit of power.

* Set PA stream properties based on GST tags such as GST_TAG_TITLE,
  GST_TAG_ARTIST, and so on.
Signed-off-by: Lennart Poettering's avatarLennart Poettering <lennart@poettering.net>
parent 9cf73bdd
......@@ -769,6 +769,18 @@ dnl *** pulseaudio ***
translit(dnm, m, l) AM_CONDITIONAL(USE_PULSE, true)
AG_GST_CHECK_FEATURE(PULSE, [pulseaudio plug-in], pulseaudio, [
AG_GST_PKG_CHECK_MODULES(PULSE, libpulse >= 0.9.8)
AG_GST_PKG_CHECK_MODULES(PULSE_0_9_11, libpulse >= 0.9.11)
if test x$HAVE_PULSE_0_9_11 = xyes; then
AC_DEFINE(HAVE_PULSE_0_9_11, 1, [defined if pulseaudio >= 0.9.11 is available])
fi
AG_GST_PKG_CHECK_MODULES(PULSE_0_9_12, libpulse >= 0.9.12)
if test x$HAVE_PULSE_0_9_12 = xyes; then
AC_DEFINE(HAVE_PULSE_0_9_12, 1, [defined if pulseaudio >= 0.9.12 is available])
fi
AG_GST_PKG_CHECK_MODULES(PULSE_0_9_13, libpulse >= 0.9.13)
if test x$HAVE_PULSE_0_9_13 = xyes; then
AC_DEFINE(HAVE_PULSE_0_9_13, 1, [defined if pulseaudio >= 0.9.13 is available])
fi
])
dnl *** dv1394 ***
......
......@@ -92,7 +92,7 @@ gst_pulseprobe_invalidate (GstPulseProbe * c)
g_list_foreach (c->devices, (GFunc) g_free, NULL);
g_list_free (c->devices);
c->devices = NULL;
c->devices_valid = 0;
c->devices_valid = FALSE;
}
static gboolean
......@@ -126,13 +126,22 @@ gst_pulseprobe_open (GstPulseProbe * c)
goto unlock_and_fail;
}
/* Wait until the context is ready */
pa_threaded_mainloop_wait (c->mainloop);
for (;;) {
pa_context_state_t state;
if (pa_context_get_state (c->context) != PA_CONTEXT_READY) {
GST_WARNING_OBJECT (c->object, "Failed to connect context: %s",
pa_strerror (pa_context_errno (c->context)));
goto unlock_and_fail;
state = pa_context_get_state (c->context);
if (!PA_CONTEXT_IS_GOOD (state)) {
GST_WARNING_OBJECT (c->object, "Failed to connect context: %s",
pa_strerror (pa_context_errno (c->context)));
goto unlock_and_fail;
}
if (state == PA_CONTEXT_READY)
break;
/* Wait until the context is ready */
pa_threaded_mainloop_wait (c->mainloop);
}
pa_threaded_mainloop_unlock (c->mainloop);
......@@ -152,12 +161,23 @@ unlock_and_fail:
return FALSE;
}
#define CHECK_DEAD_GOTO(c, label) do { \
if (!(c)->context || pa_context_get_state((c)->context) != PA_CONTEXT_READY) { \
GST_WARNING_OBJECT((c)->object, "Not connected: %s", (c)->context ? pa_strerror(pa_context_errno((c)->context)) : "NULL"); \
goto label; \
} \
} while(0);
static gboolean
gst_pulseprobe_is_dead (GstPulseProbe * pulseprobe)
{
if (!pulseprobe->context ||
!PA_CONTEXT_IS_GOOD (pa_context_get_state (pulseprobe->context))) {
const gchar *err_str =
pulseprobe->context ?
pa_strerror (pa_context_errno (pulseprobe->context)) : NULL;
GST_ELEMENT_ERROR ((pulseprobe), RESOURCE, FAILED,
("Disconnected: %s", err_str), (NULL));
return TRUE;
}
return FALSE;
}
static gboolean
gst_pulseprobe_enumerate (GstPulseProbe * c)
......@@ -178,9 +198,13 @@ gst_pulseprobe_enumerate (GstPulseProbe * c)
}
c->operation_success = 0;
while (pa_operation_get_state (o) != PA_OPERATION_DONE) {
while (pa_operation_get_state (o) == PA_OPERATION_RUNNING) {
if (gst_pulseprobe_is_dead (c))
goto unlock_and_fail;
pa_threaded_mainloop_wait (c->mainloop);
CHECK_DEAD_GOTO (c, unlock_and_fail);
}
if (!c->operation_success) {
......@@ -205,9 +229,12 @@ gst_pulseprobe_enumerate (GstPulseProbe * c)
}
c->operation_success = 0;
while (pa_operation_get_state (o) != PA_OPERATION_DONE) {
while (pa_operation_get_state (o) == PA_OPERATION_RUNNING) {
if (gst_pulseprobe_is_dead (c))
goto unlock_and_fail;
pa_threaded_mainloop_wait (c->mainloop);
CHECK_DEAD_GOTO (c, unlock_and_fail);
}
if (!c->operation_success) {
......@@ -220,7 +247,7 @@ gst_pulseprobe_enumerate (GstPulseProbe * c)
o = NULL;
}
c->devices_valid = 1;
c->devices_valid = TRUE;
pa_threaded_mainloop_unlock (c->mainloop);
......@@ -246,6 +273,7 @@ gst_pulseprobe_close (GstPulseProbe * c)
if (c->context) {
pa_context_disconnect (c->context);
pa_context_set_state_callback (c->context, NULL, NULL);
pa_context_unref (c->context);
c->context = NULL;
}
......@@ -257,8 +285,8 @@ gst_pulseprobe_close (GstPulseProbe * c)
}
GstPulseProbe *
gst_pulseprobe_new (GObject * object, GObjectClass * klass, guint prop_id,
const gchar * server, gboolean sinks, gboolean sources)
gst_pulseprobe_new (GObject * object, GObjectClass * klass,
guint prop_id, const gchar * server, gboolean sinks, gboolean sources)
{
GstPulseProbe *c = NULL;
......@@ -335,7 +363,9 @@ gst_pulseprobe_get_values (GstPulseProbe * c, guint prop_id,
const GParamSpec * pspec)
{
GValueArray *array;
GValue value = { 0 };
GValue value = {
0
};
GList *item;
if (prop_id != c->prop_id) {
......
......@@ -37,7 +37,7 @@ struct _GstPulseProbe
GObject *object;
gchar *server;
GList *devices;
int devices_valid;
gboolean devices_valid;
pa_threaded_mainloop *mainloop;
pa_context *context;
......
This diff is collapsed.
......@@ -29,7 +29,6 @@
#include <pulse/thread-mainloop.h>
#include "pulseprobe.h"
#include "pulsemixerctrl.h"
G_BEGIN_DECLS
......@@ -57,18 +56,18 @@ struct _GstPulseSink
pa_context *context;
pa_stream *stream;
GMutex *stream_mutex;
pa_sample_spec sample_spec;
GstPulseMixerCtrl *mixer;
GstPulseProbe *probe;
#if 0
gdouble volume;
#endif
gboolean volume_set;
int operation_success;
gchar *device_description;
gboolean operation_success;
gboolean did_reset, in_write;
};
struct _GstPulseSinkClass
......
......@@ -68,8 +68,6 @@ static void gst_pulsesrc_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static void gst_pulsesrc_finalize (GObject * object);
static void gst_pulsesrc_dispose (GObject * object);
static gboolean gst_pulsesrc_open (GstAudioSrc * asrc);
static gboolean gst_pulsesrc_close (GstAudioSrc * asrc);
......@@ -83,6 +81,8 @@ static guint gst_pulsesrc_read (GstAudioSrc * asrc, gpointer data,
guint length);
static guint gst_pulsesrc_delay (GstAudioSrc * asrc);
static void gst_pulsesrc_reset (GstAudioSrc * src);
static gboolean gst_pulsesrc_negotiate (GstBaseSrc * basesrc);
static GstStateChangeReturn gst_pulsesrc_change_state (GstElement *
......@@ -161,30 +161,30 @@ gst_pulsesrc_base_init (gpointer g_class)
"width = (int) 16, "
"depth = (int) 16, "
"rate = (int) [ 1, MAX ], "
"channels = (int) [ 1, 16 ];"
"audio/x-raw-int, "
"channels = (int) [ 1, 32 ];"
"audio/x-raw-float, "
"endianness = (int) { " ENDIANNESS " }, "
"signed = (boolean) TRUE, "
"width = (int) 32, "
"depth = (int) 32, "
"rate = (int) [ 1, MAX ], "
"channels = (int) [ 1, 16 ];"
"audio/x-raw-float, "
"channels = (int) [ 1, 32 ];"
"audio/x-raw-int, "
"endianness = (int) { " ENDIANNESS " }, "
"signed = (boolean) TRUE, "
"width = (int) 32, "
"depth = (int) 32, "
"rate = (int) [ 1, MAX ], "
"channels = (int) [ 1, 16 ];"
"channels = (int) [ 1, 32 ];"
"audio/x-raw-int, "
"signed = (boolean) FALSE, "
"width = (int) 8, "
"depth = (int) 8, "
"rate = (int) [ 1, MAX ], "
"channels = (int) [ 1, 16 ];"
"channels = (int) [ 1, 32 ];"
"audio/x-alaw, "
"rate = (int) [ 1, MAX], "
"channels = (int) [ 1, 16 ];"
"channels = (int) [ 1, 32 ];"
"audio/x-mulaw, "
"rate = (int) [ 1, MAX], " "channels = (int) [ 1, 16 ]")
"rate = (int) [ 1, MAX], " "channels = (int) [ 1, 32 ]")
);
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
......@@ -205,14 +205,13 @@ gst_pulsesrc_class_init (GstPulseSrcClass * klass)
GstBaseSrcClass *gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
gstelement_class->change_state =
GST_DEBUG_FUNCPTR (gst_pulsesrc_change_state);
gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_pulsesrc_dispose);
gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_pulsesrc_finalize);
gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_pulsesrc_set_property);
gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_pulsesrc_get_property);
gstelement_class->change_state =
GST_DEBUG_FUNCPTR (gst_pulsesrc_change_state);
gstbasesrc_class->negotiate = GST_DEBUG_FUNCPTR (gst_pulsesrc_negotiate);
gstaudiosrc_class->open = GST_DEBUG_FUNCPTR (gst_pulsesrc_open);
......@@ -221,6 +220,7 @@ gst_pulsesrc_class_init (GstPulseSrcClass * klass)
gstaudiosrc_class->unprepare = GST_DEBUG_FUNCPTR (gst_pulsesrc_unprepare);
gstaudiosrc_class->read = GST_DEBUG_FUNCPTR (gst_pulsesrc_read);
gstaudiosrc_class->delay = GST_DEBUG_FUNCPTR (gst_pulsesrc_delay);
gstaudiosrc_class->reset = GST_DEBUG_FUNCPTR (gst_pulsesrc_reset);
/* Overwrite GObject fields */
g_object_class_install_property (gobject_class,
......@@ -245,7 +245,7 @@ gst_pulsesrc_init (GstPulseSrc * pulsesrc, GstPulseSrcClass * klass)
{
int e;
pulsesrc->server = pulsesrc->device = NULL;
pulsesrc->server = pulsesrc->device = pulsesrc->device_description = NULL;
pulsesrc->context = NULL;
pulsesrc->stream = NULL;
......@@ -253,6 +253,18 @@ gst_pulsesrc_init (GstPulseSrc * pulsesrc, GstPulseSrcClass * klass)
pulsesrc->read_buffer = NULL;
pulsesrc->read_buffer_length = 0;
#if HAVE_PULSE_0_9_13
pa_sample_spec_init (&pulsesrc->sample_spec);
#else
pulsesrc->sample_spec.format = PA_SAMPLE_INVALID;
pulsesrc->sample_spec.rate = 0;
pulsesrc->sample_spec.channels = 0;
#endif
pulsesrc->operation_success = FALSE;
pulsesrc->did_reset = FALSE;
pulsesrc->in_read = FALSE;
pulsesrc->mainloop = pa_threaded_mainloop_new ();
g_assert (pulsesrc->mainloop);
......@@ -272,6 +284,9 @@ gst_pulsesrc_destroy_stream (GstPulseSrc * pulsesrc)
pa_stream_unref (pulsesrc->stream);
pulsesrc->stream = NULL;
}
g_free (pulsesrc->device_description);
pulsesrc->device_description = NULL;
}
static void
......@@ -301,8 +316,10 @@ gst_pulsesrc_finalize (GObject * object)
pa_threaded_mainloop_free (pulsesrc->mainloop);
if (pulsesrc->mixer)
if (pulsesrc->mixer) {
gst_pulsemixer_ctrl_free (pulsesrc->mixer);
pulsesrc->mixer = NULL;
}
if (pulsesrc->probe) {
gst_pulseprobe_free (pulsesrc->probe);
......@@ -312,12 +329,26 @@ gst_pulsesrc_finalize (GObject * object)
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gst_pulsesrc_dispose (GObject * object)
static gboolean
gst_pulsesrc_is_dead (GstPulseSrc * pulsesrc)
{
G_OBJECT_CLASS (parent_class)->dispose (object);
if (!pulsesrc->context
|| !PA_CONTEXT_IS_GOOD (pa_context_get_state (pulsesrc->context))
|| !pulsesrc->stream
|| !PA_STREAM_IS_GOOD (pa_stream_get_state (pulsesrc->stream))) {
const gchar *err_str = pulsesrc->context ?
pa_strerror (pa_context_errno (pulsesrc->context)) : NULL;
GST_ELEMENT_ERROR ((pulsesrc), RESOURCE, FAILED, ("Disconnected: %s",
err_str), (NULL));
return TRUE;
}
return FALSE;
}
static void
gst_pulsesrc_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec)
......@@ -346,6 +377,65 @@ gst_pulsesrc_set_property (GObject * object,
}
}
static void
gst_pulsesrc_source_info_cb (pa_context * c, const pa_source_info * i, int eol,
void *userdata)
{
GstPulseSrc *pulsesrc = GST_PULSESRC (userdata);
if (!i)
return;
if (!pulsesrc->stream)
return;
g_assert (i->index == pa_stream_get_device_index (pulsesrc->stream));
g_free (pulsesrc->device_description);
pulsesrc->device_description = g_strdup (i->description);
}
static gchar *
gst_pulsesrc_device_description (GstPulseSrc * pulsesrc)
{
pa_operation *o = NULL;
gchar *t;
pa_threaded_mainloop_lock (pulsesrc->mainloop);
if (!pulsesrc->stream)
goto unlock;
if (!(o = pa_context_get_source_info_by_index (pulsesrc->context,
pa_stream_get_device_index (pulsesrc->stream),
gst_pulsesrc_source_info_cb, pulsesrc))) {
GST_ELEMENT_ERROR (pulsesrc, RESOURCE, FAILED,
("pa_stream_get_source_info() failed: %s",
pa_strerror (pa_context_errno (pulsesrc->context))), (NULL));
goto unlock;
}
while (pa_operation_get_state (o) == PA_OPERATION_RUNNING) {
if (gst_pulsesrc_is_dead (pulsesrc))
goto unlock;
pa_threaded_mainloop_wait (pulsesrc->mainloop);
}
unlock:
if (o)
pa_operation_unref (o);
t = g_strdup (pulsesrc->device_description);
pa_threaded_mainloop_unlock (pulsesrc->mainloop);
return t;
}
static void
gst_pulsesrc_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec)
......@@ -362,14 +452,12 @@ gst_pulsesrc_get_property (GObject * object,
g_value_set_string (value, pulsesrc->device);
break;
case PROP_DEVICE_NAME:
if (pulsesrc->mixer)
g_value_set_string (value, pulsesrc->mixer->description);
else
g_value_set_string (value, NULL);
case PROP_DEVICE_NAME:{
char *t = gst_pulsesrc_device_description (pulsesrc);
g_value_set_string (value, t);
g_free (t);
break;
}
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
......@@ -424,15 +512,25 @@ gst_pulsesrc_stream_request_cb (pa_stream * s, size_t length, void *userdata)
pa_threaded_mainloop_signal (pulsesrc->mainloop, 0);
}
static void
gst_pulsesrc_stream_latency_update_cb (pa_stream * s, void *userdata)
{
GstPulseSrc *pulsesrc = GST_PULSESRC (userdata);
pa_threaded_mainloop_signal (pulsesrc->mainloop, 0);
}
static gboolean
gst_pulsesrc_open (GstAudioSrc * asrc)
{
GstPulseSrc *pulsesrc = GST_PULSESRC (asrc);
gchar *name = gst_pulse_client_name ();
pa_threaded_mainloop_lock (pulsesrc->mainloop);
g_assert (!pulsesrc->context);
g_assert (!pulsesrc->stream);
if (!(pulsesrc->context =
pa_context_new (pa_threaded_mainloop_get_api (pulsesrc->mainloop),
name))) {
......@@ -450,13 +548,22 @@ gst_pulsesrc_open (GstAudioSrc * asrc)
goto unlock_and_fail;
}
/* Wait until the context is ready */
pa_threaded_mainloop_wait (pulsesrc->mainloop);
for (;;) {
pa_context_state_t state;
if (pa_context_get_state (pulsesrc->context) != PA_CONTEXT_READY) {
GST_ELEMENT_ERROR (pulsesrc, RESOURCE, FAILED, ("Failed to connect: %s",
pa_strerror (pa_context_errno (pulsesrc->context))), (NULL));
goto unlock_and_fail;
state = pa_context_get_state (pulsesrc->context);
if (!PA_CONTEXT_IS_GOOD (state)) {
GST_ELEMENT_ERROR (pulsesrc, RESOURCE, FAILED, ("Failed to connect: %s",
pa_strerror (pa_context_errno (pulsesrc->context))), (NULL));
goto unlock_and_fail;
}
if (state == PA_CONTEXT_READY)
break;
/* Wait until the context is ready */
pa_threaded_mainloop_wait (pulsesrc->mainloop);
}
pa_threaded_mainloop_unlock (pulsesrc->mainloop);
......@@ -466,6 +573,8 @@ gst_pulsesrc_open (GstAudioSrc * asrc)
unlock_and_fail:
gst_pulsesrc_destroy_context (pulsesrc);
pa_threaded_mainloop_unlock (pulsesrc->mainloop);
g_free (name);
......@@ -500,23 +609,15 @@ gst_pulsesrc_unprepare (GstAudioSrc * asrc)
return TRUE;
}
#define CHECK_DEAD_GOTO(pulsesrc, label) \
if (!(pulsesrc)->context || pa_context_get_state((pulsesrc)->context) != PA_CONTEXT_READY || \
!(pulsesrc)->stream || pa_stream_get_state((pulsesrc)->stream) != PA_STREAM_READY) { \
GST_ELEMENT_ERROR((pulsesrc), RESOURCE, FAILED, ("Disconnected: %s", (pulsesrc)->context ? pa_strerror(pa_context_errno((pulsesrc)->context)) : NULL), (NULL)); \
goto label; \
}
static guint
gst_pulsesrc_read (GstAudioSrc * asrc, gpointer data, guint length)
{
GstPulseSrc *pulsesrc = GST_PULSESRC (asrc);
size_t sum = 0;
pa_threaded_mainloop_lock (pulsesrc->mainloop);
CHECK_DEAD_GOTO (pulsesrc, unlock_and_fail);
pulsesrc->in_read = TRUE;
while (length > 0) {
size_t l;
......@@ -524,6 +625,9 @@ gst_pulsesrc_read (GstAudioSrc * asrc, gpointer data, guint length)
if (!pulsesrc->read_buffer) {
for (;;) {
if (gst_pulsesrc_is_dead (pulsesrc))
goto unlock_and_fail;
if (pa_stream_peek (pulsesrc->stream, &pulsesrc->read_buffer,
&pulsesrc->read_buffer_length) < 0) {
GST_ELEMENT_ERROR (pulsesrc, RESOURCE, FAILED,
......@@ -535,9 +639,10 @@ gst_pulsesrc_read (GstAudioSrc * asrc, gpointer data, guint length)
if (pulsesrc->read_buffer)
break;
pa_threaded_mainloop_wait (pulsesrc->mainloop);
if (pulsesrc->did_reset)
goto unlock_and_fail;
CHECK_DEAD_GOTO (pulsesrc, unlock_and_fail);
pa_threaded_mainloop_wait (pulsesrc->mainloop);
}
}
......@@ -570,16 +675,19 @@ gst_pulsesrc_read (GstAudioSrc * asrc, gpointer data, guint length)
}
}
pa_threaded_mainloop_unlock (pulsesrc->mainloop);
pulsesrc->did_reset = FALSE;
pulsesrc->in_read = FALSE;
pa_threaded_mainloop_unlock (pulsesrc->mainloop);
return sum;
/* ERRORS */
unlock_and_fail:
{
pa_threaded_mainloop_unlock (pulsesrc->mainloop);
return -1;
}
pulsesrc->did_reset = FALSE;
pulsesrc->in_read = FALSE;
pa_threaded_mainloop_unlock (pulsesrc->mainloop);
return (guint) - 1;
}
static guint
......@@ -593,9 +701,12 @@ gst_pulsesrc_delay (GstAudioSrc * asrc)
pa_threaded_mainloop_lock (pulsesrc->mainloop);
CHECK_DEAD_GOTO (pulsesrc, unlock_and_fail);
for (;;) {
if (gst_pulsesrc_is_dead (pulsesrc))
goto unlock_and_fail;
if (pa_stream_get_latency (pulsesrc->stream, &t, &negative) < 0) {
if (pa_stream_get_latency (pulsesrc->stream, &t, &negative) >= 0)
break;
if (pa_context_errno (pulsesrc->context) != PA_ERR_NODATA) {
GST_ELEMENT_ERROR (pulsesrc, RESOURCE, FAILED,
......@@ -604,9 +715,10 @@ gst_pulsesrc_delay (GstAudioSrc * asrc)
goto unlock_and_fail;
}
GST_WARNING_OBJECT (pulsesrc, "Not data while querying latency");
t = 0;
} else if (negative)
pa_threaded_mainloop_wait (pulsesrc->mainloop);
}
if (negative)
t = 0;
pa_threaded_mainloop_unlock (pulsesrc->mainloop);
......@@ -645,12 +757,8 @@ gst_pulsesrc_create_stream (GstPulseSrc * pulsesrc, GstCaps * caps)
pa_threaded_mainloop_lock (pulsesrc->mainloop);
if (!pulsesrc->context
|| pa_context_get_state (pulsesrc->context) != PA_CONTEXT_READY) {
GST_ELEMENT_ERROR (pulsesrc, RESOURCE, FAILED, ("Bad context state: %s",
pulsesrc->
context ? pa_strerror (pa_context_errno (pulsesrc->context)) :
NULL), (NULL));
if (!pulsesrc->context) {
GST_ELEMENT_ERROR (pulsesrc, RESOURCE, FAILED, ("Bad context"), (NULL));
goto unlock_and_fail;
}
......@@ -688,10 +796,16 @@ gst_pulsesrc_create_stream (GstPulseSrc * pulsesrc, GstCaps * caps)
pulsesrc);
pa_stream_set_read_callback (pulsesrc->stream, gst_pulsesrc_stream_request_cb,
pulsesrc);
pa_stream_set_latency_update_callback (pulsesrc->stream,
gst_pulsesrc_stream_latency_update_cb, pulsesrc);
pa_threaded_mainloop_unlock (pulsesrc->mainloop);
return TRUE;