summaryrefslogtreecommitdiff
path: root/src/android/camera_metadata.h
AgeCommit message (Expand)Author
2021-12-22android: camera_metadata: Add setEntry helperPaul Elder
2021-11-24android: Convert to pragma onceKieran Bingham
2021-10-19android: camera_metadata: Rename get() to getMetadata()Umang Jain
2021-08-02android: metadata: Fix addEntry template typePaul Elder
2021-08-02android: metadata: Add hasEntry and entryContainsPaul Elder
2021-05-26android: camera_metadata: Add functions for instrumenting resizingPaul Elder
2021-05-18android: camera_metadata: Add type sanity check to updateEntry()Laurent Pinchart
2021-05-18android: camera_metadata: Make internal functions privateLaurent Pinchart
2021-05-18android: camera_metadata: Constify argument to addEntry() and updateEntry()Laurent Pinchart
2021-05-18android: camera_metadata: Rename sizeofT argument to elementSizeLaurent Pinchart
2021-05-14android: camera_metadata: Auto-resize CameraMetadataPaul Elder
2021-01-22android: camera_metadata: Add defaul constructorJacopo Mondi
2021-01-22android: camera_metadata: Add copy constructor and getEntryPaul Elder
2020-08-05android: camera_metadata: Add method to update an entryJacopo Mondi
2020-08-05android: camera_metadata: Add const version of get()Jacopo Mondi
2020-08-05android: camera_metadata: Mark isValid() as constJacopo Mondi
2019-09-05android: Add CameraMetadata helper classLaurent Pinchart
' href='#n220'>220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
/* 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_;
}