Patch Detail
Show a patch.
GET /api/1.1/patches/14021/?format=api
{ "id": 14021, "url": "https://patchwork.libcamera.org/api/1.1/patches/14021/?format=api", "web_url": "https://patchwork.libcamera.org/patch/14021/", "project": { "id": 1, "url": "https://patchwork.libcamera.org/api/1.1/projects/1/?format=api", "name": "libcamera", "link_name": "libcamera", "list_id": "libcamera_core", "list_email": "libcamera-devel@lists.libcamera.org", "web_url": "", "scm_url": "", "webscm_url": "" }, "msgid": "<20211001103325.1077590-7-paul.elder@ideasonboard.com>", "date": "2021-10-01T10:33:24", "name": "[libcamera-devel,v2,6/7] android: Plumb all AE-related controls", "commit_ref": null, "pull_url": null, "state": "superseded", "archived": false, "hash": "f4272944e7c5af10b684d5b72dcef16e8951edcd", "submitter": { "id": 17, "url": "https://patchwork.libcamera.org/api/1.1/people/17/?format=api", "name": "Paul Elder", "email": "paul.elder@ideasonboard.com" }, "delegate": { "id": 17, "url": "https://patchwork.libcamera.org/api/1.1/users/17/?format=api", "username": "epaul", "first_name": "Paul", "last_name": "Elder", "email": "paul.elder@ideasonboard.com" }, "mbox": "https://patchwork.libcamera.org/patch/14021/mbox/", "series": [ { "id": 2589, "url": "https://patchwork.libcamera.org/api/1.1/series/2589/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=2589", "date": "2021-10-01T10:33:18", "name": "The Great AE Changes", "version": 2, "mbox": "https://patchwork.libcamera.org/series/2589/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/14021/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/14021/checks/", "tags": {}, "headers": { "Return-Path": "<libcamera-devel-bounces@lists.libcamera.org>", "X-Original-To": "parsemail@patchwork.libcamera.org", "Delivered-To": "parsemail@patchwork.libcamera.org", "Received": [ "from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 79B7EBDC71\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 1 Oct 2021 10:33:49 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 41197691B6;\n\tFri, 1 Oct 2021 12:33:49 +0200 (CEST)", "from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 3E153691AA\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 1 Oct 2021 12:33:48 +0200 (CEST)", "from pyrite.rasen.tech (unknown\n\t[IPv6:2400:4051:61:600:2c71:1b79:d06d:5032])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id CC763A16;\n\tFri, 1 Oct 2021 12:33:46 +0200 (CEST)" ], "Authentication-Results": "lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"G99FhBgo\"; dkim-atps=neutral", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1633084428;\n\tbh=2JdAS4NO+dDKWBHjAjtpumf0GTZhtUlaaonJgmNDkmI=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=G99FhBgoJWg85Q4gALp9rJjCzoYGXdDBLI50eFtGZInl7lEUz9KtG66CYVXrk4ESk\n\tj1OyDTsUHalCK2/WzMDQP7U+NJvQ0wwja7+P5aaQ0tcMTVZMc+0HrIQ1T1kgOpK9Z4\n\tt+x+Yd3Xb2Y4AYbXgzLCgAvWN2shg/je8kckW37w=", "From": "Paul Elder <paul.elder@ideasonboard.com>", "To": "libcamera-devel@lists.libcamera.org", "Date": "Fri, 1 Oct 2021 19:33:24 +0900", "Message-Id": "<20211001103325.1077590-7-paul.elder@ideasonboard.com>", "X-Mailer": "git-send-email 2.27.0", "In-Reply-To": "<20211001103325.1077590-1-paul.elder@ideasonboard.com>", "References": "<20211001103325.1077590-1-paul.elder@ideasonboard.com>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit", "Subject": "[libcamera-devel] [PATCH v2 6/7] android: Plumb all AE-related\n\tcontrols", "X-BeenThere": "libcamera-devel@lists.libcamera.org", "X-Mailman-Version": "2.1.29", "Precedence": "list", "List-Id": "<libcamera-devel.lists.libcamera.org>", "List-Unsubscribe": "<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>", "List-Archive": "<https://lists.libcamera.org/pipermail/libcamera-devel/>", "List-Post": "<mailto:libcamera-devel@lists.libcamera.org>", "List-Help": "<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>", "List-Subscribe": "<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>", "Errors-To": "libcamera-devel-bounces@lists.libcamera.org", "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>" }, "content": "With the AE-related controls reorganized in libcamera, with separate\nvalue and lever controls for exposure and gain, plumb these through the\nHAL layer (in order of appearence in the patch):\n- static metadata: available AE modes, AE lock available\n- manual template: add AE off (the other templates already have AE on)\n- request metadata: HAL -> libcamera controls conversion\n- result metadata: libcamera -> HAL controls conversion\n- result metadata: AE state\n\nWe add class variables to CameraDevice to save the last set android AE\ncontrols, as that is how android controls function (no need to resubmit\ncontrols if they are unchanged).\n\nWe also save libcamera's AE state in the request descriptor, as\notherwise there is insufficient information from libcamera's result\nmetadata alone to tell if the state is locked or manual (as they are an\ninternal state in libcamera).\n\nBug: https://bugs.libcamera.org/show_bug.cgi?id=42\nBug: https://bugs.libcamera.org/show_bug.cgi?id=43\nBug: https://bugs.libcamera.org/show_bug.cgi?id=47\nSigned-off-by: Paul Elder <paul.elder@ideasonboard.com>\n\n---\nNew in v2\n---\n src/android/camera_capabilities.cpp | 63 ++++++++++++++++---\n src/android/camera_device.cpp | 98 +++++++++++++++++++++++++++--\n src/android/camera_device.h | 16 +++++\n 3 files changed, 165 insertions(+), 12 deletions(-)", "diff": "diff --git a/src/android/camera_capabilities.cpp b/src/android/camera_capabilities.cpp\nindex f7a6cda9..3fed3f83 100644\n--- a/src/android/camera_capabilities.cpp\n+++ b/src/android/camera_capabilities.cpp\n@@ -830,12 +830,62 @@ int CameraCapabilities::initializeStaticMetadata()\n \tstaticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES,\n \t\t\t\t aeAvailableAntiBandingModes);\n \n-\tstd::vector<uint8_t> aeAvailableModes = {\n-\t\tANDROID_CONTROL_AE_MODE_ON,\n-\t};\n+\tstd::vector<uint8_t> aeAvailableModes;\n+\t/* This will cause problems if one supports only on and the other only off */\n+\tbool aeAddedOn = false;\n+\tbool aeAddedOff = false;\n+\n+\tconst auto &analogGainModeInfo = controlsInfo.find(&controls::AnalogueGainMode);\n+\tif (analogGainModeInfo != controlsInfo.end()) {\n+\t\tfor (const auto &value : analogGainModeInfo->second.values()) {\n+\t\t\tswitch (value.get<int32_t>()) {\n+\t\t\tcase controls::AnalogueGainModeAuto:\n+\t\t\t\tif (!aeAddedOn)\n+\t\t\t\t\taeAvailableModes.push_back(ANDROID_CONTROL_AE_MODE_ON);\n+\t\t\t\taeAddedOn = true;\n+\t\t\t\tbreak;\n+\t\t\tcase controls::AnalogueGainModeDisabled:\n+\t\t\t\tif (!aeAddedOff)\n+\t\t\t\t\taeAvailableModes.push_back(ANDROID_CONTROL_AE_MODE_OFF);\n+\t\t\t\taeAddedOff = true;\n+\t\t\t\tbreak;\n+\t\t\tdefault:\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tconst auto &exposureTimeModeInfo = controlsInfo.find(&controls::ExposureTimeMode);\n+\tif (exposureTimeModeInfo != controlsInfo.end()) {\n+\t\tfor (const auto &value : exposureTimeModeInfo->second.values()) {\n+\t\t\tswitch (value.get<int32_t>()) {\n+\t\t\tcase controls::ExposureTimeModeAuto:\n+\t\t\t\tif (!aeAddedOn)\n+\t\t\t\t\taeAvailableModes.push_back(ANDROID_CONTROL_AE_MODE_ON);\n+\t\t\t\taeAddedOn = true;\n+\t\t\t\tbreak;\n+\t\t\tcase controls::ExposureTimeModeDisabled:\n+\t\t\t\tif (!aeAddedOff)\n+\t\t\t\t\taeAvailableModes.push_back(ANDROID_CONTROL_AE_MODE_OFF);\n+\t\t\t\taeAddedOff = true;\n+\t\t\t\tbreak;\n+\t\t\tdefault:\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tif (aeAvailableModes.empty())\n+\t\taeAvailableModes.push_back(ANDROID_CONTROL_AE_MODE_ON);\n \tstaticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_MODES,\n \t\t\t\t aeAvailableModes);\n \n+\t/* In libcamera, turning off AE is equivalient to locking. */\n+\tuint8_t aeLockAvailable = aeAddedOff ? ANDROID_CONTROL_AE_LOCK_AVAILABLE_TRUE\n+\t\t\t\t\t : ANDROID_CONTROL_AE_LOCK_AVAILABLE_FALSE;\n+\tstaticMetadata_->addEntry(ANDROID_CONTROL_AE_LOCK_AVAILABLE,\n+\t\t\t\t aeLockAvailable);\n+\n \tint64_t minFrameDurationNsec = -1;\n \tint64_t maxFrameDurationNsec = -1;\n \tconst auto frameDurationsInfo = controlsInfo.find(&controls::FrameDurationLimits);\n@@ -946,10 +996,6 @@ int CameraCapabilities::initializeStaticMetadata()\n \tstaticMetadata_->addEntry(ANDROID_CONTROL_SCENE_MODE_OVERRIDES,\n \t\t\t\t sceneModesOverride);\n \n-\tuint8_t aeLockAvailable = ANDROID_CONTROL_AE_LOCK_AVAILABLE_FALSE;\n-\tstaticMetadata_->addEntry(ANDROID_CONTROL_AE_LOCK_AVAILABLE,\n-\t\t\t\t aeLockAvailable);\n-\n \tuint8_t awbLockAvailable = ANDROID_CONTROL_AWB_LOCK_AVAILABLE_FALSE;\n \tstaticMetadata_->addEntry(ANDROID_CONTROL_AWB_LOCK_AVAILABLE,\n \t\t\t\t awbLockAvailable);\n@@ -1358,6 +1404,9 @@ std::unique_ptr<CameraMetadata> CameraCapabilities::requestTemplateManual() cons\n \tif (!manualTemplate)\n \t\treturn nullptr;\n \n+\tuint8_t aeMode = ANDROID_CONTROL_AE_MODE_OFF;\n+\tmanualTemplate->addEntry(ANDROID_CONTROL_AE_MODE, aeMode);\n+\n \treturn manualTemplate;\n }\n \ndiff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp\nindex ef4fbab8..d5027ec5 100644\n--- a/src/android/camera_device.cpp\n+++ b/src/android/camera_device.cpp\n@@ -263,7 +263,9 @@ CameraDevice::Camera3RequestDescriptor::Camera3RequestDescriptor(\n \n CameraDevice::CameraDevice(unsigned int id, std::shared_ptr<Camera> camera)\n \t: id_(id), state_(State::Stopped), camera_(std::move(camera)),\n-\t facing_(CAMERA_FACING_FRONT), orientation_(0)\n+\t facing_(CAMERA_FACING_FRONT), orientation_(0),\n+\t aeOn_(true), aeLocked_(false), lastExposureTime_(0),\n+\t lastAnalogueGain_(1.0f), lastDigitalGain_(1.0f)\n {\n \tcamera_->requestCompleted.connect(this, &CameraDevice::requestComplete);\n \n@@ -857,6 +859,62 @@ int CameraDevice::processControls(Camera3RequestDescriptor *descriptor)\n \t\tcontrols.set(controls::draft::TestPatternMode, testPatternMode);\n \t}\n \n+\t/*\n+\t * \\todo Can we trust that we won't receive values that we didn't\n+\t * report supporting?\n+\t */\n+\tif (settings.getEntry(ANDROID_CONTROL_AE_LOCK, &entry))\n+\t\taeLocked_ = *entry.data.u8 != ANDROID_CONTROL_AE_LOCK_OFF;\n+\n+\tif (settings.getEntry(ANDROID_CONTROL_AE_MODE, &entry))\n+\t\taeOn_ = *entry.data.u8 != ANDROID_CONTROL_AE_MODE_OFF;\n+\n+\tAeMode aeMode;\n+\tif (aeLocked_ && aeOn_)\n+\t\taeMode = AeMode::Lock;\n+\telse if (aeLocked_ && !aeOn_)\n+\t\taeMode = AeMode::Manual;\n+\telse if (!aeLocked_ && aeOn_)\n+\t\taeMode = AeMode::Auto;\n+\telse /* !aeLocked_ && !aeOn_ */\n+\t\taeMode = AeMode::Manual;\n+\n+\t/* Save this so that we can recover it in the result */\n+\tdescriptor->aeMode_ = aeMode;\n+\n+\tconst auto &eInfo = camera_->controls().find(&controls::ExposureTimeMode);\n+\tif (eInfo != camera_->controls().end()) {\n+\t\tcontrols.set(controls::ExposureTimeMode,\n+\t\t\t\taeMode == AeMode::Auto ?\n+\t\t\t\tcontrols::ExposureTimeModeAuto :\n+\t\t\t\tcontrols::ExposureTimeModeDisabled);\n+\t}\n+\n+\tconst auto &gInfo = camera_->controls().find(&controls::AnalogueGainMode);\n+\tif (gInfo != camera_->controls().end()) {\n+\t\tcontrols.set(controls::AnalogueGainMode,\n+\t\t\t\taeMode == AeMode::Auto ?\n+\t\t\t\tcontrols::AnalogueGainModeAuto :\n+\t\t\t\tcontrols::AnalogueGainModeDisabled);\n+\t}\n+\n+\tif (settings.getEntry(ANDROID_SENSOR_EXPOSURE_TIME, &entry)) {\n+\t\tconst auto &info = camera_->controls().find(&controls::ExposureTime);\n+\t\tif (info != camera_->controls().end()) {\n+\t\t\tlastExposureTime_ = (*entry.data.i64) / 1000;\n+\t\t\t/* Don't disable libcamera's internal AeMode::Lock */\n+\t\t\tif (aeMode != AeMode::Lock)\n+\t\t\t\tcontrols.set(controls::ExposureTime, lastExposureTime_);\n+\t\t}\n+\t}\n+\n+\t/* Trigger libcamera's locked -> manual state change */\n+\tif (aeMode == AeMode::Manual && !settings.hasEntry(ANDROID_SENSOR_EXPOSURE_TIME)) {\n+\t\tconst auto &info = camera_->controls().find(&controls::ExposureTime);\n+\t\tif (info != camera_->controls().end())\n+\t\t\tcontrols.set(controls::ExposureTime, lastExposureTime_);\n+\t}\n+\n \treturn 0;\n }\n \n@@ -1345,11 +1403,16 @@ CameraDevice::getResultMetadata(const Camera3RequestDescriptor &descriptor) cons\n \tresultMetadata->addEntry(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,\n \t\t\t\t value32);\n \n-\tvalue = ANDROID_CONTROL_AE_LOCK_OFF;\n-\tresultMetadata->addEntry(ANDROID_CONTROL_AE_LOCK, value);\n+\tuint8_t aeLock = (descriptor.aeMode_ == AeMode::Lock)\n+\t\t ? ANDROID_CONTROL_AE_LOCK_ON\n+\t\t : ANDROID_CONTROL_AE_LOCK_OFF;\n+\tresultMetadata->addEntry(ANDROID_CONTROL_AE_LOCK, aeLock);\n \n-\tvalue = ANDROID_CONTROL_AE_MODE_ON;\n-\tresultMetadata->addEntry(ANDROID_CONTROL_AE_MODE, value);\n+\t/* Locked means auto + locked in android */\n+\tuint8_t aeMode = (descriptor.aeMode_ != AeMode::Manual)\n+\t\t ? ANDROID_CONTROL_AE_MODE_ON\n+\t\t : ANDROID_CONTROL_AE_MODE_OFF;\n+\tresultMetadata->addEntry(ANDROID_CONTROL_AE_MODE, aeMode);\n \n \tif (settings.getEntry(ANDROID_CONTROL_AE_TARGET_FPS_RANGE, &entry))\n \t\t/*\n@@ -1366,6 +1429,31 @@ CameraDevice::getResultMetadata(const Camera3RequestDescriptor &descriptor) cons\n \tresultMetadata->addEntry(ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER, value);\n \n \tvalue = ANDROID_CONTROL_AE_STATE_CONVERGED;\n+\tif (metadata.contains(controls::AeState)) {\n+\t\tswitch (metadata.get(controls::AeState)) {\n+\t\tcase controls::AeStateInactive:\n+\t\t\tvalue = ANDROID_CONTROL_AE_STATE_INACTIVE;\n+\t\t\tbreak;\n+\t\tcase controls::AeStateSearching:\n+\t\t\tvalue = ANDROID_CONTROL_AE_STATE_SEARCHING;\n+\t\t\tbreak;\n+\t\tcase controls::AeStateConverged:\n+\t\t\tvalue = ANDROID_CONTROL_AE_STATE_CONVERGED;\n+\t\t\tbreak;\n+\t\tcase controls::AeStateFlashRequired:\n+\t\t\tvalue = ANDROID_CONTROL_AE_STATE_FLASH_REQUIRED;\n+\t\t\tbreak;\n+\t\tcase controls::AeStatePrecapture:\n+\t\t\tvalue = ANDROID_CONTROL_AE_STATE_PRECAPTURE;\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tif (descriptor.aeMode_ == AeMode::Lock) {\n+\t\t\t\tvalue = ANDROID_CONTROL_AE_STATE_LOCKED;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tLOG(HAL, Error) << \"Invalid AeState, setting converged\";\n+\t\t}\n+\t}\n \tresultMetadata->addEntry(ANDROID_CONTROL_AE_STATE, value);\n \n \tvalue = ANDROID_CONTROL_AF_MODE_OFF;\ndiff --git a/src/android/camera_device.h b/src/android/camera_device.h\nindex b7d774fe..f693cdbc 100644\n--- a/src/android/camera_device.h\n+++ b/src/android/camera_device.h\n@@ -73,6 +73,12 @@ private:\n \n \tCameraDevice(unsigned int id, std::shared_ptr<libcamera::Camera> camera);\n \n+\tenum class AeMode {\n+\t\tAuto,\n+\t\tLock,\n+\t\tManual,\n+\t};\n+\n \tstruct Camera3RequestDescriptor {\n \t\tenum class Status {\n \t\t\tPending,\n@@ -96,6 +102,9 @@ private:\n \n \t\tcamera3_capture_result_t captureResult_ = {};\n \t\tStatus status_ = Status::Pending;\n+\n+\t\t/* The libcamera internal AE state for this request */\n+\t\tAeMode aeMode_ = AeMode::Auto;\n \t};\n \n \tenum class State {\n@@ -146,6 +155,13 @@ private:\n \tint facing_;\n \tint orientation_;\n \n+\t/* Track the last-set android AE controls */\n+\tbool aeOn_;\n+\tbool aeLocked_;\n+\tint32_t lastExposureTime_;\n+\tfloat lastAnalogueGain_;\n+\tfloat lastDigitalGain_;\n+\n \tCameraMetadata lastSettings_;\n };\n \n", "prefixes": [ "libcamera-devel", "v2", "6/7" ] }