Show a patch.

GET /api/patches/15184/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 15184,
    "url": "https://patchwork.libcamera.org/api/patches/15184/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/15184/",
    "project": {
        "id": 1,
        "url": "https://patchwork.libcamera.org/api/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": "<20211221043610.2512334-7-paul.elder@ideasonboard.com>",
    "date": "2021-12-21T04:36:08",
    "name": "[libcamera-devel,v3,6/8] android: Plumb all AE-related controls",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": false,
    "hash": "31e502f3a306866a78ed75724c3d4413d35c1ea3",
    "submitter": {
        "id": 17,
        "url": "https://patchwork.libcamera.org/api/people/17/?format=api",
        "name": "Paul Elder",
        "email": "paul.elder@ideasonboard.com"
    },
    "delegate": {
        "id": 17,
        "url": "https://patchwork.libcamera.org/api/users/17/?format=api",
        "username": "epaul",
        "first_name": "Paul",
        "last_name": "Elder",
        "email": "paul.elder@ideasonboard.com"
    },
    "mbox": "https://patchwork.libcamera.org/patch/15184/mbox/",
    "series": [
        {
            "id": 2849,
            "url": "https://patchwork.libcamera.org/api/series/2849/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=2849",
            "date": "2021-12-21T04:36:02",
            "name": "The Great AE Changes",
            "version": 3,
            "mbox": "https://patchwork.libcamera.org/series/2849/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/15184/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/15184/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 BC0D0C3259\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 21 Dec 2021 04:36:29 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 326BF60900;\n\tTue, 21 Dec 2021 05:36:29 +0100 (CET)",
            "from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 3A023608F8\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 21 Dec 2021 05:36:26 +0100 (CET)",
            "from pyrite.mediacom.info (unknown\n\t[IPv6:2604:2d80:ad90:fb00:96fd:8874:873:6c16])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 55BD2881;\n\tTue, 21 Dec 2021 05:36:25 +0100 (CET)"
        ],
        "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=\"WnU0IEU8\"; dkim-atps=neutral",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1640061386;\n\tbh=MyRNP/dDBWmJxj5zE8GNAUEn8v6pO9tLjUCL9N+TAy8=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=WnU0IEU8eGaJaedGE9Dx+soRM+N9swY/5Mq4bdQ28lzX3Rzm5UueAEIrRLtgp2wQV\n\tlqex/PTy2/qj9Q6M6LKZwzMxhM/RRwwlJORJwDS45NSQDvIiH+UIDgr51OX8TBhBAz\n\tiIFUlHQQmsLLvkSWGKJPEp1wFg+g/Q50Z5rmuEmc=",
        "From": "Paul Elder <paul.elder@ideasonboard.com>",
        "To": "libcamera-devel@lists.libcamera.org",
        "Date": "Mon, 20 Dec 2021 22:36:08 -0600",
        "Message-Id": "<20211221043610.2512334-7-paul.elder@ideasonboard.com>",
        "X-Mailer": "git-send-email 2.27.0",
        "In-Reply-To": "<20211221043610.2512334-1-paul.elder@ideasonboard.com>",
        "References": "<20211221043610.2512334-1-paul.elder@ideasonboard.com>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "Subject": "[libcamera-devel] [PATCH v3 6/8] 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- preview template: set exposure time to the minimum in the exposure\n  time range\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---\nChanges in v3:\n- rename AeMode to AutoMode as we'll also use it for awb later\n- set the exposure time in the preview template\n- a bunch of other big changes (mentioned by Jacopo, and discussed a\n  bit) were not done, as more discussion is needed\n\nNew in v2\n---\n src/android/camera_capabilities.cpp | 71 +++++++++++++++++++---\n src/android/camera_device.cpp       | 93 +++++++++++++++++++++++++++--\n src/android/camera_device.h         | 13 ++++\n src/android/camera_request.h        |  9 +++\n 4 files changed, 174 insertions(+), 12 deletions(-)",
    "diff": "diff --git a/src/android/camera_capabilities.cpp b/src/android/camera_capabilities.cpp\nindex a2e42a5c..318ab666 100644\n--- a/src/android/camera_capabilities.cpp\n+++ b/src/android/camera_capabilities.cpp\n@@ -922,12 +922,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 \tstd::vector<int32_t> aeCompensationRange = {\n \t\t0, 0,\n \t};\n@@ -988,10 +1038,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@@ -1468,6 +1514,12 @@ std::unique_ptr<CameraMetadata> CameraCapabilities::requestTemplateManual() cons\n \t\tmanualTemplate->appendEntry(ANDROID_CONTROL_MODE, mode);\n \t}\n \n+\tif (staticMetadata_->entryContains<uint8_t>(ANDROID_CONTROL_AE_AVAILABLE_MODES,\n+\t\t\t\t\t\t    ANDROID_CONTROL_AE_MODE_OFF)) {\n+\t\tuint8_t aeMode = ANDROID_CONTROL_AE_MODE_OFF;\n+\t\tmanualTemplate->appendEntry(ANDROID_CONTROL_AE_MODE, aeMode);\n+\t}\n+\n \treturn manualTemplate;\n }\n \n@@ -1542,6 +1594,11 @@ std::unique_ptr<CameraMetadata> CameraCapabilities::requestTemplatePreview() con\n \tuint8_t awbLock = ANDROID_CONTROL_AWB_LOCK_OFF;\n \trequestTemplate->addEntry(ANDROID_CONTROL_AWB_LOCK, awbLock);\n \n+\tif (availableRequestKeys_.count(ANDROID_SENSOR_EXPOSURE_TIME)) {\n+\t\tstaticMetadata_->getEntry(ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE, &entry);\n+\t\trequestTemplate->addEntry(ANDROID_SENSOR_EXPOSURE_TIME, entry.data.i64[0]);\n+\t}\n+\n \tuint8_t flashMode = ANDROID_FLASH_MODE_OFF;\n \trequestTemplate->addEntry(ANDROID_FLASH_MODE, flashMode);\n \ndiff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp\nindex 83825736..3ade44c4 100644\n--- a/src/android/camera_device.cpp\n+++ b/src/android/camera_device.cpp\n@@ -246,7 +246,9 @@ bool validateCropRotate(const camera3_stream_configuration_t &streamList)\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@@ -812,6 +814,59 @@ 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+\tCamera3RequestDescriptor::AutoMode aeMode;\n+\tif (aeLocked_ && aeOn_)\n+\t\taeMode = Camera3RequestDescriptor::AutoMode::Lock;\n+\telse if (aeLocked_ && !aeOn_)\n+\t\taeMode = Camera3RequestDescriptor::AutoMode::Manual;\n+\telse if (!aeLocked_ && aeOn_)\n+\t\taeMode = Camera3RequestDescriptor::AutoMode::Auto;\n+\telse /* !aeLocked_ && !aeOn_ */\n+\t\taeMode = Camera3RequestDescriptor::AutoMode::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     aeMode == Camera3RequestDescriptor::AutoMode::Auto ?\n+\t\t\t     controls::ExposureTimeModeAuto :\n+\t\t\t     controls::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     aeMode == Camera3RequestDescriptor::AutoMode::Auto ?\n+\t\t\t     controls::AnalogueGainModeAuto :\n+\t\t\t     controls::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}\n+\t}\n+\n+\t/* Trigger libcamera's locked -> manual state change */\n+\tif (aeMode == Camera3RequestDescriptor::AutoMode::Manual) {\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@@ -1336,11 +1391,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_ == Camera3RequestDescriptor::AutoMode::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_ != Camera3RequestDescriptor::AutoMode::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@@ -1357,6 +1417,29 @@ 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\tLOG(HAL, Error) << \"Invalid AeState, setting converged\";\n+\t\t}\n+\t}\n+\tif (descriptor.aeMode_ == Camera3RequestDescriptor::AutoMode::Lock)\n+\t\tvalue = ANDROID_CONTROL_AE_STATE_LOCKED;\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 64050416..a65f1670 100644\n--- a/src/android/camera_device.h\n+++ b/src/android/camera_device.h\n@@ -126,5 +126,18 @@ private:\n \tint facing_;\n \tint orientation_;\n \n+\t/*\n+\t * Normally resubmission of controls would be handled on libcamera's\n+\t * side, but these controls are not one-to-one between libcamera and\n+\t * android, so we need to save them here\n+\t */\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 };\ndiff --git a/src/android/camera_request.h b/src/android/camera_request.h\nindex 37b6ae32..b2809179 100644\n--- a/src/android/camera_request.h\n+++ b/src/android/camera_request.h\n@@ -28,6 +28,12 @@ class CameraStream;\n class Camera3RequestDescriptor\n {\n public:\n+\tenum class AutoMode {\n+\t\tAuto,\n+\t\tLock,\n+\t\tManual,\n+\t};\n+\n \tenum class Status {\n \t\tSuccess,\n \t\tError,\n@@ -78,6 +84,9 @@ public:\n \tbool complete_ = false;\n \tStatus status_ = Status::Success;\n \n+\t/* The libcamera internal AE state for this request */\n+\tAutoMode aeMode_ = AutoMode::Auto;\n+\n private:\n \tLIBCAMERA_DISABLE_COPY(Camera3RequestDescriptor)\n };\n",
    "prefixes": [
        "libcamera-devel",
        "v3",
        "6/8"
    ]
}