/* * Copyright (C) 2022 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 #include #include #include #include #include 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_aidl_hal_configuration.xml"; std::string_view ConfigManager::sConfigOverridePath = "/vendor/etc/automotive/evs/evs_configuration_override.xml"; void ConfigManager::printElementNames(const XMLElement* rootElem, 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(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(aCamera)); /* assign a camera device to a position group */ mCameraPosition[pos].insert(std::move(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(); const int32_t minVal = std::stoi(ctrlElem->FindAttribute("min")->Value()); const int32_t maxVal = std::stoi(ctrlElem->FindAttribute("max")->Value()); int32_t stepVal = 1; const XMLAttribute* stepAttr = ctrlElem->FindAttribute("step"); if (stepAttr != nullptr) { stepVal = std::stoi(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"); const int32_t id = std::stoi(idAttr->Value()); int32_t framerate = 0; if (fpsAttr != nullptr) { framerate = std::stoi(fpsAttr->Value()); } PixelFormat format = PixelFormat::UNSPECIFIED; if (ConfigManagerUtil::convertToPixelFormat(fmtAttr->Value(), format)) { StreamConfiguration cfg = { .id = id, .width = std::stoi(widthAttr->Value()), .height = std::stoi(heightAttr->Value()), .format = format, .type = ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT, .framerate = 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() * 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 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) { mSystemInfo.numCameras = std::stoi(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 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"); const int32_t id = std::stoi(idAttr->Value()); PixelFormat format = PixelFormat::UNSPECIFIED; if (ConfigManagerUtil::convertToPixelFormat(fmtAttr->Value(), format)) { StreamConfiguration cfg = { .id = id, .width = std::stoi(widthAttr->Value()), .height = std::stoi(heightAttr->Value()), .format = format, .type = ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT, }; 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")) { LOG(ERROR) << "A configuration file is not in the required format. " << "See /etc/automotive/evs/evs_configuration.dtd"; return false; } std::unique_lock 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 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(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(p)); p += sizeof(std::string); /* size of camera_metadata_t */ const size_t num_entry = *(reinterpret_cast(p)); p += sizeof(size_t); const size_t num_data = *(reinterpret_cast(p)); p += sizeof(size_t); /* create CameraInfo and add it to hash map */ std::unique_ptr 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(p)); p += sizeof(size_t); CameraCtrl* ptr = reinterpret_cast(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(ptr); /* stream configurations */ sz = *(reinterpret_cast(p)); p += sizeof(size_t); int32_t* i32_ptr = reinterpret_cast(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(i32_ptr); /* synchronization */ aCamera->synchronized = *(reinterpret_cast(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(p); p += sizeof(camera_metadata_tag_t); const size_t count = *reinterpret_cast(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(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(p)); p += sizeof(std::string); /* size of camera_metadata_t */ const size_t num_entry = *(reinterpret_cast(p)); p += sizeof(size_t); const size_t num_data = *(reinterpret_cast(p)); p += sizeof(size_t); /* create CameraInfo and add it to hash map */ std::unique_ptr 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(p)); p += sizeof(size_t); CameraCtrl* ptr = reinterpret_cast(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(ptr); /* stream configurations */ sz = *(reinterpret_cast(p)); p += sizeof(size_t); int32_t* i32_ptr = reinterpret_cast(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(i32_ptr); for (size_t idx = 0; idx < num_entry; ++idx) { /* Read camera metadata entries */ camera_metadata_tag_t tag = *reinterpret_cast(p); p += sizeof(camera_metadata_tag_t); const size_t count = *reinterpret_cast(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 lock(mConfigLock); /* write camera group information */ size_t sz = mCameraGroups.size(); outFile.write(reinterpret_cast(&sz), sizeof(size_t)); for (auto&& [camId, camInfo] : mCameraGroups) { LOG(INFO) << "Storing camera group " << camId; /* write a camera identifier string */ outFile.write(reinterpret_cast(&camId), sizeof(std::string)); /* controls */ sz = camInfo->controls.size(); outFile.write(reinterpret_cast(&sz), sizeof(size_t)); for (auto&& [ctrl, range] : camInfo->controls) { outFile.write(reinterpret_cast(&ctrl), sizeof(CameraParam)); outFile.write(reinterpret_cast(&std::get<0>(range)), sizeof(int32_t)); outFile.write(reinterpret_cast(&std::get<1>(range)), sizeof(int32_t)); outFile.write(reinterpret_cast(&std::get<2>(range)), sizeof(int32_t)); } /* stream configurations */ sz = camInfo->streamConfigurations.size(); outFile.write(reinterpret_cast(&sz), sizeof(size_t)); for (auto&& [sid, cfg] : camInfo->streamConfigurations) { outFile.write(reinterpret_cast(sid), sizeof(int32_t)); outFile.write(reinterpret_cast(&cfg), sizeof(cfg)); } /* synchronization */ outFile.write(reinterpret_cast(&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(&num_entry), sizeof(size_t)); outFile.write(reinterpret_cast(&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(&entry.tag), sizeof(entry.tag)); outFile.write(reinterpret_cast(&entry.count), sizeof(entry.count)); int32_t type = get_camera_metadata_tag_type(entry.tag); switch (type) { case TYPE_BYTE: outFile.write(reinterpret_cast(entry.data.u8), sizeof(uint8_t) * entry.count); break; case TYPE_INT32: outFile.write(reinterpret_cast(entry.data.i32), sizeof(int32_t) * entry.count); break; case TYPE_FLOAT: outFile.write(reinterpret_cast(entry.data.f), sizeof(float) * entry.count); break; case TYPE_INT64: outFile.write(reinterpret_cast(entry.data.i64), sizeof(int64_t) * entry.count); break; case TYPE_DOUBLE: outFile.write(reinterpret_cast(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(&sz), sizeof(size_t)); for (auto&& [camId, camInfo] : mCameraInfo) { LOG(INFO) << "Storing camera " << camId; /* write a camera identifier string */ outFile.write(reinterpret_cast(&camId), sizeof(std::string)); /* controls */ sz = camInfo->controls.size(); outFile.write(reinterpret_cast(&sz), sizeof(size_t)); for (auto& [ctrl, range] : camInfo->controls) { outFile.write(reinterpret_cast(&ctrl), sizeof(CameraParam)); outFile.write(reinterpret_cast(&std::get<0>(range)), sizeof(int32_t)); outFile.write(reinterpret_cast(&std::get<1>(range)), sizeof(int32_t)); outFile.write(reinterpret_cast(&std::get<2>(range)), sizeof(int32_t)); } /* stream configurations */ sz = camInfo->streamConfigurations.size(); outFile.write(reinterpret_cast(&sz), sizeof(size_t)); for (auto&& [sid, cfg] : camInfo->streamConfigurations) { outFile.write(reinterpret_cast(sid), sizeof(int32_t)); outFile.write(reinterpret_cast(&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(&num_entry), sizeof(size_t)); outFile.write(reinterpret_cast(&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(&entry.tag), sizeof(entry.tag)); outFile.write(reinterpret_cast(&entry.count), sizeof(entry.count)); int32_t type = get_camera_metadata_tag_type(entry.tag); switch (type) { case TYPE_BYTE: outFile.write(reinterpret_cast(entry.data.u8), sizeof(uint8_t) * entry.count); break; case TYPE_INT32: outFile.write(reinterpret_cast(entry.data.i32), sizeof(int32_t) * entry.count); break; case TYPE_FLOAT: outFile.write(reinterpret_cast(entry.data.f), sizeof(float) * entry.count); break; case TYPE_INT64: outFile.write(reinterpret_cast(entry.data.i64), sizeof(int64_t) * entry.count); break; case TYPE_DOUBLE: outFile.write(reinterpret_cast(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::Create() { std::unique_ptr 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(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(val.first); break; } default: LOG(WARNING) << "Tag " << std::hex << tag << " is not supported. " << "Data may be corrupted?"; break; } } }