blob: da791ed0b9c44ba2f274d98eaed691ea5422dee2 [file] [log] [blame]
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ConfigManager.h"
#include <android-base/parseint.h>
#include <hardware/gralloc.h>
#include <utils/SystemClock.h>
#include <fstream>
#include <sstream>
#include <string_view>
#include <thread>
namespace {
using ::aidl::android::hardware::automotive::evs::CameraParam;
using ::aidl::android::hardware::graphics::common::PixelFormat;
using ::tinyxml2::XMLAttribute;
using ::tinyxml2::XMLDocument;
using ::tinyxml2::XMLElement;
} // namespace
std::string_view ConfigManager::sConfigDefaultPath =
"/vendor/etc/automotive/evs/evs_mock_hal_configuration.xml";
std::string_view ConfigManager::sConfigOverridePath =
"/vendor/etc/automotive/evs/evs_configuration_override.xml";
void ConfigManager::printElementNames(const XMLElement* rootElem, const std::string& prefix) const {
const XMLElement* curElem = rootElem;
while (curElem != nullptr) {
LOG(VERBOSE) << "[ELEM] " << prefix << curElem->Name();
const XMLAttribute* curAttr = curElem->FirstAttribute();
while (curAttr) {
LOG(VERBOSE) << "[ATTR] " << prefix << curAttr->Name() << ": " << curAttr->Value();
curAttr = curAttr->Next();
}
/* recursively go down to descendants */
printElementNames(curElem->FirstChildElement(), prefix + "\t");
curElem = curElem->NextSiblingElement();
}
}
void ConfigManager::readCameraInfo(const XMLElement* const aCameraElem) {
if (aCameraElem == nullptr) {
LOG(WARNING) << "XML file does not have required camera element";
return;
}
const XMLElement* curElem = aCameraElem->FirstChildElement();
while (curElem != nullptr) {
if (!strcmp(curElem->Name(), "group")) {
/* camera group identifier */
const char* id = curElem->FindAttribute("id")->Value();
/* create a camera group to be filled */
CameraGroupInfo* aCamera = new CameraGroupInfo();
/* read camera device information */
if (!readCameraDeviceInfo(aCamera, curElem)) {
LOG(WARNING) << "Failed to read a camera information of " << id;
delete aCamera;
continue;
}
/* camera group synchronization */
const char* sync = curElem->FindAttribute("synchronized")->Value();
if (!strcmp(sync, "CALIBRATED")) {
aCamera->synchronized = ANDROID_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_CALIBRATED;
} else if (!strcmp(sync, "APPROXIMATE")) {
aCamera->synchronized = ANDROID_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_APPROXIMATE;
} else {
aCamera->synchronized = 0; // Not synchronized
}
/* add a group to hash map */
mCameraGroups.insert_or_assign(id, std::unique_ptr<CameraGroupInfo>(aCamera));
} else if (!std::strcmp(curElem->Name(), "device")) {
/* camera unique identifier */
const char* id = curElem->FindAttribute("id")->Value();
/* camera mount location */
const char* pos = curElem->FindAttribute("position")->Value();
/* create a camera device to be filled */
CameraInfo* aCamera = new CameraInfo();
/* read camera device information */
if (!readCameraDeviceInfo(aCamera, curElem)) {
LOG(WARNING) << "Failed to read a camera information of " << id;
delete aCamera;
continue;
}
/* store read camera module information */
mCameraInfo.insert_or_assign(id, std::unique_ptr<CameraInfo>(aCamera));
/* assign a camera device to a position group */
mCameraPosition[pos].insert(id);
} else {
/* ignore other device types */
LOG(DEBUG) << "Unknown element " << curElem->Name() << " is ignored";
}
curElem = curElem->NextSiblingElement();
}
}
bool ConfigManager::readCameraDeviceInfo(CameraInfo* aCamera, const XMLElement* aDeviceElem) {
if (aCamera == nullptr || aDeviceElem == nullptr) {
return false;
}
/* size information to allocate camera_metadata_t */
size_t totalEntries = 0;
size_t totalDataSize = 0;
/* read device capabilities */
totalEntries +=
readCameraCapabilities(aDeviceElem->FirstChildElement("caps"), aCamera, totalDataSize);
/* read camera metadata */
totalEntries += readCameraMetadata(aDeviceElem->FirstChildElement("characteristics"), aCamera,
totalDataSize);
/* construct camera_metadata_t */
if (!constructCameraMetadata(aCamera, totalEntries, totalDataSize)) {
LOG(WARNING) << "Either failed to allocate memory or "
<< "allocated memory was not large enough";
}
return true;
}
size_t ConfigManager::readCameraCapabilities(const XMLElement* const aCapElem, CameraInfo* aCamera,
size_t& dataSize) {
if (aCapElem == nullptr || aCamera == nullptr) {
return 0;
}
std::string token;
const XMLElement* curElem = nullptr;
/* a list of supported camera parameters/controls */
curElem = aCapElem->FirstChildElement("supported_controls");
if (curElem != nullptr) {
const XMLElement* ctrlElem = curElem->FirstChildElement("control");
while (ctrlElem != nullptr) {
const char* nameAttr = ctrlElem->FindAttribute("name")->Value();
int32_t minVal = INT32_MIN, maxVal = INT32_MAX;
if (!android::base::ParseInt(ctrlElem->FindAttribute("min")->Value(), &minVal)) {
LOG(WARNING) << "Failed to parse " << ctrlElem->FindAttribute("min")->Value();
}
if (!android::base::ParseInt(ctrlElem->FindAttribute("max")->Value(), &maxVal)) {
LOG(WARNING) << "Failed to parse " << ctrlElem->FindAttribute("max")->Value();
}
int32_t stepVal = 1;
const XMLAttribute* stepAttr = ctrlElem->FindAttribute("step");
if (stepAttr != nullptr) {
if (!android::base::ParseInt(stepAttr->Value(), &stepVal)) {
LOG(WARNING) << "Failed to parse " << stepAttr->Value();
}
}
CameraParam aParam;
if (ConfigManagerUtil::convertToEvsCameraParam(nameAttr, aParam)) {
aCamera->controls.insert_or_assign(
aParam, std::move(std::make_tuple(minVal, maxVal, stepVal)));
}
ctrlElem = ctrlElem->NextSiblingElement("control");
}
}
/* a list of camera stream configurations */
curElem = aCapElem->FirstChildElement("stream");
while (curElem != nullptr) {
/* read 5 attributes */
const XMLAttribute* idAttr = curElem->FindAttribute("id");
const XMLAttribute* widthAttr = curElem->FindAttribute("width");
const XMLAttribute* heightAttr = curElem->FindAttribute("height");
const XMLAttribute* fmtAttr = curElem->FindAttribute("format");
const XMLAttribute* fpsAttr = curElem->FindAttribute("framerate");
int32_t id = -1;
int32_t framerate = 0;
if (!android::base::ParseInt(idAttr->Value(), &id)) {
LOG(WARNING) << "Failed to parse " << idAttr->Value();
}
if (fpsAttr != nullptr) {
if (!android::base::ParseInt(fpsAttr->Value(), &framerate)) {
LOG(WARNING) << "Failed to parse " << fpsAttr->Value();
}
}
PixelFormat format = PixelFormat::UNSPECIFIED;
if (ConfigManagerUtil::convertToPixelFormat(fmtAttr->Value(), format)) {
StreamConfiguration cfg = {
.id = id,
.format = format,
.type = ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT,
.framerate = framerate,
};
if (!android::base::ParseInt(widthAttr->Value(), &cfg.width) ||
!android::base::ParseInt(heightAttr->Value(), &cfg.height)) {
LOG(WARNING) << "Failed to parse " << widthAttr->Value() << " and "
<< heightAttr->Value();
}
aCamera->streamConfigurations.insert_or_assign(id, cfg);
}
curElem = curElem->NextSiblingElement("stream");
}
dataSize = calculate_camera_metadata_entry_data_size(
get_camera_metadata_tag_type(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS),
aCamera->streamConfigurations.size() * sizeof(StreamConfiguration));
/* a single camera metadata entry contains multiple stream configurations */
return dataSize > 0 ? 1 : 0;
}
size_t ConfigManager::readCameraMetadata(const XMLElement* const aParamElem, CameraInfo* aCamera,
size_t& dataSize) {
if (aParamElem == nullptr || aCamera == nullptr) {
return 0;
}
const XMLElement* curElem = aParamElem->FirstChildElement("parameter");
size_t numEntries = 0;
camera_metadata_tag_t tag;
while (curElem != nullptr) {
if (ConfigManagerUtil::convertToMetadataTag(curElem->FindAttribute("name")->Value(), tag)) {
switch (tag) {
case ANDROID_LENS_DISTORTION:
case ANDROID_LENS_POSE_ROTATION:
case ANDROID_LENS_POSE_TRANSLATION:
case ANDROID_LENS_INTRINSIC_CALIBRATION: {
/* float[] */
size_t count = 0;
void* data = ConfigManagerUtil::convertFloatArray(
curElem->FindAttribute("size")->Value(),
curElem->FindAttribute("value")->Value(), count);
aCamera->cameraMetadata.insert_or_assign(tag, std::make_pair(data, count));
++numEntries;
dataSize += calculate_camera_metadata_entry_data_size(
get_camera_metadata_tag_type(tag), count);
break;
}
case ANDROID_REQUEST_AVAILABLE_CAPABILITIES: {
camera_metadata_enum_android_request_available_capabilities_t* data =
new camera_metadata_enum_android_request_available_capabilities_t[1];
if (ConfigManagerUtil::convertToCameraCapability(
curElem->FindAttribute("value")->Value(), *data)) {
aCamera->cameraMetadata.insert_or_assign(tag,
std::make_pair((void*)data, 1));
++numEntries;
dataSize += calculate_camera_metadata_entry_data_size(
get_camera_metadata_tag_type(tag), 1);
}
break;
}
case ANDROID_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS: {
/* a comma-separated list of physical camera devices */
size_t len = strlen(curElem->FindAttribute("value")->Value());
char* data = new char[len + 1];
memcpy(data, curElem->FindAttribute("value")->Value(), len * sizeof(char));
/* replace commas with null char */
char* p = data;
while (*p != '\0') {
if (*p == ',') {
*p = '\0';
}
++p;
}
aCamera->cameraMetadata.insert_or_assign(tag,
std::make_pair((void*)data, len + 1));
++numEntries;
dataSize += calculate_camera_metadata_entry_data_size(
get_camera_metadata_tag_type(tag), len);
break;
}
/* TODO(b/140416878): add vendor-defined/custom tag support */
default:
LOG(WARNING) << "Parameter " << curElem->FindAttribute("name")->Value()
<< " is not supported";
break;
}
} else {
LOG(WARNING) << "Unsupported metadata tag " << curElem->FindAttribute("name")->Value()
<< " is found.";
}
curElem = curElem->NextSiblingElement("parameter");
}
return numEntries;
}
bool ConfigManager::constructCameraMetadata(CameraInfo* aCamera, size_t totalEntries,
size_t totalDataSize) {
if (aCamera == nullptr || !aCamera->allocate(totalEntries, totalDataSize)) {
LOG(ERROR) << "Failed to allocate memory for camera metadata";
return false;
}
const size_t numStreamConfigs = aCamera->streamConfigurations.size();
std::unique_ptr<int32_t[]> data(new int32_t[sizeof(StreamConfiguration) * numStreamConfigs]);
int32_t* ptr = data.get();
for (auto& cfg : aCamera->streamConfigurations) {
memcpy(ptr, &cfg.second, sizeof(StreamConfiguration));
ptr += sizeof(StreamConfiguration);
}
int32_t err = add_camera_metadata_entry(
aCamera->characteristics, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, data.get(),
numStreamConfigs * sizeof(StreamConfiguration));
if (err) {
LOG(ERROR) << "Failed to add stream configurations to metadata, ignored";
return false;
}
bool success = true;
for (auto& [tag, entry] : aCamera->cameraMetadata) {
/* try to add new camera metadata entry */
int32_t err =
add_camera_metadata_entry(aCamera->characteristics, tag, entry.first, entry.second);
if (err) {
LOG(ERROR) << "Failed to add an entry with a tag, " << std::hex << tag;
/* may exceed preallocated capacity */
LOG(ERROR) << "Camera metadata has "
<< get_camera_metadata_entry_count(aCamera->characteristics) << " / "
<< get_camera_metadata_entry_capacity(aCamera->characteristics)
<< " entries and "
<< get_camera_metadata_data_count(aCamera->characteristics) << " / "
<< get_camera_metadata_data_capacity(aCamera->characteristics)
<< " bytes are filled.";
LOG(ERROR) << "\tCurrent metadata entry requires "
<< calculate_camera_metadata_entry_data_size(tag, entry.second) << " bytes.";
success = false;
}
}
LOG(VERBOSE) << "Camera metadata has "
<< get_camera_metadata_entry_count(aCamera->characteristics) << " / "
<< get_camera_metadata_entry_capacity(aCamera->characteristics) << " entries and "
<< get_camera_metadata_data_count(aCamera->characteristics) << " / "
<< get_camera_metadata_data_capacity(aCamera->characteristics)
<< " bytes are filled.";
return success;
}
void ConfigManager::readSystemInfo(const XMLElement* const aSysElem) {
if (aSysElem == nullptr) {
return;
}
/*
* Please note that this function assumes that a given system XML element
* and its child elements follow DTD. If it does not, it will cause a
* segmentation fault due to the failure of finding expected attributes.
*/
/* read number of cameras available in the system */
const XMLElement* xmlElem = aSysElem->FirstChildElement("num_cameras");
if (xmlElem != nullptr) {
if (!android::base::ParseInt(xmlElem->FindAttribute("value")->Value(),
&mSystemInfo.numCameras)) {
LOG(WARNING) << "Failed to parse " << xmlElem->FindAttribute("value")->Value();
}
}
}
void ConfigManager::readDisplayInfo(const XMLElement* const aDisplayElem) {
if (aDisplayElem == nullptr) {
LOG(WARNING) << "XML file does not have required camera element";
return;
}
const XMLElement* curDev = aDisplayElem->FirstChildElement("device");
while (curDev != nullptr) {
const char* id = curDev->FindAttribute("id")->Value();
std::unique_ptr<DisplayInfo> dpy(new DisplayInfo());
if (dpy == nullptr) {
LOG(ERROR) << "Failed to allocate memory for DisplayInfo";
return;
}
const XMLElement* cap = curDev->FirstChildElement("caps");
if (cap != nullptr) {
const XMLElement* curStream = cap->FirstChildElement("stream");
while (curStream != nullptr) {
/* read 4 attributes */
const XMLAttribute* idAttr = curStream->FindAttribute("id");
const XMLAttribute* widthAttr = curStream->FindAttribute("width");
const XMLAttribute* heightAttr = curStream->FindAttribute("height");
const XMLAttribute* fmtAttr = curStream->FindAttribute("format");
int32_t id = -1;
if (!android::base::ParseInt(idAttr->Value(), &id)) {
LOG(WARNING) << "Failed to parse " << idAttr->Value();
}
PixelFormat format = PixelFormat::UNSPECIFIED;
if (ConfigManagerUtil::convertToPixelFormat(fmtAttr->Value(), format)) {
StreamConfiguration cfg = {
.id = id,
.format = format,
.type = ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT,
};
if (!android::base::ParseInt(widthAttr->Value(), &cfg.width) ||
!android::base::ParseInt(heightAttr->Value(), &cfg.height)) {
LOG(WARNING) << "Failed to parse " << widthAttr->Value() << " and "
<< heightAttr->Value();
}
dpy->streamConfigurations.insert_or_assign(id, cfg);
}
curStream = curStream->NextSiblingElement("stream");
}
}
mDisplayInfo.insert_or_assign(id, std::move(dpy));
curDev = curDev->NextSiblingElement("device");
}
return;
}
bool ConfigManager::readConfigDataFromXML() noexcept {
XMLDocument xmlDoc;
const int64_t parsingStart = android::elapsedRealtimeNano();
/* load and parse a configuration file */
xmlDoc.LoadFile(sConfigOverridePath.data());
if (xmlDoc.ErrorID() != tinyxml2::XML_SUCCESS) {
xmlDoc.LoadFile(sConfigDefaultPath.data());
if (xmlDoc.ErrorID() != tinyxml2::XML_SUCCESS) {
LOG(ERROR) << "Failed to load and/or parse a configuration file, " << xmlDoc.ErrorStr();
return false;
}
}
/* retrieve the root element */
const XMLElement* rootElem = xmlDoc.RootElement();
if (std::strcmp(rootElem->Name(), "configuration") != 0) {
LOG(ERROR) << "A configuration file is not in the required format. "
<< "See /etc/automotive/evs/evs_configuration.dtd";
return false;
}
std::unique_lock<std::mutex> lock(mConfigLock);
/*
* parse camera information; this needs to be done before reading system
* information
*/
readCameraInfo(rootElem->FirstChildElement("camera"));
/* parse system information */
readSystemInfo(rootElem->FirstChildElement("system"));
/* parse display information */
readDisplayInfo(rootElem->FirstChildElement("display"));
/* configuration data is ready to be consumed */
mIsReady = true;
/* notify that configuration data is ready */
lock.unlock();
mConfigCond.notify_all();
const int64_t parsingEnd = android::elapsedRealtimeNano();
LOG(INFO) << "Parsing configuration file takes " << std::scientific
<< (double)(parsingEnd - parsingStart) / 1000000.0 << " ms.";
return true;
}
bool ConfigManager::readConfigDataFromBinary() {
/* Temporary buffer to hold configuration data read from a binary file */
char mBuffer[1024];
std::fstream srcFile;
const int64_t readStart = android::elapsedRealtimeNano();
srcFile.open(mBinaryFilePath, std::fstream::in | std::fstream::binary);
if (!srcFile) {
LOG(ERROR) << "Failed to open a source binary file, " << mBinaryFilePath;
return false;
}
std::unique_lock<std::mutex> lock(mConfigLock);
mIsReady = false;
/* read configuration data into the internal buffer */
srcFile.read(mBuffer, sizeof(mBuffer));
LOG(VERBOSE) << __FUNCTION__ << ": " << srcFile.gcount() << " bytes are read.";
char* p = mBuffer;
size_t sz = 0;
/* read number of camera group information entries */
const size_t ngrps = *(reinterpret_cast<size_t*>(p));
p += sizeof(size_t);
/* read each camera information entry */
for (size_t cidx = 0; cidx < ngrps; ++cidx) {
/* read camera identifier */
std::string cameraId = *(reinterpret_cast<std::string*>(p));
p += sizeof(std::string);
/* size of camera_metadata_t */
const size_t num_entry = *(reinterpret_cast<size_t*>(p));
p += sizeof(size_t);
const size_t num_data = *(reinterpret_cast<size_t*>(p));
p += sizeof(size_t);
/* create CameraInfo and add it to hash map */
std::unique_ptr<ConfigManager::CameraGroupInfo> aCamera;
if (aCamera == nullptr || !aCamera->allocate(num_entry, num_data)) {
LOG(ERROR) << "Failed to create new CameraInfo object";
mCameraInfo.clear();
return false;
}
/* controls */
typedef struct {
CameraParam cid;
int32_t min;
int32_t max;
int32_t step;
} CameraCtrl;
sz = *(reinterpret_cast<size_t*>(p));
p += sizeof(size_t);
CameraCtrl* ptr = reinterpret_cast<CameraCtrl*>(p);
for (size_t idx = 0; idx < sz; ++idx) {
CameraCtrl temp = *ptr++;
aCamera->controls.insert_or_assign(
temp.cid, std::move(std::make_tuple(temp.min, temp.max, temp.step)));
}
p = reinterpret_cast<char*>(ptr);
/* stream configurations */
sz = *(reinterpret_cast<size_t*>(p));
p += sizeof(size_t);
int32_t* i32_ptr = reinterpret_cast<int32_t*>(p);
for (size_t idx = 0; idx < sz; ++idx) {
const int32_t id = *i32_ptr++;
StreamConfiguration temp;
memcpy(&temp, i32_ptr, sizeof(StreamConfiguration));
i32_ptr += sizeof(StreamConfiguration);
aCamera->streamConfigurations.insert_or_assign(id, temp);
}
p = reinterpret_cast<char*>(i32_ptr);
/* synchronization */
aCamera->synchronized = *(reinterpret_cast<int32_t*>(p));
p += sizeof(int32_t);
for (size_t idx = 0; idx < num_entry; ++idx) {
/* Read camera metadata entries */
camera_metadata_tag_t tag = *reinterpret_cast<camera_metadata_tag_t*>(p);
p += sizeof(camera_metadata_tag_t);
const size_t count = *reinterpret_cast<size_t*>(p);
p += sizeof(size_t);
const int32_t type = get_camera_metadata_tag_type(tag);
switch (type) {
case TYPE_BYTE: {
add_camera_metadata_entry(aCamera->characteristics, tag, p, count);
p += count * sizeof(uint8_t);
break;
}
case TYPE_INT32: {
add_camera_metadata_entry(aCamera->characteristics, tag, p, count);
p += count * sizeof(int32_t);
break;
}
case TYPE_FLOAT: {
add_camera_metadata_entry(aCamera->characteristics, tag, p, count);
p += count * sizeof(float);
break;
}
case TYPE_INT64: {
add_camera_metadata_entry(aCamera->characteristics, tag, p, count);
p += count * sizeof(int64_t);
break;
}
case TYPE_DOUBLE: {
add_camera_metadata_entry(aCamera->characteristics, tag, p, count);
p += count * sizeof(double);
break;
}
case TYPE_RATIONAL:
p += count * sizeof(camera_metadata_rational_t);
break;
default:
LOG(WARNING) << "Type " << type << " is unknown; "
<< "data may be corrupted.";
break;
}
}
mCameraInfo.insert_or_assign(cameraId, std::move(aCamera));
}
/* read number of camera information entries */
const size_t ncams = *(reinterpret_cast<size_t*>(p));
p += sizeof(size_t);
/* read each camera information entry */
for (size_t cidx = 0; cidx < ncams; ++cidx) {
/* read camera identifier */
std::string cameraId = *(reinterpret_cast<std::string*>(p));
p += sizeof(std::string);
/* size of camera_metadata_t */
const size_t num_entry = *(reinterpret_cast<size_t*>(p));
p += sizeof(size_t);
const size_t num_data = *(reinterpret_cast<size_t*>(p));
p += sizeof(size_t);
/* create CameraInfo and add it to hash map */
std::unique_ptr<ConfigManager::CameraInfo> aCamera;
if (aCamera == nullptr || !aCamera->allocate(num_entry, num_data)) {
LOG(ERROR) << "Failed to create new CameraInfo object";
mCameraInfo.clear();
return false;
}
/* controls */
typedef struct {
CameraParam cid;
int32_t min;
int32_t max;
int32_t step;
} CameraCtrl;
sz = *(reinterpret_cast<size_t*>(p));
p += sizeof(size_t);
CameraCtrl* ptr = reinterpret_cast<CameraCtrl*>(p);
for (size_t idx = 0; idx < sz; ++idx) {
CameraCtrl temp = *ptr++;
aCamera->controls.insert_or_assign(
temp.cid, std::move(std::make_tuple(temp.min, temp.max, temp.step)));
}
p = reinterpret_cast<char*>(ptr);
/* stream configurations */
sz = *(reinterpret_cast<size_t*>(p));
p += sizeof(size_t);
int32_t* i32_ptr = reinterpret_cast<int32_t*>(p);
for (size_t idx = 0; idx < sz; ++idx) {
const int32_t id = *i32_ptr++;
StreamConfiguration temp;
memcpy(&temp, i32_ptr, sizeof(StreamConfiguration));
i32_ptr += sizeof(StreamConfiguration);
aCamera->streamConfigurations.insert_or_assign(id, temp);
}
p = reinterpret_cast<char*>(i32_ptr);
for (size_t idx = 0; idx < num_entry; ++idx) {
/* Read camera metadata entries */
camera_metadata_tag_t tag = *reinterpret_cast<camera_metadata_tag_t*>(p);
p += sizeof(camera_metadata_tag_t);
const size_t count = *reinterpret_cast<size_t*>(p);
p += sizeof(size_t);
const int32_t type = get_camera_metadata_tag_type(tag);
switch (type) {
case TYPE_BYTE: {
add_camera_metadata_entry(aCamera->characteristics, tag, p, count);
p += count * sizeof(uint8_t);
break;
}
case TYPE_INT32: {
add_camera_metadata_entry(aCamera->characteristics, tag, p, count);
p += count * sizeof(int32_t);
break;
}
case TYPE_FLOAT: {
add_camera_metadata_entry(aCamera->characteristics, tag, p, count);
p += count * sizeof(float);
break;
}
case TYPE_INT64: {
add_camera_metadata_entry(aCamera->characteristics, tag, p, count);
p += count * sizeof(int64_t);
break;
}
case TYPE_DOUBLE: {
add_camera_metadata_entry(aCamera->characteristics, tag, p, count);
p += count * sizeof(double);
break;
}
case TYPE_RATIONAL:
p += count * sizeof(camera_metadata_rational_t);
break;
default:
LOG(WARNING) << "Type " << type << " is unknown; "
<< "data may be corrupted.";
break;
}
}
mCameraInfo.insert_or_assign(cameraId, std::move(aCamera));
}
mIsReady = true;
/* notify that configuration data is ready */
lock.unlock();
mConfigCond.notify_all();
int64_t readEnd = android::elapsedRealtimeNano();
LOG(INFO) << __FUNCTION__ << " takes " << std::scientific
<< (double)(readEnd - readStart) / 1000000.0 << " ms.";
return true;
}
bool ConfigManager::writeConfigDataToBinary() {
std::fstream outFile;
const int64_t writeStart = android::elapsedRealtimeNano();
outFile.open(mBinaryFilePath, std::fstream::out | std::fstream::binary);
if (!outFile) {
LOG(ERROR) << "Failed to open a destination binary file, " << mBinaryFilePath;
return false;
}
/* lock a configuration data while it's being written to the filesystem */
std::lock_guard<std::mutex> lock(mConfigLock);
/* write camera group information */
size_t sz = mCameraGroups.size();
outFile.write(reinterpret_cast<const char*>(&sz), sizeof(size_t));
for (auto&& [camId, camInfo] : mCameraGroups) {
LOG(INFO) << "Storing camera group " << camId;
/* write a camera identifier string */
outFile.write(reinterpret_cast<const char*>(&camId), sizeof(std::string));
/* controls */
sz = camInfo->controls.size();
outFile.write(reinterpret_cast<const char*>(&sz), sizeof(size_t));
for (auto&& [ctrl, range] : camInfo->controls) {
outFile.write(reinterpret_cast<const char*>(&ctrl), sizeof(CameraParam));
outFile.write(reinterpret_cast<const char*>(&std::get<0>(range)), sizeof(int32_t));
outFile.write(reinterpret_cast<const char*>(&std::get<1>(range)), sizeof(int32_t));
outFile.write(reinterpret_cast<const char*>(&std::get<2>(range)), sizeof(int32_t));
}
/* stream configurations */
sz = camInfo->streamConfigurations.size();
outFile.write(reinterpret_cast<const char*>(&sz), sizeof(size_t));
for (auto&& [sid, cfg] : camInfo->streamConfigurations) {
outFile.write(reinterpret_cast<const char*>(sid), sizeof(int32_t));
outFile.write(reinterpret_cast<const char*>(&cfg), sizeof(cfg));
}
/* synchronization */
outFile.write(reinterpret_cast<const char*>(&camInfo->synchronized), sizeof(int32_t));
/* size of camera_metadata_t */
size_t num_entry = 0;
size_t num_data = 0;
if (camInfo->characteristics != nullptr) {
num_entry = get_camera_metadata_entry_count(camInfo->characteristics);
num_data = get_camera_metadata_data_count(camInfo->characteristics);
}
outFile.write(reinterpret_cast<const char*>(&num_entry), sizeof(size_t));
outFile.write(reinterpret_cast<const char*>(&num_data), sizeof(size_t));
/* write each camera metadata entry */
if (num_entry > 0) {
camera_metadata_entry_t entry;
for (size_t idx = 0; idx < num_entry; ++idx) {
if (get_camera_metadata_entry(camInfo->characteristics, idx, &entry)) {
LOG(ERROR) << "Failed to retrieve camera metadata entry " << idx;
outFile.close();
return false;
}
outFile.write(reinterpret_cast<const char*>(&entry.tag), sizeof(entry.tag));
outFile.write(reinterpret_cast<const char*>(&entry.count), sizeof(entry.count));
int32_t type = get_camera_metadata_tag_type(entry.tag);
switch (type) {
case TYPE_BYTE:
outFile.write(reinterpret_cast<const char*>(entry.data.u8),
sizeof(uint8_t) * entry.count);
break;
case TYPE_INT32:
outFile.write(reinterpret_cast<const char*>(entry.data.i32),
sizeof(int32_t) * entry.count);
break;
case TYPE_FLOAT:
outFile.write(reinterpret_cast<const char*>(entry.data.f),
sizeof(float) * entry.count);
break;
case TYPE_INT64:
outFile.write(reinterpret_cast<const char*>(entry.data.i64),
sizeof(int64_t) * entry.count);
break;
case TYPE_DOUBLE:
outFile.write(reinterpret_cast<const char*>(entry.data.d),
sizeof(double) * entry.count);
break;
case TYPE_RATIONAL:
[[fallthrough]];
default:
LOG(WARNING) << "Type " << type << " is not supported.";
break;
}
}
}
}
/* write camera device information */
sz = mCameraInfo.size();
outFile.write(reinterpret_cast<const char*>(&sz), sizeof(size_t));
for (auto&& [camId, camInfo] : mCameraInfo) {
LOG(INFO) << "Storing camera " << camId;
/* write a camera identifier string */
outFile.write(reinterpret_cast<const char*>(&camId), sizeof(std::string));
/* controls */
sz = camInfo->controls.size();
outFile.write(reinterpret_cast<const char*>(&sz), sizeof(size_t));
for (auto& [ctrl, range] : camInfo->controls) {
outFile.write(reinterpret_cast<const char*>(&ctrl), sizeof(CameraParam));
outFile.write(reinterpret_cast<const char*>(&std::get<0>(range)), sizeof(int32_t));
outFile.write(reinterpret_cast<const char*>(&std::get<1>(range)), sizeof(int32_t));
outFile.write(reinterpret_cast<const char*>(&std::get<2>(range)), sizeof(int32_t));
}
/* stream configurations */
sz = camInfo->streamConfigurations.size();
outFile.write(reinterpret_cast<const char*>(&sz), sizeof(size_t));
for (auto&& [sid, cfg] : camInfo->streamConfigurations) {
outFile.write(reinterpret_cast<const char*>(sid), sizeof(int32_t));
outFile.write(reinterpret_cast<const char*>(&cfg), sizeof(cfg));
}
/* size of camera_metadata_t */
size_t num_entry = 0;
size_t num_data = 0;
if (camInfo->characteristics != nullptr) {
num_entry = get_camera_metadata_entry_count(camInfo->characteristics);
num_data = get_camera_metadata_data_count(camInfo->characteristics);
}
outFile.write(reinterpret_cast<const char*>(&num_entry), sizeof(size_t));
outFile.write(reinterpret_cast<const char*>(&num_data), sizeof(size_t));
/* write each camera metadata entry */
if (num_entry > 0) {
camera_metadata_entry_t entry;
for (size_t idx = 0; idx < num_entry; ++idx) {
if (get_camera_metadata_entry(camInfo->characteristics, idx, &entry)) {
LOG(ERROR) << "Failed to retrieve camera metadata entry " << idx;
outFile.close();
return false;
}
outFile.write(reinterpret_cast<const char*>(&entry.tag), sizeof(entry.tag));
outFile.write(reinterpret_cast<const char*>(&entry.count), sizeof(entry.count));
int32_t type = get_camera_metadata_tag_type(entry.tag);
switch (type) {
case TYPE_BYTE:
outFile.write(reinterpret_cast<const char*>(entry.data.u8),
sizeof(uint8_t) * entry.count);
break;
case TYPE_INT32:
outFile.write(reinterpret_cast<const char*>(entry.data.i32),
sizeof(int32_t) * entry.count);
break;
case TYPE_FLOAT:
outFile.write(reinterpret_cast<const char*>(entry.data.f),
sizeof(float) * entry.count);
break;
case TYPE_INT64:
outFile.write(reinterpret_cast<const char*>(entry.data.i64),
sizeof(int64_t) * entry.count);
break;
case TYPE_DOUBLE:
outFile.write(reinterpret_cast<const char*>(entry.data.d),
sizeof(double) * entry.count);
break;
case TYPE_RATIONAL:
[[fallthrough]];
default:
LOG(WARNING) << "Type " << type << " is not supported.";
break;
}
}
}
}
outFile.close();
int64_t writeEnd = android::elapsedRealtimeNano();
LOG(INFO) << __FUNCTION__ << " takes " << std::scientific
<< (double)(writeEnd - writeStart) / 1000000.0 << " ms.";
return true;
}
std::unique_ptr<ConfigManager> ConfigManager::Create() {
std::unique_ptr<ConfigManager> cfgMgr(new ConfigManager());
/*
* Read a configuration from XML file
*
* If this is too slow, ConfigManager::readConfigDataFromBinary() and
* ConfigManager::writeConfigDataToBinary()can serialize CameraInfo object
* to the filesystem and construct CameraInfo instead; this was
* evaluated as 10x faster.
*/
if (!cfgMgr->readConfigDataFromXML()) {
return nullptr;
} else {
return cfgMgr;
}
}
ConfigManager::CameraInfo::~CameraInfo() {
free_camera_metadata(characteristics);
for (auto&& [tag, val] : cameraMetadata) {
switch (tag) {
case ANDROID_LENS_DISTORTION:
case ANDROID_LENS_POSE_ROTATION:
case ANDROID_LENS_POSE_TRANSLATION:
case ANDROID_LENS_INTRINSIC_CALIBRATION: {
delete[] reinterpret_cast<float*>(val.first);
break;
}
case ANDROID_REQUEST_AVAILABLE_CAPABILITIES: {
delete[] reinterpret_cast<
camera_metadata_enum_android_request_available_capabilities_t*>(val.first);
break;
}
case ANDROID_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS: {
delete[] reinterpret_cast<char*>(val.first);
break;
}
default:
LOG(WARNING) << "Tag " << std::hex << tag << " is not supported. "
<< "Data may be corrupted?";
break;
}
}
}