diff options
author | Jan Schmidt <thaytan@noraisin.net> | 2009-01-28 20:34:40 +0000 |
---|---|---|
committer | Jan Schmidt <thaytan@noraisin.net> | 2009-01-28 20:34:40 +0000 |
commit | 0037635bf2c9f6e3db0c97425d2b113b6f47fe7f (patch) | |
tree | d50e8321bf5a6aa17f69d4daaa4e1217dd7183e8 /ext/pulse/pulsesrc.c | |
parent | 9cf73bdd8f18505269fa4b6d1d80127945e735b1 (diff) |
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 <lennart@poettering.net>
Diffstat (limited to 'ext/pulse/pulsesrc.c')
-rw-r--r-- | ext/pulse/pulsesrc.c | 377 |
1 files changed, 305 insertions, 72 deletions
diff --git a/ext/pulse/pulsesrc.c b/ext/pulse/pulsesrc.c index 08fec595..bd05ffa1 100644 --- a/ext/pulse/pulsesrc.c +++ b/ext/pulse/pulsesrc.c @@ -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) @@ -347,6 +378,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; unlock_and_fail: + gst_pulsesrc_destroy_stream (pulsesrc); + pa_threaded_mainloop_unlock (pulsesrc->mainloop); fail: @@ -776,27 +890,42 @@ gst_pulsesrc_prepare (GstAudioSrc * asrc, GstRingBufferSpec * spec) pa_buffer_attr buf_attr; GstPulseSrc *pulsesrc = GST_PULSESRC (asrc); + pa_threaded_mainloop_lock (pulsesrc->mainloop); + memset (&buf_attr, 0, sizeof (buf_attr)); buf_attr.maxlength = spec->segtotal * spec->segsize * 2; buf_attr.fragsize = spec->segsize; if (pa_stream_connect_record (pulsesrc->stream, pulsesrc->device, &buf_attr, - PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE | - PA_STREAM_NOT_MONOTONOUS) < 0) { + PA_STREAM_INTERPOLATE_TIMING | + PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_NOT_MONOTONOUS | +#if HAVE_PULSE_0_9_11 + PA_STREAM_ADJUST_LATENCY | +#endif + PA_STREAM_START_CORKED) < 0) { GST_ELEMENT_ERROR (pulsesrc, RESOURCE, FAILED, ("Failed to connect stream: %s", pa_strerror (pa_context_errno (pulsesrc->context))), (NULL)); goto unlock_and_fail; } - /* Wait until the stream is ready */ - pa_threaded_mainloop_wait (pulsesrc->mainloop); + for (;;) { + pa_stream_state_t state; - if (pa_stream_get_state (pulsesrc->stream) != PA_STREAM_READY) { - GST_ELEMENT_ERROR (pulsesrc, RESOURCE, FAILED, - ("Failed to connect stream: %s", - pa_strerror (pa_context_errno (pulsesrc->context))), (NULL)); - goto unlock_and_fail; + state = pa_stream_get_state (pulsesrc->stream); + + if (!PA_STREAM_IS_GOOD (state)) { + GST_ELEMENT_ERROR (pulsesrc, RESOURCE, FAILED, + ("Failed to connect stream: %s", + pa_strerror (pa_context_errno (pulsesrc->context))), (NULL)); + goto unlock_and_fail; + } + + if (state == PA_STREAM_READY) + break; + + /* Wait until the stream is ready */ + pa_threaded_mainloop_wait (pulsesrc->mainloop); } pa_threaded_mainloop_unlock (pulsesrc->mainloop); @@ -804,16 +933,120 @@ gst_pulsesrc_prepare (GstAudioSrc * asrc, GstRingBufferSpec * spec) return TRUE; unlock_and_fail: + + gst_pulsesrc_destroy_stream (pulsesrc); + pa_threaded_mainloop_unlock (pulsesrc->mainloop); return FALSE; } +static void +gst_pulsesrc_success_cb (pa_stream * s, int success, void *userdata) +{ + GstPulseSrc *pulsesrc = GST_PULSESRC (userdata); + + pulsesrc->operation_success = !!success; + pa_threaded_mainloop_signal (pulsesrc->mainloop, 0); +} + +static void +gst_pulsesrc_reset (GstAudioSrc * asrc) +{ + GstPulseSrc *pulsesrc = GST_PULSESRC (asrc); + pa_operation *o = NULL; + + pa_threaded_mainloop_lock (pulsesrc->mainloop); + + if (gst_pulsesrc_is_dead (pulsesrc)) + goto unlock_and_fail; + + if (!(o = + pa_stream_flush (pulsesrc->stream, gst_pulsesrc_success_cb, + pulsesrc))) { + GST_ELEMENT_ERROR (pulsesrc, RESOURCE, FAILED, + ("pa_stream_flush() failed: %s", + pa_strerror (pa_context_errno (pulsesrc->context))), (NULL)); + goto unlock_and_fail; + } + + /* Inform anyone waiting in _write() call that it shall wakeup */ + if (pulsesrc->in_read) { + pulsesrc->did_reset = TRUE; + pa_threaded_mainloop_signal (pulsesrc->mainloop, 0); + } + + pulsesrc->operation_success = FALSE; + while (pa_operation_get_state (o) == PA_OPERATION_RUNNING) { + + if (gst_pulsesrc_is_dead (pulsesrc)) + goto unlock_and_fail; + + pa_threaded_mainloop_wait (pulsesrc->mainloop); + } + + if (!pulsesrc->operation_success) { + GST_ELEMENT_ERROR (pulsesrc, RESOURCE, FAILED, ("Flush failed: %s", + pa_strerror (pa_context_errno (pulsesrc->context))), (NULL)); + goto unlock_and_fail; + } + +unlock_and_fail: + + if (o) { + pa_operation_cancel (o); + pa_operation_unref (o); + } + + pa_threaded_mainloop_unlock (pulsesrc->mainloop); +} + +static void +gst_pulsesrc_pause (GstPulseSrc * pulsesrc, gboolean b) +{ + pa_operation *o = NULL; + + pa_threaded_mainloop_lock (pulsesrc->mainloop); + + if (gst_pulsesrc_is_dead (pulsesrc)) + goto unlock; + + if (!(o = pa_stream_cork (pulsesrc->stream, b, NULL, NULL))) { + + GST_ELEMENT_ERROR (pulsesrc, RESOURCE, FAILED, + ("pa_stream_cork() 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); + + pa_threaded_mainloop_unlock (pulsesrc->mainloop); +} + static GstStateChangeReturn gst_pulsesrc_change_state (GstElement * element, GstStateChange transition) { GstPulseSrc *this = GST_PULSESRC (element); switch (transition) { + + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + gst_pulsesrc_pause (this, + GST_STATE_TRANSITION_NEXT (transition) == GST_STATE_PAUSED); + break; + case GST_STATE_CHANGE_NULL_TO_READY: if (!this->mixer) |