[libcamera-devel] [PATCH v4 3/5] android: Add CameraHalConfig class

Hirokazu Honda hiroh at chromium.org
Wed Apr 7 05:45:12 CEST 2021


Hi Jacopo, Thanks for this patch.

On Wed, Apr 7, 2021 at 12:45 AM 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 | 433 ++++++++++++++++++++++++++++++
>  src/android/camera_hal_config.h   |  38 +++
>  src/android/meson.build           |   2 +
>  4 files changed, 474 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 c77e54b48b7a..fcf0f47f14c5 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..5b51fa4dd61b
> --- /dev/null
> +++ b/src/android/camera_hal_config.cpp
> @@ -0,0 +1,433 @@
> +/* 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 <filesystem>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string>
> +#include <yaml.h>
> +
> +#include <hardware/camera3.h>
> +
> +#include "libcamera/internal/file.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, CameraProps> *cameras);
> +
> +private:
> +       std::string parseValue();
> +       std::string parseKey();
> +       int parseValueBlock();
> +       int parseCameraLocation(CameraProps *cameraProps, const std::string &location);
> +       int parseCameraProps(const std::string &cameraId);
> +       int parseCameras();
> +       int parseEntry();
> +
> +       yaml_parser_t parser_;
> +       std::map<std::string, CameraProps> *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(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
> +               return -EINVAL;
> +
> +       return 0;
> +}
> +
> +int CameraHalConfig::Private::parseCameraProps(const std::string &cameraId)
> +{
> +       int ret = parseValueBlock();
> +       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();
> +                       std::string value = parseValue();
> +                       if (key.empty() || value.empty())
> +                               return -EINVAL;
> +
> +                       if (key == "location") {
> +                               ret = parseCameraLocation(&cameraProps, 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: "
> +                                               << cameraProps.rotation;
> +                                       return -EINVAL;
> +                               }
> +                               cameraProps.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;
> +
> +       cameraProps.valid = true;
> +       (*cameras_)[cameraId] = cameraProps;
> +
> +       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 = parseCameraProps(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, CameraProps> *cameras)
> +{
> +       cameras_ = cameras;
> +
> +       int ret = yaml_parser_initialize(&parser_);
> +       if (!ret) {
> +               LOG(HALConfig, Fatal) << "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;
> +}
> +
> +std::string CameraHalConfig::findFilePath(const std::string &filename) const
> +{
> +       static const std::array<std::filesystem::path, 2> searchPaths = {
> +               LIBCAMERA_SYSCONF_DIR,
> +               LIBCAMERA_DATA_DIR,
> +       };
> +
> +       if (File::exists(filename))

nit: Not only the file
> +               return filename;
> +
> +       std::filesystem::path root = utils::libcameraSourcePath();
> +       if (!root.empty()) {
> +               std::string configurationPath = root / "data" / filename;
> +               if (File::exists(configurationPath))
> +                       return configurationPath;
> +       }
> +
> +       for (const std::filesystem::path &path : searchPaths) {
> +               std::string configurationPath = path / filename;
> +               if (File::exists(configurationPath))
> +                       return configurationPath;
> +       }
> +
> +       return "";
> +}
> +

If we use std::filesystem::path, we can use
std::filesystem::is_character_file(filepath). FWIW,
is_character_file() returns false if the file doesn't exist.
I would do as following,
std::filesystem::path findFilePath(const std::filesystem::path&)
FILE *CameraHalConfig::openFile(const std::filesystem::path&)
Then oepnFile("camera_hal.yaml") should work std::filesystem::path can
construct from char*.

> +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;
> +}
> +
> +CameraHalConfig::CameraHalConfig()
> +       : Extensible(new Private(this))
> +{
> +}
> +
> +/*
> + * Open the HAL configuration file and validate its content.
> + * Return 0 on success, a negative error code otherwise
> + */
> +int CameraHalConfig::open()
> +{
> +       FILE *fh = openFile("camera_hal.yaml");
> +       if (!fh)
> +               return -ENOENT;
> +
> +       Private *const d = LIBCAMERA_D_PTR();
> +       int ret = d->parseConfigFile(fh, &cameras_);
> +       fclose(fh);
> +       if (ret)
> +               return ret;
> +
> +       for (const auto &c : cameras_) {
> +               const std::string &cameraId = c.first;
> +               const CameraProps &camera = c.second;
> +               LOG(HALConfig, Debug) << "'" << cameraId << "' "
> +                                     << "(" << 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..65569559fbf0
> --- /dev/null
> +++ b/src/android/camera_hal_config.h
> @@ -0,0 +1,38 @@
> +/* 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 <map>
> +#include <string>
> +
> +#include <libcamera/class.h>
> +
> +struct CameraProps {
> +       int facing;
> +       int rotation;
> +
> +       bool valid = false;
> +};
> +

nit: I would set facing and rotation to -1 as invalid values.

With those nits,
Reviewed-by: Hirokazu Honda <hiroh at chromium.org>

Thanks,
-Hiro
> +class CameraHalConfig final : public libcamera::Extensible
> +{
> +       LIBCAMERA_DECLARE_PRIVATE(CameraBuffer)
> +
> +public:
> +       CameraHalConfig();
> +
> +       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);
> +
> +       std::map<std::string, CameraProps> cameras_;
> +};
> +#endif /* __ANDROID_CAMERA_HAL_CONFIG_H__ */
> diff --git a/src/android/meson.build b/src/android/meson.build
> index 2be20c97e118..3893e5b5b832 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',
> --
> 2.31.1
>
> _______________________________________________
> 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