* 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);
guint64 pts_distance;
GstClockTime dts;
guint64 dts_distance;
+ guint64 offset;
+ guint64 offset_distance;
gsize scan_offset;
GSList *scan_entry;
+ guint64 offset_discont;
+
GstMapInfo info;
};
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
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)) {
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 */
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, "
/* 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;
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);
}
/* 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;
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;
}
/**
+ * 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
guint64 dist;
guint8 *data;
const guint8 *cdata;
+ guint64 offset;
adapter = gst_adapter_new ();
fail_unless (adapter != NULL);
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);
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;
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;
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;
}