gst-libs/gst/audio/: Do the delay calculation in the source/sink base classes as...
authorWim Taymans <wim.taymans@gmail.com>
Fri, 15 Sep 2006 14:53:44 +0000 (14:53 +0000)
committerWim Taymans <wim.taymans@gmail.com>
Fri, 15 Sep 2006 14:53:44 +0000 (14:53 +0000)
Original commit message from CVS:
* gst-libs/gst/audio/gstbaseaudiosink.c:
(gst_base_audio_sink_get_time), (gst_base_audio_sink_callback):
* gst-libs/gst/audio/gstbaseaudiosrc.c:
(gst_base_audio_src_get_time), (gst_base_audio_src_fixate),
(gst_base_audio_src_get_times), (gst_base_audio_src_get_offset),
(gst_base_audio_src_create), (gst_base_audio_src_change_state):
Do the delay calculation in the source/sink base classes as this is
specific for the capture/playback mode.
Try to fixate a bit better, like round depth up to a multiple of 8
bigger than width.
Handle underruns correctly by marking DISCONT on buffers and adjusting
timestamps to handle the gap.
Set offset/offset_end correctly on buffers.
* gst-libs/gst/audio/gstringbuffer.c: (gst_ring_buffer_pause),
(gst_ring_buffer_samples_done), (gst_ring_buffer_commit),
(gst_ring_buffer_read):
Remove resync and underrun recovery from the ringbuffer.
Fix ringbuffer read code on under/overrun.

ChangeLog
gst-libs/gst/audio/gstbaseaudiosink.c
gst-libs/gst/audio/gstbaseaudiosrc.c
gst-libs/gst/audio/gstringbuffer.c

index fc9465b..1030f59 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,27 @@
 2006-09-15  Wim Taymans  <wim@fluendo.com>
 
+       * gst-libs/gst/audio/gstbaseaudiosink.c:
+       (gst_base_audio_sink_get_time), (gst_base_audio_sink_callback):
+       * gst-libs/gst/audio/gstbaseaudiosrc.c:
+       (gst_base_audio_src_get_time), (gst_base_audio_src_fixate),
+       (gst_base_audio_src_get_times), (gst_base_audio_src_get_offset),
+       (gst_base_audio_src_create), (gst_base_audio_src_change_state):
+       Do the delay calculation in the source/sink base classes as this is
+       specific for the capture/playback mode.
+       Try to fixate a bit better, like round depth up to a multiple of 8
+       bigger than width.
+       Handle underruns correctly by marking DISCONT on buffers and adjusting
+       timestamps to handle the gap.
+       Set offset/offset_end correctly on buffers.
+
+       * gst-libs/gst/audio/gstringbuffer.c: (gst_ring_buffer_pause),
+       (gst_ring_buffer_samples_done), (gst_ring_buffer_commit),
+       (gst_ring_buffer_read):
+       Remove resync and underrun recovery from the ringbuffer.
+       Fix ringbuffer read code on under/overrun.
+
+2006-09-15  Wim Taymans  <wim@fluendo.com>
+
        * gst/playback/gstplaybasebin.c: (gst_play_base_bin_class_init),
        (gst_play_base_bin_init), (fill_buffer), (check_queue),
        (queue_threshold_reached), (gst_play_base_bin_set_property),
index c946310..d20ccb6 100644 (file)
@@ -91,7 +91,7 @@ static void gst_base_audio_sink_get_times (GstBaseSink * bsink,
 static gboolean gst_base_audio_sink_setcaps (GstBaseSink * bsink,
     GstCaps * caps);
 
-//static guint gst_base_audio_sink_signals[LAST_SIGNAL] = { 0 };
+/* static guint gst_base_audio_sink_signals[LAST_SIGNAL] = { 0 }; */
 
 static void
 gst_base_audio_sink_base_init (gpointer g_class)
@@ -217,18 +217,32 @@ clock_disabled:
 static GstClockTime
 gst_base_audio_sink_get_time (GstClock * clock, GstBaseAudioSink * sink)
 {
-  guint64 samples;
+  guint64 raw, samples;
+  guint delay;
   GstClockTime result;
 
   if (sink->ringbuffer == NULL || sink->ringbuffer->spec.rate == 0)
     return GST_CLOCK_TIME_NONE;
 
   /* our processed samples are always increasing */
-  samples = gst_ring_buffer_samples_done (sink->ringbuffer);
+  raw = samples = gst_ring_buffer_samples_done (sink->ringbuffer);
+
+  /* the number of samples not yet processed, this is still queued in the
+   * device (not played for playback). */
+  delay = gst_ring_buffer_delay (sink->ringbuffer);
+
+  if (G_LIKELY (samples >= delay))
+    samples -= delay;
+  else
+    samples = 0;
 
   result = gst_util_uint64_scale_int (samples, GST_SECOND,
       sink->ringbuffer->spec.rate);
 
+  GST_DEBUG_OBJECT (sink,
+      "processed samples: raw %llu, delay %u, real %llu, time %"
+      GST_TIME_FORMAT, raw, delay, samples, GST_TIME_ARGS (result));
+
   return result;
 }
 
@@ -707,7 +721,7 @@ static void
 gst_base_audio_sink_callback (GstRingBuffer * rbuf, guint8 * data, guint len,
     gpointer user_data)
 {
-  //GstBaseAudioSink *sink = GST_BASE_AUDIO_SINK (data);
+  /* GstBaseAudioSink *sink = GST_BASE_AUDIO_SINK (data); */
 }
 
 /* should be called with the LOCK */
index d427566..3e68901 100644 (file)
@@ -75,7 +75,7 @@ static void gst_base_audio_src_get_times (GstBaseSrc * bsrc,
     GstBuffer * buffer, GstClockTime * start, GstClockTime * end);
 static gboolean gst_base_audio_src_setcaps (GstBaseSrc * bsrc, GstCaps * caps);
 
-//static guint gst_base_audio_src_signals[LAST_SIGNAL] = { 0 };
+/* static guint gst_base_audio_src_signals[LAST_SIGNAL] = { 0 }; */
 
 static void
 gst_base_audio_src_base_init (gpointer g_class)
@@ -201,17 +201,28 @@ wrong_state:
 static GstClockTime
 gst_base_audio_src_get_time (GstClock * clock, GstBaseAudioSrc * src)
 {
-  guint64 samples;
+  guint64 raw, samples;
+  guint delay;
   GstClockTime result;
 
   if (G_UNLIKELY (src->ringbuffer == NULL || src->ringbuffer->spec.rate == 0))
     return GST_CLOCK_TIME_NONE;
 
-  samples = gst_ring_buffer_samples_done (src->ringbuffer);
+  raw = samples = gst_ring_buffer_samples_done (src->ringbuffer);
+
+  /* the number of samples not yet processed, this is still queued in the
+   * device (not yet read for capture). */
+  delay = gst_ring_buffer_delay (src->ringbuffer);
+
+  samples += delay;
 
   result = gst_util_uint64_scale_int (samples, GST_SECOND,
       src->ringbuffer->spec.rate);
 
+  GST_DEBUG_OBJECT (src,
+      "processed samples: raw %llu, delay %u, real %llu, time %"
+      GST_TIME_FORMAT, raw, delay, samples, GST_TIME_ARGS (result));
+
   return result;
 }
 
@@ -271,14 +282,24 @@ static void
 gst_base_audio_src_fixate (GstPad * pad, GstCaps * caps)
 {
   GstStructure *s;
+  gint width, depth;
 
   s = gst_caps_get_structure (caps, 0);
 
+  /* fields for all formats */
   gst_structure_fixate_field_nearest_int (s, "rate", 44100);
   gst_structure_fixate_field_nearest_int (s, "channels", 2);
-  gst_structure_fixate_field_nearest_int (s, "depth", 16);
   gst_structure_fixate_field_nearest_int (s, "width", 16);
-  gst_structure_set (s, "signed", G_TYPE_BOOLEAN, TRUE, NULL);
+
+  /* fields for int */
+  if (gst_structure_has_field (s, "depth")) {
+    gst_structure_get_int (s, "width", &width);
+    /* round width to nearest multiple of 8 for the depth */
+    depth = GST_ROUND_UP_8 (width);
+    gst_structure_fixate_field_nearest_int (s, "depth", depth);
+  }
+  if (gst_structure_has_field (s, "signed"))
+    gst_structure_fixate_field_boolean (s, "signed", TRUE);
   if (gst_structure_has_field (s, "endianness"))
     gst_structure_fixate_field_nearest_int (s, "endianness", G_BYTE_ORDER);
 }
@@ -341,9 +362,8 @@ static void
 gst_base_audio_src_get_times (GstBaseSrc * bsrc, GstBuffer * buffer,
     GstClockTime * start, GstClockTime * end)
 {
-  /* ne need to sync to a clock here, we schedule the samples based
-   * on our own clock for the moment. FIXME, implement this when
-   * we are not using our own clock */
+  /* no need to sync to a clock here, we schedule the samples based
+   * on our own clock for the moment. */
   *start = GST_CLOCK_TIME_NONE;
   *end = GST_CLOCK_TIME_NONE;
 }
@@ -369,6 +389,45 @@ gst_base_audio_src_event (GstBaseSrc * bsrc, GstEvent * event)
   return TRUE;
 }
 
+/* get the next offset in the ringbuffer for reading samples.
+ * If the next sample is too far away, this function will position itself to the
+ * next most recent sample, creating discontinuity */
+static guint64
+gst_base_audio_src_get_offset (GstBaseAudioSrc * src)
+{
+  guint64 sample;
+  gint readseg, segdone, segtotal, sps;
+  gint diff;
+
+  /* assume we can append to the previous sample */
+  sample = src->next_sample;
+  /* no previous sample, try to read from position 0 */
+  if (sample == -1)
+    sample = 0;
+
+  sps = src->ringbuffer->samples_per_seg;
+  segtotal = src->ringbuffer->spec.segtotal;
+
+  /* figure out the segment and the offset inside the segment where
+   * the sample should be read from. */
+  readseg = sample / sps;
+
+  /* get the currently processed segment */
+  segdone = g_atomic_int_get (&src->ringbuffer->segdone)
+      - src->ringbuffer->segbase;
+
+  /* see how far away it is from the read segment, normally segdone (where new
+   * data is written in the ringbuffer) is bigger than readseg (where we are
+   * reading). */
+  diff = segdone - readseg;
+  if (diff >= segtotal) {
+    /* sample would be dropped, position to next playable position */
+    sample = (segdone - segtotal + 1) * sps;
+  }
+
+  return sample;
+}
+
 static GstFlowReturn
 gst_base_audio_src_create (GstBaseSrc * bsrc, guint64 offset, guint length,
     GstBuffer ** outbuf)
@@ -396,14 +455,17 @@ gst_base_audio_src_create (GstBaseSrc * bsrc, guint64 offset, guint length,
     /* make sure we round down to an integral number of samples */
     length -= length % bps;
 
-  /* calculate the sequentially next sample we need to read */
-  sample = (src->next_sample != -1 ? src->next_sample : 0);
-
+  /* figure out the offset in the ringbuffer */
   if (G_UNLIKELY (offset != -1)) {
-    /* if a specific offset was given it must be the next
-     * sequential offset we expect or we fail. */
-    if (offset / bps != sample)
+    sample = offset / bps;
+    /* if a specific offset was given it must be the next sequential
+     * offset we expect or we fail for now. */
+    if (src->next_sample != -1 && sample != src->next_sample)
       goto wrong_offset;
+  } else {
+    /* calculate the sequentially next sample we need to read. This can jump and
+     * create a DISCONT. */
+    sample = gst_base_audio_src_get_offset (src);
   }
 
   /* get the number of samples to read */
@@ -413,10 +475,19 @@ gst_base_audio_src_create (GstBaseSrc * bsrc, guint64 offset, guint length,
   buf = gst_buffer_new_and_alloc (length);
   data = GST_BUFFER_DATA (buf);
 
+  /* read the sample */
   res = gst_ring_buffer_read (ringbuffer, sample, data, samples);
   if (G_UNLIKELY (res == -1))
     goto stopped;
 
+  /* mark discontinuity if needed */
+  if (G_UNLIKELY (sample != src->next_sample) && src->next_sample != -1) {
+    GST_WARNING_OBJECT (src,
+        "create DISCONT of %" G_GUINT64_FORMAT " samples at sample %"
+        G_GUINT64_FORMAT, sample - src->next_sample, sample);
+    GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
+  }
+
   /* FIXME, we timestamp against our own clock, also handle the case
    * where we are slaved to another clock. We currently refuse to accept
    * any other clock than the one we provide, so this code is fine for
@@ -426,6 +497,8 @@ gst_base_audio_src_create (GstBaseSrc * bsrc, guint64 offset, guint length,
   src->next_sample = sample + samples;
   GST_BUFFER_DURATION (buf) = gst_util_uint64_scale_int (src->next_sample,
       GST_SECOND, ringbuffer->spec.rate) - GST_BUFFER_TIMESTAMP (buf);
+  GST_BUFFER_OFFSET (buf) = sample;
+  GST_BUFFER_OFFSET_END (buf) = sample + samples;
 
   gst_buffer_set_caps (buf, GST_PAD_CAPS (GST_BASE_SRC_PAD (bsrc)));
 
@@ -470,13 +543,6 @@ gst_base_audio_src_create_ringbuffer (GstBaseAudioSrc * src)
   return buffer;
 }
 
-void
-gst_base_audio_src_callback (GstRingBuffer * rbuf, guint8 * data, guint len,
-    gpointer user_data)
-{
-  //GstBaseAudioSrc *src = GST_BASE_AUDIO_SRC (data);
-}
-
 static GstStateChangeReturn
 gst_base_audio_src_change_state (GstElement * element,
     GstStateChange transition)
@@ -488,12 +554,10 @@ gst_base_audio_src_change_state (GstElement * element,
     case GST_STATE_CHANGE_NULL_TO_READY:
       if (src->ringbuffer == NULL) {
         src->ringbuffer = gst_base_audio_src_create_ringbuffer (src);
-        gst_ring_buffer_set_callback (src->ringbuffer,
-            gst_base_audio_src_callback, src);
       }
       if (!gst_ring_buffer_open_device (src->ringbuffer))
         return GST_STATE_CHANGE_FAILURE;
-      src->next_sample = 0;
+      src->next_sample = -1;
       break;
     case GST_STATE_CHANGE_READY_TO_PAUSED:
       gst_ring_buffer_set_flushing (src->ringbuffer, FALSE);
@@ -515,7 +579,7 @@ gst_base_audio_src_change_state (GstElement * element,
     case GST_STATE_CHANGE_PAUSED_TO_READY:
       gst_ring_buffer_set_flushing (src->ringbuffer, TRUE);
       gst_ring_buffer_release (src->ringbuffer);
-      src->next_sample = 0;
+      src->next_sample = -1;
       break;
     case GST_STATE_CHANGE_READY_TO_NULL:
       gst_ring_buffer_close_device (src->ringbuffer);
index ee5ad71..11ee320 100644 (file)
@@ -920,6 +920,7 @@ gst_ring_buffer_pause (GstRingBuffer * buf)
 
   return res;
 
+  /* ERRORS */
 flushing:
   {
     GST_OBJECT_UNLOCK (buf);
@@ -989,6 +990,12 @@ done:
  * implementation uses another internal buffer between the audio
  * device.
  *
+ * For playback ringbuffers this is the amount of samples transfered from the
+ * ringbuffer to the device but still not played.
+ *
+ * For capture ringbuffers this is the amount of samples in the device that are
+ * not yet transfered to the ringbuffer.
+ *
  * Returns: The number of samples queued in the audio device.
  *
  * MT safe.
@@ -1020,7 +1027,8 @@ done:
  * @buf: the #GstRingBuffer to query
  *
  * Get the number of samples that were processed by the ringbuffer
- * since it was last started.
+ * since it was last started. This does not include the number of samples not
+ * yet processed (see gst_ring_buffer_delay()).
  *
  * Returns: The number of samples processed by the ringbuffer.
  *
@@ -1038,18 +1046,8 @@ gst_ring_buffer_samples_done (GstRingBuffer * buf)
   /* get the amount of segments we processed */
   segdone = g_atomic_int_get (&buf->segdone);
 
-  /* and the number of samples not yet processed */
-  delay = gst_ring_buffer_delay (buf);
-
-  raw = samples = ((guint64) segdone) * buf->samples_per_seg;
-
-  if (G_LIKELY (samples >= delay))
-    samples -= delay;
-  else
-    samples = 0;
-
-  GST_DEBUG_OBJECT (buf, "processed samples: raw %llu, delay %u, real %llu",
-      raw, delay, samples);
+  /* convert to samples */
+  samples = ((guint64) segdone) * buf->samples_per_seg;
 
   return samples;
 }
@@ -1237,9 +1235,10 @@ gst_ring_buffer_commit (GstRingBuffer * buf, guint64 sample, guchar * data,
 
       GST_DEBUG
           ("pointer at %d, sample %llu, write to %d-%d, to_write %d, diff %d, segtotal %d, segsize %d",
-          segdone, sample, writeseg, sampleoff, to_write, diff, segtotal, sps);
+          segdone, sample, writeseg, sampleoff, to_write, diff, segtotal,
+          segsize);
 
-      /* segment too far ahead, we need to drop, hopefully UNLIKELY */
+      /* segment too far ahead, writer too slow, we need to drop, hopefully UNLIKELY */
       if (G_UNLIKELY (diff < 0)) {
         /* we need to drop one segment at a time, pretend we wrote a
          * segment. */
@@ -1310,6 +1309,7 @@ gst_ring_buffer_read (GstRingBuffer * buf, guint64 sample, guchar * data,
   gint segdone;
   gint segsize, segtotal, bps, sps;
   guint8 *dest;
+  guint to_read;
 
   g_return_val_if_fail (GST_IS_RING_BUFFER (buf), -1);
   g_return_val_if_fail (buf->data != NULL, -1);
@@ -1321,13 +1321,14 @@ gst_ring_buffer_read (GstRingBuffer * buf, guint64 sample, guchar * data,
   bps = buf->spec.bytes_per_sample;
   sps = buf->samples_per_seg;
 
+  to_read = len;
   /* read enough samples */
-  while (len > 0) {
+  while (to_read > 0) {
     gint sampleslen;
     gint readseg, sampleoff;
 
     /* figure out the segment and the offset inside the segment where
-     * the sample should be written. */
+     * the sample should be read from. */
     readseg = sample / sps;
     sampleoff = (sample % sps);
 
@@ -1337,33 +1338,29 @@ gst_ring_buffer_read (GstRingBuffer * buf, guint64 sample, guchar * data,
       /* get the currently processed segment */
       segdone = g_atomic_int_get (&buf->segdone) - buf->segbase;
 
-      /* see how far away it is from the read segment */
+      /* see how far away it is from the read segment, normally segdone (where
+       * the hardware is writing) is bigger than readseg (where software is
+       * reading) */
       diff = segdone - readseg;
 
       GST_DEBUG
-          ("pointer at %d, sample %llu, read from %d-%d, len %d, diff %d, segtotal %d, segsize %d",
-          segdone, sample, readseg, sampleoff, len, diff, segtotal, segsize);
-
-      /* segment too far ahead, we need to drop */
-      if (diff < 0) {
-        /* we need to drop one segment at a time, pretend we read an
-         * empty segment. */
-        sampleslen = MIN (sps, len);
+          ("pointer at %d, sample %llu, read from %d-%d, to_read %d, diff %d, segtotal %d, segsize %d",
+          segdone, sample, readseg, sampleoff, to_read, diff, segtotal,
+          segsize);
+
+      /* segment too far ahead, reader too slow */
+      if (G_UNLIKELY (diff >= segtotal)) {
+        /* pretend we read an empty segment. */
+        sampleslen = MIN (sps, to_read);
         memcpy (data, buf->empty_seg, sampleslen * bps);
         goto next;
       }
 
       /* read segment is within readable range, we can break the loop and
        * start reading the data. */
-      if (diff > 0 && diff < segtotal)
+      if (diff > 0)
         break;
 
-      /* flush if diff has grown bigger than ringbuffer */
-      if (diff >= segtotal) {
-        gst_ring_buffer_clear_all (buf);
-        buf->segdone = readseg;
-      }
-
       /* else we need to wait for the segment to become readable. */
       if (!wait_segment (buf))
         goto not_started;
@@ -1371,26 +1368,27 @@ gst_ring_buffer_read (GstRingBuffer * buf, guint64 sample, guchar * data,
 
     /* we can read now */
     readseg = readseg % segtotal;
-    sampleslen = MIN (sps - sampleoff, len);
+    sampleslen = MIN (sps - sampleoff, to_read);
 
-    GST_DEBUG_OBJECT (buf, "read @%p seg %d, off %d, len %d",
+    GST_DEBUG_OBJECT (buf, "read @%p seg %d, off %d, sampleslen %d",
         dest + readseg * segsize, readseg, sampleoff, sampleslen);
 
     memcpy (data, dest + (readseg * segsize) + (sampleoff * bps),
         (sampleslen * bps));
 
   next:
-    len -= sampleslen;
+    to_read -= sampleslen;
     sample += sampleslen;
     data += sampleslen * bps;
   }
 
-  return len;
+  return len - to_read;
 
   /* ERRORS */
 not_started:
   {
     GST_DEBUG_OBJECT (buf, "stopped processing");
+    /* FIXME, return len - to_read after fixing caller */
     return -1;
   }
 }