blob: ca8cfae150265612ca0697ad568920868adb8199 [file] [log] [blame]
/*
* Copyright (C) 2019 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/hardware/camera/device/3.2/ICameraDevice.h>
#include <hardware/gralloc.h>
#include <utils/SystemClock.h>
#include <fstream>
#include <sstream>
#include <thread>
namespace android::hardware::automotive::evs::V1_1::implementation {
using namespace std;
using namespace tinyxml2;
using hardware::camera::device::V3_2::StreamRotation;
ConfigManager::~ConfigManager() {
/* Nothing to do */
}
void ConfigManager::readCameraInfo(const XMLElement* const aCameraElem) {
if (aCameraElem == nullptr) {
ALOGW("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)) {
ALOGW("Failed to read a camera information of %s", 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 */
mCameraGroupInfos.insert_or_assign(id, unique_ptr<CameraGroupInfo>(aCamera));
} else if (!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)) {
ALOGW("Failed to read a camera information of %s", id);
delete aCamera;
continue;
}
/* store read camera module information */
mCameraInfo.insert_or_assign(id, unique_ptr<CameraInfo>(aCamera));
/* assign a camera device to a position group */
mCameraPosition[pos].emplace(id);
} else {
/* ignore other device types */
ALOGD("Unknown element %s is ignored", curElem->Name());
}
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)) {
ALOGW("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;
}
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();
;
const int32_t minVal = stoi(ctrlElem->FindAttribute("min")->Value());
const int32_t maxVal = stoi(ctrlElem->FindAttribute("max")->Value());
int32_t stepVal = 1;
const XMLAttribute* stepAttr = ctrlElem->FindAttribute("step");
if (stepAttr != nullptr) {
stepVal = stoi(stepAttr->Value());
}
CameraParam aParam;
if (ConfigManagerUtil::convertToEvsCameraParam(nameAttr, aParam)) {
aCamera->controls.emplace(aParam, 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");
const int32_t id = stoi(idAttr->Value());
int32_t framerate = 0;
if (fpsAttr != nullptr) {
framerate = stoi(fpsAttr->Value());
}
int32_t pixFormat;
if (ConfigManagerUtil::convertToPixelFormat(fmtAttr->Value(), pixFormat)) {
RawStreamConfiguration cfg = {id,
stoi(widthAttr->Value()),
stoi(heightAttr->Value()),
pixFormat,
ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT,
framerate};
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() * kStreamCfgSz);
/* 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, make_pair(make_unique<void*>(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)) {
curElem->FindAttribute("value")->Value(),
aCamera->cameraMetadata.insert_or_assign(
tag, make_pair(make_unique<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, make_pair(make_unique<void*>(data), len));
++numEntries;
dataSize += calculate_camera_metadata_entry_data_size(
get_camera_metadata_tag_type(tag), len);
break;
}
default:
ALOGW("Parameter %s is not supported", curElem->FindAttribute("name")->Value());
break;
}
}
curElem = curElem->NextSiblingElement("parameter");
}
return numEntries;
}
bool ConfigManager::constructCameraMetadata(CameraInfo* aCamera, const size_t totalEntries,
const size_t totalDataSize) {
if (aCamera == nullptr || !aCamera->allocate(totalEntries, totalDataSize)) {
ALOGE("Failed to allocate memory for camera metadata");
return false;
}
const size_t numStreamConfigs = aCamera->streamConfigurations.size();
unique_ptr<int32_t[]> data(new int32_t[kStreamCfgSz * numStreamConfigs]);
int32_t* ptr = data.get();
for (auto& cfg : aCamera->streamConfigurations) {
for (auto i = 0; i < kStreamCfgSz; ++i) {
*ptr++ = cfg.second[i];
}
}
int32_t err = add_camera_metadata_entry(aCamera->characteristics,
ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
data.get(), numStreamConfigs * kStreamCfgSz);
if (err) {
ALOGE("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.get(),
entry.second);
if (err) {
ALOGE("Failed to add an entry with a tag 0x%X", tag);
/* may exceed preallocated capacity */
ALOGE("Camera metadata has %ld / %ld entries and %ld / %ld bytes are filled",
(long)get_camera_metadata_entry_count(aCamera->characteristics),
(long)get_camera_metadata_entry_capacity(aCamera->characteristics),
(long)get_camera_metadata_data_count(aCamera->characteristics),
(long)get_camera_metadata_data_capacity(aCamera->characteristics));
ALOGE("\tCurrent metadata entry requires %ld bytes",
(long)calculate_camera_metadata_entry_data_size(tag, entry.second));
success = false;
}
}
ALOGV("Camera metadata has %ld / %ld entries and %ld / %ld bytes are filled",
(long)get_camera_metadata_entry_count(aCamera->characteristics),
(long)get_camera_metadata_entry_capacity(aCamera->characteristics),
(long)get_camera_metadata_data_count(aCamera->characteristics),
(long)get_camera_metadata_data_capacity(aCamera->characteristics));
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) {
mSystemInfo.numCameras = stoi(xmlElem->FindAttribute("value")->Value());
}
}
void ConfigManager::readDisplayInfo(const XMLElement* const aDisplayElem) {
if (aDisplayElem == nullptr) {
ALOGW("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();
// const char *pos = curDev->FirstAttribute("position")->Value();
unique_ptr<DisplayInfo> dpy(new DisplayInfo());
if (dpy == nullptr) {
ALOGE("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");
const int32_t id = stoi(idAttr->Value());
int32_t pixFormat;
if (ConfigManagerUtil::convertToPixelFormat(fmtAttr->Value(), pixFormat)) {
RawStreamConfiguration cfg = {
id,
stoi(widthAttr->Value()),
stoi(heightAttr->Value()),
pixFormat,
ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT,
0 // unused
};
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(mConfigFilePath);
if (xmlDoc.ErrorID() != XML_SUCCESS) {
ALOGE("Failed to load and/or parse a configuration file, %s", xmlDoc.ErrorStr());
return false;
}
/* retrieve the root element */
const XMLElement* rootElem = xmlDoc.RootElement();
if (strcmp(rootElem->Name(), "configuration")) {
ALOGE("A configuration file is not in the required format. "
"See /etc/automotive/evs/evs_configuration.dtd");
return false;
}
/*
* 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"));
const int64_t parsingEnd = android::elapsedRealtimeNano();
ALOGI("Parsing configuration file takes %lf (ms)",
(double)(parsingEnd - parsingStart) / 1000000.0);
return true;
}
std::unique_ptr<ConfigManager> ConfigManager::Create(const char* path) {
unique_ptr<ConfigManager> cfgMgr(new ConfigManager(path));
/*
* 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;
}
}
} // namespace android::hardware::automotive::evs::V1_1::implementation