Show a patch.

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

{
    "id": 12670,
    "url": "https://patchwork.libcamera.org/api/patches/12670/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/12670/",
    "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": "<20210621152954.40299-3-jacopo@jmondi.org>",
    "date": "2021-06-21T15:29:54",
    "name": "[libcamera-devel,2/2] android: Introduce CameraCapabilties class",
    "commit_ref": null,
    "pull_url": null,
    "state": "accepted",
    "archived": false,
    "hash": "47b52c4f315339a81bf695f7a549b9d651711e21",
    "submitter": {
        "id": 3,
        "url": "https://patchwork.libcamera.org/api/people/3/?format=api",
        "name": "Jacopo Mondi",
        "email": "jacopo@jmondi.org"
    },
    "delegate": {
        "id": 15,
        "url": "https://patchwork.libcamera.org/api/users/15/?format=api",
        "username": "jmondi",
        "first_name": "Jacopo",
        "last_name": "Mondi",
        "email": "jacopo@jmondi.org"
    },
    "mbox": "https://patchwork.libcamera.org/patch/12670/mbox/",
    "series": [
        {
            "id": 2162,
            "url": "https://patchwork.libcamera.org/api/series/2162/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=2162",
            "date": "2021-06-21T15:29:52",
            "name": "android: Introduce CameraCapabilities",
            "version": 1,
            "mbox": "https://patchwork.libcamera.org/series/2162/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/12670/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/12670/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 6ABDEC3218\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 21 Jun 2021 15:29:14 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 2EDAC68939;\n\tMon, 21 Jun 2021 17:29:14 +0200 (CEST)",
            "from relay7-d.mail.gandi.net (relay7-d.mail.gandi.net\n\t[217.70.183.200])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 880DF6893C\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 21 Jun 2021 17:29:11 +0200 (CEST)",
            "(Authenticated sender: jacopo@jmondi.org)\n\tby relay7-d.mail.gandi.net (Postfix) with ESMTPSA id 6019F20007;\n\tMon, 21 Jun 2021 15:29:10 +0000 (UTC)"
        ],
        "From": "Jacopo Mondi <jacopo@jmondi.org>",
        "To": "libcamera-devel@lists.libcamera.org",
        "Date": "Mon, 21 Jun 2021 17:29:54 +0200",
        "Message-Id": "<20210621152954.40299-3-jacopo@jmondi.org>",
        "X-Mailer": "git-send-email 2.31.1",
        "In-Reply-To": "<20210621152954.40299-1-jacopo@jmondi.org>",
        "References": "<20210621152954.40299-1-jacopo@jmondi.org>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "Subject": "[libcamera-devel] [PATCH 2/2] android: Introduce CameraCapabilties\n\tclass",
        "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": "The camera_device.cpp has grown a little too much, and it has quickly\nbecome hard to maintain. Break out the handling of the static\ninformation collected at camera initialization time to a new\nCameraCapabilities class.\n\nBreak out from the camera_device.cpp file all the functions relative to:\n- Initialization of supported stream configurations\n- Initialization of static metadata\n- Initialization of request templates\n\nSigned-off-by: Jacopo Mondi <jacopo@jmondi.org>\nAcked-by: Paul Elder <paul.elder@ideasonboard.com>\nTested-by: Paul Elder <paul.elder@ideasonboard.com>\n---\n src/android/camera_capabilities.cpp | 1164 +++++++++++++++++++++++++++\n src/android/camera_capabilities.h   |   65 ++\n src/android/camera_device.cpp       | 1147 +-------------------------\n src/android/camera_device.h         |   27 +-\n src/android/meson.build             |    1 +\n 5 files changed, 1245 insertions(+), 1159 deletions(-)\n create mode 100644 src/android/camera_capabilities.cpp\n create mode 100644 src/android/camera_capabilities.h",
    "diff": "diff --git a/src/android/camera_capabilities.cpp b/src/android/camera_capabilities.cpp\nnew file mode 100644\nindex 000000000000..311a2c839586\n--- /dev/null\n+++ b/src/android/camera_capabilities.cpp\n@@ -0,0 +1,1164 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2021, Google Inc.\n+ *\n+ * camera_capabilities.cpp - Camera static properties manager\n+ */\n+\n+#include \"camera_capabilities.h\"\n+\n+#include <array>\n+#include <cmath>\n+\n+#include <hardware/camera3.h>\n+\n+#include <libcamera/control_ids.h>\n+#include <libcamera/controls.h>\n+#include <libcamera/property_ids.h>\n+\n+#include \"libcamera/internal/formats.h\"\n+#include \"libcamera/internal/log.h\"\n+\n+using namespace libcamera;\n+\n+LOG_DECLARE_CATEGORY(HAL)\n+\n+namespace {\n+\n+/*\n+ * \\var camera3Resolutions\n+ * \\brief The list of image resolutions defined as mandatory to be supported by\n+ * the Android Camera3 specification\n+ */\n+const std::vector<Size> camera3Resolutions = {\n+\t{ 320, 240 },\n+\t{ 640, 480 },\n+\t{ 1280, 720 },\n+\t{ 1920, 1080 }\n+};\n+\n+/*\n+ * \\struct Camera3Format\n+ * \\brief Data associated with an Android format identifier\n+ * \\var libcameraFormats List of libcamera pixel formats compatible with the\n+ * Android format\n+ * \\var name The human-readable representation of the Android format code\n+ */\n+struct Camera3Format {\n+\tstd::vector<PixelFormat> libcameraFormats;\n+\tbool mandatory;\n+\tconst char *name;\n+};\n+\n+/*\n+ * \\var camera3FormatsMap\n+ * \\brief Associate Android format code with ancillary data\n+ */\n+const std::map<int, const Camera3Format> camera3FormatsMap = {\n+\t{\n+\t\tHAL_PIXEL_FORMAT_BLOB, {\n+\t\t\t{ formats::MJPEG },\n+\t\t\ttrue,\n+\t\t\t\"BLOB\"\n+\t\t}\n+\t}, {\n+\t\tHAL_PIXEL_FORMAT_YCbCr_420_888, {\n+\t\t\t{ formats::NV12, formats::NV21 },\n+\t\t\ttrue,\n+\t\t\t\"YCbCr_420_888\"\n+\t\t}\n+\t}, {\n+\t\t/*\n+\t\t * \\todo Translate IMPLEMENTATION_DEFINED inspecting the gralloc\n+\t\t * usage flag. For now, copy the YCbCr_420 configuration.\n+\t\t */\n+\t\tHAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, {\n+\t\t\t{ formats::NV12, formats::NV21 },\n+\t\t\ttrue,\n+\t\t\t\"IMPLEMENTATION_DEFINED\"\n+\t\t}\n+\t}, {\n+\t\tHAL_PIXEL_FORMAT_RAW10, {\n+\t\t\t{\n+\t\t\t\tformats::SBGGR10_CSI2P,\n+\t\t\t\tformats::SGBRG10_CSI2P,\n+\t\t\t\tformats::SGRBG10_CSI2P,\n+\t\t\t\tformats::SRGGB10_CSI2P\n+\t\t\t},\n+\t\t\tfalse,\n+\t\t\t\"RAW10\"\n+\t\t}\n+\t}, {\n+\t\tHAL_PIXEL_FORMAT_RAW12, {\n+\t\t\t{\n+\t\t\t\tformats::SBGGR12_CSI2P,\n+\t\t\t\tformats::SGBRG12_CSI2P,\n+\t\t\t\tformats::SGRBG12_CSI2P,\n+\t\t\t\tformats::SRGGB12_CSI2P\n+\t\t\t},\n+\t\t\tfalse,\n+\t\t\t\"RAW12\"\n+\t\t}\n+\t}, {\n+\t\tHAL_PIXEL_FORMAT_RAW16, {\n+\t\t\t{\n+\t\t\t\tformats::SBGGR16,\n+\t\t\t\tformats::SGBRG16,\n+\t\t\t\tformats::SGRBG16,\n+\t\t\t\tformats::SRGGB16\n+\t\t\t},\n+\t\t\tfalse,\n+\t\t\t\"RAW16\"\n+\t\t}\n+\t},\n+};\n+\n+} /* namespace */\n+\n+int CameraCapabilities::initialize(std::shared_ptr<libcamera::Camera> camera,\n+\t\t\t\t   int orientation, int facing)\n+{\n+\tcamera_ = camera;\n+\torientation_ = orientation;\n+\tfacing_ = facing;\n+\n+\t/* Acquire the camera and initialize available stream configurations. */\n+\tint ret = camera_->acquire();\n+\tif (ret) {\n+\t\tLOG(HAL, Error) << \"Failed to temporarily acquire the camera\";\n+\t\treturn ret;\n+\t}\n+\n+\tret = initializeStreamConfigurations();\n+\tcamera_->release();\n+\tif (ret)\n+\t\treturn ret;\n+\n+\treturn initializeStaticMetadata();\n+}\n+\n+std::vector<Size> CameraCapabilities::getYUVResolutions(CameraConfiguration *cameraConfig,\n+\t\t\t\t\t\t\tconst PixelFormat &pixelFormat,\n+\t\t\t\t\t\t\tconst std::vector<Size> &resolutions)\n+{\n+\tstd::vector<Size> supportedResolutions;\n+\n+\tStreamConfiguration &cfg = cameraConfig->at(0);\n+\tfor (const Size &res : resolutions) {\n+\t\tcfg.pixelFormat = pixelFormat;\n+\t\tcfg.size = res;\n+\n+\t\tCameraConfiguration::Status status = cameraConfig->validate();\n+\t\tif (status != CameraConfiguration::Valid) {\n+\t\t\tLOG(HAL, Debug) << cfg.toString() << \" not supported\";\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tLOG(HAL, Debug) << cfg.toString() << \" supported\";\n+\n+\t\tsupportedResolutions.push_back(res);\n+\t}\n+\n+\treturn supportedResolutions;\n+}\n+\n+std::vector<Size> CameraCapabilities::getRawResolutions(const libcamera::PixelFormat &pixelFormat)\n+{\n+\tstd::unique_ptr<CameraConfiguration> cameraConfig =\n+\t\tcamera_->generateConfiguration({ StreamRole::Raw });\n+\tStreamConfiguration &cfg = cameraConfig->at(0);\n+\tconst StreamFormats &formats = cfg.formats();\n+\tstd::vector<Size> supportedResolutions = formats.sizes(pixelFormat);\n+\n+\treturn supportedResolutions;\n+}\n+\n+/*\n+ * Initialize the format conversion map to translate from Android format\n+ * identifier to libcamera pixel formats and fill in the list of supported\n+ * stream configurations to be reported to the Android camera framework through\n+ * the Camera static metadata.\n+ */\n+int CameraCapabilities::initializeStreamConfigurations()\n+{\n+\t/*\n+\t * Get the maximum output resolutions\n+\t * \\todo Get this from the camera properties once defined\n+\t */\n+\tstd::unique_ptr<CameraConfiguration> cameraConfig =\n+\t\tcamera_->generateConfiguration({ StillCapture });\n+\tif (!cameraConfig) {\n+\t\tLOG(HAL, Error) << \"Failed to get maximum resolution\";\n+\t\treturn -EINVAL;\n+\t}\n+\tStreamConfiguration &cfg = cameraConfig->at(0);\n+\n+\t/*\n+\t * \\todo JPEG - Adjust the maximum available resolution by taking the\n+\t * JPEG encoder requirements into account (alignment and aspect ratio).\n+\t */\n+\tconst Size maxRes = cfg.size;\n+\tLOG(HAL, Debug) << \"Maximum supported resolution: \" << maxRes.toString();\n+\n+\t/*\n+\t * Build the list of supported image resolutions.\n+\t *\n+\t * The resolutions listed in camera3Resolution are mandatory to be\n+\t * supported, up to the camera maximum resolution.\n+\t *\n+\t * Augment the list by adding resolutions calculated from the camera\n+\t * maximum one.\n+\t */\n+\tstd::vector<Size> cameraResolutions;\n+\tstd::copy_if(camera3Resolutions.begin(), camera3Resolutions.end(),\n+\t\t     std::back_inserter(cameraResolutions),\n+\t\t     [&](const Size &res) { return res < maxRes; });\n+\n+\t/*\n+\t * The Camera3 specification suggests adding 1/2 and 1/4 of the maximum\n+\t * resolution.\n+\t */\n+\tfor (unsigned int divider = 2;; divider <<= 1) {\n+\t\tSize derivedSize{\n+\t\t\tmaxRes.width / divider,\n+\t\t\tmaxRes.height / divider,\n+\t\t};\n+\n+\t\tif (derivedSize.width < 320 ||\n+\t\t    derivedSize.height < 240)\n+\t\t\tbreak;\n+\n+\t\tcameraResolutions.push_back(derivedSize);\n+\t}\n+\tcameraResolutions.push_back(maxRes);\n+\n+\t/* Remove duplicated entries from the list of supported resolutions. */\n+\tstd::sort(cameraResolutions.begin(), cameraResolutions.end());\n+\tauto last = std::unique(cameraResolutions.begin(), cameraResolutions.end());\n+\tcameraResolutions.erase(last, cameraResolutions.end());\n+\n+\t/*\n+\t * Build the list of supported camera formats.\n+\t *\n+\t * To each Android format a list of compatible libcamera formats is\n+\t * associated. The first libcamera format that tests successful is added\n+\t * to the format translation map used when configuring the streams.\n+\t * It is then tested against the list of supported camera resolutions to\n+\t * build the stream configuration map reported through the camera static\n+\t * metadata.\n+\t */\n+\tSize maxJpegSize;\n+\tfor (const auto &format : camera3FormatsMap) {\n+\t\tint androidFormat = format.first;\n+\t\tconst Camera3Format &camera3Format = format.second;\n+\t\tconst std::vector<PixelFormat> &libcameraFormats =\n+\t\t\tcamera3Format.libcameraFormats;\n+\n+\t\tLOG(HAL, Debug) << \"Trying to map Android format \"\n+\t\t\t\t<< camera3Format.name;\n+\n+\t\t/*\n+\t\t * JPEG is always supported, either produced directly by the\n+\t\t * camera, or encoded in the HAL.\n+\t\t */\n+\t\tif (androidFormat == HAL_PIXEL_FORMAT_BLOB) {\n+\t\t\tformatsMap_[androidFormat] = formats::MJPEG;\n+\t\t\tLOG(HAL, Debug) << \"Mapped Android format \"\n+\t\t\t\t\t<< camera3Format.name << \" to \"\n+\t\t\t\t\t<< formats::MJPEG.toString()\n+\t\t\t\t\t<< \" (fixed mapping)\";\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\t/*\n+\t\t * Test the libcamera formats that can produce images\n+\t\t * compatible with the format defined by Android.\n+\t\t */\n+\t\tPixelFormat mappedFormat;\n+\t\tfor (const PixelFormat &pixelFormat : libcameraFormats) {\n+\n+\t\t\tLOG(HAL, Debug) << \"Testing \" << pixelFormat.toString();\n+\n+\t\t\t/*\n+\t\t\t * The stream configuration size can be adjusted,\n+\t\t\t * not the pixel format.\n+\t\t\t *\n+\t\t\t * \\todo This could be simplified once all pipeline\n+\t\t\t * handlers will report the StreamFormats list of\n+\t\t\t * supported formats.\n+\t\t\t */\n+\t\t\tcfg.pixelFormat = pixelFormat;\n+\n+\t\t\tCameraConfiguration::Status status = cameraConfig->validate();\n+\t\t\tif (status != CameraConfiguration::Invalid &&\n+\t\t\t    cfg.pixelFormat == pixelFormat) {\n+\t\t\t\tmappedFormat = pixelFormat;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\n+\t\tif (!mappedFormat.isValid()) {\n+\t\t\t/* If the format is not mandatory, skip it. */\n+\t\t\tif (!camera3Format.mandatory)\n+\t\t\t\tcontinue;\n+\n+\t\t\tLOG(HAL, Error)\n+\t\t\t\t<< \"Failed to map mandatory Android format \"\n+\t\t\t\t<< camera3Format.name << \" (\"\n+\t\t\t\t<< utils::hex(androidFormat) << \"): aborting\";\n+\t\t\treturn -EINVAL;\n+\t\t}\n+\n+\t\t/*\n+\t\t * Record the mapping and then proceed to generate the\n+\t\t * stream configurations map, by testing the image resolutions.\n+\t\t */\n+\t\tformatsMap_[androidFormat] = mappedFormat;\n+\t\tLOG(HAL, Debug) << \"Mapped Android format \"\n+\t\t\t\t<< camera3Format.name << \" to \"\n+\t\t\t\t<< mappedFormat.toString();\n+\n+\t\tstd::vector<Size> resolutions;\n+\t\tconst PixelFormatInfo &info = PixelFormatInfo::info(mappedFormat);\n+\t\tif (info.colourEncoding == PixelFormatInfo::ColourEncodingRAW)\n+\t\t\tresolutions = getRawResolutions(mappedFormat);\n+\t\telse\n+\t\t\tresolutions = getYUVResolutions(cameraConfig.get(),\n+\t\t\t\t\t\t\tmappedFormat,\n+\t\t\t\t\t\t\tcameraResolutions);\n+\n+\t\tfor (const Size &res : resolutions) {\n+\t\t\tstreamConfigurations_.push_back({ res, androidFormat });\n+\n+\t\t\t/*\n+\t\t\t * If the format is HAL_PIXEL_FORMAT_YCbCr_420_888\n+\t\t\t * from which JPEG is produced, add an entry for\n+\t\t\t * the JPEG stream.\n+\t\t\t *\n+\t\t\t * \\todo Wire the JPEG encoder to query the supported\n+\t\t\t * sizes provided a list of formats it can encode.\n+\t\t\t *\n+\t\t\t * \\todo Support JPEG streams produced by the Camera\n+\t\t\t * natively.\n+\t\t\t */\n+\t\t\tif (androidFormat == HAL_PIXEL_FORMAT_YCbCr_420_888) {\n+\t\t\t\tstreamConfigurations_.push_back(\n+\t\t\t\t\t{ res, HAL_PIXEL_FORMAT_BLOB });\n+\t\t\t\tmaxJpegSize = std::max(maxJpegSize, res);\n+\t\t\t}\n+\t\t}\n+\n+\t\t/*\n+\t\t * \\todo Calculate the maximum JPEG buffer size by asking the\n+\t\t * encoder giving the maximum frame size required.\n+\t\t */\n+\t\tmaxJpegBufferSize_ = maxJpegSize.width * maxJpegSize.height * 1.5;\n+\t}\n+\n+\tLOG(HAL, Debug) << \"Collected stream configuration map: \";\n+\tfor (const auto &entry : streamConfigurations_)\n+\t\tLOG(HAL, Debug) << \"{ \" << entry.resolution.toString() << \" - \"\n+\t\t\t\t<< utils::hex(entry.androidFormat) << \" }\";\n+\n+\treturn 0;\n+}\n+\n+int CameraCapabilities::initializeStaticMetadata()\n+{\n+\tstaticMetadata_ = std::make_unique<CameraMetadata>(64, 1024);\n+\tif (!staticMetadata_->isValid()) {\n+\t\tLOG(HAL, Error) << \"Failed to allocate static metadata\";\n+\t\tstaticMetadata_.reset();\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tconst ControlInfoMap &controlsInfo = camera_->controls();\n+\tconst ControlList &properties = camera_->properties();\n+\n+\t/* Color correction static metadata. */\n+\t{\n+\t\tstd::vector<uint8_t> data;\n+\t\tdata.reserve(3);\n+\t\tconst auto &infoMap = controlsInfo.find(&controls::draft::ColorCorrectionAberrationMode);\n+\t\tif (infoMap != controlsInfo.end()) {\n+\t\t\tfor (const auto &value : infoMap->second.values())\n+\t\t\t\tdata.push_back(value.get<int32_t>());\n+\t\t} else {\n+\t\t\tdata.push_back(ANDROID_COLOR_CORRECTION_ABERRATION_MODE_OFF);\n+\t\t}\n+\t\tstaticMetadata_->addEntry(ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,\n+\t\t\t\t\t  data);\n+\t}\n+\n+\t/* Control static metadata. */\n+\tstd::vector<uint8_t> aeAvailableAntiBandingModes = {\n+\t\tANDROID_CONTROL_AE_ANTIBANDING_MODE_OFF,\n+\t\tANDROID_CONTROL_AE_ANTIBANDING_MODE_50HZ,\n+\t\tANDROID_CONTROL_AE_ANTIBANDING_MODE_60HZ,\n+\t\tANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO,\n+\t};\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+\tstaticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_MODES,\n+\t\t\t\t  aeAvailableModes);\n+\n+\tint64_t minFrameDurationNsec = -1;\n+\tint64_t maxFrameDurationNsec = -1;\n+\tconst auto frameDurationsInfo = controlsInfo.find(&controls::FrameDurationLimits);\n+\tif (frameDurationsInfo != controlsInfo.end()) {\n+\t\tminFrameDurationNsec = frameDurationsInfo->second.min().get<int64_t>() * 1000;\n+\t\tmaxFrameDurationNsec = frameDurationsInfo->second.max().get<int64_t>() * 1000;\n+\n+\t\t/*\n+\t\t * Adjust the minimum frame duration to comply with Android\n+\t\t * requirements. The camera service mandates all preview/record\n+\t\t * streams to have a minimum frame duration < 33,366 milliseconds\n+\t\t * (see MAX_PREVIEW_RECORD_DURATION_NS in the camera service\n+\t\t * implementation).\n+\t\t *\n+\t\t * If we're close enough (+ 500 useconds) to that value, round\n+\t\t * the minimum frame duration of the camera to an accepted\n+\t\t * value.\n+\t\t */\n+\t\tstatic constexpr int64_t MAX_PREVIEW_RECORD_DURATION_NS = 1e9 / 29.97;\n+\t\tif (minFrameDurationNsec > MAX_PREVIEW_RECORD_DURATION_NS &&\n+\t\t    minFrameDurationNsec < MAX_PREVIEW_RECORD_DURATION_NS + 500000)\n+\t\t\tminFrameDurationNsec = MAX_PREVIEW_RECORD_DURATION_NS - 1000;\n+\n+\t\t/*\n+\t\t * The AE routine frame rate limits are computed using the frame\n+\t\t * duration limits, as libcamera clips the AE routine to the\n+\t\t * frame durations.\n+\t\t */\n+\t\tint32_t maxFps = std::round(1e9 / minFrameDurationNsec);\n+\t\tint32_t minFps = std::round(1e9 / maxFrameDurationNsec);\n+\t\tminFps = std::max(1, minFps);\n+\n+\t\t/*\n+\t\t * Force rounding errors so that we have the proper frame\n+\t\t * durations for when we reuse these variables later\n+\t\t */\n+\t\tminFrameDurationNsec = 1e9 / maxFps;\n+\t\tmaxFrameDurationNsec = 1e9 / minFps;\n+\n+\t\t/*\n+\t\t * Register to the camera service {min, max} and {max, max}\n+\t\t * intervals as requested by the metadata documentation.\n+\t\t */\n+\t\tint32_t availableAeFpsTarget[] = {\n+\t\t\tminFps, maxFps, maxFps, maxFps\n+\t\t};\n+\t\tstaticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,\n+\t\t\t\t\t  availableAeFpsTarget);\n+\t}\n+\n+\tstd::vector<int32_t> aeCompensationRange = {\n+\t\t0, 0,\n+\t};\n+\tstaticMetadata_->addEntry(ANDROID_CONTROL_AE_COMPENSATION_RANGE,\n+\t\t\t\t  aeCompensationRange);\n+\n+\tconst camera_metadata_rational_t aeCompensationStep[] = {\n+\t\t{ 0, 1 }\n+\t};\n+\tstaticMetadata_->addEntry(ANDROID_CONTROL_AE_COMPENSATION_STEP,\n+\t\t\t\t  aeCompensationStep);\n+\n+\tstd::vector<uint8_t> availableAfModes = {\n+\t\tANDROID_CONTROL_AF_MODE_OFF,\n+\t};\n+\tstaticMetadata_->addEntry(ANDROID_CONTROL_AF_AVAILABLE_MODES,\n+\t\t\t\t  availableAfModes);\n+\n+\tstd::vector<uint8_t> availableEffects = {\n+\t\tANDROID_CONTROL_EFFECT_MODE_OFF,\n+\t};\n+\tstaticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_EFFECTS,\n+\t\t\t\t  availableEffects);\n+\n+\tstd::vector<uint8_t> availableSceneModes = {\n+\t\tANDROID_CONTROL_SCENE_MODE_DISABLED,\n+\t};\n+\tstaticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_SCENE_MODES,\n+\t\t\t\t  availableSceneModes);\n+\n+\tstd::vector<uint8_t> availableStabilizationModes = {\n+\t\tANDROID_CONTROL_VIDEO_STABILIZATION_MODE_OFF,\n+\t};\n+\tstaticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,\n+\t\t\t\t  availableStabilizationModes);\n+\n+\t/*\n+\t * \\todo Inspect the Camera capabilities to report the available\n+\t * AWB modes. Default to AUTO as CTS tests require it.\n+\t */\n+\tstd::vector<uint8_t> availableAwbModes = {\n+\t\tANDROID_CONTROL_AWB_MODE_AUTO,\n+\t};\n+\tstaticMetadata_->addEntry(ANDROID_CONTROL_AWB_AVAILABLE_MODES,\n+\t\t\t\t  availableAwbModes);\n+\n+\tstd::vector<int32_t> availableMaxRegions = {\n+\t\t0, 0, 0,\n+\t};\n+\tstaticMetadata_->addEntry(ANDROID_CONTROL_MAX_REGIONS,\n+\t\t\t\t  availableMaxRegions);\n+\n+\tstd::vector<uint8_t> sceneModesOverride = {\n+\t\tANDROID_CONTROL_AE_MODE_ON,\n+\t\tANDROID_CONTROL_AWB_MODE_AUTO,\n+\t\tANDROID_CONTROL_AF_MODE_OFF,\n+\t};\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+\n+\tchar availableControlModes = ANDROID_CONTROL_MODE_AUTO;\n+\tstaticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_MODES,\n+\t\t\t\t  availableControlModes);\n+\n+\t/* JPEG static metadata. */\n+\n+\t/*\n+\t * Create the list of supported thumbnail sizes by inspecting the\n+\t * available JPEG resolutions collected in streamConfigurations_ and\n+\t * generate one entry for each aspect ratio.\n+\t *\n+\t * The JPEG thumbnailer can freely scale, so pick an arbitrary\n+\t * (160, 160) size as the bounding rectangle, which is then cropped to\n+\t * the different supported aspect ratios.\n+\t */\n+\tconstexpr Size maxJpegThumbnail(160, 160);\n+\tstd::vector<Size> thumbnailSizes;\n+\tthumbnailSizes.push_back({ 0, 0 });\n+\tfor (const auto &entry : streamConfigurations_) {\n+\t\tif (entry.androidFormat != HAL_PIXEL_FORMAT_BLOB)\n+\t\t\tcontinue;\n+\n+\t\tSize thumbnailSize = maxJpegThumbnail\n+\t\t\t\t     .boundedToAspectRatio({ entry.resolution.width,\n+\t\t\t\t\t\t\t     entry.resolution.height });\n+\t\tthumbnailSizes.push_back(thumbnailSize);\n+\t}\n+\n+\tstd::sort(thumbnailSizes.begin(), thumbnailSizes.end());\n+\tauto last = std::unique(thumbnailSizes.begin(), thumbnailSizes.end());\n+\tthumbnailSizes.erase(last, thumbnailSizes.end());\n+\n+\t/* Transform sizes in to a list of integers that can be consumed. */\n+\tstd::vector<int32_t> thumbnailEntries;\n+\tthumbnailEntries.reserve(thumbnailSizes.size() * 2);\n+\tfor (const auto &size : thumbnailSizes) {\n+\t\tthumbnailEntries.push_back(size.width);\n+\t\tthumbnailEntries.push_back(size.height);\n+\t}\n+\tstaticMetadata_->addEntry(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES,\n+\t\t\t\t  thumbnailEntries);\n+\n+\tstaticMetadata_->addEntry(ANDROID_JPEG_MAX_SIZE, maxJpegBufferSize_);\n+\n+\t/* Sensor static metadata. */\n+\tstd::array<int32_t, 2> pixelArraySize;\n+\t{\n+\t\tconst Size &size = properties.get(properties::PixelArraySize);\n+\t\tpixelArraySize[0] = size.width;\n+\t\tpixelArraySize[1] = size.height;\n+\t\tstaticMetadata_->addEntry(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,\n+\t\t\t\t\t  pixelArraySize);\n+\t}\n+\n+\tif (properties.contains(properties::UnitCellSize)) {\n+\t\tconst Size &cellSize = properties.get<Size>(properties::UnitCellSize);\n+\t\tstd::array<float, 2> physicalSize{\n+\t\t\tcellSize.width * pixelArraySize[0] / 1e6f,\n+\t\t\tcellSize.height * pixelArraySize[1] / 1e6f\n+\t\t};\n+\t\tstaticMetadata_->addEntry(ANDROID_SENSOR_INFO_PHYSICAL_SIZE,\n+\t\t\t\t\t  physicalSize);\n+\t}\n+\n+\t{\n+\t\tconst Span<const Rectangle> &rects =\n+\t\t\tproperties.get(properties::PixelArrayActiveAreas);\n+\t\tstd::vector<int32_t> data{\n+\t\t\tstatic_cast<int32_t>(rects[0].x),\n+\t\t\tstatic_cast<int32_t>(rects[0].y),\n+\t\t\tstatic_cast<int32_t>(rects[0].width),\n+\t\t\tstatic_cast<int32_t>(rects[0].height),\n+\t\t};\n+\t\tstaticMetadata_->addEntry(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,\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+\n+\t/* Report the color filter arrangement if the camera reports it. */\n+\tif (properties.contains(properties::draft::ColorFilterArrangement)) {\n+\t\tuint8_t filterArr = properties.get(properties::draft::ColorFilterArrangement);\n+\t\tstaticMetadata_->addEntry(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT,\n+\t\t\t\t\t  filterArr);\n+\t}\n+\n+\tconst auto &exposureInfo = controlsInfo.find(&controls::ExposureTime);\n+\tif (exposureInfo != controlsInfo.end()) {\n+\t\tint64_t exposureTimeRange[2] = {\n+\t\t\texposureInfo->second.min().get<int32_t>() * 1000LL,\n+\t\t\texposureInfo->second.max().get<int32_t>() * 1000LL,\n+\t\t};\n+\t\tstaticMetadata_->addEntry(ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE,\n+\t\t\t\t\t  exposureTimeRange, 2);\n+\t}\n+\n+\tstaticMetadata_->addEntry(ANDROID_SENSOR_ORIENTATION, orientation_);\n+\n+\tstd::vector<int32_t> testPatternModes = {\n+\t\tANDROID_SENSOR_TEST_PATTERN_MODE_OFF\n+\t};\n+\tconst auto &testPatternsInfo =\n+\t\tcontrolsInfo.find(&controls::draft::TestPatternMode);\n+\tif (testPatternsInfo != controlsInfo.end()) {\n+\t\tconst auto &values = testPatternsInfo->second.values();\n+\t\tASSERT(!values.empty());\n+\t\tfor (const auto &value : values) {\n+\t\t\tswitch (value.get<int32_t>()) {\n+\t\t\tcase controls::draft::TestPatternModeOff:\n+\t\t\t\t/*\n+\t\t\t\t * ANDROID_SENSOR_TEST_PATTERN_MODE_OFF is\n+\t\t\t\t * already in testPatternModes.\n+\t\t\t\t */\n+\t\t\t\tbreak;\n+\n+\t\t\tcase controls::draft::TestPatternModeSolidColor:\n+\t\t\t\ttestPatternModes.push_back(\n+\t\t\t\t\tANDROID_SENSOR_TEST_PATTERN_MODE_SOLID_COLOR);\n+\t\t\t\tbreak;\n+\n+\t\t\tcase controls::draft::TestPatternModeColorBars:\n+\t\t\t\ttestPatternModes.push_back(\n+\t\t\t\t\tANDROID_SENSOR_TEST_PATTERN_MODE_COLOR_BARS);\n+\t\t\t\tbreak;\n+\n+\t\t\tcase controls::draft::TestPatternModeColorBarsFadeToGray:\n+\t\t\t\ttestPatternModes.push_back(\n+\t\t\t\t\tANDROID_SENSOR_TEST_PATTERN_MODE_COLOR_BARS_FADE_TO_GRAY);\n+\t\t\t\tbreak;\n+\n+\t\t\tcase controls::draft::TestPatternModePn9:\n+\t\t\t\ttestPatternModes.push_back(\n+\t\t\t\t\tANDROID_SENSOR_TEST_PATTERN_MODE_PN9);\n+\t\t\t\tbreak;\n+\n+\t\t\tcase controls::draft::TestPatternModeCustom1:\n+\t\t\t\t/* We don't support this yet. */\n+\t\t\t\tbreak;\n+\n+\t\t\tdefault:\n+\t\t\t\tLOG(HAL, Error) << \"Unknown test pattern mode: \"\n+\t\t\t\t\t\t<< value.get<int32_t>();\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t}\n+\t}\n+\tstaticMetadata_->addEntry(ANDROID_SENSOR_AVAILABLE_TEST_PATTERN_MODES,\n+\t\t\t\t  testPatternModes);\n+\n+\tuint8_t timestampSource = ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN;\n+\tstaticMetadata_->addEntry(ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE,\n+\t\t\t\t  timestampSource);\n+\n+\tif (maxFrameDurationNsec > 0)\n+\t\tstaticMetadata_->addEntry(ANDROID_SENSOR_INFO_MAX_FRAME_DURATION,\n+\t\t\t\t\t  maxFrameDurationNsec);\n+\n+\t/* Statistics static metadata. */\n+\tuint8_t faceDetectMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;\n+\tstaticMetadata_->addEntry(ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,\n+\t\t\t\t  faceDetectMode);\n+\n+\tint32_t maxFaceCount = 0;\n+\tstaticMetadata_->addEntry(ANDROID_STATISTICS_INFO_MAX_FACE_COUNT,\n+\t\t\t\t  maxFaceCount);\n+\n+\t{\n+\t\tstd::vector<uint8_t> data;\n+\t\tdata.reserve(2);\n+\t\tconst auto &infoMap = controlsInfo.find(&controls::draft::LensShadingMapMode);\n+\t\tif (infoMap != controlsInfo.end()) {\n+\t\t\tfor (const auto &value : infoMap->second.values())\n+\t\t\t\tdata.push_back(value.get<int32_t>());\n+\t\t} else {\n+\t\t\tdata.push_back(ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF);\n+\t\t}\n+\t\tstaticMetadata_->addEntry(ANDROID_STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES,\n+\t\t\t\t\t  data);\n+\t}\n+\n+\t/* Sync static metadata. */\n+\tint32_t maxLatency = ANDROID_SYNC_MAX_LATENCY_UNKNOWN;\n+\tstaticMetadata_->addEntry(ANDROID_SYNC_MAX_LATENCY, maxLatency);\n+\n+\t/* Flash static metadata. */\n+\tchar flashAvailable = ANDROID_FLASH_INFO_AVAILABLE_FALSE;\n+\tstaticMetadata_->addEntry(ANDROID_FLASH_INFO_AVAILABLE,\n+\t\t\t\t  flashAvailable);\n+\n+\t/* Lens static metadata. */\n+\tstd::vector<float> lensApertures = {\n+\t\t2.53 / 100,\n+\t};\n+\tstaticMetadata_->addEntry(ANDROID_LENS_INFO_AVAILABLE_APERTURES,\n+\t\t\t\t  lensApertures);\n+\n+\tuint8_t lensFacing;\n+\tswitch (facing_) {\n+\tdefault:\n+\tcase CAMERA_FACING_FRONT:\n+\t\tlensFacing = ANDROID_LENS_FACING_FRONT;\n+\t\tbreak;\n+\tcase CAMERA_FACING_BACK:\n+\t\tlensFacing = ANDROID_LENS_FACING_BACK;\n+\t\tbreak;\n+\tcase CAMERA_FACING_EXTERNAL:\n+\t\tlensFacing = ANDROID_LENS_FACING_EXTERNAL;\n+\t\tbreak;\n+\t}\n+\tstaticMetadata_->addEntry(ANDROID_LENS_FACING, lensFacing);\n+\n+\tstd::vector<float> lensFocalLengths = {\n+\t\t1,\n+\t};\n+\tstaticMetadata_->addEntry(ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS,\n+\t\t\t\t  lensFocalLengths);\n+\n+\tstd::vector<uint8_t> opticalStabilizations = {\n+\t\tANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF,\n+\t};\n+\tstaticMetadata_->addEntry(ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,\n+\t\t\t\t  opticalStabilizations);\n+\n+\tfloat hypeFocalDistance = 0;\n+\tstaticMetadata_->addEntry(ANDROID_LENS_INFO_HYPERFOCAL_DISTANCE,\n+\t\t\t\t  hypeFocalDistance);\n+\n+\tfloat minFocusDistance = 0;\n+\tstaticMetadata_->addEntry(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,\n+\t\t\t\t  minFocusDistance);\n+\n+\t/* Noise reduction modes. */\n+\t{\n+\t\tstd::vector<uint8_t> data;\n+\t\tdata.reserve(5);\n+\t\tconst auto &infoMap = controlsInfo.find(&controls::draft::NoiseReductionMode);\n+\t\tif (infoMap != controlsInfo.end()) {\n+\t\t\tfor (const auto &value : infoMap->second.values())\n+\t\t\t\tdata.push_back(value.get<int32_t>());\n+\t\t} else {\n+\t\t\tdata.push_back(ANDROID_NOISE_REDUCTION_MODE_OFF);\n+\t\t}\n+\t\tstaticMetadata_->addEntry(ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,\n+\t\t\t\t\t  data);\n+\t}\n+\n+\t/* Scaler static metadata. */\n+\n+\t/*\n+\t * \\todo The digital zoom factor is a property that depends on the\n+\t * desired output configuration and the sensor frame size input to the\n+\t * ISP. This information is not available to the Android HAL, not at\n+\t * initialization time at least.\n+\t *\n+\t * As a workaround rely on pipeline handlers initializing the\n+\t * ScalerCrop control with the camera default configuration and use the\n+\t * maximum and minimum crop rectangles to calculate the digital zoom\n+\t * factor.\n+\t */\n+\tfloat maxZoom = 1.0f;\n+\tconst auto scalerCrop = controlsInfo.find(&controls::ScalerCrop);\n+\tif (scalerCrop != controlsInfo.end()) {\n+\t\tRectangle min = scalerCrop->second.min().get<Rectangle>();\n+\t\tRectangle max = scalerCrop->second.max().get<Rectangle>();\n+\t\tmaxZoom = std::min(1.0f * max.width / min.width,\n+\t\t\t\t   1.0f * max.height / min.height);\n+\t}\n+\tstaticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM,\n+\t\t\t\t  maxZoom);\n+\n+\tstd::vector<uint32_t> availableStreamConfigurations;\n+\tavailableStreamConfigurations.reserve(streamConfigurations_.size() * 4);\n+\tfor (const auto &entry : streamConfigurations_) {\n+\t\tavailableStreamConfigurations.push_back(entry.androidFormat);\n+\t\tavailableStreamConfigurations.push_back(entry.resolution.width);\n+\t\tavailableStreamConfigurations.push_back(entry.resolution.height);\n+\t\tavailableStreamConfigurations.push_back(\n+\t\t\tANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT);\n+\t}\n+\tstaticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,\n+\t\t\t\t  availableStreamConfigurations);\n+\n+\tstd::vector<int64_t> availableStallDurations = {\n+\t\tANDROID_SCALER_AVAILABLE_FORMATS_BLOB, 2560, 1920, 33333333,\n+\t};\n+\tstaticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_STALL_DURATIONS,\n+\t\t\t\t  availableStallDurations);\n+\n+\t/* Use the minimum frame duration for all the YUV/RGB formats. */\n+\tif (minFrameDurationNsec > 0) {\n+\t\tstd::vector<int64_t> minFrameDurations;\n+\t\tminFrameDurations.reserve(streamConfigurations_.size() * 4);\n+\t\tfor (const auto &entry : streamConfigurations_) {\n+\t\t\tminFrameDurations.push_back(entry.androidFormat);\n+\t\t\tminFrameDurations.push_back(entry.resolution.width);\n+\t\t\tminFrameDurations.push_back(entry.resolution.height);\n+\t\t\tminFrameDurations.push_back(minFrameDurationNsec);\n+\t\t}\n+\t\tstaticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,\n+\t\t\t\t\t  minFrameDurations);\n+\t}\n+\n+\tuint8_t croppingType = ANDROID_SCALER_CROPPING_TYPE_CENTER_ONLY;\n+\tstaticMetadata_->addEntry(ANDROID_SCALER_CROPPING_TYPE, croppingType);\n+\n+\t/* Info static metadata. */\n+\tuint8_t supportedHWLevel = ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;\n+\tstaticMetadata_->addEntry(ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL,\n+\t\t\t\t  supportedHWLevel);\n+\n+\t/* Request static metadata. */\n+\tint32_t partialResultCount = 1;\n+\tstaticMetadata_->addEntry(ANDROID_REQUEST_PARTIAL_RESULT_COUNT,\n+\t\t\t\t  partialResultCount);\n+\n+\t{\n+\t\t/* Default the value to 2 if not reported by the camera. */\n+\t\tuint8_t maxPipelineDepth = 2;\n+\t\tconst auto &infoMap = controlsInfo.find(&controls::draft::PipelineDepth);\n+\t\tif (infoMap != controlsInfo.end())\n+\t\t\tmaxPipelineDepth = infoMap->second.max().get<int32_t>();\n+\t\tstaticMetadata_->addEntry(ANDROID_REQUEST_PIPELINE_MAX_DEPTH,\n+\t\t\t\t\t  maxPipelineDepth);\n+\t}\n+\n+\t/* LIMITED does not support reprocessing. */\n+\tuint32_t maxNumInputStreams = 0;\n+\tstaticMetadata_->addEntry(ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS,\n+\t\t\t\t  maxNumInputStreams);\n+\n+\tstd::vector<uint8_t> availableCapabilities = {\n+\t\tANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE,\n+\t};\n+\n+\t/* Report if camera supports RAW. */\n+\tbool rawStreamAvailable = false;\n+\tstd::unique_ptr<CameraConfiguration> cameraConfig =\n+\t\tcamera_->generateConfiguration({ StreamRole::Raw });\n+\tif (cameraConfig && !cameraConfig->empty()) {\n+\t\tconst PixelFormatInfo &info =\n+\t\t\tPixelFormatInfo::info(cameraConfig->at(0).pixelFormat);\n+\t\t/* Only advertise RAW support if RAW16 is possible. */\n+\t\tif (info.colourEncoding == PixelFormatInfo::ColourEncodingRAW &&\n+\t\t    info.bitsPerPixel == 16) {\n+\t\t\trawStreamAvailable = true;\n+\t\t\tavailableCapabilities.push_back(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_RAW);\n+\t\t}\n+\t}\n+\n+\t/* Number of { RAW, YUV, JPEG } supported output streams */\n+\tint32_t numOutStreams[] = { rawStreamAvailable, 2, 1 };\n+\tstaticMetadata_->addEntry(ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS,\n+\t\t\t\t  numOutStreams);\n+\n+\tstaticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_CAPABILITIES,\n+\t\t\t\t  availableCapabilities);\n+\n+\tstd::vector<int32_t> availableCharacteristicsKeys = {\n+\t\tANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,\n+\t\tANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES,\n+\t\tANDROID_CONTROL_AE_AVAILABLE_MODES,\n+\t\tANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,\n+\t\tANDROID_CONTROL_AE_COMPENSATION_RANGE,\n+\t\tANDROID_CONTROL_AE_COMPENSATION_STEP,\n+\t\tANDROID_CONTROL_AE_LOCK_AVAILABLE,\n+\t\tANDROID_CONTROL_AF_AVAILABLE_MODES,\n+\t\tANDROID_CONTROL_AVAILABLE_EFFECTS,\n+\t\tANDROID_CONTROL_AVAILABLE_MODES,\n+\t\tANDROID_CONTROL_AVAILABLE_SCENE_MODES,\n+\t\tANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,\n+\t\tANDROID_CONTROL_AWB_AVAILABLE_MODES,\n+\t\tANDROID_CONTROL_AWB_LOCK_AVAILABLE,\n+\t\tANDROID_CONTROL_MAX_REGIONS,\n+\t\tANDROID_CONTROL_SCENE_MODE_OVERRIDES,\n+\t\tANDROID_FLASH_INFO_AVAILABLE,\n+\t\tANDROID_INFO_SUPPORTED_HARDWARE_LEVEL,\n+\t\tANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES,\n+\t\tANDROID_JPEG_MAX_SIZE,\n+\t\tANDROID_LENS_FACING,\n+\t\tANDROID_LENS_INFO_AVAILABLE_APERTURES,\n+\t\tANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS,\n+\t\tANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,\n+\t\tANDROID_LENS_INFO_HYPERFOCAL_DISTANCE,\n+\t\tANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,\n+\t\tANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,\n+\t\tANDROID_REQUEST_AVAILABLE_CAPABILITIES,\n+\t\tANDROID_REQUEST_MAX_NUM_INPUT_STREAMS,\n+\t\tANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS,\n+\t\tANDROID_REQUEST_PARTIAL_RESULT_COUNT,\n+\t\tANDROID_REQUEST_PIPELINE_MAX_DEPTH,\n+\t\tANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM,\n+\t\tANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,\n+\t\tANDROID_SCALER_AVAILABLE_STALL_DURATIONS,\n+\t\tANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,\n+\t\tANDROID_SCALER_CROPPING_TYPE,\n+\t\tANDROID_SENSOR_AVAILABLE_TEST_PATTERN_MODES,\n+\t\tANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,\n+\t\tANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT,\n+\t\tANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE,\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+\t\tANDROID_STATISTICS_INFO_MAX_FACE_COUNT,\n+\t\tANDROID_SYNC_MAX_LATENCY,\n+\t};\n+\tstaticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS,\n+\t\t\t\t  availableCharacteristicsKeys);\n+\n+\tstd::vector<int32_t> availableRequestKeys = {\n+\t\tANDROID_COLOR_CORRECTION_ABERRATION_MODE,\n+\t\tANDROID_CONTROL_AE_ANTIBANDING_MODE,\n+\t\tANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,\n+\t\tANDROID_CONTROL_AE_LOCK,\n+\t\tANDROID_CONTROL_AE_MODE,\n+\t\tANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,\n+\t\tANDROID_CONTROL_AE_TARGET_FPS_RANGE,\n+\t\tANDROID_CONTROL_AF_MODE,\n+\t\tANDROID_CONTROL_AF_TRIGGER,\n+\t\tANDROID_CONTROL_AWB_LOCK,\n+\t\tANDROID_CONTROL_AWB_MODE,\n+\t\tANDROID_CONTROL_CAPTURE_INTENT,\n+\t\tANDROID_CONTROL_EFFECT_MODE,\n+\t\tANDROID_CONTROL_MODE,\n+\t\tANDROID_CONTROL_SCENE_MODE,\n+\t\tANDROID_CONTROL_VIDEO_STABILIZATION_MODE,\n+\t\tANDROID_FLASH_MODE,\n+\t\tANDROID_JPEG_ORIENTATION,\n+\t\tANDROID_JPEG_QUALITY,\n+\t\tANDROID_JPEG_THUMBNAIL_QUALITY,\n+\t\tANDROID_JPEG_THUMBNAIL_SIZE,\n+\t\tANDROID_LENS_APERTURE,\n+\t\tANDROID_LENS_OPTICAL_STABILIZATION_MODE,\n+\t\tANDROID_NOISE_REDUCTION_MODE,\n+\t\tANDROID_SCALER_CROP_REGION,\n+\t\tANDROID_STATISTICS_FACE_DETECT_MODE\n+\t};\n+\tstaticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS,\n+\t\t\t\t  availableRequestKeys);\n+\n+\tstd::vector<int32_t> availableResultKeys = {\n+\t\tANDROID_COLOR_CORRECTION_ABERRATION_MODE,\n+\t\tANDROID_CONTROL_AE_ANTIBANDING_MODE,\n+\t\tANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,\n+\t\tANDROID_CONTROL_AE_LOCK,\n+\t\tANDROID_CONTROL_AE_MODE,\n+\t\tANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,\n+\t\tANDROID_CONTROL_AE_STATE,\n+\t\tANDROID_CONTROL_AE_TARGET_FPS_RANGE,\n+\t\tANDROID_CONTROL_AF_MODE,\n+\t\tANDROID_CONTROL_AF_STATE,\n+\t\tANDROID_CONTROL_AF_TRIGGER,\n+\t\tANDROID_CONTROL_AWB_LOCK,\n+\t\tANDROID_CONTROL_AWB_MODE,\n+\t\tANDROID_CONTROL_AWB_STATE,\n+\t\tANDROID_CONTROL_CAPTURE_INTENT,\n+\t\tANDROID_CONTROL_EFFECT_MODE,\n+\t\tANDROID_CONTROL_MODE,\n+\t\tANDROID_CONTROL_SCENE_MODE,\n+\t\tANDROID_CONTROL_VIDEO_STABILIZATION_MODE,\n+\t\tANDROID_FLASH_MODE,\n+\t\tANDROID_FLASH_STATE,\n+\t\tANDROID_JPEG_GPS_COORDINATES,\n+\t\tANDROID_JPEG_GPS_PROCESSING_METHOD,\n+\t\tANDROID_JPEG_GPS_TIMESTAMP,\n+\t\tANDROID_JPEG_ORIENTATION,\n+\t\tANDROID_JPEG_QUALITY,\n+\t\tANDROID_JPEG_SIZE,\n+\t\tANDROID_JPEG_THUMBNAIL_QUALITY,\n+\t\tANDROID_JPEG_THUMBNAIL_SIZE,\n+\t\tANDROID_LENS_APERTURE,\n+\t\tANDROID_LENS_FOCAL_LENGTH,\n+\t\tANDROID_LENS_OPTICAL_STABILIZATION_MODE,\n+\t\tANDROID_LENS_STATE,\n+\t\tANDROID_NOISE_REDUCTION_MODE,\n+\t\tANDROID_REQUEST_PIPELINE_DEPTH,\n+\t\tANDROID_SCALER_CROP_REGION,\n+\t\tANDROID_SENSOR_EXPOSURE_TIME,\n+\t\tANDROID_SENSOR_FRAME_DURATION,\n+\t\tANDROID_SENSOR_ROLLING_SHUTTER_SKEW,\n+\t\tANDROID_SENSOR_TEST_PATTERN_MODE,\n+\t\tANDROID_SENSOR_TIMESTAMP,\n+\t\tANDROID_STATISTICS_FACE_DETECT_MODE,\n+\t\tANDROID_STATISTICS_LENS_SHADING_MAP_MODE,\n+\t\tANDROID_STATISTICS_HOT_PIXEL_MAP_MODE,\n+\t\tANDROID_STATISTICS_SCENE_FLICKER,\n+\t};\n+\tstaticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_RESULT_KEYS,\n+\t\t\t\t  availableResultKeys);\n+\n+\tif (!staticMetadata_->isValid()) {\n+\t\tLOG(HAL, Error) << \"Failed to construct static metadata\";\n+\t\tstaticMetadata_.reset();\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tif (staticMetadata_->resized()) {\n+\t\tauto [entryCount, dataCount] = staticMetadata_->usage();\n+\t\tLOG(HAL, Info)\n+\t\t\t<< \"Static metadata resized: \" << entryCount\n+\t\t\t<< \" entries and \" << dataCount << \" bytes used\";\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/* Translate Android format code to libcamera pixel format. */\n+PixelFormat CameraCapabilities::toPixelFormat(int format) const\n+{\n+\tauto it = formatsMap_.find(format);\n+\tif (it == formatsMap_.end()) {\n+\t\tLOG(HAL, Error) << \"Requested format \" << utils::hex(format)\n+\t\t\t\t<< \" not supported\";\n+\t\treturn PixelFormat();\n+\t}\n+\n+\treturn it->second;\n+}\n+\n+std::unique_ptr<CameraMetadata> CameraCapabilities::requestTemplatePreview() const\n+{\n+\t/*\n+\t * \\todo Keep this in sync with the actual number of entries.\n+\t * Currently: 20 entries, 35 bytes\n+\t */\n+\tauto requestTemplate = std::make_unique<CameraMetadata>(21, 36);\n+\tif (!requestTemplate->isValid()) {\n+\t\treturn nullptr;\n+\t}\n+\n+\t/* Get the FPS range registered in the static metadata. */\n+\tcamera_metadata_ro_entry_t entry;\n+\tbool found = staticMetadata_->getEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,\n+\t\t\t\t\t       &entry);\n+\tif (!found) {\n+\t\tLOG(HAL, Error) << \"Cannot create capture template without FPS range\";\n+\t\treturn nullptr;\n+\t}\n+\n+\t/*\n+\t * Assume the AE_AVAILABLE_TARGET_FPS_RANGE static metadata\n+\t * has been assembled as {{min, max} {max, max}}.\n+\t */\n+\trequestTemplate->addEntry(ANDROID_CONTROL_AE_TARGET_FPS_RANGE,\n+\t\t\t\t  entry.data.i32, 2);\n+\n+\tuint8_t aeMode = ANDROID_CONTROL_AE_MODE_ON;\n+\trequestTemplate->addEntry(ANDROID_CONTROL_AE_MODE, aeMode);\n+\n+\tint32_t aeExposureCompensation = 0;\n+\trequestTemplate->addEntry(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,\n+\t\t\t\t  aeExposureCompensation);\n+\n+\tuint8_t aePrecaptureTrigger = ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_IDLE;\n+\trequestTemplate->addEntry(ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,\n+\t\t\t\t  aePrecaptureTrigger);\n+\n+\tuint8_t aeLock = ANDROID_CONTROL_AE_LOCK_OFF;\n+\trequestTemplate->addEntry(ANDROID_CONTROL_AE_LOCK, aeLock);\n+\n+\tuint8_t aeAntibandingMode = ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO;\n+\trequestTemplate->addEntry(ANDROID_CONTROL_AE_ANTIBANDING_MODE,\n+\t\t\t\t  aeAntibandingMode);\n+\n+\tuint8_t afMode = ANDROID_CONTROL_AF_MODE_OFF;\n+\trequestTemplate->addEntry(ANDROID_CONTROL_AF_MODE, afMode);\n+\n+\tuint8_t afTrigger = ANDROID_CONTROL_AF_TRIGGER_IDLE;\n+\trequestTemplate->addEntry(ANDROID_CONTROL_AF_TRIGGER, afTrigger);\n+\n+\tuint8_t awbMode = ANDROID_CONTROL_AWB_MODE_AUTO;\n+\trequestTemplate->addEntry(ANDROID_CONTROL_AWB_MODE, awbMode);\n+\n+\tuint8_t awbLock = ANDROID_CONTROL_AWB_LOCK_OFF;\n+\trequestTemplate->addEntry(ANDROID_CONTROL_AWB_LOCK, awbLock);\n+\n+\tuint8_t flashMode = ANDROID_FLASH_MODE_OFF;\n+\trequestTemplate->addEntry(ANDROID_FLASH_MODE, flashMode);\n+\n+\tuint8_t faceDetectMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;\n+\trequestTemplate->addEntry(ANDROID_STATISTICS_FACE_DETECT_MODE,\n+\t\t\t\t  faceDetectMode);\n+\n+\tuint8_t noiseReduction = ANDROID_NOISE_REDUCTION_MODE_OFF;\n+\trequestTemplate->addEntry(ANDROID_NOISE_REDUCTION_MODE,\n+\t\t\t\t  noiseReduction);\n+\n+\tuint8_t aberrationMode = ANDROID_COLOR_CORRECTION_ABERRATION_MODE_OFF;\n+\trequestTemplate->addEntry(ANDROID_COLOR_CORRECTION_ABERRATION_MODE,\n+\t\t\t\t  aberrationMode);\n+\n+\tuint8_t controlMode = ANDROID_CONTROL_MODE_AUTO;\n+\trequestTemplate->addEntry(ANDROID_CONTROL_MODE, controlMode);\n+\n+\tfloat lensAperture = 2.53 / 100;\n+\trequestTemplate->addEntry(ANDROID_LENS_APERTURE, lensAperture);\n+\n+\tuint8_t opticalStabilization = ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF;\n+\trequestTemplate->addEntry(ANDROID_LENS_OPTICAL_STABILIZATION_MODE,\n+\t\t\t\t  opticalStabilization);\n+\n+\tuint8_t captureIntent = ANDROID_CONTROL_CAPTURE_INTENT_PREVIEW;\n+\trequestTemplate->addEntry(ANDROID_CONTROL_CAPTURE_INTENT,\n+\t\t\t\t  captureIntent);\n+\n+\treturn requestTemplate;\n+}\n+\n+std::unique_ptr<CameraMetadata> CameraCapabilities::requestTemplateVideo() const\n+{\n+\tstd::unique_ptr<CameraMetadata> previewTemplate = requestTemplatePreview();\n+\tif (!previewTemplate)\n+\t\treturn nullptr;\n+\n+\t/*\n+\t * The video template requires a fixed FPS range. Everything else\n+\t * stays the same as the preview template.\n+\t */\n+\tcamera_metadata_ro_entry_t entry;\n+\tstaticMetadata_->getEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,\n+\t\t\t\t  &entry);\n+\n+\t/*\n+\t * Assume the AE_AVAILABLE_TARGET_FPS_RANGE static metadata\n+\t * has been assembled as {{min, max} {max, max}}.\n+\t */\n+\tpreviewTemplate->updateEntry(ANDROID_CONTROL_AE_TARGET_FPS_RANGE,\n+\t\t\t\t     entry.data.i32 + 2, 2);\n+\n+\treturn previewTemplate;\n+}\ndiff --git a/src/android/camera_capabilities.h b/src/android/camera_capabilities.h\nnew file mode 100644\nindex 000000000000..f511607bbd90\n--- /dev/null\n+++ b/src/android/camera_capabilities.h\n@@ -0,0 +1,65 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2021, Google Inc.\n+ *\n+ * camera_capabilities.h - Camera static properties manager\n+ */\n+#ifndef __ANDROID_CAMERA_CAPABILITIES_H__\n+#define __ANDROID_CAMERA_CAPABILITIES_H__\n+\n+#include <map>\n+#include <memory>\n+#include <vector>\n+\n+#include <libcamera/camera.h>\n+#include <libcamera/class.h>\n+#include <libcamera/formats.h>\n+#include <libcamera/geometry.h>\n+\n+#include \"camera_metadata.h\"\n+\n+class CameraCapabilities\n+{\n+public:\n+\tCameraCapabilities() = default;\n+\n+\tint initialize(std::shared_ptr<libcamera::Camera> camera,\n+\t\t       int orientation, int facing);\n+\n+\tCameraMetadata *staticMetadata() const { return staticMetadata_.get(); }\n+\tlibcamera::PixelFormat toPixelFormat(int format) const;\n+\tunsigned int maxJpegBufferSize() const { return maxJpegBufferSize_; }\n+\n+\tstd::unique_ptr<CameraMetadata> requestTemplatePreview() const;\n+\tstd::unique_ptr<CameraMetadata> requestTemplateVideo() const;\n+\n+private:\n+\tLIBCAMERA_DISABLE_COPY_AND_MOVE(CameraCapabilities)\n+\n+\tstruct Camera3StreamConfiguration {\n+\t\tlibcamera::Size resolution;\n+\t\tint androidFormat;\n+\t};\n+\n+\tstd::vector<libcamera::Size>\n+\tgetYUVResolutions(libcamera::CameraConfiguration *cameraConfig,\n+\t\t\t  const libcamera::PixelFormat &pixelFormat,\n+\t\t\t  const std::vector<libcamera::Size> &resolutions);\n+\tstd::vector<libcamera::Size>\n+\tgetRawResolutions(const libcamera::PixelFormat &pixelFormat);\n+\tint initializeStreamConfigurations();\n+\n+\tint initializeStaticMetadata();\n+\n+\tstd::shared_ptr<libcamera::Camera> camera_;\n+\n+\tint facing_;\n+\tint orientation_;\n+\n+\tstd::vector<Camera3StreamConfiguration> streamConfigurations_;\n+\tstd::map<int, libcamera::PixelFormat> formatsMap_;\n+\tstd::unique_ptr<CameraMetadata> staticMetadata_;\n+\tunsigned int maxJpegBufferSize_;\n+};\n+\n+#endif /* __ANDROID_CAMERA_CAPABILITIES_H__ */\ndiff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp\nindex 8c71fd0675d3..4bd125d7020a 100644\n--- a/src/android/camera_device.cpp\n+++ b/src/android/camera_device.cpp\n@@ -10,11 +10,8 @@\n #include \"camera_ops.h\"\n #include \"post_processor.h\"\n \n-#include <array>\n-#include <cmath>\n #include <fstream>\n #include <sys/mman.h>\n-#include <tuple>\n #include <unistd.h>\n #include <vector>\n \n@@ -23,7 +20,6 @@\n #include <libcamera/formats.h>\n #include <libcamera/property_ids.h>\n \n-#include \"libcamera/internal/formats.h\"\n #include \"libcamera/internal/log.h\"\n #include \"libcamera/internal/thread.h\"\n #include \"libcamera/internal/utils.h\"\n@@ -36,94 +32,6 @@ LOG_DECLARE_CATEGORY(HAL)\n \n namespace {\n \n-/*\n- * \\var camera3Resolutions\n- * \\brief The list of image resolutions defined as mandatory to be supported by\n- * the Android Camera3 specification\n- */\n-const std::vector<Size> camera3Resolutions = {\n-\t{ 320, 240 },\n-\t{ 640, 480 },\n-\t{ 1280, 720 },\n-\t{ 1920, 1080 }\n-};\n-\n-/*\n- * \\struct Camera3Format\n- * \\brief Data associated with an Android format identifier\n- * \\var libcameraFormats List of libcamera pixel formats compatible with the\n- * Android format\n- * \\var name The human-readable representation of the Android format code\n- */\n-struct Camera3Format {\n-\tstd::vector<PixelFormat> libcameraFormats;\n-\tbool mandatory;\n-\tconst char *name;\n-};\n-\n-/*\n- * \\var camera3FormatsMap\n- * \\brief Associate Android format code with ancillary data\n- */\n-const std::map<int, const Camera3Format> camera3FormatsMap = {\n-\t{\n-\t\tHAL_PIXEL_FORMAT_BLOB, {\n-\t\t\t{ formats::MJPEG },\n-\t\t\ttrue,\n-\t\t\t\"BLOB\"\n-\t\t}\n-\t}, {\n-\t\tHAL_PIXEL_FORMAT_YCbCr_420_888, {\n-\t\t\t{ formats::NV12, formats::NV21 },\n-\t\t\ttrue,\n-\t\t\t\"YCbCr_420_888\"\n-\t\t}\n-\t}, {\n-\t\t/*\n-\t\t * \\todo Translate IMPLEMENTATION_DEFINED inspecting the gralloc\n-\t\t * usage flag. For now, copy the YCbCr_420 configuration.\n-\t\t */\n-\t\tHAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, {\n-\t\t\t{ formats::NV12, formats::NV21 },\n-\t\t\ttrue,\n-\t\t\t\"IMPLEMENTATION_DEFINED\"\n-\t\t}\n-\t}, {\n-\t\tHAL_PIXEL_FORMAT_RAW10, {\n-\t\t\t{\n-\t\t\t\tformats::SBGGR10_CSI2P,\n-\t\t\t\tformats::SGBRG10_CSI2P,\n-\t\t\t\tformats::SGRBG10_CSI2P,\n-\t\t\t\tformats::SRGGB10_CSI2P\n-\t\t\t},\n-\t\t\tfalse,\n-\t\t\t\"RAW10\"\n-\t\t}\n-\t}, {\n-\t\tHAL_PIXEL_FORMAT_RAW12, {\n-\t\t\t{\n-\t\t\t\tformats::SBGGR12_CSI2P,\n-\t\t\t\tformats::SGBRG12_CSI2P,\n-\t\t\t\tformats::SGRBG12_CSI2P,\n-\t\t\t\tformats::SRGGB12_CSI2P\n-\t\t\t},\n-\t\t\tfalse,\n-\t\t\t\"RAW12\"\n-\t\t}\n-\t}, {\n-\t\tHAL_PIXEL_FORMAT_RAW16, {\n-\t\t\t{\n-\t\t\t\tformats::SBGGR16,\n-\t\t\t\tformats::SGBRG16,\n-\t\t\t\tformats::SGRBG16,\n-\t\t\t\tformats::SRGGB16\n-\t\t\t},\n-\t\t\tfalse,\n-\t\t\t\"RAW16\"\n-\t\t}\n-\t},\n-};\n-\n /*\n  * \\struct Camera3StreamConfig\n  * \\brief Data to store StreamConfiguration associated with camera3_stream(s)\n@@ -512,242 +420,7 @@ int CameraDevice::initialize(const CameraConfigData *cameraConfigData)\n \t\torientation_ = 0;\n \t}\n \n-\t/* Acquire the camera and initialize available stream configurations. */\n-\tint ret = camera_->acquire();\n-\tif (ret) {\n-\t\tLOG(HAL, Error) << \"Failed to temporarily acquire the camera\";\n-\t\treturn ret;\n-\t}\n-\n-\tret = initializeStreamConfigurations();\n-\tcamera_->release();\n-\treturn ret;\n-}\n-\n-std::vector<Size> CameraDevice::getYUVResolutions(CameraConfiguration *cameraConfig,\n-\t\t\t\t\t\t  const PixelFormat &pixelFormat,\n-\t\t\t\t\t\t  const std::vector<Size> &resolutions)\n-{\n-\tstd::vector<Size> supportedResolutions;\n-\n-\tStreamConfiguration &cfg = cameraConfig->at(0);\n-\tfor (const Size &res : resolutions) {\n-\t\tcfg.pixelFormat = pixelFormat;\n-\t\tcfg.size = res;\n-\n-\t\tCameraConfiguration::Status status = cameraConfig->validate();\n-\t\tif (status != CameraConfiguration::Valid) {\n-\t\t\tLOG(HAL, Debug) << cfg.toString() << \" not supported\";\n-\t\t\tcontinue;\n-\t\t}\n-\n-\t\tLOG(HAL, Debug) << cfg.toString() << \" supported\";\n-\n-\t\tsupportedResolutions.push_back(res);\n-\t}\n-\n-\treturn supportedResolutions;\n-}\n-\n-std::vector<Size> CameraDevice::getRawResolutions(const libcamera::PixelFormat &pixelFormat)\n-{\n-\tstd::unique_ptr<CameraConfiguration> cameraConfig =\n-\t\tcamera_->generateConfiguration({ StreamRole::Raw });\n-\tStreamConfiguration &cfg = cameraConfig->at(0);\n-\tconst StreamFormats &formats = cfg.formats();\n-\tstd::vector<Size> supportedResolutions = formats.sizes(pixelFormat);\n-\n-\treturn supportedResolutions;\n-}\n-\n-/*\n- * Initialize the format conversion map to translate from Android format\n- * identifier to libcamera pixel formats and fill in the list of supported\n- * stream configurations to be reported to the Android camera framework through\n- * the static stream configuration metadata.\n- */\n-int CameraDevice::initializeStreamConfigurations()\n-{\n-\t/*\n-\t * Get the maximum output resolutions\n-\t * \\todo Get this from the camera properties once defined\n-\t */\n-\tstd::unique_ptr<CameraConfiguration> cameraConfig =\n-\t\tcamera_->generateConfiguration({ StillCapture });\n-\tif (!cameraConfig) {\n-\t\tLOG(HAL, Error) << \"Failed to get maximum resolution\";\n-\t\treturn -EINVAL;\n-\t}\n-\tStreamConfiguration &cfg = cameraConfig->at(0);\n-\n-\t/*\n-\t * \\todo JPEG - Adjust the maximum available resolution by taking the\n-\t * JPEG encoder requirements into account (alignment and aspect ratio).\n-\t */\n-\tconst Size maxRes = cfg.size;\n-\tLOG(HAL, Debug) << \"Maximum supported resolution: \" << maxRes.toString();\n-\n-\t/*\n-\t * Build the list of supported image resolutions.\n-\t *\n-\t * The resolutions listed in camera3Resolution are mandatory to be\n-\t * supported, up to the camera maximum resolution.\n-\t *\n-\t * Augment the list by adding resolutions calculated from the camera\n-\t * maximum one.\n-\t */\n-\tstd::vector<Size> cameraResolutions;\n-\tstd::copy_if(camera3Resolutions.begin(), camera3Resolutions.end(),\n-\t\t     std::back_inserter(cameraResolutions),\n-\t\t     [&](const Size &res) { return res < maxRes; });\n-\n-\t/*\n-\t * The Camera3 specification suggests adding 1/2 and 1/4 of the maximum\n-\t * resolution.\n-\t */\n-\tfor (unsigned int divider = 2;; divider <<= 1) {\n-\t\tSize derivedSize{\n-\t\t\tmaxRes.width / divider,\n-\t\t\tmaxRes.height / divider,\n-\t\t};\n-\n-\t\tif (derivedSize.width < 320 ||\n-\t\t    derivedSize.height < 240)\n-\t\t\tbreak;\n-\n-\t\tcameraResolutions.push_back(derivedSize);\n-\t}\n-\tcameraResolutions.push_back(maxRes);\n-\n-\t/* Remove duplicated entries from the list of supported resolutions. */\n-\tstd::sort(cameraResolutions.begin(), cameraResolutions.end());\n-\tauto last = std::unique(cameraResolutions.begin(), cameraResolutions.end());\n-\tcameraResolutions.erase(last, cameraResolutions.end());\n-\n-\t/*\n-\t * Build the list of supported camera formats.\n-\t *\n-\t * To each Android format a list of compatible libcamera formats is\n-\t * associated. The first libcamera format that tests successful is added\n-\t * to the format translation map used when configuring the streams.\n-\t * It is then tested against the list of supported camera resolutions to\n-\t * build the stream configuration map reported through the camera static\n-\t * metadata.\n-\t */\n-\tSize maxJpegSize;\n-\tfor (const auto &format : camera3FormatsMap) {\n-\t\tint androidFormat = format.first;\n-\t\tconst Camera3Format &camera3Format = format.second;\n-\t\tconst std::vector<PixelFormat> &libcameraFormats =\n-\t\t\tcamera3Format.libcameraFormats;\n-\n-\t\tLOG(HAL, Debug) << \"Trying to map Android format \"\n-\t\t\t\t<< camera3Format.name;\n-\n-\t\t/*\n-\t\t * JPEG is always supported, either produced directly by the\n-\t\t * camera, or encoded in the HAL.\n-\t\t */\n-\t\tif (androidFormat == HAL_PIXEL_FORMAT_BLOB) {\n-\t\t\tformatsMap_[androidFormat] = formats::MJPEG;\n-\t\t\tLOG(HAL, Debug) << \"Mapped Android format \"\n-\t\t\t\t\t<< camera3Format.name << \" to \"\n-\t\t\t\t\t<< formats::MJPEG.toString()\n-\t\t\t\t\t<< \" (fixed mapping)\";\n-\t\t\tcontinue;\n-\t\t}\n-\n-\t\t/*\n-\t\t * Test the libcamera formats that can produce images\n-\t\t * compatible with the format defined by Android.\n-\t\t */\n-\t\tPixelFormat mappedFormat;\n-\t\tfor (const PixelFormat &pixelFormat : libcameraFormats) {\n-\n-\t\t\tLOG(HAL, Debug) << \"Testing \" << pixelFormat.toString();\n-\n-\t\t\t/*\n-\t\t\t * The stream configuration size can be adjusted,\n-\t\t\t * not the pixel format.\n-\t\t\t *\n-\t\t\t * \\todo This could be simplified once all pipeline\n-\t\t\t * handlers will report the StreamFormats list of\n-\t\t\t * supported formats.\n-\t\t\t */\n-\t\t\tcfg.pixelFormat = pixelFormat;\n-\n-\t\t\tCameraConfiguration::Status status = cameraConfig->validate();\n-\t\t\tif (status != CameraConfiguration::Invalid &&\n-\t\t\t    cfg.pixelFormat == pixelFormat) {\n-\t\t\t\tmappedFormat = pixelFormat;\n-\t\t\t\tbreak;\n-\t\t\t}\n-\t\t}\n-\n-\t\tif (!mappedFormat.isValid()) {\n-\t\t\t/* If the format is not mandatory, skip it. */\n-\t\t\tif (!camera3Format.mandatory)\n-\t\t\t\tcontinue;\n-\n-\t\t\tLOG(HAL, Error)\n-\t\t\t\t<< \"Failed to map mandatory Android format \"\n-\t\t\t\t<< camera3Format.name << \" (\"\n-\t\t\t\t<< utils::hex(androidFormat) << \"): aborting\";\n-\t\t\treturn -EINVAL;\n-\t\t}\n-\n-\t\t/*\n-\t\t * Record the mapping and then proceed to generate the\n-\t\t * stream configurations map, by testing the image resolutions.\n-\t\t */\n-\t\tformatsMap_[androidFormat] = mappedFormat;\n-\t\tLOG(HAL, Debug) << \"Mapped Android format \"\n-\t\t\t\t<< camera3Format.name << \" to \"\n-\t\t\t\t<< mappedFormat.toString();\n-\n-\t\tstd::vector<Size> resolutions;\n-\t\tconst PixelFormatInfo &info = PixelFormatInfo::info(mappedFormat);\n-\t\tif (info.colourEncoding == PixelFormatInfo::ColourEncodingRAW)\n-\t\t\tresolutions = getRawResolutions(mappedFormat);\n-\t\telse\n-\t\t\tresolutions = getYUVResolutions(cameraConfig.get(),\n-\t\t\t\t\t\t\tmappedFormat,\n-\t\t\t\t\t\t\tcameraResolutions);\n-\n-\t\tfor (const Size &res : resolutions) {\n-\t\t\tstreamConfigurations_.push_back({ res, androidFormat });\n-\n-\t\t\t/*\n-\t\t\t * If the format is HAL_PIXEL_FORMAT_YCbCr_420_888\n-\t\t\t * from which JPEG is produced, add an entry for\n-\t\t\t * the JPEG stream.\n-\t\t\t *\n-\t\t\t * \\todo Wire the JPEG encoder to query the supported\n-\t\t\t * sizes provided a list of formats it can encode.\n-\t\t\t *\n-\t\t\t * \\todo Support JPEG streams produced by the Camera\n-\t\t\t * natively.\n-\t\t\t */\n-\t\t\tif (androidFormat == HAL_PIXEL_FORMAT_YCbCr_420_888) {\n-\t\t\t\tstreamConfigurations_.push_back(\n-\t\t\t\t\t{ res, HAL_PIXEL_FORMAT_BLOB });\n-\t\t\t\tmaxJpegSize = std::max(maxJpegSize, res);\n-\t\t\t}\n-\t\t}\n-\n-\t\t/*\n-\t\t * \\todo Calculate the maximum JPEG buffer size by asking the\n-\t\t * encoder giving the maximum frame size required.\n-\t\t */\n-\t\tmaxJpegBufferSize_ = maxJpegSize.width * maxJpegSize.height * 1.5;\n-\t}\n-\n-\tLOG(HAL, Debug) << \"Collected stream configuration map: \";\n-\tfor (const auto &entry : streamConfigurations_)\n-\t\tLOG(HAL, Debug) << \"{ \" << entry.resolution.toString() << \" - \"\n-\t\t\t\t<< utils::hex(entry.androidFormat) << \" }\";\n-\n-\treturn 0;\n+\treturn capabilities_.initialize(camera_, orientation_, facing_);\n }\n \n /*\n@@ -817,802 +490,19 @@ void CameraDevice::stop()\n \tstate_ = State::Stopped;\n }\n \n-void CameraDevice::setCallbacks(const camera3_callback_ops_t *callbacks)\n+unsigned int CameraDevice::maxJpegBufferSize() const\n {\n-\tcallbacks_ = callbacks;\n+\treturn capabilities_.maxJpegBufferSize();\n }\n \n-/*\n- * Return static information for the camera.\n- */\n-const camera_metadata_t *CameraDevice::getStaticMetadata()\n-{\n-\tif (staticMetadata_)\n-\t\treturn staticMetadata_->get();\n-\n-\tstaticMetadata_ = std::make_unique<CameraMetadata>(64, 1024);\n-\tif (!staticMetadata_->isValid()) {\n-\t\tLOG(HAL, Error) << \"Failed to allocate static metadata\";\n-\t\tstaticMetadata_.reset();\n-\t\treturn nullptr;\n-\t}\n-\n-\tconst ControlInfoMap &controlsInfo = camera_->controls();\n-\tconst ControlList &properties = camera_->properties();\n-\n-\t/* Color correction static metadata. */\n-\t{\n-\t\tstd::vector<uint8_t> data;\n-\t\tdata.reserve(3);\n-\t\tconst auto &infoMap = controlsInfo.find(&controls::draft::ColorCorrectionAberrationMode);\n-\t\tif (infoMap != controlsInfo.end()) {\n-\t\t\tfor (const auto &value : infoMap->second.values())\n-\t\t\t\tdata.push_back(value.get<int32_t>());\n-\t\t} else {\n-\t\t\tdata.push_back(ANDROID_COLOR_CORRECTION_ABERRATION_MODE_OFF);\n-\t\t}\n-\t\tstaticMetadata_->addEntry(ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,\n-\t\t\t\t\t  data);\n-\t}\n-\n-\t/* Control static metadata. */\n-\tstd::vector<uint8_t> aeAvailableAntiBandingModes = {\n-\t\tANDROID_CONTROL_AE_ANTIBANDING_MODE_OFF,\n-\t\tANDROID_CONTROL_AE_ANTIBANDING_MODE_50HZ,\n-\t\tANDROID_CONTROL_AE_ANTIBANDING_MODE_60HZ,\n-\t\tANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO,\n-\t};\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-\tstaticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_MODES,\n-\t\t\t\t  aeAvailableModes);\n-\n-\tint64_t minFrameDurationNsec = -1;\n-\tint64_t maxFrameDurationNsec = -1;\n-\tconst auto frameDurationsInfo = controlsInfo.find(&controls::FrameDurationLimits);\n-\tif (frameDurationsInfo != controlsInfo.end()) {\n-\t\tminFrameDurationNsec = frameDurationsInfo->second.min().get<int64_t>() * 1000;\n-\t\tmaxFrameDurationNsec = frameDurationsInfo->second.max().get<int64_t>() * 1000;\n-\n-\t\t/*\n-\t\t * Adjust the minimum frame duration to comply with Android\n-\t\t * requirements. The camera service mandates all preview/record\n-\t\t * streams to have a minimum frame duration < 33,366 milliseconds\n-\t\t * (see MAX_PREVIEW_RECORD_DURATION_NS in the camera service\n-\t\t * implementation).\n-\t\t *\n-\t\t * If we're close enough (+ 500 useconds) to that value, round\n-\t\t * the minimum frame duration of the camera to an accepted\n-\t\t * value.\n-\t\t */\n-\t\tstatic constexpr int64_t MAX_PREVIEW_RECORD_DURATION_NS = 1e9 / 29.97;\n-\t\tif (minFrameDurationNsec > MAX_PREVIEW_RECORD_DURATION_NS &&\n-\t\t    minFrameDurationNsec < MAX_PREVIEW_RECORD_DURATION_NS + 500000)\n-\t\t\tminFrameDurationNsec = MAX_PREVIEW_RECORD_DURATION_NS - 1000;\n-\n-\t\t/*\n-\t\t * The AE routine frame rate limits are computed using the frame\n-\t\t * duration limits, as libcamera clips the AE routine to the\n-\t\t * frame durations.\n-\t\t */\n-\t\tint32_t maxFps = std::round(1e9 / minFrameDurationNsec);\n-\t\tint32_t minFps = std::round(1e9 / maxFrameDurationNsec);\n-\t\tminFps = std::max(1, minFps);\n-\n-\t\t/*\n-\t\t * Force rounding errors so that we have the proper frame\n-\t\t * durations for when we reuse these variables later\n-\t\t */\n-\t\tminFrameDurationNsec = 1e9 / maxFps;\n-\t\tmaxFrameDurationNsec = 1e9 / minFps;\n-\n-\t\t/*\n-\t\t * Register to the camera service {min, max} and {max, max}\n-\t\t * intervals as requested by the metadata documentation.\n-\t\t */\n-\t\tint32_t availableAeFpsTarget[] = {\n-\t\t\tminFps, maxFps, maxFps, maxFps\n-\t\t};\n-\t\tstaticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,\n-\t\t\t\t\t  availableAeFpsTarget);\n-\t}\n-\n-\tstd::vector<int32_t> aeCompensationRange = {\n-\t\t0, 0,\n-\t};\n-\tstaticMetadata_->addEntry(ANDROID_CONTROL_AE_COMPENSATION_RANGE,\n-\t\t\t\t  aeCompensationRange);\n-\n-\tconst camera_metadata_rational_t aeCompensationStep[] = {\n-\t\t{ 0, 1 }\n-\t};\n-\tstaticMetadata_->addEntry(ANDROID_CONTROL_AE_COMPENSATION_STEP,\n-\t\t\t\t  aeCompensationStep);\n-\n-\tstd::vector<uint8_t> availableAfModes = {\n-\t\tANDROID_CONTROL_AF_MODE_OFF,\n-\t};\n-\tstaticMetadata_->addEntry(ANDROID_CONTROL_AF_AVAILABLE_MODES,\n-\t\t\t\t  availableAfModes);\n-\n-\tstd::vector<uint8_t> availableEffects = {\n-\t\tANDROID_CONTROL_EFFECT_MODE_OFF,\n-\t};\n-\tstaticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_EFFECTS,\n-\t\t\t\t  availableEffects);\n-\n-\tstd::vector<uint8_t> availableSceneModes = {\n-\t\tANDROID_CONTROL_SCENE_MODE_DISABLED,\n-\t};\n-\tstaticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_SCENE_MODES,\n-\t\t\t\t  availableSceneModes);\n-\n-\tstd::vector<uint8_t> availableStabilizationModes = {\n-\t\tANDROID_CONTROL_VIDEO_STABILIZATION_MODE_OFF,\n-\t};\n-\tstaticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,\n-\t\t\t\t  availableStabilizationModes);\n-\n-\t/*\n-\t * \\todo Inspect the Camera capabilities to report the available\n-\t * AWB modes. Default to AUTO as CTS tests require it.\n-\t */\n-\tstd::vector<uint8_t> availableAwbModes = {\n-\t\tANDROID_CONTROL_AWB_MODE_AUTO,\n-\t};\n-\tstaticMetadata_->addEntry(ANDROID_CONTROL_AWB_AVAILABLE_MODES,\n-\t\t\t\t  availableAwbModes);\n-\n-\tstd::vector<int32_t> availableMaxRegions = {\n-\t\t0, 0, 0,\n-\t};\n-\tstaticMetadata_->addEntry(ANDROID_CONTROL_MAX_REGIONS,\n-\t\t\t\t  availableMaxRegions);\n-\n-\tstd::vector<uint8_t> sceneModesOverride = {\n-\t\tANDROID_CONTROL_AE_MODE_ON,\n-\t\tANDROID_CONTROL_AWB_MODE_AUTO,\n-\t\tANDROID_CONTROL_AF_MODE_OFF,\n-\t};\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-\n-\tchar availableControlModes = ANDROID_CONTROL_MODE_AUTO;\n-\tstaticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_MODES,\n-\t\t\t\t  availableControlModes);\n-\n-\t/* JPEG static metadata. */\n-\n-\t/*\n-\t * Create the list of supported thumbnail sizes by inspecting the\n-\t * available JPEG resolutions collected in streamConfigurations_ and\n-\t * generate one entry for each aspect ratio.\n-\t *\n-\t * The JPEG thumbnailer can freely scale, so pick an arbitrary\n-\t * (160, 160) size as the bounding rectangle, which is then cropped to\n-\t * the different supported aspect ratios.\n-\t */\n-\tconstexpr Size maxJpegThumbnail(160, 160);\n-\tstd::vector<Size> thumbnailSizes;\n-\tthumbnailSizes.push_back({ 0, 0 });\n-\tfor (const auto &entry : streamConfigurations_) {\n-\t\tif (entry.androidFormat != HAL_PIXEL_FORMAT_BLOB)\n-\t\t\tcontinue;\n-\n-\t\tSize thumbnailSize = maxJpegThumbnail\n-\t\t\t\t     .boundedToAspectRatio({ entry.resolution.width,\n-\t\t\t\t\t\t\t     entry.resolution.height });\n-\t\tthumbnailSizes.push_back(thumbnailSize);\n-\t}\n-\n-\tstd::sort(thumbnailSizes.begin(), thumbnailSizes.end());\n-\tauto last = std::unique(thumbnailSizes.begin(), thumbnailSizes.end());\n-\tthumbnailSizes.erase(last, thumbnailSizes.end());\n-\n-\t/* Transform sizes in to a list of integers that can be consumed. */\n-\tstd::vector<int32_t> thumbnailEntries;\n-\tthumbnailEntries.reserve(thumbnailSizes.size() * 2);\n-\tfor (const auto &size : thumbnailSizes) {\n-\t\tthumbnailEntries.push_back(size.width);\n-\t\tthumbnailEntries.push_back(size.height);\n-\t}\n-\tstaticMetadata_->addEntry(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES,\n-\t\t\t\t  thumbnailEntries);\n-\n-\tstaticMetadata_->addEntry(ANDROID_JPEG_MAX_SIZE, maxJpegBufferSize_);\n-\n-\t/* Sensor static metadata. */\n-\tstd::array<int32_t, 2> pixelArraySize;\n-\t{\n-\t\tconst Size &size = properties.get(properties::PixelArraySize);\n-\t\tpixelArraySize[0] = size.width;\n-\t\tpixelArraySize[1] = size.height;\n-\t\tstaticMetadata_->addEntry(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,\n-\t\t\t\t\t  pixelArraySize);\n-\t}\n-\n-\tif (properties.contains(properties::UnitCellSize)) {\n-\t\tconst Size &cellSize = properties.get<Size>(properties::UnitCellSize);\n-\t\tstd::array<float, 2> physicalSize{\n-\t\t\tcellSize.width * pixelArraySize[0] / 1e6f,\n-\t\t\tcellSize.height * pixelArraySize[1] / 1e6f\n-\t\t};\n-\t\tstaticMetadata_->addEntry(ANDROID_SENSOR_INFO_PHYSICAL_SIZE,\n-\t\t\t\t\t  physicalSize);\n-\t}\n-\n-\t{\n-\t\tconst Span<const Rectangle> &rects =\n-\t\t\tproperties.get(properties::PixelArrayActiveAreas);\n-\t\tstd::vector<int32_t> data{\n-\t\t\tstatic_cast<int32_t>(rects[0].x),\n-\t\t\tstatic_cast<int32_t>(rects[0].y),\n-\t\t\tstatic_cast<int32_t>(rects[0].width),\n-\t\t\tstatic_cast<int32_t>(rects[0].height),\n-\t\t};\n-\t\tstaticMetadata_->addEntry(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,\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-\n-\t/* Report the color filter arrangement if the camera reports it. */\n-\tif (properties.contains(properties::draft::ColorFilterArrangement)) {\n-\t\tuint8_t filterArr = properties.get(properties::draft::ColorFilterArrangement);\n-\t\tstaticMetadata_->addEntry(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT,\n-\t\t\t\t\t  filterArr);\n-\t}\n-\n-\tconst auto &exposureInfo = controlsInfo.find(&controls::ExposureTime);\n-\tif (exposureInfo != controlsInfo.end()) {\n-\t\tint64_t exposureTimeRange[2] = {\n-\t\t\texposureInfo->second.min().get<int32_t>() * 1000LL,\n-\t\t\texposureInfo->second.max().get<int32_t>() * 1000LL,\n-\t\t};\n-\t\tstaticMetadata_->addEntry(ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE,\n-\t\t\t\t\t  exposureTimeRange, 2);\n-\t}\n-\n-\tstaticMetadata_->addEntry(ANDROID_SENSOR_ORIENTATION, orientation_);\n-\n-\tstd::vector<int32_t> testPatternModes = {\n-\t\tANDROID_SENSOR_TEST_PATTERN_MODE_OFF\n-\t};\n-\tconst auto &testPatternsInfo =\n-\t\tcontrolsInfo.find(&controls::draft::TestPatternMode);\n-\tif (testPatternsInfo != controlsInfo.end()) {\n-\t\tconst auto &values = testPatternsInfo->second.values();\n-\t\tASSERT(!values.empty());\n-\t\tfor (const auto &value : values) {\n-\t\t\tswitch (value.get<int32_t>()) {\n-\t\t\tcase controls::draft::TestPatternModeOff:\n-\t\t\t\t/*\n-\t\t\t\t * ANDROID_SENSOR_TEST_PATTERN_MODE_OFF is\n-\t\t\t\t * already in testPatternModes.\n-\t\t\t\t */\n-\t\t\t\tbreak;\n-\n-\t\t\tcase controls::draft::TestPatternModeSolidColor:\n-\t\t\t\ttestPatternModes.push_back(\n-\t\t\t\t\tANDROID_SENSOR_TEST_PATTERN_MODE_SOLID_COLOR);\n-\t\t\t\tbreak;\n-\n-\t\t\tcase controls::draft::TestPatternModeColorBars:\n-\t\t\t\ttestPatternModes.push_back(\n-\t\t\t\t\tANDROID_SENSOR_TEST_PATTERN_MODE_COLOR_BARS);\n-\t\t\t\tbreak;\n-\n-\t\t\tcase controls::draft::TestPatternModeColorBarsFadeToGray:\n-\t\t\t\ttestPatternModes.push_back(\n-\t\t\t\t\tANDROID_SENSOR_TEST_PATTERN_MODE_COLOR_BARS_FADE_TO_GRAY);\n-\t\t\t\tbreak;\n-\n-\t\t\tcase controls::draft::TestPatternModePn9:\n-\t\t\t\ttestPatternModes.push_back(\n-\t\t\t\t\tANDROID_SENSOR_TEST_PATTERN_MODE_PN9);\n-\t\t\t\tbreak;\n-\n-\t\t\tcase controls::draft::TestPatternModeCustom1:\n-\t\t\t\t/* We don't support this yet. */\n-\t\t\t\tbreak;\n-\n-\t\t\tdefault:\n-\t\t\t\tLOG(HAL, Error) << \"Unknown test pattern mode: \"\n-\t\t\t\t\t\t<< value.get<int32_t>();\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t}\n-\t}\n-\tstaticMetadata_->addEntry(ANDROID_SENSOR_AVAILABLE_TEST_PATTERN_MODES,\n-\t\t\t\t  testPatternModes);\n-\n-\tuint8_t timestampSource = ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN;\n-\tstaticMetadata_->addEntry(ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE,\n-\t\t\t\t  timestampSource);\n-\n-\tif (maxFrameDurationNsec > 0)\n-\t\tstaticMetadata_->addEntry(ANDROID_SENSOR_INFO_MAX_FRAME_DURATION,\n-\t\t\t\t\t  maxFrameDurationNsec);\n-\n-\t/* Statistics static metadata. */\n-\tuint8_t faceDetectMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;\n-\tstaticMetadata_->addEntry(ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,\n-\t\t\t\t  faceDetectMode);\n-\n-\tint32_t maxFaceCount = 0;\n-\tstaticMetadata_->addEntry(ANDROID_STATISTICS_INFO_MAX_FACE_COUNT,\n-\t\t\t\t  maxFaceCount);\n-\n-\t{\n-\t\tstd::vector<uint8_t> data;\n-\t\tdata.reserve(2);\n-\t\tconst auto &infoMap = controlsInfo.find(&controls::draft::LensShadingMapMode);\n-\t\tif (infoMap != controlsInfo.end()) {\n-\t\t\tfor (const auto &value : infoMap->second.values())\n-\t\t\t\tdata.push_back(value.get<int32_t>());\n-\t\t} else {\n-\t\t\tdata.push_back(ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF);\n-\t\t}\n-\t\tstaticMetadata_->addEntry(ANDROID_STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES,\n-\t\t\t\t\t  data);\n-\t}\n-\n-\t/* Sync static metadata. */\n-\tint32_t maxLatency = ANDROID_SYNC_MAX_LATENCY_UNKNOWN;\n-\tstaticMetadata_->addEntry(ANDROID_SYNC_MAX_LATENCY, maxLatency);\n-\n-\t/* Flash static metadata. */\n-\tchar flashAvailable = ANDROID_FLASH_INFO_AVAILABLE_FALSE;\n-\tstaticMetadata_->addEntry(ANDROID_FLASH_INFO_AVAILABLE,\n-\t\t\t\t  flashAvailable);\n-\n-\t/* Lens static metadata. */\n-\tstd::vector<float> lensApertures = {\n-\t\t2.53 / 100,\n-\t};\n-\tstaticMetadata_->addEntry(ANDROID_LENS_INFO_AVAILABLE_APERTURES,\n-\t\t\t\t  lensApertures);\n-\n-\tuint8_t lensFacing;\n-\tswitch (facing_) {\n-\tdefault:\n-\tcase CAMERA_FACING_FRONT:\n-\t\tlensFacing = ANDROID_LENS_FACING_FRONT;\n-\t\tbreak;\n-\tcase CAMERA_FACING_BACK:\n-\t\tlensFacing = ANDROID_LENS_FACING_BACK;\n-\t\tbreak;\n-\tcase CAMERA_FACING_EXTERNAL:\n-\t\tlensFacing = ANDROID_LENS_FACING_EXTERNAL;\n-\t\tbreak;\n-\t}\n-\tstaticMetadata_->addEntry(ANDROID_LENS_FACING, lensFacing);\n-\n-\tstd::vector<float> lensFocalLengths = {\n-\t\t1,\n-\t};\n-\tstaticMetadata_->addEntry(ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS,\n-\t\t\t\t  lensFocalLengths);\n-\n-\tstd::vector<uint8_t> opticalStabilizations = {\n-\t\tANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF,\n-\t};\n-\tstaticMetadata_->addEntry(ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,\n-\t\t\t\t  opticalStabilizations);\n-\n-\tfloat hypeFocalDistance = 0;\n-\tstaticMetadata_->addEntry(ANDROID_LENS_INFO_HYPERFOCAL_DISTANCE,\n-\t\t\t\t  hypeFocalDistance);\n-\n-\tfloat minFocusDistance = 0;\n-\tstaticMetadata_->addEntry(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,\n-\t\t\t\t  minFocusDistance);\n-\n-\t/* Noise reduction modes. */\n-\t{\n-\t\tstd::vector<uint8_t> data;\n-\t\tdata.reserve(5);\n-\t\tconst auto &infoMap = controlsInfo.find(&controls::draft::NoiseReductionMode);\n-\t\tif (infoMap != controlsInfo.end()) {\n-\t\t\tfor (const auto &value : infoMap->second.values())\n-\t\t\t\tdata.push_back(value.get<int32_t>());\n-\t\t} else {\n-\t\t\tdata.push_back(ANDROID_NOISE_REDUCTION_MODE_OFF);\n-\t\t}\n-\t\tstaticMetadata_->addEntry(ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,\n-\t\t\t\t\t  data);\n-\t}\n-\n-\t/* Scaler static metadata. */\n-\n-\t/*\n-\t * \\todo The digital zoom factor is a property that depends on the\n-\t * desired output configuration and the sensor frame size input to the\n-\t * ISP. This information is not available to the Android HAL, not at\n-\t * initialization time at least.\n-\t *\n-\t * As a workaround rely on pipeline handlers initializing the\n-\t * ScalerCrop control with the camera default configuration and use the\n-\t * maximum and minimum crop rectangles to calculate the digital zoom\n-\t * factor.\n-\t */\n-\tfloat maxZoom = 1.0f;\n-\tconst auto scalerCrop = controlsInfo.find(&controls::ScalerCrop);\n-\tif (scalerCrop != controlsInfo.end()) {\n-\t\tRectangle min = scalerCrop->second.min().get<Rectangle>();\n-\t\tRectangle max = scalerCrop->second.max().get<Rectangle>();\n-\t\tmaxZoom = std::min(1.0f * max.width / min.width,\n-\t\t\t\t   1.0f * max.height / min.height);\n-\t}\n-\tstaticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM,\n-\t\t\t\t  maxZoom);\n-\n-\tstd::vector<uint32_t> availableStreamConfigurations;\n-\tavailableStreamConfigurations.reserve(streamConfigurations_.size() * 4);\n-\tfor (const auto &entry : streamConfigurations_) {\n-\t\tavailableStreamConfigurations.push_back(entry.androidFormat);\n-\t\tavailableStreamConfigurations.push_back(entry.resolution.width);\n-\t\tavailableStreamConfigurations.push_back(entry.resolution.height);\n-\t\tavailableStreamConfigurations.push_back(\n-\t\t\tANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT);\n-\t}\n-\tstaticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,\n-\t\t\t\t  availableStreamConfigurations);\n-\n-\tstd::vector<int64_t> availableStallDurations = {\n-\t\tANDROID_SCALER_AVAILABLE_FORMATS_BLOB, 2560, 1920, 33333333,\n-\t};\n-\tstaticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_STALL_DURATIONS,\n-\t\t\t\t  availableStallDurations);\n-\n-\t/* Use the minimum frame duration for all the YUV/RGB formats. */\n-\tif (minFrameDurationNsec > 0) {\n-\t\tstd::vector<int64_t> minFrameDurations;\n-\t\tminFrameDurations.reserve(streamConfigurations_.size() * 4);\n-\t\tfor (const auto &entry : streamConfigurations_) {\n-\t\t\tminFrameDurations.push_back(entry.androidFormat);\n-\t\t\tminFrameDurations.push_back(entry.resolution.width);\n-\t\t\tminFrameDurations.push_back(entry.resolution.height);\n-\t\t\tminFrameDurations.push_back(minFrameDurationNsec);\n-\t\t}\n-\t\tstaticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,\n-\t\t\t\t\t  minFrameDurations);\n-\t}\n-\n-\tuint8_t croppingType = ANDROID_SCALER_CROPPING_TYPE_CENTER_ONLY;\n-\tstaticMetadata_->addEntry(ANDROID_SCALER_CROPPING_TYPE, croppingType);\n-\n-\t/* Info static metadata. */\n-\tuint8_t supportedHWLevel = ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;\n-\tstaticMetadata_->addEntry(ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL,\n-\t\t\t\t  supportedHWLevel);\n-\n-\t/* Request static metadata. */\n-\tint32_t partialResultCount = 1;\n-\tstaticMetadata_->addEntry(ANDROID_REQUEST_PARTIAL_RESULT_COUNT,\n-\t\t\t\t  partialResultCount);\n-\n-\t{\n-\t\t/* Default the value to 2 if not reported by the camera. */\n-\t\tuint8_t maxPipelineDepth = 2;\n-\t\tconst auto &infoMap = controlsInfo.find(&controls::draft::PipelineDepth);\n-\t\tif (infoMap != controlsInfo.end())\n-\t\t\tmaxPipelineDepth = infoMap->second.max().get<int32_t>();\n-\t\tstaticMetadata_->addEntry(ANDROID_REQUEST_PIPELINE_MAX_DEPTH,\n-\t\t\t\t\t  maxPipelineDepth);\n-\t}\n-\n-\t/* LIMITED does not support reprocessing. */\n-\tuint32_t maxNumInputStreams = 0;\n-\tstaticMetadata_->addEntry(ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS,\n-\t\t\t\t  maxNumInputStreams);\n-\n-\tstd::vector<uint8_t> availableCapabilities = {\n-\t\tANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE,\n-\t};\n-\n-\t/* Report if camera supports RAW. */\n-\tbool rawStreamAvailable = false;\n-\tstd::unique_ptr<CameraConfiguration> cameraConfig =\n-\t\tcamera_->generateConfiguration({ StreamRole::Raw });\n-\tif (cameraConfig && !cameraConfig->empty()) {\n-\t\tconst PixelFormatInfo &info =\n-\t\t\tPixelFormatInfo::info(cameraConfig->at(0).pixelFormat);\n-\t\t/* Only advertise RAW support if RAW16 is possible. */\n-\t\tif (info.colourEncoding == PixelFormatInfo::ColourEncodingRAW &&\n-\t\t    info.bitsPerPixel == 16) {\n-\t\t\trawStreamAvailable = true;\n-\t\t\tavailableCapabilities.push_back(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_RAW);\n-\t\t}\n-\t}\n-\n-\t/* Number of { RAW, YUV, JPEG } supported output streams */\n-\tint32_t numOutStreams[] = { rawStreamAvailable, 2, 1 };\n-\tstaticMetadata_->addEntry(ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS,\n-\t\t\t\t  numOutStreams);\n-\n-\tstaticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_CAPABILITIES,\n-\t\t\t\t  availableCapabilities);\n-\n-\tstd::vector<int32_t> availableCharacteristicsKeys = {\n-\t\tANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,\n-\t\tANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES,\n-\t\tANDROID_CONTROL_AE_AVAILABLE_MODES,\n-\t\tANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,\n-\t\tANDROID_CONTROL_AE_COMPENSATION_RANGE,\n-\t\tANDROID_CONTROL_AE_COMPENSATION_STEP,\n-\t\tANDROID_CONTROL_AE_LOCK_AVAILABLE,\n-\t\tANDROID_CONTROL_AF_AVAILABLE_MODES,\n-\t\tANDROID_CONTROL_AVAILABLE_EFFECTS,\n-\t\tANDROID_CONTROL_AVAILABLE_MODES,\n-\t\tANDROID_CONTROL_AVAILABLE_SCENE_MODES,\n-\t\tANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,\n-\t\tANDROID_CONTROL_AWB_AVAILABLE_MODES,\n-\t\tANDROID_CONTROL_AWB_LOCK_AVAILABLE,\n-\t\tANDROID_CONTROL_MAX_REGIONS,\n-\t\tANDROID_CONTROL_SCENE_MODE_OVERRIDES,\n-\t\tANDROID_FLASH_INFO_AVAILABLE,\n-\t\tANDROID_INFO_SUPPORTED_HARDWARE_LEVEL,\n-\t\tANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES,\n-\t\tANDROID_JPEG_MAX_SIZE,\n-\t\tANDROID_LENS_FACING,\n-\t\tANDROID_LENS_INFO_AVAILABLE_APERTURES,\n-\t\tANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS,\n-\t\tANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,\n-\t\tANDROID_LENS_INFO_HYPERFOCAL_DISTANCE,\n-\t\tANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,\n-\t\tANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,\n-\t\tANDROID_REQUEST_AVAILABLE_CAPABILITIES,\n-\t\tANDROID_REQUEST_MAX_NUM_INPUT_STREAMS,\n-\t\tANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS,\n-\t\tANDROID_REQUEST_PARTIAL_RESULT_COUNT,\n-\t\tANDROID_REQUEST_PIPELINE_MAX_DEPTH,\n-\t\tANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM,\n-\t\tANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,\n-\t\tANDROID_SCALER_AVAILABLE_STALL_DURATIONS,\n-\t\tANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,\n-\t\tANDROID_SCALER_CROPPING_TYPE,\n-\t\tANDROID_SENSOR_AVAILABLE_TEST_PATTERN_MODES,\n-\t\tANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,\n-\t\tANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT,\n-\t\tANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE,\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-\t\tANDROID_STATISTICS_INFO_MAX_FACE_COUNT,\n-\t\tANDROID_SYNC_MAX_LATENCY,\n-\t};\n-\tstaticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS,\n-\t\t\t\t  availableCharacteristicsKeys);\n-\n-\tstd::vector<int32_t> availableRequestKeys = {\n-\t\tANDROID_COLOR_CORRECTION_ABERRATION_MODE,\n-\t\tANDROID_CONTROL_AE_ANTIBANDING_MODE,\n-\t\tANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,\n-\t\tANDROID_CONTROL_AE_LOCK,\n-\t\tANDROID_CONTROL_AE_MODE,\n-\t\tANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,\n-\t\tANDROID_CONTROL_AE_TARGET_FPS_RANGE,\n-\t\tANDROID_CONTROL_AF_MODE,\n-\t\tANDROID_CONTROL_AF_TRIGGER,\n-\t\tANDROID_CONTROL_AWB_LOCK,\n-\t\tANDROID_CONTROL_AWB_MODE,\n-\t\tANDROID_CONTROL_CAPTURE_INTENT,\n-\t\tANDROID_CONTROL_EFFECT_MODE,\n-\t\tANDROID_CONTROL_MODE,\n-\t\tANDROID_CONTROL_SCENE_MODE,\n-\t\tANDROID_CONTROL_VIDEO_STABILIZATION_MODE,\n-\t\tANDROID_FLASH_MODE,\n-\t\tANDROID_JPEG_ORIENTATION,\n-\t\tANDROID_JPEG_QUALITY,\n-\t\tANDROID_JPEG_THUMBNAIL_QUALITY,\n-\t\tANDROID_JPEG_THUMBNAIL_SIZE,\n-\t\tANDROID_LENS_APERTURE,\n-\t\tANDROID_LENS_OPTICAL_STABILIZATION_MODE,\n-\t\tANDROID_NOISE_REDUCTION_MODE,\n-\t\tANDROID_SCALER_CROP_REGION,\n-\t\tANDROID_STATISTICS_FACE_DETECT_MODE\n-\t};\n-\tstaticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS,\n-\t\t\t\t  availableRequestKeys);\n-\n-\tstd::vector<int32_t> availableResultKeys = {\n-\t\tANDROID_COLOR_CORRECTION_ABERRATION_MODE,\n-\t\tANDROID_CONTROL_AE_ANTIBANDING_MODE,\n-\t\tANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,\n-\t\tANDROID_CONTROL_AE_LOCK,\n-\t\tANDROID_CONTROL_AE_MODE,\n-\t\tANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,\n-\t\tANDROID_CONTROL_AE_STATE,\n-\t\tANDROID_CONTROL_AE_TARGET_FPS_RANGE,\n-\t\tANDROID_CONTROL_AF_MODE,\n-\t\tANDROID_CONTROL_AF_STATE,\n-\t\tANDROID_CONTROL_AF_TRIGGER,\n-\t\tANDROID_CONTROL_AWB_LOCK,\n-\t\tANDROID_CONTROL_AWB_MODE,\n-\t\tANDROID_CONTROL_AWB_STATE,\n-\t\tANDROID_CONTROL_CAPTURE_INTENT,\n-\t\tANDROID_CONTROL_EFFECT_MODE,\n-\t\tANDROID_CONTROL_MODE,\n-\t\tANDROID_CONTROL_SCENE_MODE,\n-\t\tANDROID_CONTROL_VIDEO_STABILIZATION_MODE,\n-\t\tANDROID_FLASH_MODE,\n-\t\tANDROID_FLASH_STATE,\n-\t\tANDROID_JPEG_GPS_COORDINATES,\n-\t\tANDROID_JPEG_GPS_PROCESSING_METHOD,\n-\t\tANDROID_JPEG_GPS_TIMESTAMP,\n-\t\tANDROID_JPEG_ORIENTATION,\n-\t\tANDROID_JPEG_QUALITY,\n-\t\tANDROID_JPEG_SIZE,\n-\t\tANDROID_JPEG_THUMBNAIL_QUALITY,\n-\t\tANDROID_JPEG_THUMBNAIL_SIZE,\n-\t\tANDROID_LENS_APERTURE,\n-\t\tANDROID_LENS_FOCAL_LENGTH,\n-\t\tANDROID_LENS_OPTICAL_STABILIZATION_MODE,\n-\t\tANDROID_LENS_STATE,\n-\t\tANDROID_NOISE_REDUCTION_MODE,\n-\t\tANDROID_REQUEST_PIPELINE_DEPTH,\n-\t\tANDROID_SCALER_CROP_REGION,\n-\t\tANDROID_SENSOR_EXPOSURE_TIME,\n-\t\tANDROID_SENSOR_FRAME_DURATION,\n-\t\tANDROID_SENSOR_ROLLING_SHUTTER_SKEW,\n-\t\tANDROID_SENSOR_TEST_PATTERN_MODE,\n-\t\tANDROID_SENSOR_TIMESTAMP,\n-\t\tANDROID_STATISTICS_FACE_DETECT_MODE,\n-\t\tANDROID_STATISTICS_LENS_SHADING_MAP_MODE,\n-\t\tANDROID_STATISTICS_HOT_PIXEL_MAP_MODE,\n-\t\tANDROID_STATISTICS_SCENE_FLICKER,\n-\t};\n-\tstaticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_RESULT_KEYS,\n-\t\t\t\t  availableResultKeys);\n-\n-\tif (!staticMetadata_->isValid()) {\n-\t\tLOG(HAL, Error) << \"Failed to construct static metadata\";\n-\t\tstaticMetadata_.reset();\n-\t\treturn nullptr;\n-\t}\n-\n-\tif (staticMetadata_->resized()) {\n-\t\tauto [entryCount, dataCount] = staticMetadata_->usage();\n-\t\tLOG(HAL, Info)\n-\t\t\t<< \"Static metadata resized: \" << entryCount\n-\t\t\t<< \" entries and \" << dataCount << \" bytes used\";\n-\t}\n-\n-\treturn staticMetadata_->get();\n-}\n-\n-std::unique_ptr<CameraMetadata> CameraDevice::requestTemplatePreview()\n+void CameraDevice::setCallbacks(const camera3_callback_ops_t *callbacks)\n {\n-\t/*\n-\t * \\todo Keep this in sync with the actual number of entries.\n-\t * Currently: 20 entries, 35 bytes\n-\t */\n-\tauto requestTemplate = std::make_unique<CameraMetadata>(21, 36);\n-\tif (!requestTemplate->isValid()) {\n-\t\treturn nullptr;\n-\t}\n-\n-\t/* Get the FPS range registered in the static metadata. */\n-\tcamera_metadata_ro_entry_t entry;\n-\tbool found = staticMetadata_->getEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,\n-\t\t\t\t\t       &entry);\n-\tif (!found) {\n-\t\tLOG(HAL, Error) << \"Cannot create capture template without FPS range\";\n-\t\treturn nullptr;\n-\t}\n-\n-\t/*\n-\t * Assume the AE_AVAILABLE_TARGET_FPS_RANGE static metadata\n-\t * has been assembled as {{min, max} {max, max}}.\n-\t */\n-\trequestTemplate->addEntry(ANDROID_CONTROL_AE_TARGET_FPS_RANGE,\n-\t\t\t\t  entry.data.i32, 2);\n-\n-\tuint8_t aeMode = ANDROID_CONTROL_AE_MODE_ON;\n-\trequestTemplate->addEntry(ANDROID_CONTROL_AE_MODE, aeMode);\n-\n-\tint32_t aeExposureCompensation = 0;\n-\trequestTemplate->addEntry(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,\n-\t\t\t\t  aeExposureCompensation);\n-\n-\tuint8_t aePrecaptureTrigger = ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_IDLE;\n-\trequestTemplate->addEntry(ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,\n-\t\t\t\t  aePrecaptureTrigger);\n-\n-\tuint8_t aeLock = ANDROID_CONTROL_AE_LOCK_OFF;\n-\trequestTemplate->addEntry(ANDROID_CONTROL_AE_LOCK, aeLock);\n-\n-\tuint8_t aeAntibandingMode = ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO;\n-\trequestTemplate->addEntry(ANDROID_CONTROL_AE_ANTIBANDING_MODE,\n-\t\t\t\t  aeAntibandingMode);\n-\n-\tuint8_t afMode = ANDROID_CONTROL_AF_MODE_OFF;\n-\trequestTemplate->addEntry(ANDROID_CONTROL_AF_MODE, afMode);\n-\n-\tuint8_t afTrigger = ANDROID_CONTROL_AF_TRIGGER_IDLE;\n-\trequestTemplate->addEntry(ANDROID_CONTROL_AF_TRIGGER, afTrigger);\n-\n-\tuint8_t awbMode = ANDROID_CONTROL_AWB_MODE_AUTO;\n-\trequestTemplate->addEntry(ANDROID_CONTROL_AWB_MODE, awbMode);\n-\n-\tuint8_t awbLock = ANDROID_CONTROL_AWB_LOCK_OFF;\n-\trequestTemplate->addEntry(ANDROID_CONTROL_AWB_LOCK, awbLock);\n-\n-\tuint8_t flashMode = ANDROID_FLASH_MODE_OFF;\n-\trequestTemplate->addEntry(ANDROID_FLASH_MODE, flashMode);\n-\n-\tuint8_t faceDetectMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;\n-\trequestTemplate->addEntry(ANDROID_STATISTICS_FACE_DETECT_MODE,\n-\t\t\t\t  faceDetectMode);\n-\n-\tuint8_t noiseReduction = ANDROID_NOISE_REDUCTION_MODE_OFF;\n-\trequestTemplate->addEntry(ANDROID_NOISE_REDUCTION_MODE,\n-\t\t\t\t  noiseReduction);\n-\n-\tuint8_t aberrationMode = ANDROID_COLOR_CORRECTION_ABERRATION_MODE_OFF;\n-\trequestTemplate->addEntry(ANDROID_COLOR_CORRECTION_ABERRATION_MODE,\n-\t\t\t\t  aberrationMode);\n-\n-\tuint8_t controlMode = ANDROID_CONTROL_MODE_AUTO;\n-\trequestTemplate->addEntry(ANDROID_CONTROL_MODE, controlMode);\n-\n-\tfloat lensAperture = 2.53 / 100;\n-\trequestTemplate->addEntry(ANDROID_LENS_APERTURE, lensAperture);\n-\n-\tuint8_t opticalStabilization = ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF;\n-\trequestTemplate->addEntry(ANDROID_LENS_OPTICAL_STABILIZATION_MODE,\n-\t\t\t\t  opticalStabilization);\n-\n-\tuint8_t captureIntent = ANDROID_CONTROL_CAPTURE_INTENT_PREVIEW;\n-\trequestTemplate->addEntry(ANDROID_CONTROL_CAPTURE_INTENT,\n-\t\t\t\t  captureIntent);\n-\n-\treturn requestTemplate;\n+\tcallbacks_ = callbacks;\n }\n \n-std::unique_ptr<CameraMetadata> CameraDevice::requestTemplateVideo()\n+const camera_metadata_t *CameraDevice::getStaticMetadata()\n {\n-\tstd::unique_ptr<CameraMetadata> previewTemplate = requestTemplatePreview();\n-\tif (!previewTemplate)\n-\t\treturn nullptr;\n-\n-\t/*\n-\t * The video template requires a fixed FPS range. Everything else\n-\t * stays the same as the preview template.\n-\t */\n-\tcamera_metadata_ro_entry_t entry;\n-\tstaticMetadata_->getEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,\n-\t\t\t\t  &entry);\n-\n-\t/*\n-\t * Assume the AE_AVAILABLE_TARGET_FPS_RANGE static metadata\n-\t * has been assembled as {{min, max} {max, max}}.\n-\t */\n-\tpreviewTemplate->updateEntry(ANDROID_CONTROL_AE_TARGET_FPS_RANGE,\n-\t\t\t\t     entry.data.i32 + 2, 2);\n-\n-\treturn previewTemplate;\n+\treturn capabilities_.staticMetadata()->get();\n }\n \n /*\n@@ -1630,7 +520,7 @@ const camera_metadata_t *CameraDevice::constructDefaultRequestSettings(int type)\n \tswitch (type) {\n \tcase CAMERA3_TEMPLATE_PREVIEW:\n \t\tcaptureIntent = ANDROID_CONTROL_CAPTURE_INTENT_PREVIEW;\n-\t\trequestTemplate = requestTemplatePreview();\n+\t\trequestTemplate = capabilities_.requestTemplatePreview();\n \t\tbreak;\n \tcase CAMERA3_TEMPLATE_STILL_CAPTURE:\n \t\t/*\n@@ -1638,15 +528,15 @@ const camera_metadata_t *CameraDevice::constructDefaultRequestSettings(int type)\n \t\t * for the torch mode we currently do not support.\n \t\t */\n \t\tcaptureIntent = ANDROID_CONTROL_CAPTURE_INTENT_STILL_CAPTURE;\n-\t\trequestTemplate = requestTemplatePreview();\n+\t\trequestTemplate = capabilities_.requestTemplatePreview();\n \t\tbreak;\n \tcase CAMERA3_TEMPLATE_VIDEO_RECORD:\n \t\tcaptureIntent = ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_RECORD;\n-\t\trequestTemplate = requestTemplateVideo();\n+\t\trequestTemplate = capabilities_.requestTemplateVideo();\n \t\tbreak;\n \tcase CAMERA3_TEMPLATE_VIDEO_SNAPSHOT:\n \t\tcaptureIntent = ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT;\n-\t\trequestTemplate = requestTemplateVideo();\n+\t\trequestTemplate = capabilities_.requestTemplateVideo();\n \t\tbreak;\n \t/* \\todo Implement templates generation for the remaining use cases. */\n \tcase CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG:\n@@ -1668,19 +558,6 @@ const camera_metadata_t *CameraDevice::constructDefaultRequestSettings(int type)\n \treturn requestTemplates_[type]->get();\n }\n \n-PixelFormat CameraDevice::toPixelFormat(int format) const\n-{\n-\t/* Translate Android format code to libcamera pixel format. */\n-\tauto it = formatsMap_.find(format);\n-\tif (it == formatsMap_.end()) {\n-\t\tLOG(HAL, Error) << \"Requested format \" << utils::hex(format)\n-\t\t\t\t<< \" not supported\";\n-\t\treturn PixelFormat();\n-\t}\n-\n-\treturn it->second;\n-}\n-\n /*\n  * Inspect the stream_list to produce a list of StreamConfiguration to\n  * be use to configure the Camera.\n@@ -1727,7 +604,7 @@ int CameraDevice::configureStreams(camera3_stream_configuration_t *stream_list)\n \t\tcamera3_stream_t *stream = stream_list->streams[i];\n \t\tSize size(stream->width, stream->height);\n \n-\t\tPixelFormat format = toPixelFormat(stream->format);\n+\t\tPixelFormat format = capabilities_.toPixelFormat(stream->format);\n \n \t\tLOG(HAL, Info) << \"Stream #\" << i\n \t\t\t       << \", direction: \" << stream->stream_type\ndiff --git a/src/android/camera_device.h b/src/android/camera_device.h\nindex 4aadb27c562c..090fe28a551e 100644\n--- a/src/android/camera_device.h\n+++ b/src/android/camera_device.h\n@@ -10,14 +10,12 @@\n #include <map>\n #include <memory>\n #include <mutex>\n-#include <tuple>\n #include <vector>\n \n #include <hardware/camera3.h>\n \n #include <libcamera/buffer.h>\n #include <libcamera/camera.h>\n-#include <libcamera/geometry.h>\n #include <libcamera/request.h>\n #include <libcamera/stream.h>\n \n@@ -26,6 +24,7 @@\n #include \"libcamera/internal/message.h\"\n #include \"libcamera/internal/thread.h\"\n \n+#include \"camera_capabilities.h\"\n #include \"camera_metadata.h\"\n #include \"camera_stream.h\"\n #include \"camera_worker.h\"\n@@ -57,7 +56,7 @@ public:\n \tconst std::string &model() const { return model_; }\n \tint facing() const { return facing_; }\n \tint orientation() const { return orientation_; }\n-\tunsigned int maxJpegBufferSize() const { return maxJpegBufferSize_; }\n+\tunsigned int maxJpegBufferSize() const;\n \n \tvoid setCallbacks(const camera3_callback_ops_t *callbacks);\n \tconst camera_metadata_t *getStaticMetadata();\n@@ -86,11 +85,6 @@ private:\n \t\tstd::unique_ptr<CaptureRequest> request_;\n \t};\n \n-\tstruct Camera3StreamConfiguration {\n-\t\tlibcamera::Size resolution;\n-\t\tint androidFormat;\n-\t};\n-\n \tenum class State {\n \t\tStopped,\n \t\tFlushing,\n@@ -99,22 +93,11 @@ private:\n \n \tvoid stop();\n \n-\tint initializeStreamConfigurations();\n-\tstd::vector<libcamera::Size>\n-\tgetYUVResolutions(libcamera::CameraConfiguration *cameraConfig,\n-\t\t\t  const libcamera::PixelFormat &pixelFormat,\n-\t\t\t  const std::vector<libcamera::Size> &resolutions);\n-\tstd::vector<libcamera::Size>\n-\tgetRawResolutions(const libcamera::PixelFormat &pixelFormat);\n-\n \tlibcamera::FrameBuffer *createFrameBuffer(const buffer_handle_t camera3buffer);\n \tvoid abortRequest(camera3_capture_request_t *request);\n \tvoid notifyShutter(uint32_t frameNumber, uint64_t timestamp);\n \tvoid notifyError(uint32_t frameNumber, camera3_stream_t *stream,\n \t\t\t camera3_error_msg_code code);\n-\tstd::unique_ptr<CameraMetadata> requestTemplatePreview();\n-\tstd::unique_ptr<CameraMetadata> requestTemplateVideo();\n-\tlibcamera::PixelFormat toPixelFormat(int format) const;\n \tint processControls(Camera3RequestDescriptor *descriptor);\n \tstd::unique_ptr<CameraMetadata> getResultMetadata(\n \t\tconst Camera3RequestDescriptor &descriptor) const;\n@@ -129,13 +112,11 @@ private:\n \n \tstd::shared_ptr<libcamera::Camera> camera_;\n \tstd::unique_ptr<libcamera::CameraConfiguration> config_;\n+\tCameraCapabilities capabilities_;\n \n-\tstd::unique_ptr<CameraMetadata> staticMetadata_;\n \tstd::map<unsigned int, std::unique_ptr<CameraMetadata>> requestTemplates_;\n \tconst camera3_callback_ops_t *callbacks_;\n \n-\tstd::vector<Camera3StreamConfiguration> streamConfigurations_;\n-\tstd::map<int, libcamera::PixelFormat> formatsMap_;\n \tstd::vector<CameraStream> streams_;\n \n \tlibcamera::Mutex descriptorsMutex_; /* Protects descriptors_. */\n@@ -147,8 +128,6 @@ private:\n \tint facing_;\n \tint orientation_;\n \n-\tunsigned int maxJpegBufferSize_;\n-\n \tCameraMetadata lastSettings_;\n };\n \ndiff --git a/src/android/meson.build b/src/android/meson.build\nindex f27fd5316705..6270fb201338 100644\n--- a/src/android/meson.build\n+++ b/src/android/meson.build\n@@ -44,6 +44,7 @@ subdir('cros')\n \n android_hal_sources = files([\n     'camera3_hal.cpp',\n+    'camera_capabilities.cpp',\n     'camera_device.cpp',\n     'camera_hal_config.cpp',\n     'camera_hal_manager.cpp',\n",
    "prefixes": [
        "libcamera-devel",
        "2/2"
    ]
}