/** * gst_adapter_take: * @adapter: a #GstAdapter * @nbytes: the number of bytes to take * * Returns a freshly allocated buffer containing the first @nbytes bytes of the * @adapter. The returned bytes will be flushed from the adapter. * * Caller owns returned value. g_free after usage. * * Returns: oven-fresh hot data, or #NULL if @nbytes bytes are not available */ guint8 * gst_adapter_take (GstAdapter * adapter, guint nbytes) { guint8 *data; g_return_val_if_fail (GST_IS_ADAPTER (adapter), NULL); g_return_val_if_fail (nbytes > 0, NULL); /* we don't have enough data, return NULL. This is unlikely * as one usually does an _available() first instead of peeking a * random size. */ if (G_UNLIKELY (nbytes > adapter->size)) return NULL; /* we have enough assembled data, take from there */ if (adapter->assembled_len >= nbytes) { GST_LOG_OBJECT (adapter, "taking %u bytes already assembled", nbytes); data = adapter->assembled_data; /* allocate new data, assembled_len will be set to 0 in the flush below */ adapter->assembled_data = g_malloc (adapter->assembled_size); } else { /* we need to allocate and copy. We could potentially copy bytes from the * assembled data before doing the copy_into */ GST_LOG_OBJECT (adapter, "taking %u bytes by collection", nbytes); data = g_malloc (nbytes); copy_into_unchecked (adapter, data, adapter->skip, nbytes); } gst_adapter_flush_unchecked (adapter, nbytes); return data; }
/** * gst_adapter_copy: * @adapter: a #GstAdapter * @dest: the memory where to copy to * @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. * * 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, guint8 * dest, guint offset, guint size) { g_return_if_fail (GST_IS_ADAPTER (adapter)); g_return_if_fail (size > 0); g_return_if_fail (offset + size <= adapter->size); copy_into_unchecked (adapter, dest, offset + adapter->skip, size); }
/** * gst_adapter_peek: * @adapter: a #GstAdapter * @size: the number of bytes to peek * * Gets the first @size bytes stored in the @adapter. The returned pointer is * valid until the next function is called on the adapter. * * Note that setting the returned pointer as the data of a #GstBuffer is * incorrect for general-purpose plugins. The reason is that if a downstream * element stores the buffer so that it has access to it outside of the bounds * 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 * gst_adapter_take_buffer(). * * Returns #NULL if @size bytes are not available. * * Returns: a pointer to the first @size bytes of data, or NULL. */ const guint8 * gst_adapter_peek (GstAdapter * adapter, guint size) { GstBuffer *cur; guint skip; g_return_val_if_fail (GST_IS_ADAPTER (adapter), NULL); g_return_val_if_fail (size > 0, NULL); /* we don't have enough data, return NULL. This is unlikely * as one usually does an _available() first instead of peeking a * random size. */ if (G_UNLIKELY (size > adapter->size)) return NULL; /* we have enough assembled data, return it */ if (adapter->assembled_len >= size) return adapter->assembled_data; /* our head buffer has enough data left, return it */ cur = adapter->buflist->data; skip = adapter->skip; if (GST_BUFFER_SIZE (cur) >= size + skip) return GST_BUFFER_DATA (cur) + 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 (gst_adapter_try_to_merge_up (adapter, size)) { /* Merged something! Check if there's enough avail now */ cur = adapter->buflist->data; if (GST_BUFFER_SIZE (cur) >= size + skip) return GST_BUFFER_DATA (cur) + skip; } /* Gonna need to copy stuff out */ if (G_UNLIKELY (adapter->assembled_size < size)) { adapter->assembled_size = (size / DEFAULT_SIZE + 1) * DEFAULT_SIZE; GST_DEBUG_OBJECT (adapter, "resizing internal buffer to %u", adapter->assembled_size); /* no g_realloc to avoid a memcpy that is not desired here since we are * going to copy new data into the area below */ g_free (adapter->assembled_data); adapter->assembled_data = g_malloc (adapter->assembled_size); } adapter->assembled_len = size; GST_CAT_DEBUG (GST_CAT_PERFORMANCE, "copy data from adapter"); copy_into_unchecked (adapter, adapter->assembled_data, skip, size); return adapter->assembled_data; }
/* internal function, nbytes should be flushed after calling this function */ static guint8 * gst_adapter_take_internal (GstAdapter * adapter, gsize nbytes) { guint8 *data; gsize toreuse, tocopy; /* see how much data we can reuse from the assembled memory and how much * we need to copy */ toreuse = MIN (nbytes, adapter->assembled_len); tocopy = nbytes - toreuse; /* find memory to return */ if (adapter->assembled_size >= nbytes && toreuse > 0) { /* we reuse already allocated memory but only when we're going to reuse * something from it because else we are worse than the malloc and copy * case below */ GST_LOG_OBJECT (adapter, "reusing %" G_GSIZE_FORMAT " bytes of assembled" " data", toreuse); /* we have enough free space in the assembled array */ data = adapter->assembled_data; /* flush after this function should set the assembled_size to 0 */ adapter->assembled_data = g_malloc (adapter->assembled_size); } else { GST_LOG_OBJECT (adapter, "allocating %" G_GSIZE_FORMAT " bytes", nbytes); /* not enough bytes in the assembled array, just allocate new space */ data = g_malloc (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); } } if (tocopy) { /* copy the remaining data */ copy_into_unchecked (adapter, toreuse + data, toreuse + adapter->skip, tocopy); } return data; }
/** * 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. * * Caller owns returned value. gst_buffer_unref() after usage. * * Since: 0.10.6 * * Returns: a #GstBuffer containing the first @nbytes of the adapter, * or #NULL if @nbytes bytes are not available */ GstBuffer * gst_adapter_take_buffer (GstAdapter * adapter, guint nbytes) { GstBuffer *buffer; GstBuffer *cur; guint hsize, skip; 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 %u 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; cur = adapter->buflist->data; skip = adapter->skip; hsize = GST_BUFFER_SIZE (cur); /* our head buffer has enough data left, return it */ if (skip == 0 && hsize == nbytes) { GST_LOG_OBJECT (adapter, "providing buffer of %d bytes as head buffer", nbytes); buffer = gst_buffer_ref (cur); goto done; } else if (hsize >= nbytes + skip) { GST_LOG_OBJECT (adapter, "providing buffer of %d bytes via sub-buffer", nbytes); buffer = gst_buffer_create_sub (cur, skip, nbytes); goto done; } if (gst_adapter_try_to_merge_up (adapter, nbytes)) { /* Merged something, let's try again for sub-buffering */ cur = adapter->buflist->data; if (GST_BUFFER_SIZE (cur) >= nbytes + skip) { GST_LOG_OBJECT (adapter, "providing buffer of %d bytes via sub-buffer", nbytes); buffer = gst_buffer_create_sub (cur, skip, nbytes); goto done; } } /* we have enough assembled data, copy from there */ if (adapter->assembled_len >= nbytes) { GST_LOG_OBJECT (adapter, "taking %u bytes already assembled", nbytes); buffer = gst_buffer_new (); GST_BUFFER_SIZE (buffer) = nbytes; GST_BUFFER_DATA (buffer) = adapter->assembled_data; GST_BUFFER_MALLOCDATA (buffer) = adapter->assembled_data; /* flush will set the assembled_len to 0 */ adapter->assembled_data = g_malloc (adapter->assembled_size); } else { /* we need to allocate and copy. We could potentially copy bytes from the * assembled data before doing the copy_into */ buffer = gst_buffer_new_and_alloc (nbytes); GST_LOG_OBJECT (adapter, "taking %u bytes by collection", nbytes); copy_into_unchecked (adapter, GST_BUFFER_DATA (buffer), skip, nbytes); } done: gst_adapter_flush_unchecked (adapter, nbytes); return buffer; }
/** * gst_adapter_map: * @adapter: a #GstAdapter * @size: the number of bytes to map/peek * * Gets the first @size bytes stored in the @adapter. The returned pointer is * valid until the next function is called on the adapter. * * Note that setting the returned pointer as the data of a #GstBuffer is * incorrect for general-purpose plugins. The reason is that if a downstream * element stores the buffer so that it has access to it outside of the bounds * 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 memory or the potentially more performant * gst_adapter_take_buffer(). * * Returns #NULL if @size bytes are not available. * * Returns: (transfer none) (array length=size) (element-type guint8): * a pointer to the first @size bytes of data, or NULL */ gconstpointer gst_adapter_map (GstAdapter * adapter, gsize size) { GstBuffer *cur; gsize skip, csize; gsize toreuse, tocopy; guint8 *data; 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. */ if (G_UNLIKELY (size > adapter->size)) return NULL; /* we have enough assembled data, return it */ if (adapter->assembled_len >= size) return adapter->assembled_data; #if 0 do { #endif cur = adapter->buflist->data; skip = adapter->skip; csize = gst_buffer_get_size (cur); if (csize >= size + 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 */ toreuse = adapter->assembled_len; tocopy = size - toreuse; /* Gonna need to copy stuff out */ if (G_UNLIKELY (adapter->assembled_size < size)) { adapter->assembled_size = (size / DEFAULT_SIZE + 1) * DEFAULT_SIZE; GST_DEBUG_OBJECT (adapter, "resizing internal buffer to %" G_GSIZE_FORMAT, adapter->assembled_size); if (toreuse == 0) { GST_CAT_DEBUG (GST_CAT_PERFORMANCE, "alloc new buffer"); /* no g_realloc to avoid a memcpy that is not desired here since we are * not going to reuse any data here */ g_free (adapter->assembled_data); adapter->assembled_data = g_malloc (adapter->assembled_size); } else { /* we are going to reuse all data, realloc then */ GST_CAT_DEBUG (GST_CAT_PERFORMANCE, "reusing %" G_GSIZE_FORMAT " bytes", toreuse); adapter->assembled_data = g_realloc (adapter->assembled_data, adapter->assembled_size); } } GST_CAT_DEBUG (GST_CAT_PERFORMANCE, "copy remaining %" G_GSIZE_FORMAT " bytes from adapter", tocopy); data = adapter->assembled_data; copy_into_unchecked (adapter, data + toreuse, skip + toreuse, tocopy); adapter->assembled_len = size; return adapter->assembled_data; }