summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacopo Mondi <jacopo@jmondi.org>2021-03-23 15:14:28 +0100
committerJacopo Mondi <jacopo@jmondi.org>2021-05-25 15:21:59 +0200
commit07c2a4257d9ef3bae35af1848f9f36ac8ce8044d (patch)
tree01f5eca1598af8fcdb061cc9670252cdee48b087
parent6570f85454e2ef6ef07680f088fa01ad675bf16e (diff)
android: Add CameraHalConfig class
Add a CameraHalConfig class to the Android Camera3 HAL layer. Signed-off-by: Jacopo Mondi <jacopo@jmondi.org> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Hirokazu Honda <hiroh@chromium.org>
-rw-r--r--README.rst2
-rw-r--r--src/android/camera_hal_config.cpp400
-rw-r--r--src/android/camera_hal_config.h40
-rw-r--r--src/android/meson.build2
4 files changed, 443 insertions, 1 deletions
diff --git a/README.rst b/README.rst
index c77e54b4..fcf0f47f 100644
--- a/README.rst
+++ b/README.rst
@@ -88,7 +88,7 @@ for tracing with lttng: [optional]
liblttng-ust-dev python3-jinja2 lttng-tools
for android: [optional]
- libexif libjpeg
+ libexif libjpeg libyaml
Using GStreamer plugin
~~~~~~~~~~~~~~~~~~~~~~
diff --git a/src/android/camera_hal_config.cpp b/src/android/camera_hal_config.cpp
new file mode 100644
index 00000000..12d69a3f
--- /dev/null
+++ b/src/android/camera_hal_config.cpp
@@ -0,0 +1,400 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2021, Google Inc.
+ *
+ * camera_hal_config.cpp - Camera HAL configuration file manager
+ */
+#include "camera_hal_config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string>
+#include <yaml.h>
+
+#include <hardware/camera3.h>
+
+#include "libcamera/internal/log.h"
+
+using namespace libcamera;
+
+LOG_DEFINE_CATEGORY(HALConfig)
+
+class CameraHalConfig::Private : public Extensible::Private
+{
+ LIBCAMERA_DECLARE_PUBLIC(CameraHalConfig)
+
+public:
+ Private(CameraHalConfig *halConfig);
+
+ int parseConfigFile(FILE *fh, std::map<std::string, CameraConfigData> *cameras);
+
+private:
+ std::string parseValue();
+ std::string parseKey();
+ int parseValueBlock();
+ int parseCameraLocation(CameraConfigData *cameraConfigData,
+ const std::string &location);
+ int parseCameraConfigData(const std::string &cameraId);
+ int parseCameras();
+ int parseEntry();
+
+ yaml_parser_t parser_;
+ std::map<std::string, CameraConfigData> *cameras_;
+};
+
+CameraHalConfig::Private::Private(CameraHalConfig *halConfig)
+ : Extensible::Private(halConfig)
+{
+}
+
+std::string CameraHalConfig::Private::parseValue()
+{
+ yaml_token_t token;
+
+ /* Make sure the token type is a value and get its content. */
+ yaml_parser_scan(&parser_, &token);
+ if (token.type != YAML_VALUE_TOKEN) {
+ yaml_token_delete(&token);
+ return "";
+ }
+ yaml_token_delete(&token);
+
+ yaml_parser_scan(&parser_, &token);
+ if (token.type != YAML_SCALAR_TOKEN) {
+ yaml_token_delete(&token);
+ return "";
+ }
+
+ std::string value(reinterpret_cast<char *>(token.data.scalar.value),
+ token.data.scalar.length);
+ yaml_token_delete(&token);
+
+ return value;
+}
+
+std::string CameraHalConfig::Private::parseKey()
+{
+ yaml_token_t token;
+
+ /* Make sure the token type is a key and get its value. */
+ yaml_parser_scan(&parser_, &token);
+ if (token.type != YAML_SCALAR_TOKEN) {
+ yaml_token_delete(&token);
+ return "";
+ }
+
+ std::string value(reinterpret_cast<char *>(token.data.scalar.value),
+ token.data.scalar.length);
+ yaml_token_delete(&token);
+
+ return value;
+}
+
+int CameraHalConfig::Private::parseValueBlock()
+{
+ yaml_token_t token;
+
+ /* Make sure the next token are VALUE and BLOCK_MAPPING_START. */
+ yaml_parser_scan(&parser_, &token);
+ if (token.type != YAML_VALUE_TOKEN) {
+ yaml_token_delete(&token);
+ return -EINVAL;
+ }
+ yaml_token_delete(&token);
+
+ yaml_parser_scan(&parser_, &token);
+ if (token.type != YAML_BLOCK_MAPPING_START_TOKEN) {
+ yaml_token_delete(&token);
+ return -EINVAL;
+ }
+ yaml_token_delete(&token);
+
+ return 0;
+}
+
+int CameraHalConfig::Private::parseCameraLocation(CameraConfigData *cameraConfigData,
+ const std::string &location)
+{
+ if (location == "front")
+ cameraConfigData->facing = CAMERA_FACING_FRONT;
+ else if (location == "back")
+ cameraConfigData->facing = CAMERA_FACING_BACK;
+ else if (location == "external")
+ cameraConfigData->facing = CAMERA_FACING_EXTERNAL;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+int CameraHalConfig::Private::parseCameraConfigData(const std::string &cameraId)
+{
+ int ret = parseValueBlock();
+ if (ret)
+ return ret;
+
+ /*
+ * Parse the camera properties and store them in a cameraConfigData
+ * instance.
+ *
+ * Add a safety counter to make sure we don't loop indefinitely in case
+ * the configuration file is malformed.
+ */
+ CameraConfigData cameraConfigData;
+ unsigned int sentinel = 100;
+ bool blockEnd = false;
+ yaml_token_t token;
+
+ do {
+ yaml_parser_scan(&parser_, &token);
+ switch (token.type) {
+ case YAML_KEY_TOKEN: {
+ yaml_token_delete(&token);
+
+ /*
+ * Parse the camera property key and make sure it is
+ * valid.
+ */
+ std::string key = parseKey();
+ std::string value = parseValue();
+ if (key.empty() || value.empty())
+ return -EINVAL;
+
+ if (key == "location") {
+ ret = parseCameraLocation(&cameraConfigData, value);
+ if (ret) {
+ LOG(HALConfig, Error)
+ << "Unknown location: " << value;
+ return -EINVAL;
+ }
+ } else if (key == "rotation") {
+ ret = std::stoi(value);
+ if (ret < 0 || ret >= 360) {
+ LOG(HALConfig, Error)
+ << "Unknown rotation: " << value;
+ return -EINVAL;
+ }
+ cameraConfigData.rotation = ret;
+ } else {
+ LOG(HALConfig, Error)
+ << "Unknown key: " << key;
+ return -EINVAL;
+ }
+ break;
+ }
+
+ case YAML_BLOCK_END_TOKEN:
+ blockEnd = true;
+ [[fallthrough]];
+ default:
+ yaml_token_delete(&token);
+ break;
+ }
+
+ --sentinel;
+ } while (!blockEnd && sentinel);
+ if (!sentinel)
+ return -EINVAL;
+
+ (*cameras_)[cameraId] = cameraConfigData;
+
+ return 0;
+}
+
+int CameraHalConfig::Private::parseCameras()
+{
+ int ret = parseValueBlock();
+ if (ret) {
+ LOG(HALConfig, Error) << "Configuration file is not valid";
+ return ret;
+ }
+
+ /*
+ * Parse the camera properties.
+ *
+ * Each camera properties block is a list of properties associated
+ * with the ID (as assembled by CameraSensor::generateId()) of the
+ * camera they refer to.
+ *
+ * cameras:
+ * "camera0 id":
+ * key: value
+ * key: value
+ * ...
+ *
+ * "camera1 id":
+ * key: value
+ * key: value
+ * ...
+ */
+ bool blockEnd = false;
+ yaml_token_t token;
+ do {
+ yaml_parser_scan(&parser_, &token);
+ switch (token.type) {
+ case YAML_KEY_TOKEN: {
+ yaml_token_delete(&token);
+
+ /* Parse the camera ID as key of the property list. */
+ std::string cameraId = parseKey();
+ if (cameraId.empty())
+ return -EINVAL;
+
+ ret = parseCameraConfigData(cameraId);
+ if (ret)
+ return -EINVAL;
+ break;
+ }
+ case YAML_BLOCK_END_TOKEN:
+ blockEnd = true;
+ [[fallthrough]];
+ default:
+ yaml_token_delete(&token);
+ break;
+ }
+ } while (!blockEnd);
+
+ return 0;
+}
+
+int CameraHalConfig::Private::parseEntry()
+{
+ int ret = -EINVAL;
+
+ /*
+ * Parse each key we find in the file.
+ *
+ * The 'cameras' keys maps to a list of (lists) of camera properties.
+ */
+
+ std::string key = parseKey();
+ if (key.empty())
+ return ret;
+
+ if (key == "cameras")
+ ret = parseCameras();
+ else
+ LOG(HALConfig, Error) << "Unknown key: " << key;
+
+ return ret;
+}
+
+int CameraHalConfig::Private::parseConfigFile(FILE *fh,
+ std::map<std::string, CameraConfigData> *cameras)
+{
+ cameras_ = cameras;
+
+ int ret = yaml_parser_initialize(&parser_);
+ if (!ret) {
+ LOG(HALConfig, Error) << "Failed to initialize yaml parser";
+ return -EINVAL;
+ }
+ yaml_parser_set_input_file(&parser_, fh);
+
+ yaml_token_t token;
+ yaml_parser_scan(&parser_, &token);
+ if (token.type != YAML_STREAM_START_TOKEN) {
+ LOG(HALConfig, Error) << "Configuration file is not valid";
+ yaml_token_delete(&token);
+ yaml_parser_delete(&parser_);
+ return -EINVAL;
+ }
+ yaml_token_delete(&token);
+
+ yaml_parser_scan(&parser_, &token);
+ if (token.type != YAML_BLOCK_MAPPING_START_TOKEN) {
+ LOG(HALConfig, Error) << "Configuration file is not valid";
+ yaml_token_delete(&token);
+ yaml_parser_delete(&parser_);
+ return -EINVAL;
+ }
+ yaml_token_delete(&token);
+
+ /* Parse the file and parse each single key one by one. */
+ do {
+ yaml_parser_scan(&parser_, &token);
+ switch (token.type) {
+ case YAML_KEY_TOKEN:
+ yaml_token_delete(&token);
+ ret = parseEntry();
+ break;
+
+ case YAML_STREAM_END_TOKEN:
+ ret = -ENOENT;
+ [[fallthrough]];
+ default:
+ yaml_token_delete(&token);
+ break;
+ }
+ } while (ret >= 0);
+ yaml_parser_delete(&parser_);
+
+ if (ret && ret != -ENOENT)
+ LOG(HALConfig, Error) << "Configuration file is not valid";
+
+ return ret == -ENOENT ? 0 : ret;
+}
+
+CameraHalConfig::CameraHalConfig()
+ : Extensible(new Private(this)), exists_(false), valid_(false)
+{
+ parseConfigurationFile();
+}
+
+/*
+ * Open the HAL configuration file and validate its content.
+ * Return 0 on success, a negative error code otherwise
+ * retval -ENOENT The configuration file is not available
+ * retval -EINVAL The configuration file is available but not valid
+ */
+int CameraHalConfig::parseConfigurationFile()
+{
+ std::filesystem::path filePath = LIBCAMERA_SYSCONF_DIR;
+ filePath /= "camera_hal.yaml";
+ if (!std::filesystem::is_regular_file(filePath)) {
+ LOG(HALConfig, Debug)
+ << "Configuration file: \"" << filePath << "\" not found";
+ return -ENOENT;
+ }
+
+ FILE *fh = fopen(filePath.c_str(), "r");
+ if (!fh) {
+ int ret = -errno;
+ LOG(HALConfig, Error) << "Failed to open configuration file "
+ << filePath << ": " << strerror(-ret);
+ return ret;
+ }
+
+ exists_ = true;
+
+ Private *const d = LIBCAMERA_D_PTR();
+ int ret = d->parseConfigFile(fh, &cameras_);
+ fclose(fh);
+ if (ret)
+ return -EINVAL;
+
+ valid_ = true;
+
+ for (const auto &c : cameras_) {
+ const std::string &cameraId = c.first;
+ const CameraConfigData &camera = c.second;
+ LOG(HALConfig, Debug) << "'" << cameraId << "' "
+ << "(" << camera.facing << ")["
+ << camera.rotation << "]";
+ }
+
+ return 0;
+}
+
+const CameraConfigData *CameraHalConfig::cameraConfigData(const std::string &cameraId) const
+{
+ const auto &it = cameras_.find(cameraId);
+ if (it == cameras_.end()) {
+ LOG(HALConfig, Error)
+ << "Camera '" << cameraId
+ << "' not described in the HAL configuration file";
+ return nullptr;
+ }
+
+ return &it->second;
+}
diff --git a/src/android/camera_hal_config.h b/src/android/camera_hal_config.h
new file mode 100644
index 00000000..0555c6da
--- /dev/null
+++ b/src/android/camera_hal_config.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2021, Google Inc.
+ *
+ * camera_hal_config.h - Camera HAL configuration file manager
+ */
+#ifndef __ANDROID_CAMERA_HAL_CONFIG_H__
+#define __ANDROID_CAMERA_HAL_CONFIG_H__
+
+#include <filesystem>
+#include <map>
+#include <string>
+
+#include <libcamera/class.h>
+
+struct CameraConfigData {
+ int facing = -1;
+ int rotation = -1;
+};
+
+class CameraHalConfig final : public libcamera::Extensible
+{
+ LIBCAMERA_DECLARE_PRIVATE()
+
+public:
+ CameraHalConfig();
+
+ bool exists() const { return exists_; }
+ bool isValid() const { return valid_; }
+
+ const CameraConfigData *cameraConfigData(const std::string &cameraId) const;
+
+private:
+ bool exists_;
+ bool valid_;
+ std::map<std::string, CameraConfigData> cameras_;
+
+ int parseConfigurationFile();
+};
+#endif /* __ANDROID_CAMERA_HAL_CONFIG_H__ */
diff --git a/src/android/meson.build b/src/android/meson.build
index 2be20c97..3893e5b5 100644
--- a/src/android/meson.build
+++ b/src/android/meson.build
@@ -3,6 +3,7 @@
android_deps = [
dependency('libexif', required : get_option('android')),
dependency('libjpeg', required : get_option('android')),
+ dependency('yaml-0.1', required : get_option('android')),
]
android_enabled = true
@@ -45,6 +46,7 @@ android_hal_sources = files([
'camera3_hal.cpp',
'camera_hal_manager.cpp',
'camera_device.cpp',
+ 'camera_hal_config.cpp',
'camera_metadata.cpp',
'camera_ops.cpp',
'camera_stream.cpp',