From patchwork Thu Jul 24 13:33:43 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Umang Jain X-Patchwork-Id: 23946 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 16F89C3323 for ; Thu, 24 Jul 2025 13:33:50 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id E971869116; Thu, 24 Jul 2025 15:33:47 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=igalia.com header.i=@igalia.com header.b="fMOegIcj"; dkim-atps=neutral Received: from fanzine2.igalia.com (fanzine2.igalia.com [213.97.179.56]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id DAE6B690F3 for ; Thu, 24 Jul 2025 15:33:41 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=igalia.com; s=20170329; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=X3aUhfq1vNmHLfj4hgENauGM5sf+f87bJJirO/GZEEc=; b=fMOegIcjvJaPS5eK1shGoP5OZ+ kTx1k+/OQtAEG7t2eikyIoNNKqwUdSNqiqz21gmFMxPDGSRjCfUrgfECxoyztEa7Mna/5YRbk69E6 kwkW3ZlqUNlK+kfk+/4MNWU0fsqss1Vsw2p63ZzTS5O33i8EsGiUe7XhcvpqyHGET7Hdy3Gi/xqHp mnFnRcvCcc7qHcX5aO2C9GKVgyFVEz1hEXtON54Sv9Jzof2f5nuF1Qgi8DP5Er9Jkw+WKX/5K97Rc A7Rj2mW0vLmFoIlxL2xLijtSxPz7DFcdN5WNp8NBAh742koTNpC31Rc9tE4440m42p+vbMmHMcBle Yl1fr+Kg==; Received: from [49.36.71.87] (helo=uajain) by fanzine2.igalia.com with esmtpsa (Cipher TLS1.3:ECDHE_X25519__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim) id 1uew4q-003CfR-VO; Thu, 24 Jul 2025 15:33:41 +0200 From: Umang Jain To: libcamera-devel@lists.libcamera.org Cc: Jaslo Ziska , Nicolas Dufresne , Umang Jain Subject: [PATCH v4 3/3] gstreamer: Report camera properties as device properties Date: Thu, 24 Jul 2025 19:03:43 +0530 Message-ID: <20250724133343.353044-4-uajain@igalia.com> X-Mailer: git-send-email 2.50.0 In-Reply-To: <20250724133343.353044-1-uajain@igalia.com> References: <20250724133343.353044-1-uajain@igalia.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" 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 --- 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 data = value.get>(); + 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(), nullptr); + } + break; + case ControlTypeByte: + if (is_array) { + Span data = value.get>(); + 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(), nullptr); + } + break; + case ControlTypeUnsigned16: + if (is_array) { + Span data = value.get>(); + 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(), nullptr); + } + break; + case ControlTypeUnsigned32: + if (is_array) { + Span data = value.get>(); + 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(), nullptr); + } + break; + case ControlTypeInteger32: + if (is_array) { + Span data = value.get>(); + 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(); + 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(), nullptr); + } + } + break; + case ControlTypeInteger64: + if (is_array) { + Span data = value.get>(); + 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(), nullptr); + } + break; + case ControlTypeFloat: + if (is_array) { + Span data = value.get>(); + 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(), 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 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; + } + + /* + * 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 #include +#include #include "gstlibcamerasrc.h" #include "gstlibcamera-utils.h" @@ -144,12 +145,27 @@ 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); + + 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)); }