mirror of
https://git.libcamera.org/libcamera/libcamera.git
synced 2025-07-16 08:55:06 +03:00
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>
This commit is contained in:
parent
d343326d28
commit
bde275408c
5 changed files with 421 additions and 4 deletions
245
src/gstreamer/gstlibcameraallocator.cpp
Normal file
245
src/gstreamer/gstlibcameraallocator.cpp
Normal file
|
@ -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;
|
||||
}
|
30
src/gstreamer/gstlibcameraallocator.h
Normal file
30
src/gstreamer/gstlibcameraallocator.h
Normal file
|
@ -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__ */
|
110
src/gstreamer/gstlibcamerapool.cpp
Normal file
110
src/gstreamer/gstlibcamerapool.cpp
Normal file
|
@ -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;
|
||||
}
|
27
src/gstreamer/gstlibcamerapool.h
Normal file
27
src/gstreamer/gstlibcamerapool.h
Normal file
|
@ -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__ */
|
|
@ -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')),
|
||||
)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue