shithub: dav1d

Download patch

ref: 1c42f8bf81d8fc1ac2d7a89dd00337fd70c5ae80
parent: 793c50481b0fa407f5b0bd6eda8599c6c3249563
author: Steve Lhomme <robux4@ycbcr.xyz>
date: Fri Oct 19 13:34:41 EDT 2018

Add support for external picture buffer allocation

--- a/include/dav1d/dav1d.h
+++ b/include/dav1d/dav1d.h
@@ -38,6 +38,7 @@
 typedef struct Dav1dSettings {
     int n_frame_threads;
     int n_tile_threads;
+    Dav1dPicAllocator allocator;
 } Dav1dSettings;
 
 /**
--- a/include/dav1d/picture.h
+++ b/include/dav1d/picture.h
@@ -143,7 +143,42 @@
     Dav1dPictureParameters p;
 
     int poc; ///< frame number
+
+    void *allocator_data; ///< pointer managed by the allocator
 } Dav1dPicture;
+
+typedef struct Dav1dPicAllocator {
+    void *cookie; ///< custom data to pass to the allocator callbacks.
+    /**
+     * Allocate the picture buffer based on the Dav1dPictureParameters.
+     *
+     * The data[0], data[1] and data[2] must be 32 bits aligned and with a
+     * pixel width/height multiple of 128 pixels.
+     * data[1] and data[2] must share the same stride[1].
+     *
+     * @param  pic The picture to allocate the buffer for. The callback needs to
+     *             fill the picture data[0], data[1], data[2], stride[0] and
+     *             stride[1].
+     *             The allocator can fill the pic allocator_data pointer with
+     *             a custom pointer that will be passed to
+     *             release_picture_callback().
+     * @param cookie Custom pointer passed to all calls.
+    *
+    * @return 0 on success. A negative errno value on error.
+     */
+    int (*alloc_picture_callback)(Dav1dPicture *pic, void *cookie);
+    /**
+     * Release the picture buffer.
+     *
+     * @param buf           The buffer that was returned by 
+     *                                   alloc_picture_callback().
+     * @param allocator_tag The Dav1dPicture.allocator_data that was filled by
+     *                      alloc_picture_callback()
+     * @param cookie        Custom pointer passed to all calls.
+     */
+    void (*release_picture_callback)(uint8_t *buf, void *allocator_data,
+                                     void *cookie);
+} Dav1dPicAllocator;
 
 /**
  * Release reference to a picture.
--- a/src/data.c
+++ b/src/data.c
@@ -56,7 +56,7 @@
     validate_input_or_ret(ptr != NULL, -EINVAL);
     validate_input_or_ret(free_callback != NULL, -EINVAL);
 
-    buf->ref = dav1d_ref_wrap(ptr, sz, free_callback, user_data);
+    buf->ref = dav1d_ref_wrap(ptr, free_callback, user_data);
     if (!buf->ref) return -ENOMEM;
     buf->data = ptr;
     buf->sz = sz;
--- a/src/decode.c
+++ b/src/decode.c
@@ -2891,7 +2891,8 @@
                                           f->frame_hdr.height,
                                           f->seq_hdr.layout, f->seq_hdr.bpc,
                                           c->n_fc > 1 ? &f->frame_thread.td : NULL,
-                                          f->frame_hdr.show_frame)) < 0)
+                                          f->frame_hdr.show_frame,
+                                          &c->allocator)) < 0)
     {
         if (f->frame_hdr.refresh_context)
             dav1d_cdf_thread_unref(&f->out_cdf);
--- a/src/internal.h
+++ b/src/internal.h
@@ -110,6 +110,8 @@
         EdgeTip tip_sb128[256];
         EdgeTip tip_sb64[64];
     } intra_edge;
+
+    Dav1dPicAllocator allocator;
 };
 
 struct Dav1dFrameContext {
--- a/src/lib.c
+++ b/src/lib.c
@@ -57,6 +57,9 @@
 void dav1d_default_settings(Dav1dSettings *const s) {
     s->n_frame_threads = 1;
     s->n_tile_threads = 1;
+    s->allocator.cookie = NULL;
+    s->allocator.alloc_picture_callback = default_picture_allocator;
+    s->allocator.release_picture_callback = default_picture_release;
 }
 
 int dav1d_open(Dav1dContext **const c_out,
@@ -71,11 +74,16 @@
                           s->n_tile_threads <= 64, -EINVAL);
     validate_input_or_ret(s->n_frame_threads >= 1 &&
                           s->n_frame_threads <= 256, -EINVAL);
+    validate_input_or_ret(s->allocator.alloc_picture_callback != NULL,
+                          -EINVAL);
+    validate_input_or_ret(s->allocator.release_picture_callback != NULL,
+                          -EINVAL);
 
     Dav1dContext *const c = *c_out = dav1d_alloc_aligned(sizeof(*c), 32);
     if (!c) goto error;
     memset(c, 0, sizeof(*c));
 
+    c->allocator = s->allocator;
     c->n_fc = s->n_frame_threads;
     c->fc = dav1d_alloc_aligned(sizeof(*c->fc) * s->n_frame_threads, 32);
     if (!c->fc) goto error;
--- a/src/picture.c
+++ b/src/picture.c
@@ -41,14 +41,71 @@
 #include "src/ref.h"
 #include "src/thread.h"
 
+int default_picture_allocator(Dav1dPicture *const p, void *cookie) {
+    assert(cookie == NULL);
+    const int hbd = p->p.bpc > 8;
+    const int aligned_w = (p->p.w + 127) & ~127;
+    const int aligned_h = (p->p.h + 127) & ~127;
+    const int has_chroma = p->p.layout != DAV1D_PIXEL_LAYOUT_I400;
+    const int ss_ver = p->p.layout == DAV1D_PIXEL_LAYOUT_I420;
+    const int ss_hor = p->p.layout != DAV1D_PIXEL_LAYOUT_I444;
+    p->stride[0] = aligned_w << hbd;
+    p->stride[1] = has_chroma ? (aligned_w >> ss_hor) << hbd : 0;
+    const size_t y_sz = p->stride[0] * aligned_h;
+    const size_t uv_sz = p->stride[1] * (aligned_h >> ss_ver);
+    const size_t pic_size = y_sz + 2 * uv_sz;
+
+    uint8_t *data = dav1d_alloc_aligned(pic_size, 32);
+    if (data == NULL) {
+        fprintf(stderr, "Failed to allocate memory of size %zu: %s\n",
+                pic_size, strerror(errno));
+        return -1;
+    }
+
+    p->data[0] = data;
+    p->data[1] = has_chroma ? data + y_sz : NULL;
+    p->data[2] = has_chroma ? data + y_sz + uv_sz : NULL;
+
+#ifndef NDEBUG /* safety check */
+    p->allocator_data = data;
+#endif
+
+    return 0;
+}
+
+void default_picture_release(uint8_t *const data, void *const allocator_data,
+                             void *cookie)
+{
+    assert(cookie == NULL);
+#ifndef NDEBUG /* safety check */
+    assert(allocator_data == data);
+#endif
+    dav1d_free_aligned(data);
+}
+
+struct pic_ctx_context {
+    Dav1dPicAllocator allocator;
+    void *allocator_data;
+    void *extra_ptr; /* MUST BE AT THE END */
+};
+
+static void free_buffer(uint8_t *data, void *user_data)
+{
+    struct pic_ctx_context *pic_ctx = user_data;
+
+    pic_ctx->allocator.release_picture_callback(data,
+                                                pic_ctx->allocator_data,
+                                                pic_ctx->allocator.cookie);
+    free(pic_ctx);
+}
+
 static int picture_alloc_with_edges(Dav1dPicture *const p,
                                     const int w, const int h,
                                     const enum Dav1dPixelLayout layout,
                                     const int bpc,
-                                    const int extra, void **const extra_ptr)
+                                    Dav1dPicAllocator *const p_allocator,
+                                    const size_t extra, void **const extra_ptr)
 {
-    int aligned_h;
-
     if (p->data[0]) {
         fprintf(stderr, "Picture already allocated!\n");
         return -1;
@@ -55,13 +112,11 @@
     }
     assert(bpc > 0 && bpc <= 16);
 
-    const int hbd = bpc > 8;
-    const int aligned_w = (w + 127) & ~127;
-    const int has_chroma = layout != DAV1D_PIXEL_LAYOUT_I400;
-    const int ss_ver = layout == DAV1D_PIXEL_LAYOUT_I420;
-    const int ss_hor = layout != DAV1D_PIXEL_LAYOUT_I444;
-    p->stride[0] = aligned_w << hbd;
-    p->stride[1] = has_chroma ? (aligned_w >> ss_hor) << hbd : 0;
+    struct pic_ctx_context *pic_ctx = malloc(extra + sizeof(struct pic_ctx_context));
+    if (pic_ctx == NULL) {
+        return -ENOMEM;
+    }
+
     p->p.w = w;
     p->p.h = h;
     p->p.pri = DAV1D_COLOR_PRI_UNKNOWN;
@@ -68,24 +123,27 @@
     p->p.trc = DAV1D_TRC_UNKNOWN;
     p->p.mtrx = DAV1D_MC_UNKNOWN;
     p->p.chr = DAV1D_CHR_UNKNOWN;
-    aligned_h = (h + 127) & ~127;
     p->p.layout = layout;
     p->p.bpc = bpc;
-    const size_t y_sz = p->stride[0] * aligned_h;
-    const size_t uv_sz = p->stride[1] * (aligned_h >> ss_ver);
-    if (!(p->ref = dav1d_ref_create(y_sz + 2 * uv_sz + extra))) {
-        fprintf(stderr, "Failed to allocate memory of size %zu: %s\n",
-                y_sz + 2 * uv_sz + extra, strerror(errno));
+    int res = p_allocator->alloc_picture_callback(p, p_allocator->cookie);
+    if (res < 0) {
+        free(pic_ctx);
         return -ENOMEM;
     }
-    uint8_t *data = p->ref->data;
-    p->data[0] = data;
-    p->data[1] = has_chroma ? data + y_sz : NULL;
-    p->data[2] = has_chroma ? data + y_sz + uv_sz : NULL;
 
-    if (extra)
-        *extra_ptr = &data[y_sz + uv_sz * 2];
+    pic_ctx->allocator = *p_allocator;
+    pic_ctx->allocator_data = p->allocator_data;
 
+    if (!(p->ref = dav1d_ref_wrap(p->data[0], free_buffer, pic_ctx))) {
+        p_allocator->release_picture_callback(p->data[0], p->allocator_data,
+                                              p_allocator->cookie);
+        fprintf(stderr, "Failed to wrap picture: %s\n", strerror(errno));
+        return -ENOMEM;
+    }
+
+    if (extra && extra_ptr)
+        *extra_ptr = &pic_ctx->extra_ptr;
+
     return 0;
 }
 
@@ -92,12 +150,13 @@
 int dav1d_thread_picture_alloc(Dav1dThreadPicture *const p,
                                const int w, const int h,
                                const enum Dav1dPixelLayout layout, const int bpc,
-                               struct thread_data *const t, const int visible)
+                               struct thread_data *const t, const int visible,
+                               Dav1dPicAllocator *const p_allocator)
 {
     p->t = t;
 
     const int res =
-        picture_alloc_with_edges(&p->p, w, h, layout, bpc,
+        picture_alloc_with_edges(&p->p, w, h, layout, bpc, p_allocator,
                                  t != NULL ? sizeof(atomic_int) * 2 : 0,
                                  (void **) &p->progress);
 
--- a/src/picture.h
+++ b/src/picture.h
@@ -56,7 +56,8 @@
  */
 int dav1d_thread_picture_alloc(Dav1dThreadPicture *p, int w, int h,
                                enum Dav1dPixelLayout layout, int bpc,
-                               struct thread_data *t, int visible);
+                               struct thread_data *t, int visible,
+                               Dav1dPicAllocator *);
 
 /**
  * Create a copy of a picture.
@@ -87,5 +88,8 @@
  */
 void dav1d_thread_picture_signal(const Dav1dThreadPicture *p, int y,
                                  enum PlaneType plane_type);
+
+int default_picture_allocator(Dav1dPicture *, void *cookie);
+void default_picture_release(uint8_t *, void *allocator_data, void *cookie);
 
 #endif /* __DAV1D_SRC_PICTURE_H__ */
--- a/src/ref.c
+++ b/src/ref.c
@@ -36,24 +36,21 @@
 }
 
 Dav1dRef *dav1d_ref_create(const size_t size) {
-    Dav1dRef *res = malloc(sizeof(Dav1dRef));
+    Dav1dRef *res;
     void *data = dav1d_alloc_aligned(size, 32);
-
-    if (!res || !data) {
-        if (res) free(res);
-        if (data) free(data);
+    if (!data) {
         return NULL;
     }
 
-    res->size = size;
-    atomic_init(&res->ref_cnt, 1);
-    res->data = data;
-    res->free_callback = default_free_callback;
+    res = dav1d_ref_wrap(data, default_free_callback, NULL);
+    if (!res) {
+        free(data);
+    }
 
     return res;
 }
 
-Dav1dRef *dav1d_ref_wrap(uint8_t *const ptr, const size_t sz,
+Dav1dRef *dav1d_ref_wrap(uint8_t *const ptr,
                          void (*free_callback)(uint8_t *data, void *user_data),
                          void *user_data)
 {
@@ -61,7 +58,6 @@
     if (!res) return NULL;
 
     res->data = ptr;
-    res->size = sz;
     atomic_init(&res->ref_cnt, 1);
     res->free_callback = free_callback;
     res->user_data = user_data;
--- a/src/ref.h
+++ b/src/ref.h
@@ -35,7 +35,6 @@
 
 struct Dav1dRef {
     void *data;
-    size_t size;
     atomic_int ref_cnt;
     void (*free_callback)(uint8_t *data, void *user_data);
     void *user_data;
@@ -42,7 +41,7 @@
 };
 
 Dav1dRef *dav1d_ref_create(size_t size);
-Dav1dRef *dav1d_ref_wrap(uint8_t *ptr, size_t sz,
+Dav1dRef *dav1d_ref_wrap(uint8_t *ptr,
                          void (*free_callback)(uint8_t *data, void *user_data),
                          void *user_data);
 void dav1d_ref_inc(Dav1dRef *ref);