[libcamera-devel] [PATCH v2 17/27] gst: Add a pool and an allocator implementation
Laurent Pinchart
laurent.pinchart at ideasonboard.com
Sat Feb 29 15:28:01 CET 2020
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 at 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 at 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 at 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 at 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 at 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')),
> )
--
Regards,
Laurent Pinchart
More information about the libcamera-devel
mailing list