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)); }