[libcamera-devel] [PATCH v3 3/5] android: Add CameraHalConfig class
Hirokazu Honda
hiroh at chromium.org
Wed Mar 31 11:30:34 CEST 2021
Hi Jacopo, thanks for the patch.
On Tue, Mar 30, 2021 at 11:20 PM Jacopo Mondi <jacopo at jmondi.org> wrote:
>
> Add a CameraHalConfig class to the Android Camera3 HAL layer.
>
> Signed-off-by: Jacopo Mondi <jacopo at jmondi.org>
> ---
> README.rst | 2 +-
> src/android/camera_hal_config.cpp | 407 ++++++++++++++++++++++++++++++
> src/android/camera_hal_config.h | 36 +++
> src/android/meson.build | 2 +
> 4 files changed, 446 insertions(+), 1 deletion(-)
> create mode 100644 src/android/camera_hal_config.cpp
> create mode 100644 src/android/camera_hal_config.h
>
> diff --git a/README.rst b/README.rst
> index 7a98164bbb0a..f68d435196de 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 000000000000..b109ad24e1d1
> --- /dev/null
> +++ b/src/android/camera_hal_config.cpp
> @@ -0,0 +1,407 @@
> +/* 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.h>
> +#include <yaml.h>
> +
> +#include <hardware/camera3.h>
> +
> +#include "libcamera/internal/file.h"
> +#include "libcamera/internal/log.h"
> +
> +using namespace libcamera;
> +
> +LOG_DEFINE_CATEGORY(HALConfig)
> +
> +namespace {
> +
> +std::map<std::string, CameraProps> cameras;
> +
> +std::string parseValue(yaml_parser_t *parser)
> +{
> + 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 "";
> + }
> +
> + char *scalar = reinterpret_cast<char *>(token.data.scalar.value);
> + std::string value(scalar, token.data.scalar.length);
> + yaml_token_delete(&token);
> +
> + return value;
> +}
> +
> +std::string parseKey(yaml_parser_t *parser)
> +{
> + 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 "";
> + }
> +
> + char *scalar = reinterpret_cast<char *>(token.data.scalar.value);
> + std::string key(scalar, token.data.scalar.length);
> + yaml_token_delete(&token);
> +
> + return key;
> +}
> +
> +int parseValueBlock(yaml_parser_t *parser)
> +{
> + 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 parseCameraLocation(CameraProps *cameraProps, const std::string &location)
> +{
> + if (location == "front") {
> + cameraProps->facing = CAMERA_FACING_FRONT;
> + } else if (location == "back") {
> + cameraProps->facing = CAMERA_FACING_BACK;
> + } else if (location == "external") {
> + cameraProps->facing = CAMERA_FACING_EXTERNAL;
> + } else {
> + cameraProps->facing = -EINVAL;
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +int parseCameraProps(yaml_parser_t *parser, const std::string &cameraID)
> +{
> + int ret = parseValueBlock(parser);
> + if (ret)
> + return ret;
> +
> + /*
> + * Parse the camera properties and store them in a cameraProps instance.
> + *
> + * Add a safety counter to make sure we don't loop indefinitely in case
> + * the configuration file is malformed.
> + */
> + unsigned int sentinel = 100;
> + CameraProps cameraProps{};
> + 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(parser);
> + std::string value = parseValue(parser);
> + if (key.empty() || value.empty())
> + return -EINVAL;
> +
> + if (key == "location") {
> + ret = parseCameraLocation(&cameraProps, value);
> + if (ret) {
> + LOG(HALConfig, Error)
> + << "Unupported location: "
> + << value;
> + return -EINVAL;
> + }
> + } else if (key == "rotation") {
> + cameraProps.rotation = strtoul(value.c_str(),
> + NULL, 10);
> + if (cameraProps.rotation < 0) {
> + LOG(HALConfig, Error)
> + << "Unknown rotation: "
> + << cameraProps.rotation;
> + return -EINVAL;
> + }
> + } else if (key == "model") {
> + cameraProps.model = value;
> + } else {
> + LOG(HALConfig, Error)
> + << "Unknown key: " << key;
> + return -EINVAL;
> + }
> + break;
> + }
> + case YAML_BLOCK_END_TOKEN:
> + blockEnd = true;
> + /* Fall-through */
> + default:
> + yaml_token_delete(&token);
> + break;
> + }
> +
> + --sentinel;
> + } while (!blockEnd && sentinel);
> + if (!sentinel)
> + return -EINVAL;
> +
> + cameraProps.valid = true;
> + cameras[cameraID] = cameraProps;
> +
> + return 0;
> +}
> +
> +int parseCameras(yaml_parser_t *parser)
> +{
> + int ret = parseValueBlock(parser);
> + 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(parser);
> + if (cameraId.empty())
> + return -EINVAL;
> +
> + ret = parseCameraProps(parser, cameraId);
> + if (ret)
> + return -EINVAL;
> + break;
> + }
> + case YAML_BLOCK_END_TOKEN:
> + blockEnd = true;
> + /* Fall-through */
> + default:
> + yaml_token_delete(&token);
> + break;
> + }
> + } while (!blockEnd);
> +
> + return 0;
> +}
> +
> +int parseEntry(yaml_parser_t *parser)
> +{
> + 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(parser);
> + if (key.empty())
> + return ret;
> +
> + if (key == "cameras") {
> + ret = parseCameras(parser);
> + } else
> + LOG(HALConfig, Error) << "Unknown key: " << key;
> +
> + return ret;
> +}
> +
> +int parseConfigFile(yaml_parser_t *parser)
> +{
> + yaml_token_t token;
> + int ret = 0;
> +
> + yaml_parser_scan(parser, &token);
> + if (token.type != YAML_STREAM_START_TOKEN) {
> + LOG(HALConfig, Error) << "Configuration file is not valid";
> + yaml_token_delete(&token);
> + 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);
> + 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(parser);
> + break;
> +
> + case YAML_STREAM_END_TOKEN:
> + ret = -ENOENT;
> + /* Fall-through */
> + default:
> + yaml_token_delete(&token);
> + break;
> + }
> + } while (ret >= 0);
> +
> + if (ret && ret != -ENOENT)
> + LOG(HALConfig, Error) << "Configuration file is not valid";
> +
> + return ret == -ENOENT ? 0 : ret;
> +}
> +
> +} /* namespace */
> +
> +std::string CameraHalConfig::findFilePath(const std::string &filename) const
> +{
> + static std::array<std::string, 2> searchPaths = {
> + LIBCAMERA_SYSCONF_DIR,
> + LIBCAMERA_DATA_DIR,
> + };
> +
> + if (File::exists(filename))
> + return filename;
> +
> + std::string root = utils::libcameraSourcePath();
> + if (!root.empty()) {
> + std::string configurationPath = root + "data/" + filename;
> + if (File::exists(configurationPath))
> + return configurationPath;
> + }
> +
> + for (std::string &path : searchPaths) {
> + std::string configurationPath = path + "/" + filename;
> + if (File::exists(configurationPath))
> + return configurationPath;
> + }
> +
> + return "";
> +}
> +
> +FILE *CameraHalConfig::openFile(const std::string &filename)
> +{
> + const std::string filePath = findFilePath(filename);
> + if (filePath.empty()) {
> + LOG(HALConfig, Error)
> + << "Configuration file: \"" << filename << "\" not found";
> + return nullptr;
> + }
> +
> + LOG(HALConfig, Debug) << "Reading configuration file from " << filePath;
> +
> + FILE *fh = fopen(filePath.c_str(), "r");
> + if (!fh) {
> + int ret = -errno;
> + LOG(HALConfig, Error) << "Failed to open configuration file "
> + << filePath << ": " << strerror(-ret);
> + return nullptr;
> + }
> +
> + return fh;
> +}
> +
I don't know if we would like to hide like this way.
We may want to achieve this by D-pointer like libcamera::CameraManager.
I at least think cameras should be a member variable of CameraHalConfig.
I would wait for Laurent's comments.
> +/*
> + * Open the HAL configuration file and validate its content.
> + * Return 0 on success, a negative error code otherwise
> + */
> +int CameraHalConfig::open()
> +{
> + yaml_parser_t parser;
> + int ret = yaml_parser_initialize(&parser);
> + if (!ret) {
> + LOG(HALConfig, Fatal) << "Failed to initialize yaml parser";
> + return -EINVAL;
> + }
> +
> + FILE *fh = openFile("camera_hal.yaml");
> + if (!fh)
> + return -ENOENT;
> +
> + yaml_parser_set_input_file(&parser, fh);
> + ret = parseConfigFile(&parser);
> + fclose(fh);
> + yaml_parser_delete(&parser);
> + if (ret)
> + return ret;
> +
> + for (const auto &c : cameras) {
> + const std::string &cameraId = c.first;
> + const CameraProps &camera = c.second;
> + LOG(HALConfig, Debug) << "'" << cameraId << "' "
> + << " model: " << camera.model
> + << "(" << camera.facing << ")["
> + << camera.rotation << "]";
> + }
> +
> + return 0;
> +}
> +
> +const CameraProps &CameraHalConfig::cameraProps(const std::string &cameraID) const
> +{
> + static CameraProps empty;
> +
> + const auto &it = cameras.find(cameraID);
> + if (it == cameras.end()) {
> + LOG(HALConfig, Error)
> + << "Camera '" << cameraID
> + << "' not described in the HAL configuration file";
> + return empty;
> + }
> +
> + return it->second;
> +}
> diff --git a/src/android/camera_hal_config.h b/src/android/camera_hal_config.h
> new file mode 100644
> index 000000000000..5491a12fdffd
> --- /dev/null
> +++ b/src/android/camera_hal_config.h
> @@ -0,0 +1,36 @@
> +/* 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 <string>
> +
> +class CameraProps
> +{
> +public:
> + CameraProps()
> + : valid(false) {}
> +
> + int facing;
> + std::string model;
> + unsigned int rotation;
> +
> + bool valid;
> +};
This doesn't have a function.
So I would declare this as struct.
https://google.github.io/styleguide/cppguide.html#Structs_vs._Classes
Best Regards,
-Hiro
> +
> +class CameraHalConfig
> +{
> +public:
> + int open();
> + const CameraProps &cameraProps(const std::string &cameraID) const;
> +
> +private:
> + std::string findFilePath(const std::string &filename) const;
> + FILE *openFile(const std::string &filename);
> +};
> +#endif /* __ANDROID_CAMERA_HAL_CONFIG_H__ */
> +
> diff --git a/src/android/meson.build b/src/android/meson.build
> index 8e7d07d9be3c..c0ede407bc38 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
> @@ -41,6 +42,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',
> --
> 2.30.0
>
> _______________________________________________
> libcamera-devel mailing list
> libcamera-devel at lists.libcamera.org
> https://lists.libcamera.org/listinfo/libcamera-devel
More information about the libcamera-devel
mailing list