Message ID | 20250724133343.353044-4-uajain@igalia.com |
---|---|
State | Superseded |
Headers | show |
Series |
|
Related | show |
Le jeudi 24 juillet 2025 à 19:03 +0530, Umang Jain a écrit : > 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. > Cosmetic, but the line width seems to very more then needed. > > Signed-off-by: Umang Jain <uajain@igalia.com> > --- > src/gstreamer/gstlibcamera-utils.cpp | 199 +++++++++++++++++++++++++ > src/gstreamer/gstlibcamera-utils.h | 3 + > src/gstreamer/gstlibcameraprovider.cpp | 16 ++ > 3 files changed, 218 insertions(+) > > diff --git a/src/gstreamer/gstlibcamera-utils.cpp b/src/gstreamer/gstlibcamera-utils.cpp > index 63e00fe0..3d728cbc 100644 > --- a/src/gstreamer/gstlibcamera-utils.cpp > +++ b/src/gstreamer/gstlibcamera-utils.cpp > @@ -333,6 +333,33 @@ bare_structure_from_format(const PixelFormat &format) > } > } > > +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_LONG }, > + { 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) > { > @@ -706,3 +733,175 @@ gst_libcamera_get_camera_manager(int &ret) > > return cm; > } > + > +int gst_libcamera_set_structure_field(GstStructure *structure, GString *field, > + const ControlId *id, const ControlValue &value) > +{ > + GValue v = G_VALUE_INIT; > + 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); > + g_value_init(&x, type); > + } > + > + switch (value.type()) { > + case ControlTypeBool: > + if (is_array) { > + Span<const bool> data = value.get<Span<const bool>>(); > + for (auto it = data.begin(); it != data.end(); ++it) { > + g_value_set_boolean(&x, *it); > + gst_value_array_append_and_take_value(&v, &x); > + g_value_init(&x, type); > + } > + } else { > + gst_structure_set(structure, field->str, G_TYPE_BOOLEAN, > + value.get<const bool>(), nullptr); > + } > + break; > + case ControlTypeByte: > + if (is_array) { > + Span<const uint8_t> data = value.get<Span<const uint8_t>>(); > + for (auto it = data.begin(); it != data.end(); ++it) { > + g_value_set_uint(&x, *it); > + gst_value_array_append_and_take_value(&v, &x); > + g_value_init(&x, type); > + } > + } else { > + gst_structure_set(structure, field->str, G_TYPE_UINT, > + value.get<const uint8_t>(), nullptr); > + } > + break; > + case ControlTypeUnsigned16: > + if (is_array) { > + Span<const uint16_t> data = value.get<Span<const uint16_t>>(); > + for (auto it = data.begin(); it != data.end(); ++it) { > + g_value_set_uint(&x, *it); > + gst_value_array_append_and_take_value(&v, &x); > + g_value_init(&x, type); > + } > + } else { > + gst_structure_set(structure, field->str, G_TYPE_UINT, > + value.get<const uint16_t>(), nullptr); > + } > + break; > + case ControlTypeUnsigned32: > + if (is_array) { > + Span<const uint32_t> data = value.get<Span<const uint32_t>>(); > + for (auto it = data.begin(); it != data.end(); ++it) { > + g_value_set_ulong(&x, *it); You don't really need a long do you ? > + gst_value_array_append_and_take_value(&v, &x); > + g_value_init(&x, type); > + } > + } else { > + gst_structure_set(structure, field->str, G_TYPE_ULONG, > + value.get<const uint32_t>(), nullptr); > + } > + break; > + case ControlTypeInteger32: > + if (is_array) { > + Span<const int32_t> data = value.get<Span<const int32_t>>(); > + for (auto it = data.begin(); it != data.end(); ++it) { > + g_value_set_long(&x, *it); > + gst_value_array_append_and_take_value(&v, &x); > + g_value_init(&x, type); > + } > + } else { > + if (!id->enumerators().empty()) { > + int32_t val = value.get<int32_t>(); > + const auto &iter = id->enumerators().find(val); > + if (iter != id->enumerators().end()) { > + gst_structure_set(structure, field->str, > + G_TYPE_STRING, > + iter->second.c_str(), > + nullptr); > + } else { > + return -EINVAL; > + } > + } else { > + gst_structure_set(structure, field->str, G_TYPE_LONG, > + value.get<const int32_t>(), nullptr); > + } > + } > + break; > + case ControlTypeInteger64: > + if (is_array) { > + Span<const int64_t> data = value.get<Span<const int64_t>>(); > + for (auto it = data.begin(); it != data.end(); ++it) { > + g_value_set_int64(&x, *it); > + gst_value_array_append_and_take_value(&v, &x); > + g_value_init(&x, type); > + } > + } else { > + gst_structure_set(structure, field->str, G_TYPE_INT64, > + value.get<const int64_t>(), nullptr); > + } > + break; > + case ControlTypeFloat: > + if (is_array) { > + Span<const float> data = value.get<Span<const float>>(); > + for (auto it = data.begin(); it != data.end(); ++it) { > + g_value_set_float(&x, *it); > + gst_value_array_append_and_take_value(&v, &x); > + g_value_init(&x, type); > + } > + } else { > + gst_structure_set(structure, field->str, G_TYPE_FLOAT, > + value.get<const float>(), nullptr); > + } > + break; > + case ControlTypeString: Why not using GST_TYPE_ARRAY too ? Otherwise, unset 'v'. > + gst_structure_set(structure, field->str, G_TYPE_STRING, > + value.toString().c_str(), nullptr); > + break; > + case ControlTypeSize: > + if (is_array) { > + Span<const Size> data = value.get<Span<const Size>>(); > + 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<const Size>()); > + } > + break; > + case ControlTypePoint: > + if (is_array) { > + Span<const Point> data = value.get<Span<const Point>>(); > + 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<const Point>()); > + } > + break; > + case ControlTypeRectangle: > + if (is_array) { > + Span<const Rectangle> data = value.get<Span<const Rectangle>>(); > + 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<const Rectangle>()); > + } > + break; > + case ControlTypeNone: > + [[fallthrough]]; > + default: > + return -EINVAL; > + } > + > + /* > + * Set values for array types with the only exception of strings which > + * is handled by ControlValue::toString() helper directly above. > + */ > + if ((is_array || type == GST_TYPE_ARRAY) && > + (value.type() != ControlTypeString)) This would be simpler no ? if (GST_VALUE_HOLDS_ARRAY(v) > + gst_structure_set_value(structure, field->str, &v); > + > + g_value_unset(&v); > + g_value_unset(&x); > + > + return 0; > +} > diff --git a/src/gstreamer/gstlibcamera-utils.h b/src/gstreamer/gstlibcamera-utils.h > index 1812be75..53ad6911 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, GString *field, > + 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..06501fd8 100644 > --- a/src/gstreamer/gstlibcameraprovider.cpp > +++ b/src/gstreamer/gstlibcameraprovider.cpp > @@ -12,6 +12,7 @@ > > #include <libcamera/camera.h> > #include <libcamera/camera_manager.h> > +#include <libcamera/property_ids.h> > > #include "gstlibcamerasrc.h" > #include "gstlibcamera-utils.h" > @@ -144,12 +145,27 @@ gst_libcamera_device_new(const std::shared_ptr<Camera> &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); > + > + g_autoptr(GString) prop_str = g_string_new("api.libcamera."); > + g_string_append(prop_str, id->name().c_str()); Wouldn't this be nicer using C++ ? > + > + int ret = gst_libcamera_set_structure_field(props, prop_str, 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)); > } >
On Mon, Jul 28, 2025 at 07:07:09PM -0400, Nicolas Dufresne wrote: > Le jeudi 24 juillet 2025 à 19:03 +0530, Umang Jain a écrit : > > 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. > > > > Cosmetic, but the line width seems to very more then needed. > > > > > Signed-off-by: Umang Jain <uajain@igalia.com> > > --- > > src/gstreamer/gstlibcamera-utils.cpp | 199 +++++++++++++++++++++++++ > > src/gstreamer/gstlibcamera-utils.h | 3 + > > src/gstreamer/gstlibcameraprovider.cpp | 16 ++ > > 3 files changed, 218 insertions(+) > > > > diff --git a/src/gstreamer/gstlibcamera-utils.cpp b/src/gstreamer/gstlibcamera-utils.cpp > > index 63e00fe0..3d728cbc 100644 > > --- a/src/gstreamer/gstlibcamera-utils.cpp > > +++ b/src/gstreamer/gstlibcamera-utils.cpp > > @@ -333,6 +333,33 @@ bare_structure_from_format(const PixelFormat &format) > > } > > } > > > > +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_LONG }, > > + { 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) > > { > > @@ -706,3 +733,175 @@ gst_libcamera_get_camera_manager(int &ret) > > > > return cm; > > } > > + > > +int gst_libcamera_set_structure_field(GstStructure *structure, GString *field, > > + const ControlId *id, const ControlValue &value) > > +{ > > + GValue v = G_VALUE_INIT; > > + 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); > > + g_value_init(&x, type); > > + } > > + > > + switch (value.type()) { > > + case ControlTypeBool: > > + if (is_array) { > > + Span<const bool> data = value.get<Span<const bool>>(); > > + for (auto it = data.begin(); it != data.end(); ++it) { > > + g_value_set_boolean(&x, *it); > > + gst_value_array_append_and_take_value(&v, &x); > > + g_value_init(&x, type); > > + } > > + } else { > > + gst_structure_set(structure, field->str, G_TYPE_BOOLEAN, > > + value.get<const bool>(), nullptr); > > + } > > + break; > > + case ControlTypeByte: > > + if (is_array) { > > + Span<const uint8_t> data = value.get<Span<const uint8_t>>(); > > + for (auto it = data.begin(); it != data.end(); ++it) { > > + g_value_set_uint(&x, *it); > > + gst_value_array_append_and_take_value(&v, &x); > > + g_value_init(&x, type); > > + } > > + } else { > > + gst_structure_set(structure, field->str, G_TYPE_UINT, > > + value.get<const uint8_t>(), nullptr); > > + } > > + break; > > + case ControlTypeUnsigned16: > > + if (is_array) { > > + Span<const uint16_t> data = value.get<Span<const uint16_t>>(); > > + for (auto it = data.begin(); it != data.end(); ++it) { > > + g_value_set_uint(&x, *it); > > + gst_value_array_append_and_take_value(&v, &x); > > + g_value_init(&x, type); > > + } > > + } else { > > + gst_structure_set(structure, field->str, G_TYPE_UINT, > > + value.get<const uint16_t>(), nullptr); > > + } > > + break; > > + case ControlTypeUnsigned32: > > + if (is_array) { > > + Span<const uint32_t> data = value.get<Span<const uint32_t>>(); > > + for (auto it = data.begin(); it != data.end(); ++it) { > > + g_value_set_ulong(&x, *it); > > You don't really need a long do you ? No? Should it be set to uint? I initially thought of it, but I saw GLib provides long types, I decided to use it pedantically. I also realised in type_gtype_map[] I have set ControlTypeUnsigned32 to G_TYPE_UINT; whereas for consistency I should have used G_TYPE_ULONG. > > > + gst_value_array_append_and_take_value(&v, &x); > > + g_value_init(&x, type); > > + } > > + } else { > > + gst_structure_set(structure, field->str, G_TYPE_ULONG, > > + value.get<const uint32_t>(), nullptr); > > + } > > + break; > > + case ControlTypeInteger32: > > + if (is_array) { > > + Span<const int32_t> data = value.get<Span<const int32_t>>(); > > + for (auto it = data.begin(); it != data.end(); ++it) { > > + g_value_set_long(&x, *it); > > + gst_value_array_append_and_take_value(&v, &x); > > + g_value_init(&x, type); > > + } > > + } else { > > + if (!id->enumerators().empty()) { > > + int32_t val = value.get<int32_t>(); > > + const auto &iter = id->enumerators().find(val); > > + if (iter != id->enumerators().end()) { > > + gst_structure_set(structure, field->str, > > + G_TYPE_STRING, > > + iter->second.c_str(), > > + nullptr); > > + } else { > > + return -EINVAL; > > + } > > + } else { > > + gst_structure_set(structure, field->str, G_TYPE_LONG, > > + value.get<const int32_t>(), nullptr); > > + } > > + } > > + break; > > + case ControlTypeInteger64: > > + if (is_array) { > > + Span<const int64_t> data = value.get<Span<const int64_t>>(); > > + for (auto it = data.begin(); it != data.end(); ++it) { > > + g_value_set_int64(&x, *it); > > + gst_value_array_append_and_take_value(&v, &x); > > + g_value_init(&x, type); > > + } > > + } else { > > + gst_structure_set(structure, field->str, G_TYPE_INT64, > > + value.get<const int64_t>(), nullptr); > > + } > > + break; > > + case ControlTypeFloat: > > + if (is_array) { > > + Span<const float> data = value.get<Span<const float>>(); > > + for (auto it = data.begin(); it != data.end(); ++it) { > > + g_value_set_float(&x, *it); > > + gst_value_array_append_and_take_value(&v, &x); > > + g_value_init(&x, type); > > + } > > + } else { > > + gst_structure_set(structure, field->str, G_TYPE_FLOAT, > > + value.get<const float>(), nullptr); > > + } > > + break; > > + case ControlTypeString: > > Why not using GST_TYPE_ARRAY too ? Otherwise, unset 'v'. ControlTypeString is always an array of chars. It would simply split off each char if I use GST_TYPE_ARRAY here. Directly using string helpers from libcamera are helpful here for ControlTypeString. Regarding unsetting v, I preferred to it at end of the function, near to return; Does g_autoptr() works for GValue? I'll probably experiment this for RAII style cleanup. > > > + gst_structure_set(structure, field->str, G_TYPE_STRING, > > + value.toString().c_str(), nullptr); > > + break; > > + case ControlTypeSize: > > + if (is_array) { > > + Span<const Size> data = value.get<Span<const Size>>(); > > + 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<const Size>()); > > + } > > + break; > > + case ControlTypePoint: > > + if (is_array) { > > + Span<const Point> data = value.get<Span<const Point>>(); > > + 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<const Point>()); > > + } > > + break; > > + case ControlTypeRectangle: > > + if (is_array) { > > + Span<const Rectangle> data = value.get<Span<const Rectangle>>(); > > + 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<const Rectangle>()); > > + } > > + break; > > + case ControlTypeNone: > > + [[fallthrough]]; > > + default: > > + return -EINVAL; > > + } > > + > > + /* > > + * Set values for array types with the only exception of strings which > > + * is handled by ControlValue::toString() helper directly above. > > + */ > > + if ((is_array || type == GST_TYPE_ARRAY) && > > + (value.type() != ControlTypeString)) > > This would be simpler no ? > > if (GST_VALUE_HOLDS_ARRAY(v) > > > + gst_structure_set_value(structure, field->str, &v); > > + > > + g_value_unset(&v); > > + g_value_unset(&x); > > + > > + return 0; > > +} > > diff --git a/src/gstreamer/gstlibcamera-utils.h b/src/gstreamer/gstlibcamera-utils.h > > index 1812be75..53ad6911 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, GString *field, > > + 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..06501fd8 100644 > > --- a/src/gstreamer/gstlibcameraprovider.cpp > > +++ b/src/gstreamer/gstlibcameraprovider.cpp > > @@ -12,6 +12,7 @@ > > > > #include <libcamera/camera.h> > > #include <libcamera/camera_manager.h> > > +#include <libcamera/property_ids.h> > > > > #include "gstlibcamerasrc.h" > > #include "gstlibcamera-utils.h" > > @@ -144,12 +145,27 @@ gst_libcamera_device_new(const std::shared_ptr<Camera> &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); > > + > > + g_autoptr(GString) prop_str = g_string_new("api.libcamera."); > > + g_string_append(prop_str, id->name().c_str()); > > Wouldn't this be nicer using C++ ? > > > + > > + int ret = gst_libcamera_set_structure_field(props, prop_str, 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)); > > } > >
Le mardi 29 juillet 2025 à 12:03 +0530, Umang Jain a écrit : > On Mon, Jul 28, 2025 at 07:07:09PM -0400, Nicolas Dufresne wrote: > > Le jeudi 24 juillet 2025 à 19:03 +0530, Umang Jain a écrit : > > > 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. > > > > > > > Cosmetic, but the line width seems to very more then needed. > > > > > > > > Signed-off-by: Umang Jain <uajain@igalia.com> > > > --- > > > src/gstreamer/gstlibcamera-utils.cpp | 199 +++++++++++++++++++++++++ > > > src/gstreamer/gstlibcamera-utils.h | 3 + > > > src/gstreamer/gstlibcameraprovider.cpp | 16 ++ > > > 3 files changed, 218 insertions(+) > > > > > > diff --git a/src/gstreamer/gstlibcamera-utils.cpp > > > b/src/gstreamer/gstlibcamera-utils.cpp > > > index 63e00fe0..3d728cbc 100644 > > > --- a/src/gstreamer/gstlibcamera-utils.cpp > > > +++ b/src/gstreamer/gstlibcamera-utils.cpp > > > @@ -333,6 +333,33 @@ bare_structure_from_format(const PixelFormat &format) > > > } > > > } > > > > > > +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_LONG }, > > > + { 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) > > > { > > > @@ -706,3 +733,175 @@ gst_libcamera_get_camera_manager(int &ret) > > > > > > return cm; > > > } > > > + > > > +int gst_libcamera_set_structure_field(GstStructure *structure, GString > > > *field, > > > + const ControlId *id, const > > > ControlValue &value) > > > +{ > > > + GValue v = G_VALUE_INIT; > > > + 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); > > > + g_value_init(&x, type); > > > + } > > > + > > > + switch (value.type()) { > > > + case ControlTypeBool: > > > + if (is_array) { > > > + Span<const bool> data = value.get<Span<const > > > bool>>(); > > > + for (auto it = data.begin(); it != data.end(); > > > ++it) { > > > + g_value_set_boolean(&x, *it); > > > + gst_value_array_append_and_take_value(&v, > > > &x); > > > + g_value_init(&x, type); > > > + } > > > + } else { > > > + gst_structure_set(structure, field->str, > > > G_TYPE_BOOLEAN, > > > + value.get<const bool>(), > > > nullptr); > > > + } > > > + break; > > > + case ControlTypeByte: > > > + if (is_array) { > > > + Span<const uint8_t> data = value.get<Span<const > > > uint8_t>>(); > > > + for (auto it = data.begin(); it != data.end(); > > > ++it) { > > > + g_value_set_uint(&x, *it); > > > + gst_value_array_append_and_take_value(&v, > > > &x); > > > + g_value_init(&x, type); > > > + } > > > + } else { > > > + gst_structure_set(structure, field->str, > > > G_TYPE_UINT, > > > + value.get<const uint8_t>(), > > > nullptr); > > > + } > > > + break; > > > + case ControlTypeUnsigned16: > > > + if (is_array) { > > > + Span<const uint16_t> data = value.get<Span<const > > > uint16_t>>(); > > > + for (auto it = data.begin(); it != data.end(); > > > ++it) { > > > + g_value_set_uint(&x, *it); > > > + gst_value_array_append_and_take_value(&v, > > > &x); > > > + g_value_init(&x, type); > > > + } > > > + } else { > > > + gst_structure_set(structure, field->str, > > > G_TYPE_UINT, > > > + value.get<const uint16_t>(), > > > nullptr); > > > + } > > > + break; > > > + case ControlTypeUnsigned32: > > > + if (is_array) { > > > + Span<const uint32_t> data = value.get<Span<const > > > uint32_t>>(); > > > + for (auto it = data.begin(); it != data.end(); > > > ++it) { > > > + g_value_set_ulong(&x, *it); > > > > You don't really need a long do you ? > > > No? Should it be set to uint? I initially thought of it, but I saw GLib > provides long types, I decided to use it pedantically. > > I also realised in type_gtype_map[] I have set ControlTypeUnsigned32 to > G_TYPE_UINT; whereas for consistency I should have used G_TYPE_ULONG. Typically, you want to use unsigned long for cases where you need the integers to match the size of a pointer. For all supported platform, uint32 fits into uint, and its size won't depends on the pointer size. (glib have no support for anything that is not 32 or 64 bits) > > > > > > + gst_value_array_append_and_take_value(&v, > > > &x); > > > + g_value_init(&x, type); > > > + } > > > + } else { > > > + gst_structure_set(structure, field->str, > > > G_TYPE_ULONG, > > > + value.get<const uint32_t>(), > > > nullptr); > > > + } > > > + break; > > > + case ControlTypeInteger32: > > > + if (is_array) { > > > + Span<const int32_t> data = value.get<Span<const > > > int32_t>>(); > > > + for (auto it = data.begin(); it != data.end(); > > > ++it) { > > > + g_value_set_long(&x, *it); > > > + gst_value_array_append_and_take_value(&v, > > > &x); > > > + g_value_init(&x, type); > > > + } > > > + } else { > > > + if (!id->enumerators().empty()) { > > > + int32_t val = value.get<int32_t>(); > > > + const auto &iter = id- > > > >enumerators().find(val); > > > + if (iter != id->enumerators().end()) { > > > + gst_structure_set(structure, > > > field->str, > > > + G_TYPE_STRING, > > > + iter- > > > >second.c_str(), > > > + nullptr); > > > + } else { > > > + return -EINVAL; > > > + } > > > + } else { > > > + gst_structure_set(structure, field->str, > > > G_TYPE_LONG, > > > + value.get<const > > > int32_t>(), nullptr); > > > + } > > > + } > > > + break; > > > + case ControlTypeInteger64: > > > + if (is_array) { > > > + Span<const int64_t> data = value.get<Span<const > > > int64_t>>(); > > > + for (auto it = data.begin(); it != data.end(); > > > ++it) { > > > + g_value_set_int64(&x, *it); > > > + gst_value_array_append_and_take_value(&v, > > > &x); > > > + g_value_init(&x, type); > > > + } > > > + } else { > > > + gst_structure_set(structure, field->str, > > > G_TYPE_INT64, > > > + value.get<const int64_t>(), > > > nullptr); > > > + } > > > + break; > > > + case ControlTypeFloat: > > > + if (is_array) { > > > + Span<const float> data = value.get<Span<const > > > float>>(); > > > + for (auto it = data.begin(); it != data.end(); > > > ++it) { > > > + g_value_set_float(&x, *it); > > > + gst_value_array_append_and_take_value(&v, > > > &x); > > > + g_value_init(&x, type); > > > + } > > > + } else { > > > + gst_structure_set(structure, field->str, > > > G_TYPE_FLOAT, > > > + value.get<const float>(), > > > nullptr); > > > + } > > > + break; > > > + case ControlTypeString: > > > > Why not using GST_TYPE_ARRAY too ? Otherwise, unset 'v'. > > ControlTypeString is always an array of chars. It would simply split off > each char if I use GST_TYPE_ARRAY here. Directly using string helpers from > libcamera are helpful here for ControlTypeString. > > Regarding unsetting v, I preferred to it at end of the function, near to > return; Does g_autoptr() works for GValue? I'll probably experiment > this for RAII style cleanup. > > > > > + gst_structure_set(structure, field->str, G_TYPE_STRING, > > > + value.toString().c_str(), nullptr); > > > + break; > > > + case ControlTypeSize: > > > + if (is_array) { > > > + Span<const Size> data = value.get<Span<const > > > Size>>(); > > > + 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<const > > > Size>()); > > > + } > > > + break; > > > + case ControlTypePoint: > > > + if (is_array) { > > > + Span<const Point> data = value.get<Span<const > > > Point>>(); > > > + 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<const Point>()); > > > + } > > > + break; > > > + case ControlTypeRectangle: > > > + if (is_array) { > > > + Span<const Rectangle> data = value.get<Span<const > > > Rectangle>>(); > > > + 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<const Rectangle>()); > > > + } > > > + break; > > > + case ControlTypeNone: > > > + [[fallthrough]]; > > > + default: > > > + return -EINVAL; > > > + } > > > + > > > + /* > > > + * Set values for array types with the only exception of strings > > > which > > > + * is handled by ControlValue::toString() helper directly above. > > > + */ > > > + if ((is_array || type == GST_TYPE_ARRAY) && > > > + (value.type() != ControlTypeString)) > > > > This would be simpler no ? > > > > if (GST_VALUE_HOLDS_ARRAY(v) > > > > > + gst_structure_set_value(structure, field->str, &v); > > > + > > > + g_value_unset(&v); > > > + g_value_unset(&x); > > > + > > > + return 0; > > > +} > > > diff --git a/src/gstreamer/gstlibcamera-utils.h > > > b/src/gstreamer/gstlibcamera-utils.h > > > index 1812be75..53ad6911 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, GString > > > *field, > > > + 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..06501fd8 100644 > > > --- a/src/gstreamer/gstlibcameraprovider.cpp > > > +++ b/src/gstreamer/gstlibcameraprovider.cpp > > > @@ -12,6 +12,7 @@ > > > > > > #include <libcamera/camera.h> > > > #include <libcamera/camera_manager.h> > > > +#include <libcamera/property_ids.h> > > > > > > #include "gstlibcamerasrc.h" > > > #include "gstlibcamera-utils.h" > > > @@ -144,12 +145,27 @@ gst_libcamera_device_new(const > > > std::shared_ptr<Camera> &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); > > > + > > > + g_autoptr(GString) prop_str = > > > g_string_new("api.libcamera."); > > > + g_string_append(prop_str, id->name().c_str()); > > > > Wouldn't this be nicer using C++ ? > > > > > + > > > + int ret = gst_libcamera_set_structure_field(props, > > > prop_str, 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)); > > > } > > > >
diff --git a/src/gstreamer/gstlibcamera-utils.cpp b/src/gstreamer/gstlibcamera-utils.cpp index 63e00fe0..3d728cbc 100644 --- a/src/gstreamer/gstlibcamera-utils.cpp +++ b/src/gstreamer/gstlibcamera-utils.cpp @@ -333,6 +333,33 @@ bare_structure_from_format(const PixelFormat &format) } } +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_LONG }, + { 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) { @@ -706,3 +733,175 @@ gst_libcamera_get_camera_manager(int &ret) return cm; } + +int gst_libcamera_set_structure_field(GstStructure *structure, GString *field, + const ControlId *id, const ControlValue &value) +{ + GValue v = G_VALUE_INIT; + 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); + g_value_init(&x, type); + } + + switch (value.type()) { + case ControlTypeBool: + if (is_array) { + Span<const bool> data = value.get<Span<const bool>>(); + for (auto it = data.begin(); it != data.end(); ++it) { + g_value_set_boolean(&x, *it); + gst_value_array_append_and_take_value(&v, &x); + g_value_init(&x, type); + } + } else { + gst_structure_set(structure, field->str, G_TYPE_BOOLEAN, + value.get<const bool>(), nullptr); + } + break; + case ControlTypeByte: + if (is_array) { + Span<const uint8_t> data = value.get<Span<const uint8_t>>(); + for (auto it = data.begin(); it != data.end(); ++it) { + g_value_set_uint(&x, *it); + gst_value_array_append_and_take_value(&v, &x); + g_value_init(&x, type); + } + } else { + gst_structure_set(structure, field->str, G_TYPE_UINT, + value.get<const uint8_t>(), nullptr); + } + break; + case ControlTypeUnsigned16: + if (is_array) { + Span<const uint16_t> data = value.get<Span<const uint16_t>>(); + for (auto it = data.begin(); it != data.end(); ++it) { + g_value_set_uint(&x, *it); + gst_value_array_append_and_take_value(&v, &x); + g_value_init(&x, type); + } + } else { + gst_structure_set(structure, field->str, G_TYPE_UINT, + value.get<const uint16_t>(), nullptr); + } + break; + case ControlTypeUnsigned32: + if (is_array) { + Span<const uint32_t> data = value.get<Span<const uint32_t>>(); + for (auto it = data.begin(); it != data.end(); ++it) { + g_value_set_ulong(&x, *it); + gst_value_array_append_and_take_value(&v, &x); + g_value_init(&x, type); + } + } else { + gst_structure_set(structure, field->str, G_TYPE_ULONG, + value.get<const uint32_t>(), nullptr); + } + break; + case ControlTypeInteger32: + if (is_array) { + Span<const int32_t> data = value.get<Span<const int32_t>>(); + for (auto it = data.begin(); it != data.end(); ++it) { + g_value_set_long(&x, *it); + gst_value_array_append_and_take_value(&v, &x); + g_value_init(&x, type); + } + } else { + if (!id->enumerators().empty()) { + int32_t val = value.get<int32_t>(); + const auto &iter = id->enumerators().find(val); + if (iter != id->enumerators().end()) { + gst_structure_set(structure, field->str, + G_TYPE_STRING, + iter->second.c_str(), + nullptr); + } else { + return -EINVAL; + } + } else { + gst_structure_set(structure, field->str, G_TYPE_LONG, + value.get<const int32_t>(), nullptr); + } + } + break; + case ControlTypeInteger64: + if (is_array) { + Span<const int64_t> data = value.get<Span<const int64_t>>(); + for (auto it = data.begin(); it != data.end(); ++it) { + g_value_set_int64(&x, *it); + gst_value_array_append_and_take_value(&v, &x); + g_value_init(&x, type); + } + } else { + gst_structure_set(structure, field->str, G_TYPE_INT64, + value.get<const int64_t>(), nullptr); + } + break; + case ControlTypeFloat: + if (is_array) { + Span<const float> data = value.get<Span<const float>>(); + for (auto it = data.begin(); it != data.end(); ++it) { + g_value_set_float(&x, *it); + gst_value_array_append_and_take_value(&v, &x); + g_value_init(&x, type); + } + } else { + gst_structure_set(structure, field->str, G_TYPE_FLOAT, + value.get<const float>(), nullptr); + } + break; + case ControlTypeString: + gst_structure_set(structure, field->str, G_TYPE_STRING, + value.toString().c_str(), nullptr); + break; + case ControlTypeSize: + if (is_array) { + Span<const Size> data = value.get<Span<const Size>>(); + 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<const Size>()); + } + break; + case ControlTypePoint: + if (is_array) { + Span<const Point> data = value.get<Span<const Point>>(); + 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<const Point>()); + } + break; + case ControlTypeRectangle: + if (is_array) { + Span<const Rectangle> data = value.get<Span<const Rectangle>>(); + 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<const Rectangle>()); + } + break; + case ControlTypeNone: + [[fallthrough]]; + default: + return -EINVAL; + } + + /* + * Set values for array types with the only exception of strings which + * is handled by ControlValue::toString() helper directly above. + */ + if ((is_array || type == GST_TYPE_ARRAY) && + (value.type() != ControlTypeString)) + gst_structure_set_value(structure, field->str, &v); + + g_value_unset(&v); + g_value_unset(&x); + + return 0; +} diff --git a/src/gstreamer/gstlibcamera-utils.h b/src/gstreamer/gstlibcamera-utils.h index 1812be75..53ad6911 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, GString *field, + 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..06501fd8 100644 --- a/src/gstreamer/gstlibcameraprovider.cpp +++ b/src/gstreamer/gstlibcameraprovider.cpp @@ -12,6 +12,7 @@ #include <libcamera/camera.h> #include <libcamera/camera_manager.h> +#include <libcamera/property_ids.h> #include "gstlibcamerasrc.h" #include "gstlibcamera-utils.h" @@ -144,12 +145,27 @@ gst_libcamera_device_new(const std::shared_ptr<Camera> &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); + + g_autoptr(GString) prop_str = g_string_new("api.libcamera."); + g_string_append(prop_str, id->name().c_str()); + + int ret = gst_libcamera_set_structure_field(props, prop_str, 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)); }
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 <uajain@igalia.com> --- src/gstreamer/gstlibcamera-utils.cpp | 199 +++++++++++++++++++++++++ src/gstreamer/gstlibcamera-utils.h | 3 + src/gstreamer/gstlibcameraprovider.cpp | 16 ++ 3 files changed, 218 insertions(+)