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

Laurent Pinchart laurent.pinchart at ideasonboard.com
Tue Apr 13 06:18:47 CEST 2021


Hi Jacopo,

Thank you for the patch.

On Tue, Apr 06, 2021 at 05:45:55PM +0200, Jacopo Mondi 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,

Could you please reply to the comments in v1 or v2 about this ?

> +	};
> +
> +	if (File::exists(filename))
> +		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 "";
> +}
> +
> +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;
> +}

You could possibly inline this function in CameraHalConfig::open(), up
to you.

Other than these two comments, this looks good.

> +
> +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;
> +};
> +
> +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',

-- 
Regards,

Laurent Pinchart


More information about the libcamera-devel mailing list