typedef struct _GstPulseContext GstPulseContext;
+/* Store the PA contexts in a hash table to allow easy sharing among
+ * multiple instances of the sink. Keys are $context_name@$server_name
+ * (strings) and values should be GstPulseContext pointers.
+ */
struct _GstPulseContext
{
pa_context *context;
GSList *ring_buffers;
};
-/* Store the PA contexts in a hash table to allow easy sharing among
- * multiple instances of the sink. Keys are $context_name@$server_name
- * (strings) and values should be GstPulseContext pointers. */
static GHashTable *gst_pulse_shared_contexts = NULL;
+/* use one static main-loop for all instances
+ * this is needed to make the context sharing work as the contexts are
+ * released when releasing their parent main-loop
+ */
+static pa_threaded_mainloop *mainloop = NULL;
+static guint mainloop_ref_ct = 0;
+
+/* lock for access to shared resources */
static GMutex *pa_shared_ressource_mutex = NULL;
/* We keep a custom ringbuffer that is backed up by data allocated by
else
pbuf->context_name = g_strdup (psink->client_name);
- pa_threaded_mainloop_lock (psink->mainloop);
+ pa_threaded_mainloop_lock (mainloop);
g_mutex_lock (pa_shared_ressource_mutex);
pctx = g_hash_table_lookup (gst_pulse_shared_contexts, pbuf->context_name);
/* get the mainloop api and create a context */
GST_INFO_OBJECT (psink, "new context with name %s, pbuf=%p, pctx=%p",
pbuf->context_name, pbuf, pctx);
- api = pa_threaded_mainloop_get_api (psink->mainloop);
+ api = pa_threaded_mainloop_get_api (mainloop);
if (!(pctx->context = pa_context_new (api, pbuf->context_name)))
goto create_failed;
g_strdup (pbuf->context_name), (gpointer) pctx);
/* register some essential callbacks */
pa_context_set_state_callback (pctx->context,
- gst_pulsering_context_state_cb, psink->mainloop);
+ gst_pulsering_context_state_cb, mainloop);
#ifdef HAVE_PULSE_0_9_12
pa_context_set_subscribe_callback (pctx->context,
gst_pulsering_context_subscribe_cb, pctx);
/* Wait until the context is ready */
GST_LOG_OBJECT (psink, "waiting..");
- pa_threaded_mainloop_wait (psink->mainloop);
+ pa_threaded_mainloop_wait (mainloop);
}
GST_LOG_OBJECT (psink, "opened the device");
g_mutex_unlock (pa_shared_ressource_mutex);
- pa_threaded_mainloop_unlock (psink->mainloop);
+ pa_threaded_mainloop_unlock (mainloop);
return TRUE;
g_mutex_unlock (pa_shared_ressource_mutex);
gst_pulsering_destroy_context (pbuf);
- pa_threaded_mainloop_unlock (psink->mainloop);
+ pa_threaded_mainloop_unlock (mainloop);
return FALSE;
}
create_failed:
GST_LOG_OBJECT (psink, "closing device");
- pa_threaded_mainloop_lock (psink->mainloop);
+ pa_threaded_mainloop_lock (mainloop);
gst_pulsering_destroy_context (pbuf);
- pa_threaded_mainloop_unlock (psink->mainloop);
+ pa_threaded_mainloop_unlock (mainloop);
GST_LOG_OBJECT (psink, "closed device");
case PA_STREAM_FAILED:
case PA_STREAM_TERMINATED:
GST_LOG_OBJECT (psink, "signaling");
- pa_threaded_mainloop_signal (psink->mainloop, 0);
+ pa_threaded_mainloop_signal (mainloop, 0);
break;
case PA_STREAM_UNCONNECTED:
case PA_STREAM_CREATING:
if (pbuf->in_commit && (length >= rbuf->spec.segsize)) {
/* only signal when we are waiting in the commit thread
* and got request for atleast a segment */
- pa_threaded_mainloop_signal (psink->mainloop, 0);
+ pa_threaded_mainloop_signal (mainloop, 0);
}
}
if (!gst_pulse_fill_sample_spec (spec, &pbuf->sample_spec))
goto invalid_spec;
- pa_threaded_mainloop_lock (psink->mainloop);
+ pa_threaded_mainloop_lock (mainloop);
/* we need a context and a no stream */
g_assert (pbuf->context);
break;
/* Wait until the stream is ready */
- pa_threaded_mainloop_wait (psink->mainloop);
+ pa_threaded_mainloop_wait (mainloop);
}
/* After we passed the volume off of to PA we never want to set it
spec->segsize = actual->minreq;
spec->segtotal = actual->tlength / spec->segsize;
- pa_threaded_mainloop_unlock (psink->mainloop);
+ pa_threaded_mainloop_unlock (mainloop);
return TRUE;
unlock_and_fail:
{
gst_pulsering_destroy_stream (pbuf);
- pa_threaded_mainloop_unlock (psink->mainloop);
+ pa_threaded_mainloop_unlock (mainloop);
return FALSE;
}
psink = GST_PULSESINK_CAST (GST_OBJECT_PARENT (buf));
pbuf = GST_PULSERING_BUFFER_CAST (buf);
- pa_threaded_mainloop_lock (psink->mainloop);
+ pa_threaded_mainloop_lock (mainloop);
gst_pulsering_destroy_stream (pbuf);
- pa_threaded_mainloop_unlock (psink->mainloop);
+ pa_threaded_mainloop_unlock (mainloop);
return TRUE;
}
pbuf = GST_PULSERING_BUFFER_CAST (userdata);
psink = GST_PULSESINK_CAST (GST_OBJECT_PARENT (pbuf));
- pa_threaded_mainloop_signal (psink->mainloop, 0);
+ pa_threaded_mainloop_signal (mainloop, 0);
}
/* update the corked state of a stream, must be called with the mainloop
goto cork_failed;
while (wait && pa_operation_get_state (o) == PA_OPERATION_RUNNING) {
- pa_threaded_mainloop_wait (psink->mainloop);
+ pa_threaded_mainloop_wait (mainloop);
if (gst_pulsering_is_dead (psink, pbuf))
goto server_dead;
}
pbuf = GST_PULSERING_BUFFER_CAST (buf);
psink = GST_PULSESINK_CAST (GST_OBJECT_PARENT (pbuf));
- pa_threaded_mainloop_lock (psink->mainloop);
+ pa_threaded_mainloop_lock (mainloop);
GST_DEBUG_OBJECT (psink, "clearing");
if (pbuf->stream) {
/* don't wait for the flush to complete */
if ((o = pa_stream_flush (pbuf->stream, NULL, pbuf)))
pa_operation_unref (o);
}
- pa_threaded_mainloop_unlock (psink->mainloop);
+ pa_threaded_mainloop_unlock (mainloop);
}
static void
/* signal the waiter */
pulsesink->pa_defer_ran = TRUE;
- pa_threaded_mainloop_signal (pulsesink->mainloop, 0);
+ pa_threaded_mainloop_signal (mainloop, 0);
}
/* start/resume playback ASAP, we don't uncork here but in the commit method */
pbuf = GST_PULSERING_BUFFER_CAST (buf);
psink = GST_PULSESINK_CAST (GST_OBJECT_PARENT (pbuf));
- pa_threaded_mainloop_lock (psink->mainloop);
+ pa_threaded_mainloop_lock (mainloop);
GST_DEBUG_OBJECT (psink, "scheduling stream status");
psink->pa_defer_ran = FALSE;
- pa_mainloop_api_once (pa_threaded_mainloop_get_api (psink->mainloop),
+ pa_mainloop_api_once (pa_threaded_mainloop_get_api (mainloop),
mainloop_enter_defer_cb, psink);
GST_DEBUG_OBJECT (psink, "starting");
pbuf->paused = FALSE;
gst_pulsering_set_corked (pbuf, FALSE, FALSE);
- pa_threaded_mainloop_unlock (psink->mainloop);
+ pa_threaded_mainloop_unlock (mainloop);
return TRUE;
}
pbuf = GST_PULSERING_BUFFER_CAST (buf);
psink = GST_PULSESINK_CAST (GST_OBJECT_PARENT (pbuf));
- pa_threaded_mainloop_lock (psink->mainloop);
+ pa_threaded_mainloop_lock (mainloop);
GST_DEBUG_OBJECT (psink, "pausing and corking");
/* make sure the commit method stops writing */
pbuf->paused = TRUE;
if (pbuf->in_commit) {
/* we are waiting in a commit, signal */
GST_DEBUG_OBJECT (psink, "signal commit");
- pa_threaded_mainloop_signal (psink->mainloop, 0);
+ pa_threaded_mainloop_signal (mainloop, 0);
}
- pa_threaded_mainloop_unlock (psink->mainloop);
+ pa_threaded_mainloop_unlock (mainloop);
return res;
}
gst_element_post_message (GST_ELEMENT (pulsesink), message);
pulsesink->pa_defer_ran = TRUE;
- pa_threaded_mainloop_signal (pulsesink->mainloop, 0);
+ pa_threaded_mainloop_signal (mainloop, 0);
gst_object_unref (pulsesink);
}
pbuf = GST_PULSERING_BUFFER_CAST (buf);
psink = GST_PULSESINK_CAST (GST_OBJECT_PARENT (pbuf));
- pa_threaded_mainloop_lock (psink->mainloop);
+ pa_threaded_mainloop_lock (mainloop);
pbuf->paused = TRUE;
res = gst_pulsering_set_corked (pbuf, TRUE, TRUE);
/* Inform anyone waiting in _commit() call that it shall wakeup */
if (pbuf->in_commit) {
GST_DEBUG_OBJECT (psink, "signal commit thread");
- pa_threaded_mainloop_signal (psink->mainloop, 0);
+ pa_threaded_mainloop_signal (mainloop, 0);
}
if (strcmp (psink->pa_version, "0.9.12")) {
if ((o = pa_stream_flush (pbuf->stream, gst_pulsering_success_cb, pbuf))) {
while (pa_operation_get_state (o) == PA_OPERATION_RUNNING) {
GST_DEBUG_OBJECT (psink, "wait for completion");
- pa_threaded_mainloop_wait (psink->mainloop);
+ pa_threaded_mainloop_wait (mainloop);
if (gst_pulsering_is_dead (psink, pbuf))
goto server_dead;
}
GST_DEBUG_OBJECT (psink, "scheduling stream status");
psink->pa_defer_ran = FALSE;
gst_object_ref (psink);
- pa_mainloop_api_once (pa_threaded_mainloop_get_api (psink->mainloop),
+ pa_mainloop_api_once (pa_threaded_mainloop_get_api (mainloop),
mainloop_leave_defer_cb, psink);
GST_DEBUG_OBJECT (psink, "waiting for stream status");
- pa_threaded_mainloop_unlock (psink->mainloop);
+ pa_threaded_mainloop_unlock (mainloop);
return res;
goto start_failed;
}
- pa_threaded_mainloop_lock (psink->mainloop);
+ pa_threaded_mainloop_lock (mainloop);
GST_DEBUG_OBJECT (psink, "entering commit");
pbuf->in_commit = TRUE;
/* we can't write a single byte, wait a bit */
GST_LOG_OBJECT (psink, "waiting for free space");
- pa_threaded_mainloop_wait (psink->mainloop);
+ pa_threaded_mainloop_wait (mainloop);
if (pbuf->paused)
goto was_paused;
/* we can't write a single byte, wait a bit */
GST_LOG_OBJECT (psink, "waiting for free space");
- pa_threaded_mainloop_wait (psink->mainloop);
+ pa_threaded_mainloop_wait (mainloop);
if (pbuf->paused)
goto was_paused;
data = data_end + bps;
pbuf->in_commit = FALSE;
- pa_threaded_mainloop_unlock (psink->mainloop);
+ pa_threaded_mainloop_unlock (mainloop);
done:
result = inr - ((data_end - data) / bps);
{
pbuf->in_commit = FALSE;
GST_LOG_OBJECT (psink, "we are reset");
- pa_threaded_mainloop_unlock (psink->mainloop);
+ pa_threaded_mainloop_unlock (mainloop);
goto done;
}
no_start:
{
pbuf->in_commit = FALSE;
GST_ERROR_OBJECT (psink, "uncork failed");
- pa_threaded_mainloop_unlock (psink->mainloop);
+ pa_threaded_mainloop_unlock (mainloop);
goto done;
}
was_paused:
{
pbuf->in_commit = FALSE;
GST_LOG_OBJECT (psink, "we are paused");
- pa_threaded_mainloop_unlock (psink->mainloop);
+ pa_threaded_mainloop_unlock (mainloop);
goto done;
}
writable_size_failed:
pbuf = GST_PULSERING_BUFFER_CAST (sink->ringbuffer);
psink = GST_PULSESINK_CAST (GST_OBJECT_PARENT (pbuf));
- pa_threaded_mainloop_lock (psink->mainloop);
+ pa_threaded_mainloop_lock (mainloop);
if (gst_pulsering_is_dead (psink, pbuf))
goto server_dead;
time = GST_CLOCK_TIME_NONE;
} else
time *= 1000;
- pa_threaded_mainloop_unlock (psink->mainloop);
+ pa_threaded_mainloop_unlock (mainloop);
GST_LOG_OBJECT (psink, "current time is %" GST_TIME_FORMAT,
GST_TIME_ARGS (time));
server_dead:
{
GST_DEBUG_OBJECT (psink, "the server is dead");
- pa_threaded_mainloop_unlock (psink->mainloop);
+ pa_threaded_mainloop_unlock (mainloop);
return GST_CLOCK_TIME_NONE;
}
GstPulseRingBuffer *pbuf;
uint32_t idx;
- if (!psink->mainloop)
+ if (!mainloop)
goto no_mainloop;
- pa_threaded_mainloop_lock (psink->mainloop);
+ pa_threaded_mainloop_lock (mainloop);
GST_DEBUG_OBJECT (psink, "setting volume to %f", volume);
if (o)
pa_operation_unref (o);
- pa_threaded_mainloop_unlock (psink->mainloop);
+ pa_threaded_mainloop_unlock (mainloop);
return;
GstPulseRingBuffer *pbuf;
uint32_t idx;
- if (!psink->mainloop)
+ if (!mainloop)
goto no_mainloop;
- pa_threaded_mainloop_lock (psink->mainloop);
+ pa_threaded_mainloop_lock (mainloop);
GST_DEBUG_OBJECT (psink, "setting mute state to %d", mute);
if (o)
pa_operation_unref (o);
- pa_threaded_mainloop_unlock (psink->mainloop);
+ pa_threaded_mainloop_unlock (mainloop);
return;
}
done:
- pa_threaded_mainloop_signal (psink->mainloop, 0);
+ pa_threaded_mainloop_signal (mainloop, 0);
}
static gdouble
gdouble v = DEFAULT_VOLUME;
uint32_t idx;
- if (!psink->mainloop)
+ if (!mainloop)
goto no_mainloop;
- pa_threaded_mainloop_lock (psink->mainloop);
+ pa_threaded_mainloop_lock (mainloop);
pbuf = GST_PULSERING_BUFFER_CAST (GST_BASE_AUDIO_SINK (psink)->ringbuffer);
if (pbuf == NULL || pbuf->stream == NULL)
goto info_failed;
while (pa_operation_get_state (o) == PA_OPERATION_RUNNING) {
- pa_threaded_mainloop_wait (psink->mainloop);
+ pa_threaded_mainloop_wait (mainloop);
if (gst_pulsering_is_dead (psink, pbuf))
goto unlock;
}
if (o)
pa_operation_unref (o);
- pa_threaded_mainloop_unlock (psink->mainloop);
+ pa_threaded_mainloop_unlock (mainloop);
if (v > MAX_VOLUME) {
GST_WARNING_OBJECT (psink, "Clipped volume from %f to %f", v, MAX_VOLUME);
uint32_t idx;
gboolean mute = FALSE;
- if (!psink->mainloop)
+ if (!mainloop)
goto no_mainloop;
- pa_threaded_mainloop_lock (psink->mainloop);
+ pa_threaded_mainloop_lock (mainloop);
mute = psink->mute;
pbuf = GST_PULSERING_BUFFER_CAST (GST_BASE_AUDIO_SINK (psink)->ringbuffer);
goto info_failed;
while (pa_operation_get_state (o) == PA_OPERATION_RUNNING) {
- pa_threaded_mainloop_wait (psink->mainloop);
+ pa_threaded_mainloop_wait (mainloop);
if (gst_pulsering_is_dead (psink, pbuf))
goto unlock;
}
if (o)
pa_operation_unref (o);
- pa_threaded_mainloop_unlock (psink->mainloop);
+ pa_threaded_mainloop_unlock (mainloop);
return mute;
psink->device_description = g_strdup (i->description);
done:
- pa_threaded_mainloop_signal (psink->mainloop, 0);
+ pa_threaded_mainloop_signal (mainloop, 0);
}
static gchar *
pa_operation *o = NULL;
gchar *t;
- if (!psink->mainloop)
+ if (!mainloop)
goto no_mainloop;
- pa_threaded_mainloop_lock (psink->mainloop);
+ pa_threaded_mainloop_lock (mainloop);
pbuf = GST_PULSERING_BUFFER_CAST (GST_BASE_AUDIO_SINK (psink)->ringbuffer);
if (pbuf == NULL || pbuf->stream == NULL)
goto no_buffer;
goto info_failed;
while (pa_operation_get_state (o) == PA_OPERATION_RUNNING) {
- pa_threaded_mainloop_wait (psink->mainloop);
+ pa_threaded_mainloop_wait (mainloop);
if (gst_pulsering_is_dead (psink, pbuf))
goto unlock;
}
pa_operation_unref (o);
t = g_strdup (psink->device_description);
- pa_threaded_mainloop_unlock (psink->mainloop);
+ pa_threaded_mainloop_unlock (mainloop);
return t;
pa_operation *o = NULL;
GstPulseRingBuffer *pbuf;
- pa_threaded_mainloop_lock (psink->mainloop);
+ pa_threaded_mainloop_lock (mainloop);
pbuf = GST_PULSERING_BUFFER_CAST (GST_BASE_AUDIO_SINK (psink)->ringbuffer);
if (o)
pa_operation_unref (o);
- pa_threaded_mainloop_unlock (psink->mainloop);
+ pa_threaded_mainloop_unlock (mainloop);
return;
if (empty)
goto finish;
- pa_threaded_mainloop_lock (psink->mainloop);
+ pa_threaded_mainloop_lock (mainloop);
pbuf = GST_PULSERING_BUFFER_CAST (GST_BASE_AUDIO_SINK (psink)->ringbuffer);
if (pbuf == NULL || pbuf->stream == NULL)
goto no_buffer;
if (o)
pa_operation_unref (o);
- pa_threaded_mainloop_unlock (psink->mainloop);
+ pa_threaded_mainloop_unlock (mainloop);
finish:
gst_pulsesink_change_state (GstElement * element, GstStateChange transition)
{
GstPulseSink *pulsesink = GST_PULSESINK (element);
- GstPulseSinkClass *klass = GST_PULSESINK_GET_CLASS (pulsesink);
GstStateChangeReturn ret;
guint res;
switch (transition) {
case GST_STATE_CHANGE_NULL_TO_READY:
g_mutex_lock (pa_shared_ressource_mutex);
- if (!klass->main_loop_ref_ct) {
+ if (!mainloop_ref_ct) {
GST_INFO_OBJECT (element, "new pa main loop thread");
- klass->mainloop = pa_threaded_mainloop_new ();
- if (!klass->mainloop)
+ if (!(mainloop = pa_threaded_mainloop_new ()))
goto mainloop_failed;
- klass->main_loop_ref_ct = 1;
- res = pa_threaded_mainloop_start (klass->mainloop);
+ mainloop_ref_ct = 1;
+ res = pa_threaded_mainloop_start (mainloop);
g_assert (res == 0);
g_mutex_unlock (pa_shared_ressource_mutex);
-
- pulsesink->mainloop = klass->mainloop;
} else {
GST_INFO_OBJECT (element, "reusing pa main loop thread");
- pulsesink->mainloop = klass->mainloop;
- klass->main_loop_ref_ct++;
+ mainloop_ref_ct++;
g_mutex_unlock (pa_shared_ressource_mutex);
}
break;
GST_BASE_AUDIO_SINK (pulsesink)->provided_clock));
break;
case GST_STATE_CHANGE_READY_TO_NULL:
- if (pulsesink->mainloop) {
+ if (mainloop) {
g_mutex_lock (pa_shared_ressource_mutex);
- klass->main_loop_ref_ct--;
- pulsesink->mainloop = NULL;
- if (!klass->main_loop_ref_ct) {
+ mainloop_ref_ct--;
+ if (!mainloop_ref_ct) {
GST_INFO_OBJECT (element, "terminating pa main loop thread");
- pa_threaded_mainloop_stop (klass->mainloop);
- pa_threaded_mainloop_free (klass->mainloop);
- klass->mainloop = NULL;
+ pa_threaded_mainloop_stop (mainloop);
+ pa_threaded_mainloop_free (mainloop);
+ mainloop = NULL;
}
g_mutex_unlock (pa_shared_ressource_mutex);
}
{
if (transition == GST_STATE_CHANGE_NULL_TO_READY) {
/* Clear the PA mainloop if baseaudiosink failed to open the ring_buffer */
- g_assert (pulsesink->mainloop);
+ g_assert (mainloop);
g_mutex_lock (pa_shared_ressource_mutex);
- klass->main_loop_ref_ct--;
- pulsesink->mainloop = NULL;
- if (!klass->main_loop_ref_ct) {
+ mainloop_ref_ct--;
+ if (!mainloop_ref_ct) {
GST_INFO_OBJECT (element, "terminating pa main loop thread");
- pa_threaded_mainloop_stop (klass->mainloop);
- pa_threaded_mainloop_free (klass->mainloop);
- klass->mainloop = NULL;
+ pa_threaded_mainloop_stop (mainloop);
+ pa_threaded_mainloop_free (mainloop);
+ mainloop = NULL;
}
g_mutex_unlock (pa_shared_ressource_mutex);
}