blob: 72086f4d36f19ff945b40ff24a1bf9c816f240bc [file] [log] [blame]
/*
* Copyright (C) 2012-2017 Intel Corporation
*
* 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.
*/
#define LOG_TAG "CpfConf"
#include "CameraConf.h"
#include "LogHelper.h"
#include "PlatformData.h"
#include <ctype.h> // tolower()
#include <dirent.h> // DIR, dirent
#include <fnmatch.h> // fnmatch()
#include <fcntl.h> // open(), close()
#include <math.h> // pow, log10
#include <algorithm> // sort
#include <linux/media.h> // media controller
#include <linux/kdev_t.h> // MAJOR(), MINOR()
#include <utils/Errors.h> // Error codes
#include <sys/stat.h>
#include <string>
#include <sstream>
#include "ia_exc.h"
#include "CameraMetadataHelper.h"
#include "Intel3aPlus.h"
#include "Intel3aExc.h"
namespace cros {
namespace intel {
using std::string;
static const int TRANSFORM_MATRIX_SIZE = 9;
static const float EPSILON = 0.00001;
static const int FORWARD_MATRIX_PRECISION = 65536;
/**
* Location of CPF files (actually they are .aiqb)
*/
const char *cpfConfigPath = "/etc/camera/ipu3/";
// FIXME: The spec for following is "dr%02d[0-9][0-9]??????????????.aiqb"
const char *cpfConfigPatternSuffix = "*.aiqb"; // How CPF file name should look
const int FRAME_USE_MODE_NUMBER = 3;
const char* frameUseModeList[FRAME_USE_MODE_NUMBER] = {CPF_MODE_STILL,
CPF_MODE_VIDEO,
CPF_MODE_PREVIEW};
AiqConf::AiqConf(const int cameraId, const int size):
mPtr(nullptr),
mSize(size),
mCmc(cameraId),
mMetadata(nullptr),
mCameraId(cameraId)
{
UNUSED(mCameraId);
if (size)
mPtr = ::operator new(size);
}
AiqConf::~AiqConf()
{
mCmc.deinit();
if (mMetadata)
mMetadata = nullptr;
::operator delete(mPtr);
mPtr = nullptr;
}
status_t AiqConf::initCMC()
{
if (mCmc.getCmc() != nullptr
|| ptr() == nullptr) {
LOGE("@%s, Error Initializing CMC", __FUNCTION__);
return NO_INIT;
}
ia_binary_data cpfData;
CLEAR(cpfData);
cpfData.data = ptr();
cpfData.size = size();
bool ret = mCmc.init((ia_binary_data*)&(cpfData));
CheckError(ret == false, NO_INIT, "@%s, mCmc->init fails", __FUNCTION__);
ia_cmc_t* cmc = mCmc.getCmc();
cmc_lens_shading_t* cmc_lens_shading = cmc->cmc_parsed_lens_shading.cmc_lens_shading;
if (cmc_lens_shading) {
LOG1("@%s, grid_width:%d, grid_height:%d",
__FUNCTION__, cmc_lens_shading->grid_width, cmc_lens_shading->grid_height);
}
return NO_ERROR;
}
status_t AiqConf::initMapping(LightSrcMap &lightSourceMap)
{
// init light source mapping
lightSourceMap.clear();
lightSourceMap.insert(std::make_pair(cmc_light_source_d65,
ANDROID_SENSOR_REFERENCE_ILLUMINANT1_DAYLIGHT));
lightSourceMap.insert(std::make_pair(cmc_light_source_f11,
ANDROID_SENSOR_REFERENCE_ILLUMINANT1_FLUORESCENT));
lightSourceMap.insert(std::make_pair(cmc_light_source_a,
ANDROID_SENSOR_REFERENCE_ILLUMINANT1_TUNGSTEN));
lightSourceMap.insert(std::make_pair(cmc_light_source_d65,
ANDROID_SENSOR_REFERENCE_ILLUMINANT1_FLASH));
lightSourceMap.insert(std::make_pair(cmc_light_source_d65,
ANDROID_SENSOR_REFERENCE_ILLUMINANT1_FINE_WEATHER));
lightSourceMap.insert(std::make_pair(cmc_light_source_d75,
ANDROID_SENSOR_REFERENCE_ILLUMINANT1_CLOUDY_WEATHER));
lightSourceMap.insert(std::make_pair(cmc_light_source_d75,
ANDROID_SENSOR_REFERENCE_ILLUMINANT1_SHADE));
lightSourceMap.insert(std::make_pair(cmc_light_source_f1,
ANDROID_SENSOR_REFERENCE_ILLUMINANT1_DAYLIGHT_FLUORESCENT));
lightSourceMap.insert(std::make_pair(cmc_light_source_f3,
ANDROID_SENSOR_REFERENCE_ILLUMINANT1_DAY_WHITE_FLUORESCENT));
lightSourceMap.insert(std::make_pair(cmc_light_source_f2,
ANDROID_SENSOR_REFERENCE_ILLUMINANT1_COOL_WHITE_FLUORESCENT));
lightSourceMap.insert(std::make_pair(cmc_light_source_a,
ANDROID_SENSOR_REFERENCE_ILLUMINANT1_WHITE_FLUORESCENT));
lightSourceMap.insert(std::make_pair(cmc_light_source_a,
ANDROID_SENSOR_REFERENCE_ILLUMINANT1_STANDARD_A));
lightSourceMap.insert(std::make_pair(cmc_light_source_b,
ANDROID_SENSOR_REFERENCE_ILLUMINANT1_STANDARD_B));
lightSourceMap.insert(std::make_pair(cmc_light_source_c,
ANDROID_SENSOR_REFERENCE_ILLUMINANT1_STANDARD_C));
lightSourceMap.insert(std::make_pair(cmc_light_source_d55,
ANDROID_SENSOR_REFERENCE_ILLUMINANT1_D55));
lightSourceMap.insert(std::make_pair(cmc_light_source_d65,
ANDROID_SENSOR_REFERENCE_ILLUMINANT1_D65));
lightSourceMap.insert(std::make_pair(cmc_light_source_d75,
ANDROID_SENSOR_REFERENCE_ILLUMINANT1_D75));
lightSourceMap.insert(std::make_pair(cmc_light_source_d50,
ANDROID_SENSOR_REFERENCE_ILLUMINANT1_D50));
lightSourceMap.insert(std::make_pair(cmc_light_source_a,
ANDROID_SENSOR_REFERENCE_ILLUMINANT1_ISO_STUDIO_TUNGSTEN));
return NO_ERROR;
}
status_t AiqConf::fillStaticMetadataFromCMC(camera_metadata_t * metadata)
{
status_t res = OK;
if (mCmc.getCmc() == nullptr) {
res = initCMC();
if (res != NO_ERROR)
return res;
}
// Check if the metadata has been filled
if (mMetadata == metadata) {
return res;
} else {
mMetadata = metadata;
}
// Lens
res |= fillLensStaticMetadata(metadata);
res |= fillSensorStaticMetadata(metadata);
res |= fillLscSizeStaticMetadata(metadata);
return res;
}
status_t AiqConf::fillLensStaticMetadata(camera_metadata_t * metadata)
{
status_t res = OK;
ia_cmc_t* cmc = mCmc.getCmc();
CheckError(cmc == nullptr, UNKNOWN_ERROR, "@%s, mCmc.getCmc fails", __FUNCTION__);
if (cmc->cmc_parsed_optics.cmc_optomechanics != nullptr) {
uint16_t camera_features = cmc->cmc_parsed_optics.cmc_optomechanics->camera_actuator_features;
// Lens: aperture
float fn = 2.53;
if (!(camera_features & cmc_camera_feature_variable_apertures)
&& cmc->cmc_parsed_optics.cmc_optomechanics->num_apertures == 1
&& cmc->cmc_parsed_optics.lut_apertures != nullptr) {
// fixed aperture, the fn should be divided 100 because the value is multiplied 100 in cmc
fn = (float)cmc->cmc_parsed_optics.lut_apertures[0] / 100;
res |= MetadataHelper::updateMetadata(metadata, ANDROID_LENS_INFO_AVAILABLE_APERTURES, &fn, 1);
LOG2("static ANDROID_LENS_INFO_AVAILABLE_APERTURES :%.2f", fn);
}
// Lens: FilterDensities
int32_t nd_gain = 0;
if (camera_features & cmc_camera_feature_nd_filter)
nd_gain = cmc->cmc_parsed_optics.cmc_optomechanics->nd_gain;
res |= MetadataHelper::updateMetadata(metadata, ANDROID_LENS_INFO_AVAILABLE_FILTER_DENSITIES, (float*)&nd_gain, 1);
LOG2("static ANDROID_LENS_INFO_AVAILABLE_FILTER_DENSITIES :%d", nd_gain);
/**
* check the type of focus actuator
* 0 means no actuator, in this case we signal that this is a fixed
* focus sensor by setting min_focus_distance to 0.0
* (see documentation for this tag)
*
*/
float min_focus_distance = 0.0;
if (cmc->cmc_parsed_optics.cmc_optomechanics->actuator != 0) {
// the unit from CMC is cm, convert to diopters (1/m)
if (cmc->cmc_parsed_optics.cmc_optomechanics->min_focus_distance != 0)
min_focus_distance = 100 / (float)cmc->cmc_parsed_optics.cmc_optomechanics->min_focus_distance;
}
res |= MetadataHelper::updateMetadata(metadata, ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE, &min_focus_distance, 1);
LOG2("static ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE :%.2f", min_focus_distance);
float hyperfocal_distance_diopter = 1000 / Intel3aPlus::calculateHyperfocalDistance(*cmc);
res |= MetadataHelper::updateMetadata(metadata, ANDROID_LENS_INFO_HYPERFOCAL_DISTANCE, &hyperfocal_distance_diopter, 1);
LOG2("static ANDROID_LENS_INFO_HYPERFOCAL_DISTANCE :%.2f", hyperfocal_distance_diopter);
// TODO: lens.info.availableOpticalStabilization
}
return res;
}
status_t AiqConf::fillLightSourceStaticMetadata(camera_metadata_t * metadata)
{
status_t res = UNKNOWN_ERROR;
LightSrcMap lightSourceMap;
initMapping(lightSourceMap);
ia_cmc_t* cmc = mCmc.getCmc();
CheckError(cmc == nullptr, UNKNOWN_ERROR, "@%s, mCmc.getCmc fails", __FUNCTION__);
// color matrices
if (cmc->cmc_parsed_color_matrices.cmc_color_matrix != nullptr && cmc->cmc_parsed_color_matrices.cmc_color_matrices != nullptr) {
int num_matrices = cmc->cmc_parsed_color_matrices.cmc_color_matrices->num_matrices;
// android metadata requests 2 light source
if (num_matrices < 2)
return res;
res = OK;
// calibrationTransform1
// the below matrix should be sample specific transformation to golden module
// in the future the matrix should be calculated by the data from NVM
camera_metadata_rational_t calibrationTransform[TRANSFORM_MATRIX_SIZE] = {{1,1}, {0,1}, {0,1}, {0,1}, {1,1}, {0,1},
{0,1}, {0,1}, {1,1}};
int sensor_calibration_transform_tag[2] = {ANDROID_SENSOR_CALIBRATION_TRANSFORM1, ANDROID_SENSOR_CALIBRATION_TRANSFORM2};
int sensor_reference_illuminant_tag[2] = {ANDROID_SENSOR_REFERENCE_ILLUMINANT1, ANDROID_SENSOR_REFERENCE_ILLUMINANT2};
int sensor_color_transform_tag[2] = {ANDROID_SENSOR_COLOR_TRANSFORM1, ANDROID_SENSOR_COLOR_TRANSFORM2};
int sensor_forward_matrix_tag[2] = {ANDROID_SENSOR_FORWARD_MATRIX1, ANDROID_SENSOR_FORWARD_MATRIX2};
int16_t reference_illuminant[2];
CLEAR(reference_illuminant);
float XYZ_to_sRGB[TRANSFORM_MATRIX_SIZE] = {3.2404542, -1.5371385, -0.4985314,
-0.9692660, 1.8760108, 0.0415560,
0.0556434, -0.2040259, 1.0572252};
float XYZ_to_sRGB_det = XYZ_to_sRGB[0] * XYZ_to_sRGB[4] * XYZ_to_sRGB[8] +
XYZ_to_sRGB[1] * XYZ_to_sRGB[5] * XYZ_to_sRGB[6] +
XYZ_to_sRGB[2] * XYZ_to_sRGB[3] * XYZ_to_sRGB[7] -
XYZ_to_sRGB[2] * XYZ_to_sRGB[4] * XYZ_to_sRGB[6] -
XYZ_to_sRGB[0] * XYZ_to_sRGB[5] * XYZ_to_sRGB[7] -
XYZ_to_sRGB[1] * XYZ_to_sRGB[3] * XYZ_to_sRGB[8];
float XYZ_to_sRGB_adj[TRANSFORM_MATRIX_SIZE];
//adjugate matrix
XYZ_to_sRGB_adj[0] = XYZ_to_sRGB[4] * XYZ_to_sRGB[8] - XYZ_to_sRGB[5] * XYZ_to_sRGB[7];
XYZ_to_sRGB_adj[1] = XYZ_to_sRGB[2] * XYZ_to_sRGB[7] - XYZ_to_sRGB[1] * XYZ_to_sRGB[8];
XYZ_to_sRGB_adj[2] = XYZ_to_sRGB[1] * XYZ_to_sRGB[5] - XYZ_to_sRGB[2] * XYZ_to_sRGB[4];
XYZ_to_sRGB_adj[3] = XYZ_to_sRGB[5] * XYZ_to_sRGB[6] - XYZ_to_sRGB[3] * XYZ_to_sRGB[8];
XYZ_to_sRGB_adj[4] = XYZ_to_sRGB[0] * XYZ_to_sRGB[8] - XYZ_to_sRGB[2] * XYZ_to_sRGB[6];
XYZ_to_sRGB_adj[5] = XYZ_to_sRGB[2] * XYZ_to_sRGB[3] - XYZ_to_sRGB[0] * XYZ_to_sRGB[5];
XYZ_to_sRGB_adj[6] = XYZ_to_sRGB[3] * XYZ_to_sRGB[7] - XYZ_to_sRGB[4] * XYZ_to_sRGB[6];
XYZ_to_sRGB_adj[7] = XYZ_to_sRGB[1] * XYZ_to_sRGB[6] - XYZ_to_sRGB[0] * XYZ_to_sRGB[7];
XYZ_to_sRGB_adj[8] = XYZ_to_sRGB[0] * XYZ_to_sRGB[4] - XYZ_to_sRGB[1] * XYZ_to_sRGB[3];
// inverse matrix
if (!(XYZ_to_sRGB_det >= -EPSILON && XYZ_to_sRGB_det <= EPSILON)) {
// need to iterate loop down, otherwise gtest produces a segfault
// todo: figure out why iterating from 0 to 8 causes that segfault
for (int i = TRANSFORM_MATRIX_SIZE - 1; i >= 0; --i)
XYZ_to_sRGB_adj[i] = XYZ_to_sRGB_adj[i] / XYZ_to_sRGB_det;
}
camera_metadata_rational_t forward_matrix[TRANSFORM_MATRIX_SIZE];
CLEAR(forward_matrix);
for(int light_src_num = 0; light_src_num < 2; light_src_num++) {
cmc_light_source light_src = (cmc_light_source)cmc->cmc_parsed_color_matrices.cmc_color_matrix[light_src_num].light_src_type;
LightSrcMap::iterator it = lightSourceMap.find(light_src);
if (it == lightSourceMap.end()) {
LOG2("light source not found, use default!!");
reference_illuminant[light_src_num] = ANDROID_SENSOR_REFERENCE_ILLUMINANT1_DAYLIGHT;
} else {
reference_illuminant[light_src_num] = it->second;
}
// referenceIlluminant
res |= MetadataHelper::updateMetadata(metadata, sensor_reference_illuminant_tag[light_src_num], &reference_illuminant[light_src_num], 1);
// calibrationTransform
res |= MetadataHelper::updateMetadata(metadata, sensor_calibration_transform_tag[light_src_num], &calibrationTransform, TRANSFORM_MATRIX_SIZE);
// colorTransform
int32_t *matrix_accurate = cmc->cmc_parsed_color_matrices.cmc_color_matrix[light_src_num].matrix_accurate;
// match tag type: rational
camera_metadata_rational_t matrix_accurate_rational[TRANSFORM_MATRIX_SIZE];
for (size_t i = 0; i < TRANSFORM_MATRIX_SIZE; i++) {
matrix_accurate_rational[i] = {matrix_accurate[i], 1};
}
res |= MetadataHelper::updateMetadata(metadata, sensor_color_transform_tag[light_src_num], &matrix_accurate_rational, TRANSFORM_MATRIX_SIZE);
LOG2("matrix_accurate:%d,%d,%d,%d,%d,%d,%d,%d,%d",matrix_accurate[0],
matrix_accurate[1],matrix_accurate[2],matrix_accurate[3],
matrix_accurate[4],matrix_accurate[5],matrix_accurate[6],
matrix_accurate[7],matrix_accurate[8]);
// forwardMatrix
if (!(XYZ_to_sRGB_det >= -EPSILON && XYZ_to_sRGB_det <= EPSILON)) {
int row = 0;
int col = 0;
for (int i = 0; i < TRANSFORM_MATRIX_SIZE; i++) {
row = i / 3;
col = i % 3;
forward_matrix[i].numerator = 0;
// CMC matrix is in Q16 format, so denominator is 65536
forward_matrix[i].denominator = FORWARD_MATRIX_PRECISION;
// matrix multiply
for (int j = 0; j < 3; j++)
forward_matrix[i].numerator = forward_matrix[i].numerator + XYZ_to_sRGB_adj[row * 3 + j] * matrix_accurate[j * 3 + col];
LOG2("forward matrix [%d] = {%d, %d}", i, forward_matrix[i].numerator, forward_matrix[i].denominator);
}
res |= MetadataHelper::updateMetadata(metadata, sensor_forward_matrix_tag[light_src_num], forward_matrix, TRANSFORM_MATRIX_SIZE);
}
}
}
return res;
}
status_t AiqConf::fillSensorStaticMetadata(camera_metadata_t * metadata)
{
LOG1("%s", __FUNCTION__);
status_t res = OK;
ia_cmc_t* cmc = mCmc.getCmc();
CheckError(cmc == nullptr, UNKNOWN_ERROR, "@%s, mCmc.getCmc fails", __FUNCTION__);
// colorFilterArrangement
if (cmc->cmc_general_data != nullptr) {
uint16_t color_order = 0;
switch (cmc->cmc_general_data->color_order) {
case cmc_bayer_order_grbg:
color_order = ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG;
break;
case cmc_bayer_order_rggb:
color_order = ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB;
break;
case cmc_bayer_order_bggr:
color_order = ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR;
break;
case cmc_bayer_order_gbrg:
color_order = ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG;
break;
default:
LOGE("Unsupported color_order inside cmc general data: %d", color_order);
break;
}
res |= MetadataHelper::updateMetadata(metadata, ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT, &color_order, 1);
LOG2("color order:%d", color_order);
}
// whiteLevel
if (cmc->cmc_saturation_level != nullptr) {
int32_t saturation_level = cmc->cmc_saturation_level->saturation_cc1;
res |= MetadataHelper::updateMetadata(metadata, ANDROID_SENSOR_INFO_WHITE_LEVEL, &saturation_level, 1);
LOG2("saturation_level:%d", saturation_level);
}
int16_t base_iso = 0;
// baseGainFactor
if (cmc->cmc_sensitivity != nullptr) {
// baseGainFactor's definition is gain factor from electrons to raw units when ISO=100
camera_metadata_rational_t baseGainFactor;
baseGainFactor.numerator = 100;
baseGainFactor.denominator = cmc->cmc_sensitivity->base_iso;
base_iso = cmc->cmc_sensitivity->base_iso;
res |= MetadataHelper::updateMetadata(metadata, ANDROID_SENSOR_BASE_GAIN_FACTOR, &baseGainFactor, 1);
LOG2("base_iso:%d", base_iso);
}
// blackLevelPattern
if (cmc->cmc_parsed_black_level.cmc_black_level_luts != nullptr) {
uint16_t cc1 = cmc->cmc_parsed_black_level.cmc_black_level_luts->color_channels.cc1 / 256;
uint16_t cc2 = cmc->cmc_parsed_black_level.cmc_black_level_luts->color_channels.cc2 / 256;
uint16_t cc3 = cmc->cmc_parsed_black_level.cmc_black_level_luts->color_channels.cc3 / 256;
uint16_t cc4 = cmc->cmc_parsed_black_level.cmc_black_level_luts->color_channels.cc4 / 256;
int32_t black_level_pattern[4];
black_level_pattern[0] = cc1;
black_level_pattern[1] = cc2;
black_level_pattern[2] = cc3;
black_level_pattern[3] = cc4;
res |= MetadataHelper::updateMetadata(metadata, ANDROID_SENSOR_BLACK_LEVEL_PATTERN, &black_level_pattern, 4);
LOG2("blackLevelPattern, cc1:%d,cc2:%d,cc3:%d,cc4:%d", cc1, cc2, cc3, cc4);
}
// fill metadata: referenceIlluminant, sensor.colorTransform,
// sensor.forwardMatrix, sensor.calibrationTransform
fillLightSourceStaticMetadata(metadata);
// maxAnalogSensitivity
if (cmc->cmc_parsed_analog_gain_conversion.cmc_analog_gain_conversion != nullptr) {
Intel3aExc exc;
int32_t max_analog_sensitivity = 0;
float max_analog_gain = 0.0;
unsigned short analog_gain_code = 0;
// we can give a large value (e.g. 1000) as input to ia_exc.
// Output from ia_exc is a clipped sensor specific MAX.
exc.AnalogGainToSensorUnits(&(cmc->cmc_parsed_analog_gain_conversion), 1000, &analog_gain_code);
exc.SensorUnitsToAnalogGain(&(cmc->cmc_parsed_analog_gain_conversion),
analog_gain_code,
&max_analog_gain);
// caclulate the iso base on max analog gain
max_analog_sensitivity = max_analog_gain * base_iso;
res |= MetadataHelper::updateMetadata(metadata, ANDROID_SENSOR_MAX_ANALOG_SENSITIVITY, &max_analog_sensitivity, 1);
}
// noiseProfile, this should be got from CMC in the future, currently use default value first
return res;
}
/*
Android requires lensShadingMapSize must be smaller than 64*64,
and it is static, however, in some case, such as video recording,
the width and height got from pa_results->weighted_lsc->cmc_lens_shading->grid_width
and grid_height are variable for different resolution.
here we are calculating the size of the downscaled LSC table we will calculate for them.
Preserve the same Aspect Ratio.
*/
status_t AiqConf::fillLscSizeStaticMetadata(camera_metadata_t * metadata)
{
status_t res = OK;
ia_cmc_t* cmc = mCmc.getCmc();
CheckError(cmc == nullptr, UNKNOWN_ERROR, "@%s, mCmc.getCmc fails", __FUNCTION__);
if (cmc->cmc_parsed_lens_shading.cmc_lens_shading != nullptr) {
int32_t lensShadingMapSize[2];
lensShadingMapSize[0] = cmc->cmc_parsed_lens_shading.cmc_lens_shading->grid_width;
lensShadingMapSize[1] = cmc->cmc_parsed_lens_shading.cmc_lens_shading->grid_height;
int i = 1;
uint16_t destWidth = lensShadingMapSize[0];
uint16_t destHeight = lensShadingMapSize[1];
while (destWidth > MAX_LSC_GRID_WIDTH || destHeight > MAX_LSC_GRID_HEIGHT) {
i++;
destWidth = lensShadingMapSize[0] / i;
destHeight = lensShadingMapSize[1] / i;
}
lensShadingMapSize[0] = destWidth;
lensShadingMapSize[1] = destHeight;
LOG2("grid_width:%d, grid_height:%d", lensShadingMapSize[0], lensShadingMapSize[1]);
res = MetadataHelper::updateMetadata(metadata, ANDROID_LENS_INFO_SHADING_MAP_SIZE, &lensShadingMapSize, 2);
}
return res;
}
CpfStore::CpfStore(const int xmlCameraId, CameraHWInfo * cameraHWInfo)
: mCameraId(xmlCameraId)
, mHasMediaController(false)
, mIsOldConfig(false)
{
LOG1("@%s, mCameraId:%d", __FUNCTION__, mCameraId);
mAiqConf.clear();
// If anything goes wrong here, we simply return silently.
// CPF should merely be seen as a way to do multiple configurations
// at once; failing in that is not a reason to return with errors
// and terminate the camera (some cameras may not have any CPF at all)
/**
* There is no CPF file for SoC sensors.
* avoid getting any error messages when looking for it
*/
const CameraCapInfo *capInfo = PlatformData::getCameraCapInfo(mCameraId);
if (capInfo == nullptr) {
LOGE("Cannot find xml camera id: %d", mCameraId);
return;
}
if (capInfo->sensorType() == SENSOR_TYPE_SOC)
return;
mRegisteredDrivers = cameraHWInfo->mSensorInfo;
mHasMediaController = cameraHWInfo->mHasMediaController;
// Find out the related file names
if (initFileNames(mCpfFileNames)) {
// Error message given already
return;
}
string mode;
string fileName;
mAiqConfig.clear();
for (size_t i = 0; i < mCpfFileNames.size(); i++) {
fileName = mCpfFileNames[i];
//extract aiqb file name from the full file path
std::size_t pos = fileName.rfind('/');
if (pos != string::npos)
fileName = fileName.substr(pos + 1);
getCpfFileMode(fileName, mode);
LOG1("%s: mode %s, file name: %s", __FUNCTION__,
mode.c_str(), mCpfFileNames[i].c_str());
// Obtain the configurations
if (loadConf(mCpfFileNames[i])) {
// Error message given already
return;
}
// Provide configuration data for algorithms and image
// quality purposes, and continue further even if errors did occur.
// Pointer to that data is cleared later, whenever seen suitable,
// so that the memory reserved for CPF data can then be freed
// get the mode name and store in AIQ config vector
mAiqConfig.insert(std::make_pair(mode, mAiqConf.at(i)));
}
}
CpfStore::~CpfStore()
{
mAiqConfig.clear();
for (auto &it : mAiqConf) {
delete it;
}
mAiqConf.clear();
LOG1("@%s", __FUNCTION__);
}
status_t CpfStore::initFileNames(std::vector<string> &cpfFileNames)
{
LOG1("@%s", __FUNCTION__);
status_t ret = 0;
string cpfPathName;
struct stat statInfo;
int PathExists = stat(cpfConfigPath, &statInfo);
if (PathExists == 0) {
cpfPathName = cpfConfigPath;
} else {
LOGE("Failed to find path for AIQB files!! - BUG");
return UNKNOWN_ERROR;
}
// Secondly, we will find all the matching configuration files
if ((ret = findConfigFile(cpfPathName, cpfFileNames))) {
// Error message given already
return ret;
}
LOG1("cpf config file name: %s", cpfPathName.c_str());
return ret;
}
/**
* Search the path were CPF files are stored
* Find a CPF file that follows the following pattern:
* <cameraID><sensorname>[.<frame_use>][.<device_id>].aiqb
* where
* - cameraID is the android camera id, traditionally 00 for back camera
* and 01 for front camera
* - sensor name is the sensor name provided by the driver
* - frame use is an optional part which presents the aiq frame use mode,
* like preview, still, video.
* The frame use is used in the event that different tunings are required
* for video/still use cases for3A+ algorithms.
* - device_id is an optional part that can be one of the following values
* -- spid based string (what it has been used until now)
* The device id is used in the event that the same sensor is used by multiple
* product devices. The reason why there are several properties is that not all
* platforms support SPID so we need to have a back up.
*
* In the event that multiple files in the CPF directory match the first 2
* parts of the pattern then the logic in this method will select the first file
* that matches any of the device_ids in the following order:
* - spid
* From the most detailed to the more generic
*
* ex: back sensor 0v8858, used in multiple devices: moorefield (with SPID)
* and sofia3g product (MRD5)
* the files that we get after the first filter could be:
* 00ov8858.aiqb
* 00ov8858-0x10x20x3.aiqb
* 00ov8858-sf3g_mrd5_p1.aiqb
*
* The device_ids return from platform data will be
* in Moorefield:
* 0x10x20x3
* mofd_v1
* moorefield
* moorefield
* in Sofia MRD5
* sf3g_mrd5_p1
* sofia3g
* sofia3g
* by selecting the file that matches the in order the device id we can
* select a very specific one or fallback to platform default.
* in case no match is done we can always select one and warn about it
*
* \param[in] path String with the path where the cpf files are stored
* \param[out] cpfFileNames A vector of strings with the valid file names
*
* \return OK if the file name was found
* \return NO_INIT if no valid configuration file was found
*/
status_t CpfStore::findConfigFile(const string &path,
std::vector<string> &cpfFileNames)
{
status_t ret = OK;
// Let's filter first the files that look like CPF Files
// the name should follow the pattern: "%02d*.aiqb"
// ex: 00xxxxx.aiqb if mCameraId is 00
std::vector<string> allCpfFileNames;
ret = getCpfFileList(path, allCpfFileNames);
if (ret != OK) {
LOGE("ERROR Finding CPF Files!");
return ret;
}
/**
* Now we filter the cpf files that match in the name the name of one of
* sensor drivers we have discovered in the first phase. Those names are
* stored in mRegisteredDrivers
*/
std::vector<string> registeredCpfFiles;
filterKnownSensors(allCpfFileNames, registeredCpfFiles);
if (registeredCpfFiles.empty()) {
LOGW("No valid CPF file (is ok for SoC sensors)");
return NO_INIT;
}
std::vector<string> deviceIds;
ret = PlatformData::getDeviceIds(deviceIds);
bool found = false;
// Filter all the cpf files for a device id.
if (registeredCpfFiles.size() == 1) {
cpfFileNames.push_back(path + registeredCpfFiles[0]);
found = true;
} else {
for (size_t i = 0; i < deviceIds.size(); i++) {
// iterate through all the device ids in order try to find a match
// in the cpf files
for (size_t j = 0; j < registeredCpfFiles.size(); j++) {
if (registeredCpfFiles[j].find(deviceIds[i]) != string::npos) {
cpfFileNames.push_back(path + registeredCpfFiles[j]);
found = true;
}
}
if (found)
break;
}
}
if (!found) {
LOGW("Could not find a good fit for CPF file, using default %s",
registeredCpfFiles[0].c_str());
cpfFileNames.push_back(path + registeredCpfFiles[0]);
}
return OK;
}
/**
* compareString
*
* Take 2 string as input, and compare them, used to sort the cpf file,
* called by getCpfFileList().
* \param[in] 2 string lhs and rhs
* \return true : if lhs is less than rhs
* \return false : if lhs is greater than or equal to rhs
*/
static bool compareString(const string &lhs, const string &rhs)
{
return lhs < rhs;
}
/**
* getCpfFileList
*
* Return the list of CPF files found in the cpfConfigPath that matches the
* pattern of a CPF file, this is:
* "%02d*.aiqb"
*
* \param[in] path String with the path where the cpf files are stored
* \param[out] cpfFileNames List of file names
* \return OK: No error looking for files
* \return FAILED_TRANSACTION: failed to read the contents of the directory
* \return ENOTDIR if the path where the CPF files should be is not available
*/
status_t CpfStore::getCpfFileList(const string &path,
std::vector<string> &cpfFileNames)
{
status_t ret = 0;
DIR *dir = opendir(path.c_str());
if (!dir) {
LOGE("ERROR in opening CPF folder \"%s\": %s!",
cpfConfigPath, strerror(errno));
return ENOTDIR;
}
do {
dirent *entry;
if (errno = 0, !(entry = readdir(dir))) {
if (errno) {
LOGE("ERROR in browsing CPF folder \"%s\": %s!",
cpfConfigPath, strerror(errno));
ret = FAILED_TRANSACTION;
}
break;
}
if ((strcmp(entry->d_name, ".") == 0) ||
(strcmp(entry->d_name, "..") == 0)) {
continue; // Skip self and parent
}
// Check that the file name follows the pattern
std::ostringstream stringStream;
stringStream << "0" << mCameraId << cpfConfigPatternSuffix;
std::string pattern = stringStream.str();
int r = fnmatch(pattern.c_str(), entry->d_name, 0);
switch (r) {
case 0:
// The file name looks like a valid CPF file name
cpfFileNames.push_back(string(entry->d_name));
continue;
case FNM_NOMATCH:
// The file name did not look like a CPF file name
continue;
default:
// Unknown error (the looping ends at 'while')
LOGE("ERROR in pattern matching file name \"%s\"!", entry->d_name);
ret = UNKNOWN_ERROR;
continue;
}
} while (!ret);
// sort filenames
std::sort(cpfFileNames.begin(), cpfFileNames.end(), compareString);
if (closedir(dir)) {
LOGE("ERROR in closing CPF folder \"%s\": %s!",
cpfConfigPath, strerror(errno));
if (!ret) ret = EPERM;
}
return ret;
}
/**
* filterKnownSensors
*
* Goes through the list of cpf files found in the directory and removes the
* ones that do not match any of the names registered to the driver
* The names registered to the driver are stored in mRegisteredDrivers
*
* \param[in] allCpfFileNames List of all CPF files found in file system
* \param[out] registeredCpfFileNames List of CPF files to filter
*/
void CpfStore::filterKnownSensors(std::vector<string> &allCpfFileNames,
std::vector<string> &registeredCpfFileNames)
{
registeredCpfFileNames.clear();
for (size_t i = 0; i < allCpfFileNames.size(); i++) {
for (size_t j = 0; j < mRegisteredDrivers.size(); j++) {
// return cpfFile according to sensorName in non-MediaController path
const char* sensorName = mRegisteredDrivers[j].mSensorName.c_str();
if (!mHasMediaController &&
allCpfFileNames[i].find(sensorName) != string::npos) {
registeredCpfFileNames.push_back(allCpfFileNames[i]);
} else if (mHasMediaController &&
allCpfFileNames[i].find(sensorName) != string::npos) {
// TODO remove: This else-if-branch a hack to overcome the issue
// caused by above mCameraId vs. mIspPort comparison in
// MediaController-enabled platforms.
registeredCpfFileNames.push_back(allCpfFileNames[i]);
}
}
}
}
/**
* CPF files for a given sensor will have a name with the following structure:
* <cameraId><sensor_name>[.<frame_use>][.<device_id>].aiqb
* or <cameraId><sensor_name>.<device_id>.aiqb
* ex:
* 00imx214.preview.bxt_rvp.aiqb
* 00imx214.video.bxt_rvp.aiqb
* 00imx214.still.bxt_rvp.aiqb
* or
* 00imx214.bxt_rvp.aiqb
* or
* 00imx214.still.aiqb
* 00imx214.video.aiqb
* or
* 00imx214.aiqb
*
* Parse the frame_use section as the frame use mode. Frame_use section is
* an optional part. If it is absent, the frame use mode will be considered
* to be "default".
*
* \param[in] cpfFileName String with the cpf file name
* \param[out] mode Frame use mode of the input cpf file.
*/
void CpfStore::getCpfFileMode(const string &cpfFileName, string &mode)
{
mode = string(CPF_MODE_DEFAULT);
for (int i = 0; i < FRAME_USE_MODE_NUMBER; i++) {
if (cpfFileName.find(frameUseModeList[i]) != string::npos) {
mode = string(frameUseModeList[i]);
break;
}
}
return;
}
status_t CpfStore::loadConf(const string &cpfFileName)
{
LOG1("@%s, cameraId:%d", __FUNCTION__, mCameraId);
FILE *file;
struct stat statCurrent;
status_t ret = 0;
LOG1("Opening CPF file \"%s\"", cpfFileName.c_str());
file = fopen(cpfFileName.c_str(), "rb");
if (!file) {
LOGE("ERROR in opening CPF file \"%s\": %s!",
cpfFileName.c_str(), strerror(errno));
return NAME_NOT_FOUND;
}
do {
int fileSize;
if ((fseek(file, 0, SEEK_END) < 0) ||
((fileSize = ftell(file)) < 0) ||
(fseek(file, 0, SEEK_SET) < 0)) {
LOGE("ERROR querying properties of CPF file \"%s\": %s!",
cpfFileName.c_str(), strerror(errno));
ret = ESPIPE;
break;
}
AiqConf *conf = new AiqConf(mCameraId, fileSize);
if (!conf->ptr() || fread(conf->ptr(), fileSize, 1, file) < 1) {
LOGE("ERROR reading CPF file \"%s\"!", cpfFileName.c_str());
ret = EIO;
delete conf;
break;
}
mAiqConf.push_back(conf);
// We use file statistics for file identification purposes.
// The access time was just changed (because of us!),
// so let's nullify the access time info
if (stat(cpfFileName.c_str(), &statCurrent) < 0) {
LOGE("ERROR querying filestat of CPF file \"%s\": %s!",
cpfFileName.c_str(), strerror(errno));
ret = FAILED_TRANSACTION;
break;
}
statCurrent.st_atime = 0;
} while (0);
if (fclose(file)) {
LOGE("ERROR in closing CPF file \"%s\": %s!",
cpfFileName.c_str(), strerror(errno));
if (!ret) ret = EPERM;
}
return ret;
}
} /* namespace intel */
} /* namespace cros */