mirror of
https://git.libcamera.org/libcamera/libcamera.git
synced 2025-07-13 07:19:45 +03:00
`FrameBufferAllocator::allocate()` might return a negative error code, but currently this is handled the same way as success. So instead of continuing, abort the construction of the allocator object. Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
274 lines
7 KiB
C++
274 lines
7 KiB
C++
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
/*
|
|
* Copyright (C) 2020, Collabora Ltd.
|
|
* Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
|
|
*
|
|
* 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.get(),
|
|
plane.offset + plane.length,
|
|
GST_FD_MEMORY_FLAG_DONT_CLOSE);
|
|
gst_memory_resize(mem, plane.offset, plane.length);
|
|
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()
|
|
{
|
|
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;
|
|
/*
|
|
* The camera manager represents the library, which needs to be kept
|
|
* alive until all the memory has been released.
|
|
*/
|
|
std::shared_ptr<CameraManager> *cm_ptr;
|
|
};
|
|
|
|
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;
|
|
|
|
/* Keep last. */
|
|
delete self->cm_ptr;
|
|
|
|
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,
|
|
CameraConfiguration *config_)
|
|
{
|
|
auto *self = GST_LIBCAMERA_ALLOCATOR(g_object_new(GST_TYPE_LIBCAMERA_ALLOCATOR,
|
|
nullptr));
|
|
gint ret;
|
|
|
|
self->cm_ptr = new std::shared_ptr<CameraManager>(gst_libcamera_get_camera_manager(ret));
|
|
if (ret) {
|
|
g_object_unref(self);
|
|
return nullptr;
|
|
}
|
|
|
|
self->fb_allocator = new FrameBufferAllocator(camera);
|
|
for (StreamConfiguration &streamCfg : *config_) {
|
|
Stream *stream = streamCfg.stream();
|
|
|
|
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;
|
|
}
|
|
|
|
FrameBuffer *
|
|
gst_libcamera_memory_get_frame_buffer(GstMemory *mem)
|
|
{
|
|
auto *frame = reinterpret_cast<FrameWrap *>(gst_mini_object_get_qdata(GST_MINI_OBJECT_CAST(mem),
|
|
FrameWrap::getQuark()));
|
|
return frame->buffer_;
|
|
}
|