X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=libs%2Fgst%2Fbase%2Fgstadapter.c;h=bedb3e1b5b62dbca47a8e65a135cb3461ac51059;hb=08bd835f095a27e5f265bf6132abdea11b8045e3;hp=3604019a8d879f07b8aa4393cb8980e61923c030;hpb=17ddbc3072752c903e6c769e63c0e74b83fdd781;p=platform%2Fupstream%2Fgstreamer.git diff --git a/libs/gst/base/gstadapter.c b/libs/gst/base/gstadapter.c index 3604019..bedb3e1 100644 --- a/libs/gst/base/gstadapter.c +++ b/libs/gst/base/gstadapter.c @@ -14,12 +14,13 @@ * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ /** * SECTION:gstadapter + * @title: GstAdapter * @short_description: adapts incoming data on a sink pad into chunks of N bytes * * This class is for elements that receive buffers in an undesired size. @@ -43,20 +44,21 @@ * * For example, a sink pad's chain function that needs to pass data to a library * in 512-byte chunks could be implemented like this: - * |[ + * |[ * static GstFlowReturn - * sink_pad_chain (GstPad *pad, GstBuffer *buffer) + * sink_pad_chain (GstPad *pad, GstObject *parent, GstBuffer *buffer) * { * MyElement *this; * GstAdapter *adapter; * GstFlowReturn ret = GST_FLOW_OK; * - * // will give the element an extra ref; remember to drop it - * this = MY_ELEMENT (gst_pad_get_parent (pad)); + * this = MY_ELEMENT (parent); + * * adapter = this->adapter; * * // put buffer into adapter * gst_adapter_push (adapter, buffer); + * * // while we can read out 512 bytes, process them * while (gst_adapter_available (adapter) >= 512 && ret == GST_FLOW_OK) { * const guint8 *data = gst_adapter_map (adapter, 512); @@ -65,33 +67,45 @@ * gst_adapter_unmap (adapter); * gst_adapter_flush (adapter, 512); * } - * - * gst_object_unref (this); * return ret; * } * ]| * - * For another example, a simple element inside GStreamer that uses GstAdapter + * For another example, a simple element inside GStreamer that uses #GstAdapter * is the libvisual element. * - * An element using GstAdapter in its sink pad chain function should ensure that + * An element using #GstAdapter in its sink pad chain function should ensure that * when the FLUSH_STOP event is received, that any queued data is cleared using * gst_adapter_clear(). Data should also be cleared or processed on EOS and - * when changing state from #GST_STATE_PAUSED to #GST_STATE_READY. + * when changing state from %GST_STATE_PAUSED to %GST_STATE_READY. * * Also check the GST_BUFFER_FLAG_DISCONT flag on the buffer. Some elements might * need to clear the adapter after a discontinuity. * - * Since 0.10.24, the adapter will keep track of the timestamps of the buffers + * The adapter will keep track of the timestamps of the buffers * that were pushed. The last seen timestamp before the current position - * can be queried with gst_adapter_prev_timestamp(). This function can - * optionally return the amount of bytes between the start of the buffer that + * can be queried with gst_adapter_prev_pts(). This function can + * optionally return the number of bytes between the start of the buffer that * carried the timestamp and the current adapter position. The distance is * useful when dealing with, for example, raw audio samples because it allows * you to calculate the timestamp of the current adapter position by using the - * last seen timestamp and the amount of bytes since. - * - * A last thing to note is that while GstAdapter is pretty optimized, + * last seen timestamp and the amount of bytes since. Additionally, the + * 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. + * + * Additionally the adapter also keeps track of the PTS, DTS and buffer offset + * at the last discontinuity, which can be retrieved with + * gst_adapter_pts_at_discont(), gst_adapter_dts_at_discont() and + * gst_adapter_offset_at_discont(). The number of bytes that were consumed + * since then can be queried with gst_adapter_distance_from_discont(). + * + * 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 * this, some functions like gst_adapter_available_fast() are provided to help @@ -99,22 +113,21 @@ * gst_adapter_copy() can be used to copy data into a (statically allocated) * user provided buffer. * - * GstAdapter is not MT safe. All operations on an adapter must be serialized by + * #GstAdapter is not MT safe. All operations on an adapter must be serialized by * the caller. This is not normally a problem, however, as the normal use case - * of GstAdapter is inside one pad's chain function, in which case access is + * of #GstAdapter is inside one pad's chain function, in which case access is * serialized via the pad's STREAM_LOCK. * * Note that gst_adapter_push() takes ownership of the buffer passed. Use * gst_buffer_ref() before pushing it into the adapter if you still want to * access the buffer later. The adapter will never modify the data in the * buffer pushed in it. - * - * Last reviewed on 2009-05-13 (0.10.24). */ #include #include "gstadapter.h" #include +#include /* default size for the assembled data buffer */ #define DEFAULT_SIZE 4096 @@ -124,21 +137,44 @@ static void gst_adapter_flush_unchecked (GstAdapter * adapter, gsize flush); GST_DEBUG_CATEGORY_STATIC (gst_adapter_debug); #define GST_CAT_DEFAULT gst_adapter_debug -#define GST_ADAPTER_GET_PRIVATE(obj) \ - (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_ADAPTER, GstAdapterPrivate)) - -struct _GstAdapterPrivate +struct _GstAdapter { + GObject object; + + /*< private > */ + GstQueueArray *bufqueue; + gsize size; + gsize skip; + guint count; + + /* we keep state of assembled pieces */ + gpointer assembled_data; + gsize assembled_size; + gsize assembled_len; + GstClockTime pts; guint64 pts_distance; GstClockTime dts; guint64 dts_distance; + guint64 offset; + guint64 offset_distance; gsize scan_offset; - GSList *scan_entry; + /* G_MAXUINT when unset */ + guint scan_entry_idx; + + GstClockTime pts_at_discont; + GstClockTime dts_at_discont; + guint64 offset_at_discont; + + guint64 distance_from_discont; - gpointer cdata; - gsize csize; + GstMapInfo info; +}; + +struct _GstAdapterClass +{ + GObjectClass parent_class; }; #define _do_init \ @@ -154,8 +190,6 @@ gst_adapter_class_init (GstAdapterClass * klass) { GObjectClass *object = G_OBJECT_CLASS (klass); - g_type_class_add_private (klass, sizeof (GstAdapterPrivate)); - object->dispose = gst_adapter_dispose; object->finalize = gst_adapter_finalize; } @@ -163,13 +197,19 @@ gst_adapter_class_init (GstAdapterClass * klass) static void gst_adapter_init (GstAdapter * adapter) { - adapter->priv = GST_ADAPTER_GET_PRIVATE (adapter); adapter->assembled_data = g_malloc (DEFAULT_SIZE); adapter->assembled_size = DEFAULT_SIZE; - adapter->priv->pts = GST_CLOCK_TIME_NONE; - adapter->priv->pts_distance = 0; - adapter->priv->dts = GST_CLOCK_TIME_NONE; - adapter->priv->dts_distance = 0; + adapter->pts = GST_CLOCK_TIME_NONE; + 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->pts_at_discont = GST_CLOCK_TIME_NONE; + adapter->dts_at_discont = GST_CLOCK_TIME_NONE; + adapter->offset_at_discont = GST_BUFFER_OFFSET_NONE; + adapter->distance_from_discont = 0; + adapter->bufqueue = gst_queue_array_new (10); } static void @@ -189,6 +229,8 @@ gst_adapter_finalize (GObject * object) g_free (adapter->assembled_data); + gst_queue_array_free (adapter->bufqueue); + GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object)); } @@ -202,7 +244,7 @@ gst_adapter_finalize (GObject * object) GstAdapter * gst_adapter_new (void) { - return g_object_newv (GST_TYPE_ADAPTER, 0, NULL); + return g_object_new (GST_TYPE_ADAPTER, NULL); } /** @@ -214,39 +256,64 @@ gst_adapter_new (void) void gst_adapter_clear (GstAdapter * adapter) { + GstMiniObject *obj; g_return_if_fail (GST_IS_ADAPTER (adapter)); - g_slist_foreach (adapter->buflist, (GFunc) gst_mini_object_unref, NULL); - g_slist_free (adapter->buflist); - adapter->buflist = NULL; - adapter->buflist_end = NULL; + if (adapter->info.memory) + gst_adapter_unmap (adapter); + + while ((obj = gst_queue_array_pop_head (adapter->bufqueue))) + gst_mini_object_unref (obj); + + adapter->count = 0; adapter->size = 0; adapter->skip = 0; adapter->assembled_len = 0; - adapter->priv->pts = GST_CLOCK_TIME_NONE; - adapter->priv->pts_distance = 0; - adapter->priv->dts = GST_CLOCK_TIME_NONE; - adapter->priv->dts_distance = 0; - adapter->priv->scan_offset = 0; - adapter->priv->scan_entry = NULL; + adapter->pts = GST_CLOCK_TIME_NONE; + 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->pts_at_discont = GST_CLOCK_TIME_NONE; + adapter->dts_at_discont = GST_CLOCK_TIME_NONE; + adapter->offset_at_discont = GST_BUFFER_OFFSET_NONE; + adapter->distance_from_discont = 0; + adapter->scan_offset = 0; + adapter->scan_entry_idx = G_MAXUINT; } 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)) { GST_LOG_OBJECT (adapter, "new pts %" GST_TIME_FORMAT, GST_TIME_ARGS (pts)); - adapter->priv->pts = pts; - adapter->priv->pts_distance = 0; + adapter->pts = pts; + adapter->pts_distance = 0; } dts = GST_BUFFER_DTS (buf); if (GST_CLOCK_TIME_IS_VALID (dts)) { GST_LOG_OBJECT (adapter, "new dts %" GST_TIME_FORMAT, GST_TIME_ARGS (dts)); - adapter->priv->dts = dts; - adapter->priv->dts_distance = 0; + adapter->dts = dts; + adapter->dts_distance = 0; + } + offset = GST_BUFFER_OFFSET (buf); + if (offset != GST_BUFFER_OFFSET_NONE) { + GST_LOG_OBJECT (adapter, "new offset %" G_GUINT64_FORMAT, offset); + adapter->offset = offset; + adapter->offset_distance = 0; + } + + if (GST_BUFFER_IS_DISCONT (buf)) { + /* Take values as-is (might be NONE) */ + adapter->pts_at_discont = pts; + adapter->dts_at_discont = dts; + adapter->offset_at_discont = offset; + adapter->distance_from_discont = 0; } } @@ -255,41 +322,43 @@ static void copy_into_unchecked (GstAdapter * adapter, guint8 * dest, gsize skip, gsize size) { - GSList *g; GstBuffer *buf; gsize bsize, csize; + guint idx = 0; /* first step, do skipping */ /* we might well be copying where we were scanning */ - if (adapter->priv->scan_entry && (adapter->priv->scan_offset <= skip)) { - g = adapter->priv->scan_entry; - skip -= adapter->priv->scan_offset; + if (adapter->scan_entry_idx != G_MAXUINT && (adapter->scan_offset <= skip)) { + idx = adapter->scan_entry_idx; + skip -= adapter->scan_offset; } else { - g = adapter->buflist; + idx = 0; } - buf = g->data; + buf = gst_queue_array_peek_nth (adapter->bufqueue, idx++); bsize = gst_buffer_get_size (buf); while (G_UNLIKELY (skip >= bsize)) { skip -= bsize; - g = g_slist_next (g); - buf = g->data; + buf = gst_queue_array_peek_nth (adapter->bufqueue, idx++); bsize = gst_buffer_get_size (buf); } /* copy partial buffer */ csize = MIN (bsize - skip, size); GST_DEBUG ("bsize %" G_GSIZE_FORMAT ", skip %" G_GSIZE_FORMAT ", csize %" G_GSIZE_FORMAT, bsize, skip, csize); + GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, adapter, "extract %" G_GSIZE_FORMAT + " bytes", csize); gst_buffer_extract (buf, skip, dest, csize); size -= csize; dest += csize; /* second step, copy remainder */ while (size > 0) { - g = g_slist_next (g); - buf = g->data; + buf = gst_queue_array_peek_nth (adapter->bufqueue, idx++); bsize = gst_buffer_get_size (buf); if (G_LIKELY (bsize > 0)) { csize = MIN (bsize, size); + GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, adapter, + "extract %" G_GSIZE_FORMAT " bytes", csize); gst_buffer_extract (buf, 0, dest, csize); size -= csize; dest += csize; @@ -317,24 +386,25 @@ gst_adapter_push (GstAdapter * adapter, GstBuffer * buf) adapter->size += size; /* Note: merging buffers at this point is premature. */ - if (G_UNLIKELY (adapter->buflist == NULL)) { - GST_LOG_OBJECT (adapter, "pushing first %" G_GSIZE_FORMAT " bytes", size); - adapter->buflist = adapter->buflist_end = g_slist_append (NULL, buf); - update_timestamps (adapter, buf); + if (gst_queue_array_is_empty (adapter->bufqueue)) { + GST_LOG_OBJECT (adapter, "pushing %p first %" G_GSIZE_FORMAT " bytes", + buf, size); + gst_queue_array_push_tail (adapter->bufqueue, buf); + update_timestamps_and_offset (adapter, buf); } else { /* Otherwise append to the end, and advance our end pointer */ - GST_LOG_OBJECT (adapter, "pushing %" G_GSIZE_FORMAT " bytes at end, " - "size now %" G_GSIZE_FORMAT, size, adapter->size); - adapter->buflist_end = g_slist_append (adapter->buflist_end, buf); - adapter->buflist_end = g_slist_next (adapter->buflist_end); + GST_LOG_OBJECT (adapter, "pushing %p %" G_GSIZE_FORMAT " bytes at end, " + "size now %" G_GSIZE_FORMAT, buf, size, adapter->size); + gst_queue_array_push_tail (adapter->bufqueue, buf); } + ++adapter->count; } +#if 0 /* Internal method only. Tries to merge buffers at the head of the queue - * to form a single larger buffer of size 'size'. Only merges buffers that - * where 'gst_buffer_is_span_fast' returns TRUE. + * to form a single larger buffer of size 'size'. * - * Returns TRUE if it managed to merge anything. + * Returns %TRUE if it managed to merge anything. */ static gboolean gst_adapter_try_to_merge_up (GstAdapter * adapter, gsize size) @@ -349,24 +419,26 @@ gst_adapter_try_to_merge_up (GstAdapter * adapter, gsize size) return FALSE; head = g->data; - g = g_slist_next (g); - /* How large do we want our head buffer? The requested size, plus whatever's - * been skipped already */ - size += adapter->skip; hsize = gst_buffer_get_size (head); + /* Remove skipped part from the buffer (otherwise the buffer might grow indefinitely) */ + head = gst_buffer_make_writable (head); + gst_buffer_resize (head, adapter->skip, hsize - adapter->skip); + hsize -= adapter->skip; + adapter->skip = 0; + g->data = head; + + g = g_slist_next (g); + while (g != NULL && hsize < size) { cur = g->data; - if (!gst_buffer_is_span_fast (head, cur)) - return ret; - /* Merge the head buffer and the next in line */ GST_LOG_OBJECT (adapter, "Merging buffers of size %" G_GSIZE_FORMAT " & %" G_GSIZE_FORMAT " in search of target %" G_GSIZE_FORMAT, hsize, gst_buffer_get_size (cur), size); - head = gst_buffer_join (head, cur); + head = gst_buffer_append (head, cur); hsize = gst_buffer_get_size (head); ret = TRUE; @@ -376,14 +448,15 @@ gst_adapter_try_to_merge_up (GstAdapter * adapter, gsize size) g->data = head; /* invalidate scan position */ - adapter->priv->scan_offset = 0; - adapter->priv->scan_entry = NULL; + adapter->scan_offset = 0; + adapter->scan_entry = NULL; g = g_slist_next (g); } return ret; } +#endif /** * gst_adapter_map: @@ -399,13 +472,13 @@ gst_adapter_try_to_merge_up (GstAdapter * adapter, gsize size) * of its chain function, the buffer will have an invalid data pointer after * your element flushes the bytes. In that case you should use * gst_adapter_take(), which returns a freshly-allocated buffer that you can set - * as #GstBuffer malloc_data or the potentially more performant + * as #GstBuffer memory or the potentially more performant * gst_adapter_take_buffer(). * - * Returns #NULL if @size bytes are not available. + * Returns %NULL if @size bytes are not available. * - * Returns: (transfer none) (array length=size): a pointer to the first - * @size bytes of data, or NULL + * Returns: (transfer none) (array length=size) (element-type guint8) (nullable): + * a pointer to the first @size bytes of data, or %NULL */ gconstpointer gst_adapter_map (GstAdapter * adapter, gsize size) @@ -418,6 +491,9 @@ gst_adapter_map (GstAdapter * adapter, gsize size) g_return_val_if_fail (GST_IS_ADAPTER (adapter), NULL); g_return_val_if_fail (size > 0, NULL); + if (adapter->info.memory) + gst_adapter_unmap (adapter); + /* we don't have enough data, return NULL. This is unlikely * as one usually does an _available() first instead of peeking a * random size. */ @@ -428,20 +504,24 @@ gst_adapter_map (GstAdapter * adapter, gsize size) if (adapter->assembled_len >= size) return adapter->assembled_data; +#if 0 do { - cur = adapter->buflist->data; +#endif + cur = gst_queue_array_peek_head (adapter->bufqueue); skip = adapter->skip; csize = gst_buffer_get_size (cur); if (csize >= size + skip) { - data = gst_buffer_map (cur, &csize, NULL, GST_MAP_READ); - adapter->priv->cdata = data; - adapter->priv->csize = csize; - return data + skip; + if (!gst_buffer_map (cur, &adapter->info, GST_MAP_READ)) + return FALSE; + + return (guint8 *) adapter->info.data + skip; } /* We may be able to efficiently merge buffers in our pool to * gather a big enough chunk to return it from the head buffer directly */ +#if 0 } while (gst_adapter_try_to_merge_up (adapter, size)); +#endif /* see how much data we can reuse from the assembled memory and how much * we need to copy */ @@ -487,28 +567,28 @@ gst_adapter_unmap (GstAdapter * adapter) { g_return_if_fail (GST_IS_ADAPTER (adapter)); - if (adapter->priv->cdata) { - GstBuffer *cur = adapter->buflist->data; - gst_buffer_unmap (cur, adapter->priv->cdata, adapter->priv->csize); - adapter->priv->cdata = NULL; + if (adapter->info.memory) { + GstBuffer *cur = gst_queue_array_peek_head (adapter->bufqueue); + GST_LOG_OBJECT (adapter, "unmap memory buffer %p", cur); + gst_buffer_unmap (cur, &adapter->info); + adapter->info.memory = NULL; } } /** - * gst_adapter_copy: + * gst_adapter_copy: (skip) * @adapter: a #GstAdapter - * @dest: (out caller-allocates) (array length=size): the memory to copy into + * @dest: (out caller-allocates) (array length=size) (element-type guint8): + * the memory to copy into * @offset: the bytes offset in the adapter to start from * @size: the number of bytes to copy * * Copies @size bytes of data starting at @offset out of the buffers - * contained in @GstAdapter into an array @dest provided by the caller. + * contained in #GstAdapter into an array @dest provided by the caller. * * The array @dest should be large enough to contain @size bytes. * The user should check that the adapter has (@offset + @size) bytes * available before calling this function. - * - * Since: 0.10.12 */ void gst_adapter_copy (GstAdapter * adapter, gpointer dest, gsize offset, gsize size) @@ -521,26 +601,40 @@ gst_adapter_copy (GstAdapter * adapter, gpointer dest, gsize offset, gsize size) } /** - * gst_adapter_flush: + * gst_adapter_copy_bytes: (rename-to gst_adapter_copy) * @adapter: a #GstAdapter - * @flush: the number of bytes to flush + * @offset: the bytes offset in the adapter to start from + * @size: the number of bytes to copy * - * Flushes the first @flush bytes in the @adapter. The caller must ensure that - * at least this many bytes are available. + * Similar to gst_adapter_copy, but more suitable for language bindings. @size + * bytes of data starting at @offset will be copied out of the buffers contained + * in @adapter and into a new #GBytes structure which is returned. Depending on + * the value of the @size argument an empty #GBytes structure may be returned. * - * See also: gst_adapter_map(), gst_adapter_unmap() + * Returns: (transfer full): A new #GBytes structure containing the copied data. + * + * Since: 1.4 */ +GBytes * +gst_adapter_copy_bytes (GstAdapter * adapter, gsize offset, gsize size) +{ + gpointer data; + data = g_malloc (size); + gst_adapter_copy (adapter, data, offset, size); + return g_bytes_new_take (data, size); +} + +/*Flushes the first @flush bytes in the @adapter*/ static void gst_adapter_flush_unchecked (GstAdapter * adapter, gsize flush) { GstBuffer *cur; gsize size; - GstAdapterPrivate *priv; - GSList *g; GST_LOG_OBJECT (adapter, "flushing %" G_GSIZE_FORMAT " bytes", flush); - priv = adapter->priv; + if (adapter->info.memory) + gst_adapter_unmap (adapter); /* clear state */ adapter->size -= flush; @@ -549,42 +643,57 @@ gst_adapter_flush_unchecked (GstAdapter * adapter, gsize flush) /* take skip into account */ flush += adapter->skip; /* distance is always at least the amount of skipped bytes */ - priv->pts_distance -= adapter->skip; - priv->dts_distance -= adapter->skip; + adapter->pts_distance -= adapter->skip; + adapter->dts_distance -= adapter->skip; + adapter->offset_distance -= adapter->skip; + adapter->distance_from_discont -= adapter->skip; - g = adapter->buflist; - cur = g->data; + cur = gst_queue_array_peek_head (adapter->bufqueue); size = gst_buffer_get_size (cur); while (flush >= size) { /* can skip whole buffer */ GST_LOG_OBJECT (adapter, "flushing out head buffer"); - priv->pts_distance += size; - priv->dts_distance += size; + adapter->pts_distance += size; + adapter->dts_distance += size; + adapter->offset_distance += size; + adapter->distance_from_discont += size; flush -= size; - gst_buffer_unref (cur); - g = g_slist_delete_link (g, g); + --adapter->count; - if (G_UNLIKELY (g == NULL)) { + cur = NULL; + gst_buffer_unref (gst_queue_array_pop_head (adapter->bufqueue)); + + if (gst_queue_array_is_empty (adapter->bufqueue)) { GST_LOG_OBJECT (adapter, "adapter empty now"); - adapter->buflist_end = NULL; break; } /* there is a new head buffer, update the timestamps */ - cur = g->data; - update_timestamps (adapter, cur); + cur = gst_queue_array_peek_head (adapter->bufqueue); + update_timestamps_and_offset (adapter, cur); size = gst_buffer_get_size (cur); } - adapter->buflist = g; /* account for the remaining bytes */ adapter->skip = flush; - adapter->priv->pts_distance += flush; - adapter->priv->dts_distance += flush; + adapter->pts_distance += flush; + adapter->dts_distance += flush; + adapter->offset_distance += flush; + adapter->distance_from_discont += flush; /* invalidate scan position */ - priv->scan_offset = 0; - priv->scan_entry = NULL; + adapter->scan_offset = 0; + adapter->scan_entry_idx = G_MAXUINT; } +/** + * gst_adapter_flush: + * @adapter: a #GstAdapter + * @flush: the number of bytes to flush + * + * Flushes the first @flush bytes in the @adapter. The caller must ensure that + * at least this many bytes are available. + * + * See also: gst_adapter_map(), gst_adapter_unmap() + */ void gst_adapter_flush (GstAdapter * adapter, gsize flush) { @@ -598,9 +707,9 @@ gst_adapter_flush (GstAdapter * adapter, gsize flush) gst_adapter_flush_unchecked (adapter, flush); } -/* internal function, nbytes should be flushed after calling this function */ +/* internal function, nbytes should be flushed if needed after calling this function */ static guint8 * -gst_adapter_take_internal (GstAdapter * adapter, gsize nbytes) +gst_adapter_get_internal (GstAdapter * adapter, gsize nbytes) { guint8 *data; gsize toreuse, tocopy; @@ -628,6 +737,8 @@ gst_adapter_take_internal (GstAdapter * adapter, gsize nbytes) /* reuse what we can from the already assembled data */ if (toreuse) { GST_LOG_OBJECT (adapter, "reusing %" G_GSIZE_FORMAT " bytes", toreuse); + GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, adapter, + "memcpy %" G_GSIZE_FORMAT " bytes", toreuse); memcpy (data, adapter->assembled_data, toreuse); } } @@ -651,8 +762,8 @@ gst_adapter_take_internal (GstAdapter * adapter, gsize nbytes) * * Free-function: g_free * - * Returns: (transfer full) (array length=nbytes): oven-fresh hot data, or - * #NULL if @nbytes bytes are not available + * Returns: (transfer full) (array length=nbytes) (element-type guint8) (nullable): + * oven-fresh hot data, or %NULL if @nbytes bytes are not available */ gpointer gst_adapter_take (GstAdapter * adapter, gsize nbytes) @@ -668,7 +779,7 @@ gst_adapter_take (GstAdapter * adapter, gsize nbytes) if (G_UNLIKELY (nbytes > adapter->size)) return NULL; - data = gst_adapter_take_internal (adapter, nbytes); + data = gst_adapter_get_internal (adapter, nbytes); gst_adapter_flush_unchecked (adapter, nbytes); @@ -676,27 +787,180 @@ gst_adapter_take (GstAdapter * adapter, gsize nbytes) } /** - * gst_adapter_take_buffer: - * @adapter: a #GstAdapter + * gst_adapter_get_buffer_fast: + * @adapter: a #GstAdapter + * @nbytes: the number of bytes to get + * + * Returns a #GstBuffer containing the first @nbytes of the @adapter, but + * does not flush them from the adapter. See gst_adapter_take_buffer_fast() + * for details. + * + * Caller owns a reference to the returned buffer. gst_buffer_unref() after + * usage. + * + * Free-function: gst_buffer_unref + * + * Returns: (transfer full) (nullable): a #GstBuffer containing the first + * @nbytes of the adapter, or %NULL if @nbytes bytes are not available. + * gst_buffer_unref() when no longer needed. + * + * Since: 1.6 + */ +GstBuffer * +gst_adapter_get_buffer_fast (GstAdapter * adapter, gsize nbytes) +{ + GstBuffer *buffer = NULL; + GstBuffer *cur; + gsize skip; + gsize left = nbytes; + guint idx, len; + + g_return_val_if_fail (GST_IS_ADAPTER (adapter), NULL); + g_return_val_if_fail (nbytes > 0, NULL); + + GST_LOG_OBJECT (adapter, "getting buffer of %" G_GSIZE_FORMAT " bytes", + nbytes); + + /* we don't have enough data, return NULL. This is unlikely + * as one usually does an _available() first instead of grabbing a + * random size. */ + if (G_UNLIKELY (nbytes > adapter->size)) + return NULL; + + skip = adapter->skip; + cur = gst_queue_array_peek_head (adapter->bufqueue); + + if (skip == 0 && gst_buffer_get_size (cur) == nbytes) { + GST_LOG_OBJECT (adapter, "providing buffer of %" G_GSIZE_FORMAT " bytes" + " as head buffer", nbytes); + buffer = gst_buffer_ref (cur); + goto done; + } + + len = gst_queue_array_get_length (adapter->bufqueue); + + for (idx = 0; idx < len && left > 0; idx++) { + gsize size, cur_size; + + cur = gst_queue_array_peek_nth (adapter->bufqueue, idx); + cur_size = gst_buffer_get_size (cur); + size = MIN (cur_size - skip, left); + + GST_LOG_OBJECT (adapter, "appending %" G_GSIZE_FORMAT " bytes" + " via region copy", size); + if (buffer) + gst_buffer_copy_into (buffer, cur, + GST_BUFFER_COPY_MEMORY | GST_BUFFER_COPY_META, skip, size); + else + buffer = gst_buffer_copy_region (cur, GST_BUFFER_COPY_ALL, skip, size); + skip = 0; + left -= size; + } + +done: + + return buffer; +} + +/** + * gst_adapter_take_buffer_fast: + * @adapter: a #GstAdapter * @nbytes: the number of bytes to take * - * Returns a #GstBuffer containing the first @nbytes bytes of the - * @adapter. The returned bytes will be flushed from the adapter. - * This function is potentially more performant than gst_adapter_take() - * since it can reuse the memory in pushed buffers by subbuffering - * or merging. + * Returns a #GstBuffer containing the first @nbytes of the @adapter. + * The returned bytes will be flushed from the adapter. This function + * is potentially more performant than gst_adapter_take_buffer() since + * it can reuse the memory in pushed buffers by subbuffering or + * merging. Unlike gst_adapter_take_buffer(), the returned buffer may + * be composed of multiple non-contiguous #GstMemory objects, no + * copies are made. + * + * Note that no assumptions should be made as to whether certain buffer + * flags such as the DISCONT flag are set on the returned buffer, or not. + * The caller needs to explicitly set or unset flags that should be set or + * unset. + * + * This will also copy over all GstMeta of the input buffers except + * for meta with the %GST_META_FLAG_POOLED flag or with the "memory" tag. * - * Caller owns returned value. gst_buffer_unref() after usage. + * This function can return buffer up to the return value of + * gst_adapter_available() without making copies if possible. + * + * Caller owns a reference to the returned buffer. gst_buffer_unref() after + * usage. * * Free-function: gst_buffer_unref * - * Returns: (transfer full): a #GstBuffer containing the first @nbytes of - * the adapter, or #NULL if @nbytes bytes are not available + * Returns: (transfer full) (nullable): a #GstBuffer containing the first + * @nbytes of the adapter, or %NULL if @nbytes bytes are not available. + * gst_buffer_unref() when no longer needed. * - * Since: 0.10.6 + * Since: 1.2 */ GstBuffer * -gst_adapter_take_buffer (GstAdapter * adapter, gsize nbytes) +gst_adapter_take_buffer_fast (GstAdapter * adapter, gsize nbytes) +{ + GstBuffer *buffer; + + g_return_val_if_fail (GST_IS_ADAPTER (adapter), NULL); + g_return_val_if_fail (nbytes > 0, NULL); + + buffer = gst_adapter_get_buffer_fast (adapter, nbytes); + if (buffer) + gst_adapter_flush_unchecked (adapter, nbytes); + + return buffer; +} + +static gboolean +foreach_metadata (GstBuffer * inbuf, GstMeta ** meta, gpointer user_data) +{ + GstBuffer *outbuf = user_data; + const GstMetaInfo *info = (*meta)->info; + gboolean do_copy = FALSE; + + if (gst_meta_api_type_has_tag (info->api, _gst_meta_tag_memory)) { + /* never call the transform_meta with memory specific metadata */ + GST_DEBUG ("not copying memory specific metadata %s", + g_type_name (info->api)); + do_copy = FALSE; + } else { + do_copy = TRUE; + GST_DEBUG ("copying metadata %s", g_type_name (info->api)); + } + + if (do_copy && info->transform_func) { + GstMetaTransformCopy copy_data = { FALSE, 0, -1 }; + GST_DEBUG ("copy metadata %s", g_type_name (info->api)); + /* simply copy then */ + info->transform_func (outbuf, *meta, inbuf, + _gst_meta_transform_copy, ©_data); + } + return TRUE; +} + +/** + * gst_adapter_get_buffer: + * @adapter: a #GstAdapter + * @nbytes: the number of bytes to get + * + * Returns a #GstBuffer containing the first @nbytes of the @adapter, but + * does not flush them from the adapter. See gst_adapter_take_buffer() + * for details. + * + * Caller owns a reference to the returned buffer. gst_buffer_unref() after + * usage. + * + * Free-function: gst_buffer_unref + * + * Returns: (transfer full) (nullable): a #GstBuffer containing the first + * @nbytes of the adapter, or %NULL if @nbytes bytes are not available. + * gst_buffer_unref() when no longer needed. + * + * Since: 1.6 + */ +GstBuffer * +gst_adapter_get_buffer (GstAdapter * adapter, gsize nbytes) { GstBuffer *buffer; GstBuffer *cur; @@ -706,7 +970,7 @@ gst_adapter_take_buffer (GstAdapter * adapter, gsize nbytes) g_return_val_if_fail (GST_IS_ADAPTER (adapter), NULL); g_return_val_if_fail (nbytes > 0, NULL); - GST_LOG_OBJECT (adapter, "taking buffer of %" G_GSIZE_FORMAT " bytes", + GST_LOG_OBJECT (adapter, "getting buffer of %" G_GSIZE_FORMAT " bytes", nbytes); /* we don't have enough data, return NULL. This is unlikely @@ -715,7 +979,7 @@ gst_adapter_take_buffer (GstAdapter * adapter, gsize nbytes) if (G_UNLIKELY (nbytes > adapter->size)) return NULL; - cur = adapter->buflist->data; + cur = gst_queue_array_peek_head (adapter->bufqueue); skip = adapter->skip; hsize = gst_buffer_get_size (cur); @@ -731,10 +995,11 @@ gst_adapter_take_buffer (GstAdapter * adapter, gsize nbytes) buffer = gst_buffer_copy_region (cur, GST_BUFFER_COPY_ALL, skip, nbytes); goto done; } - +#if 0 if (gst_adapter_try_to_merge_up (adapter, nbytes)) { /* Merged something, let's try again for sub-buffering */ cur = adapter->buflist->data; + skip = adapter->skip; if (gst_buffer_get_size (cur) >= nbytes + skip) { GST_LOG_OBJECT (adapter, "providing buffer of %" G_GSIZE_FORMAT " bytes" " via sub-buffer", nbytes); @@ -742,15 +1007,75 @@ gst_adapter_take_buffer (GstAdapter * adapter, gsize nbytes) goto done; } } +#endif + + data = gst_adapter_get_internal (adapter, nbytes); + + buffer = gst_buffer_new_wrapped (data, nbytes); - data = gst_adapter_take_internal (adapter, nbytes); + { + guint idx, len; + GstBuffer *cur; + gsize read_offset = 0; - buffer = gst_buffer_new (); - gst_buffer_take_memory (buffer, -1, - gst_memory_new_wrapped (0, data, g_free, nbytes, 0, nbytes)); + idx = 0; + len = gst_queue_array_get_length (adapter->bufqueue); + + while (idx < len && read_offset < nbytes + adapter->skip) { + cur = gst_queue_array_peek_nth (adapter->bufqueue, idx); + + gst_buffer_foreach_meta (cur, foreach_metadata, buffer); + read_offset += gst_buffer_get_size (cur); + + idx++; + } + } done: - gst_adapter_flush_unchecked (adapter, nbytes); + + return buffer; +} + +/** + * gst_adapter_take_buffer: + * @adapter: a #GstAdapter + * @nbytes: the number of bytes to take + * + * Returns a #GstBuffer containing the first @nbytes bytes of the + * @adapter. The returned bytes will be flushed from the adapter. + * This function is potentially more performant than + * gst_adapter_take() since it can reuse the memory in pushed buffers + * by subbuffering or merging. This function will always return a + * buffer with a single memory region. + * + * Note that no assumptions should be made as to whether certain buffer + * flags such as the DISCONT flag are set on the returned buffer, or not. + * The caller needs to explicitly set or unset flags that should be set or + * unset. + * + * Since 1.6 this will also copy over all GstMeta of the input buffers except + * for meta with the %GST_META_FLAG_POOLED flag or with the "memory" tag. + * + * Caller owns a reference to the returned buffer. gst_buffer_unref() after + * usage. + * + * Free-function: gst_buffer_unref + * + * Returns: (transfer full) (nullable): a #GstBuffer containing the first + * @nbytes of the adapter, or %NULL if @nbytes bytes are not available. + * gst_buffer_unref() when no longer needed. + */ +GstBuffer * +gst_adapter_take_buffer (GstAdapter * adapter, gsize nbytes) +{ + GstBuffer *buffer; + + g_return_val_if_fail (GST_IS_ADAPTER (adapter), NULL); + g_return_val_if_fail (nbytes > 0, NULL); + + buffer = gst_adapter_get_buffer (adapter, nbytes); + if (buffer) + gst_adapter_flush_unchecked (adapter, nbytes); return buffer; } @@ -768,18 +1093,16 @@ done: * Caller owns returned list and contained buffers. gst_buffer_unref() each * buffer in the list before freeing the list after usage. * - * Returns: (element-type Gst.Buffer) (transfer full): a #GList of buffers - * containing the first @nbytes of the adapter, or #NULL if @nbytes bytes - * are not available - * - * Since: 0.10.31 + * Returns: (element-type Gst.Buffer) (transfer full) (nullable): a #GList of + * buffers containing the first @nbytes of the adapter, or %NULL if @nbytes + * bytes are not available */ GList * gst_adapter_take_list (GstAdapter * adapter, gsize nbytes) { GQueue queue = G_QUEUE_INIT; GstBuffer *cur; - gsize hsize, skip; + gsize hsize, skip, cur_size; g_return_val_if_fail (GST_IS_ADAPTER (adapter), NULL); g_return_val_if_fail (nbytes <= adapter->size, NULL); @@ -787,9 +1110,10 @@ gst_adapter_take_list (GstAdapter * adapter, gsize nbytes) GST_LOG_OBJECT (adapter, "taking %" G_GSIZE_FORMAT " bytes", nbytes); while (nbytes > 0) { - cur = adapter->buflist->data; + cur = gst_queue_array_peek_head (adapter->bufqueue); skip = adapter->skip; - hsize = MIN (nbytes, gst_buffer_get_size (cur) - skip); + cur_size = gst_buffer_get_size (cur); + hsize = MIN (nbytes, cur_size - skip); cur = gst_adapter_take_buffer (adapter, hsize); @@ -801,12 +1125,193 @@ gst_adapter_take_list (GstAdapter * adapter, gsize nbytes) } /** + * gst_adapter_get_list: + * @adapter: a #GstAdapter + * @nbytes: the number of bytes to get + * + * Returns a #GList of buffers containing the first @nbytes bytes of the + * @adapter, but does not flush them from the adapter. See + * gst_adapter_take_list() for details. + * + * Caller owns returned list and contained buffers. gst_buffer_unref() each + * buffer in the list before freeing the list after usage. + * + * Returns: (element-type Gst.Buffer) (transfer full) (nullable): a #GList of + * buffers containing the first @nbytes of the adapter, or %NULL if @nbytes + * bytes are not available + * + * Since: 1.6 + */ +GList * +gst_adapter_get_list (GstAdapter * adapter, gsize nbytes) +{ + GQueue queue = G_QUEUE_INIT; + GstBuffer *cur, *buffer; + gsize hsize, skip, cur_size; + guint idx; + + g_return_val_if_fail (GST_IS_ADAPTER (adapter), NULL); + g_return_val_if_fail (nbytes <= adapter->size, NULL); + + GST_LOG_OBJECT (adapter, "getting %" G_GSIZE_FORMAT " bytes", nbytes); + + idx = 0; + skip = adapter->skip; + + while (nbytes > 0) { + cur = gst_queue_array_peek_nth (adapter->bufqueue, idx++); + cur_size = gst_buffer_get_size (cur); + hsize = MIN (nbytes, cur_size - skip); + + if (skip == 0 && cur_size == hsize) { + GST_LOG_OBJECT (adapter, + "inserting a buffer of %" G_GSIZE_FORMAT " bytes", hsize); + buffer = gst_buffer_ref (cur); + } else { + GST_LOG_OBJECT (adapter, "inserting a buffer of %" G_GSIZE_FORMAT " bytes" + " via region copy", hsize); + buffer = gst_buffer_copy_region (cur, GST_BUFFER_COPY_ALL, skip, hsize); + } + + g_queue_push_tail (&queue, buffer); + + nbytes -= hsize; + skip = 0; + } + + return queue.head; +} + +/** + * gst_adapter_take_buffer_list: + * @adapter: a #GstAdapter + * @nbytes: the number of bytes to take + * + * Returns a #GstBufferList of buffers containing the first @nbytes bytes of + * the @adapter. The returned bytes will be flushed from the adapter. + * When the caller can deal with individual buffers, this function is more + * performant because no memory should be copied. + * + * Caller owns the returned list. Call gst_buffer_list_unref() to free + * the list after usage. + * + * Returns: (transfer full) (nullable): a #GstBufferList of buffers containing + * the first @nbytes of the adapter, or %NULL if @nbytes bytes are not + * available + * + * Since: 1.6 + */ +GstBufferList * +gst_adapter_take_buffer_list (GstAdapter * adapter, gsize nbytes) +{ + GstBufferList *buffer_list; + GstBuffer *cur; + gsize hsize, skip, cur_size; + guint n_bufs; + + g_return_val_if_fail (GST_IS_ADAPTER (adapter), NULL); + + if (nbytes > adapter->size) + return NULL; + + GST_LOG_OBJECT (adapter, "taking %" G_GSIZE_FORMAT " bytes", nbytes); + + /* try to create buffer list with sufficient size, so no resize is done later */ + if (adapter->count < 64) + n_bufs = adapter->count; + else + n_bufs = (adapter->count * nbytes * 1.2 / adapter->size) + 1; + + buffer_list = gst_buffer_list_new_sized (n_bufs); + + while (nbytes > 0) { + cur = gst_queue_array_peek_head (adapter->bufqueue); + skip = adapter->skip; + cur_size = gst_buffer_get_size (cur); + hsize = MIN (nbytes, cur_size - skip); + + gst_buffer_list_add (buffer_list, gst_adapter_take_buffer (adapter, hsize)); + nbytes -= hsize; + } + return buffer_list; +} + +/** + * gst_adapter_get_buffer_list: + * @adapter: a #GstAdapter + * @nbytes: the number of bytes to get + * + * Returns a #GstBufferList of buffers containing the first @nbytes bytes of + * the @adapter but does not flush them from the adapter. See + * gst_adapter_take_buffer_list() for details. + * + * Caller owns the returned list. Call gst_buffer_list_unref() to free + * the list after usage. + * + * Returns: (transfer full) (nullable): a #GstBufferList of buffers containing + * the first @nbytes of the adapter, or %NULL if @nbytes bytes are not + * available + * + * Since: 1.6 + */ +GstBufferList * +gst_adapter_get_buffer_list (GstAdapter * adapter, gsize nbytes) +{ + GstBufferList *buffer_list; + GstBuffer *cur, *buffer; + gsize hsize, skip, cur_size; + guint n_bufs; + guint idx; + + g_return_val_if_fail (GST_IS_ADAPTER (adapter), NULL); + + if (nbytes > adapter->size) + return NULL; + + GST_LOG_OBJECT (adapter, "getting %" G_GSIZE_FORMAT " bytes", nbytes); + + /* try to create buffer list with sufficient size, so no resize is done later */ + if (adapter->count < 64) + n_bufs = adapter->count; + else + n_bufs = (adapter->count * nbytes * 1.2 / adapter->size) + 1; + + buffer_list = gst_buffer_list_new_sized (n_bufs); + + idx = 0; + skip = adapter->skip; + + while (nbytes > 0) { + cur = gst_queue_array_peek_nth (adapter->bufqueue, idx++); + cur_size = gst_buffer_get_size (cur); + hsize = MIN (nbytes, cur_size - skip); + + if (skip == 0 && cur_size == hsize) { + GST_LOG_OBJECT (adapter, + "inserting a buffer of %" G_GSIZE_FORMAT " bytes", hsize); + buffer = gst_buffer_ref (cur); + } else { + GST_LOG_OBJECT (adapter, "inserting a buffer of %" G_GSIZE_FORMAT " bytes" + " via region copy", hsize); + buffer = gst_buffer_copy_region (cur, GST_BUFFER_COPY_ALL, skip, hsize); + } + + gst_buffer_list_add (buffer_list, buffer); + + nbytes -= hsize; + skip = 0; + } + + return buffer_list; +} + +/** * gst_adapter_available: * @adapter: a #GstAdapter * * Gets the maximum amount of bytes available, that is it returns the maximum * value that can be supplied to gst_adapter_map() without that function - * returning NULL. + * returning %NULL. * * Returns: number of bytes available in @adapter */ @@ -834,7 +1339,7 @@ gst_adapter_available_fast (GstAdapter * adapter) { GstBuffer *cur; gsize size; - GSList *g; + guint idx; g_return_val_if_fail (GST_IS_ADAPTER (adapter), 0); @@ -847,13 +1352,12 @@ gst_adapter_available_fast (GstAdapter * adapter) return adapter->assembled_len; /* take the first non-zero buffer */ - g = adapter->buflist; + idx = 0; while (TRUE) { - cur = g->data; + cur = gst_queue_array_peek_nth (adapter->bufqueue, idx++); size = gst_buffer_get_size (cur); if (size != 0) break; - g = g_slist_next (g); } /* we can quickly get the (remaining) data of the first buffer */ @@ -861,9 +1365,116 @@ gst_adapter_available_fast (GstAdapter * adapter) } /** + * gst_adapter_get_distance_from_discont: + * @adapter: a #GstAdapter + * + * Get the distance in bytes since the last buffer with the + * %GST_BUFFER_FLAG_DISCONT flag. + * + * The distance will be reset to 0 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_distance_from_discont (GstAdapter * adapter) +{ + return adapter->distance_from_discont; +} + +/** + * gst_adapter_offset_at_discont: + * @adapter: a #GstAdapter + * + * Get the offset that was on the last buffer with the GST_BUFFER_FLAG_DISCONT + * flag, or GST_BUFFER_OFFSET_NONE. + * + * Since: 1.10 + * + * Returns: The offset at the last discont or GST_BUFFER_OFFSET_NONE. + */ +guint64 +gst_adapter_offset_at_discont (GstAdapter * adapter) +{ + g_return_val_if_fail (GST_IS_ADAPTER (adapter), GST_BUFFER_OFFSET_NONE); + + return adapter->offset_at_discont; +} + +/** + * gst_adapter_pts_at_discont: + * @adapter: a #GstAdapter + * + * Get the PTS that was on the last buffer with the GST_BUFFER_FLAG_DISCONT + * flag, or GST_CLOCK_TIME_NONE. + * + * Since: 1.10 + * + * Returns: The PTS at the last discont or GST_CLOCK_TIME_NONE. + */ +GstClockTime +gst_adapter_pts_at_discont (GstAdapter * adapter) +{ + g_return_val_if_fail (GST_IS_ADAPTER (adapter), GST_CLOCK_TIME_NONE); + + return adapter->pts_at_discont; +} + +/** + * gst_adapter_dts_at_discont: + * @adapter: a #GstAdapter + * + * Get the DTS that was on the last buffer with the GST_BUFFER_FLAG_DISCONT + * flag, or GST_CLOCK_TIME_NONE. + * + * Since: 1.10 + * + * Returns: The DTS at the last discont or GST_CLOCK_TIME_NONE. + */ +GstClockTime +gst_adapter_dts_at_discont (GstAdapter * adapter) +{ + g_return_val_if_fail (GST_IS_ADAPTER (adapter), GST_CLOCK_TIME_NONE); + + return adapter->dts_at_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 + * @distance: (out) (allow-none): pointer to location for distance, or %NULL * * Get the pts that was before the current byte in the adapter. When * @distance is given, the amount of bytes between the pts and the current @@ -882,15 +1493,15 @@ gst_adapter_prev_pts (GstAdapter * adapter, guint64 * distance) g_return_val_if_fail (GST_IS_ADAPTER (adapter), GST_CLOCK_TIME_NONE); if (distance) - *distance = adapter->priv->pts_distance; + *distance = adapter->pts_distance; - return adapter->priv->pts; + return adapter->pts; } /** * gst_adapter_prev_dts: * @adapter: a #GstAdapter - * @distance: (out) (allow-none): pointer to location for distance, or NULL + * @distance: (out) (allow-none): pointer to location for distance, or %NULL * * Get the dts that was before the current byte in the adapter. When * @distance is given, the amount of bytes between the dts and the current @@ -909,9 +1520,109 @@ gst_adapter_prev_dts (GstAdapter * adapter, guint64 * distance) g_return_val_if_fail (GST_IS_ADAPTER (adapter), GST_CLOCK_TIME_NONE); if (distance) - *distance = adapter->priv->dts_distance; + *distance = adapter->dts_distance; + + return adapter->dts; +} + +/** + * gst_adapter_prev_pts_at_offset: + * @adapter: a #GstAdapter + * @offset: the offset in the adapter at which to get timestamp + * @distance: (out) (allow-none): pointer to location for distance, or %NULL + * + * Get the pts that was before the byte at offset @offset in the adapter. When + * @distance is given, the amount of bytes between the pts and the current + * position is returned. + * + * The pts is reset to GST_CLOCK_TIME_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 a pts is removed from the adapter, the pts + * and distance returned are GST_CLOCK_TIME_NONE and 0 respectively. + * + * Since: 1.2 + * Returns: The previously seen pts at given offset. + */ +GstClockTime +gst_adapter_prev_pts_at_offset (GstAdapter * adapter, gsize offset, + guint64 * distance) +{ + GstBuffer *cur; + gsize read_offset = 0; + gsize pts_offset = 0; + GstClockTime pts = adapter->pts; + guint idx, len; + + g_return_val_if_fail (GST_IS_ADAPTER (adapter), GST_CLOCK_TIME_NONE); + + idx = 0; + len = gst_queue_array_get_length (adapter->bufqueue); + + while (idx < len && read_offset < offset + adapter->skip) { + cur = gst_queue_array_peek_nth (adapter->bufqueue, idx++); + + if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_PTS (cur))) { + pts = GST_BUFFER_PTS (cur); + pts_offset = read_offset; + } + + read_offset += gst_buffer_get_size (cur); + } + + if (distance) + *distance = adapter->pts_distance + offset - pts_offset; - return adapter->priv->dts; + return pts; +} + +/** + * gst_adapter_prev_dts_at_offset: + * @adapter: a #GstAdapter + * @offset: the offset in the adapter at which to get timestamp + * @distance: (out) (allow-none): pointer to location for distance, or %NULL + * + * Get the dts that was before the byte at offset @offset in the adapter. When + * @distance is given, the amount of bytes between the dts and the current + * position is returned. + * + * The dts is reset to GST_CLOCK_TIME_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 a dts is removed from the adapter, the dts + * and distance returned are GST_CLOCK_TIME_NONE and 0 respectively. + * + * Since: 1.2 + * Returns: The previously seen dts at given offset. + */ +GstClockTime +gst_adapter_prev_dts_at_offset (GstAdapter * adapter, gsize offset, + guint64 * distance) +{ + GstBuffer *cur; + gsize read_offset = 0; + gsize dts_offset = 0; + GstClockTime dts = adapter->dts; + guint idx, len; + + g_return_val_if_fail (GST_IS_ADAPTER (adapter), GST_CLOCK_TIME_NONE); + + idx = 0; + len = gst_queue_array_get_length (adapter->bufqueue); + + while (idx < len && read_offset < offset + adapter->skip) { + cur = gst_queue_array_peek_nth (adapter->bufqueue, idx++); + + if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_DTS (cur))) { + dts = GST_BUFFER_DTS (cur); + dts_offset = read_offset; + } + + read_offset += gst_buffer_get_size (cur); + } + + if (distance) + *distance = adapter->dts_distance + offset - dts_offset; + + return dts; } /** @@ -922,7 +1633,7 @@ gst_adapter_prev_dts (GstAdapter * adapter, guint64 * distance) * @offset: offset into the adapter data from which to start scanning, returns * the last scanned position. * @size: number of bytes to scan from offset - * @value: pointer to uint32 to return matching data + * @value: (out) (allow-none): pointer to uint32 to return matching data * * Scan for pattern @pattern with applied mask @mask in the adapter data, * starting from offset @offset. If a match is found, the value that matched @@ -936,18 +1647,17 @@ gst_adapter_prev_dts (GstAdapter * adapter, guint64 * distance) * enough data (offset+size bytes) in the adapter. * * Returns: offset of the first match, or -1 if no match was found. - * - * Since: 0.10.30 */ -gsize +gssize gst_adapter_masked_scan_uint32_peek (GstAdapter * adapter, guint32 mask, guint32 pattern, gsize offset, gsize size, guint32 * value) { - GSList *g; - gsize skip, bsize, osize, i; + gsize skip, bsize, i; guint32 state; - guint8 *bdata, *odata; + GstMapInfo info; + guint8 *bdata; GstBuffer *buf; + guint idx; g_return_val_if_fail (size > 0, -1); g_return_val_if_fail (offset + size <= adapter->size, -1); @@ -961,29 +1671,29 @@ gst_adapter_masked_scan_uint32_peek (GstAdapter * adapter, guint32 mask, /* first step, do skipping and position on the first buffer */ /* optimistically assume scanning continues sequentially */ - if (adapter->priv->scan_entry && (adapter->priv->scan_offset <= skip)) { - g = adapter->priv->scan_entry; - skip -= adapter->priv->scan_offset; + if (adapter->scan_entry_idx != G_MAXUINT && (adapter->scan_offset <= skip)) { + idx = adapter->scan_entry_idx; + skip -= adapter->scan_offset; } else { - g = adapter->buflist; - adapter->priv->scan_offset = 0; - adapter->priv->scan_entry = NULL; + idx = 0; + adapter->scan_offset = 0; + adapter->scan_entry_idx = G_MAXUINT; } - buf = g->data; + buf = gst_queue_array_peek_nth (adapter->bufqueue, idx++); bsize = gst_buffer_get_size (buf); while (G_UNLIKELY (skip >= bsize)) { skip -= bsize; - g = g_slist_next (g); - adapter->priv->scan_offset += bsize; - adapter->priv->scan_entry = g; - buf = g->data; + adapter->scan_offset += bsize; + adapter->scan_entry_idx = idx; + buf = gst_queue_array_peek_nth (adapter->bufqueue, idx++); bsize = gst_buffer_get_size (buf); } /* get the data now */ - odata = gst_buffer_map (buf, &osize, NULL, GST_MAP_READ); + if (!gst_buffer_map (buf, &info, GST_MAP_READ)) + return -1; - bdata = odata + skip; - bsize = osize - skip; + bdata = (guint8 *) info.data + skip; + bsize = info.size - skip; skip = 0; /* set the state to something that does not match */ @@ -1000,7 +1710,7 @@ gst_adapter_masked_scan_uint32_peek (GstAdapter * adapter, guint32 mask, if (G_LIKELY (skip + i >= 3)) { if (G_LIKELY (value)) *value = state; - gst_buffer_unmap (buf, odata, osize); + gst_buffer_unmap (buf, &info); return offset + skip + i - 3; } } @@ -1011,18 +1721,19 @@ gst_adapter_masked_scan_uint32_peek (GstAdapter * adapter, guint32 mask, /* nothing found yet, go to next buffer */ skip += bsize; - g = g_slist_next (g); - adapter->priv->scan_offset += osize; - adapter->priv->scan_entry = g; - gst_buffer_unmap (buf, odata, osize); - buf = g->data; - - odata = gst_buffer_map (buf, &osize, NULL, GST_MAP_READ); - bsize = osize; - bdata = odata; + adapter->scan_offset += info.size; + adapter->scan_entry_idx = idx; + gst_buffer_unmap (buf, &info); + buf = gst_queue_array_peek_nth (adapter->bufqueue, idx++); + + if (!gst_buffer_map (buf, &info, GST_MAP_READ)) + return -1; + + bsize = info.size; + bdata = info.data; } while (TRUE); - gst_buffer_unmap (buf, odata, osize); + gst_buffer_unmap (buf, &info); /* nothing found */ return -1; @@ -1047,13 +1758,13 @@ gst_adapter_masked_scan_uint32_peek (GstAdapter * adapter, guint32 mask, * It is an error to call this function without making sure that there is * enough data (offset+size bytes) in the adapter. * - * This function calls gst_adapter_masked_scan_uint32_peek() passing NULL + * This function calls gst_adapter_masked_scan_uint32_peek() passing %NULL * for value. * * Returns: offset of the first match, or -1 if no match was found. * * Example: - * + * |[ * // Assume the adapter contains 0x00 0x01 0x02 ... 0xfe 0xff * * gst_adapter_masked_scan_uint32 (adapter, 0xffffffff, 0x00010203, 0, 256); @@ -1070,11 +1781,9 @@ gst_adapter_masked_scan_uint32_peek (GstAdapter * adapter, guint32 mask, * // -> returns 2 * gst_adapter_masked_scan_uint32 (adapter, 0xffff0000, 0x02030000, 0, 4); * // -> returns -1 - * - * - * Since: 0.10.24 + * ]| */ -gsize +gssize gst_adapter_masked_scan_uint32 (GstAdapter * adapter, guint32 mask, guint32 pattern, gsize offset, gsize size) {