Patch Detail
Show a patch.
GET /api/1.1/patches/11792/?format=api
{ "id": 11792, "url": "https://patchwork.libcamera.org/api/1.1/patches/11792/?format=api", "web_url": "https://patchwork.libcamera.org/patch/11792/", "project": { "id": 1, "url": "https://patchwork.libcamera.org/api/1.1/projects/1/?format=api", "name": "libcamera", "link_name": "libcamera", "list_id": "libcamera_core", "list_email": "libcamera-devel@lists.libcamera.org", "web_url": "", "scm_url": "", "webscm_url": "" }, "msgid": "<20210330142113.37457-4-jacopo@jmondi.org>", "date": "2021-03-30T14:21:11", "name": "[libcamera-devel,v3,3/5] android: Add CameraHalConfig class", "commit_ref": null, "pull_url": null, "state": "changes-requested", "archived": false, "hash": "e0c323790cf3efd47bf195b64bc9f94a2ed3bc9d", "submitter": { "id": 3, "url": "https://patchwork.libcamera.org/api/1.1/people/3/?format=api", "name": "Jacopo Mondi", "email": "jacopo@jmondi.org" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/11792/mbox/", "series": [ { "id": 1871, "url": "https://patchwork.libcamera.org/api/1.1/series/1871/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=1871", "date": "2021-03-30T14:21:08", "name": "android: Introduce HAL configuration file", "version": 3, "mbox": "https://patchwork.libcamera.org/series/1871/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/11792/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/11792/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 DECA8C0DA3\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 30 Mar 2021 14:20:48 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id A5D7768789;\n\tTue, 30 Mar 2021 16:20:48 +0200 (CEST)", "from relay3-d.mail.gandi.net (relay3-d.mail.gandi.net\n\t[217.70.183.195])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id AFCB168787\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 30 Mar 2021 16:20:44 +0200 (CEST)", "from uno.lan (93-34-118-233.ip49.fastwebnet.it [93.34.118.233])\n\t(Authenticated sender: jacopo@jmondi.org)\n\tby relay3-d.mail.gandi.net (Postfix) with ESMTPSA id 468806000C;\n\tTue, 30 Mar 2021 14:20:44 +0000 (UTC)" ], "X-Originating-IP": "93.34.118.233", "From": "Jacopo Mondi <jacopo@jmondi.org>", "To": "libcamera-devel@lists.libcamera.org", "Date": "Tue, 30 Mar 2021 16:21:11 +0200", "Message-Id": "<20210330142113.37457-4-jacopo@jmondi.org>", "X-Mailer": "git-send-email 2.30.0", "In-Reply-To": "<20210330142113.37457-1-jacopo@jmondi.org>", "References": "<20210330142113.37457-1-jacopo@jmondi.org>", "MIME-Version": "1.0", "Subject": "[libcamera-devel] [PATCH v3 3/5] android: Add CameraHalConfig class", "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>", "Content-Type": "text/plain; charset=\"us-ascii\"", "Content-Transfer-Encoding": "7bit", "Errors-To": "libcamera-devel-bounces@lists.libcamera.org", "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>" }, "content": "Add a CameraHalConfig class to the Android Camera3 HAL layer.\n\nSigned-off-by: Jacopo Mondi <jacopo@jmondi.org>\n---\n README.rst | 2 +-\n src/android/camera_hal_config.cpp | 407 ++++++++++++++++++++++++++++++\n src/android/camera_hal_config.h | 36 +++\n src/android/meson.build | 2 +\n 4 files changed, 446 insertions(+), 1 deletion(-)\n create mode 100644 src/android/camera_hal_config.cpp\n create mode 100644 src/android/camera_hal_config.h", "diff": "diff --git a/README.rst b/README.rst\nindex 7a98164bbb0a..f68d435196de 100644\n--- a/README.rst\n+++ b/README.rst\n@@ -88,7 +88,7 @@ for tracing with lttng: [optional]\n liblttng-ust-dev python3-jinja2 lttng-tools\n \n for android: [optional]\n- libexif libjpeg\n+ libexif libjpeg libyaml\n \n Using GStreamer plugin\n ~~~~~~~~~~~~~~~~~~~~~~\ndiff --git a/src/android/camera_hal_config.cpp b/src/android/camera_hal_config.cpp\nnew file mode 100644\nindex 000000000000..b109ad24e1d1\n--- /dev/null\n+++ b/src/android/camera_hal_config.cpp\n@@ -0,0 +1,407 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2021, Google Inc.\n+ *\n+ * camera_hal_config.cpp - Camera HAL configuration file manager\n+ */\n+#include \"camera_hal_config.h\"\n+\n+#include <stdio.h>\n+#include <stdlib.h>\n+#include <string.h>\n+#include <yaml.h>\n+\n+#include <hardware/camera3.h>\n+\n+#include \"libcamera/internal/file.h\"\n+#include \"libcamera/internal/log.h\"\n+\n+using namespace libcamera;\n+\n+LOG_DEFINE_CATEGORY(HALConfig)\n+\n+namespace {\n+\n+std::map<std::string, CameraProps> cameras;\n+\n+std::string parseValue(yaml_parser_t *parser)\n+{\n+\tyaml_token_t token;\n+\n+\t/* Make sure the token type is a value and get its content. */\n+\tyaml_parser_scan(parser, &token);\n+\tif (token.type != YAML_VALUE_TOKEN) {\n+\t\tyaml_token_delete(&token);\n+\t\treturn \"\";\n+\t}\n+\tyaml_token_delete(&token);\n+\n+\tyaml_parser_scan(parser, &token);\n+\tif (token.type != YAML_SCALAR_TOKEN) {\n+\t\tyaml_token_delete(&token);\n+\t\treturn \"\";\n+\t}\n+\n+\tchar *scalar = reinterpret_cast<char *>(token.data.scalar.value);\n+\tstd::string value(scalar, token.data.scalar.length);\n+\tyaml_token_delete(&token);\n+\n+\treturn value;\n+}\n+\n+std::string parseKey(yaml_parser_t *parser)\n+{\n+\tyaml_token_t token;\n+\n+\t/* Make sure the token type is a key and get its value. */\n+\tyaml_parser_scan(parser, &token);\n+\tif (token.type != YAML_SCALAR_TOKEN) {\n+\t\tyaml_token_delete(&token);\n+\t\treturn \"\";\n+\t}\n+\n+\tchar *scalar = reinterpret_cast<char *>(token.data.scalar.value);\n+\tstd::string key(scalar, token.data.scalar.length);\n+\tyaml_token_delete(&token);\n+\n+\treturn key;\n+}\n+\n+int parseValueBlock(yaml_parser_t *parser)\n+{\n+\tyaml_token_t token;\n+\n+\t/* Make sure the next token are VALUE and BLOCK_MAPPING_START. */\n+\tyaml_parser_scan(parser, &token);\n+\tif (token.type != YAML_VALUE_TOKEN) {\n+\t\tyaml_token_delete(&token);\n+\t\treturn -EINVAL;\n+\t}\n+\tyaml_token_delete(&token);\n+\n+\tyaml_parser_scan(parser, &token);\n+\tif (token.type != YAML_BLOCK_MAPPING_START_TOKEN) {\n+\t\tyaml_token_delete(&token);\n+\t\treturn -EINVAL;\n+\t}\n+\tyaml_token_delete(&token);\n+\n+\treturn 0;\n+}\n+\n+int parseCameraLocation(CameraProps *cameraProps, const std::string &location)\n+{\n+\tif (location == \"front\") {\n+\t\tcameraProps->facing = CAMERA_FACING_FRONT;\n+\t} else if (location == \"back\") {\n+\t\tcameraProps->facing = CAMERA_FACING_BACK;\n+\t} else if (location == \"external\") {\n+\t\tcameraProps->facing = CAMERA_FACING_EXTERNAL;\n+\t} else {\n+\t\tcameraProps->facing = -EINVAL;\n+\t\treturn -EINVAL;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+int parseCameraProps(yaml_parser_t *parser, const std::string &cameraID)\n+{\n+\tint ret = parseValueBlock(parser);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/*\n+\t * Parse the camera properties and store them in a cameraProps instance.\n+\t *\n+\t * Add a safety counter to make sure we don't loop indefinitely in case\n+\t * the configuration file is malformed.\n+\t */\n+\tunsigned int sentinel = 100;\n+\tCameraProps cameraProps{};\n+\tbool blockEnd = false;\n+\tyaml_token_t token;\n+\n+\tdo {\n+\t\tyaml_parser_scan(parser, &token);\n+\t\tswitch (token.type) {\n+\t\tcase YAML_KEY_TOKEN: {\n+\t\t\tyaml_token_delete(&token);\n+\n+\t\t\t/*\n+\t\t\t * Parse the camera property key and make sure it is\n+\t\t\t * valid.\n+\t\t\t */\n+\t\t\tstd::string key = parseKey(parser);\n+\t\t\tstd::string value = parseValue(parser);\n+\t\t\tif (key.empty() || value.empty())\n+\t\t\t\treturn -EINVAL;\n+\n+\t\t\tif (key == \"location\") {\n+\t\t\t\tret = parseCameraLocation(&cameraProps, value);\n+\t\t\t\tif (ret) {\n+\t\t\t\t\tLOG(HALConfig, Error)\n+\t\t\t\t\t\t<< \"Unupported location: \"\n+\t\t\t\t\t\t<< value;\n+\t\t\t\t\treturn -EINVAL;\n+\t\t\t\t}\n+\t\t\t} else if (key == \"rotation\") {\n+\t\t\t\tcameraProps.rotation = strtoul(value.c_str(),\n+\t\t\t\t\t\t\t NULL, 10);\n+\t\t\t\tif (cameraProps.rotation < 0) {\n+\t\t\t\t\tLOG(HALConfig, Error)\n+\t\t\t\t\t\t<< \"Unknown rotation: \"\n+\t\t\t\t\t\t<< cameraProps.rotation;\n+\t\t\t\t\treturn -EINVAL;\n+\t\t\t\t}\n+\t\t\t} else if (key == \"model\") {\n+\t\t\t\tcameraProps.model = value;\n+\t\t\t} else {\n+\t\t\t\tLOG(HALConfig, Error)\n+\t\t\t\t\t<< \"Unknown key: \" << key;\n+\t\t\t\treturn -EINVAL;\n+\t\t\t}\n+\t\t\tbreak;\n+\t\t}\n+\t\tcase YAML_BLOCK_END_TOKEN:\n+\t\t\tblockEnd = true;\n+\t\t\t/* Fall-through */\n+\t\tdefault:\n+\t\t\tyaml_token_delete(&token);\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\t--sentinel;\n+\t} while (!blockEnd && sentinel);\n+\tif (!sentinel)\n+\t\treturn -EINVAL;\n+\n+\tcameraProps.valid = true;\n+\tcameras[cameraID] = cameraProps;\n+\n+\treturn 0;\n+}\n+\n+int parseCameras(yaml_parser_t *parser)\n+{\n+\tint ret = parseValueBlock(parser);\n+\tif (ret) {\n+\t\tLOG(HALConfig, Error) << \"Configuration file is not valid\";\n+\t\treturn ret;\n+\t}\n+\n+\t/*\n+\t * Parse the camera properties.\n+\t *\n+\t * Each camera properties block is a list of properties associated\n+\t * with the ID (as assembled by CameraSensor::generateId()) of the\n+\t * camera they refer to.\n+\t *\n+\t * cameras:\n+\t * \"camera0 id\":\n+\t * key: value\n+\t * key: value\n+\t * ...\n+\t *\n+\t * \"camera1 id\":\n+\t * key: value\n+\t * key: value\n+\t * ...\n+\t */\n+\tbool blockEnd = false;\n+\tyaml_token_t token;\n+\tdo {\n+\t\tyaml_parser_scan(parser, &token);\n+\t\tswitch (token.type) {\n+\t\tcase YAML_KEY_TOKEN: {\n+\t\t\tyaml_token_delete(&token);\n+\n+\t\t\t/* Parse the camera ID as key of the property list. */\n+\t\t\tstd::string cameraId = parseKey(parser);\n+\t\t\tif (cameraId.empty())\n+\t\t\t\treturn -EINVAL;\n+\n+\t\t\tret = parseCameraProps(parser, cameraId);\n+\t\t\tif (ret)\n+\t\t\t\treturn -EINVAL;\n+\t\t\tbreak;\n+\t\t}\n+\t\tcase YAML_BLOCK_END_TOKEN:\n+\t\t\tblockEnd = true;\n+\t\t\t/* Fall-through */\n+\t\tdefault:\n+\t\t\tyaml_token_delete(&token);\n+\t\t\tbreak;\n+\t\t}\n+\t} while (!blockEnd);\n+\n+\treturn 0;\n+}\n+\n+int parseEntry(yaml_parser_t *parser)\n+{\n+\tint ret = -EINVAL;\n+\n+\t/*\n+\t * Parse each key we find in the file.\n+\t *\n+\t * The 'cameras' keys maps to a list of (lists) of camera properties.\n+\t */\n+\n+\tstd::string key = parseKey(parser);\n+\tif (key.empty())\n+\t\treturn ret;\n+\n+\tif (key == \"cameras\") {\n+\t\tret = parseCameras(parser);\n+\t} else\n+\t\tLOG(HALConfig, Error) << \"Unknown key: \" << key;\n+\n+\treturn ret;\n+}\n+\n+int parseConfigFile(yaml_parser_t *parser)\n+{\n+\tyaml_token_t token;\n+\tint ret = 0;\n+\n+\tyaml_parser_scan(parser, &token);\n+\tif (token.type != YAML_STREAM_START_TOKEN) {\n+\t\tLOG(HALConfig, Error) << \"Configuration file is not valid\";\n+\t\tyaml_token_delete(&token);\n+\t\treturn -EINVAL;\n+\t}\n+\tyaml_token_delete(&token);\n+\n+\tyaml_parser_scan(parser, &token);\n+\tif (token.type != YAML_BLOCK_MAPPING_START_TOKEN) {\n+\t\tLOG(HALConfig, Error) << \"Configuration file is not valid\";\n+\t\tyaml_token_delete(&token);\n+\t\treturn -EINVAL;\n+\t}\n+\tyaml_token_delete(&token);\n+\n+\t/* Parse the file and parse each single key one by one. */\n+\tdo {\n+\t\tyaml_parser_scan(parser, &token);\n+\t\tswitch (token.type) {\n+\t\tcase YAML_KEY_TOKEN:\n+\t\t\tyaml_token_delete(&token);\n+\t\t\tret = parseEntry(parser);\n+\t\t\tbreak;\n+\n+\t\tcase YAML_STREAM_END_TOKEN:\n+\t\t\tret = -ENOENT;\n+\t\t\t/* Fall-through */\n+\t\tdefault:\n+\t\t\tyaml_token_delete(&token);\n+\t\t\tbreak;\n+\t\t}\n+\t} while (ret >= 0);\n+\n+\tif (ret && ret != -ENOENT)\n+\t\tLOG(HALConfig, Error) << \"Configuration file is not valid\";\n+\n+\treturn ret == -ENOENT ? 0 : ret;\n+}\n+\n+} /* namespace */\n+\n+std::string CameraHalConfig::findFilePath(const std::string &filename) const\n+{\n+\tstatic std::array<std::string, 2> searchPaths = {\n+\t\tLIBCAMERA_SYSCONF_DIR,\n+\t\tLIBCAMERA_DATA_DIR,\n+\t};\n+\n+\tif (File::exists(filename))\n+\t\treturn filename;\n+\n+\tstd::string root = utils::libcameraSourcePath();\n+\tif (!root.empty()) {\n+\t\tstd::string configurationPath = root + \"data/\" + filename;\n+\t\tif (File::exists(configurationPath))\n+\t\t\treturn configurationPath;\n+\t}\n+\n+\tfor (std::string &path : searchPaths) {\n+\t\tstd::string configurationPath = path + \"/\" + filename;\n+\t\tif (File::exists(configurationPath))\n+\t\t\treturn configurationPath;\n+\t}\n+\n+\treturn \"\";\n+}\n+\n+FILE *CameraHalConfig::openFile(const std::string &filename)\n+{\n+\tconst std::string filePath = findFilePath(filename);\n+\tif (filePath.empty()) {\n+\t\tLOG(HALConfig, Error)\n+\t\t\t<< \"Configuration file: \\\"\" << filename << \"\\\" not found\";\n+\t\treturn nullptr;\n+\t}\n+\n+\tLOG(HALConfig, Debug) << \"Reading configuration file from \" << filePath;\n+\n+\tFILE *fh = fopen(filePath.c_str(), \"r\");\n+\tif (!fh) {\n+\t\tint ret = -errno;\n+\t\tLOG(HALConfig, Error) << \"Failed to open configuration file \"\n+\t\t\t\t << filePath << \": \" << strerror(-ret);\n+\t\treturn nullptr;\n+\t}\n+\n+\treturn fh;\n+}\n+\n+/*\n+ * Open the HAL configuration file and validate its content.\n+ * Return 0 on success, a negative error code otherwise\n+ */\n+int CameraHalConfig::open()\n+{\n+\tyaml_parser_t parser;\n+\tint ret = yaml_parser_initialize(&parser);\n+\tif (!ret) {\n+\t\tLOG(HALConfig, Fatal) << \"Failed to initialize yaml parser\";\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tFILE *fh = openFile(\"camera_hal.yaml\");\n+\tif (!fh)\n+\t\treturn -ENOENT;\n+\n+\tyaml_parser_set_input_file(&parser, fh);\n+\tret = parseConfigFile(&parser);\n+\tfclose(fh);\n+\tyaml_parser_delete(&parser);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tfor (const auto &c : cameras) {\n+\t\tconst std::string &cameraId = c.first;\n+\t\tconst CameraProps &camera = c.second;\n+\t\tLOG(HALConfig, Debug) << \"'\" << cameraId << \"' \"\n+\t\t\t\t << \" model: \" << camera.model\n+\t\t\t\t << \"(\" << camera.facing << \")[\"\n+\t\t\t\t << camera.rotation << \"]\";\n+\t}\n+\n+\treturn 0;\n+}\n+\n+const CameraProps &CameraHalConfig::cameraProps(const std::string &cameraID) const\n+{\n+\tstatic CameraProps empty;\n+\n+\tconst auto &it = cameras.find(cameraID);\n+\tif (it == cameras.end()) {\n+\t\tLOG(HALConfig, Error)\n+\t\t\t<< \"Camera '\" << cameraID\n+\t\t\t<< \"' not described in the HAL configuration file\";\n+\t\treturn empty;\n+\t}\n+\n+\treturn it->second;\n+}\ndiff --git a/src/android/camera_hal_config.h b/src/android/camera_hal_config.h\nnew file mode 100644\nindex 000000000000..5491a12fdffd\n--- /dev/null\n+++ b/src/android/camera_hal_config.h\n@@ -0,0 +1,36 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2021, Google Inc.\n+ *\n+ * camera_hal_config.h - Camera HAL configuration file manager\n+ */\n+#ifndef __ANDROID_CAMERA_HAL_CONFIG_H__\n+#define __ANDROID_CAMERA_HAL_CONFIG_H__\n+\n+#include <string>\n+\n+class CameraProps\n+{\n+public:\n+\tCameraProps()\n+\t\t: valid(false) {}\n+\n+\tint facing;\n+\tstd::string model;\n+\tunsigned int rotation;\n+\n+\tbool valid;\n+};\n+\n+class CameraHalConfig\n+{\n+public:\n+\tint open();\n+\tconst CameraProps &cameraProps(const std::string &cameraID) const;\n+\n+private:\n+\tstd::string findFilePath(const std::string &filename) const;\n+\tFILE *openFile(const std::string &filename);\n+};\n+#endif /* __ANDROID_CAMERA_HAL_CONFIG_H__ */\n+\ndiff --git a/src/android/meson.build b/src/android/meson.build\nindex 8e7d07d9be3c..c0ede407bc38 100644\n--- a/src/android/meson.build\n+++ b/src/android/meson.build\n@@ -3,6 +3,7 @@\n android_deps = [\n dependency('libexif', required : get_option('android')),\n dependency('libjpeg', required : get_option('android')),\n+ dependency('yaml-0.1', required : get_option('android')),\n ]\n \n android_enabled = true\n@@ -41,6 +42,7 @@ android_hal_sources = files([\n 'camera3_hal.cpp',\n 'camera_hal_manager.cpp',\n 'camera_device.cpp',\n+ 'camera_hal_config.cpp',\n 'camera_metadata.cpp',\n 'camera_ops.cpp',\n 'camera_stream.cpp',\n", "prefixes": [ "libcamera-devel", "v3", "3/5" ] }