gst-libs/gst/audio/gstbaseaudiosink.c
authorAndy Wingo <wingo@pobox.com>
Mon, 8 Aug 2005 16:42:10 +0000 (16:42 +0000)
committerAndy Wingo <wingo@pobox.com>
Mon, 8 Aug 2005 16:42:10 +0000 (16:42 +0000)
Original commit message from CVS:
2005-08-08  Andy Wingo  <wingo@pobox.com>

* 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.

ChangeLog
ext/alsa/gstalsasink.c
gst-libs/gst/audio/gstaudiosink.c
gst-libs/gst/audio/gstaudiosink.h
gst-libs/gst/audio/gstbaseaudiosink.c
gst-libs/gst/audio/gstringbuffer.c
gst-libs/gst/audio/gstringbuffer.h

index b1df7d79c42edd2b1e6fd87cc5228539daff9d6b..90af8bea26ed808c867c7586207974202fd923d6 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,34 @@
+2005-08-08  Andy Wingo  <wingo@pobox.com>
+
+       * 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  <tim at centricular dot net>
 
        * gst-libs/gst/interfaces/mixer.h:
index aaf8c72ece8fdc56ef26fd0fa68d254d9cf7412e..e8859f96da32f6f76b89c410d3d3ba0b59e71360 100644 (file)
@@ -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,19 +484,38 @@ error:
 }
 
 static gboolean
-gst_alsasink_open (GstAudioSink * asink, GstRingBufferSpec * spec)
+gst_alsasink_open (GstAudioSink * asink)
 {
   GstAlsaSink *alsa;
   gint err;
 
   alsa = GST_ALSA_SINK (asink);
 
-  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);
 
+  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;
+
+  alsa = GST_ALSA_SINK (asink);
+
+  if (!alsasink_parse_spec (alsa, spec))
+    goto spec_parse;
+
   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;
index f46084d4e7a202dd5e578741ce5e147671aa04d6..8c8e87cd74ce0d7a9ed9a40102135c193506ea29 100644 (file)
@@ -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;
 }
index 70544d3e0277ee566707536b3f1542d82ce4f82f..b724257190ef412c4a6e46536c81df2c1526c151 100644 (file)
@@ -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);
index 92d93979c82967a72cd9a5c37ddcad3e2009499f..52fb1c02dcac8a0dd32b7b9389f5582b2bd4f997 100644 (file)
@@ -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;
index 05eebb736a55501f15b9a740754291949d5cfb39..765d07ba1919deea9ff19c09258528af70bb94db 100644 (file)
@@ -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);
index c879cf846356010fe5ecc73d6991b349ec3a6353..37ffb74470244cf8a6baf75381f52e49b39eaa7b 100644 (file)
@@ -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);