224 lines
6 KiB
C++
224 lines
6 KiB
C++
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
/*
|
|
* Copyright (C) 2020, Collabora Ltd.
|
|
* Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
|
|
*
|
|
* GStreamer Device Provider
|
|
*/
|
|
|
|
#include <array>
|
|
|
|
#include "gstlibcameraprovider.h"
|
|
|
|
#include <libcamera/camera.h>
|
|
#include <libcamera/camera_manager.h>
|
|
|
|
#include "gstlibcamerasrc.h"
|
|
#include "gstlibcamera-utils.h"
|
|
|
|
using namespace libcamera;
|
|
|
|
GST_DEBUG_CATEGORY_STATIC(provider_debug);
|
|
#define GST_CAT_DEFAULT provider_debug
|
|
|
|
/**
|
|
* \struct _GstLibcameraDevice
|
|
* \brief libcamera GstDevice implementation
|
|
*
|
|
* This object is used by GstLibcameraProvider to abstract a libcamera
|
|
* device. It also provides helpers to create and configure the
|
|
* libcamerasrc GstElement to be used with this device. The implementation is
|
|
* private to the plugin.
|
|
*/
|
|
|
|
enum {
|
|
PROP_DEVICE_ = 1,
|
|
};
|
|
|
|
#define GST_TYPE_LIBCAMERA_DEVICE gst_libcamera_device_get_type()
|
|
G_DECLARE_FINAL_TYPE(GstLibcameraDevice, gst_libcamera_device,
|
|
GST_LIBCAMERA, DEVICE, GstDevice)
|
|
|
|
struct _GstLibcameraDevice {
|
|
GstDevice parent;
|
|
gchar *name;
|
|
};
|
|
|
|
G_DEFINE_TYPE(GstLibcameraDevice, gst_libcamera_device, GST_TYPE_DEVICE)
|
|
|
|
static GstElement *
|
|
gst_libcamera_device_create_element(GstDevice *device, const gchar *name)
|
|
{
|
|
GstElement *source = gst_element_factory_make("libcamerasrc", name);
|
|
|
|
/*
|
|
* Provider and source lives in the same plugin, so making the source
|
|
* should never fail.
|
|
*/
|
|
g_assert(source);
|
|
|
|
g_object_set(source, "camera-name", GST_LIBCAMERA_DEVICE(device)->name, nullptr);
|
|
|
|
return source;
|
|
}
|
|
|
|
static gboolean
|
|
gst_libcamera_device_reconfigure_element(GstDevice *device,
|
|
GstElement *element)
|
|
{
|
|
if (!GST_LIBCAMERA_IS_SRC(element))
|
|
return FALSE;
|
|
|
|
g_object_set(element, "camera-name", GST_LIBCAMERA_DEVICE(device)->name, nullptr);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
gst_libcamera_device_set_property(GObject *object, guint prop_id,
|
|
[[maybe_unused]]const GValue *value, GParamSpec *pspec)
|
|
{
|
|
// GstLibcameraDevice *device = GST_LIBCAMERA_DEVICE(object);
|
|
|
|
switch (prop_id) {
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_libcamera_device_init([[maybe_unused]] GstLibcameraDevice *self)
|
|
{
|
|
}
|
|
|
|
static void
|
|
gst_libcamera_device_finalize(GObject *object)
|
|
{
|
|
GstLibcameraDevice *self = GST_LIBCAMERA_DEVICE(object);
|
|
gpointer klass = gst_libcamera_device_parent_class;
|
|
|
|
g_free(self->name);
|
|
|
|
G_OBJECT_CLASS(klass)->finalize(object);
|
|
}
|
|
|
|
static void
|
|
gst_libcamera_device_class_init(GstLibcameraDeviceClass *klass)
|
|
{
|
|
GstDeviceClass *device_class = GST_DEVICE_CLASS(klass);
|
|
GObjectClass *object_class = G_OBJECT_CLASS(klass);
|
|
|
|
device_class->create_element = gst_libcamera_device_create_element;
|
|
device_class->reconfigure_element = gst_libcamera_device_reconfigure_element;
|
|
|
|
object_class->set_property = gst_libcamera_device_set_property;
|
|
object_class->finalize = gst_libcamera_device_finalize;
|
|
}
|
|
|
|
static GstDevice *
|
|
gst_libcamera_device_new(const std::shared_ptr<Camera> &camera)
|
|
{
|
|
static const std::array roles{ StreamRole::VideoRecording };
|
|
g_autoptr(GstCaps) caps = gst_caps_new_empty();
|
|
const gchar *name = camera->id().c_str();
|
|
|
|
std::unique_ptr<CameraConfiguration> config = camera->generateConfiguration(roles);
|
|
if (!config || config->size() != roles.size()) {
|
|
GST_ERROR("Failed to generate a default configuration for %s", name);
|
|
return nullptr;
|
|
}
|
|
|
|
for (const StreamConfiguration &stream_cfg : *config) {
|
|
GstCaps *sub_caps = gst_libcamera_stream_formats_to_caps(stream_cfg.formats());
|
|
if (sub_caps)
|
|
gst_caps_append(caps, sub_caps);
|
|
}
|
|
|
|
return GST_DEVICE(g_object_new(GST_TYPE_LIBCAMERA_DEVICE,
|
|
/* \todo Use a unique identifier instead of camera name. */
|
|
"name", name,
|
|
"display-name", name,
|
|
"caps", caps,
|
|
"device-class", "Source/Video",
|
|
nullptr));
|
|
}
|
|
|
|
/**
|
|
* \struct _GstLibcameraProvider
|
|
* \brief libcamera GstDeviceProvider implementation
|
|
*
|
|
* This GstFeature is used by GstDeviceMonitor to probe the available
|
|
* libcamera devices. The implementation is private to the plugin.
|
|
*/
|
|
|
|
struct _GstLibcameraProvider {
|
|
GstDeviceProvider parent;
|
|
};
|
|
|
|
G_DEFINE_TYPE_WITH_CODE(GstLibcameraProvider, gst_libcamera_provider,
|
|
GST_TYPE_DEVICE_PROVIDER,
|
|
GST_DEBUG_CATEGORY_INIT(provider_debug, "libcamera-provider", 0,
|
|
"libcamera Device Provider"))
|
|
|
|
static GList *
|
|
gst_libcamera_provider_probe(GstDeviceProvider *provider)
|
|
{
|
|
GstLibcameraProvider *self = GST_LIBCAMERA_PROVIDER(provider);
|
|
std::shared_ptr<CameraManager> cm;
|
|
GList *devices = nullptr;
|
|
gint ret;
|
|
|
|
GST_INFO_OBJECT(self, "Probing cameras using libcamera");
|
|
|
|
/* \todo Move the CameraMananger start()/stop() calls into
|
|
* GstDeviceProvider start()/stop() virtual function when CameraMananger
|
|
* gains monitoring support. Meanwhile we need to cycle start()/stop()
|
|
* to ensure every probe() calls return the latest list.
|
|
*/
|
|
cm = gst_libcamera_get_camera_manager(ret);
|
|
if (ret) {
|
|
GST_ERROR_OBJECT(self, "Failed to retrieve device list: %s",
|
|
g_strerror(-ret));
|
|
return nullptr;
|
|
}
|
|
|
|
for (const std::shared_ptr<Camera> &camera : cm->cameras()) {
|
|
GST_INFO_OBJECT(self, "Found camera '%s'", camera->id().c_str());
|
|
|
|
GstDevice *dev = gst_libcamera_device_new(camera);
|
|
if (!dev) {
|
|
GST_ERROR_OBJECT(self, "Failed to add camera '%s'",
|
|
camera->id().c_str());
|
|
return nullptr;
|
|
}
|
|
|
|
devices = g_list_append(devices,
|
|
g_object_ref_sink(dev));
|
|
}
|
|
|
|
return devices;
|
|
}
|
|
|
|
static void
|
|
gst_libcamera_provider_init(GstLibcameraProvider *self)
|
|
{
|
|
GstDeviceProvider *provider = GST_DEVICE_PROVIDER(self);
|
|
|
|
/* Avoid devices being duplicated. */
|
|
gst_device_provider_hide_provider(provider, "v4l2deviceprovider");
|
|
}
|
|
|
|
static void
|
|
gst_libcamera_provider_class_init(GstLibcameraProviderClass *klass)
|
|
{
|
|
GstDeviceProviderClass *provider_class = GST_DEVICE_PROVIDER_CLASS(klass);
|
|
|
|
provider_class->probe = gst_libcamera_provider_probe;
|
|
|
|
gst_device_provider_class_set_metadata(provider_class,
|
|
"libcamera Device Provider",
|
|
"Source/Video",
|
|
"List camera device using libcamera",
|
|
"Nicolas Dufresne <nicolas.dufresne@collabora.com>");
|
|
}
|