android: Add CameraHalConfig class
Add a CameraHalConfig class to the Android Camera3 HAL layer. Signed-off-by: Jacopo Mondi <jacopo@jmondi.org> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Hirokazu Honda <hiroh@chromium.org>
This commit is contained in:
parent
6570f85454
commit
07c2a4257d
4 changed files with 443 additions and 1 deletions
|
@ -88,7 +88,7 @@ for tracing with lttng: [optional]
|
||||||
liblttng-ust-dev python3-jinja2 lttng-tools
|
liblttng-ust-dev python3-jinja2 lttng-tools
|
||||||
|
|
||||||
for android: [optional]
|
for android: [optional]
|
||||||
libexif libjpeg
|
libexif libjpeg libyaml
|
||||||
|
|
||||||
Using GStreamer plugin
|
Using GStreamer plugin
|
||||||
~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
400
src/android/camera_hal_config.cpp
Normal file
400
src/android/camera_hal_config.cpp
Normal file
|
@ -0,0 +1,400 @@
|
||||||
|
/* 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, CameraConfigData> *cameras);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string parseValue();
|
||||||
|
std::string parseKey();
|
||||||
|
int parseValueBlock();
|
||||||
|
int parseCameraLocation(CameraConfigData *cameraConfigData,
|
||||||
|
const std::string &location);
|
||||||
|
int parseCameraConfigData(const std::string &cameraId);
|
||||||
|
int parseCameras();
|
||||||
|
int parseEntry();
|
||||||
|
|
||||||
|
yaml_parser_t parser_;
|
||||||
|
std::map<std::string, CameraConfigData> *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(CameraConfigData *cameraConfigData,
|
||||||
|
const std::string &location)
|
||||||
|
{
|
||||||
|
if (location == "front")
|
||||||
|
cameraConfigData->facing = CAMERA_FACING_FRONT;
|
||||||
|
else if (location == "back")
|
||||||
|
cameraConfigData->facing = CAMERA_FACING_BACK;
|
||||||
|
else if (location == "external")
|
||||||
|
cameraConfigData->facing = CAMERA_FACING_EXTERNAL;
|
||||||
|
else
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CameraHalConfig::Private::parseCameraConfigData(const std::string &cameraId)
|
||||||
|
{
|
||||||
|
int ret = parseValueBlock();
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse the camera properties and store them in a cameraConfigData
|
||||||
|
* instance.
|
||||||
|
*
|
||||||
|
* Add a safety counter to make sure we don't loop indefinitely in case
|
||||||
|
* the configuration file is malformed.
|
||||||
|
*/
|
||||||
|
CameraConfigData cameraConfigData;
|
||||||
|
unsigned int sentinel = 100;
|
||||||
|
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(&cameraConfigData, 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: " << value;
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
cameraConfigData.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;
|
||||||
|
|
||||||
|
(*cameras_)[cameraId] = cameraConfigData;
|
||||||
|
|
||||||
|
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 = parseCameraConfigData(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, CameraConfigData> *cameras)
|
||||||
|
{
|
||||||
|
cameras_ = cameras;
|
||||||
|
|
||||||
|
int ret = yaml_parser_initialize(&parser_);
|
||||||
|
if (!ret) {
|
||||||
|
LOG(HALConfig, Error) << "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;
|
||||||
|
}
|
||||||
|
|
||||||
|
CameraHalConfig::CameraHalConfig()
|
||||||
|
: Extensible(new Private(this)), exists_(false), valid_(false)
|
||||||
|
{
|
||||||
|
parseConfigurationFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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::parseConfigurationFile()
|
||||||
|
{
|
||||||
|
std::filesystem::path filePath = LIBCAMERA_SYSCONF_DIR;
|
||||||
|
filePath /= "camera_hal.yaml";
|
||||||
|
if (!std::filesystem::is_regular_file(filePath)) {
|
||||||
|
LOG(HALConfig, Debug)
|
||||||
|
<< "Configuration file: \"" << filePath << "\" not found";
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *fh = fopen(filePath.c_str(), "r");
|
||||||
|
if (!fh) {
|
||||||
|
int ret = -errno;
|
||||||
|
LOG(HALConfig, Error) << "Failed to open configuration file "
|
||||||
|
<< filePath << ": " << strerror(-ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 CameraConfigData &camera = c.second;
|
||||||
|
LOG(HALConfig, Debug) << "'" << cameraId << "' "
|
||||||
|
<< "(" << camera.facing << ")["
|
||||||
|
<< camera.rotation << "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CameraConfigData *CameraHalConfig::cameraConfigData(const std::string &cameraId) const
|
||||||
|
{
|
||||||
|
const auto &it = cameras_.find(cameraId);
|
||||||
|
if (it == cameras_.end()) {
|
||||||
|
LOG(HALConfig, Error)
|
||||||
|
<< "Camera '" << cameraId
|
||||||
|
<< "' not described in the HAL configuration file";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return &it->second;
|
||||||
|
}
|
40
src/android/camera_hal_config.h
Normal file
40
src/android/camera_hal_config.h
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
/* 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 CameraConfigData {
|
||||||
|
int facing = -1;
|
||||||
|
int rotation = -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CameraHalConfig final : public libcamera::Extensible
|
||||||
|
{
|
||||||
|
LIBCAMERA_DECLARE_PRIVATE()
|
||||||
|
|
||||||
|
public:
|
||||||
|
CameraHalConfig();
|
||||||
|
|
||||||
|
bool exists() const { return exists_; }
|
||||||
|
bool isValid() const { return valid_; }
|
||||||
|
|
||||||
|
const CameraConfigData *cameraConfigData(const std::string &cameraId) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool exists_;
|
||||||
|
bool valid_;
|
||||||
|
std::map<std::string, CameraConfigData> cameras_;
|
||||||
|
|
||||||
|
int parseConfigurationFile();
|
||||||
|
};
|
||||||
|
#endif /* __ANDROID_CAMERA_HAL_CONFIG_H__ */
|
|
@ -3,6 +3,7 @@
|
||||||
android_deps = [
|
android_deps = [
|
||||||
dependency('libexif', required : get_option('android')),
|
dependency('libexif', required : get_option('android')),
|
||||||
dependency('libjpeg', required : get_option('android')),
|
dependency('libjpeg', required : get_option('android')),
|
||||||
|
dependency('yaml-0.1', required : get_option('android')),
|
||||||
]
|
]
|
||||||
|
|
||||||
android_enabled = true
|
android_enabled = true
|
||||||
|
@ -45,6 +46,7 @@ android_hal_sources = files([
|
||||||
'camera3_hal.cpp',
|
'camera3_hal.cpp',
|
||||||
'camera_hal_manager.cpp',
|
'camera_hal_manager.cpp',
|
||||||
'camera_device.cpp',
|
'camera_device.cpp',
|
||||||
|
'camera_hal_config.cpp',
|
||||||
'camera_metadata.cpp',
|
'camera_metadata.cpp',
|
||||||
'camera_ops.cpp',
|
'camera_ops.cpp',
|
||||||
'camera_stream.cpp',
|
'camera_stream.cpp',
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue