Show a patch.

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

{
    "id": 14022,
    "url": "https://patchwork.libcamera.org/api/1.1/patches/14022/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/14022/",
    "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-8-paul.elder@ideasonboard.com>",
    "date": "2021-10-01T10:33:25",
    "name": "[libcamera-devel,v2,7/7] android: Plumb all sensitivity-related controls",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": false,
    "hash": "974a8513dabdde6637ae8500fc1a477f4456b7a8",
    "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/14022/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/14022/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/14022/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 DD7B3BDC71\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri,  1 Oct 2021 10:33:51 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id AB748691BB;\n\tFri,  1 Oct 2021 12:33:51 +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 1CD65691AC\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri,  1 Oct 2021 12:33:50 +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 A74F1596;\n\tFri,  1 Oct 2021 12:33:48 +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=\"jkvv5wqh\"; dkim-atps=neutral",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1633084429;\n\tbh=K9auD2iBnOpNG4ZMLIaruFtpf06EYn/2zB6+cFeCFGs=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=jkvv5wqh03b48sSkc6TyQECEo0V5/+QZniIsUHS4ZP9SjQ0qUucOZTQQG57IdNRvR\n\t1hg+iLj4iZUPkH+8Jx5vhdHYsX6bZvCa9NDM+eM7GFzFO6ORn+7IHwbOAmB9s7MagJ\n\tcKhBs4Vy+hwFCflQDwnMQFStGe6p6tE5wSJzAS4A=",
        "From": "Paul Elder <paul.elder@ideasonboard.com>",
        "To": "libcamera-devel@lists.libcamera.org",
        "Date": "Fri,  1 Oct 2021 19:33:25 +0900",
        "Message-Id": "<20211001103325.1077590-8-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 7/7] android: Plumb all\n\tsensitivity-related controls",
        "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": "Plumb through the HAL the following three controls:\n- ANDROID_SENSOR_INFO_SENSITIVITY_RANGE (static)\n- ANDROID_SENSOR_MAX_ANALOG_SENSITIVITY (static)\n- ANDROID_SENSOR_SENSITIVITY (request, result)\n\nAlso add a minISO to the HAL config, and add appropriate capability\ndetection.\n\nThe sensitivity range comes from:\n- min: hardcode to 100, or take minISO from the HAL config\n- max: set to minISO * max(AnalogueGain) * max(DigitalGain)\n\nThe max analog sensitivity comes from:\n- hardcode to minISO * max(AnalogueGain)\n\nThe request sensitivity is mapped to requested ISO / minISO, and split\nbetween analog and digital gain.\n\nThe result sensitivity is mapped from minISO * analog gain * digital\ngain.\n\nBug: https://bugs.libcamera.org/show_bug.cgi?id=47\nSigned-off-by: Paul Elder <paul.elder@ideasonboard.com>\n\n---\nNo changes in v2\n- I'm moving this into the AE-related series, because this is AE-related\n---\n src/android/camera_capabilities.cpp | 61 ++++++++++++++++++++---\n src/android/camera_capabilities.h   |  3 +-\n src/android/camera_device.cpp       | 76 ++++++++++++++++++++++++++++-\n src/android/camera_device.h         |  1 +\n src/android/camera_hal_config.cpp   | 10 +++-\n src/android/camera_hal_config.h     |  1 +\n 6 files changed, 142 insertions(+), 10 deletions(-)",
    "diff": "diff --git a/src/android/camera_capabilities.cpp b/src/android/camera_capabilities.cpp\nindex 3fed3f83..8e84a736 100644\n--- a/src/android/camera_capabilities.cpp\n+++ b/src/android/camera_capabilities.cpp\n@@ -233,6 +233,15 @@ bool CameraCapabilities::validateManualSensorCapability()\n \t\treturn false;\n \t}\n \n+\t/*\n+\t * Checking the sensitivity range is sufficient, as it also covers the\n+\t * max analog sensitivity and the sensor sensitivity request/result key\n+\t */\n+\tif (!staticMetadata_->hasEntry(ANDROID_SENSOR_INFO_SENSITIVITY_RANGE)) {\n+\t\tLOG(HAL, Info) << noMode << \"missing sensitivity range\";\n+\t\treturn false;\n+\t}\n+\n \tif (!staticMetadata_->hasEntry(ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE)) {\n \t\tLOG(HAL, Info) << noMode << \"missing exposure time range\";\n \t\treturn false;\n@@ -401,11 +410,12 @@ void CameraCapabilities::computeHwLevel(\n }\n \n int CameraCapabilities::initialize(std::shared_ptr<Camera> camera,\n-\t\t\t\t   int orientation, int facing)\n+\t\t\t\t   int orientation, int facing, int minISO)\n {\n \tcamera_ = camera;\n \torientation_ = orientation;\n \tfacing_ = facing;\n+\tminISO_ = minISO;\n \trawStreamAvailable_ = false;\n \n \t/* Acquire the camera and initialize available stream configurations. */\n@@ -721,7 +731,6 @@ int CameraCapabilities::initializeStaticMetadata()\n \t\tANDROID_SENSOR_INFO_MAX_FRAME_DURATION,\n \t\tANDROID_SENSOR_INFO_PHYSICAL_SIZE,\n \t\tANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,\n-\t\tANDROID_SENSOR_INFO_SENSITIVITY_RANGE,\n \t\tANDROID_SENSOR_INFO_TIMESTAMP_SOURCE,\n \t\tANDROID_SENSOR_ORIENTATION,\n \t\tANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,\n@@ -1077,11 +1086,49 @@ int CameraCapabilities::initializeStaticMetadata()\n \t\t\t\t\t  data);\n \t}\n \n-\tint32_t sensitivityRange[] = {\n-\t\t32, 2400,\n-\t};\n-\tstaticMetadata_->addEntry(ANDROID_SENSOR_INFO_SENSITIVITY_RANGE,\n-\t\t\t\t  sensitivityRange);\n+\t/*\n+\t * The following three controls are only necessary for FULL\n+\t * (specifically the manual sensor capability):\n+\t * - ANDROID_SENSOR_INFO_SENSITIVITY_RANGE (static)\n+\t * - ANDROID_SENSOR_MAX_ANALOG_SENSITIVITY (static)\n+\t * - ANDROID_SENSOR_SENSITIVITY (request, result)\n+\t *\n+\t * The first and second depend on analog gain, and the third depend on\n+\t * both, so if analog gain is not available then we can cross out all\n+\t * of them. We'll default to 100 minimum sensitivity if it isn't\n+\t * specified in the HAL configuration file.\n+\t *\n+\t * Digital gain is optional; if it's unavailable then the max analog\n+\t * sensitivity will be equal to the max sensitivity range.\n+\t *\n+\t * The minimum sensitivity must be at most 100, while the maximum must\n+\t * be at least 800.\n+\t */\n+\tconst auto &analogGainInfo = controlsInfo.find(&controls::AnalogueGain);\n+\tif (analogGainInfo != controlsInfo.end()) {\n+\t\tfloat maxAnalogGain = analogGainInfo->second.max().get<float>();\n+\n+\t\tconst auto &digitalGainInfo = controlsInfo.find(&controls::DigitalGain);\n+\t\tfloat maxDigitalGain = digitalGainInfo == controlsInfo.end() ?\n+\t\t\t1.0f :\n+\t\t\tdigitalGainInfo->second.max().get<float>();\n+\n+\t\tint32_t maxISO = minISO_ * maxAnalogGain * maxDigitalGain;\n+\t\tint32_t sensitivityRange[] = {\n+\t\t\tminISO_,\n+\t\t\tmaxISO < 800 ? 800 : maxISO,\n+\t\t};\n+\t\tstaticMetadata_->addEntry(ANDROID_SENSOR_INFO_SENSITIVITY_RANGE,\n+\t\t\t\t\t  sensitivityRange);\n+\n+\t\tstaticMetadata_->addEntry(ANDROID_SENSOR_MAX_ANALOG_SENSITIVITY,\n+\t\t\t\t\t  minISO_ * maxAnalogGain);\n+\n+\t\tavailableCharacteristicsKeys_.insert(ANDROID_SENSOR_INFO_SENSITIVITY_RANGE);\n+\t\tavailableCharacteristicsKeys_.insert(ANDROID_SENSOR_MAX_ANALOG_SENSITIVITY);\n+\t\tavailableRequestKeys_.insert(ANDROID_SENSOR_SENSITIVITY);\n+\t\tavailableResultKeys_.insert(ANDROID_SENSOR_SENSITIVITY);\n+\t}\n \n \t/* Report the color filter arrangement if the camera reports it. */\n \tif (properties.contains(properties::draft::ColorFilterArrangement)) {\ndiff --git a/src/android/camera_capabilities.h b/src/android/camera_capabilities.h\nindex a1259699..b1eee37f 100644\n--- a/src/android/camera_capabilities.h\n+++ b/src/android/camera_capabilities.h\n@@ -26,7 +26,7 @@ public:\n \tCameraCapabilities() = default;\n \n \tint initialize(std::shared_ptr<libcamera::Camera> camera,\n-\t\t       int orientation, int facing);\n+\t\t       int orientation, int facing, int minISO);\n \n \tCameraMetadata *staticMetadata() const { return staticMetadata_.get(); }\n \tlibcamera::PixelFormat toPixelFormat(int format) const;\n@@ -68,6 +68,7 @@ private:\n \n \tint facing_;\n \tint orientation_;\n+\tint minISO_;\n \tbool rawStreamAvailable_;\n \tcamera_metadata_enum_android_info_supported_hardware_level hwLevel_;\n \tstd::set<camera_metadata_enum_android_request_available_capabilities> capabilities_;\ndiff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp\nindex d5027ec5..8854a960 100644\n--- a/src/android/camera_device.cpp\n+++ b/src/android/camera_device.cpp\n@@ -393,7 +393,15 @@ int CameraDevice::initialize(const CameraConfigData *cameraConfigData)\n \t\torientation_ = 0;\n \t}\n \n-\treturn capabilities_.initialize(camera_, orientation_, facing_);\n+\tif (cameraConfigData && cameraConfigData->minISO != -1) {\n+\t\tminISO_ = cameraConfigData->minISO;\n+\t} else {\n+\t\tLOG(HAL, Error)\n+\t\t\t<< \"Minimum ISO not in configuration file. Using 100.\";\n+\t\tminISO_ = 100;\n+\t}\n+\n+\treturn capabilities_.initialize(camera_, orientation_, facing_, minISO_);\n }\n \n /*\n@@ -908,6 +916,51 @@ int CameraDevice::processControls(Camera3RequestDescriptor *descriptor)\n \t\t}\n \t}\n \n+\tif (settings.getEntry(ANDROID_SENSOR_SENSITIVITY, &entry)) {\n+\t\tconst auto &info = camera_->controls().find(&controls::AnalogueGain);\n+\t\tif (info != camera_->controls().end()) {\n+\t\t\tconst auto &dInfo =\n+\t\t\t\tcamera_->controls().find(&controls::DigitalGain);\n+\t\t\tbool digitalGainAvailable =\n+\t\t\t\tdInfo == camera_->controls().end();\n+\n+\t\t\tfloat maxAnalogGain = info->second.max().get<float>();\n+\t\t\tfloat maxDigitalGain = digitalGainAvailable ?\n+\t\t\t\t\t       dInfo->second.max().get<float>() : 1.0f;\n+\n+\t\t\t/* target ISO / min ISO = gain */\n+\t\t\tfloat gain = *entry.data.i32 / minISO_;\n+\n+\t\t\t/*\n+\t\t\t * Max out analog gain before applying digital gain, if\n+\t\t\t * digital gain is available.\n+\t\t\t */\n+\t\t\tbool setDigital = false;\n+\t\t\tif (gain <= maxAnalogGain) {\n+\t\t\t\tlastAnalogueGain_ = gain;\n+\t\t\t} else if (gain <= maxAnalogGain * maxDigitalGain) {\n+\t\t\t\tlastAnalogueGain_ = maxAnalogGain;\n+\t\t\t\tif (digitalGainAvailable) {\n+\t\t\t\t\tsetDigital = true;\n+\t\t\t\t\tlastDigitalGain_ = gain / maxAnalogGain;\n+\t\t\t\t}\n+\t\t\t} else {\n+\t\t\t\tlastAnalogueGain_ = maxAnalogGain;\n+\t\t\t\tif (digitalGainAvailable) {\n+\t\t\t\t\tsetDigital = true;\n+\t\t\t\t\tlastDigitalGain_ = maxDigitalGain;\n+\t\t\t\t}\n+\t\t\t}\n+\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::AnalogueGain, lastAnalogueGain_);\n+\n+\t\t\tif (setDigital && (aeMode != AeMode::Lock))\n+\t\t\t\tcontrols.set(controls::DigitalGain, lastDigitalGain_);\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@@ -915,6 +968,17 @@ int CameraDevice::processControls(Camera3RequestDescriptor *descriptor)\n \t\t\tcontrols.set(controls::ExposureTime, lastExposureTime_);\n \t}\n \n+\t/* Trigger libcamera's locked -> manual state change */\n+\tif (aeMode == AeMode::Manual && !settings.hasEntry(ANDROID_SENSOR_SENSITIVITY)) {\n+\t\tconst auto &aInfo = camera_->controls().find(&controls::AnalogueGain);\n+\t\tif (aInfo != camera_->controls().end())\n+\t\t\tcontrols.set(controls::AnalogueGain, lastAnalogueGain_);\n+\n+\t\tconst auto &dInfo = camera_->controls().find(&controls::DigitalGain);\n+\t\tif (dInfo != camera_->controls().end())\n+\t\t\tcontrols.set(controls::DigitalGain, lastDigitalGain_);\n+\t}\n+\n \treturn 0;\n }\n \n@@ -1548,6 +1612,16 @@ CameraDevice::getResultMetadata(const Camera3RequestDescriptor &descriptor) cons\n \t\tresultMetadata->addEntry(ANDROID_SENSOR_EXPOSURE_TIME, exposure);\n \t}\n \n+\tif (metadata.contains(controls::AnalogueGain) &&\n+\t    settings.hasEntry(ANDROID_SENSOR_SENSITIVITY)) {\n+\t\tbool hasDigitalGain = metadata.contains(controls::DigitalGain);\n+\t\tfloat analogGain = metadata.get(controls::AnalogueGain);\n+\t\tfloat digitalGain = hasDigitalGain ? metadata.get(controls::DigitalGain) : 1.0f;\n+\n+\t\tresultMetadata->addEntry(ANDROID_SENSOR_SENSITIVITY,\n+\t\t\t\t\t minISO_ * analogGain * digitalGain);\n+\t}\n+\n \tif (metadata.contains(controls::FrameDuration)) {\n \t\tint64_t duration = metadata.get(controls::FrameDuration) * 1000;\n \t\tresultMetadata->addEntry(ANDROID_SENSOR_FRAME_DURATION,\ndiff --git a/src/android/camera_device.h b/src/android/camera_device.h\nindex f693cdbc..e119dfd9 100644\n--- a/src/android/camera_device.h\n+++ b/src/android/camera_device.h\n@@ -154,6 +154,7 @@ private:\n \n \tint facing_;\n \tint orientation_;\n+\tint minISO_;\n \n \t/* Track the last-set android AE controls */\n \tbool aeOn_;\ndiff --git a/src/android/camera_hal_config.cpp b/src/android/camera_hal_config.cpp\nindex aa90dac7..863cdff0 100644\n--- a/src/android/camera_hal_config.cpp\n+++ b/src/android/camera_hal_config.cpp\n@@ -180,6 +180,13 @@ int CameraHalConfig::Private::parseCameraConfigData(const std::string &cameraId)\n \t\t\t\t\treturn -EINVAL;\n \t\t\t\t}\n \t\t\t\tcameraConfigData.rotation = ret;\n+\t\t\t} else if (key == \"minISO\") {\n+\t\t\t\tret = std::stoi(value);\n+\t\t\t\tif (ret < 0 || ret > 100) {\n+\t\t\t\t\tLOG(HALConfig, Error)\n+\t\t\t\t\t\t<< \"Invalid minimum ISO: \" << value;\n+\t\t\t\t\treturn -EINVAL;\n+\t\t\t\t}\n \t\t\t} else {\n \t\t\t\tLOG(HALConfig, Error)\n \t\t\t\t\t<< \"Unknown key: \" << key;\n@@ -384,7 +391,8 @@ int CameraHalConfig::parseConfigurationFile()\n \t\tconst CameraConfigData &camera = c.second;\n \t\tLOG(HALConfig, Debug) << \"'\" << cameraId << \"' \"\n \t\t\t\t      << \"(\" << camera.facing << \")[\"\n-\t\t\t\t      << camera.rotation << \"]\";\n+\t\t\t\t      << camera.rotation << \"], \"\n+\t\t\t\t      << \"minISO: \" << camera.minISO;\n \t}\n \n \treturn 0;\ndiff --git a/src/android/camera_hal_config.h b/src/android/camera_hal_config.h\nindex a79d5d6c..afa02a9b 100644\n--- a/src/android/camera_hal_config.h\n+++ b/src/android/camera_hal_config.h\n@@ -15,6 +15,7 @@\n struct CameraConfigData {\n \tint facing = -1;\n \tint rotation = -1;\n+\tint minISO = -1;\n };\n \n class CameraHalConfig final : public libcamera::Extensible\n",
    "prefixes": [
        "libcamera-devel",
        "v2",
        "7/7"
    ]
}