From patchwork Mon Aug 5 09:28:36 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jaslo Ziska X-Patchwork-Id: 20767 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 0E027C323E for ; Mon, 5 Aug 2024 10:01:43 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id C1F1063383; Mon, 5 Aug 2024 12:01:42 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=ziska.de header.i=@ziska.de header.b="rsBIs5CN"; dkim=permerror (0-bit key) header.d=ziska.de header.i=@ziska.de header.b="A5/WIWVE"; dkim-atps=neutral Received: from mo4-p00-ob.smtp.rzone.de (mo4-p00-ob.smtp.rzone.de [81.169.146.219]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 8E4506337E for ; Mon, 5 Aug 2024 12:01:41 +0200 (CEST) ARC-Seal: i=1; a=rsa-sha256; t=1722852101; cv=none; d=strato.com; s=strato-dkim-0002; b=EJLzDICVq1EixzfxKcNO1kceygMtm1XAKXq5pJSJ5oRtr3BlrNAWaoghT2XaiLhg3C DBstHEjYwImW0Wlpssx+UH0Exz2W1eaSv8d7dCX8PuJ6KGozQcnNDy4BtkO+h4QCJO75 3UpFol17Mf3imJzDz2rXGxFwdJRAxDmk5YLvkAUjMC3vku/c6f3yZZGDgQLNHy0g96vi Lg7sa1hnoX2sZabpxJqeNVwrRo/x6np9RTQlSQnuOznUCoyoXuM/NZ9UQK3OX90lj1kg oU9V3xKNI74dLhGogZUhY+5NyMRB1pXEzmC4KRGz4AKxnRDn+Fex/wcjHsfbwu2SOiZw Cv1g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; t=1722852101; s=strato-dkim-0002; d=strato.com; h=References:In-Reply-To:Message-ID:Date:Subject:Cc:To:From:Cc:Date: From:Subject:Sender; bh=hiOd+hNFSxGfhI+NNFS30nToAf7iyESp67roLlCqKJg=; b=L7h6Zah4Zk3BIbxvQwmjJDHpjopdeMO0+4Rh++4C/E5t0gei5Qi1MIVeX5knpneEL6 n4zw78vF2+CDF4jgaACS2xNgr+I421r/GmOLANq/G9cKrf61kc4nyz1a5sR2iRUVxT3W 5Y/2285ZQUULBcHeHDrdZ2JQtwY/Tm5lzjaKdTCE7v1/flxyClOp0ay7pRNmDwElFUF4 2aLFN9nhDr1/QRIHsRqJ0TbG5VosGuqPA5w24SQQk4v3HX5g3G++C6qSo/54zP6ccycS usWDdl9OSRfVkj8NnLIPU2cVbJCkxU6VA0MFBhAJkfjRH+Em24yV5D7OksTJET8DSXo0 TaBA== ARC-Authentication-Results: i=1; strato.com; arc=none; dkim=none X-RZG-CLASS-ID: mo00 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; t=1722852101; s=strato-dkim-0002; d=ziska.de; h=References:In-Reply-To:Message-ID:Date:Subject:Cc:To:From:Cc:Date: From:Subject:Sender; bh=hiOd+hNFSxGfhI+NNFS30nToAf7iyESp67roLlCqKJg=; b=rsBIs5CNxq8Op0WaI0+5qrK3Gt9Y3U4EpxFxCv+y36rUTO3FGKlO25yex/dCTIHCO7 4kiPbbRTfFrQv8Czn51lBv7JAjLEZyhDzMktc/GfNuxyVqD+jazwCXQshl1QDMcrpijU ceui48GblOSi7/FGrSw+A7VJu41Tz4W8plfVZgpM1yBreEyGMEoPRSTjF2buFQOAhGPh tUl6DB5SxneskO+OnMCPnXddu2y7sibMNqDqM3KRiR4F74c8UC3ieB6/4U/edLTrialy qePPG7sM2HhtjREpLLFeb4xxcslVDACC2/uINLrRfAZFPlx3y+6AxP92HP4yaXbnqMEO cfWA== DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; t=1722852101; s=strato-dkim-0003; d=ziska.de; h=References:In-Reply-To:Message-ID:Date:Subject:Cc:To:From:Cc:Date: From:Subject:Sender; bh=hiOd+hNFSxGfhI+NNFS30nToAf7iyESp67roLlCqKJg=; b=A5/WIWVEqmuaH2lS+Q+WTKCFVZC3PEB+SuGsefHWoGzZ/lluZvx2rK0ocX4Ub3OsXu cSrGWzY0JaQj0cYhvrCg== X-RZG-AUTH: ":Jm0XeU+IYfb0x77LHmrjN5Wlb7TBwusDqIM6Hizy8VdfzvKi4yoFC9cCg4qxBvJaP2L5sFjJoIK+3CsR3+pCW/FVb/tK" Received: from archlinux.fritz.box by smtp.strato.de (RZmta 51.1.0 AUTH) with ESMTPSA id zb9f0a075A1evkF (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256 bits)) (Client did not present a certificate); Mon, 5 Aug 2024 12:01:40 +0200 (CEST) From: Jaslo Ziska To: libcamera-devel@lists.libcamera.org Cc: Jaslo Ziska Subject: [PATCH 1/3] gstreamer: Remove auto-focus-mode property Date: Mon, 5 Aug 2024 11:28:36 +0200 Message-ID: <20240805100038.11972-2-jaslo@ziska.de> X-Mailer: git-send-email 2.46.0 In-Reply-To: <20240805100038.11972-1-jaslo@ziska.de> References: <20240805100038.11972-1-jaslo@ziska.de> 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" In preparation of the next commit remove the auto-focus-mode property from the libcamera element. Signed-off-by: Jaslo Ziska --- src/gstreamer/gstlibcamerasrc.cpp | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp index e1bb6b4c..5a3e2989 100644 --- a/src/gstreamer/gstlibcamerasrc.cpp +++ b/src/gstreamer/gstlibcamerasrc.cpp @@ -142,7 +142,6 @@ struct _GstLibcameraSrc { GstTask *task; gchar *camera_name; - controls::AfModeEnum auto_focus_mode = controls::AfModeManual; std::atomic pending_eos; @@ -154,7 +153,6 @@ struct _GstLibcameraSrc { enum { PROP_0, PROP_CAMERA_NAME, - PROP_AUTO_FOCUS_MODE, }; static void gst_libcamera_src_child_proxy_init(gpointer g_iface, @@ -663,18 +661,6 @@ gst_libcamera_src_task_enter(GstTask *task, [[maybe_unused]] GThread *thread, gst_pad_push_event(srcpad, gst_event_new_segment(&segment)); } - if (self->auto_focus_mode != controls::AfModeManual) { - const ControlInfoMap &infoMap = state->cam_->controls(); - if (infoMap.find(&controls::AfMode) != infoMap.end()) { - state->initControls_.set(controls::AfMode, self->auto_focus_mode); - } else { - GST_ELEMENT_ERROR(self, RESOURCE, SETTINGS, - ("Failed to enable auto focus"), - ("AfMode not supported by this camera, " - "please retry with 'auto-focus-mode=AfModeManual'")); - } - } - ret = state->cam_->start(&state->initControls_); if (ret) { GST_ELEMENT_ERROR(self, RESOURCE, SETTINGS, @@ -742,9 +728,6 @@ gst_libcamera_src_set_property(GObject *object, guint prop_id, g_free(self->camera_name); self->camera_name = g_value_dup_string(value); break; - case PROP_AUTO_FOCUS_MODE: - self->auto_focus_mode = static_cast(g_value_get_enum(value)); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; @@ -762,9 +745,6 @@ gst_libcamera_src_get_property(GObject *object, guint prop_id, GValue *value, case PROP_CAMERA_NAME: g_value_set_string(value, self->camera_name); break; - case PROP_AUTO_FOCUS_MODE: - g_value_set_enum(value, static_cast(self->auto_focus_mode)); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; @@ -967,14 +947,6 @@ gst_libcamera_src_class_init(GstLibcameraSrcClass *klass) | G_PARAM_STATIC_STRINGS)); g_object_class_install_property(object_class, PROP_CAMERA_NAME, spec); - spec = g_param_spec_enum("auto-focus-mode", - "Set auto-focus mode", - "Available options: AfModeManual, " - "AfModeAuto or AfModeContinuous.", - gst_libcamera_auto_focus_get_type(), - static_cast(controls::AfModeManual), - G_PARAM_WRITABLE); - g_object_class_install_property(object_class, PROP_AUTO_FOCUS_MODE, spec); } /* GstChildProxy implementation */ From patchwork Mon Aug 5 09:28:37 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jaslo Ziska X-Patchwork-Id: 20768 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 BDD3AC323E for ; Mon, 5 Aug 2024 10:01:48 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 5E08A63381; Mon, 5 Aug 2024 12:01:48 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=ziska.de header.i=@ziska.de header.b="Cf78g8L8"; dkim=permerror (0-bit key) header.d=ziska.de header.i=@ziska.de header.b="HebAEFhc"; dkim-atps=neutral Received: from mo4-p00-ob.smtp.rzone.de (mo4-p00-ob.smtp.rzone.de [81.169.146.217]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 0A9696337E for ; Mon, 5 Aug 2024 12:01:47 +0200 (CEST) ARC-Seal: i=1; a=rsa-sha256; t=1722852106; cv=none; d=strato.com; s=strato-dkim-0002; b=B7qNlkFUSfPdm5rdszkqvJSL8RyGs09TCKWZQrB3vF+6dBtwdLurTtfv171/BJTUD4 Yb90Qo3Qs79wR5ItMUK0iv1HnVGr0Zc4byN3zSvc1xa+O5o8SliNyo5eGbwox2WeJ7h6 8r3RLYuhgvtMexNrfyEuN1qkio8Q7UPmeVlpA3R3dAOnjV7V3ueXsTuXKd/DcixEZxWz MYoKWZX8Cow7pIv0GsLxjISlsddT/SRdjLmVdkSfOX0FTuNesurY5lJC29lz/MhbNE07 Jwub1AQfOdIAetgE4yxvRBUorQZW6YNrRnJR1dovMgqsPDKCJ8Od65maxFvEPtljv9qO G03Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; t=1722852106; s=strato-dkim-0002; d=strato.com; h=References:In-Reply-To:Message-ID:Date:Subject:Cc:To:From:Cc:Date: From:Subject:Sender; bh=3SHd+fgDM0zwoJ6gc3Yv4Pl6CBD3qYQKnUY+lVuijqk=; b=KLH7Ek7ii4WRJnnJM4uc/MYPTjfTkH4oqBtRjsf74vw+MeoIg8ASq2nK2U2NfXWPkw HGRVh1U48jNe8kbDF6HF3TRi8b+Q/dqzHv4dzBI/tINLnrA2j74cdj1Xzf/U074bN12b IcET2ivuz1oa0iq9coImZheajzg11X/+OtGStTJEqlubO6Aj5tEoAh9xVxYHj2nH3NVq JN19Xj2qyfaCU4wsDzFS1J0g3Cj2g+lS/+8Yqi5caX/B08vnY+Z8jTDwcOkb+odSjISo SzLyBlPjy1u2Wg2XD957jTjPt9w2n3o2dZWDfK6fzZXHh2M9qjD+Z/aY68dtaiP6sIeQ 0O2g== ARC-Authentication-Results: i=1; strato.com; arc=none; dkim=none X-RZG-CLASS-ID: mo00 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; t=1722852106; s=strato-dkim-0002; d=ziska.de; h=References:In-Reply-To:Message-ID:Date:Subject:Cc:To:From:Cc:Date: From:Subject:Sender; bh=3SHd+fgDM0zwoJ6gc3Yv4Pl6CBD3qYQKnUY+lVuijqk=; b=Cf78g8L8hMcH2tx5mU3oDlC767K/zpU5mX4yQNMC+yiWZJn72eEmpZSCke9DKEsxqb XQfCyoDk42CcPabA9dsalIvDxXB1/iKJClxqfgfLwcIPaW0iixClGTGFwixkMBff62vY gMRKdFVdVUMD4DtQ3kjCc1lzPg7Y5UqPlXkS/TkTZzPajjHSQ/X1q0EgF9OKw4xQY/Ju 1Jp0jtf4H8q0ydPe+B1HYantStwFBjvrmoukPIDDr3Z51pFWrRssBzyjt9TidWSIODbF uuQz/i3PYIfGEBxzigRNBpHW9YH3xwSbdxdlTFjg8WOTjHrgNS2mODXRTCXAQeLlO3fq jWew== DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; t=1722852106; s=strato-dkim-0003; d=ziska.de; h=References:In-Reply-To:Message-ID:Date:Subject:Cc:To:From:Cc:Date: From:Subject:Sender; bh=3SHd+fgDM0zwoJ6gc3Yv4Pl6CBD3qYQKnUY+lVuijqk=; b=HebAEFhc0FgnjX0Kbfn4YAYz+XFNhu/xef5EbDJjXAs9UbIiIEcBIqr/3yqP5fow4j i9Oufk7ssmBYzsZNp6DQ== X-RZG-AUTH: ":Jm0XeU+IYfb0x77LHmrjN5Wlb7TBwusDqIM6Hizy8VdfzvKi4yoFC9cCg4qxBvJaP2L5sFjJoIK+3CsR3+pCW/FVb/tK" Received: from archlinux.fritz.box by smtp.strato.de (RZmta 51.1.0 AUTH) with ESMTPSA id zb9f0a075A1kvkK (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256 bits)) (Client did not present a certificate); Mon, 5 Aug 2024 12:01:46 +0200 (CEST) From: Jaslo Ziska To: libcamera-devel@lists.libcamera.org Cc: Jaslo Ziska Subject: [PATCH 2/3] gstreamer: Generate controls from control_ids_*.yaml files Date: Mon, 5 Aug 2024 11:28:37 +0200 Message-ID: <20240805100038.11972-3-jaslo@ziska.de> X-Mailer: git-send-email 2.46.0 In-Reply-To: <20240805100038.11972-1-jaslo@ziska.de> References: <20240805100038.11972-1-jaslo@ziska.de> 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" This commit implements gstreamer controls for the libcamera element by generating the controls from the control_ids_*.yaml files using a new gen-gst-controls.py script. The appropriate meson files are also changed to automatically run the script when building. The gen-gst-controls.py script works similar to the gen-controls.py script by parsing the control_ids_*.yaml files and generating C++ code for each control. For the controls to be used as gstreamer properties the type for each control needs to be translated to the appropriate glib type and a GEnumValue is generated for each enum control. Then a g_object_install_property(), _get_property() and _set_property() function is generated for each control. The vendor controls get prefixed with "$vendor-" in the final gstreamer property name. The C++ code generated by the gen-gst-controls.py script is written into the template gstlibcamerasrc-controls.cpp.in file. The matching gstlibcamerasrc-controls.h header defines the GstCameraControls class which handles the installation of the gstreamer properties as well as keeping track of the control values and setting and getting the controls. The content of these functions is generated in the Python script. Finally the libcamerasrc element itself is edited to make use of the new GstCameraControls class. The way this works is by defining a PROP_LAST enum variant which is passed to the installProperties() function so the properties are defined with the appropriate offset. When getting or setting a property PROP_LAST is subtracted from the requested property to translate the control back into a libcamera::controls:: enum variant. Signed-off-by: Jaslo Ziska --- src/gstreamer/gstlibcamera-controls.cpp.in | 46 +++ src/gstreamer/gstlibcamera-controls.h | 36 ++ src/gstreamer/gstlibcamerasrc.cpp | 17 +- src/gstreamer/meson.build | 14 + utils/gen-gst-controls.py | 398 +++++++++++++++++++++ utils/meson.build | 1 + 6 files changed, 509 insertions(+), 3 deletions(-) create mode 100644 src/gstreamer/gstlibcamera-controls.cpp.in create mode 100644 src/gstreamer/gstlibcamera-controls.h create mode 100755 utils/gen-gst-controls.py diff --git a/src/gstreamer/gstlibcamera-controls.cpp.in b/src/gstreamer/gstlibcamera-controls.cpp.in new file mode 100644 index 00000000..ff93f5c3 --- /dev/null +++ b/src/gstreamer/gstlibcamera-controls.cpp.in @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2023, Collabora Ltd. + * Author: Nicolas Dufresne + * + * gstlibcamera-controls.cpp - GStreamer Camera Controls + * + * This file is auto-generated. Do not edit. + */ + +#include "gstlibcamera-controls.h" + +#include + +using namespace libcamera; + +${enum_def} + +void GstCameraControls::installProperties(GObjectClass *klass, int lastPropId) +{ +${install_properties} +} + +bool GstCameraControls::getProperty(guint propId, GValue *value, GParamSpec *pspec) +{ + switch (propId) { +${get_properties} + default: + return false; + } +} + +bool GstCameraControls::setProperty(guint propId, const GValue *value, + [[maybe_unused]] GParamSpec *pspec) +{ + switch (propId) { +${set_properties} + default: + return false; + } +} + +void GstCameraControls::applyControls(std::unique_ptr &request) +{ + request->controls().merge(controls_); +} diff --git a/src/gstreamer/gstlibcamera-controls.h b/src/gstreamer/gstlibcamera-controls.h new file mode 100644 index 00000000..4e1d5bf9 --- /dev/null +++ b/src/gstreamer/gstlibcamera-controls.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2023, Collabora Ltd. + * Author: Nicolas Dufresne + * + * gstlibcamera-controls.h - GStreamer Camera Controls + */ + +#pragma once + +#include +#include + +#include "gstlibcamerasrc.h" + +namespace libcamera { + +class GstCameraControls +{ +public: + GstCameraControls() {}; + ~GstCameraControls() {}; + + static void installProperties(GObjectClass *klass, int lastProp); + + bool getProperty(guint propId, GValue *value, GParamSpec *pspec); + bool setProperty(guint propId, const GValue *value, GParamSpec *pspec); + + void applyControls(std::unique_ptr &request); + +private: + /* set of user modified controls */ + ControlList controls_; +}; + +} /* namespace libcamera */ diff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp index 5a3e2989..85dab67f 100644 --- a/src/gstreamer/gstlibcamerasrc.cpp +++ b/src/gstreamer/gstlibcamerasrc.cpp @@ -37,10 +37,11 @@ #include +#include "gstlibcamera-controls.h" +#include "gstlibcamera-utils.h" #include "gstlibcameraallocator.h" #include "gstlibcamerapad.h" #include "gstlibcamerapool.h" -#include "gstlibcamera-utils.h" using namespace libcamera; @@ -128,6 +129,7 @@ struct GstLibcameraSrcState { ControlList initControls_; guint group_id_; + GstCameraControls controls_; int queueRequest(); void requestCompleted(Request *request); @@ -153,6 +155,7 @@ struct _GstLibcameraSrc { enum { PROP_0, PROP_CAMERA_NAME, + PROP_LAST }; static void gst_libcamera_src_child_proxy_init(gpointer g_iface, @@ -183,6 +186,9 @@ int GstLibcameraSrcState::queueRequest() if (!request) return -ENOMEM; + /* Apply controls */ + controls_.applyControls(request); + std::unique_ptr wrap = std::make_unique(std::move(request)); @@ -722,6 +728,7 @@ gst_libcamera_src_set_property(GObject *object, guint prop_id, { GLibLocker lock(GST_OBJECT(object)); GstLibcameraSrc *self = GST_LIBCAMERA_SRC(object); + GstLibcameraSrcState *state = self->state; switch (prop_id) { case PROP_CAMERA_NAME: @@ -729,7 +736,8 @@ gst_libcamera_src_set_property(GObject *object, guint prop_id, self->camera_name = g_value_dup_string(value); break; default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + if (!state->controls_.setProperty(prop_id - PROP_LAST, value, pspec)) + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } } @@ -740,13 +748,15 @@ gst_libcamera_src_get_property(GObject *object, guint prop_id, GValue *value, { GLibLocker lock(GST_OBJECT(object)); GstLibcameraSrc *self = GST_LIBCAMERA_SRC(object); + GstLibcameraSrcState *state = self->state; switch (prop_id) { case PROP_CAMERA_NAME: g_value_set_string(value, self->camera_name); break; default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + if (!state->controls_.getProperty(prop_id - PROP_LAST, value, pspec)) + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } } @@ -947,6 +957,7 @@ gst_libcamera_src_class_init(GstLibcameraSrcClass *klass) | G_PARAM_STATIC_STRINGS)); g_object_class_install_property(object_class, PROP_CAMERA_NAME, spec); + GstCameraControls::installProperties(object_class, PROP_LAST); } /* GstChildProxy implementation */ diff --git a/src/gstreamer/meson.build b/src/gstreamer/meson.build index c2a01e7b..e6c20124 100644 --- a/src/gstreamer/meson.build +++ b/src/gstreamer/meson.build @@ -25,6 +25,20 @@ libcamera_gst_sources = [ 'gstlibcamerasrc.cpp', ] +# Generate gstreamer control properties + +gen_gst_controls_input_files = [] +gen_gst_controls_template = files('gstlibcamera-controls.cpp.in') +foreach file : controls_files + gen_gst_controls_input_files += files('../libcamera/' + file) +endforeach + +libcamera_gst_sources += custom_target('gstlibcamera-controls.cpp', + input : gen_gst_controls_input_files, + output : 'gstlibcamera-controls.cpp', + command : [gen_gst_controls, '-o', '@OUTPUT@', + '-t', gen_gst_controls_template, '@INPUT@']) + libcamera_gst_cpp_args = [ '-DVERSION="@0@"'.format(libcamera_git_version), '-DPACKAGE="@0@"'.format(meson.project_name()), diff --git a/utils/gen-gst-controls.py b/utils/gen-gst-controls.py new file mode 100755 index 00000000..d0c12b50 --- /dev/null +++ b/utils/gen-gst-controls.py @@ -0,0 +1,398 @@ +#!/usr/bin/env python3 + +import argparse +import re +import string +import sys +import yaml + + +class ControlEnum(object): + def __init__(self, data): + self.__data = data + + @property + def description(self): + """The enum description""" + return self.__data.get('description') + + @property + def name(self): + """The enum name""" + return self.__data.get('name') + + @property + def value(self): + """The enum value""" + return self.__data.get('value') + + +class Control(object): + def __init__(self, name, data, vendor): + self.__name = name + self.__data = data + self.__enum_values = None + self.__size = None + self.__vendor = vendor + + enum_values = data.get('enum') + if enum_values is not None: + self.__enum_values = [ControlEnum(enum) for enum in enum_values] + + size = self.__data.get('size') + if size is not None: + if len(size) == 0: + raise RuntimeError(f'Control `{self.__name}` size must have at least one dimension') + + # Compute the total number of elements in the array. If any of the + # array dimension is a string, the array is variable-sized. + num_elems = 1 + for dim in size: + if type(dim) is str: + num_elems = 0 + break + + dim = int(dim) + if dim <= 0: + raise RuntimeError(f'Control `{self.__name}` size must have positive values only') + + num_elems *= dim + + self.__size = num_elems + + @property + def description(self): + """The control description""" + return self.__data.get('description') + + @property + def enum_values(self): + """The enum values, if the control is an enumeration""" + if self.__enum_values is None: + return + for enum in self.__enum_values: + yield enum + + @property + def is_enum(self): + """Is the control an enumeration""" + return self.__enum_values is not None + + @property + def vendor(self): + """The vendor string, or None""" + return self.__vendor + + @property + def name(self): + """The control name (CamelCase)""" + return self.__name + + @property + def type(self): + typ = self.__data.get('type') + size = self.__data.get('size') + + if typ == 'string': + return 'std::string' + + if self.__size is None: + return typ + + if self.__size: + return f"Span" + else: + return f"Span" + + @property + def element_type(self): + typ = self.__data.get('type') + return typ + + @property + def size(self): + return self.__size + + +def find_common_prefix(strings): + prefix = strings[0] + + for string in strings[1:]: + while string[:len(prefix)] != prefix and prefix: + prefix = prefix[:len(prefix) - 1] + if not prefix: + break + + return prefix + + +def format_description(description, indent = 0): + # Substitute doxygen keywords \sa (see also) and \todo + description = re.sub(r'\\sa((?: \w+)+)', + lambda match: 'See also: ' + ', '.join(map(kebab_case, match.group(1).strip().split(' '))) + '.', description) + description = re.sub(r'\\todo', 'Todo:', description) + + description = description.strip().split('\n') + return '\n'.join([indent * '\t' + '"' + line.replace('\\', r'\\').replace('"', r'\"') + ' "' for line in description if line]).rstrip() + + +def snake_case(s): + return ''.join([c.isupper() and ('_' + c.lower()) or c for c in s]).strip('_') + + +def kebab_case(s): + return snake_case(s).replace('_', '-') + + +def indent(s, n): + lines = s.split('\n') + return '\n'.join([n * '\t' + line for line in lines]) + + +def generate_cpp(controls): + # Beginning of a GEnumValue definition for enum controls + enum_values_start = string.Template('static const GEnumValue ${name_snake_case}_types[] = {') + # Definition of the GEnumValue variant for each enum control variant + # Because the description might have multiple lines it will get indented below + enum_values_values = string.Template('''{ +\tcontrols${vendor_namespace}::${name}, +${description}, +\t"${nick}" +},''') + # End of a GEnumValue definition and definition of the matching _get_type() function + enum_values_end = string.Template('''\t{0, NULL, NULL} +}; + +#define TYPE_${name_upper} (${name_snake_case}_get_type()) +static GType ${name_snake_case}_get_type(void) +{ +\tstatic GType ${name_snake_case}_type = 0; + +\tif (!${name_snake_case}_type) +\t\t${name_snake_case}_type = g_enum_register_static("${name}", ${name_snake_case}_types); + +\treturn ${name_snake_case}_type; +} +''') + + # Creation of the type spec for the different types of controls + # The description (and the element_spec for the array) might have multiple lines and will get indented below + spec_array = string.Template('''gst_param_spec_array( +\t"${spec_name}", +\t"${nick}", +${description}, +${element_spec}, +\t(GParamFlags) (GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) +)''') + spec_bool = string.Template('''g_param_spec_boolean( +\t"${spec_name}", +\t"${nick}", +${description}, +\t${default}, +\t(GParamFlags) (GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) +)''') + spec_enum = string.Template('''g_param_spec_enum( +\t"${spec_name}", +\t"${nick}", +${description}, +\tTYPE_${enum}, +\t${default}, +\t(GParamFlags) (GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) +)''') + spec_numeric = string.Template('''g_param_spec_${gtype}( +\t"${spec_name}", +\t"${nick}", +${description}, +\t${min}, +\t${max}, +\t${default}, +\t(GParamFlags) (GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) +)''') + + # The g_object_class_install_property() function call for each control + install_property = string.Template('''\tg_object_class_install_property( +\t\tklass, +\t\tlastPropId + controls${vendor_namespace}::${name_upper}, +${spec} +\t);''') + + # The _get_property() switch cases for each control + get_property = string.Template('''case controls${vendor_namespace}::${name_upper}: { +\tauto val = controls_.get(controls${vendor_namespace}::${name}); +\tauto spec = G_PARAM_SPEC_${gtype_upper}(pspec); +\tg_value_set_${gtype}(value, val.value_or(spec->default_value)); +\treturn true; +}''') + get_array_property = string.Template('''case controls${vendor_namespace}::${name_upper}: { +\tauto val = controls_.get(controls${vendor_namespace}::${name}); +\tif (val) { +\t\tfor (size_t i = 0; i < val->size(); ++i) { +\t\t\tGValue v = G_VALUE_INIT; +\t\t\tg_value_init(&v, G_TYPE_${gtype_upper}); +\t\t\tg_value_set_${gtype}(&v, (*val)[i]); +\t\t\tgst_value_array_append_and_take_value(value, &v); +\t\t} +\t} +\treturn true; +}''') + + # The _set_property() switch cases for each control + set_property = string.Template('''case controls${vendor_namespace}::${name_upper}: +\tcontrols_.set(controls${vendor_namespace}::${name}, g_value_get_${gtype}(value)); +\treturn true;''') + set_array_property = string.Template('''case controls${vendor_namespace}::${name_upper}: { +\tsize_t size = gst_value_array_get_size(value); +\tstd::vector<${type}> val(size); +\tfor (size_t i = 0; i < size; ++i) { +\t\tconst GValue *v = gst_value_array_get_value(value, i); +\t\tval[i] = g_value_get_${gtype}(v); +\t} +\tcontrols_.set(controls${vendor_namespace}::${name}, Span(val.data(), size)); +\treturn true; +}''') + + enum_def = [] + install_properties = [] + get_properties = [] + set_properties = [] + + for ctrl in controls: + is_array = ctrl.size is not None + size = ctrl.size + if size == 0: + size = 'dynamic_extent' + + # Determine the matching glib type for each C++ type used in the controls + gtype = '' + if ctrl.is_enum: + gtype = 'enum' + elif ctrl.element_type == 'bool': + gtype = 'boolean' + elif ctrl.element_type == 'float': + gtype = 'float' + elif ctrl.element_type == 'int32_t': + gtype = 'int' + elif ctrl.element_type == 'int64_t': + gtype = 'int64' + elif ctrl.element_type == 'uint8_t': + gtype = 'uchar' + elif ctrl.element_type == 'Rectangle': + # TODO: Handle Rectangle + continue + else: + raise RuntimeError(f'The type `{ctrl.element_type}` is unknown') + + vendor_prefix = '' + vendor_namespace = '' + if ctrl.vendor != 'libcamera': + vendor_prefix = ctrl.vendor + '-' + vendor_namespace = '::' + ctrl.vendor + + name_snake_case = snake_case(ctrl.name) + name_upper = name_snake_case.upper() + + info = { + 'name': ctrl.name, + 'vendor_namespace': vendor_namespace, + 'name_snake_case': name_snake_case, + 'name_upper': name_upper, + 'spec_name': vendor_prefix + kebab_case(ctrl.name), + 'nick': ''.join([c.isupper() and (' ' + c) or c for c in ctrl.name]).strip(' '), + 'description': format_description(ctrl.description, indent=1), + 'gtype': gtype, + 'gtype_upper': gtype.upper(), + 'type': ctrl.element_type, + 'size': size, + } + + if ctrl.is_enum: + enum_def.append(enum_values_start.substitute(info)) + + common_prefix = find_common_prefix([enum.name for enum in ctrl.enum_values]) + + for enum in ctrl.enum_values: + values_info = { + 'name': enum.name, + 'vendor_namespace': vendor_namespace, + 'description': format_description(enum.description, indent=1), + 'nick': kebab_case(enum.name.removeprefix(common_prefix)), + } + enum_def.append(indent(enum_values_values.substitute(values_info), 1)) + + enum_def.append(enum_values_end.substitute(info)) + + spec = '' + if ctrl.is_enum: + spec = spec_enum.substitute({'enum': name_upper, 'default': 0, **info}) + elif gtype == 'boolean': + spec = spec_bool.substitute({'default': 'false', **info}) + elif gtype == 'float': + spec = spec_numeric.substitute({'min': '-G_MAXFLOAT', 'max': 'G_MAXFLOAT', 'default': 0, **info}) + elif gtype == 'int': + spec = spec_numeric.substitute({'min': 'G_MININT', 'max': 'G_MAXINT', 'default': 0, **info}) + elif gtype == 'int64': + spec = spec_numeric.substitute({'min': 'G_MININT64', 'max': 'G_MAXINT64', 'default': 0, **info}) + elif gtype == 'uchar': + spec = spec_numeric.substitute({'min': '0', 'max': 'G_MAXUINT8', 'default': 0, **info}) + + if is_array: + spec = spec_array.substitute({'element_spec': indent(spec, 1), **info}) + + install_properties.append(install_property.substitute({'spec': indent(spec, 2), **info})) + + if is_array: + get_properties.append(indent(get_array_property.substitute(info), 1)) + set_properties.append(indent(set_array_property.substitute(info), 1)) + else: + get_properties.append(indent(get_property.substitute(info), 1)) + set_properties.append(indent(set_property.substitute(info), 1)) + + return { + 'enum_def': '\n'.join(enum_def), + 'install_properties': '\n'.join(install_properties), + 'get_properties': '\n'.join(get_properties), + 'set_properties': '\n'.join(set_properties), + } + + +def fill_template(template, data): + template = open(template, 'rb').read() + template = template.decode('utf-8') + template = string.Template(template) + return template.substitute(data) + + +def main(argv): + # Parse command line arguments + parser = argparse.ArgumentParser() + parser.add_argument('--output', '-o', metavar='file', type=str, + help='Output file name. Defaults to standard output if not specified.') + parser.add_argument('--template', '-t', dest='template', type=str, required=True, + help='Template file name.') + parser.add_argument('input', type=str, nargs='+', + help='Input file name.') + args = parser.parse_args(argv[1:]) + + controls = [] + for input in args.input: + data = open(input, 'rb').read() + vendor = yaml.safe_load(data)['vendor'] + ctrls = yaml.safe_load(data)['controls'] + controls = controls + [Control(*ctrl.popitem(), vendor) for ctrl in ctrls] + + data = generate_cpp(controls) + + data = fill_template(args.template, data) + + if args.output: + output = open(args.output, 'wb') + output.write(data.encode('utf-8')) + output.close() + else: + sys.stdout.write(data) + + return 0 + + +if __name__ == '__main__': + sys.exit(main(sys.argv)) diff --git a/utils/meson.build b/utils/meson.build index 8e28ada7..89597f7f 100644 --- a/utils/meson.build +++ b/utils/meson.build @@ -9,6 +9,7 @@ py_modules += ['yaml'] gen_controls = files('gen-controls.py') gen_formats = files('gen-formats.py') gen_header = files('gen-header.sh') +gen_gst_controls = files('gen-gst-controls.py') ## Module signing gen_ipa_priv_key = files('gen-ipa-priv-key.sh') From patchwork Mon Aug 5 09:28:38 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jaslo Ziska X-Patchwork-Id: 20769 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 61C46C323E for ; Mon, 5 Aug 2024 10:01:52 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 0AB636337E; Mon, 5 Aug 2024 12:01:52 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=ziska.de header.i=@ziska.de header.b="n/DaR7MO"; dkim=permerror (0-bit key) header.d=ziska.de header.i=@ziska.de header.b="vWTw3UYC"; dkim-atps=neutral Received: from mo4-p00-ob.smtp.rzone.de (mo4-p00-ob.smtp.rzone.de [81.169.146.221]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 748966337E for ; Mon, 5 Aug 2024 12:01:50 +0200 (CEST) ARC-Seal: i=1; a=rsa-sha256; t=1722852110; cv=none; d=strato.com; s=strato-dkim-0002; b=f8w5k6rtxjlqPaGVI58apUbuyPSN+bfbvGW58sy+pGy5rq9BlxRE/10+H9gOo5UWH/ xSlECzkWjQ4zLYDvnLBItEtOUW//uo5g8PQubulflUhoulBVrYwVJaB+J4eSJXoRJ05C QJbesTujLdF0r0mxEJszRHt9/HmPtuKODDZHIkiANAVaT7r61vsVI0xKS6kGCwymkLGU eK77b95u9USdBlL4HT7J5FxHhcKKdrFsr8PXdJPcGnkNtgoggVEbKJ4qTGEYWVNxjpGW 7O0VUUrpXqDnr7U+QpJRUY+Ib+nMq/E45FmYBaxXHDXNipB13EUEfARFiWWamqnyInuj cCBA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; t=1722852110; s=strato-dkim-0002; d=strato.com; h=References:In-Reply-To:Message-ID:Date:Subject:Cc:To:From:Cc:Date: From:Subject:Sender; bh=7COneWYK1VRHaZ+TjGhbFa3/cl+TmImIBCApZxv1/hk=; b=TQmDjxCvs4Fy9gT8I5Y6qlguTz5f7wz7FlqGVClxYhwXz5SJvztohAHdd8NP+cVPUG 6qWDK6EoseZasfg1J96DOfcYVJ7UYynF3X02BwmyUAbfZT5LvhQm3vinDhmge02bt6Vr 3Q7L0Jariy5OdyQ1TfQhmL37n/e7KQcnFZDxDUz4sskmIz7MkQt7rhk9+IEkPL7DKu6Q z52qCsL3PMXdsz2nwjL2bu0nXb3jVrjQZLvsvVZvMGtzTn5wypvRlP+uiBPBpsy/ECJk +eWnCdoC87eXYnbGLQNOq7NfvqluTFaX1hVt2jO7Kc+vlLRPiHFlXukQaobFGubeYZW1 H15A== ARC-Authentication-Results: i=1; strato.com; arc=none; dkim=none X-RZG-CLASS-ID: mo00 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; t=1722852110; s=strato-dkim-0002; d=ziska.de; h=References:In-Reply-To:Message-ID:Date:Subject:Cc:To:From:Cc:Date: From:Subject:Sender; bh=7COneWYK1VRHaZ+TjGhbFa3/cl+TmImIBCApZxv1/hk=; b=n/DaR7MOAZgRx+1vkPCv0sVo7EWDSWX9RIguOXjJPrW4xiYjgZFpsO0aSIMTB28r3V oeRKIBqjbvjlkLEIv/Noo7f74q4pqF62IJVKjT8qkM5isEJ4s3TWKRNrMDXjasRF3TmC kPcChnJ70B/pZ1D36SYN+IvhxUDjtCG/Ktp+1XcV4AHrN6x2Ts1HCY/PdvZKqdwt9cak M3cq7CGglHGdVq2SsFiM0Y+r2f81O2AdIFQvjyVKzJYCtrH4BQho3RMzjKrLUQVNj9qH oA4CrxKBEYXb6PncxnCjt9uo0ahvx/+w5K/r3CoyQ2bLCOMD7BaTVLRkXRSJI4XMMW8c N1pw== DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; t=1722852110; s=strato-dkim-0003; d=ziska.de; h=References:In-Reply-To:Message-ID:Date:Subject:Cc:To:From:Cc:Date: From:Subject:Sender; bh=7COneWYK1VRHaZ+TjGhbFa3/cl+TmImIBCApZxv1/hk=; b=vWTw3UYCjNRMwpmc/LyY51kASAONv5Gta8X+ljMUTIGKlfsMcrQXDVFD8NgtUckl+k 6vkNXygqh8WS5zsJQgDg== X-RZG-AUTH: ":Jm0XeU+IYfb0x77LHmrjN5Wlb7TBwusDqIM6Hizy8VdfzvKi4yoFC9cCg4qxBvJaP2L5sFjJoIK+3CsR3+pCW/FVb/tK" Received: from archlinux.fritz.box by smtp.strato.de (RZmta 51.1.0 AUTH) with ESMTPSA id zb9f0a075A1nvkO (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256 bits)) (Client did not present a certificate); Mon, 5 Aug 2024 12:01:49 +0200 (CEST) From: Jaslo Ziska To: libcamera-devel@lists.libcamera.org Cc: Jaslo Ziska Subject: [PATCH 3/3] gstreamer: Fix missing "greater than" symbol in author string Date: Mon, 5 Aug 2024 11:28:38 +0200 Message-ID: <20240805100038.11972-4-jaslo@ziska.de> X-Mailer: git-send-email 2.46.0 In-Reply-To: <20240805100038.11972-1-jaslo@ziska.de> References: <20240805100038.11972-1-jaslo@ziska.de> 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" Signed-off-by: Jaslo Ziska Reviewed-by: Nicolas Dufresne Reviewed-by: Kieran Bingham --- src/gstreamer/gstlibcamerasrc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp index 85dab67f..1a277fcc 100644 --- a/src/gstreamer/gstlibcamerasrc.cpp +++ b/src/gstreamer/gstlibcamerasrc.cpp @@ -941,7 +941,7 @@ gst_libcamera_src_class_init(GstLibcameraSrcClass *klass) gst_element_class_set_metadata(element_class, "libcamera Source", "Source/Video", "Linux Camera source using libcamera", - "Nicolas Dufresne "); gst_element_class_add_static_pad_template_with_gtype(element_class, &src_template, GST_TYPE_LIBCAMERA_PAD);