diff --git a/docs/libs/gstreamer-libs-sections.txt b/docs/libs/gstreamer-libs-sections.txt index 09d0b1160a..a43624d287 100644 --- a/docs/libs/gstreamer-libs-sections.txt +++ b/docs/libs/gstreamer-libs-sections.txt @@ -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 diff --git a/libs/gst/base/gstadapter.c b/libs/gst/base/gstadapter.c index c0174868c5..c37d2de28d 100644 --- a/libs/gst/base/gstadapter.c +++ b/libs/gst/base/gstadapter.c @@ -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 diff --git a/libs/gst/base/gstadapter.h b/libs/gst/base/gstadapter.h index bb8c77d1e7..92d9ba35fa 100644 --- a/libs/gst/base/gstadapter.h +++ b/libs/gst/base/gstadapter.h @@ -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); diff --git a/tests/check/libs/adapter.c b/tests/check/libs/adapter.c index 95d2952805..a0b7070667 100644 --- a/tests/check/libs/adapter.c +++ b/tests/check/libs/adapter.c @@ -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; } diff --git a/win32/common/libgstbase.def b/win32/common/libgstbase.def index ff25b5131c..18dd221e7c 100644 --- a/win32/common/libgstbase.def +++ b/win32/common/libgstbase.def @@ -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