Message ID | 20200227200407.490616-18-nicolas.dufresne@collabora.com |
---|---|
State | Accepted |
Headers | show |
Series |
|
Related | show |
Hi Nicolas, Thank you for the patch. On Thu, Feb 27, 2020 at 03:03:57PM -0500, Nicolas Dufresne wrote: > This is needed to track the lifetime of the FrameBufferAllocator in relation to > the GstBuffer/GstMemory objects travelling inside GStreamer. > > Signed-off-by: Nicolas Dufresne <nicolas.dufresne@collabora.com> > --- > src/gstreamer/gstlibcameraallocator.cpp | 244 ++++++++++++++++++++++++ > src/gstreamer/gstlibcameraallocator.h | 29 +++ > src/gstreamer/gstlibcamerapool.cpp | 109 +++++++++++ > src/gstreamer/gstlibcamerapool.h | 26 +++ > src/gstreamer/meson.build | 13 +- > 5 files changed, 417 insertions(+), 4 deletions(-) > create mode 100644 src/gstreamer/gstlibcameraallocator.cpp > create mode 100644 src/gstreamer/gstlibcameraallocator.h > create mode 100644 src/gstreamer/gstlibcamerapool.cpp > create mode 100644 src/gstreamer/gstlibcamerapool.h > > diff --git a/src/gstreamer/gstlibcameraallocator.cpp b/src/gstreamer/gstlibcameraallocator.cpp > new file mode 100644 > index 0000000..f268561 > --- /dev/null > +++ b/src/gstreamer/gstlibcameraallocator.cpp > @@ -0,0 +1,244 @@ > +/* SPDX-License-Identifier: LGPL-2.1-or-later */ > +/* > + * Copyright (C) 2020, Collabora Ltd. > + * Author: Nicolas Dufresne <nicolas.dufresne@collabora.com> > + * > + * gstlibcameraallocator.cpp - GStreamer Custom Allocator > + */ > + > +#include "gstlibcameraallocator.h" > +#include "gstlibcamera-utils.h" > + > +#include <libcamera/camera.h> > +#include <libcamera/framebuffer_allocator.h> > +#include <libcamera/stream.h> > + > +using namespace libcamera; > + > +static gboolean gst_libcamera_allocator_release(GstMiniObject *mini_object); > + > +/** > + * \struct FrameWrap > + * \brief An internal wrapper to track the relation between FrameBuffer and > + * GstMemory(s) > + * > + * This wrapper maintains a count of the outstanding GstMemory (there may be > + * multiple GstMemory per FrameBuffer), and give back the FrameBuffer to the > + * allocator pool when all memory objects have returned. > + */ > + > +struct FrameWrap { > + FrameWrap(GstAllocator *allocator, FrameBuffer *buffer, > + gpointer stream); > + ~FrameWrap(); > + > + void acquirePlane() { ++outstandingPlanes_; } > + bool releasePlane() { return --outstandingPlanes_ == 0; } > + > + static GQuark getQuark(); > + > + gpointer stream_; > + FrameBuffer *buffer_; > + std::vector<GstMemory *> planes_; > + gint outstandingPlanes_; > +}; > + > +FrameWrap::FrameWrap(GstAllocator *allocator, FrameBuffer *buffer, > + gpointer stream) > + > + : stream_(stream), > + buffer_(buffer), > + outstandingPlanes_(0) > +{ > + for (const FrameBuffer::Plane &plane : buffer->planes()) { > + GstMemory *mem = gst_fd_allocator_alloc(allocator, plane.fd.fd(), plane.length, > + GST_FD_MEMORY_FLAG_DONT_CLOSE); > + gst_mini_object_set_qdata(GST_MINI_OBJECT(mem), FrameWrap::getQuark(), this, nullptr); You can write getQuark() instead of FrameWrap::getQuark(). > + GST_MINI_OBJECT(mem)->dispose = gst_libcamera_allocator_release; > + g_object_unref(mem->allocator); > + planes_.push_back(mem); > + } > +} > + > +FrameWrap::~FrameWrap() > +{ > + for (GstMemory *mem : planes_) { > + GST_MINI_OBJECT(mem)->dispose = nullptr; > + g_object_ref(mem->allocator); > + gst_memory_unref(mem); > + } > +} > + > +GQuark FrameWrap::getQuark(void) > +{ > + static gsize frame_quark = 0; > + > + if (g_once_init_enter(&frame_quark)) { > + GQuark quark = g_quark_from_string("GstLibcameraFrameWrap"); > + g_once_init_leave(&frame_quark, quark); > + } > + > + return frame_quark; > +} > + > + > + One blank line is enough. > +/** > + * \struct _GstLibcameraAllocator > + * \brief A pooling GstDmaBufAllocator for libcamera > + * > + * This is a pooling GstDmaBufAllocator implementation. This implementation override > + * the dispose function of memory object in order to keep them alive when they > + * are disposed by downstream elements. > + */ > +struct _GstLibcameraAllocator { > + GstDmaBufAllocator parent; > + FrameBufferAllocator *fb_allocator; > + /* A hash table using Stream pointer as key and returning a GQueue of > + * FrameWrap. */ /* * ... */ > + GHashTable *pools; > +}; > + > +G_DEFINE_TYPE(GstLibcameraAllocator, gst_libcamera_allocator, > + GST_TYPE_DMABUF_ALLOCATOR); > + > +static gboolean > +gst_libcamera_allocator_release(GstMiniObject *mini_object) > +{ > + GstMemory *mem = GST_MEMORY_CAST(mini_object); > + GstLibcameraAllocator *self = GST_LIBCAMERA_ALLOCATOR(mem->allocator); > + GLibLocker lock(GST_OBJECT(self)); > + auto *frame = reinterpret_cast<FrameWrap *>(gst_mini_object_get_qdata(mini_object, FrameWrap::getQuark())); > + > + gst_memory_ref(mem); > + > + if (frame->releasePlane()) { > + auto *pool = reinterpret_cast<GQueue *>(g_hash_table_lookup(self->pools, frame->stream_)); > + g_return_val_if_fail(pool, TRUE); > + g_queue_push_tail(pool, frame); > + } > + > + /* Keep last in case we are holding on the last allocator ref. */ > + g_object_unref(mem->allocator); > + > + /* Return FALSE so that our mini object isn't freed. */ > + return FALSE; > +} > + > +static void > +gst_libcamera_allocator_free_pool(gpointer data) > +{ > + GQueue *queue = reinterpret_cast<GQueue *>(data); > + FrameWrap *frame; > + > + while ((frame = reinterpret_cast<FrameWrap *>(g_queue_pop_head(queue)))) { > + g_warn_if_fail(frame->outstandingPlanes_ == 0); > + delete frame; > + } > + > + g_queue_free(queue); > +} > + > +static void > +gst_libcamera_allocator_init(GstLibcameraAllocator *self) > +{ > + self->pools = g_hash_table_new_full(nullptr, nullptr, nullptr, > + gst_libcamera_allocator_free_pool); > + GST_OBJECT_FLAG_SET(self, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC); > +} > + > +static void > +gst_libcamera_allocator_dispose(GObject *object) > +{ > + GstLibcameraAllocator *self = GST_LIBCAMERA_ALLOCATOR(object); > + > + if (self->pools) { > + g_hash_table_unref(self->pools); > + self->pools = nullptr; > + } > + > + G_OBJECT_CLASS(gst_libcamera_allocator_parent_class)->dispose(object); > +} > + > +static void > +gst_libcamera_allocator_finalize(GObject *object) > +{ > + GstLibcameraAllocator *self = GST_LIBCAMERA_ALLOCATOR(object); > + > + delete self->fb_allocator; > + > + G_OBJECT_CLASS(gst_libcamera_allocator_parent_class)->finalize(object); > +} > + > +static void > +gst_libcamera_allocator_class_init(GstLibcameraAllocatorClass *klass) > +{ > + auto *allocator_class = GST_ALLOCATOR_CLASS(klass); > + auto *object_class = G_OBJECT_CLASS(klass); > + > + object_class->dispose = gst_libcamera_allocator_dispose; > + object_class->finalize = gst_libcamera_allocator_finalize; > + allocator_class->alloc = nullptr; > +} > + > +GstLibcameraAllocator * > +gst_libcamera_allocator_new(std::shared_ptr<Camera> camera) > +{ > + auto *self = (GstLibcameraAllocator *)g_object_new(GST_TYPE_LIBCAMERA_ALLOCATOR, > + nullptr); Is there a GStreamer/GLib typecast macro you could use here ? Or reinterpret_cast<>() ? > + > + self->fb_allocator = FrameBufferAllocator::create(camera); > + for (Stream *stream : camera->streams()) { > + gint ret; > + > + ret = self->fb_allocator->allocate(stream); > + if (ret == 0) > + return nullptr; > + > + GQueue *pool = g_queue_new(); > + for (const std::unique_ptr<FrameBuffer> &buffer : > + self->fb_allocator->buffers(stream)) { > + auto *fb = new FrameWrap(GST_ALLOCATOR(self), > + buffer.get(), stream); > + g_queue_push_tail(pool, fb); > + } > + > + g_hash_table_insert(self->pools, stream, pool); > + } > + > + return self; > +} > + > +bool > +gst_libcamera_allocator_prepare_buffer(GstLibcameraAllocator *self, > + Stream *stream, GstBuffer *buffer) > +{ > + GLibLocker lock(GST_OBJECT(self)); > + > + auto *pool = reinterpret_cast<GQueue *>(g_hash_table_lookup(self->pools, stream)); > + g_return_val_if_fail(pool, false); > + > + auto *frame = reinterpret_cast<FrameWrap *>(g_queue_pop_head(pool)); > + if (!frame) > + return false; > + > + for (GstMemory *mem : frame->planes_) { > + frame->acquirePlane(); > + gst_buffer_append_memory(buffer, mem); > + g_object_ref(mem->allocator); > + } > + > + return true; > +} > + > +gsize > +gst_libcamera_allocator_get_pool_size(GstLibcameraAllocator *self, > + Stream *stream) > +{ > + GLibLocker lock(GST_OBJECT(self)); > + > + auto *pool = reinterpret_cast<GQueue *>(g_hash_table_lookup(self->pools, stream)); > + g_return_val_if_fail(pool, false); > + > + return pool->length; > +} > diff --git a/src/gstreamer/gstlibcameraallocator.h b/src/gstreamer/gstlibcameraallocator.h > new file mode 100644 > index 0000000..25cbf85 > --- /dev/null > +++ b/src/gstreamer/gstlibcameraallocator.h > @@ -0,0 +1,29 @@ > +/* SPDX-License-Identifier: LGPL-2.1-or-later */ > +/* > + * Copyright (C) 2020, Collabora Ltd. > + * Author: Nicolas Dufresne <nicolas.dufresne@collabora.com> > + * > + * gstlibcameraallocator.h - GStreamer Custom Allocator > + */ > + > +#ifndef __GST_LIBCAMERA_ALLOCATOR_H__ > +#define __GST_LIBCAMERA_ALLOCATOR_H__ > + > +#include <gst/gst.h> > +#include <gst/allocators/allocators.h> > +#include <libcamera/stream.h> > + > +#define GST_TYPE_LIBCAMERA_ALLOCATOR gst_libcamera_allocator_get_type() > +G_DECLARE_FINAL_TYPE(GstLibcameraAllocator, gst_libcamera_allocator, > + GST_LIBCAMERA, ALLOCATOR, GstDmaBufAllocator) > + > +GstLibcameraAllocator *gst_libcamera_allocator_new(std::shared_ptr<libcamera::Camera> camera); > + > +bool gst_libcamera_allocator_prepare_buffer(GstLibcameraAllocator *self, > + libcamera::Stream *stream, > + GstBuffer *buffer); > + > +gsize gst_libcamera_allocator_get_pool_size(GstLibcameraAllocator *allocator, > + libcamera::Stream *stream); > + > +#endif /* __GST_LIBCAMERA_ALLOCATOR_H__ */ > diff --git a/src/gstreamer/gstlibcamerapool.cpp b/src/gstreamer/gstlibcamerapool.cpp > new file mode 100644 > index 0000000..ee106a7 > --- /dev/null > +++ b/src/gstreamer/gstlibcamerapool.cpp > @@ -0,0 +1,109 @@ > +/* SPDX-License-Identifier: LGPL-2.1-or-later */ > +/* > + * Copyright (C) 2020, Collabora Ltd. > + * Author: Nicolas Dufresne <nicolas.dufresne@collabora.com> > + * > + * gstlibcamerapool.cpp - GStreamer Buffer Pool > + */ > + > +#include "gstlibcamera-utils.h" > +#include "gstlibcamerapool.h" gstlibcamerapool.h should go first, then libcamera, then gstlibcamera-utils.h. > + > +#include <libcamera/stream.h> > + > +using namespace libcamera; > + > +struct _GstLibcameraPool { > + GstBufferPool parent; > + > + GstAtomicQueue *queue; > + GstLibcameraAllocator *allocator; > + Stream *stream; > +}; > + > +G_DEFINE_TYPE(GstLibcameraPool, gst_libcamera_pool, GST_TYPE_BUFFER_POOL); > + > +static GstFlowReturn > +gst_libcamera_pool_acquire_buffer(GstBufferPool *pool, GstBuffer **buffer, > + GstBufferPoolAcquireParams *params) > +{ > + GstLibcameraPool *self = GST_LIBCAMERA_POOL(pool); > + GstBuffer *buf = GST_BUFFER(gst_atomic_queue_pop(self->queue)); > + if (!buf) > + return GST_FLOW_ERROR; > + > + if (!gst_libcamera_allocator_prepare_buffer(self->allocator, self->stream, buf)) > + return GST_FLOW_ERROR; > + > + *buffer = buf; > + return GST_FLOW_OK; > +} > + > +static void > +gst_libcamera_pool_reset_buffer(GstBufferPool *pool, GstBuffer *buffer) > +{ > + GstBufferPoolClass *klass = GST_BUFFER_POOL_CLASS(gst_libcamera_pool_parent_class); > + > + /* Clears all the memories and only pool the GstBuffer objects */ > + gst_buffer_remove_all_memory(buffer); > + klass->reset_buffer(pool, buffer); > + GST_BUFFER_FLAGS(buffer) = 0; > +} > + > +static void > +gst_libcamera_pool_release_buffer(GstBufferPool *pool, GstBuffer *buffer) > +{ > + GstLibcameraPool *self = GST_LIBCAMERA_POOL(pool); > + gst_atomic_queue_push(self->queue, buffer); > +} > + > +static void > +gst_libcamera_pool_init(GstLibcameraPool *self) > +{ > + self->queue = gst_atomic_queue_new(4); > +} > + > +static void > +gst_libcamera_pool_finalize(GObject *object) > +{ > + GstLibcameraPool *self = GST_LIBCAMERA_POOL(object); > + GstBuffer *buf; > + > + while ((buf = GST_BUFFER(gst_atomic_queue_pop(self->queue)))) > + gst_buffer_unref(buf); > + > + gst_atomic_queue_unref(self->queue); > + g_object_unref(self->allocator); > + > + G_OBJECT_CLASS(gst_libcamera_pool_parent_class)->finalize(object); > +} > + > +static void > +gst_libcamera_pool_class_init(GstLibcameraPoolClass *klass) > +{ > + auto *object_class = G_OBJECT_CLASS(klass); > + auto *pool_class = GST_BUFFER_POOL_CLASS(klass); > + > + object_class->finalize = gst_libcamera_pool_finalize; > + pool_class->start = nullptr; > + pool_class->acquire_buffer = gst_libcamera_pool_acquire_buffer; > + pool_class->reset_buffer = gst_libcamera_pool_reset_buffer; > + pool_class->release_buffer = gst_libcamera_pool_release_buffer; > +} > + > +GstLibcameraPool * > +gst_libcamera_pool_new(GstLibcameraAllocator *allocator, Stream *stream) > +{ > + auto *pool = GST_LIBCAMERA_POOL(g_object_new(GST_TYPE_LIBCAMERA_POOL, nullptr)); > + > + pool->allocator = GST_LIBCAMERA_ALLOCATOR(g_object_ref(allocator)); > + pool->stream = stream; > + > + gsize pool_size = gst_libcamera_allocator_get_pool_size(allocator, stream); > + for (gsize i = 0; i < pool_size; i++) { > + GstBuffer *buffer = gst_buffer_new(); > + gst_atomic_queue_push(pool->queue, buffer); > + } > + > + return pool; > +} > diff --git a/src/gstreamer/gstlibcamerapool.h b/src/gstreamer/gstlibcamerapool.h > new file mode 100644 > index 0000000..a764c75 > --- /dev/null > +++ b/src/gstreamer/gstlibcamerapool.h > @@ -0,0 +1,26 @@ > +/* SPDX-License-Identifier: LGPL-2.1-or-later */ > +/* > + * Copyright (C) 2020, Collabora Ltd. > + * Author: Nicolas Dufresne <nicolas.dufresne@collabora.com> > + * > + * gstlibcamerapool.h - GStreamer Buffer Pool > + * > + * This is a partial implementation of GstBufferPool intended for internal use > + * only. This pool cannot be configured or activated. > + */ > + > +#ifndef __GST_LIBCAMERA_POOL_H__ > +#define __GST_LIBCAMERA_POOL_H__ > + > +#include <gst/gst.h> > +#include <libcamera/stream.h> > + > +#include "gstlibcameraallocator.h" > + > +#define GST_TYPE_LIBCAMERA_POOL gst_libcamera_pool_get_type() > +G_DECLARE_FINAL_TYPE(GstLibcameraPool, gst_libcamera_pool, GST_LIBCAMERA, POOL, GstBufferPool) > + > +GstLibcameraPool *gst_libcamera_pool_new(GstLibcameraAllocator *allocator, > + libcamera::Stream *stream); > + > +#endif /* __GST_LIBCAMERA_POOL_H__ */ > diff --git a/src/gstreamer/meson.build b/src/gstreamer/meson.build > index 1c4a2e3..90773af 100644 > --- a/src/gstreamer/meson.build > +++ b/src/gstreamer/meson.build > @@ -1,7 +1,9 @@ > libcamera_gst_sources = [ > 'gstlibcamera-utils.cpp', > 'gstlibcamera.c', > + 'gstlibcameraallocator.cpp', > 'gstlibcamerapad.cpp', > + 'gstlibcamerapool.cpp', > 'gstlibcameraprovider.cpp', > 'gstlibcamerasrc.cpp', > ] > @@ -11,14 +13,17 @@ libcamera_gst_c_args = [ > '-DPACKAGE="@0@"'.format(meson.project_name()), > ] > > -gst_dep = dependency('gstreamer-video-1.0', version : '>=1.16.1', > - required : get_option('gstreamer')) > +gst_req = '>=1.16.1' Maybe gst_dep_version instead of gst_req ? I should have commented about this in the review of the first patch, but do we still have a dependency on v1.16 ? I thought v2 was trying to target v1.14. > +gstvideo_dep = dependency('gstreamer-video-1.0', version : gst_req, > + required : get_option('gstreamer')) > +gstallocator_dep = dependency('gstreamer-allocators-1.0', version : gst_req, > + required : get_option('gstreamer')) > > -if gst_dep.found() > +if gstvideo_dep.found() and gstallocator_dep.found() > libcamera_gst = shared_library('gstlibcamera', > libcamera_gst_sources, > c_args : libcamera_gst_c_args, > - dependencies : [libcamera_dep, gst_dep], > + dependencies : [libcamera_dep, gstvideo_dep, gstallocator_dep], > install: true, > install_dir : '@0@/gstreamer-1.0'.format(get_option('libdir')), > )
diff --git a/src/gstreamer/gstlibcameraallocator.cpp b/src/gstreamer/gstlibcameraallocator.cpp new file mode 100644 index 0000000..f268561 --- /dev/null +++ b/src/gstreamer/gstlibcameraallocator.cpp @@ -0,0 +1,244 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2020, Collabora Ltd. + * Author: Nicolas Dufresne <nicolas.dufresne@collabora.com> + * + * gstlibcameraallocator.cpp - GStreamer Custom Allocator + */ + +#include "gstlibcameraallocator.h" +#include "gstlibcamera-utils.h" + +#include <libcamera/camera.h> +#include <libcamera/framebuffer_allocator.h> +#include <libcamera/stream.h> + +using namespace libcamera; + +static gboolean gst_libcamera_allocator_release(GstMiniObject *mini_object); + +/** + * \struct FrameWrap + * \brief An internal wrapper to track the relation between FrameBuffer and + * GstMemory(s) + * + * This wrapper maintains a count of the outstanding GstMemory (there may be + * multiple GstMemory per FrameBuffer), and give back the FrameBuffer to the + * allocator pool when all memory objects have returned. + */ + +struct FrameWrap { + FrameWrap(GstAllocator *allocator, FrameBuffer *buffer, + gpointer stream); + ~FrameWrap(); + + void acquirePlane() { ++outstandingPlanes_; } + bool releasePlane() { return --outstandingPlanes_ == 0; } + + static GQuark getQuark(); + + gpointer stream_; + FrameBuffer *buffer_; + std::vector<GstMemory *> planes_; + gint outstandingPlanes_; +}; + +FrameWrap::FrameWrap(GstAllocator *allocator, FrameBuffer *buffer, + gpointer stream) + + : stream_(stream), + buffer_(buffer), + outstandingPlanes_(0) +{ + for (const FrameBuffer::Plane &plane : buffer->planes()) { + GstMemory *mem = gst_fd_allocator_alloc(allocator, plane.fd.fd(), plane.length, + GST_FD_MEMORY_FLAG_DONT_CLOSE); + gst_mini_object_set_qdata(GST_MINI_OBJECT(mem), FrameWrap::getQuark(), this, nullptr); + GST_MINI_OBJECT(mem)->dispose = gst_libcamera_allocator_release; + g_object_unref(mem->allocator); + planes_.push_back(mem); + } +} + +FrameWrap::~FrameWrap() +{ + for (GstMemory *mem : planes_) { + GST_MINI_OBJECT(mem)->dispose = nullptr; + g_object_ref(mem->allocator); + gst_memory_unref(mem); + } +} + +GQuark FrameWrap::getQuark(void) +{ + static gsize frame_quark = 0; + + if (g_once_init_enter(&frame_quark)) { + GQuark quark = g_quark_from_string("GstLibcameraFrameWrap"); + g_once_init_leave(&frame_quark, quark); + } + + return frame_quark; +} + + + +/** + * \struct _GstLibcameraAllocator + * \brief A pooling GstDmaBufAllocator for libcamera + * + * This is a pooling GstDmaBufAllocator implementation. This implementation override + * the dispose function of memory object in order to keep them alive when they + * are disposed by downstream elements. + */ +struct _GstLibcameraAllocator { + GstDmaBufAllocator parent; + FrameBufferAllocator *fb_allocator; + /* A hash table using Stream pointer as key and returning a GQueue of + * FrameWrap. */ + GHashTable *pools; +}; + +G_DEFINE_TYPE(GstLibcameraAllocator, gst_libcamera_allocator, + GST_TYPE_DMABUF_ALLOCATOR); + +static gboolean +gst_libcamera_allocator_release(GstMiniObject *mini_object) +{ + GstMemory *mem = GST_MEMORY_CAST(mini_object); + GstLibcameraAllocator *self = GST_LIBCAMERA_ALLOCATOR(mem->allocator); + GLibLocker lock(GST_OBJECT(self)); + auto *frame = reinterpret_cast<FrameWrap *>(gst_mini_object_get_qdata(mini_object, FrameWrap::getQuark())); + + gst_memory_ref(mem); + + if (frame->releasePlane()) { + auto *pool = reinterpret_cast<GQueue *>(g_hash_table_lookup(self->pools, frame->stream_)); + g_return_val_if_fail(pool, TRUE); + g_queue_push_tail(pool, frame); + } + + /* Keep last in case we are holding on the last allocator ref. */ + g_object_unref(mem->allocator); + + /* Return FALSE so that our mini object isn't freed. */ + return FALSE; +} + +static void +gst_libcamera_allocator_free_pool(gpointer data) +{ + GQueue *queue = reinterpret_cast<GQueue *>(data); + FrameWrap *frame; + + while ((frame = reinterpret_cast<FrameWrap *>(g_queue_pop_head(queue)))) { + g_warn_if_fail(frame->outstandingPlanes_ == 0); + delete frame; + } + + g_queue_free(queue); +} + +static void +gst_libcamera_allocator_init(GstLibcameraAllocator *self) +{ + self->pools = g_hash_table_new_full(nullptr, nullptr, nullptr, + gst_libcamera_allocator_free_pool); + GST_OBJECT_FLAG_SET(self, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC); +} + +static void +gst_libcamera_allocator_dispose(GObject *object) +{ + GstLibcameraAllocator *self = GST_LIBCAMERA_ALLOCATOR(object); + + if (self->pools) { + g_hash_table_unref(self->pools); + self->pools = nullptr; + } + + G_OBJECT_CLASS(gst_libcamera_allocator_parent_class)->dispose(object); +} + +static void +gst_libcamera_allocator_finalize(GObject *object) +{ + GstLibcameraAllocator *self = GST_LIBCAMERA_ALLOCATOR(object); + + delete self->fb_allocator; + + G_OBJECT_CLASS(gst_libcamera_allocator_parent_class)->finalize(object); +} + +static void +gst_libcamera_allocator_class_init(GstLibcameraAllocatorClass *klass) +{ + auto *allocator_class = GST_ALLOCATOR_CLASS(klass); + auto *object_class = G_OBJECT_CLASS(klass); + + object_class->dispose = gst_libcamera_allocator_dispose; + object_class->finalize = gst_libcamera_allocator_finalize; + allocator_class->alloc = nullptr; +} + +GstLibcameraAllocator * +gst_libcamera_allocator_new(std::shared_ptr<Camera> camera) +{ + auto *self = (GstLibcameraAllocator *)g_object_new(GST_TYPE_LIBCAMERA_ALLOCATOR, + nullptr); + + self->fb_allocator = FrameBufferAllocator::create(camera); + for (Stream *stream : camera->streams()) { + gint ret; + + ret = self->fb_allocator->allocate(stream); + if (ret == 0) + return nullptr; + + GQueue *pool = g_queue_new(); + for (const std::unique_ptr<FrameBuffer> &buffer : + self->fb_allocator->buffers(stream)) { + auto *fb = new FrameWrap(GST_ALLOCATOR(self), + buffer.get(), stream); + g_queue_push_tail(pool, fb); + } + + g_hash_table_insert(self->pools, stream, pool); + } + + return self; +} + +bool +gst_libcamera_allocator_prepare_buffer(GstLibcameraAllocator *self, + Stream *stream, GstBuffer *buffer) +{ + GLibLocker lock(GST_OBJECT(self)); + + auto *pool = reinterpret_cast<GQueue *>(g_hash_table_lookup(self->pools, stream)); + g_return_val_if_fail(pool, false); + + auto *frame = reinterpret_cast<FrameWrap *>(g_queue_pop_head(pool)); + if (!frame) + return false; + + for (GstMemory *mem : frame->planes_) { + frame->acquirePlane(); + gst_buffer_append_memory(buffer, mem); + g_object_ref(mem->allocator); + } + + return true; +} + +gsize +gst_libcamera_allocator_get_pool_size(GstLibcameraAllocator *self, + Stream *stream) +{ + GLibLocker lock(GST_OBJECT(self)); + + auto *pool = reinterpret_cast<GQueue *>(g_hash_table_lookup(self->pools, stream)); + g_return_val_if_fail(pool, false); + + return pool->length; +} diff --git a/src/gstreamer/gstlibcameraallocator.h b/src/gstreamer/gstlibcameraallocator.h new file mode 100644 index 0000000..25cbf85 --- /dev/null +++ b/src/gstreamer/gstlibcameraallocator.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2020, Collabora Ltd. + * Author: Nicolas Dufresne <nicolas.dufresne@collabora.com> + * + * gstlibcameraallocator.h - GStreamer Custom Allocator + */ + +#ifndef __GST_LIBCAMERA_ALLOCATOR_H__ +#define __GST_LIBCAMERA_ALLOCATOR_H__ + +#include <gst/gst.h> +#include <gst/allocators/allocators.h> +#include <libcamera/stream.h> + +#define GST_TYPE_LIBCAMERA_ALLOCATOR gst_libcamera_allocator_get_type() +G_DECLARE_FINAL_TYPE(GstLibcameraAllocator, gst_libcamera_allocator, + GST_LIBCAMERA, ALLOCATOR, GstDmaBufAllocator) + +GstLibcameraAllocator *gst_libcamera_allocator_new(std::shared_ptr<libcamera::Camera> camera); + +bool gst_libcamera_allocator_prepare_buffer(GstLibcameraAllocator *self, + libcamera::Stream *stream, + GstBuffer *buffer); + +gsize gst_libcamera_allocator_get_pool_size(GstLibcameraAllocator *allocator, + libcamera::Stream *stream); + +#endif /* __GST_LIBCAMERA_ALLOCATOR_H__ */ diff --git a/src/gstreamer/gstlibcamerapool.cpp b/src/gstreamer/gstlibcamerapool.cpp new file mode 100644 index 0000000..ee106a7 --- /dev/null +++ b/src/gstreamer/gstlibcamerapool.cpp @@ -0,0 +1,109 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2020, Collabora Ltd. + * Author: Nicolas Dufresne <nicolas.dufresne@collabora.com> + * + * gstlibcamerapool.cpp - GStreamer Buffer Pool + */ + +#include "gstlibcamera-utils.h" +#include "gstlibcamerapool.h" + +#include <libcamera/stream.h> + +using namespace libcamera; + +struct _GstLibcameraPool { + GstBufferPool parent; + + GstAtomicQueue *queue; + GstLibcameraAllocator *allocator; + Stream *stream; +}; + +G_DEFINE_TYPE(GstLibcameraPool, gst_libcamera_pool, GST_TYPE_BUFFER_POOL); + +static GstFlowReturn +gst_libcamera_pool_acquire_buffer(GstBufferPool *pool, GstBuffer **buffer, + GstBufferPoolAcquireParams *params) +{ + GstLibcameraPool *self = GST_LIBCAMERA_POOL(pool); + GstBuffer *buf = GST_BUFFER(gst_atomic_queue_pop(self->queue)); + if (!buf) + return GST_FLOW_ERROR; + + if (!gst_libcamera_allocator_prepare_buffer(self->allocator, self->stream, buf)) + return GST_FLOW_ERROR; + + *buffer = buf; + return GST_FLOW_OK; +} + +static void +gst_libcamera_pool_reset_buffer(GstBufferPool *pool, GstBuffer *buffer) +{ + GstBufferPoolClass *klass = GST_BUFFER_POOL_CLASS(gst_libcamera_pool_parent_class); + + /* Clears all the memories and only pool the GstBuffer objects */ + gst_buffer_remove_all_memory(buffer); + klass->reset_buffer(pool, buffer); + GST_BUFFER_FLAGS(buffer) = 0; +} + +static void +gst_libcamera_pool_release_buffer(GstBufferPool *pool, GstBuffer *buffer) +{ + GstLibcameraPool *self = GST_LIBCAMERA_POOL(pool); + gst_atomic_queue_push(self->queue, buffer); +} + +static void +gst_libcamera_pool_init(GstLibcameraPool *self) +{ + self->queue = gst_atomic_queue_new(4); +} + +static void +gst_libcamera_pool_finalize(GObject *object) +{ + GstLibcameraPool *self = GST_LIBCAMERA_POOL(object); + GstBuffer *buf; + + while ((buf = GST_BUFFER(gst_atomic_queue_pop(self->queue)))) + gst_buffer_unref(buf); + + gst_atomic_queue_unref(self->queue); + g_object_unref(self->allocator); + + G_OBJECT_CLASS(gst_libcamera_pool_parent_class)->finalize(object); +} + +static void +gst_libcamera_pool_class_init(GstLibcameraPoolClass *klass) +{ + auto *object_class = G_OBJECT_CLASS(klass); + auto *pool_class = GST_BUFFER_POOL_CLASS(klass); + + object_class->finalize = gst_libcamera_pool_finalize; + pool_class->start = nullptr; + pool_class->acquire_buffer = gst_libcamera_pool_acquire_buffer; + pool_class->reset_buffer = gst_libcamera_pool_reset_buffer; + pool_class->release_buffer = gst_libcamera_pool_release_buffer; +} + +GstLibcameraPool * +gst_libcamera_pool_new(GstLibcameraAllocator *allocator, Stream *stream) +{ + auto *pool = GST_LIBCAMERA_POOL(g_object_new(GST_TYPE_LIBCAMERA_POOL, nullptr)); + + pool->allocator = GST_LIBCAMERA_ALLOCATOR(g_object_ref(allocator)); + pool->stream = stream; + + gsize pool_size = gst_libcamera_allocator_get_pool_size(allocator, stream); + for (gsize i = 0; i < pool_size; i++) { + GstBuffer *buffer = gst_buffer_new(); + gst_atomic_queue_push(pool->queue, buffer); + } + + return pool; +} diff --git a/src/gstreamer/gstlibcamerapool.h b/src/gstreamer/gstlibcamerapool.h new file mode 100644 index 0000000..a764c75 --- /dev/null +++ b/src/gstreamer/gstlibcamerapool.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2020, Collabora Ltd. + * Author: Nicolas Dufresne <nicolas.dufresne@collabora.com> + * + * gstlibcamerapool.h - GStreamer Buffer Pool + * + * This is a partial implementation of GstBufferPool intended for internal use + * only. This pool cannot be configured or activated. + */ + +#ifndef __GST_LIBCAMERA_POOL_H__ +#define __GST_LIBCAMERA_POOL_H__ + +#include <gst/gst.h> +#include <libcamera/stream.h> + +#include "gstlibcameraallocator.h" + +#define GST_TYPE_LIBCAMERA_POOL gst_libcamera_pool_get_type() +G_DECLARE_FINAL_TYPE(GstLibcameraPool, gst_libcamera_pool, GST_LIBCAMERA, POOL, GstBufferPool) + +GstLibcameraPool *gst_libcamera_pool_new(GstLibcameraAllocator *allocator, + libcamera::Stream *stream); + +#endif /* __GST_LIBCAMERA_POOL_H__ */ diff --git a/src/gstreamer/meson.build b/src/gstreamer/meson.build index 1c4a2e3..90773af 100644 --- a/src/gstreamer/meson.build +++ b/src/gstreamer/meson.build @@ -1,7 +1,9 @@ libcamera_gst_sources = [ 'gstlibcamera-utils.cpp', 'gstlibcamera.c', + 'gstlibcameraallocator.cpp', 'gstlibcamerapad.cpp', + 'gstlibcamerapool.cpp', 'gstlibcameraprovider.cpp', 'gstlibcamerasrc.cpp', ] @@ -11,14 +13,17 @@ libcamera_gst_c_args = [ '-DPACKAGE="@0@"'.format(meson.project_name()), ] -gst_dep = dependency('gstreamer-video-1.0', version : '>=1.16.1', - required : get_option('gstreamer')) +gst_req = '>=1.16.1' +gstvideo_dep = dependency('gstreamer-video-1.0', version : gst_req, + required : get_option('gstreamer')) +gstallocator_dep = dependency('gstreamer-allocators-1.0', version : gst_req, + required : get_option('gstreamer')) -if gst_dep.found() +if gstvideo_dep.found() and gstallocator_dep.found() libcamera_gst = shared_library('gstlibcamera', libcamera_gst_sources, c_args : libcamera_gst_c_args, - dependencies : [libcamera_dep, gst_dep], + dependencies : [libcamera_dep, gstvideo_dep, gstallocator_dep], install: true, install_dir : '@0@/gstreamer-1.0'.format(get_option('libdir')), )
This is needed to track the lifetime of the FrameBufferAllocator in relation to the GstBuffer/GstMemory objects travelling inside GStreamer. Signed-off-by: Nicolas Dufresne <nicolas.dufresne@collabora.com> --- src/gstreamer/gstlibcameraallocator.cpp | 244 ++++++++++++++++++++++++ src/gstreamer/gstlibcameraallocator.h | 29 +++ src/gstreamer/gstlibcamerapool.cpp | 109 +++++++++++ src/gstreamer/gstlibcamerapool.h | 26 +++ src/gstreamer/meson.build | 13 +- 5 files changed, 417 insertions(+), 4 deletions(-) create mode 100644 src/gstreamer/gstlibcameraallocator.cpp create mode 100644 src/gstreamer/gstlibcameraallocator.h create mode 100644 src/gstreamer/gstlibcamerapool.cpp create mode 100644 src/gstreamer/gstlibcamerapool.h