From 053fedb5edd3c997c09506fd4a32cf99a8894d21 Mon Sep 17 00:00:00 2001 From: Umang Jain Date: Tue, 29 Jul 2025 21:09:15 +0530 Subject: [PATCH] gstreamer: Report camera properties as device properties Iterate over all libcamera camera properties and report them as device properties. Each libcamera ControlType is mapped to the corresponding gstreamer GType. If the ControlValue is an array of values (ControlValue::isArray()), GValue with type GST_TYPE_ARRAY is used to set the value of that ControlValue with the corresponding GType. Signed-off-by: Umang Jain Reviewed-by: Nicolas Dufresne Signed-off-by: Kieran Bingham --- src/gstreamer/gstlibcamera-utils.cpp | 197 +++++++++++++++++++++++++ src/gstreamer/gstlibcamera-utils.h | 3 + src/gstreamer/gstlibcameraprovider.cpp | 13 ++ 3 files changed, 213 insertions(+) diff --git a/src/gstreamer/gstlibcamera-utils.cpp b/src/gstreamer/gstlibcamera-utils.cpp index 9d8aa12f..bfb094c9 100644 --- a/src/gstreamer/gstlibcamera-utils.cpp +++ b/src/gstreamer/gstlibcamera-utils.cpp @@ -8,6 +8,8 @@ #include "gstlibcamera-utils.h" +#include + #include #include @@ -328,6 +330,33 @@ bare_structure_from_format(const PixelFormat &format) return nullptr; } +static const struct { + ControlType c_type; + GType g_type; +} control_type_gtype_map[] = { + { ControlTypeBool, G_TYPE_BOOLEAN }, + { ControlTypeByte, G_TYPE_UINT }, + { ControlTypeUnsigned16, G_TYPE_UINT }, + { ControlTypeUnsigned32, G_TYPE_UINT }, + { ControlTypeInteger32, G_TYPE_INT }, + { ControlTypeInteger64, G_TYPE_INT64 }, + { ControlTypeFloat, G_TYPE_FLOAT }, + { ControlTypeString, G_TYPE_STRING }, + { ControlTypeRectangle, GST_TYPE_ARRAY }, + { ControlTypeSize, GST_TYPE_ARRAY }, + { ControlTypePoint, GST_TYPE_ARRAY }, +}; + +static GType +control_type_to_gtype(const ControlType &type) +{ + for (auto &a : control_type_gtype_map) { + if (a.c_type == type) + return a.g_type; + } + return G_TYPE_INVALID; +} + GstCaps * gst_libcamera_stream_formats_to_caps(const StreamFormats &formats) { @@ -701,3 +730,171 @@ gst_libcamera_get_camera_manager(int &ret) return cm; } + +int gst_libcamera_set_structure_field(GstStructure *structure, const ControlId *id, + const ControlValue &value) +{ + std::string prop = "api.libcamera." + id->name(); + g_auto(GValue) v = G_VALUE_INIT; + g_auto(GValue) x = G_VALUE_INIT; + gboolean is_array = value.isArray(); + + GType type = control_type_to_gtype(value.type()); + if (type == G_TYPE_INVALID) + return -EINVAL; + + if (is_array || type == GST_TYPE_ARRAY) + g_value_init(&v, GST_TYPE_ARRAY); + + switch (value.type()) { + case ControlTypeBool: + if (is_array) { + Span data = value.get>(); + for (auto it = data.begin(); it != data.end(); ++it) { + g_value_init(&x, type); + g_value_set_boolean(&x, *it); + gst_value_array_append_and_take_value(&v, &x); + } + } else { + gst_structure_set(structure, prop.c_str(), G_TYPE_BOOLEAN, + value.get(), nullptr); + } + break; + case ControlTypeByte: + if (is_array) { + Span data = value.get>(); + for (auto it = data.begin(); it != data.end(); ++it) { + g_value_init(&x, type); + g_value_set_uint(&x, *it); + gst_value_array_append_and_take_value(&v, &x); + } + } else { + gst_structure_set(structure, prop.c_str(), G_TYPE_UINT, + value.get(), nullptr); + } + break; + case ControlTypeUnsigned16: + if (is_array) { + Span data = value.get>(); + for (auto it = data.begin(); it != data.end(); ++it) { + g_value_init(&x, type); + g_value_set_uint(&x, *it); + gst_value_array_append_and_take_value(&v, &x); + } + } else { + gst_structure_set(structure, prop.c_str(), G_TYPE_UINT, + value.get(), nullptr); + } + break; + case ControlTypeUnsigned32: + if (is_array) { + Span data = value.get>(); + for (auto it = data.begin(); it != data.end(); ++it) { + g_value_init(&x, type); + g_value_set_uint(&x, *it); + gst_value_array_append_and_take_value(&v, &x); + } + } else { + gst_structure_set(structure, prop.c_str(), G_TYPE_UINT, + value.get(), nullptr); + } + break; + case ControlTypeInteger32: + if (is_array) { + Span data = value.get>(); + for (auto it = data.begin(); it != data.end(); ++it) { + g_value_init(&x, type); + g_value_set_int(&x, *it); + gst_value_array_append_and_take_value(&v, &x); + } + } else { + if (!id->enumerators().empty()) { + int32_t val = value.get(); + const auto &iter = id->enumerators().find(val); + if (iter != id->enumerators().end()) { + gst_structure_set(structure, prop.c_str(), + G_TYPE_STRING, + iter->second.c_str(), + nullptr); + } else { + return -EINVAL; + } + } else { + gst_structure_set(structure, prop.c_str(), G_TYPE_INT, + value.get(), nullptr); + } + } + break; + case ControlTypeInteger64: + if (is_array) { + Span data = value.get>(); + for (auto it = data.begin(); it != data.end(); ++it) { + g_value_init(&x, type); + g_value_set_int64(&x, *it); + gst_value_array_append_and_take_value(&v, &x); + } + } else { + gst_structure_set(structure, prop.c_str(), G_TYPE_INT64, + value.get(), nullptr); + } + break; + case ControlTypeFloat: + if (is_array) { + Span data = value.get>(); + for (auto it = data.begin(); it != data.end(); ++it) { + g_value_init(&x, type); + g_value_set_float(&x, *it); + gst_value_array_append_and_take_value(&v, &x); + } + } else { + gst_structure_set(structure, prop.c_str(), G_TYPE_FLOAT, + value.get(), nullptr); + } + break; + case ControlTypeString: + /* + * isArray() is always true for strings hence, unset the GValue + * array because we are going to the toString() helper directly. + */ + g_value_unset(&v); + gst_structure_set(structure, prop.c_str(), G_TYPE_STRING, + value.toString().c_str(), nullptr); + break; + case ControlTypeSize: + if (is_array) { + Span data = value.get>(); + for (auto it = data.begin(); it != data.end(); ++it) + gst_libcamera_gvalue_set_size(&v, *it); + } else { + gst_libcamera_gvalue_set_size(&v, value.get()); + } + break; + case ControlTypePoint: + if (is_array) { + Span data = value.get>(); + for (auto it = data.begin(); it != data.end(); ++it) + gst_libcamera_gvalue_set_point(&v, *it); + } else { + gst_libcamera_gvalue_set_point(&v, value.get()); + } + break; + case ControlTypeRectangle: + if (is_array) { + Span data = value.get>(); + for (auto it = data.begin(); it != data.end(); ++it) + gst_libcamera_gvalue_set_rectangle(&v, *it); + } else { + gst_libcamera_gvalue_set_rectangle(&v, value.get()); + } + break; + case ControlTypeNone: + [[fallthrough]]; + default: + return -EINVAL; + } + + if (GST_VALUE_HOLDS_ARRAY(&v)) + gst_structure_set_value(structure, prop.c_str(), &v); + + return 0; +} diff --git a/src/gstreamer/gstlibcamera-utils.h b/src/gstreamer/gstlibcamera-utils.h index 1812be75..35df56fb 100644 --- a/src/gstreamer/gstlibcamera-utils.h +++ b/src/gstreamer/gstlibcamera-utils.h @@ -29,6 +29,9 @@ void gst_libcamera_gvalue_set_point(GValue *value, const libcamera::Point &point void gst_libcamera_gvalue_set_size(GValue *value, const libcamera::Size &size); void gst_libcamera_gvalue_set_rectangle(GValue *value, const libcamera::Rectangle &rect); libcamera::Rectangle gst_libcamera_gvalue_get_rectangle(const GValue *value); +int gst_libcamera_set_structure_field(GstStructure *structure, + const libcamera::ControlId *id, + const libcamera::ControlValue &value); #if !GST_CHECK_VERSION(1, 16, 0) static inline void gst_clear_event(GstEvent **event_ptr) diff --git a/src/gstreamer/gstlibcameraprovider.cpp b/src/gstreamer/gstlibcameraprovider.cpp index 5da96ea3..1545c856 100644 --- a/src/gstreamer/gstlibcameraprovider.cpp +++ b/src/gstreamer/gstlibcameraprovider.cpp @@ -12,6 +12,7 @@ #include #include +#include #include "gstlibcamerasrc.h" #include "gstlibcamera-utils.h" @@ -144,12 +145,24 @@ gst_libcamera_device_new(const std::shared_ptr &camera) gst_caps_append(caps, sub_caps); } + g_autoptr(GstStructure) props = gst_structure_new_empty("camera-properties"); + for (const auto &[key, value] : camera->properties()) { + const ControlId *id = properties::properties.at(key); + + int ret = gst_libcamera_set_structure_field(props, id, value); + if (ret < 0) { + GST_ERROR("Failed to retrieve value for %s property", id->name().c_str()); + return nullptr; + } + } + 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", + "properties", props, nullptr)); }