Message ID | 20210413145042.48185-4-jacopo@jmondi.org |
---|---|
State | Superseded |
Headers | show |
Series |
|
Related | show |
Hi Jacopo, Thank you for the patch. On Tue, Apr 13, 2021 at 04:50:40PM +0200, Jacopo Mondi wrote: > Add a CameraHalConfig class to the Android Camera3 HAL layer. > > Reviewed-by: Hirokazu Honda <hiroh@chromium.org> > Signed-off-by: Jacopo Mondi <jacopo@jmondi.org> > --- > README.rst | 2 +- > src/android/camera_hal_config.cpp | 431 ++++++++++++++++++++++++++++++ > src/android/camera_hal_config.h | 44 +++ > src/android/meson.build | 2 + > 4 files changed, 478 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..ffc9ae9bcb70 > --- /dev/null > +++ b/src/android/camera_hal_config.cpp > @@ -0,0 +1,431 @@ > +/* 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, 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"; Should this be an Error ? We can handle this gracefully instead of crashing. > + 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::filesystem::path CameraHalConfig::findFilePath(const std::string &filename) const > +{ > + std::filesystem::path filePath = filename; > + if (std::filesystem::is_regular_file(filePath)) > + return filename; I think this is a bit dangerous. We call this function with "camera_hal.yaml", so we're looking here in the current directory, which isn't something we control (it depends on how the camera service is run). I don't expect to ever have a camera_hal.yaml file in there in normal circumstances, but if it happens for any reason, it could not be the file we want. Do you have a use case for this, or should it be dropped ? > + > + std::filesystem::path root = utils::libcameraSourcePath(); > + if (!root.empty()) { > + filePath = root / "data" / filename; > + if (std::filesystem::is_regular_file(filePath)) > + return filePath; > + } Is this needed ? It's a bit of the same question as before, we can't run the camera HAL from the source directory, as the camera service always looks for it in a system location. > + > + root = LIBCAMERA_SYSCONF_DIR; > + filePath = root / filename; > + if (std::filesystem::is_regular_file(filePath)) > + return filePath; If this becomes the only remaining case in this function, I'd inline it in the caller. > + > + return ""; > +} > + > +FILE *CameraHalConfig::openConfigFile(const std::string &filename) > +{ > + const std::filesystem::path filePath = findFilePath(filename); > + if (filePath.empty()) { > + LOG(HALConfig, Error) > + << "Configuration file: \"" << filename << "\" not found"; > + return nullptr; When using external cameras only, this isn't an error. I'd downgrade it to a Debug message, or possibly an Info message if we consider that internal cameras should be the norm. I'd prefer Debug personally. > + } > + > + 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)), exists_(false), valid_(false) > +{ > +} > + > +/* > + * 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::open() > +{ > + FILE *fh = openConfigFile("camera_hal.yaml"); > + if (!fh) > + return -ENOENT; > + > + 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 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; The caller takes the address of the returned value. Returning a const pointer would be better, as you'll be able to return nullptr here. The valid member of CameraProps can then be dropped, as all configurations we store are valid. > + } > + > + 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..8812eac87b39 > --- /dev/null > +++ b/src/android/camera_hal_config.h > @@ -0,0 +1,44 @@ > +/* 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 CameraProps { I would have named this CameraConfig, as "props" make me think about libcamera::Camera::properties(), but that would be ambiguous with libcamera::CameraConfiguration. Too many concepts in a single code base :-) I wonder if CameraConfigData could be a better name. If you think it is, feel free to rename, otherwise let's leave it as-is. > + int facing = -1; > + int rotation = -1; > + > + bool valid = false; > +}; > + > +class CameraHalConfig final : public libcamera::Extensible > +{ > + LIBCAMERA_DECLARE_PRIVATE(CameraBuffer) > + > +public: > + CameraHalConfig(); > + > + bool exists() const { return exists_; } > + bool valid() const { return valid_; } We use isValid() through the whole code base (except in MediaDevice, I'll send a patch for that), so let's do so here too to be consistent. > + > + int open(); open() could be called from the constructor, given that we ignore its return value in the caller and rely on exists() and valid() only, but that's no big deal. I'm sure we can refactor this later if needed. I'd move the function above exists() though, to match the normal call order. Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> > + const CameraProps &cameraProps(const std::string &cameraId) const; > + > +private: > + std::filesystem::path findFilePath(const std::string &filename) const; > + FILE *openConfigFile(const std::string &filename); > + > + bool exists_; > + bool valid_; > + 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',
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..ffc9ae9bcb70 --- /dev/null +++ b/src/android/camera_hal_config.cpp @@ -0,0 +1,431 @@ +/* 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, 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::filesystem::path CameraHalConfig::findFilePath(const std::string &filename) const +{ + std::filesystem::path filePath = filename; + if (std::filesystem::is_regular_file(filePath)) + return filename; + + std::filesystem::path root = utils::libcameraSourcePath(); + if (!root.empty()) { + filePath = root / "data" / filename; + if (std::filesystem::is_regular_file(filePath)) + return filePath; + } + + root = LIBCAMERA_SYSCONF_DIR; + filePath = root / filename; + if (std::filesystem::is_regular_file(filePath)) + return filePath; + + return ""; +} + +FILE *CameraHalConfig::openConfigFile(const std::string &filename) +{ + const std::filesystem::path 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)), exists_(false), valid_(false) +{ +} + +/* + * 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::open() +{ + FILE *fh = openConfigFile("camera_hal.yaml"); + if (!fh) + return -ENOENT; + + 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 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..8812eac87b39 --- /dev/null +++ b/src/android/camera_hal_config.h @@ -0,0 +1,44 @@ +/* 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 CameraProps { + int facing = -1; + int rotation = -1; + + bool valid = false; +}; + +class CameraHalConfig final : public libcamera::Extensible +{ + LIBCAMERA_DECLARE_PRIVATE(CameraBuffer) + +public: + CameraHalConfig(); + + bool exists() const { return exists_; } + bool valid() const { return valid_; } + + int open(); + const CameraProps &cameraProps(const std::string &cameraId) const; + +private: + std::filesystem::path findFilePath(const std::string &filename) const; + FILE *openConfigFile(const std::string &filename); + + bool exists_; + bool valid_; + 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',