ext/alsa/: Add clock to alsasrc. Take new capture timestamp when restarting after...
authorWim Taymans <wim.taymans@gmail.com>
Wed, 23 Jun 2004 18:08:26 +0000 (18:08 +0000)
committerWim Taymans <wim.taymans@gmail.com>
Wed, 23 Jun 2004 18:08:26 +0000 (18:08 +0000)
Original commit message from CVS:
* ext/alsa/gstalsa.c: (gst_alsa_change_state), (gst_alsa_start),
(gst_alsa_xrun_recovery):
* ext/alsa/gstalsa.h:
* ext/alsa/gstalsasink.c: (gst_alsa_sink_check_event),
(gst_alsa_sink_loop), (gst_alsa_sink_get_time):
* ext/alsa/gstalsasrc.c: (gst_alsa_src_init),
(gst_alsa_src_get_time), (gst_alsa_src_update_avail),
(gst_alsa_src_loop):
Add clock to alsasrc. Take new capture timestamp when
restarting after an overrun. Split up some functions between
alsasrc ans alsasink.

ChangeLog
ext/alsa/gstalsa.c
ext/alsa/gstalsa.h
ext/alsa/gstalsasink.c
ext/alsa/gstalsasrc.c

index f0fc6f10e4f2268fc6035317b2e2c9a1f7e4125f..1421a162a1b3a595cc5b6d4d1cb0cf2a17f64342 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2004-06-23  Wim Taymans  <wim@fluendo.com>
+
+       * ext/alsa/gstalsa.c: (gst_alsa_change_state), (gst_alsa_start),
+       (gst_alsa_xrun_recovery):
+       * ext/alsa/gstalsa.h:
+       * ext/alsa/gstalsasink.c: (gst_alsa_sink_check_event),
+       (gst_alsa_sink_loop), (gst_alsa_sink_get_time):
+       * ext/alsa/gstalsasrc.c: (gst_alsa_src_init),
+       (gst_alsa_src_get_time), (gst_alsa_src_update_avail),
+       (gst_alsa_src_loop):
+       Add clock to alsasrc. Take new capture timestamp when
+       restarting after an overrun. Split up some functions between
+       alsasrc ans alsasink.
+
 2004-06-23  Thomas Vander Stichele  <thomas at apestaart dot org>
 
        * ext/alsa/gstalsa.c: (gst_alsa_init), (gst_alsa_dispose),
index e9c21388e327a56174441056e9cae93f323ec3ce..c28ac09c76e6969adc2a53ace416223f3bd7808b 100644 (file)
@@ -1085,7 +1085,8 @@ gst_alsa_change_state (GstElement * element)
       if (!(GST_FLAG_IS_SET (element, GST_ALSA_RUNNING) ||
               gst_alsa_start_audio (this)))
         return GST_STATE_FAILURE;
-      this->transmitted = 0;
+      this->played = 0;
+      this->captured = 0;
       break;
     case GST_STATE_PAUSED_TO_PLAYING:
       if (snd_pcm_state (this->handle) == SND_PCM_STATE_PAUSED) {
@@ -1202,6 +1203,8 @@ gst_alsa_pcm_wait (GstAlsa * this)
 inline gboolean
 gst_alsa_start (GstAlsa * this)
 {
+  GstClockTime elemnow;
+
   GST_DEBUG ("Setting state to RUNNING");
 
   switch (snd_pcm_state (this->handle)) {
@@ -1212,6 +1215,13 @@ gst_alsa_start (GstAlsa * this)
       ERROR_CHECK (snd_pcm_prepare (this->handle), "error preparing: %s");
     case SND_PCM_STATE_SUSPENDED:
     case SND_PCM_STATE_PREPARED:
+      /* The strategy to recover the timestamps from the xrun is to take the
+       * current element time and pretend we just sent all the samples up to
+       * that time. This will result in an offset discontinuity in the next
+       * buffer along with the correct timestamp on that buffer, we only
+       * update the capture timestamps */
+      elemnow = gst_element_get_time (GST_ELEMENT (this));
+      this->captured = gst_alsa_timestamp_to_samples (this, elemnow);
       ERROR_CHECK (snd_pcm_start (this->handle), "error starting playback: %s");
       break;
     case SND_PCM_STATE_PAUSED:
@@ -1244,13 +1254,6 @@ gst_alsa_xrun_recovery (GstAlsa * this)
     GST_ERROR_OBJECT (this, "status error: %s", snd_strerror (err));
 
   if (snd_pcm_status_get_state (status) == SND_PCM_STATE_XRUN) {
-    struct timeval now, diff, tstamp;
-
-    gettimeofday (&now, 0);
-    snd_pcm_status_get_trigger_tstamp (status, &tstamp);
-    timersub (&now, &tstamp, &diff);
-    GST_INFO_OBJECT (this, "alsa: xrun of at least %.3f msecs",
-        diff.tv_sec * 1000 + diff.tv_usec / 1000.0);
 
     /* if we're allowed to recover, ... */
     if (this->autorecover) {
@@ -1264,12 +1267,25 @@ gst_alsa_xrun_recovery (GstAlsa * this)
         this->period_count *= 2;
       }
     }
-  }
 
-  if (!(gst_alsa_stop_audio (this) && gst_alsa_start_audio (this))) {
-    GST_ELEMENT_ERROR (this, RESOURCE, FAILED, (NULL),
-        ("Error restarting audio after xrun"));
-    return FALSE;
+    /* prepare the device again */
+    if ((err = snd_pcm_prepare (this->handle)) < 0) {
+      GST_ERROR_OBJECT (this, "prepare error: %s", snd_strerror (err));
+      return FALSE;
+    }
+    if (!gst_alsa_start (this)) {
+      GST_ELEMENT_ERROR (this, RESOURCE, FAILED, (NULL),
+          ("Error starting audio after xrun"));
+      return FALSE;
+    }
+    GST_DEBUG_OBJECT (this, "XRun!!!! pretending we captured %lld samples",
+        this->captured);
+  } else {
+    if (!(gst_alsa_stop_audio (this) && gst_alsa_start_audio (this))) {
+      GST_ELEMENT_ERROR (this, RESOURCE, FAILED, (NULL),
+          ("Error restarting audio after xrun"));
+      return FALSE;
+    }
   }
 
   return TRUE;
index b499b668df64f28f8f291c79d74202c150bb25df..89036313f415c15bc6377f2cce1f691e3405ebca 100644 (file)
@@ -156,11 +156,13 @@ struct _GstAlsa {
 
   /* clocking */
   GstAlsaClock *               clock;          /* our provided clock */
-  snd_pcm_uframes_t            transmitted;    /* samples transmitted since last sync 
+  GstClockTime                 clock_base;
+  snd_pcm_uframes_t            played;         /* samples transmitted since last sync 
                                                   This thing actually is our master clock.
                                                   We will event insert silent samples or
                                                   drop some to sync to incoming timestamps.
                                                 */
+  snd_pcm_uframes_t            captured;
   GstClockTime                 max_discont;    /* max difference between current
                                                   playback timestamp and buffers timestamps
                                                 */
index 5959b4691ed06f63b452e03ffe2420e983cd0790..3277f4e0cdc2afbddeaff6778d31f6ca953b891b 100644 (file)
@@ -217,7 +217,7 @@ gst_alsa_sink_check_event (GstAlsaSink * sink, gint pad_nr)
           break;
         }
         delay = (this->format == NULL) ? 0 :
-            GST_SECOND * this->transmitted / this->format->rate -
+            GST_SECOND * this->played / this->format->rate -
             gst_alsa_sink_get_time (this);
         if (gst_event_discont_get_value (event, GST_FORMAT_TIME, &value)) {
           gst_element_set_time_delay (GST_ELEMENT (this), MIN (value, delay),
@@ -386,11 +386,11 @@ sink_restart:
            * better way to get this info */
           if (element->base_time > this->clock->start_time) {
             expected =
-                this->transmitted - gst_alsa_timestamp_to_samples (this,
+                this->played - gst_alsa_timestamp_to_samples (this,
                 element->base_time - this->clock->start_time);
           } else {
             expected =
-                this->transmitted + gst_alsa_timestamp_to_samples (this,
+                this->played + gst_alsa_timestamp_to_samples (this,
                 this->clock->start_time - element->base_time);
           }
         } else {
@@ -485,7 +485,7 @@ sink_restart:
     if ((copied = this->transmit (this, &avail)) < 0)
       return;
     /* update our clock */
-    this->transmitted += copied;
+    this->played += copied;
     /* flush the data */
     bytes = gst_alsa_samples_to_bytes (this, copied);
     for (i = 0; i < element->numpads; i++) {
@@ -543,11 +543,11 @@ gst_alsa_sink_get_time (GstAlsa * this)
   if (!this->format)
     return 0;
   if (snd_pcm_delay (this->handle, &delay) != 0) {
-    return this->transmitted / this->format->rate;
+    return this->played / this->format->rate;
   }
-  if (this->transmitted <= delay) {
+  if (this->played <= delay) {
     return 0;
   }
 
-  return GST_SECOND * (this->transmitted - delay) / this->format->rate;
+  return GST_SECOND * (this->played - delay) / this->format->rate;
 }
index 16b33be9a74426c270be7fc82ec1fba36fd92a9a..9fce46026934fc761cca6875117a8c63272f5fe3 100644 (file)
@@ -43,6 +43,7 @@ static int gst_alsa_src_read (GstAlsa * this, snd_pcm_sframes_t * avail);
 static void gst_alsa_src_loop (GstElement * element);
 static void gst_alsa_src_flush (GstAlsaSrc * src);
 static GstElementStateReturn gst_alsa_src_change_state (GstElement * element);
+static GstClockTime gst_alsa_src_get_time (GstAlsa * this);
 
 static GstAlsa *src_parent_class = NULL;
 
@@ -127,9 +128,25 @@ gst_alsa_src_init (GstAlsaSrc * src)
   gst_pad_set_getcaps_function (this->pad[0], gst_alsa_get_caps);
   gst_element_add_pad (GST_ELEMENT (this), this->pad[0]);
 
+  this->clock =
+      gst_alsa_clock_new ("alsasrcclock", gst_alsa_src_get_time, this);
+  /* we hold a ref to our clock until we're disposed */
+  gst_object_ref (GST_OBJECT (this->clock));
+  gst_object_sink (GST_OBJECT (this->clock));
+
   gst_element_set_loop_function (GST_ELEMENT (this), gst_alsa_src_loop);
 }
 
+static GstClockTime
+gst_alsa_src_get_time (GstAlsa * this)
+{
+  GTimeVal now;
+
+  g_get_current_time (&now);
+
+  return GST_TIMEVAL_TO_TIME (now);
+}
+
 static int
 gst_alsa_src_mmap (GstAlsa * this, snd_pcm_sframes_t * avail)
 {
@@ -305,6 +322,30 @@ gst_alsa_src_set_caps (GstAlsaSrc * src, gboolean aggressive)
   return FALSE;
 }
 
+inline snd_pcm_sframes_t
+gst_alsa_src_update_avail (GstAlsa * this)
+{
+  snd_pcm_sframes_t avail = -1;
+
+  while (avail < 0) {
+    avail = snd_pcm_avail_update (this->handle);
+    if (avail < 0) {
+      if (avail == -EPIPE) {
+        gst_alsa_xrun_recovery (this);
+      } else {
+        GST_WARNING_OBJECT (this, "unknown ALSA avail_update return value (%d)",
+            (int) avail);
+      }
+    }
+    if (snd_pcm_state (this->handle) != SND_PCM_STATE_RUNNING) {
+      if (!gst_alsa_start (this)) {
+        return 0;
+      }
+    }
+  }
+  return avail;
+}
+
 /* we transmit buffers of period_size frames */
 static void
 gst_alsa_src_loop (GstElement * element)
@@ -326,14 +367,13 @@ gst_alsa_src_loop (GstElement * element)
     GstClockTime now;
 
     now = gst_element_get_time (element);
-    this->clock_base = gst_alsa_get_time (this);
-    this->transmitted = gst_alsa_timestamp_to_samples (this, now);
+    this->clock_base = gst_alsa_src_get_time (this);
+    this->captured = gst_alsa_timestamp_to_samples (this, now);
   }
 
   /* the cast to long is explicitly needed;
    * with avail = -32 and period_size = 100, avail < period_size is false */
-  while ((long) (avail =
-          gst_alsa_update_avail (this)) < (long) this->period_size) {
+  while ((avail = gst_alsa_src_update_avail (this)) < this->period_size) {
     /* wait */
     if (gst_alsa_pcm_wait (this) == FALSE)
       return;
@@ -363,7 +403,7 @@ gst_alsa_src_loop (GstElement * element)
      * what is now in the buffer */
     outreal = gst_element_get_time (GST_ELEMENT (this)) - outdur;
     /* ideal time is counting samples */
-    outideal = gst_alsa_samples_to_timestamp (this, this->transmitted);
+    outideal = gst_alsa_samples_to_timestamp (this, this->captured);
 
     outsize = gst_alsa_samples_to_bytes (this, copied);
     outtime = GST_CLOCK_TIME_NONE;
@@ -371,11 +411,9 @@ gst_alsa_src_loop (GstElement * element)
     if (GST_ELEMENT_CLOCK (this)) {
       if (GST_CLOCK (GST_ALSA (this)->clock) == GST_ELEMENT_CLOCK (this)) {
         outtime = outideal;
-
         diff = outideal - outreal;
         GST_DEBUG_OBJECT (this, "ideal %lld, real %lld, diff %lld\n", outideal,
             outreal, diff);
-        gst_alsa_clock_update (this, outideal);
       } else {
         outtime = outreal;
       }
@@ -392,14 +430,14 @@ gst_alsa_src_loop (GstElement * element)
 
       GST_BUFFER_TIMESTAMP (src->buf[i]) = outtime;
       GST_BUFFER_DURATION (src->buf[i]) = outdur;
-      GST_BUFFER_OFFSET (src->buf[i]) = this->transmitted;
-      GST_BUFFER_OFFSET_END (src->buf[i]) = this->transmitted + copied;
+      GST_BUFFER_OFFSET (src->buf[i]) = this->captured;
+      GST_BUFFER_OFFSET_END (src->buf[i]) = this->captured + copied;
 
       buf = src->buf[i];
       src->buf[i] = NULL;
       gst_pad_push (this->pad[i], GST_DATA (buf));
     }
-    this->transmitted += copied;
+    this->captured += copied;
   }
 }