diff --git a/ChangeLog b/ChangeLog index b1df7d79c4..90af8bea26 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,34 @@ +2005-08-08 Andy Wingo + + * gst-libs/gst/audio/gstbaseaudiosink.c + (gst_base_audio_sink_change_state): Open the device in NULL->READY + like good elements should. Close on READY->NULL too. + + * gst-libs/gst/audio/gstaudiosink.c + (gst_audioringbuffer_open_device, + (gst_audioringbuffer_close_device, gst_audioringbuffer_acquire) + (gst_audioringbuffer_release): Updates for new ring buffer API, + hook into the new audio sink api. + + * gst-libs/gst/audio/gstaudiosink.h (GstAudioSinkClass.open) + (GstAudioSinkClass.close): Just open and close the device -- no + resource allocation or configuration. + (GstAudioSinkClass.prepare, GstAudioSinkClass.unprepare): New + vmethods, handle device setup and resource allocation. + + * ext/alsa/gstalsasink.c (gst_alsasink_open, gst_alsasink_close) + (gst_alsasink_prepare, gst_alsasink_unprepare): Update for new + base class API. + + * gst-libs/gst/audio/gstringbuffer.h + (GstRingBufferClass.open_device, GstRingBufferClass.close_device): + New vmethods. + + * gst-libs/gst/audio/gstringbuffer.c (gst_ring_buffer_open_device) + (gst_ring_buffer_close_device, gst_ring_buffer_device_is_open): + New API functions. The device should be opened before acquiring + and closed after releasing. + 2005-08-08 Tim-Philipp Müller * gst-libs/gst/interfaces/mixer.h: diff --git a/ext/alsa/gstalsasink.c b/ext/alsa/gstalsasink.c index aaf8c72ece..e8859f96da 100644 --- a/ext/alsa/gstalsasink.c +++ b/ext/alsa/gstalsasink.c @@ -57,8 +57,10 @@ static void gst_alsasink_get_property (GObject * object, static GstCaps *gst_alsasink_getcaps (GstBaseSink * bsink); -static gboolean gst_alsasink_open (GstAudioSink * asink, +static gboolean gst_alsasink_open (GstAudioSink * asink); +static gboolean gst_alsasink_prepare (GstAudioSink * asink, GstRingBufferSpec * spec); +static gboolean gst_alsasink_unprepare (GstAudioSink * asink); static gboolean gst_alsasink_close (GstAudioSink * asink); static guint gst_alsasink_write (GstAudioSink * asink, gpointer data, guint length); @@ -163,6 +165,8 @@ gst_alsasink_class_init (GstAlsaSinkClass * klass) gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_alsasink_getcaps); gstaudiosink_class->open = GST_DEBUG_FUNCPTR (gst_alsasink_open); + gstaudiosink_class->prepare = GST_DEBUG_FUNCPTR (gst_alsasink_prepare); + gstaudiosink_class->unprepare = GST_DEBUG_FUNCPTR (gst_alsasink_unprepare); gstaudiosink_class->close = GST_DEBUG_FUNCPTR (gst_alsasink_close); gstaudiosink_class->write = GST_DEBUG_FUNCPTR (gst_alsasink_write); gstaudiosink_class->delay = GST_DEBUG_FUNCPTR (gst_alsasink_delay); @@ -480,7 +484,29 @@ error: } static gboolean -gst_alsasink_open (GstAudioSink * asink, GstRingBufferSpec * spec) +gst_alsasink_open (GstAudioSink * asink) +{ + GstAlsaSink *alsa; + gint err; + + alsa = GST_ALSA_SINK (asink); + + CHECK (snd_pcm_open (&alsa->handle, alsa->device, SND_PCM_STREAM_PLAYBACK, + SND_PCM_NONBLOCK), open_error); + + return TRUE; + + /* ERRORS */ +open_error: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ, + ("Playback open error: %s", snd_strerror (err)), (NULL)); + return FALSE; + } +} + +static gboolean +gst_alsasink_prepare (GstAudioSink * asink, GstRingBufferSpec * spec) { GstAlsaSink *alsa; gint err; @@ -490,9 +516,6 @@ gst_alsasink_open (GstAudioSink * asink, GstRingBufferSpec * spec) if (!alsasink_parse_spec (alsa, spec)) goto spec_parse; - CHECK (snd_pcm_open (&alsa->handle, alsa->device, SND_PCM_STREAM_PLAYBACK, - SND_PCM_NONBLOCK), open_error); - CHECK (snd_pcm_nonblock (alsa->handle, 0), non_block); CHECK (set_hwparams (alsa), hw_params_failed); @@ -515,12 +538,6 @@ spec_parse: ("Error parsing spec"), (NULL)); return FALSE; } -open_error: - { - GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ, - ("Playback open error: %s", snd_strerror (err)), (NULL)); - return FALSE; - } non_block: { GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ, @@ -542,12 +559,48 @@ sw_params_failed: } static gboolean -gst_alsasink_close (GstAudioSink * asink) +gst_alsasink_unprepare (GstAudioSink * asink) { GstAlsaSink *alsa; + gint err; alsa = GST_ALSA_SINK (asink); + CHECK (snd_pcm_drop (alsa->handle), drop); + + CHECK (snd_pcm_hw_free (alsa->handle), hw_free); + + CHECK (snd_pcm_nonblock (alsa->handle, 1), non_block); + + return TRUE; + + /* ERRORS */ +drop: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ, + ("Could not drop samples: %s", snd_strerror (err)), (NULL)); + return FALSE; + } +hw_free: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ, + ("Could not free hw params: %s", snd_strerror (err)), (NULL)); + return FALSE; + } +non_block: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ, + ("Could not set device to nonblocking: %s", snd_strerror (err)), + (NULL)); + return FALSE; + } +} + +static gboolean +gst_alsasink_close (GstAudioSink * asink) +{ + GstAlsaSink *alsa = GST_ALSA_SINK (asink); + snd_pcm_close (alsa->handle); return TRUE; diff --git a/gst-libs/gst/audio/gstaudiosink.c b/gst-libs/gst/audio/gstaudiosink.c index f46084d4e7..8c8e87cd74 100644 --- a/gst-libs/gst/audio/gstaudiosink.c +++ b/gst-libs/gst/audio/gstaudiosink.c @@ -70,6 +70,8 @@ static void gst_audioringbuffer_finalize (GObject * object); static GstRingBufferClass *ring_parent_class = NULL; +static gboolean gst_audioringbuffer_open_device (GstRingBuffer * buf); +static gboolean gst_audioringbuffer_close_device (GstRingBuffer * buf); static gboolean gst_audioringbuffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec); static gboolean gst_audioringbuffer_release (GstRingBuffer * buf); @@ -120,6 +122,10 @@ gst_audioringbuffer_class_init (GstAudioRingBufferClass * klass) gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_audioringbuffer_dispose); gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_audioringbuffer_finalize); + gstringbuffer_class->open_device = + GST_DEBUG_FUNCPTR (gst_audioringbuffer_open_device); + gstringbuffer_class->close_device = + GST_DEBUG_FUNCPTR (gst_audioringbuffer_close_device); gstringbuffer_class->acquire = GST_DEBUG_FUNCPTR (gst_audioringbuffer_acquire); gstringbuffer_class->release = @@ -235,6 +241,54 @@ gst_audioringbuffer_finalize (GObject * object) G_OBJECT_CLASS (ring_parent_class)->finalize (object); } +static gboolean +gst_audioringbuffer_open_device (GstRingBuffer * buf) +{ + GstAudioSink *sink; + GstAudioSinkClass *csink; + gboolean result = TRUE; + + sink = GST_AUDIO_SINK (GST_OBJECT_PARENT (buf)); + csink = GST_AUDIO_SINK_GET_CLASS (sink); + + if (csink->open) + result = csink->open (sink); + + if (!result) + goto could_not_open; + + return result; + +could_not_open: + { + return FALSE; + } +} + +static gboolean +gst_audioringbuffer_close_device (GstRingBuffer * buf) +{ + GstAudioSink *sink; + GstAudioSinkClass *csink; + gboolean result = TRUE; + + sink = GST_AUDIO_SINK (GST_OBJECT_PARENT (buf)); + csink = GST_AUDIO_SINK_GET_CLASS (sink); + + if (csink->close) + result = csink->close (sink); + + if (!result) + goto could_not_open; + + return result; + +could_not_open: + { + return FALSE; + } +} + static gboolean gst_audioringbuffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec) { @@ -246,8 +300,8 @@ gst_audioringbuffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec) sink = GST_AUDIO_SINK (GST_OBJECT_PARENT (buf)); csink = GST_AUDIO_SINK_GET_CLASS (sink); - if (csink->open) - result = csink->open (sink, spec); + if (csink->prepare) + result = csink->prepare (sink, spec); if (!result) goto could_not_open; @@ -300,8 +354,8 @@ gst_audioringbuffer_release (GstRingBuffer * buf) gst_buffer_unref (buf->data); buf->data = NULL; - if (csink->close) - result = csink->close (sink); + if (csink->unprepare) + result = csink->unprepare (sink); return result; } diff --git a/gst-libs/gst/audio/gstaudiosink.h b/gst-libs/gst/audio/gstaudiosink.h index 70544d3e02..b724257190 100644 --- a/gst-libs/gst/audio/gstaudiosink.h +++ b/gst-libs/gst/audio/gstaudiosink.h @@ -67,15 +67,19 @@ struct _GstAudioSinkClass { /* vtable */ /* open the device with given specs */ - gboolean (*open) (GstAudioSink *sink, GstRingBufferSpec *spec); + gboolean (*open) (GstAudioSink *sink); + /* prepare resources and state to operate with the given specs */ + gboolean (*prepare) (GstAudioSink *sink, GstRingBufferSpec *spec); + /* undo anything that was done in prepare() */ + gboolean (*unprepare) (GstAudioSink *sink); /* close the device */ - gboolean (*close) (GstAudioSink *sink); + gboolean (*close) (GstAudioSink *sink); /* write samples to the device */ - guint (*write) (GstAudioSink *sink, gpointer data, guint length); + guint (*write) (GstAudioSink *sink, gpointer data, guint length); /* get number of samples queued in the device */ - guint (*delay) (GstAudioSink *sink); + guint (*delay) (GstAudioSink *sink); /* reset the audio device, unblock from a write */ - void (*reset) (GstAudioSink *sink); + void (*reset) (GstAudioSink *sink); }; GType gst_audio_sink_get_type(void); diff --git a/gst-libs/gst/audio/gstbaseaudiosink.c b/gst-libs/gst/audio/gstbaseaudiosink.c index 92d93979c8..52fb1c02dc 100644 --- a/gst-libs/gst/audio/gstbaseaudiosink.c +++ b/gst-libs/gst/audio/gstbaseaudiosink.c @@ -419,13 +419,15 @@ gst_base_audio_sink_change_state (GstElement * element) switch (transition) { case GST_STATE_NULL_TO_READY: - break; - case GST_STATE_READY_TO_PAUSED: if (sink->ringbuffer == NULL) { sink->ringbuffer = gst_base_audio_sink_create_ringbuffer (sink); gst_ring_buffer_set_callback (sink->ringbuffer, gst_base_audio_sink_callback, sink); } + if (!gst_ring_buffer_open_device (sink->ringbuffer)) + return GST_STATE_FAILURE; + break; + case GST_STATE_READY_TO_PAUSED: break; case GST_STATE_PAUSED_TO_PLAYING: break; @@ -441,10 +443,11 @@ gst_base_audio_sink_change_state (GstElement * element) break; case GST_STATE_PAUSED_TO_READY: gst_ring_buffer_stop (sink->ringbuffer); - gst_ring_buffer_release (sink->ringbuffer); gst_pad_set_caps (GST_BASE_SINK_PAD (sink), NULL); + gst_ring_buffer_release (sink->ringbuffer); break; case GST_STATE_READY_TO_NULL: + gst_ring_buffer_close_device (sink->ringbuffer); break; default: break; diff --git a/gst-libs/gst/audio/gstringbuffer.c b/gst-libs/gst/audio/gstringbuffer.c index 05eebb736a..765d07ba19 100644 --- a/gst-libs/gst/audio/gstringbuffer.c +++ b/gst-libs/gst/audio/gstringbuffer.c @@ -80,6 +80,7 @@ gst_ring_buffer_class_init (GstRingBufferClass * klass) static void gst_ring_buffer_init (GstRingBuffer * ringbuffer) { + ringbuffer->open = FALSE; ringbuffer->acquired = FALSE; ringbuffer->state = GST_RING_BUFFER_STATE_STOPPED; ringbuffer->cond = g_cond_new (); @@ -327,6 +328,127 @@ gst_ring_buffer_set_callback (GstRingBuffer * buf, GstRingBufferCallback cb, GST_UNLOCK (buf); } + +/** + * gst_ring_buffer_open_device: + * @buf: the #GstRingBuffer + * + * Open the audio device associated with the ring buffer. Does not perform any + * setup on the device. You must open the device before acquiring the ring + * buffer. + * + * Returns: TRUE if the device could be opened, FALSE on error. + * + * MT safe. + */ +gboolean +gst_ring_buffer_open_device (GstRingBuffer * buf) +{ + gboolean res = TRUE; + GstRingBufferClass *rclass; + + g_return_val_if_fail (GST_IS_RING_BUFFER (buf), FALSE); + + GST_LOCK (buf); + if (buf->open) { + g_warning ("Device for ring buffer %p already open, fix your code", buf); + res = TRUE; + goto done; + } + buf->open = TRUE; + + /* if this fails, something is wrong in this file */ + g_assert (!buf->acquired); + + rclass = GST_RING_BUFFER_GET_CLASS (buf); + if (rclass->open_device) + res = rclass->open_device (buf); + + if (!res) { + buf->open = FALSE; + } + +done: + GST_UNLOCK (buf); + + return res; +} + +/** + * gst_ring_buffer_close_device: + * @buf: the #GstRingBuffer + * + * Close the audio device associated with the ring buffer. The ring buffer + * should already have been released via gst_ring_buffer_release(). + * + * Returns: TRUE if the device could be closed, FALSE on error. + * + * MT safe. + */ +gboolean +gst_ring_buffer_close_device (GstRingBuffer * buf) +{ + gboolean res = TRUE; + GstRingBufferClass *rclass; + + g_return_val_if_fail (GST_IS_RING_BUFFER (buf), FALSE); + + GST_LOCK (buf); + if (!buf->open) { + g_warning ("Device for ring buffer %p already closed, fix your code", buf); + res = TRUE; + goto done; + } + + if (buf->acquired) { + g_critical ("Resources for ring buffer %p still acquired", buf); + res = FALSE; + goto done; + } + + buf->open = FALSE; + + rclass = GST_RING_BUFFER_GET_CLASS (buf); + if (rclass->close_device) + res = rclass->close_device (buf); + + if (!res) { + buf->open = TRUE; + } + +done: + GST_UNLOCK (buf); + + return res; +} + +/** + * gst_ring_buffer_device_is_open: + * @buf: the #GstRingBuffer + * + * Checks the status of the device associated with the ring buffer. + * + * Returns: TRUE if the device was open, FALSE if it was closed. + * + * MT safe. + */ +gboolean +gst_ring_buffer_device_is_open (GstRingBuffer * buf) +{ + gboolean res = TRUE; + + g_return_val_if_fail (GST_IS_RING_BUFFER (buf), FALSE); + + GST_LOCK (buf); + + res = buf->open; + + GST_UNLOCK (buf); + + return res; +} + + /** * gst_ring_buffer_acquire: * @buf: the #GstRingBuffer to acquire @@ -349,6 +471,11 @@ gst_ring_buffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec) g_return_val_if_fail (buf != NULL, FALSE); GST_LOCK (buf); + if (!buf->open) { + g_critical ("Device for %p not opened", buf); + res = FALSE; + goto done; + } if (buf->acquired) { res = TRUE; goto done; @@ -414,6 +541,9 @@ gst_ring_buffer_release (GstRingBuffer * buf) } buf->acquired = FALSE; + /* if this fails, something is wrong in this file */ + g_assert (buf->open == TRUE); + rclass = GST_RING_BUFFER_GET_CLASS (buf); if (rclass->release) res = rclass->release (buf); diff --git a/gst-libs/gst/audio/gstringbuffer.h b/gst-libs/gst/audio/gstringbuffer.h index c879cf8463..37ffb74470 100644 --- a/gst-libs/gst/audio/gstringbuffer.h +++ b/gst-libs/gst/audio/gstringbuffer.h @@ -151,6 +151,7 @@ struct _GstRingBuffer { /*< public >*/ /* with LOCK */ GCond *cond; + gboolean open; gboolean acquired; GstBuffer *data; GstRingBufferSpec spec; @@ -174,10 +175,14 @@ struct _GstRingBufferClass { GstObjectClass parent_class; /*< public >*/ + /* just open the device, don't set any params or allocate anything */ + gboolean (*open_device) (GstRingBuffer *buf); /* allocate the resources for the ringbuffer using the given specs */ gboolean (*acquire) (GstRingBuffer *buf, GstRingBufferSpec *spec); /* free resources of the ringbuffer */ gboolean (*release) (GstRingBuffer *buf); + /* close the device */ + gboolean (*close_device) (GstRingBuffer *buf); /* playback control */ gboolean (*start) (GstRingBuffer *buf); @@ -199,9 +204,15 @@ gboolean gst_ring_buffer_parse_caps (GstRingBufferSpec *spec, GstCaps *caps); void gst_ring_buffer_debug_spec_caps (GstRingBufferSpec *spec); void gst_ring_buffer_debug_spec_buff (GstRingBufferSpec *spec); +/* device state */ +gboolean gst_ring_buffer_open_device (GstRingBuffer *buf); +gboolean gst_ring_buffer_close_device (GstRingBuffer *buf); + +gboolean gst_ring_buffer_device_is_open (GstRingBuffer *buf); + /* allocate resources */ -gboolean gst_ring_buffer_acquire (GstRingBuffer *buf, GstRingBufferSpec *spec); -gboolean gst_ring_buffer_release (GstRingBuffer *buf); +gboolean gst_ring_buffer_acquire (GstRingBuffer *buf, GstRingBufferSpec *spec); +gboolean gst_ring_buffer_release (GstRingBuffer *buf); gboolean gst_ring_buffer_is_acquired (GstRingBuffer *buf);