adapter: Add methods to query current offset
authorEdward Hervey <edward@centricular.com>
Thu, 19 May 2016 08:31:02 +0000 (10:31 +0200)
committerSebastian Dröge <sebastian@centricular.com>
Fri, 10 Jun 2016 06:49:33 +0000 (09:49 +0300)
API: gst_buffer_prev_offset
API: gst_buffer_get_offset_from_discont

The gst_buffer_get_offset_from_discont() method allows retrieving the current
offset based on the GST_BUFFER_OFFSET of the buffers that were pushed in.

The offset will be set initially by the GST_BUFFER_OFFSET of
DISCONT buffers, and then incremented by the sizes of the following
buffers.

The gst_buffer_prev_offset() method allows retrievent the previous
GST_BUFFER_OFFSET regardless of flags. It works in the same way as
the other gst_buffer_prev_*() methods.

https://bugzilla.gnome.org/show_bug.cgi?id=766647

docs/libs/gstreamer-libs-sections.txt
libs/gst/base/gstadapter.c
libs/gst/base/gstadapter.h
tests/check/libs/adapter.c
win32/common/libgstbase.def

index 09d0b11..a43624d 100644 (file)
@@ -178,6 +178,8 @@ gst_adapter_prev_pts
 gst_adapter_prev_dts
 gst_adapter_prev_pts_at_offset
 gst_adapter_prev_dts_at_offset
+gst_adapter_prev_offset
+gst_adapter_get_offset_from_discont
 gst_adapter_masked_scan_uint32
 gst_adapter_masked_scan_uint32_peek
 <SUBSECTION Standard>
index c017486..c37d2de 100644 (file)
  * gst_adapter_prev_pts_at_offset() can be used to determine the last
  * seen timestamp at a particular offset in the adapter.
  *
+ * The adapter will also keep track of the offset of the buffers
+ * (#GST_BUFFER_OFFSET) that were pushed. The last seen offset before the
+ * current position can be queried with gst_adapter_prev_offset(). This function
+ * can optionally return the number of bytes between the start of the buffer
+ * that carried the offset and the current adapter position. If the meaning of
+ * #GST_BUFFER_OFFSET for the stream being handled corresponds to bytes, then
+ * the accumulated offset since the last #GST_BUFFER_FLAG_DISCONT buffer can be
+ * queried with gst_adapter_get_offset_from_discont(). This is useful for
+ * elements that want to track the position of data in the stream based on the
+ * offset of the incoming buffers.
+ *
  * A last thing to note is that while #GstAdapter is pretty optimized,
  * merging buffers still might be an operation that requires a malloc() and
  * memcpy() operation, and these operations are not the fastest. Because of
 /* default size for the assembled data buffer */
 #define DEFAULT_SIZE 4096
 
+#define INCREASE_OFFSET(a, offs) if ((a)->offset_discont != GST_BUFFER_OFFSET_NONE) { (a)->offset_discont += (offs); }
+
 static void gst_adapter_flush_unchecked (GstAdapter * adapter, gsize flush);
 
 GST_DEBUG_CATEGORY_STATIC (gst_adapter_debug);
@@ -143,10 +156,14 @@ struct _GstAdapter
   guint64 pts_distance;
   GstClockTime dts;
   guint64 dts_distance;
+  guint64 offset;
+  guint64 offset_distance;
 
   gsize scan_offset;
   GSList *scan_entry;
 
+  guint64 offset_discont;
+
   GstMapInfo info;
 };
 
@@ -181,6 +198,9 @@ gst_adapter_init (GstAdapter * adapter)
   adapter->pts_distance = 0;
   adapter->dts = GST_CLOCK_TIME_NONE;
   adapter->dts_distance = 0;
+  adapter->offset = GST_BUFFER_OFFSET_NONE;
+  adapter->offset_distance = 0;
+  adapter->offset_discont = GST_BUFFER_OFFSET_NONE;
 }
 
 static void
@@ -242,14 +262,18 @@ gst_adapter_clear (GstAdapter * adapter)
   adapter->pts_distance = 0;
   adapter->dts = GST_CLOCK_TIME_NONE;
   adapter->dts_distance = 0;
+  adapter->offset = GST_BUFFER_OFFSET_NONE;
+  adapter->offset_distance = 0;
   adapter->scan_offset = 0;
   adapter->scan_entry = NULL;
+  adapter->offset_discont = GST_BUFFER_OFFSET_NONE;
 }
 
 static inline void
-update_timestamps (GstAdapter * adapter, GstBuffer * buf)
+update_timestamps_and_offset (GstAdapter * adapter, GstBuffer * buf)
 {
   GstClockTime pts, dts;
+  guint64 offset;
 
   pts = GST_BUFFER_PTS (buf);
   if (GST_CLOCK_TIME_IS_VALID (pts)) {
@@ -263,6 +287,18 @@ update_timestamps (GstAdapter * adapter, GstBuffer * buf)
     adapter->dts = dts;
     adapter->dts_distance = 0;
   }
+  offset = GST_BUFFER_OFFSET (buf);
+  if (GST_BUFFER_IS_DISCONT (buf)) {
+    /* Take offset as-is (might be NONE) */
+    adapter->offset_discont = offset;
+    GST_LOG_OBJECT (adapter, "offset discont now %" G_GUINT64_FORMAT,
+        adapter->offset_discont);
+  }
+  if (offset != GST_BUFFER_OFFSET_NONE) {
+    GST_LOG_OBJECT (adapter, "new offset %" G_GUINT64_FORMAT, offset);
+    adapter->offset = offset;
+    adapter->offset_distance = 0;
+  }
 }
 
 /* copy data into @dest, skipping @skip bytes from the head buffers */
@@ -340,7 +376,7 @@ gst_adapter_push (GstAdapter * adapter, GstBuffer * buf)
     GST_LOG_OBJECT (adapter, "pushing %p first %" G_GSIZE_FORMAT " bytes",
         buf, size);
     adapter->buflist = adapter->buflist_end = g_slist_append (NULL, buf);
-    update_timestamps (adapter, buf);
+    update_timestamps_and_offset (adapter, buf);
   } else {
     /* Otherwise append to the end, and advance our end pointer */
     GST_LOG_OBJECT (adapter, "pushing %p %" G_GSIZE_FORMAT " bytes at end, "
@@ -597,6 +633,8 @@ gst_adapter_flush_unchecked (GstAdapter * adapter, gsize flush)
   /* distance is always at least the amount of skipped bytes */
   adapter->pts_distance -= adapter->skip;
   adapter->dts_distance -= adapter->skip;
+  adapter->offset_distance -= adapter->skip;
+  INCREASE_OFFSET (adapter, -adapter->skip);
 
   g = adapter->buflist;
   cur = g->data;
@@ -606,7 +644,9 @@ gst_adapter_flush_unchecked (GstAdapter * adapter, gsize flush)
     GST_LOG_OBJECT (adapter, "flushing out head buffer");
     adapter->pts_distance += size;
     adapter->dts_distance += size;
+    adapter->offset_distance += size;
     flush -= size;
+    INCREASE_OFFSET (adapter, size);
 
     gst_buffer_unref (cur);
     g = g_slist_delete_link (g, g);
@@ -619,7 +659,7 @@ gst_adapter_flush_unchecked (GstAdapter * adapter, gsize flush)
     }
     /* there is a new head buffer, update the timestamps */
     cur = g->data;
-    update_timestamps (adapter, cur);
+    update_timestamps_and_offset (adapter, cur);
     size = gst_buffer_get_size (cur);
   }
   adapter->buflist = g;
@@ -627,6 +667,8 @@ gst_adapter_flush_unchecked (GstAdapter * adapter, gsize flush)
   adapter->skip = flush;
   adapter->pts_distance += flush;
   adapter->dts_distance += flush;
+  adapter->offset_distance += flush;
+  INCREASE_OFFSET (adapter, flush);
   /* invalidate scan position */
   adapter->scan_offset = 0;
   adapter->scan_entry = NULL;
@@ -1312,6 +1354,57 @@ gst_adapter_available_fast (GstAdapter * adapter)
 }
 
 /**
+ * gst_adapter_get_offset_from_discont:
+ * @adapter: a #GstAdapter
+ *
+ * Get the offset of the adapter based on the incoming buffer offset.  Will only
+ * return valid values if the incoming buffers have valid offsets set on them.
+ *
+ * The offset will be initially recorded for all buffers with
+ * %GST_BUFFER_FLAG_DISCONT on them, and then calculated for all other following
+ * buffers based on their size.
+ *
+ * Since: 1.10
+ *
+ * Returns: The offset. Can be %GST_BUFFER_OFFSET_NONE.
+ */
+guint64
+gst_adapter_get_offset_from_discont (GstAdapter * adapter)
+{
+  return adapter->offset_discont;
+}
+
+/**
+ * gst_adapter_prev_offset:
+ * @adapter: a #GstAdapter
+ * @distance: (out) (allow-none): pointer to a location for distance, or %NULL
+ *
+ * Get the offset that was before the current byte in the adapter. When
+ * @distance is given, the amount of bytes between the offset and the current
+ * position is returned.
+ *
+ * The offset is reset to GST_BUFFER_OFFSET_NONE and the distance is set to 0
+ * when the adapter is first created or when it is cleared. This also means that
+ * before the first byte with an offset is removed from the adapter, the offset
+ * and distance returned are GST_BUFFER_OFFSET_NONE and 0 respectively.
+ *
+ * Since: 1.10
+ *
+ * Returns: The previous seen offset.
+ */
+guint64
+gst_adapter_prev_offset (GstAdapter * adapter, guint64 * distance)
+{
+  g_return_val_if_fail (GST_IS_ADAPTER (adapter), GST_BUFFER_OFFSET_NONE);
+
+  if (distance)
+    *distance = adapter->offset_distance;
+
+  return adapter->offset;
+
+}
+
+/**
  * gst_adapter_prev_pts:
  * @adapter: a #GstAdapter
  * @distance: (out) (allow-none): pointer to location for distance, or %NULL
index bb8c77d..92d9ba3 100644 (file)
@@ -75,6 +75,8 @@ GstClockTime            gst_adapter_prev_pts            (GstAdapter *adapter, gu
 GstClockTime            gst_adapter_prev_dts            (GstAdapter *adapter, guint64 *distance);
 GstClockTime            gst_adapter_prev_pts_at_offset  (GstAdapter * adapter, gsize offset, guint64 * distance);
 GstClockTime            gst_adapter_prev_dts_at_offset  (GstAdapter * adapter, gsize offset, guint64 * distance);
+guint64                 gst_adapter_prev_offset         (GstAdapter *adapter, guint64 *distance);
+guint64                 gst_adapter_get_offset_from_discont (GstAdapter *adapter);
 
 gssize                  gst_adapter_masked_scan_uint32  (GstAdapter * adapter, guint32 mask,
                                                          guint32 pattern, gsize offset, gsize size);
index 95d2952..a0b7070 100644 (file)
@@ -364,6 +364,7 @@ GST_START_TEST (test_timestamp)
   guint64 dist;
   guint8 *data;
   const guint8 *cdata;
+  guint64 offset;
 
   adapter = gst_adapter_new ();
   fail_unless (adapter != NULL);
@@ -380,6 +381,10 @@ GST_START_TEST (test_timestamp)
   fail_unless (timestamp == GST_CLOCK_TIME_NONE);
   fail_unless (dist == 0);
 
+  /* Offset should be undefined */
+  offset = gst_adapter_get_offset_from_discont (adapter);
+  fail_unless (offset == GST_BUFFER_OFFSET_NONE);
+
   gst_adapter_flush (adapter, 50);
   avail = gst_adapter_available (adapter);
   fail_unless (avail == 50);
@@ -389,6 +394,10 @@ GST_START_TEST (test_timestamp)
   fail_unless (timestamp == GST_CLOCK_TIME_NONE);
   fail_unless (dist == 50);
 
+  /* Offset still undefined */
+  offset = gst_adapter_get_offset_from_discont (adapter);
+  fail_unless (offset == GST_BUFFER_OFFSET_NONE);
+
   buffer = gst_buffer_new_and_alloc (100);
   GST_BUFFER_TIMESTAMP (buffer) = 1 * GST_SECOND;
 
@@ -563,6 +572,161 @@ GST_START_TEST (test_timestamp)
 
 GST_END_TEST;
 
+GST_START_TEST (test_offset)
+{
+  GstAdapter *adapter;
+  GstBuffer *buffer;
+  guint avail;
+  guint64 offset;
+  guint64 dist;
+
+  adapter = gst_adapter_new ();
+  fail_unless (adapter != NULL);
+
+  buffer = gst_buffer_new_and_alloc (100);
+
+  /* push in the adapter */
+  gst_adapter_push (adapter, buffer);
+  avail = gst_adapter_available (adapter);
+  fail_unless (avail == 100);
+
+  /* Offset should be undefined */
+  offset = gst_adapter_get_offset_from_discont (adapter);
+  fail_unless_equals_uint64 (offset, GST_BUFFER_OFFSET_NONE);
+  offset = gst_adapter_prev_offset (adapter, &dist);
+  fail_unless_equals_uint64 (offset, GST_BUFFER_OFFSET_NONE);
+  fail_unless_equals_int (dist, 0);
+
+  gst_adapter_flush (adapter, 50);
+  avail = gst_adapter_available (adapter);
+  fail_unless (avail == 50);
+
+  /* Offset still undefined, dist changed though */
+  offset = gst_adapter_get_offset_from_discont (adapter);
+  fail_unless_equals_uint64 (offset, GST_BUFFER_OFFSET_NONE);
+  offset = gst_adapter_prev_offset (adapter, &dist);
+  fail_unless_equals_uint64 (offset, GST_BUFFER_OFFSET_NONE);
+  fail_unless_equals_int (dist, 50);
+
+  /* Let's push in a discont buffer with a valid offset */
+  buffer = gst_buffer_new_and_alloc (100);
+  GST_BUFFER_OFFSET (buffer) = 10000;
+  GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
+
+  /* push in the adapter */
+  gst_adapter_push (adapter, buffer);
+  avail = gst_adapter_available (adapter);
+  fail_unless (avail == 150);
+
+  /* offset is still undefined */
+  offset = gst_adapter_get_offset_from_discont (adapter);
+  fail_unless_equals_uint64 (offset, GST_BUFFER_OFFSET_NONE);
+  offset = gst_adapter_prev_offset (adapter, &dist);
+  fail_unless_equals_uint64 (offset, GST_BUFFER_OFFSET_NONE);
+  fail_unless_equals_int (dist, 50);
+
+  /* flush out first buffer we are now at the second buffer offset */
+  gst_adapter_flush (adapter, 50);
+  avail = gst_adapter_available (adapter);
+  fail_unless (avail == 100);
+
+  offset = gst_adapter_get_offset_from_discont (adapter);
+  fail_unless_equals_uint64 (offset, 10000);
+  offset = gst_adapter_prev_offset (adapter, &dist);
+  fail_unless_equals_uint64 (offset, 10000);
+  fail_unless_equals_int (dist, 0);
+
+  /* move some more, we should have an updated offset */
+  gst_adapter_flush (adapter, 50);
+  avail = gst_adapter_available (adapter);
+  fail_unless (avail == 50);
+
+  offset = gst_adapter_get_offset_from_discont (adapter);
+  fail_unless_equals_uint64 (offset, 10050);
+  offset = gst_adapter_prev_offset (adapter, &dist);
+  fail_unless_equals_uint64 (offset, 10000);
+  fail_unless_equals_int (dist, 50);
+
+  /* push a buffer without offset in the adapter (contiguous with the
+     other) */
+  buffer = gst_buffer_new_and_alloc (100);
+  gst_adapter_push (adapter, buffer);
+  avail = gst_adapter_available (adapter);
+  fail_unless (avail == 150);
+
+  /* push a buffer with offset in the adapter (but contiguous with the
+     other), the offset shouldn't be taken into account */
+  buffer = gst_buffer_new_and_alloc (100);
+  GST_BUFFER_OFFSET (buffer) = 50000;
+  gst_adapter_push (adapter, buffer);
+  avail = gst_adapter_available (adapter);
+  fail_unless (avail == 250);
+
+  /* offset still as it was before the push */
+  offset = gst_adapter_get_offset_from_discont (adapter);
+  fail_unless_equals_uint64 (offset, 10050);
+  offset = gst_adapter_prev_offset (adapter, &dist);
+  fail_unless_equals_uint64 (offset, 10000);
+  fail_unless_equals_int (dist, 50);
+
+  /* flush away buffer with the offset */
+  gst_adapter_flush (adapter, 50);
+  avail = gst_adapter_available (adapter);
+  fail_unless (avail == 200);
+  offset = gst_adapter_get_offset_from_discont (adapter);
+  fail_unless_equals_uint64 (offset, 10100);
+  /* The previous valid offset seen is now 100 bytes away */
+  offset = gst_adapter_prev_offset (adapter, &dist);
+  fail_unless_equals_uint64 (offset, 10000);
+  fail_unless_equals_int (dist, 100);
+
+  /* move into the second buffer */
+  gst_adapter_flush (adapter, 50);
+  avail = gst_adapter_available (adapter);
+  fail_unless (avail == 150);
+  offset = gst_adapter_get_offset_from_discont (adapter);
+  fail_unless_equals_uint64 (offset, 10150);
+  offset = gst_adapter_prev_offset (adapter, &dist);
+  fail_unless_equals_uint64 (offset, 10000);
+  fail_unless_equals_int (dist, 150);
+
+  /* move to third buffer, we should still see a continuously increasing
+   * offset and ignore the non-discont offset */
+  gst_adapter_flush (adapter, 50);
+  avail = gst_adapter_available (adapter);
+  fail_unless (avail == 100);
+  offset = gst_adapter_get_offset_from_discont (adapter);
+  fail_unless_equals_uint64 (offset, 10200);
+  /* But the prev_offset *does* give us the actual buffer offset value */
+  offset = gst_adapter_prev_offset (adapter, &dist);
+  fail_unless_equals_uint64 (offset, 50000);
+  fail_unless_equals_int (dist, 0);
+
+  /* move everything out, we end up at the last offset */
+  gst_adapter_flush (adapter, 100);
+  avail = gst_adapter_available (adapter);
+  fail_unless (avail == 0);
+  offset = gst_adapter_get_offset_from_discont (adapter);
+  fail_unless_equals_uint64 (offset, 10300);
+  offset = gst_adapter_prev_offset (adapter, &dist);
+  fail_unless_equals_uint64 (offset, 50000);
+  fail_unless_equals_int (dist, 100);
+
+  /* clear everything */
+  gst_adapter_clear (adapter);
+  avail = gst_adapter_available (adapter);
+  fail_unless (avail == 0);
+  offset = gst_adapter_get_offset_from_discont (adapter);
+  fail_unless_equals_uint64 (offset, GST_BUFFER_OFFSET_NONE);
+  offset = gst_adapter_prev_offset (adapter, &dist);
+  fail_unless_equals_uint64 (offset, GST_BUFFER_OFFSET_NONE);
+  fail_unless_equals_int (dist, 0);
+
+  g_object_unref (adapter);
+}
+
+GST_END_TEST;
+
 GST_START_TEST (test_scan)
 {
   GstAdapter *adapter;
@@ -1064,6 +1228,7 @@ gst_adapter_suite (void)
   tcase_add_test (tc_chain, test_get_buffer_list);
   tcase_add_test (tc_chain, test_merge);
   tcase_add_test (tc_chain, test_take_buffer_fast);
+  tcase_add_test (tc_chain, test_offset);
 
   return s;
 }
index ff25b51..18dd221 100644 (file)
@@ -9,6 +9,7 @@ EXPORTS
        gst_adapter_get_buffer_fast
        gst_adapter_get_buffer_list
        gst_adapter_get_list
+       gst_adapter_get_offset_from_discont
        gst_adapter_get_type
        gst_adapter_map
        gst_adapter_masked_scan_uint32
@@ -16,6 +17,7 @@ EXPORTS
        gst_adapter_new
        gst_adapter_prev_dts
        gst_adapter_prev_dts_at_offset
+       gst_adapter_prev_offset
        gst_adapter_prev_pts
        gst_adapter_prev_pts_at_offset
        gst_adapter_push