[FFmpeg-devel] [PoC][PATCH 1/2] avutil/buffer: add av_buffer_create2() to allow AVBufferRef to store complex structures

James Almer jamrial at gmail.com
Mon Jun 1 19:35:39 EEST 2020


Signed-off-by: James Almer <jamrial at gmail.com>
---
This is an attempt at solving the annoying constrain of only supporting flat
arrays within any given AVBufferRef. It introduces a new function to create
buffers that accepts two new callback functions, one to allocate a new buffer
when a new writable reference needs to be created, and one to copy data to it.

In the default scenario, the alloc and copy callbacks simply call
av_buffer_alloc() and memcpy() respectively, which is the current behavior of
treating the buffer as a flat array. In a more complex scenario, the real
benefit comes from the copy callback, which will let a custom implementation
set up the new buffer how it pleases, including handling pointers within the
complex struct it may be storing.

Patch 2/2 is an example implementation of this.

 libavutil/buffer.c          | 39 ++++++++++++++++++++++++++++++-----
 libavutil/buffer.h          | 41 +++++++++++++++++++++++++++++++++++++
 libavutil/buffer_internal.h | 10 +++++++++
 3 files changed, 85 insertions(+), 5 deletions(-)

diff --git a/libavutil/buffer.c b/libavutil/buffer.c
index 7ff6adc2ec..b048e168e8 100644
--- a/libavutil/buffer.c
+++ b/libavutil/buffer.c
@@ -26,9 +26,12 @@
 #include "mem.h"
 #include "thread.h"
 
-AVBufferRef *av_buffer_create(uint8_t *data, int size,
-                              void (*free)(void *opaque, uint8_t *data),
-                              void *opaque, int flags)
+AVBufferRef *av_buffer_create2(uint8_t *data, int size,
+                               AVBufferRef* (*alloc)(void *opaque, int size),
+                               int (*copy)(void *opaque, AVBufferRef *dst,
+                                           const uint8_t *src, int size),
+                               void (*free)(void *opaque, uint8_t *data),
+                               void *opaque, int flags)
 {
     AVBufferRef *ref = NULL;
     AVBuffer    *buf = NULL;
@@ -39,6 +42,8 @@ AVBufferRef *av_buffer_create(uint8_t *data, int size,
 
     buf->data     = data;
     buf->size     = size;
+    buf->alloc    = alloc ? alloc : av_buffer_default_alloc;
+    buf->copy     = copy ? copy : av_buffer_default_copy;
     buf->free     = free ? free : av_buffer_default_free;
     buf->opaque   = opaque;
 
@@ -59,11 +64,29 @@ AVBufferRef *av_buffer_create(uint8_t *data, int size,
     return ref;
 }
 
+AVBufferRef *av_buffer_create(uint8_t *data, int size,
+                              void (*free)(void *opaque, uint8_t *data),
+                              void *opaque, int flags)
+{
+    return av_buffer_create2(data, size, NULL, NULL, free, opaque, flags);
+}
+
 void av_buffer_default_free(void *opaque, uint8_t *data)
 {
     av_free(data);
 }
 
+AVBufferRef *av_buffer_default_alloc(void *opaque, int size)
+{
+    return av_buffer_alloc(size);
+}
+
+int av_buffer_default_copy(void *opaque, AVBufferRef *dst, const uint8_t *src, int size)
+{
+    memcpy(dst->data, src, size);
+    return 0;
+}
+
 AVBufferRef *av_buffer_alloc(int size)
 {
     AVBufferRef *ret = NULL;
@@ -151,15 +174,21 @@ int av_buffer_get_ref_count(const AVBufferRef *buf)
 int av_buffer_make_writable(AVBufferRef **pbuf)
 {
     AVBufferRef *newbuf, *buf = *pbuf;
+    AVBuffer *b = buf->buffer;
+    int ret;
 
     if (av_buffer_is_writable(buf))
         return 0;
 
-    newbuf = av_buffer_alloc(buf->size);
+    newbuf = b->alloc(b->opaque, buf->size);
     if (!newbuf)
         return AVERROR(ENOMEM);
 
-    memcpy(newbuf->data, buf->data, buf->size);
+    ret = b->copy(b->opaque, newbuf, buf->data, buf->size);
+    if (ret < 0) {
+        av_buffer_unref(&newbuf);
+        return ret;
+    }
 
     buffer_replace(pbuf, &newbuf);
 
diff --git a/libavutil/buffer.h b/libavutil/buffer.h
index e0f94314f4..375e04034a 100644
--- a/libavutil/buffer.h
+++ b/libavutil/buffer.h
@@ -131,6 +131,47 @@ AVBufferRef *av_buffer_create(uint8_t *data, int size,
                               void (*free)(void *opaque, uint8_t *data),
                               void *opaque, int flags);
 
+/**
+ * Create an AVBuffer from an existing array.
+ *
+ * If this function is successful, data is owned by the AVBuffer. The caller may
+ * only access data through the returned AVBufferRef and references derived from
+ * it.
+ * If this function fails, data is left untouched.
+ * @param data   data array
+ * @param size   size of data in bytes
+ * @param alloc  a callback for allocating a new buffer when a new writable
+ *               reference for this buffer is created
+ * @param copy   a callback for copying this buffer's data into the newly
+ *               allocated buffer by the alloc callback
+ * @param free   a callback for freeing this buffer's data
+ * @param opaque parameter to be got for processing or passed to alloc/copy/free
+ * @param flags  a combination of AV_BUFFER_FLAG_*
+ *
+ * @return an AVBufferRef referring to data on success, NULL on failure.
+ */
+AVBufferRef *av_buffer_create2(uint8_t *data, int size,
+                               AVBufferRef* (*alloc)(void *opaque, int size),
+                               int (*copy)(void *opaque, AVBufferRef *dst,
+                                           const uint8_t *src, int size),
+                               void (*free)(void *opaque, uint8_t *data),
+                               void *opaque, int flags);
+
+/**
+ * Default alloc callback, which calls av_buffer_alloc() and returns the
+ * newly allocated buffer.
+ * This function is meant to be passed to av_buffer_create2() or
+ * av_buffer_pool_init2(), not called directly.
+ */
+AVBufferRef *av_buffer_default_alloc(void *opaque, int size);
+
+/**
+ * Default copy callback, which copies the data pointed by src to dst.
+ * This function is meant to be passed to av_buffer_create2(), not called
+ * directly.
+ */
+int av_buffer_default_copy(void *opaque, AVBufferRef *dst, const uint8_t *src, int size);
+
 /**
  * Default free callback, which calls av_free() on the buffer data.
  * This function is meant to be passed to av_buffer_create(), not called
diff --git a/libavutil/buffer_internal.h b/libavutil/buffer_internal.h
index 70d2615a06..27fcb5f015 100644
--- a/libavutil/buffer_internal.h
+++ b/libavutil/buffer_internal.h
@@ -39,6 +39,16 @@ struct AVBuffer {
      */
     atomic_uint refcount;
 
+    /**
+     * a callback to allocate a new writable buffer
+     */
+    AVBufferRef* (*alloc)(void *opaque, int size);
+
+    /**
+     * a callback to copy the data into a newly allocated writable buffer
+     */
+    int (*copy)(void *opaque, AVBufferRef *dst, const uint8_t *src, int size);
+
     /**
      * a callback for freeing the data
      */
-- 
2.26.2



More information about the ffmpeg-devel mailing list