adapter: Add methods to query current offset

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
This commit is contained in:
Edward Hervey 2016-05-19 10:31:02 +02:00 committed by Sebastian Dröge
parent 665de91347
commit 67ae0ad225
5 changed files with 267 additions and 3 deletions

View 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>

View file

@ -92,6 +92,17 @@
* 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
@ -118,6 +129,8 @@
/* 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;
@ -1311,6 +1353,57 @@ gst_adapter_available_fast (GstAdapter * adapter)
return size - adapter->skip;
}
/**
* 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

View 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);

View 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;
}

View 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