From patchwork Thu Jul 3 11:42:22 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 23737 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 C8BA5BDCBF for ; Thu, 3 Jul 2025 11:42:59 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 7071868E62; Thu, 3 Jul 2025 13:42:59 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="uqk6L9MC"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 0FEF268E66 for ; Thu, 3 Jul 2025 13:42:58 +0200 (CEST) Received: from neptunite.hamster-moth.ts.net (unknown [IPv6:2404:7a81:160:2100:c61b:f3bf:2578:6674]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id D9007593; Thu, 3 Jul 2025 13:42:32 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1751542954; bh=pWJSjtrAFC6QKkA3NWNaXZsgb0xLZHDvLnzNoU29evY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=uqk6L9MCjWBNX5gVhCMCUK+m89nc4ETDQFyScN1UXYvomQ3QRLQKYKk4iUMjXdj4N gUMFhZ7pBwBBMUqyxo5ePzSbdEU9/JKKYRriXJb8yC7QPDInUzDqPUCGp3Wd6hf8I0 Ecz9ZYB8GUDPI5QqmCTffzMeT8jFNh8lDan7d4c4= From: Paul Elder To: libcamera-devel@lists.libcamera.org Cc: Paul Elder , kieran.bingham@ideasonboard.com, barnabas.pocze@ideasonboard.com Subject: [PATCH v2 7/8] layer: Add layer to inject AeEnable control Date: Thu, 3 Jul 2025 20:42:22 +0900 Message-ID: <20250703114225.2074071-8-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.47.2 In-Reply-To: <20250703114225.2074071-1-paul.elder@ideasonboard.com> References: <20250703114225.2074071-1-paul.elder@ideasonboard.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" Add a layer to implement the AeEnable control, so that we can remove it from all IPAs and the Camera class, so that it is transparent to all parties and it is available for applications to use. Signed-off-by: Paul Elder --- For layers like this that we forsee to be "always built-in" we might want a way to build it into libcamera instead of into separate shared object files. In any case, this also serves as a demo of how I envision a layer implementation to work. Changes in v2: - add init() and terminate() - use closures - remove layer namespacing - add gnu_symbol_visibility --- src/layer/inject_controls/inject_controls.cpp | 176 ++++++++++++++++++ src/layer/inject_controls/inject_controls.h | 24 +++ src/layer/inject_controls/meson.build | 16 ++ src/layer/meson.build | 2 + 4 files changed, 218 insertions(+) create mode 100644 src/layer/inject_controls/inject_controls.cpp create mode 100644 src/layer/inject_controls/inject_controls.h create mode 100644 src/layer/inject_controls/meson.build diff --git a/src/layer/inject_controls/inject_controls.cpp b/src/layer/inject_controls/inject_controls.cpp new file mode 100644 index 000000000000..f12faff8318c --- /dev/null +++ b/src/layer/inject_controls/inject_controls.cpp @@ -0,0 +1,176 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2025, Ideas On Board Oy + * + * Layer implementation for injecting controls + */ + +#include "inject_controls.h" + +#include +#include + +#include +#include +#include +#include + +void *init([[maybe_unused]] const std::string &id) +{ + InjectControls *closure = new InjectControls; + *closure = {}; + return static_cast(closure); +} + +void terminate(void *closure) +{ + InjectControls *obj = static_cast(closure); + delete obj; +} + +libcamera::ControlInfoMap::Map updateControls(void *closure, libcamera::ControlInfoMap &ctrls) +{ + InjectControls *obj = static_cast(closure); + + auto it = ctrls.find(&libcamera::controls::ExposureTimeMode); + if (it != ctrls.end()) { + for (auto entry : it->second.values()) { + if (entry == libcamera::ControlValue(libcamera::controls::ExposureTimeModeAuto)) + obj->aeAvailable = true; + if (entry == libcamera::ControlValue(libcamera::controls::ExposureTimeModeManual)) + obj->meAvailable = true; + } + } + + it = ctrls.find(&libcamera::controls::AnalogueGainMode); + if (it != ctrls.end()) { + for (auto entry : it->second.values()) { + if (entry == libcamera::ControlValue(libcamera::controls::AnalogueGainModeAuto)) + obj->agAvailable = true; + if (entry == libcamera::ControlValue(libcamera::controls::AnalogueGainModeManual)) + obj->mgAvailable = true; + } + } + + std::set values; + if (obj->aeAvailable || obj->agAvailable) + values.insert(true); + if (obj->meAvailable || obj->mgAvailable) + values.insert(false); + + if (values.empty()) + return {}; + + if (values.size() == 1) { + bool value = *values.begin(); + return { { &libcamera::controls::AeEnable, + libcamera::ControlInfo(value, value, value) } }; + } + + return { { &libcamera::controls::AeEnable, libcamera::ControlInfo(false, true, true) } }; +} + +void queueRequest(void *closure, libcamera::Request *request) +{ + InjectControls *obj = static_cast(closure); + + libcamera::ControlList &ctrls = request->controls(); + auto aeEnable = ctrls.get(libcamera::controls::AeEnable); + if (!aeEnable) + return; + + if (*aeEnable) { + if (obj->aeAvailable) { + ctrls.set(libcamera::controls::ExposureTimeMode, + libcamera::controls::ExposureTimeModeAuto); + } + + if (obj->agAvailable) { + ctrls.set(libcamera::controls::AnalogueGainMode, + libcamera::controls::AnalogueGainModeAuto); + } + } else { + if (obj->meAvailable) { + ctrls.set(libcamera::controls::ExposureTimeMode, + libcamera::controls::ExposureTimeModeManual); + } + + if (obj->mgAvailable) { + ctrls.set(libcamera::controls::AnalogueGainMode, + libcamera::controls::AnalogueGainModeManual); + } + } +} + +void requestCompleted([[maybe_unused]] void *closure, libcamera::Request *request) +{ + libcamera::ControlList &metadata = request->metadata(); + + auto eMode = metadata.get(libcamera::controls::ExposureTimeMode); + auto aMode = metadata.get(libcamera::controls::AnalogueGainMode); + + if (!eMode && !aMode) + return; + + bool ae = eMode && eMode == libcamera::controls::ExposureTimeModeAuto; + bool me = eMode && eMode == libcamera::controls::ExposureTimeModeManual; + bool ag = aMode && aMode == libcamera::controls::AnalogueGainModeAuto; + bool mg = aMode && aMode == libcamera::controls::AnalogueGainModeManual; + + /* Exposure time not reported at all; use gain only */ + if (!ae && !me) { + metadata.set(libcamera::controls::AeEnable, ag); + return; + } + + /* Analogue gain not reported at all; use exposure time only */ + if (!ag && !mg) { + metadata.set(libcamera::controls::AeEnable, ae); + return; + } + + /* + * Gain mode and exposure mode are not equal; therefore at least one is + * manual, so set AeEnable to false + */ + if (ag != ae) { + metadata.set(libcamera::controls::AeEnable, false); + return; + } + + /* ag and ae are equal, so just choose one */ + metadata.set(libcamera::controls::AeEnable, ag); + return; +} + +namespace libcamera { + +extern "C" { + +[[gnu::visibility("default")]] +struct LayerInfo layerInfo{ + .name = "inject_controls", + .layerAPIVersion = 1, +}; + +[[gnu::visibility("default")]] +struct LayerInterface layerInterface{ + .init = init, + .terminate = terminate, + .bufferCompleted = nullptr, + .requestCompleted = requestCompleted, + .disconnected = nullptr, + .acquire = nullptr, + .release = nullptr, + .controls = updateControls, + .properties = nullptr, + .configure = nullptr, + .createRequest = nullptr, + .queueRequest = queueRequest, + .start = nullptr, + .stop = nullptr, +}; + +} + +} /* namespace libcamera */ diff --git a/src/layer/inject_controls/inject_controls.h b/src/layer/inject_controls/inject_controls.h new file mode 100644 index 000000000000..42c094d7f76a --- /dev/null +++ b/src/layer/inject_controls/inject_controls.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2025, Ideas On Board Oy + * + * Layer implementation for injecting controls + */ + +#pragma once + +#include +#include + +struct InjectControls { + bool aeAvailable; + bool meAvailable; + bool agAvailable; + bool mgAvailable; +}; + +void *init(const std::string &id); +void terminate(void *closure); +libcamera::ControlInfoMap::Map updateControls(void *closure, libcamera::ControlInfoMap &ctrls); +void queueRequest(void *closure, libcamera::Request *request); +void requestCompleted([[maybe_unused]] void *closure, libcamera::Request *request); diff --git a/src/layer/inject_controls/meson.build b/src/layer/inject_controls/meson.build new file mode 100644 index 000000000000..d1e402746990 --- /dev/null +++ b/src/layer/inject_controls/meson.build @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: CC0-1.0 + +layer_name = 'layer_inject_controls' + +layer_inject_sources = files([ + 'inject_controls.h', + 'inject_controls.cpp', +]) + +mod = shared_module(layer_name, layer_inject_sources, + name_prefix : '', + include_directories : [layer_includes], + dependencies : libcamera_public, + gnu_symbol_visibility: 'hidden', + install : true, + install_dir : layer_install_dir) diff --git a/src/layer/meson.build b/src/layer/meson.build index dee5e5ac5804..d5793f8c4525 100644 --- a/src/layer/meson.build +++ b/src/layer/meson.build @@ -8,3 +8,5 @@ layer_install_dir = libcamera_libdir / 'layers' config_h.set('LAYER_DIR', '"' + get_option('prefix') / layer_install_dir + '"') + +subdir('inject_controls')