ext/alsa/: Cleanups, take queued samples into account when reporting the time.
authorWim Taymans <wim.taymans@gmail.com>
Tue, 22 Jun 2004 12:01:33 +0000 (12:01 +0000)
committerWim Taymans <wim.taymans@gmail.com>
Tue, 22 Jun 2004 12:01:33 +0000 (12:01 +0000)
Original commit message from CVS:
* ext/alsa/gstalsa.c: (gst_alsa_get_time), (gst_alsa_clock_update),
(gst_alsa_change_state), (gst_alsa_update_avail),
(gst_alsa_xrun_recovery):
* ext/alsa/gstalsa.h:
* ext/alsa/gstalsasrc.c: (gst_alsa_src_loop):
Cleanups, take queued samples into account when reporting
the time.

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

index 1bb0fb0..6426679 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,15 @@
 2004-06-22  Wim Taymans  <wim@fluendo.com>
 
+       * ext/alsa/gstalsa.c: (gst_alsa_get_time), (gst_alsa_clock_update),
+       (gst_alsa_change_state), (gst_alsa_update_avail),
+       (gst_alsa_xrun_recovery):
+       * ext/alsa/gstalsa.h:
+       * ext/alsa/gstalsasrc.c: (gst_alsa_src_loop):
+       Cleanups, take queued samples into account when reporting
+       the time.
+
+2004-06-22  Wim Taymans  <wim@fluendo.com>
+
        * gst/videorate/gstvideorate.c: (gst_videorate_class_init),
        (gst_videorate_init):
        Initialize the property as well.
index ef151b7..3915598 100644 (file)
@@ -323,22 +323,46 @@ gst_alsa_get_property (GObject * object, guint prop_id, GValue * value,
  * ask ALSA for current time using htstamp
  * FIXME: This is not very accurate, should use alsa timers instead.
  * htstamp seems to use the system clock instead of the hw clock.
+ * We also use an ugly hack for now to substract the number of queued
+ * samples in the device from the system time, this makes sure that other
+ * plugins timestamp their samples with the right time but makes the
+ * clock a little unstable. A good way to fix this is to get the exact amount
+ * of samples that were produced by ALSA but I can't find a way to get that
+ * information.. oh well.. ALSA has enough methods, there is bound to be at
+ * least 1 way of getting the info...
  */
 GstClockTime
 gst_alsa_get_time (GstAlsa * this)
 {
   int err;
   snd_htimestamp_t timestamp;
-  GstClockTime time;
+  GstClockTime time, ideal;
+  GstClockTime availtime;
+  snd_pcm_sframes_t avail;
 
   if ((err = snd_pcm_status (this->handle, this->status)) < 0) {
     GST_WARNING_OBJECT (this, "could not get snd_pcm_status");
   }
 
+  /* see how many samples are still in the buffer */
+  avail = snd_pcm_status_get_avail (this->status);
+  availtime = gst_alsa_samples_to_timestamp (this, avail);
+
+  /* get the clock time */
   snd_pcm_status_get_htstamp (this->status, &timestamp);
 
   /* time = GST_TIMESPEC_TO_TIME (timestamp); */
-  time = timestamp.tv_sec * GST_SECOND + timestamp.tv_nsec * GST_NSECOND;
+  /* we have to compensate the time for the number of queued samples 
+   * in the buffer */
+  time =
+      timestamp.tv_sec * GST_SECOND + timestamp.tv_nsec * GST_NSECOND -
+      availtime;
+  ideal =
+      this->clock_base + gst_alsa_samples_to_timestamp (this,
+      this->transmitted) - availtime;
+
+  GST_DEBUG_OBJECT (this, "clock time %lld, diff to ideal %lld\n", time,
+      time - ideal);
 
   GST_LOG_OBJECT (this, "ALSA reports time of %" GST_TIME_FORMAT,
       GST_TIME_ARGS (time));
@@ -346,6 +370,12 @@ gst_alsa_get_time (GstAlsa * this)
   return time;
 }
 
+void
+gst_alsa_clock_update (GstAlsa * this, GstClockTime ideal)
+{
+
+}
+
 static const GList *
 gst_alsa_probe_get_properties (GstPropertyProbe * probe)
 {
@@ -1122,6 +1152,7 @@ gst_alsa_change_state (GstElement * element)
               gst_alsa_start_audio (this)))
         return GST_STATE_FAILURE;
       this->transmitted = 0;
+      this->clock_base = GST_CLOCK_TIME_NONE;
       break;
     case GST_STATE_PAUSED_TO_PLAYING:
       if (snd_pcm_state (this->handle) == SND_PCM_STATE_PAUSED) {
@@ -1191,14 +1222,22 @@ gst_alsa_set_clock (GstElement * element, GstClock * clock)
 inline snd_pcm_sframes_t
 gst_alsa_update_avail (GstAlsa * this)
 {
-  snd_pcm_sframes_t avail = snd_pcm_avail_update (this->handle);
+  snd_pcm_sframes_t avail = -1;
 
-  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);
+  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;
@@ -1280,17 +1319,7 @@ 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);
-
-    /* start new timestamps from the current time */
-    this->transmitted = gst_alsa_timestamp_to_samples (this,
-        gst_element_get_time (GST_ELEMENT (this)));
+    GstClockTime elemnow;
 
     /* if we're allowed to recover, ... */
     if (this->autorecover) {
@@ -1304,12 +1333,27 @@ 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;
+    if ((err = snd_pcm_prepare (this->handle)) < 0) {
+      GST_ERROR_OBJECT (this, "prepare error: %s", snd_strerror (err));
+      return FALSE;
+    }
+    /* 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 */
+    elemnow = gst_element_get_time (GST_ELEMENT (this));
+    this->transmitted = gst_alsa_timestamp_to_samples (this, elemnow);
+    GST_DEBUG_OBJECT (this, "XRun!!!! pretending we transmitted %lld samples",
+        this->transmitted);
+
+  } else {
+    /* something else happened, reset the device */
+    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 be7ae1b..9fac58d 100644 (file)
@@ -156,6 +156,7 @@ struct _GstAlsa {
 
   /* clocking */
   GstAlsaClock *               clock;          /* our provided clock */
+  GstClockTime                 clock_base;     /* adjusted clock base time */
   snd_pcm_uframes_t            transmitted;    /* samples transmitted since last sync 
                                                   This thing actually is our master clock.
                                                   We will event insert silent samples or
@@ -193,6 +194,8 @@ GstCaps *           gst_alsa_caps           (snd_pcm_format_t       format,
                                                 gint                   channels);
 
 GstClockTime                   gst_alsa_get_time       (GstAlsa * this);
+void                           gst_alsa_clock_update   (GstAlsa * this, GstClockTime ideal);
+
 
 /* audio processing functions */
 inline snd_pcm_sframes_t       gst_alsa_update_avail   (GstAlsa * this);
index 05703d2..16b33be 100644 (file)
@@ -322,25 +322,18 @@ gst_alsa_src_loop (GstElement * element)
       return;
     }
   }
+  if (this->clock_base == GST_CLOCK_TIME_NONE) {
+    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);
+  }
 
   /* the cast to long is explicitly needed;
    * with avail = -32 and period_size = 100, avail < period_size is false */
-  while ((avail = gst_alsa_update_avail (this)) < (long) this->period_size) {
-    if (avail == -EPIPE) {
-      GST_DEBUG_OBJECT (this, "got EPIPE when checking for available bytes");
-      continue;
-    }
-    if (avail < 0) {
-      GST_DEBUG_OBJECT (this,
-          "got error %s (%d) when checking for available bytes",
-          snd_strerror (avail));
-      return;
-    }
-    if (snd_pcm_state (this->handle) != SND_PCM_STATE_RUNNING) {
-      if (!gst_alsa_start (this))
-        return;
-      continue;
-    };
+  while ((long) (avail =
+          gst_alsa_update_avail (this)) < (long) this->period_size) {
     /* wait */
     if (gst_alsa_pcm_wait (this) == FALSE)
       return;
@@ -360,17 +353,31 @@ gst_alsa_src_loop (GstElement * element)
 
   {
     gint outsize;
-    GstClockTime outtime, outdur;
+    GstClockTime outtime, outdur, outreal, outideal;
+    gint64 diff;
 
-    outsize = gst_alsa_samples_to_bytes (this, copied);
+    /* duration of buffer is just the time of the samples */
     outdur = gst_alsa_samples_to_timestamp (this, copied);
+
+    /* The real capture time is the time of the clock minus the duration and
+     * 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);
+
+    outsize = gst_alsa_samples_to_bytes (this, copied);
     outtime = GST_CLOCK_TIME_NONE;
 
     if (GST_ELEMENT_CLOCK (this)) {
       if (GST_CLOCK (GST_ALSA (this)->clock) == GST_ELEMENT_CLOCK (this)) {
-        outtime = gst_alsa_samples_to_timestamp (this, this->transmitted);
+        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 = gst_element_get_time (GST_ELEMENT (this));
+        outtime = outreal;
       }
     }