From patchwork Thu Jun 26 09:59:41 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 23664 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 AD752BDCBF for ; Thu, 26 Jun 2025 10:00:32 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 0F9BE68DFE; Thu, 26 Jun 2025 12:00:32 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="roR3kXJi"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id E4E1F68DFC for ; Thu, 26 Jun 2025 12:00:24 +0200 (CEST) Received: from neptunite.hamster-moth.ts.net (unknown [IPv6:2404:7a81:160:2100:258b:9e43:6dff:c39d]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 1B1CA743; Thu, 26 Jun 2025 12:00:04 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1750932006; bh=a0vtHLYTYKS0mx8qsHaCE3ZbBQ8aSnBmLOL29vEioM0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=roR3kXJir96qGSYESEdR41c+eQvFKFeh5YCLuaYtuGDBNNm6cQdIAM23qasAn/whf kNdWTxpd46ARks0ZwYzOoemrFAJMhw9m8BtNBqiKZ1ItmaE9NM2srTqwp0l+VDcHIe 3l18b+WY/AxxDSJkGkZmoJq5yvYqcI95amFpCmuo= From: Paul Elder To: libcamera-devel@lists.libcamera.org Cc: Paul Elder , kieran.bingham@ideasonboard.com Subject: [RFC PATCH 6/7] layer: Add layer to inject AeEnable control Date: Thu, 26 Jun 2025 18:59:41 +0900 Message-ID: <20250626095944.1746345-7-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.47.2 In-Reply-To: <20250626095944.1746345-1-paul.elder@ideasonboard.com> References: <20250626095944.1746345-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. --- src/layer/inject_controls/inject_controls.cpp | 164 ++++++++++++++++++ src/layer/inject_controls/inject_controls.h | 29 ++++ src/layer/inject_controls/meson.build | 15 ++ src/layer/meson.build | 2 + 4 files changed, 210 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..133a8f51a1f6 --- /dev/null +++ b/src/layer/inject_controls/inject_controls.cpp @@ -0,0 +1,164 @@ +/* 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 + +namespace layer { + +namespace inject_controls { + +libcamera::ControlInfoMap::Map controls(libcamera::ControlInfoMap &ctrls) +{ + auto it = ctrls.find(&libcamera::controls::ExposureTimeMode); + if (it != ctrls.end()) { + for (auto entry : it->second.values()) { + if (entry == libcamera::ControlValue(libcamera::controls::ExposureTimeModeAuto)) + aeAvailable_ = true; + if (entry == libcamera::ControlValue(libcamera::controls::ExposureTimeModeManual)) + 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)) + agAvailable_ = true; + if (entry == libcamera::ControlValue(libcamera::controls::AnalogueGainModeManual)) + mgAvailable_ = true; + } + } + + std::set values; + if (aeAvailable_ || agAvailable_) + values.insert(true); + if (meAvailable_ || 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(libcamera::Request *request) +{ + libcamera::ControlList &ctrls = request->controls(); + auto aeEnable = ctrls.get(libcamera::controls::AeEnable); + if (!aeEnable) + return; + + if (*aeEnable) { + if (aeAvailable_) { + ctrls.set(libcamera::controls::ExposureTimeMode, + libcamera::controls::ExposureTimeModeAuto); + } + + if (agAvailable_) { + ctrls.set(libcamera::controls::AnalogueGainMode, + libcamera::controls::AnalogueGainModeAuto); + } + } else { + if (meAvailable_) { + ctrls.set(libcamera::controls::ExposureTimeMode, + libcamera::controls::ExposureTimeModeManual); + } + + if (mgAvailable_) { + ctrls.set(libcamera::controls::AnalogueGainMode, + libcamera::controls::AnalogueGainModeManual); + } + } +} + +void requestCompleted(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 inject_controls */ + +} /* namespace layer */ + +namespace libcamera { + +extern "C" { + +struct Layer layerInfo { + .name = "inject_controls", + .layerAPIVersion = 1, + .init = nullptr, + .bufferCompleted = nullptr, + .requestCompleted = layer::inject_controls::requestCompleted, + .disconnected = nullptr, + .acquire = nullptr, + .release = nullptr, + .controls = layer::inject_controls::controls, + .properties = nullptr, + .streams = nullptr, + .generateConfiguration = nullptr, + .configure = nullptr, + .createRequest = nullptr, + .queueRequest = layer::inject_controls::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..a65cda897836 --- /dev/null +++ b/src/layer/inject_controls/inject_controls.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2025, Ideas On Board Oy + * + * Layer implementation for injecting controls + */ + +#pragma once + +#include +#include + +namespace layer { + +namespace inject_controls { + +libcamera::ControlInfoMap::Map controls(libcamera::ControlInfoMap &ctrls); +void queueRequest(libcamera::Request *); +void requestCompleted(libcamera::Request *); + +bool initialized_ = false; +bool aeAvailable_ = false; +bool meAvailable_ = false; +bool agAvailable_ = false; +bool mgAvailable_ = false; + +} /* namespace inject_controls */ + +} /* namespace layer */ diff --git a/src/layer/inject_controls/meson.build b/src/layer/inject_controls/meson.build new file mode 100644 index 000000000000..72f22e184923 --- /dev/null +++ b/src/layer/inject_controls/meson.build @@ -0,0 +1,15 @@ +# 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, + 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')