summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Dufresne <nicolas.dufresne@collabora.com>2020-01-25 17:47:17 -0500
committerLaurent Pinchart <laurent.pinchart@ideasonboard.com>2020-03-07 01:58:07 +0200
commitbde275408c7f614ccf6e440bdd747f7b663fafe6 (patch)
treef02635a9338550ba454583678636020793e25bc7
parentd343326d28dd03877dd3d602c5494b5887ac34e5 (diff)
gst: Add a pool and an allocator implementation
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> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
-rw-r--r--src/gstreamer/gstlibcameraallocator.cpp245
-rw-r--r--src/gstreamer/gstlibcameraallocator.h30
-rw-r--r--src/gstreamer/gstlibcamerapool.cpp110
-rw-r--r--src/gstreamer/gstlibcamerapool.h27
-rw-r--r--src/gstreamer/meson.build13
5 files changed, 421 insertions, 4 deletions
diff --git a/src/gstreamer/gstlibcameraallocator.cpp b/src/gstreamer/gstlibcameraallocator.cpp
new file mode 100644
index 00000000..861af596
--- /dev/null
+++ b/src/gstreamer/gstlibcameraallocator.cpp
@@ -0,0 +1,245 @@
+/* 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 <libcamera/camera.h>
+#include <libcamera/framebuffer_allocator.h>
+#include <libcamera/stream.h>
+
+#include "gstlibcamera-utils.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), 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 = GST_LIBCAMERA_ALLOCATOR(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 00000000..b3f7f367
--- /dev/null
+++ b/src/gstreamer/gstlibcameraallocator.h
@@ -0,0 +1,30 @@
+/* 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 00000000..75e189ab
--- /dev/null
+++ b/src/gstreamer/gstlibcamerapool.cpp
@@ -0,0 +1,110 @@
+/* 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 "gstlibcamerapool.h"
+
+#include <libcamera/stream.h>
+
+#include "gstlibcamera-utils.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 00000000..c0a04cfb
--- /dev/null
+++ b/src/gstreamer/gstlibcamerapool.h
@@ -0,0 +1,27 @@
+/* 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 "gstlibcameraallocator.h"
+
+#include <gst/gst.h>
+
+#include <libcamera/stream.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 5324b2f5..1965b504 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',
]
@@ -13,10 +15,13 @@ libcamera_gst_c_args = [
glib_dep = dependency('glib', required : get_option('gstreamer'))
-gst_dep = dependency('gstreamer-video-1.0', version : '>=1.14.0',
- required : get_option('gstreamer'))
+gst_dep_version = '>=1.14.0'
+gstvideo_dep = dependency('gstreamer-video-1.0', version : gst_dep_version,
+ required : get_option('gstreamer'))
+gstallocator_dep = dependency('gstreamer-allocators-1.0', version : gst_dep_version,
+ required : get_option('gstreamer'))
-if glib_dep.found() and gst_dep.found()
+if glib_dep.found() and gstvideo_dep.found() and gstallocator_dep.found()
# The G_DECLARE_FINAL_TYPE macro creates static inline functions that were
# not marked as possibly unused prior to GLib v2.63.0. This causes clang to
# complain about the ones we are not using. Silence the -Wunused-function
@@ -28,7 +33,7 @@ if glib_dep.found() and gst_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')),
)