473 lines
15 KiB
C++
473 lines
15 KiB
C++
/*
|
|
* eis_algo_service.h
|
|
*
|
|
* Copyright (c) 2021 Rockchip Electronics Co., Ltd.
|
|
*
|
|
* 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.
|
|
*
|
|
* Author: Cody Xie <cody.xie@rock-chips.com>
|
|
*/
|
|
#include "eis_algo_service.h"
|
|
|
|
#include <fstream>
|
|
#include <functional>
|
|
#include <iostream>
|
|
#include <memory>
|
|
#include <vector>
|
|
|
|
#include "Stream.h"
|
|
#include "dma_buffer.h"
|
|
#include "dvs_app.h"
|
|
#include "eis_loader.h"
|
|
#include "image_processor.h"
|
|
#include "imu_service.h"
|
|
#include "rk_aiq_mems_sensor.h"
|
|
#include "smart_buffer_priv.h"
|
|
#include "xcam_log.h"
|
|
|
|
#define __STDC_FORMAT_MACROS
|
|
#include <inttypes.h>
|
|
|
|
#define SUCCESS 0
|
|
#define ERROR 1
|
|
|
|
//#define DEBUG_FEC
|
|
|
|
using namespace XCam;
|
|
|
|
namespace RkCam {
|
|
|
|
namespace {
|
|
|
|
template <typename T>
|
|
struct Callback;
|
|
|
|
template <typename Ret, typename... Params>
|
|
struct Callback<Ret(Params...)> {
|
|
template <typename... Args>
|
|
static Ret callback(Args... args) {
|
|
return func(args...);
|
|
}
|
|
static std::function<Ret(Params...)> func;
|
|
};
|
|
|
|
template <typename Ret, typename... Params>
|
|
std::function<Ret(Params...)> Callback<Ret(Params...)>::func;
|
|
|
|
} // namespace
|
|
|
|
EisAlgoAdaptor::~EisAlgoAdaptor() {
|
|
Stop();
|
|
|
|
if (lib_ != nullptr && engine_ != nullptr) {
|
|
lib_->GetOps()->DeInit(engine_.get());
|
|
}
|
|
}
|
|
|
|
XCamReturn EisAlgoAdaptor::LoadLibrary() {
|
|
lib_ = std::make_shared<DvsLibrary>();
|
|
|
|
if (!lib_->Init()) {
|
|
return XCAM_RETURN_ERROR_FAILED;
|
|
}
|
|
|
|
if (!lib_->LoadSymbols()) {
|
|
return XCAM_RETURN_ERROR_FAILED;
|
|
}
|
|
|
|
return XCAM_RETURN_NO_ERROR;
|
|
}
|
|
|
|
XCamReturn EisAlgoAdaptor::CreateImuService(const rk_aiq_mems_sensor_intf_t* mems_sensor_intf) {
|
|
if (mems_sensor_intf == nullptr) {
|
|
return XCAM_RETURN_ERROR_PARAM;
|
|
}
|
|
|
|
auto adaptor = std::make_shared<EisImuAdaptor>(*mems_sensor_intf);
|
|
if (XCAM_RETURN_NO_ERROR == adaptor->Init(1000.0)) {
|
|
imu_ = std::unique_ptr<ImuService>(
|
|
new ImuService(std::unique_ptr<ImuTask>(new ImuTask(std::move(adaptor))), false, 1,
|
|
std::chrono::milliseconds(10)));
|
|
} else {
|
|
imu_ = nullptr;
|
|
return XCAM_RETURN_ERROR_PARAM;
|
|
}
|
|
|
|
return XCAM_RETURN_NO_ERROR;
|
|
}
|
|
|
|
XCamReturn EisAlgoAdaptor::CreateScalerService() {
|
|
std::unique_ptr<ImageProcessor> proc(new ImageProcessor());
|
|
proc->set_operator("rga");
|
|
scl_ = std::unique_ptr<ScalerService>(
|
|
new ScalerService(std::unique_ptr<ScalerTask>(new ScalerTask(std::move(proc))), true, 7));
|
|
|
|
return XCAM_RETURN_NO_ERROR;
|
|
}
|
|
|
|
XCamReturn EisAlgoAdaptor::CreateFecRemapBackend(const FecMeshConfig& config,
|
|
const isp_drv_share_mem_ops_t* mem_ops) {
|
|
XCAM_ASSERT(mem_ops != nullptr);
|
|
|
|
remap_ = std::unique_ptr<FecRemapBackend>(new FecRemapBackend(config, mem_ops));
|
|
|
|
return XCAM_RETURN_NO_ERROR;
|
|
}
|
|
|
|
int EisAlgoAdaptor::OnMeshCallback(struct dvsEngine* engine, struct meshxyFEC* mesh) {
|
|
XCAM_ASSERT(mesh != nullptr);
|
|
|
|
LOGD_AEIS("OnMeshCallback got img id %d , mesh idx %d, img idx %d", mesh->image_index,
|
|
mesh->mesh_buffer_index, mesh->image_buffer_index);
|
|
remap_->Remap(mesh);
|
|
if (lib_ != nullptr) {
|
|
auto* new_mesh = remap_->GetAvailUserBuffer();
|
|
if (new_mesh != nullptr) {
|
|
auto dvs_new_mesh = dvs_meshes_.find(new_mesh->Index);
|
|
if (dvs_new_mesh != dvs_meshes_.end()) {
|
|
LOGD_AEIS("OnMeshCallBack push back available mesh id %d",
|
|
dvs_new_mesh->second->mesh_buffer_index);
|
|
lib_->GetOps()->PutMesh(engine_.get(), dvs_new_mesh->second.get());
|
|
}
|
|
}
|
|
}
|
|
|
|
auto img = dvs_images_.find(mesh->image_index);
|
|
if (img != dvs_images_.end()) {
|
|
dvs_images_.erase(img);
|
|
} else {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
XCamReturn EisAlgoAdaptor::Config(const AlgoCtxInstanceCfg* config,
|
|
const CalibDbV2_Eis_t* calib) {
|
|
calib_ = calib;
|
|
enable_ = calib_->enable;
|
|
|
|
if (config->isp_hw_version == 1) {
|
|
valid_ = false;
|
|
LOGE_AEIS("EIS does not compatible with ISP21");
|
|
return XCAM_RETURN_BYPASS;
|
|
}
|
|
|
|
if (XCAM_RETURN_NO_ERROR != LoadLibrary()) {
|
|
LOGE_AEIS("EIS library does not exists");
|
|
valid_ = false;
|
|
return XCAM_RETURN_BYPASS;
|
|
}
|
|
|
|
valid_ = true;
|
|
|
|
return XCAM_RETURN_NO_ERROR;
|
|
}
|
|
|
|
XCamReturn EisAlgoAdaptor::Prepare(const rk_aiq_mems_sensor_intf_t* mems_sensor_intf,
|
|
const isp_drv_share_mem_ops_t* mem_ops) {
|
|
if (!calib_->enable && !enable_) {
|
|
return XCAM_RETURN_NO_ERROR;
|
|
}
|
|
|
|
if (!valid_) {
|
|
LOGE_AEIS("EIS Invalid, bypassing!");
|
|
return XCAM_RETURN_BYPASS;
|
|
}
|
|
|
|
XCamReturn ret;
|
|
|
|
if (calib_->mode == EIS_MODE_IMU_ONLY || calib_->mode == EIS_MODE_IMU_AND_IMG) {
|
|
ret = CreateImuService(mems_sensor_intf);
|
|
if (ret != XCAM_RETURN_NO_ERROR) {
|
|
valid_ = false;
|
|
LOGE_AEIS("EIS IMU interface invalid, bypassing!");
|
|
return XCAM_RETURN_BYPASS;
|
|
}
|
|
}
|
|
|
|
if (calib_->mode == EIS_MODE_IMG_ONLY || calib_->mode == EIS_MODE_IMU_AND_IMG) {
|
|
ret = CreateScalerService();
|
|
if (ret != XCAM_RETURN_NO_ERROR) {
|
|
valid_ = false;
|
|
if (calib_->mode == EIS_MODE_IMU_AND_IMG) {
|
|
imu_ = nullptr;
|
|
}
|
|
LOGE_AEIS("EIS scaler interface invalid, bypassing!");
|
|
return XCAM_RETURN_BYPASS;
|
|
}
|
|
}
|
|
|
|
int mesh_size;
|
|
lib_->GetOps()->GetMeshSize(calib_->src_image_height, calib_->src_image_width, &mesh_size);
|
|
FecMeshConfig FecCfg;
|
|
FecCfg.Width = calib_->src_image_width;
|
|
FecCfg.Height = calib_->src_image_height;
|
|
if (FecCfg.Width <= 1920)
|
|
FecCfg.MeshDensity = 0;
|
|
else
|
|
FecCfg.MeshDensity = 1;
|
|
FecCfg.MeshSize = mesh_size;
|
|
|
|
// TODO(Cody): FEC may support different src and dst image width/height
|
|
ret = CreateFecRemapBackend(FecCfg, mem_ops);
|
|
if (ret != XCAM_RETURN_NO_ERROR) {
|
|
valid_ = false;
|
|
LOGE_AEIS("EIS remap backend invalid, bypassing!");
|
|
return XCAM_RETURN_BYPASS;
|
|
}
|
|
|
|
// TODO(Cody): width/height may change during runtime
|
|
engine_ = std::unique_ptr<dvsEngine>(new dvsEngine());
|
|
|
|
lib_->GetOps()->Prepare(engine_.get());
|
|
|
|
initialParams init_params;
|
|
init_params.image_buffer_number = 7;
|
|
init_params.input_image_size.width = calib_->src_image_width;
|
|
init_params.input_image_size.height = calib_->src_image_height;
|
|
init_params.output_image_size.width = calib_->src_image_width;
|
|
init_params.output_image_size.height = calib_->src_image_width;
|
|
init_params.clip_ratio_x = calib_->clip_ratio_x;
|
|
init_params.clip_ratio_y = calib_->clip_ratio_y;
|
|
lib_->GetOps()->InitParams(engine_.get(), &init_params);
|
|
|
|
if (!lib_->GetOps()->InitFromXmlFile(engine_.get(), calib_->debug_xml_path)) {
|
|
valid_ = false;
|
|
LOGE_AEIS("EIS init algo from xml failed, bypassing!");
|
|
return XCAM_RETURN_BYPASS;
|
|
}
|
|
|
|
for (int i = 0; i < 7; i++) {
|
|
auto* mesh = remap_->AllocUserBuffer();
|
|
struct meshxyFEC* dvs_mesh = new meshxyFEC;
|
|
dvs_mesh->is_skip = false;
|
|
dvs_mesh->image_buffer_index = mesh->ImageBufferIndex;
|
|
dvs_mesh->image_index = mesh->FrameId;
|
|
dvs_mesh->mesh_buffer_index = mesh->Index;
|
|
dvs_mesh->mesh_size = remap_->GetConfig().MeshSize;
|
|
dvs_mesh->pMeshXF = mesh->MeshXf;
|
|
dvs_mesh->pMeshXI = mesh->MeshXi;
|
|
dvs_mesh->pMeshYF = mesh->MeshYf;
|
|
dvs_mesh->pMeshYI = mesh->MeshYi;
|
|
dvs_meshes_.emplace(dvs_mesh->mesh_buffer_index, dvs_mesh);
|
|
remap_meshes_.emplace(mesh->Index, mesh);
|
|
if (i == 6) {
|
|
dvs_mesh->image_index = mesh->FrameId = -1;
|
|
lib_->GetOps()->GetOriginalMeshXY(calib_->src_image_width, calib_->src_image_height,
|
|
calib_->clip_ratio_x, calib_->clip_ratio_y, dvs_mesh);
|
|
remap_->Remap(dvs_mesh);
|
|
default_mesh_ = mesh;
|
|
} else {
|
|
lib_->GetOps()->PutMesh(engine_.get(), dvs_mesh);
|
|
}
|
|
}
|
|
|
|
Callback<int(struct dvsEngine*, struct meshxyFEC*)>::func = std::bind(
|
|
&EisAlgoAdaptor::OnMeshCallback, this, std::placeholders::_1, std::placeholders::_2);
|
|
dvsFrameCallBackFEC mesh_cb = static_cast<dvsFrameCallBackFEC>(
|
|
Callback<int(struct dvsEngine*, struct meshxyFEC*)>::callback);
|
|
lib_->GetOps()->RegisterRemap(engine_.get(), mesh_cb);
|
|
|
|
return XCAM_RETURN_NO_ERROR;
|
|
}
|
|
|
|
void EisAlgoAdaptor::Start() {
|
|
if (started_ || !valid_) {
|
|
return;
|
|
}
|
|
|
|
if (imu_ != nullptr &&
|
|
(calib_->mode == EIS_MODE_IMU_ONLY || calib_->mode == EIS_MODE_IMU_AND_IMG)) {
|
|
imu_->start();
|
|
}
|
|
|
|
if (scl_ != nullptr &&
|
|
(calib_->mode == EIS_MODE_IMU_ONLY || calib_->mode == EIS_MODE_IMU_AND_IMG)) {
|
|
scl_->start();
|
|
}
|
|
|
|
if (lib_->GetOps()->Start(engine_.get())) {
|
|
lib_->GetOps()->DeInit(engine_.get());
|
|
goto error_out;
|
|
}
|
|
|
|
started_ = true;
|
|
return;
|
|
|
|
error_out:
|
|
if (imu_ != nullptr &&
|
|
(calib_->mode == EIS_MODE_IMU_ONLY || calib_->mode == EIS_MODE_IMU_AND_IMG)) {
|
|
imu_->stop();
|
|
imu_ = nullptr;
|
|
}
|
|
|
|
if (scl_ != nullptr &&
|
|
(calib_->mode == EIS_MODE_IMU_ONLY || calib_->mode == EIS_MODE_IMU_AND_IMG)) {
|
|
scl_->stop();
|
|
scl_ = nullptr;
|
|
}
|
|
|
|
started_ = false;
|
|
engine_ = nullptr;
|
|
valid_ = false;
|
|
}
|
|
|
|
void EisAlgoAdaptor::OnFrameEvent(const RkAiqAlgoProcAeis* input) {
|
|
if (input->orb_stats_buf == nullptr || input->nr_img_buf == nullptr) {
|
|
// valid_ = false;
|
|
LOGE_AEIS("EIS process gets no orb stats/nr image, bypassing!");
|
|
return;
|
|
}
|
|
RkAiqOrbStats* orbStats =
|
|
reinterpret_cast<RkAiqOrbStats*>(input->orb_stats_buf->map(input->orb_stats_buf));
|
|
SmartPtr<SubVideoBuffer> nrImg = reinterpret_cast<SmartBufferPriv*>(input->nr_img_buf)
|
|
->get_video_buffer()
|
|
.dynamic_cast_ptr<SubVideoBuffer>();
|
|
auto id = orbStats->orb_stats.frame_id;
|
|
auto nr_idx = nrImg->get_index();
|
|
auto nr_num = nrImg->get_buf_num();
|
|
auto nr_fd = nrImg->get_fd();
|
|
|
|
LOGV_AEIS("OnFrameEvent id %d idx %d fd %d sof %" PRId64 " skew %lf igt %f ag %d fw %u fh %u mode %d",
|
|
id, nr_idx, nr_fd, input->sof, input->rolling_shutter_skew,
|
|
input->integration_time, input->analog_gain, input->frame_width,
|
|
input->frame_height, calib_->mode);
|
|
if (imu_ != nullptr &&
|
|
(calib_->mode == EIS_MODE_IMU_ONLY || calib_->mode == EIS_MODE_IMU_AND_IMG)) {
|
|
auto p = imu_->dequeue();
|
|
if (p.state == ParamState::kAllocated) {
|
|
auto& param = p.payload;
|
|
param->frame_id = id;
|
|
p.unique_id = id;
|
|
imu_->enqueue(p);
|
|
} else if (p.state == ParamState::kProcessedSuccess ||
|
|
p.state == ParamState::kProcessedError) {
|
|
auto& param = p.payload;
|
|
if (param->data != nullptr) {
|
|
LOGD_AEIS("IMU-%d: get data state %d id %d count %d %" PRIu64 "", p.unique_id,
|
|
p.state, param->frame_id, param->data->GetCount(),
|
|
(param->data->GetData())[param->data->GetCount() - 1].timestamp_us);
|
|
lib_->GetOps()->PutImuFrame(engine_.get(),
|
|
(mems_sensor_event_t*)param->data->GetData(),
|
|
param->data->GetCount());
|
|
param->data.reset();
|
|
}
|
|
param->frame_id = id;
|
|
p.unique_id = id;
|
|
imu_->enqueue(p);
|
|
}
|
|
}
|
|
|
|
if (nr_idx < 0) {
|
|
LOGW_AEIS("Process %d frame has invalid frame idx %d", orbStats->orb_stats.frame_id,
|
|
nr_idx);
|
|
return;
|
|
}
|
|
|
|
image_indexes_[nr_idx] = nr_fd;
|
|
|
|
if (scl_ != nullptr &&
|
|
(calib_->mode == EIS_MODE_IMG_ONLY || calib_->mode == EIS_MODE_IMU_AND_IMG)) {
|
|
// TODO(Cody): dequeue and enqueue scaler param
|
|
}
|
|
|
|
struct imageData* image = new imageData();
|
|
if (image != nullptr) {
|
|
auto& meta = image->meta_data;
|
|
meta.iso_speed = input->analog_gain;
|
|
meta.exp_time = input->integration_time;
|
|
meta.rolling_shutter_skew = input->rolling_shutter_skew / 1000000000;
|
|
meta.zoom_ratio = 1;
|
|
meta.timestamp_sof_us = input->sof / 1000;
|
|
|
|
image->buffer_index = nr_idx;
|
|
image->frame_index = orbStats->orb_stats.frame_id;
|
|
|
|
dvs_images_.emplace(orbStats->orb_stats.frame_id, image);
|
|
}
|
|
|
|
#if DEBUG
|
|
if (image != nullptr) {
|
|
const char* dump_env = std::getenv("eis_dump_imu");
|
|
int dump = 0;
|
|
if (dump_env) {
|
|
dump = atoi(dump_env);
|
|
}
|
|
if (dump > 0) {
|
|
std::ofstream ofs("/data/img.txt", std::ios::app);
|
|
if (ofs.is_open()) {
|
|
ofs << image->frame_index << "," << image->buffer_index << ","
|
|
<< image->meta_data.timestamp_sof_us << std::endl;
|
|
}
|
|
ofs.close();
|
|
}
|
|
LOGD_AEIS("Put img frame id %d idx %d ts %" PRId64 "", image->frame_index,
|
|
image->buffer_index, image->meta_data.timestamp_sof_us);
|
|
#endif
|
|
lib_->GetOps()->PutImageFrame(engine_.get(), image);
|
|
}
|
|
}
|
|
|
|
void EisAlgoAdaptor::GetProcResult(RkAiqAlgoProcResAeis* output) {
|
|
auto* mesh = remap_->GetPendingHwResult();
|
|
auto config = remap_->GetConfig();
|
|
if (mesh != nullptr) {
|
|
LOGD_AEIS("Got DVS result : id %u, idx %d, fd %d", mesh->FrameId, mesh->ImageBufferIndex,
|
|
mesh->Fd);
|
|
output->update = 1;
|
|
output->frame_id = mesh->FrameId > 0 ? mesh->FrameId : 0;
|
|
output->img_buf_index = mesh->ImageBufferIndex;
|
|
if (!image_indexes_.empty()) {
|
|
output->img_size = image_indexes_[mesh->ImageBufferIndex];
|
|
} else {
|
|
output->img_size = 0;
|
|
}
|
|
#ifdef DEBUG
|
|
if (mesh->FrameId == 1) remap_->WriteMeshToFile(mesh);
|
|
#endif
|
|
output->fd = mesh->Fd;
|
|
output->mesh_size = config.MeshSize;
|
|
output->mesh_density = config.MeshDensity;
|
|
if (mesh->Fd >= 0)
|
|
delete mesh;
|
|
else
|
|
remap_->FreeUserBuffer(mesh);
|
|
} else {
|
|
output->update = 0;
|
|
output->fd = -1;
|
|
}
|
|
}
|
|
|
|
void EisAlgoAdaptor::Stop() {
|
|
if (!started_ || !valid_) {
|
|
return;
|
|
}
|
|
|
|
if (imu_ != nullptr &&
|
|
(calib_->mode == EIS_MODE_IMU_ONLY || calib_->mode == EIS_MODE_IMU_AND_IMG)) {
|
|
imu_.reset();
|
|
}
|
|
|
|
if (scl_ != nullptr &&
|
|
(calib_->mode == EIS_MODE_IMU_ONLY || calib_->mode == EIS_MODE_IMU_AND_IMG)) {
|
|
scl_.reset();
|
|
}
|
|
|
|
if (lib_ != nullptr && engine_ != nullptr) {
|
|
lib_->GetOps()->RequestStop(engine_.get());
|
|
}
|
|
started_ = false;
|
|
}
|
|
|
|
} // namespace RkCam
|