Bugfixing in alsa again:
authorBenjamin Otte <otte@gnome.org>
Mon, 10 Mar 2003 21:30:00 +0000 (21:30 +0000)
committerBenjamin Otte <otte@gnome.org>
Mon, 10 Mar 2003 21:30:00 +0000 (21:30 +0000)
Original commit message from CVS:
Bugfixing in alsa again:
- Leif's commit reverted an earlier patch
(stupid diff)
- Some comment from Leif made me clean up his code
- Moved wait() directly in front of mmap
- Assorted fixes
- fixed newbie bug: DON'T EVER USE STATIC VARIABLES WHEN YOU'RE NOT ABSOLUTELY SURE WHAT YOU'RE DOING, Leif *slap* ;)
I hope I didn't break the src now...

ext/alsa/gstalsa.c
ext/alsa/gstalsa.h

index abda314..fe88c03 100644 (file)
@@ -97,7 +97,7 @@ static GstBufferPool    *gst_alsa_src_get_buffer_pool (GstPad *pad);
 static GstElementStateReturn gst_alsa_change_state (GstElement *element);
 
 /* audio processing functions */
-static int      gst_alsa_do_mmap (GstAlsa *this, guint numpads, snd_pcm_sframes_t *avail);
+static int      gst_alsa_do_mmap (GstAlsa *this, snd_pcm_sframes_t *avail);
 
 static void     gst_alsa_sink_loop (GstElement *element);
 static void     gst_alsa_src_loop (GstElement *element);
@@ -280,6 +280,7 @@ gst_alsa_init (GstAlsa *this)
   /* init values */
   this->handle = NULL;
   this->channels = 1;
+  this->caps_set = FALSE;
 
   GST_FLAG_SET (this, GST_ELEMENT_THREAD_SUGGESTED);
 
@@ -816,20 +817,16 @@ gst_alsa_change_state (GstElement *element)
 /*** AUDIO PROCESSING *********************************************************/
 
 static int
-gst_alsa_do_mmap (GstAlsa *this, guint numpads, snd_pcm_sframes_t *avail)
+gst_alsa_do_mmap (GstAlsa *this, snd_pcm_sframes_t *avail)
 {
   snd_pcm_uframes_t offset;
   snd_pcm_channel_area_t *dst, *src, *areas;
   int i, err, width = snd_pcm_format_physical_width (this->format);
 
   /* areas points to the memory areas that belong to gstreamer. */
-  if (G_OBJECT_TYPE (this) == GST_TYPE_ALSA_SRC) {
-    areas = dst = calloc(this->channels, sizeof(snd_pcm_channel_area_t));
-  } else {
-    areas = src = calloc(this->channels, sizeof(snd_pcm_channel_area_t));
-  }
+  areas = src = dst = calloc(this->channels, sizeof(snd_pcm_channel_area_t));
 
-  if (numpads == 1) {
+  if (((GstElement *) this)->numpads == 1) {
     /* interleaved */
     for (i = 0; i < this->channels; i++) {
       areas[i].addr = this->pads[0].data;
@@ -845,18 +842,11 @@ gst_alsa_do_mmap (GstAlsa *this, guint numpads, snd_pcm_sframes_t *avail)
     }
   }
 
-  /* might want to try to do some pointer sneakery in the first if block to get
-     rid of this second if block ... but right now i'm too sleepy */
-  if (G_OBJECT_TYPE (this) == GST_TYPE_ALSA_SRC) {
-    if ((err = snd_pcm_mmap_begin (this->handle, (const snd_pcm_channel_area_t **) &src, &offset, avail)) < 0) {
-      g_warning ("gstalsa: mmap failed: %s", snd_strerror (err));
-      return -1;
-    }
-  } else {
-    if ((err = snd_pcm_mmap_begin (this->handle, (const snd_pcm_channel_area_t **) &dst, &offset, avail)) < 0) {
-      g_warning ("gstalsa: mmap failed: %s", snd_strerror (err));
-      return -1;
-    }
+  if ((err = snd_pcm_mmap_begin (this->handle, (
+             const snd_pcm_channel_area_t **) (G_OBJECT_TYPE (this) == GST_TYPE_ALSA_SRC ? &src : &dst),
+             &offset, avail)) < 0) {
+    g_warning ("gstalsa: mmap failed: %s", snd_strerror (err));
+    return -1;
   }
 
   if ((err = snd_pcm_areas_copy (dst, offset, src, 0, this->channels, *avail, this->format)) < 0) {
@@ -879,16 +869,15 @@ gst_alsa_update_avail (GstAlsa *this)
   if (avail < 0) {
     if (avail == -EPIPE) {
       gst_alsa_xrun_recovery (this);
-      return -EPIPE;
     } else {
       g_warning ("unknown ALSA avail_update return value (%d)", (int) avail);
-      return -1;
     }
   }
   return avail;
 }
 
-inline static gint
+/* returns TRUE, if the loop should go on */
+inline static gboolean
 gst_alsa_pcm_wait (GstAlsa *this)
 {
   if (snd_pcm_state (this->handle) == SND_PCM_STATE_RUNNING) {
@@ -896,20 +885,23 @@ gst_alsa_pcm_wait (GstAlsa *this)
       if (errno == EINTR) {
         /* happens mostly when run under gdb, or when exiting due to a signal */
         GST_DEBUG (GST_CAT_PLUGIN_INFO, "got interrupted while waiting");
-        if (gst_element_interrupt (GST_ELEMENT (this)))
-          return -1;
+        if (gst_element_interrupt (GST_ELEMENT (this))) {
+          return TRUE;
+        } else {
+          return FALSE;
+        }
       }
       g_warning ("error waiting for alsa pcm: (%d: %s)", errno, strerror (errno));
-      return -1;
+      return FALSE;
     }
   }
-  return 0;
+  return TRUE;
 }
 
 static void
 gst_alsa_sink_loop (GstElement *element)
 {
-  snd_pcm_sframes_t avail, copied;
+  snd_pcm_sframes_t avail, avail2, copied;
   gint i;
   gint bytes, num_bytes; /* per channel */
   GstAlsa *this = GST_ALSA (element);
@@ -932,50 +924,57 @@ gst_alsa_sink_loop (GstElement *element)
     }
   }
 
- sink_restart:
-
-  while (1) {
-    avail = gst_alsa_update_avail (this);
-    if (avail == -EPIPE) goto sink_restart;
-    if (avail == -1) break;
-    if (avail > 0) {
-      int width = snd_pcm_format_physical_width (this->format);
-
-      /* check how many bytes we still have in all our bytestreams */
-      bytes = avail * ( width / 8 ) * (element->numpads == 1 ? this->channels : 1);
-      for (i = 0; i < element->numpads; i++) {
-        g_assert (this->pads[i].pad != NULL);
-        do {
-          num_bytes = gst_bytestream_peek_bytes (this->pads[i].bs, &this->pads[i].data, bytes);
-        } while (num_bytes == 0 && gst_alsa_sink_check_event (this, i));
-        if (num_bytes == 0)
-          break;
-        bytes = MIN (bytes, num_bytes);
-      }
-
-      /* FIXME: lotsa stuff can have happened while fetching data. Do we need to check something? */
+sink_restart:
 
-      /* put this data into alsa */
-      avail = bytes / (width / 8 ) / (element->numpads == 1 ? this->channels : 1);
-      if ((copied = gst_alsa_do_mmap (this, element->numpads, &avail)) < 0)
-        break;
+  avail = gst_alsa_update_avail (this);
+  if (avail == -EPIPE) goto sink_restart;
+  if (avail < 0) return;
+  if (avail > 0) {
+    int width = snd_pcm_format_physical_width (this->format);
+    
+    /* Not enough space. We grab data nonetheless and sleep afterwards */
+    if (avail < this->period_size) {
+      avail = this->period_size;
+    }
+    
+    /* check how many bytes we still have in all our bytestreams */
+    bytes = avail * ( width / 8 ) * (element->numpads == 1 ? this->channels : 1);
+    for (i = 0; i < element->numpads; i++) {
+      g_assert (this->pads[i].pad != NULL);
+      do {
+        num_bytes = gst_bytestream_peek_bytes (this->pads[i].bs, &this->pads[i].data, bytes);
+      } while (num_bytes == 0 && gst_alsa_sink_check_event (this, i));
+      if (num_bytes == 0)
+        return;
+      bytes = MIN (bytes, num_bytes);
+    }
+    avail = bytes / (width / 8 ) / (element->numpads == 1 ? this->channels : 1);
 
-      /* flush the data */
-      bytes = copied * ( width / 8 ) * (element->numpads == 1 ? this->channels : 1);
-      for (i = 0; i < element->numpads; i++)
-        gst_bytestream_flush (this->pads[i].bs, bytes);
+    /* FIXME: lotsa stuff can have happened while fetching data. Do we need to check something? */
 
-      /* BUG: we start the stream explicitly, autostart doesn't work correctly (alsa 0.9.0rc7) */
-      if (snd_pcm_state(this->handle) == SND_PCM_STATE_PREPARED && snd_pcm_avail_update (this->handle) == 0) {
-        GST_DEBUG (GST_CAT_PLUGIN_INFO, "Explicitly starting playback");
-        snd_pcm_start(this->handle);
-      }
+    /* wait until the hw buffer has enough space */
+    while ((avail2 = gst_alsa_update_avail (this)) < avail) {
+      if (avail2 == -EPIPE) goto sink_restart;
+      if (avail < 0) return;
+      if (gst_alsa_pcm_wait (this) == FALSE)
+        return;
+    }
+    /* put this data into alsa */
+    if ((copied = gst_alsa_do_mmap (this, &avail)) < 0)
+      return;
+
+    /* flush the data */
+    bytes = copied * ( width / 8 ) * (element->numpads == 1 ? this->channels : 1);
+    for (i = 0; i < element->numpads; i++)
+      gst_bytestream_flush (this->pads[i].bs, bytes);
+
+    /* BUG: we start the stream explicitly, autostart doesn't work correctly (alsa 0.9.0rc7) */
+    if (snd_pcm_state(this->handle) == SND_PCM_STATE_PREPARED && snd_pcm_avail_update (this->handle) == 0) {
+      GST_DEBUG (GST_CAT_PLUGIN_INFO, "Explicitly starting playback");
+      snd_pcm_start(this->handle);
     }
-
-    /* wait */
-    if (gst_alsa_pcm_wait (this) < 0)
-      break;
   }
+
 }
 
 static void
@@ -991,11 +990,9 @@ gst_alsa_src_loop (GstElement *element)
 
   g_return_if_fail (this != NULL);
 
-  static gboolean caps_set = FALSE;
-
   /* set the caps on all pads */
-  if (!caps_set) {
-    GST_DEBUG (GST_CAT_NEGOTIATION, "starting caps negotiation");
+  if (!this->caps_set) {
+    GST_DEBUG (GST_CAT_NEGOTIATION, "starting caps negotiationgst_alsa_pcm_wait");
     caps = gst_alsa_caps (this->format, this->rate, this->channels);
     for (i = 0; i < element->numpads; i++) {
       if (gst_pad_try_set_caps (this->pads[i].pad, caps) <= 0) {
@@ -1003,58 +1000,55 @@ gst_alsa_src_loop (GstElement *element)
         return;
       }
     }
-    caps_set = TRUE;
+    this->caps_set = TRUE;
   }
 
- src_restart:
+src_restart:
 
-  while (1) {
-    avail = gst_alsa_update_avail (this);
+  while ((avail = gst_alsa_update_avail (this)) < this->period_size) {
     if (avail == -EPIPE) goto src_restart;
-    if (avail == -1) break;
-    if (avail > 0) {
-      int width = snd_pcm_format_physical_width (this->format);
-      int bytes_per_frame = ( width / 8 ) * (element->numpads == 1 ? this->channels : 1);
-
-      if ((copied = gst_alsa_do_mmap (this, element->numpads, &avail)) < 0)
-        break;
-
-      /* we get the buffer pool once per go round */
-      if (! pool) pool = gst_alsa_src_get_buffer_pool (this->pads[0].pad);
-
-      /* push the data to gstreamer if it's big enough to fill up a buffer. */
-      for (i = 0; i < element->numpads; i++) {
-        pad = &this->pads[i];
-        pad->offset += MIN (copied, this->period_size - pad->offset);
+    if (avail < 0) return;
+    /* wait */
+    if (gst_alsa_pcm_wait (this) == FALSE)
+      return;
+  }
+  if (avail > 0) {
+    int width = snd_pcm_format_physical_width (this->format);
+    int bytes_per_frame = ( width / 8 ) * (element->numpads == 1 ? this->channels : 1);
+    if ((copied = gst_alsa_do_mmap (this, &avail)) < 0)
+      return;
+      
+    /* we get the buffer pool once per go round */
+    if (! pool) pool = gst_alsa_src_get_buffer_pool (this->pads[0].pad);
+
+    /* push the data to gstreamer if it's big enough to fill up a buffer. */
+    for (i = 0; i < element->numpads; i++) {
+      pad = &this->pads[i];
+      pad->offset += MIN (copied, this->period_size - pad->offset);
 
-        if (pad->offset >= this->period_size) {
-          g_assert (pad->offset <= this->period_size);
+      if (pad->offset >= this->period_size) {
+        g_assert (pad->offset <= this->period_size);
 
-          buf = gst_buffer_new_from_pool (pool, 0, 0);
+        buf = gst_buffer_new_from_pool (pool, 0, 0);
 
-          GST_BUFFER_DATA (buf) = pad->data;
-          GST_BUFFER_SIZE (buf) = this->period_size * bytes_per_frame;
-          GST_BUFFER_MAXSIZE (buf) = this->period_size * bytes_per_frame;
+        GST_BUFFER_DATA (buf) = pad->data;
+        GST_BUFFER_SIZE (buf) = this->period_size * bytes_per_frame;
+        GST_BUFFER_MAXSIZE (buf) = this->period_size * bytes_per_frame;
 
-          gst_pad_push (pad->pad, buf);
+        gst_pad_push (pad->pad, buf);
 
-          pad->data = NULL;
-          pad->offset = 0;
-        }
+        pad->data = NULL;
+        pad->offset = 0;
       }
+    }
 
-      pool = NULL;
+    pool = NULL;
 
-      /* BUG: we start the stream explicitly, autostart doesn't work correctly (alsa 0.9.0rc7) */
-      if (snd_pcm_state(this->handle) == SND_PCM_STATE_PREPARED && snd_pcm_avail_update (this->handle) == 0) {
-        GST_DEBUG (GST_CAT_PLUGIN_INFO, "Explicitly starting capture");
-        snd_pcm_start(this->handle);
-      }
+    /* BUG: we start the stream explicitly, autostart doesn't work correctly (alsa 0.9.0rc7) */
+    if (snd_pcm_state(this->handle) == SND_PCM_STATE_PREPARED && snd_pcm_avail_update (this->handle) == 0) {
+      GST_DEBUG (GST_CAT_PLUGIN_INFO, "Explicitly starting capture");
+      snd_pcm_start(this->handle);
     }
-
-    /* wait */
-    if (gst_alsa_pcm_wait (this) < 0)
-      break;
   }
 }
 
index 807843e..d9c5bbe 100644 (file)
@@ -87,6 +87,7 @@ struct _GstAlsa {
   snd_pcm_format_t format;
   guint rate;
   gint channels;
+  gboolean caps_set;
 
   /* latency / performance parameters */
   snd_pcm_uframes_t period_size;