@@ -6,13 +6,19 @@
* gstlibcamera.c - GStreamer plugin
*/
+#include "gstlibcameraprovider.h"
#include "gstlibcamerasrc.h"
static gboolean
plugin_init(GstPlugin *plugin)
{
- return gst_element_register(plugin, "libcamerasrc", GST_RANK_PRIMARY,
- GST_TYPE_LIBCAMERA_SRC);
+ if (!gst_element_register(plugin, "libcamerasrc", GST_RANK_PRIMARY,
+ GST_TYPE_LIBCAMERA_SRC) ||
+ !gst_device_provider_register(plugin, "libcameraprovider",
+ GST_RANK_PRIMARY,
+ GST_TYPE_LIBCAMERA_PROVIDER))
+ return FALSE;
+
return TRUE;
}
new file mode 100644
@@ -0,0 +1,238 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2020, Collabora Ltd.
+ * Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
+ *
+ * gstlibcameraprovider.c - GStreamer Device Provider
+ */
+
+#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_NAME = 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,
+ const GValue *value, GParamSpec *pspec)
+{
+ GstLibcameraDevice *device = GST_LIBCAMERA_DEVICE(object);
+
+ switch (prop_id) {
+ case PROP_DEVICE_NAME:
+ device->name = g_value_dup_string(value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_libcamera_device_init(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_GET_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;
+
+ GParamSpec *pspec = g_param_spec_string("name", "Name",
+ "The name of the camera device", "",
+ (GParamFlags)(G_PARAM_STATIC_STRINGS | G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property(object_class, PROP_DEVICE_NAME, pspec);
+}
+
+static GstDevice *
+gst_libcamera_device_new(const std::shared_ptr<Camera> &camera)
+{
+ g_autoptr(GstCaps) caps = gst_caps_new_empty();
+ const gchar *name = camera->name().c_str();
+ StreamRoles roles;
+
+ roles.push_back(StreamRole::VideoRecording);
+ std::unique_ptr<CameraConfiguration> config = camera->generateConfiguration(roles);
+
+ 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;
+ CameraManager *cm;
+};
+
+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);
+ CameraManager *cm = self->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.
+ */
+ ret = cm->start();
+ 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->name().c_str());
+ devices = g_list_append(devices,
+ g_object_ref_sink(gst_libcamera_device_new(camera)));
+ }
+
+ cm->stop();
+
+ return devices;
+}
+
+static void
+gst_libcamera_provider_init(GstLibcameraProvider *self)
+{
+ GstDeviceProvider *provider = GST_DEVICE_PROVIDER(self);
+
+ self->cm = new CameraManager();
+
+ /* Avoid devices being duplicated. */
+ gst_device_provider_hide_provider(provider, "v4l2deviceprovider");
+}
+
+static void
+gst_libcamera_provider_finalize(GObject *object)
+{
+ GstLibcameraProvider *self = GST_LIBCAMERA_PROVIDER(object);
+ gpointer klass = gst_libcamera_provider_parent_class;
+
+ delete self->cm;
+
+ return G_OBJECT_GET_CLASS(klass)->finalize(object);
+}
+
+static void
+gst_libcamera_provider_class_init(GstLibcameraProviderClass *klass)
+{
+ GstDeviceProviderClass *provider_class = GST_DEVICE_PROVIDER_CLASS(klass);
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+ provider_class->probe = gst_libcamera_provider_probe;
+ object_class->finalize = gst_libcamera_provider_finalize;
+
+ gst_device_provider_class_set_metadata(provider_class,
+ "libcamera Device Provider",
+ "Source/Video",
+ "List camera device using libcamera",
+ "Nicolas Dufresne <nicolas.dufresne@collabora.com>");
+}
new file mode 100644
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2020, Collabora Ltd.
+ * Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
+ *
+ * gstlibcameraprovider.h - GStreamer Device Provider
+ */
+
+#ifndef __GST_LIBCAMERA_PROVIDER_H__
+#define __GST_LIBCAMERA_PROVIDER_H__
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_LIBCAMERA_PROVIDER gst_libcamera_provider_get_type()
+G_DECLARE_FINAL_TYPE(GstLibcameraProvider, gst_libcamera_provider,
+ GST_LIBCAMERA, PROVIDER, GstDeviceProvider)
+
+G_END_DECLS
+
+#endif /* __GST_LIBCAMERA_PROVIDER_H__ */
+
@@ -1,6 +1,7 @@
libcamera_gst_sources = [
'gstlibcamera-utils.cpp',
'gstlibcamera.c',
+ 'gstlibcameraprovider.cpp',
'gstlibcamerasrc.cpp',
]