[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