android13/hardware/rockchip/camera/psl/rkisp2/RKISP2ImguUnit.cpp

1253 lines
45 KiB
C++

/*
* Copyright (C) 2016-2017 Intel Corporation.
* Copyright (c) 2017, Fuzhou 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.
*/
#define LOG_TAG "RKISP2ImguUnit"
#include <vector>
#include <algorithm>
#include "RKISP2ImguUnit.h"
#include "LogHelper.h"
#include "PerformanceTraces.h"
#include "workers/RKISP2OutputFrameWorker.h"
#include "workers/RKISP2InputFrameWorker.h"
#include "CameraMetadataHelper.h"
namespace android {
namespace camera2 {
namespace rkisp2 {
RKISP2ImguUnit::RKISP2ImguUnit(int cameraId,
RKISP2GraphConfigManager &gcm,
std::shared_ptr<MediaController> sensorMediaCtl,std::shared_ptr<MediaController> imgMediaCtl) :
mState(IMGU_IDLE),
mConfigChanged(true),
mCameraId(cameraId),
mGCM(gcm),
mThreadRunning(false),
mMessageQueue("RKISP2ImguUnitThread", static_cast<int>(MESSAGE_ID_MAX)),
mCurPipeConfig(nullptr),
mRKISP2MediaCtlHelper(sensorMediaCtl,imgMediaCtl, nullptr, true),
mPollerThread(new PollerThread("ImguPollerThread")),
mFlushing(false),
mFirstRequest(true),
mNeedRestartPoll(true),
mErrCb(nullptr),
mTakingPicture(false)
{
HAL_TRACE_CALL(CAM_GLBL_DBG_HIGH);
mActiveStreams.inputStream = nullptr;
mIsFrameSkiped = true;
mMessageThread = std::unique_ptr<MessageThread>(new MessageThread(this, "ImguThread"));
if (mMessageThread == nullptr) {
LOGE("Error creating poller thread");
return;
}
mMessageThread->run();
const camera_metadata_t *meta = PlatformData::getStaticMetadata(mCameraId);
camera_metadata_ro_entry entry;
CLEAR(entry);
if (meta)
entry = MetadataHelper::getMetadataEntry(
meta, ANDROID_REQUEST_PIPELINE_MAX_DEPTH);
size_t pipelineDepth = entry.count == 1 ? entry.data.u8[0] : 1;
mMainOutWorker =
std::make_shared<RKISP2OutputFrameWorker>(mCameraId, "MainWork",
IMGU_NODE_VIDEO, pipelineDepth);
mSelfOutWorker =
std::make_shared<RKISP2OutputFrameWorker>(mCameraId, "SelfWork",
IMGU_NODE_VF_PREVIEW, pipelineDepth);
mRawOutWorker =
std::make_shared<RKISP2OutputFrameWorker>(mCameraId, "RawWork",
IMGU_NODE_RAW, pipelineDepth);
// Pre allocate hal internal buffer in order to speed up some case need
// allocate buffer temporary.
// process unit in postpipeline which is not last unit will allocate
// internal buffer, now just acquire from the PreAllocateBuffer pool.
SensorFormat availableSensorFormat;
int ret = PlatformData::getCameraHWInfo()->getAvailableSensorOutputFormats(cameraId, availableSensorFormat);
if (ret != NO_ERROR)
return ;
struct SensorFrameSize &frameSize = availableSensorFormat.begin()->second.back();
int w, h, fmt, usage, num;
w = frameSize.max_width;
h = frameSize.max_height;
fmt = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
usage = GRALLOC_USAGE_SW_READ_OFTEN |
GRALLOC_USAGE_HW_CAMERA_WRITE|
RK_GRALLOC_USAGE_SPECIFY_STRIDE|
#ifdef GRALLOC_USAGE_RGA_ACCESS
RK_GRALLOC_USAGE_RGA_ACCESS|
#endif
/* TODO: same as the temp solution in RKISP1CameraHw.cpp configStreams func
* add GRALLOC_USAGE_HW_VIDEO_ENCODER is a temp patch for gpu bug:
* gpu cant alloc a nv12 buffer when format is
* HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED. Need gpu provide a patch */
GRALLOC_USAGE_HW_VIDEO_ENCODER;
/* TODO buffer num should consider postpipeline depth
* */
num = pipelineDepth;
if (MemoryUtils::creatHandlerBufferPool(cameraId, w, h, fmt, usage, num) != OK) {
LOGE("@%s : Pre allocate buffers failed, wxh(%d,%d), num:%d", __FUNCTION__, w, h, num);
}
}
RKISP2ImguUnit::~RKISP2ImguUnit()
{
HAL_TRACE_CALL(CAM_GLBL_DBG_HIGH);
status_t status = NO_ERROR;
if (mPollerThread) {
status |= mPollerThread->requestExitAndWait();
mPollerThread.reset();
}
requestExitAndWait();
if (mMessageThread != nullptr) {
mMessageThread.reset();
mMessageThread = nullptr;
}
if (mMessagesUnderwork.size())
LOGW("There are messages that are not processed %zu:", mMessagesUnderwork.size());
if (mMessagesPending.size())
LOGW("There are pending messages %zu:", mMessagesPending.size());
mActiveStreams.blobStreams.clear();
mActiveStreams.rawStreams.clear();
mActiveStreams.yuvStreams.clear();
cleanListener();
clearWorkers();
MemoryUtils::destroyHandleBufferPool(mCameraId);
}
status_t RKISP2ImguUnit::stopAllWorkers()
{
status_t status= NO_ERROR;
// Stop all video nodes
status = mMainOutWorker->stopWorker();
if (status != OK) {
LOGE("Fail to stop main woker");
return status;
}
status = mSelfOutWorker->stopWorker();
if (status != OK) {
LOGE("Fail to stop self woker");
return status;
}
status = mRawOutWorker->stopWorker();
if (status != OK) {
LOGE("Fail to stop raw woker");
return status;
}
if(mCtrlLoop!=NULL){
mCtrlLoop->stop();
}
return status;
}
void RKISP2ImguUnit::clearWorkers()
{
for (size_t i = 0; i < PIPE_NUM; i++) {
PipeConfiguration* config = &(mPipeConfigs[i]);
config->deviceWorkers.clear();
config->pollableWorkers.clear();
config->nodes.clear();
}
mListenerDeviceWorkers.clear();
}
status_t
RKISP2ImguUnit::configStreams(std::vector<camera3_stream_t*> &activeStreams, bool configChanged, bool isStillStream)
{
PERFORMANCE_ATRACE_NAME("RKISP2ImguUnit::configStreams");
HAL_TRACE_CALL(CAM_GLBL_DBG_HIGH);
LOGI("@%s %d: configChanged :%d", __FUNCTION__, __LINE__, configChanged);
std::shared_ptr<RKISP2GraphConfig> graphConfig = mGCM.getBaseGraphConfig();
mConfigChanged = configChanged;
mIsStillChangeStream = isStillStream;
mActiveStreams.blobStreams.clear();
mActiveStreams.rawStreams.clear();
mActiveStreams.yuvStreams.clear();
mActiveStreams.inputStream = nullptr;
mFirstRequest = true;
mNeedRestartPoll = true;
mCurPipeConfig = nullptr;
mTakingPicture = false;
mFlushing = false;
/*
* use for debuging: ISP get RAW data from tools
* In this mode, the timeout period of apk needs to be extended
* this property will be set by rkaiq_tool_server
*/
char property_value[PROPERTY_VALUE_MAX] = {0};
property_get("persist.vendor.camera.polltime.debug", property_value, "0");
if(atoi(property_value) > 0) {
LOGE("@%s : the timeout of pollRequest is increased to 300s", __FUNCTION__);
mIsIncreaseTimeout = true;
} else {
mIsIncreaseTimeout = false;
}
for (unsigned int i = 0; i < activeStreams.size(); ++i) {
// treat CAMERA3_STREAM_BIDIRECTIONAL as combination with an input
// stream and an output stream
if (activeStreams.at(i)->stream_type == CAMERA3_STREAM_INPUT ||
activeStreams.at(i)->stream_type == CAMERA3_STREAM_BIDIRECTIONAL) {
mActiveStreams.inputStream = activeStreams.at(i);
if (activeStreams.at(i)->stream_type == CAMERA3_STREAM_INPUT)
continue;
}
switch (activeStreams.at(i)->format) {
case HAL_PIXEL_FORMAT_BLOB:
mActiveStreams.blobStreams.push_back(activeStreams.at(i));
graphConfig->setPipeType(RKISP2GraphConfig::PIPE_STILL);
break;
case HAL_PIXEL_FORMAT_YCbCr_420_888:
mActiveStreams.yuvStreams.push_back(activeStreams.at(i));
break;
case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
// Always put IMPL stream on the begin for mapping, in the
// 3 stream case, IMPL is prefered to use for preview
mActiveStreams.yuvStreams.insert(mActiveStreams.yuvStreams.begin(), activeStreams.at(i));
break;
case HAL_PIXEL_FORMAT_RAW_OPAQUE:
mActiveStreams.rawStreams.push_back(activeStreams.at(i));
break;
default:
LOGW("Unsupported stream format %d",
activeStreams.at(i)->format);
break;
}
}
status_t status = createProcessingTasks(graphConfig);
if (status != NO_ERROR) {
LOGE("Processing tasks creation failed (ret = %d)", status);
return UNKNOWN_ERROR;
}
status = mPollerThread->init(mCurPipeConfig->nodes,
this, POLLPRI | POLLIN | POLLOUT | POLLERR, false);
if (status != NO_ERROR) {
LOGE("PollerThread init failed (ret = %d)", status);
return UNKNOWN_ERROR;
}
return OK;
}
status_t
RKISP2ImguUnit::configStreamsDone()
{
PERFORMANCE_ATRACE_NAME("RKISP2ImguUnit::configStreamsDone");
HAL_TRACE_CALL(CAM_GLBL_DBG_HIGH);
if(!mConfigChanged)
return OK;
/*
* Moved from processNextRequest because this call will cost more than 300ms,
* and cause CTS android.hardware.camera2.cts.RecordingTest#testBasicRecording
* failed, which compares the frames numbers started to calculated from the
* first request in 3 seconds to the recording file's.
*/
const RKISP2CameraCapInfo *cap = getRKISP2CameraCapInfo(mCameraId);
int skipFrames = cap->frameInitialSkip();
status_t status = kickstart(skipFrames);
if (status != OK) {
return status;
}
/* queue buf before stream on */
return status;
#if 0
int32_t duration = 30; //default duration 30ms
status = PlatformData::getCameraHWInfo()->getSensorFrameDuration(mCameraId, duration);
if (status != NO_ERROR)
LOGW("@%s : Can't get sensor frame duration", __FUNCTION__);
// Notice: frame.initialSkip configured in camera3_profiles.xml should be
// the(actual skipFrams - 2) for the driver will alway drop 2 frames.
// the first frame can't be captured by ISP, the second frame will be
// captured to dummy buffer.
const RKISP2CameraCapInfo *cap = getRKISP2CameraCapInfo(mCameraId);
int skipFrames = cap->frameInitialSkip();
if (!mIsStillChangeStream) {
usleep(skipFrames * duration * 1000);
LOGD("@%s : skipFrames: %d, sensorFrameDuration: %d", __FUNCTION__, skipFrames, duration);
} else {
LOGD_CAP("@%s : no need skipFrames for still cap!", __FUNCTION__);
}
return status;
#endif
}
#define streamSizeGT(s1, s2) (((s1)->width * (s1)->height) > ((s2)->width * (s2)->height))
#define streamSizeEQ(s1, s2) (((s1)->width * (s1)->height) == ((s2)->width * (s2)->height))
#define streamSizeGE(s1, s2) (((s1)->width * (s1)->height) >= ((s2)->width * (s2)->height))
#define streamSizeRatio(s1) ((float)((s1)->width) / (s1)->height)
status_t RKISP2ImguUnit::mapStreamWithDeviceNode(int phyStreamsNum)
{
HAL_TRACE_CALL(CAM_GLBL_DBG_HIGH);
int blobNum = mActiveStreams.blobStreams.size();
int yuvNum = mActiveStreams.yuvStreams.size();
int streamNum = blobNum + yuvNum;
if (blobNum > 1 || phyStreamsNum <= 0) {
LOGE("Don't support blobNum %d, phyStreamsNum %d", blobNum, phyStreamsNum);
return BAD_VALUE;
}
mStreamNodeMapping.clear();
mStreamListenerMapping.clear();
std::vector<camera3_stream_t *> availableStreams = mActiveStreams.yuvStreams;
if (blobNum) {
availableStreams.insert(availableStreams.begin(), mActiveStreams.blobStreams[0]);
}
LOGI("@%s, %d streams, blobNum:%d, yuvNum:%d", __FUNCTION__, streamNum, blobNum, yuvNum);
// support up to 4 output streams, and because ISP hardware can only support
// 2 output streams directly, so other two streams should be implemented as
// listeners.
int videoIdx = -1;
int previewIdx = -1;
std::vector<std::pair<int, NodeTypes>> listeners;
if (streamNum == 1) {
// Force use video, rk use the IMGU_NODE_VIDEO firstly.
//if second stream is needed, then IMGU_NODE_VF_PREVIEW will be usde.
//and rk has no IMGU_NODE_PV_PREVIEW.
videoIdx = 0;
} else if (streamNum == 2) {
videoIdx = (streamSizeGE(availableStreams[0], availableStreams[1])) ? 0 : 1;
if (phyStreamsNum > 1) {
previewIdx = videoIdx ? 0 : 1;
} else {
std::pair<int, NodeTypes> listener;
listener.first = videoIdx ? 0 : 1;
listener.second = IMGU_NODE_VIDEO;
listeners.push_back(listener);
}
} else if (streamNum == 3 || mActiveStreams.inputStream) {
videoIdx = 0;
// find the maxium size stream
for (int i = 0; i < availableStreams.size(); i++) {
if (streamSizeGT(availableStreams[i], availableStreams[videoIdx]))
videoIdx = i;
}
if (phyStreamsNum > 1) {
for (int i = 0; i < availableStreams.size(); i++) {
if (i == videoIdx ||
availableStreams[i]->width > SP_MAX_WIDTH ||
availableStreams[i]->height > SP_MAX_HEIGHT) {
continue ;
} else {
if(previewIdx == -1)
previewIdx = i;
if (streamSizeGT(availableStreams[i], availableStreams[previewIdx]))
previewIdx = i;
}
}
if (previewIdx == -1) {
LOGE("@%s : No stream map to SP while phyStreams(%d) more than one", __FUNCTION__, phyStreamsNum);
return UNKNOWN_ERROR;
}
// deal with listners
float videoSizeRatio = streamSizeRatio(availableStreams[videoIdx]);
float previewSizeRatio = streamSizeRatio(availableStreams[previewIdx]);
float listenerSizeRatio = 0;
for (int i = 0; i < availableStreams.size(); i++) {
if (i != videoIdx && i != previewIdx) {
listenerSizeRatio = streamSizeRatio(availableStreams[i]);
std::pair<int, NodeTypes> listener;
listener.first = i;
float lpRatioDiff = fabs(listenerSizeRatio - previewSizeRatio);
float lvRatioDiff = fabs(listenerSizeRatio - videoSizeRatio);
if (fabs(lpRatioDiff - lvRatioDiff) <= 0.000001f) {
if (streamSizeEQ(availableStreams[i], availableStreams[videoIdx]))
listener.second = IMGU_NODE_VIDEO;
else if (streamSizeEQ(availableStreams[i], availableStreams[previewIdx]))
listener.second = IMGU_NODE_VF_PREVIEW;
else if (streamSizeGT(availableStreams[previewIdx], availableStreams[videoIdx]))
listener.second = IMGU_NODE_VF_PREVIEW;
else
listener.second = IMGU_NODE_VIDEO;
} else if (lpRatioDiff < lvRatioDiff) {
if (streamSizeGE(availableStreams[previewIdx], availableStreams[i]))
listener.second = IMGU_NODE_VF_PREVIEW;
else
listener.second = IMGU_NODE_VIDEO;
} else {
if (streamSizeGE(availableStreams[videoIdx], availableStreams[i]))
listener.second = IMGU_NODE_VIDEO;
else
listener.second = IMGU_NODE_VF_PREVIEW;
}
listeners.push_back(listener);
}
}
} else {
for (int i = 0; i < availableStreams.size(); i++) {
if (i != videoIdx) {
std::pair<int, NodeTypes> listener;
listener.first = i;
listener.second = IMGU_NODE_VIDEO;
listeners.push_back(listener);
}
}
}
} else {
LOGE("@%s, ERROR, blobNum:%d, yuvNum:%d", __FUNCTION__, blobNum, yuvNum);
return UNKNOWN_ERROR;
}
if (previewIdx >= 0) {
mStreamNodeMapping[IMGU_NODE_VF_PREVIEW] = availableStreams[previewIdx];
mStreamNodeMapping[IMGU_NODE_PV_PREVIEW] = mStreamNodeMapping[IMGU_NODE_VF_PREVIEW];
LOGD("@%s, %d stream %p size preview: %dx%d, format %s", __FUNCTION__,
previewIdx, availableStreams[previewIdx],
availableStreams[previewIdx]->width, availableStreams[previewIdx]->height,
METAID2STR(android_scaler_availableFormats_values,
availableStreams[previewIdx]->format));
}
if (videoIdx >= 0) {
mStreamNodeMapping[IMGU_NODE_VIDEO] = availableStreams[videoIdx];
LOGI("@%s, %d stream %p size video: %dx%d, format %s", __FUNCTION__,
videoIdx, availableStreams[videoIdx],
availableStreams[videoIdx]->width, availableStreams[videoIdx]->height,
METAID2STR(android_scaler_availableFormats_values,
availableStreams[videoIdx]->format));
}
for (auto iter : listeners) {
mStreamListenerMapping[availableStreams[iter.first]] = iter.second;
LOGI("@%s (%dx%d 0x%x), %p listen to 0x%x", __FUNCTION__,
availableStreams[iter.first]->width, availableStreams[iter.first]->height,
availableStreams[iter.first]->format, availableStreams[iter.first], iter.second);
}
if(mActiveStreams.rawStreams.size() != 0) {
//raw stream listen to mp if mp output raw or mapping to rawWork
if(!PlatformData::getCameraHWInfo()->isIspSupportRawPath())
mStreamListenerMapping[mActiveStreams.rawStreams[0]] = IMGU_NODE_VIDEO;
else
mStreamNodeMapping[IMGU_NODE_RAW] = mActiveStreams.rawStreams[0];
}
return OK;
}
/**
* Create the processing tasks and listening tasks.
* Processing tasks are:
* - video task (wraps video pipeline)
* - capture task (wraps still capture)
* - raw bypass (not done yet)
*
* \param[in] activeStreams StreamConfig struct filled during configStreams
* \param[in] graphConfig Configuration of the base graph
*/
status_t
RKISP2ImguUnit::createProcessingTasks(std::shared_ptr<RKISP2GraphConfig> graphConfig)
{
HAL_TRACE_CALL(CAM_GLBL_DBG_HIGH);
status_t status = OK;
if (CC_UNLIKELY(graphConfig.get() == nullptr)) {
LOGE("ERROR: Graph config is nullptr");
return UNKNOWN_ERROR;
}
// rk only has video config, set it as default,zyc
mCurPipeConfig = &mPipeConfigs[PIPE_VIDEO_INDEX];
if(mConfigChanged) {
//when need reconfig hw pipeline, all works should stop
stopAllWorkers();
clearWorkers();
// Open and configure imgu video nodes
status = mRKISP2MediaCtlHelper.configure(mGCM, RKISP2IStreamConfigProvider::CIO2);
if (status != OK) {
LOGE("Failed to configure input system.");
return status;
}
status = mRKISP2MediaCtlHelper.configure(mGCM, RKISP2IStreamConfigProvider::IMGU_COMMON);
if (status != OK)
return UNKNOWN_ERROR;
if (mGCM.getMediaCtlConfig(RKISP2IStreamConfigProvider::IMGU_STILL)) {
status = mRKISP2MediaCtlHelper.configurePipe(mGCM, RKISP2IStreamConfigProvider::IMGU_STILL, true);
if (status != OK)
return UNKNOWN_ERROR;
mCurPipeConfig = &mPipeConfigs[PIPE_STILL_INDEX];
}
// Set video pipe by default
if (mGCM.getMediaCtlConfig(RKISP2IStreamConfigProvider::IMGU_VIDEO)) {
status = mRKISP2MediaCtlHelper.configurePipe(mGCM, RKISP2IStreamConfigProvider::IMGU_VIDEO, true);
if (status != OK)
return UNKNOWN_ERROR;
mCurPipeConfig = &mPipeConfigs[PIPE_VIDEO_INDEX];
}
} else {
clearWorkers();
}
mConfiguredNodesPerName = mRKISP2MediaCtlHelper.getConfiguredNodesPerName();
if (mConfiguredNodesPerName.size() == 0) {
LOGD("No nodes present");
return UNKNOWN_ERROR;
}
/* TODO */
// Raw Path can not be considered as a normal phyStream now
int phyStreamsNum = mConfiguredNodesPerName.size();
LOGD("phyStreamsNum:%d",phyStreamsNum);
for (auto it = mConfiguredNodesPerName.begin(); it != mConfiguredNodesPerName.end(); ++it) {
if((*it).first == IMGU_NODE_RAW) {
phyStreamsNum--;
break;
}
}
if (mapStreamWithDeviceNode(phyStreamsNum) != OK)
return UNKNOWN_ERROR;
PipeConfiguration* videoConfig = &(mPipeConfigs[PIPE_VIDEO_INDEX]);
PipeConfiguration* stillConfig = &(mPipeConfigs[PIPE_STILL_INDEX]);
std::shared_ptr<RKISP2OutputFrameWorker> vfWorker = nullptr;
std::shared_ptr<RKISP2OutputFrameWorker> pvWorker = nullptr;
const camera_metadata_t *meta = PlatformData::getStaticMetadata(mCameraId);
camera_metadata_ro_entry entry;
CLEAR(entry);
if (meta)
entry = MetadataHelper::getMetadataEntry(
meta, ANDROID_REQUEST_PIPELINE_MAX_DEPTH);
size_t pipelineDepth = entry.count == 1 ? entry.data.u8[0] : 1;
LOGD("pipelineDepth:%d",pipelineDepth);
for (const auto &it : mConfiguredNodesPerName) {
if (it.first == IMGU_NODE_STILL || it.first == IMGU_NODE_VIDEO) {
if(mStreamNodeMapping[it.first] == NULL)
continue;
LOGD("mMainOutWorker attach node:name :%s",it.second->name());
mMainOutWorker->attachNode(it.second);
mMainOutWorker->attachStream(mStreamNodeMapping[it.first]);
videoConfig->deviceWorkers.push_back(mMainOutWorker);
videoConfig->pollableWorkers.push_back(mMainOutWorker);
videoConfig->nodes.push_back(mMainOutWorker->getNode());
setStreamListeners(it.first, mMainOutWorker);
//shutter event for non isys, zyc
mListenerDeviceWorkers.push_back(mMainOutWorker.get());
} else if (it.first == IMGU_NODE_VF_PREVIEW) {
if(mStreamNodeMapping[it.first] == NULL)
continue;
LOGD("mSelfOutWorker IMGU_NODE_VF_PREVIEW attach node:name :%s",it.second->name());
mSelfOutWorker->attachNode(it.second);
mSelfOutWorker->attachStream(mStreamNodeMapping[it.first]);
videoConfig->deviceWorkers.push_back(mSelfOutWorker);
videoConfig->pollableWorkers.push_back(mSelfOutWorker);
videoConfig->nodes.push_back(mSelfOutWorker->getNode());
setStreamListeners(it.first, mSelfOutWorker);
//shutter event for non isys, zyc
mListenerDeviceWorkers.push_back(mSelfOutWorker.get());
} else if (it.first == IMGU_NODE_PV_PREVIEW) {
if(mStreamNodeMapping[it.first] == NULL)
continue;
pvWorker = std::make_shared<RKISP2OutputFrameWorker>(mCameraId, "PVWork",
it.first, pipelineDepth);
pvWorker->attachNode(it.second);
pvWorker->attachStream(mStreamNodeMapping[it.first]);
setStreamListeners(it.first, pvWorker);
//shutter event for non isys, zyc
mListenerDeviceWorkers.push_back(pvWorker.get());
} else if (it.first == IMGU_NODE_RAW) {
if(mStreamNodeMapping[it.first] == NULL)
continue;
mRawOutWorker->attachNode(it.second);
mRawOutWorker->attachStream(mStreamNodeMapping[it.first]);
videoConfig->deviceWorkers.push_back(mRawOutWorker);
videoConfig->pollableWorkers.push_back(mRawOutWorker);
videoConfig->nodes.push_back(mRawOutWorker->getNode());
setStreamListeners(it.first, mRawOutWorker);
//shutter event for non isys, zyc
mListenerDeviceWorkers.push_back(mRawOutWorker.get());
} else {
LOGE("Unknown NodeName: %d", it.first);
return UNKNOWN_ERROR;
}
}
if (pvWorker.get()) {
// Copy common part for still pipe, then add pv
*stillConfig = *videoConfig;
stillConfig->deviceWorkers.insert(stillConfig->deviceWorkers.begin(), pvWorker);
stillConfig->pollableWorkers.insert(stillConfig->pollableWorkers.begin(), pvWorker);
stillConfig->nodes.insert(stillConfig->nodes.begin(), pvWorker->getNode());
if (mCurPipeConfig == videoConfig) {
LOGI("%s: configure postview in advance", __FUNCTION__);
pvWorker->configure(mConfigChanged);
}
}
// Prepare for video pipe
if (vfWorker.get()) {
videoConfig->deviceWorkers.insert(videoConfig->deviceWorkers.begin(), vfWorker);
videoConfig->pollableWorkers.insert(videoConfig->pollableWorkers.begin(), vfWorker);
videoConfig->nodes.insert(videoConfig->nodes.begin(), vfWorker->getNode());
// vf node provides source frame during still preview instead of pv node.
if (pvWorker.get()) {
setStreamListeners(IMGU_NODE_PV_PREVIEW, vfWorker);
}
if (mCurPipeConfig == stillConfig) {
LOGI("%s: configure preview in advance", __FUNCTION__);
vfWorker->configure(mConfigChanged);
}
}
if (mActiveStreams.inputStream) {
std::vector<camera3_stream_t*> outStreams;
outStreams.insert(outStreams.begin(), mActiveStreams.blobStreams.begin(),
mActiveStreams.blobStreams.end());
outStreams.insert(outStreams.begin(), mActiveStreams.yuvStreams.begin(),
mActiveStreams.yuvStreams.end());
std::shared_ptr<RKISP2InputFrameWorker> inWorker =
std::make_shared<RKISP2InputFrameWorker>(mCameraId,
mActiveStreams.inputStream, outStreams, pipelineDepth);
videoConfig->deviceWorkers.insert(videoConfig->deviceWorkers.begin(), inWorker);
mListenerDeviceWorkers.push_back(inWorker.get());
}
status_t ret = OK;
for (const auto &it : mCurPipeConfig->deviceWorkers) {
ret = (*it).configure(mConfigChanged);
if (ret != OK) {
LOGE("Failed to configure workers.");
return ret;
}
}
std::vector<RKISP2ICaptureEventSource*>::iterator it = mListenerDeviceWorkers.begin();
for (;it != mListenerDeviceWorkers.end(); ++it) {
for (const auto &listener : mListeners) {
(*it)->attachListener(listener);
}
}
return OK;
}
void RKISP2ImguUnit::setStreamListeners(NodeTypes nodeName,
std::shared_ptr<RKISP2OutputFrameWorker>& source)
{
for (const auto &it : mStreamListenerMapping) {
if (it.second == nodeName) {
LOGI("@%s stream %p listen to nodeName 0x%x",
__FUNCTION__, it.first, nodeName);
source->addListener(it.first);
}
}
}
void
RKISP2ImguUnit::cleanListener()
{
HAL_TRACE_CALL(CAM_GLBL_DBG_HIGH);
// clean all the listening tasks
std::shared_ptr<RKISP2ITaskEventListener> lTask = nullptr;
for (unsigned int i = 0; i < mListeningTasks.size(); i++) {
lTask = mListeningTasks.at(i);
if (lTask.get() == nullptr) {
LOGE("Listening task null - BUG.");
}
else
lTask->cleanListeners();
}
mListeningTasks.clear();
}
status_t RKISP2ImguUnit::attachListener(ICaptureEventListener *aListener)
{
HAL_TRACE_CALL(CAM_GLBL_DBG_HIGH);
mListeners.push_back(aListener);
return OK;
}
status_t
RKISP2ImguUnit::completeRequest(std::shared_ptr<RKISP2ProcUnitSettings> &processingSettings,
bool updateMeta)
{
HAL_TRACE_CALL(CAM_GLBL_DBG_HIGH);
Camera3Request *request = processingSettings->request;
if (CC_UNLIKELY(request == nullptr)) {
LOGE("ProcUnit: nullptr request - BUG");
return UNKNOWN_ERROR;
}
const std::vector<camera3_stream_buffer> *outBufs = request->getOutputBuffers();
const std::vector<camera3_stream_buffer> *inBufs = request->getInputBuffers();
int reqId = request->getId();
LOGD("@%s: Req id %d, Num outbufs %zu Num inbufs %zu",
__FUNCTION__, reqId, outBufs ? outBufs->size() : 0, inBufs ? inBufs->size() : 0);
RKISP2ProcTaskMsg procMsg;
procMsg.reqId = reqId;
procMsg.processingSettings = processingSettings;
MessageCallbackMetadata cbMetadataMsg;
cbMetadataMsg.updateMeta = updateMeta;
cbMetadataMsg.request = request;
DeviceMessage msg;
msg.id = MESSAGE_COMPLETE_REQ;
msg.pMsg = procMsg;
msg.cbMetadataMsg = cbMetadataMsg;
mMessageQueue.send(&msg);
return NO_ERROR;
}
status_t
RKISP2ImguUnit::skipBadFrames()
{
PERFORMANCE_ATRACE_NAME("RKISP2ImguUnit::skipBadFrames");
HAL_TRACE_CALL(CAM_GLBL_DBG_HIGH);
status_t status = OK;
std::shared_ptr<V4L2VideoNode> *videoNode;
mIsFrameSkiped = true;
if (mCurPipeConfig->nodes.size() <= 0) {
LOGW("@%s: no video nodes in mCurPipeConfig, skip!!");
return OK;
}
// Notice: frame.initialSkip configured in camera3_profiles.xml should be
// the(actual skipFrams - 2) for the driver will alway drop 2 frames.
// the first frame can't be captured by ISP, the second frame will be
// captured to dummy buffer.
const RKISP2CameraCapInfo *cap = getRKISP2CameraCapInfo(mCameraId);
int skipFrames = cap->frameInitialSkip();
/* skip frames */
std::vector<RKISP2ICaptureEventSource*>::iterator it = mListenerDeviceWorkers.begin();
for (;it != mListenerDeviceWorkers.end(); ++it) {
if ((*it == mMainOutWorker.get()) && mMainOutWorker->mIsStarted) {
status = mMainOutWorker->skipBadFrames(skipFrames);
break;
} else if ((*it == mSelfOutWorker.get()) && mSelfOutWorker->mIsStarted) {
status = mSelfOutWorker->skipBadFrames(skipFrames);
break;
}
}
return status;
}
status_t
RKISP2ImguUnit::handleMessageCompleteReq(DeviceMessage &msg)
{
HAL_TRACE_CALL(CAM_GLBL_DBG_HIGH);
status_t status = OK;
Camera3Request *request = msg.cbMetadataMsg.request;
if (request == nullptr) {
LOGE("Request is nullptr");
return BAD_VALUE;
}
unsigned int numOutputBufs = request->getNumberOutputBufs();
std::shared_ptr<DeviceMessage> tmp = std::make_shared<DeviceMessage>(msg);
mMessagesPending.push_back(tmp);
mCurPipeConfig->nodes.clear();
status = processNextRequest();
if (status != OK) {
LOGE("Process request %d failed", request->getId());
request->setError();
}
/**
* Send poll request for every requests(even when error), so that we can
* handle them in the right order.
*/
LOGI("%s, mCurPipeConfig->nodes.size():%d", __FUNCTION__, mCurPipeConfig->nodes.size());
if (mCurPipeConfig->nodes.size() > 0) {
if (mIsIncreaseTimeout) {
status |= mPollerThread->pollRequest(request->getId(), numOutputBufs, 300 * 1000,
&(mCurPipeConfig->nodes));
} else {
status |= mPollerThread->pollRequest(request->getId(), numOutputBufs, 3000,
&(mCurPipeConfig->nodes));
}
}
return status;
}
status_t RKISP2ImguUnit::processNextRequest()
{
status_t status = NO_ERROR;
std::shared_ptr<DeviceMessage> msg = nullptr;
Camera3Request *request = nullptr;
LOGD("%s: pending size %zu,underwork.size(%d), state %d", __FUNCTION__, mMessagesPending.size(), mMessagesUnderwork.size(), mState);
if (mMessagesPending.empty())
return NO_ERROR;
msg = mMessagesPending[0];
mMessagesPending.erase(mMessagesPending.begin());
// update and return metadata firstly
request = msg->cbMetadataMsg.request;
if (request == nullptr) {
LOGE("Request is nullptr");
// Ignore this request
return NO_ERROR;
}
LOGD("@%s:handleExecuteReq for Req id %d, ", __FUNCTION__, request->getId());
mMessagesUnderwork.push_back(msg);
// Pass settings to the listening tasks *before* sending metadata
// up to framework. Some tasks might need e.g. the result data.
std::shared_ptr<RKISP2ITaskEventListener> lTask = nullptr;
for (unsigned int i = 0; i < mListeningTasks.size(); i++) {
lTask = mListeningTasks.at(i);
if (lTask.get() == nullptr) {
LOGE("Listening task null - BUG.");
return UNKNOWN_ERROR;
}
status |= lTask->settings(msg->pMsg);
}
mCurPipeConfig->nodes.clear();
mRequestToWorkMap[request->getId()].clear();
std::vector<std::shared_ptr<RKISP2IDeviceWorker>>::iterator it = mCurPipeConfig->deviceWorkers.begin();
for (;it != mCurPipeConfig->deviceWorkers.end(); ++it) {
// construct an dummy poll event for RKISP2InputFrameWorker
// notice that this would cause poll event disorder,
// so we should do some workaround in startProcessing.
if ((*it)->getNode().get() == nullptr && request->getInputBuffers()->size() > 0) {
mRequestToWorkMap[request->getId()].push_back(*it);
MessageCallbackMetadata cbMetadataMsg;
cbMetadataMsg.request = request;
DeviceMessage dummyMsg;
dummyMsg.pollEvent.requestId = request->getId();
dummyMsg.pollEvent.numDevices = 0;
dummyMsg.pollEvent.polledDevices = 0;
dummyMsg.pollEvent.activeDevices = nullptr;
dummyMsg.id = MESSAGE_ID_POLL;
dummyMsg.cbMetadataMsg = cbMetadataMsg;
status |= (*it)->prepareRun(msg);
mMessageQueue.send(&dummyMsg);
return status;
} else
status |= (*it)->prepareRun(msg);
}
std::vector<std::shared_ptr<RKISP2FrameWorker>>::iterator pollDevice = mCurPipeConfig->pollableWorkers.begin();
for (;pollDevice != mCurPipeConfig->pollableWorkers.end(); ++pollDevice) {
bool needsPolling = (*pollDevice)->needPolling();
if (needsPolling) {
if (request->getInputBuffers()->size() == 0)
mCurPipeConfig->nodes.push_back((*pollDevice)->getNode());
LOGI("@%s:(*pollDevice)->name() %s, ", __FUNCTION__, (*pollDevice)->name());
mRequestToWorkMap[request->getId()].push_back((std::shared_ptr<RKISP2IDeviceWorker>&)(*pollDevice));
}
}
return status;
}
status_t
RKISP2ImguUnit::kickstart(int skipFrames)
{
HAL_TRACE_CALL(CAM_GLBL_DBG_HIGH);
status_t status = OK;
for (const auto &it : mCurPipeConfig->deviceWorkers) {
status = (*it).startWorker(skipFrames);
if (status != OK) {
LOGE("Failed to start workers.");
return status;
}
}
mFirstRequest = false;
mIsFrameSkiped = true;
return status;
}
/**
* Start the processing task for each input buffer.
* Each of the input buffers has an associated terminal id. This is the
* destination terminal id. This terminal id is the input terminal for one
* or the execute tasks we have.
*
* Check the map that links the input terminals of the pipelines to the
* tasks that wrap them to decide which tasks need to be executed.
*/
status_t
RKISP2ImguUnit::startProcessing(DeviceMessage pollmsg)
{
HAL_TRACE_CALL(CAM_GLBL_DBG_HIGH);
PERFORMANCE_ATRACE_CALL();
status_t status = OK;
std::shared_ptr<V4L2VideoNode> *activeNodes = pollmsg.pollEvent.activeDevices;
int processReqNum = 1;
bool deviceError = pollmsg.pollEvent.polledDevices && !activeNodes;
std::shared_ptr<DeviceMessage> msg;
Camera3Request *request;
if (mMessagesUnderwork.empty())
return status;
msg = *(mMessagesUnderwork.begin());
request = msg->cbMetadataMsg.request;
unsigned int reqId = pollmsg.pollEvent.requestId;
if (request->getId() < reqId) {
// poll event may disorder, and we should process it in
// order, so add it to the delay queue that will be processed
// later.
LOGD("%s: poll event disorder, exp %d, real %d", __FUNCTION__,
request->getId(), reqId);
mDelayProcessRequest.push_back(reqId);
return status;
} else if (request->getId() > reqId) {
LOGE("%s: request id dont match: exp %d, real %d", __FUNCTION__,
request->getId(), reqId);
return UNKNOWN_ERROR;
}
if (mDelayProcessRequest.size() > 0) {
unsigned int startId = reqId + 1;
int i = 0;
for (; i < mDelayProcessRequest.size(); i++) {
if (mDelayProcessRequest[i] != startId)
break;
processReqNum++;
startId++;
}
while (i > 0 && processReqNum > 1) {
mDelayProcessRequest.erase(mDelayProcessRequest.begin());
i--;
}
}
/* tell workers and AAL that device error occured */
if (deviceError && request->getInputBuffers()->size() == 0) {
for (const auto &it : mCurPipeConfig->deviceWorkers)
(*it).deviceError();
if (mErrCb)
mErrCb->deviceError();
/* clear the polling msg*/
mPollerThread->flush(false);
processReqNum = mMessagesUnderwork.size();
}
/* skip frames*/
if (!mIsFrameSkiped && !mIsStillChangeStream) {
status = skipBadFrames();
if (status != OK) {
LOGE("@%s: failed to skipFrames!!");
}
}
for ( int i = 0; i < processReqNum; i++) {
msg = *(mMessagesUnderwork.begin());
request = msg->cbMetadataMsg.request;
reqId = request->getId();
std::vector<std::shared_ptr<RKISP2IDeviceWorker>>::iterator it = mRequestToWorkMap[reqId].begin();
for (;it != mRequestToWorkMap[reqId].end(); ++it) {
std::shared_ptr<RKISP2FrameWorker> worker = (std::shared_ptr<RKISP2FrameWorker>&)(*it);
status |= worker->asyncPollDone(*(mMessagesUnderwork.begin()), true);
}
it = mRequestToWorkMap[reqId].begin();
for (;it != mRequestToWorkMap[reqId].end(); ++it) {
status |= (*it)->run();
}
it = mRequestToWorkMap[reqId].begin();
for (;it != mRequestToWorkMap[reqId].end(); ++it) {
status |= (*it)->postRun();
}
mRequestToWorkMap.erase(reqId);
// Report request error when anything wrong
if (status != OK || deviceError)
request->setError();
//HACK: return metadata after updated it
LOGI("%s: request %d done", __func__, request->getId());
ICaptureEventListener::CaptureMessage outMsg;
outMsg.data.event.reqId = request->getId();
outMsg.data.event.type = ICaptureEventListener::CAPTURE_REQUEST_DONE;
outMsg.id = ICaptureEventListener::CAPTURE_MESSAGE_ID_EVENT;
for (const auto &listener : mListeners)
listener->notifyCaptureEvent(&outMsg);
mMessagesUnderwork.erase(mMessagesUnderwork.begin());
}
return status;
}
status_t RKISP2ImguUnit::notifyPollEvent(PollEventMessage *pollMsg)
{
HAL_TRACE_CALL(CAM_GLBL_DBG_HIGH);
if (pollMsg == nullptr || pollMsg->data.activeDevices == nullptr)
return BAD_VALUE;
// Common thread message fields for any case
DeviceMessage msg;
msg.pollEvent.pollMsgId = pollMsg->id;
msg.pollEvent.requestId = pollMsg->data.reqId;
if (pollMsg->id == POLL_EVENT_ID_EVENT) {
int numDevices = pollMsg->data.activeDevices->size();
if (numDevices == 0) {
LOGI("@%s: devices flushed", __FUNCTION__);
return OK;
}
int numPolledDevices = pollMsg->data.polledDevices->size();
if (CC_UNLIKELY(numPolledDevices == 0)) {
LOGW("No devices Polled?");
return OK;
}
msg.pollEvent.activeDevices = new std::shared_ptr<V4L2VideoNode>[numDevices];
for (int i = 0; i < numDevices; i++) {
msg.pollEvent.activeDevices[i] = (std::shared_ptr<V4L2VideoNode>&) pollMsg->data.activeDevices->at(i);
}
msg.pollEvent.numDevices = numDevices;
msg.pollEvent.polledDevices = numPolledDevices;
if (pollMsg->data.activeDevices->size() != pollMsg->data.polledDevices->size()) {
LOGD("@%s: %zu inactive nodes for request %u, retry poll", __FUNCTION__,
pollMsg->data.inactiveDevices->size(),
pollMsg->data.reqId);
pollMsg->data.polledDevices->clear();
*pollMsg->data.polledDevices = *pollMsg->data.inactiveDevices; // retry with inactive devices
delete [] msg.pollEvent.activeDevices;
return -EAGAIN;
}
{
std::lock_guard<std::mutex> l(mFlushMutex);
if (mFlushing)
return OK;
msg.id = MESSAGE_ID_POLL;
mMessageQueue.send(&msg, MESSAGE_ID_POLL);
}
} else if (pollMsg->id == POLL_EVENT_ID_ERROR) {
LOGE("Device poll failed");
// For now, set number of device to zero in error case
msg.pollEvent.numDevices = 0;
msg.pollEvent.polledDevices = pollMsg->data.polledDevices->size();
msg.id = MESSAGE_ID_POLL;
mMessageQueue.send(&msg);
} else {
LOGW("unknown poll event id (%d)", pollMsg->id);
}
return OK;
}
status_t RKISP2ImguUnit::handleMessagePoll(DeviceMessage msg)
{
HAL_TRACE_CALL(CAM_GLBL_DBG_HIGH);
status_t status = startProcessing(msg);
if (msg.pollEvent.activeDevices)
delete [] msg.pollEvent.activeDevices;
msg.pollEvent.activeDevices = nullptr;
return status;
}
void RKISP2ImguUnit::getConfigedHwPathSize(const char* pathName, uint32_t &size)
{
mRKISP2MediaCtlHelper.getConfigedHwPathSize(pathName, size);
}
void RKISP2ImguUnit::getConfigedSensorOutputSize(uint32_t &size)
{
mRKISP2MediaCtlHelper.getConfigedSensorOutputSize(size);
}
void
RKISP2ImguUnit::messageThreadLoop(void)
{
LOGD("@%s - Start", __FUNCTION__);
mThreadRunning = true;
while (mThreadRunning) {
status_t status = NO_ERROR;
PERFORMANCE_ATRACE_BEGIN("Imgu-PollMsg");
DeviceMessage msg;
mMessageQueue.receive(&msg);
PERFORMANCE_ATRACE_END();
PERFORMANCE_ATRACE_NAME_SNPRINTF("Imgu-%s", ENUM2STR(ImguMsg_stringEnum, msg.id));
PERFORMANCE_HAL_ATRACE_PARAM1("msg", msg.id);
LOGD("@%s, receive message id:%d", __FUNCTION__, msg.id);
switch (msg.id) {
case MESSAGE_ID_EXIT:
status = handleMessageExit();
break;
case MESSAGE_COMPLETE_REQ:
status = handleMessageCompleteReq(msg);
break;
case MESSAGE_ID_POLL:
case MESSAGE_ID_POLL_META:
status = handleMessagePoll(msg);
break;
case MESSAGE_ID_FLUSH:
status = handleMessageFlush();
break;
default:
LOGE("ERROR Unknown message %d in thread loop", msg.id);
status = BAD_VALUE;
break;
}
if (status != NO_ERROR)
LOGE("error %d in handling message: %d",
status, static_cast<int>(msg.id));
LOGD("@%s, finish message id:%d", __FUNCTION__, msg.id);
mMessageQueue.reply(msg.id, status);
PERFORMANCE_ATRACE_END();
}
LOGD("%s: Exit", __FUNCTION__);
}
status_t
RKISP2ImguUnit::handleMessageExit(void)
{
HAL_TRACE_CALL(CAM_GLBL_DBG_HIGH);
mThreadRunning = false;
return NO_ERROR;
}
status_t
RKISP2ImguUnit::requestExitAndWait(void)
{
HAL_TRACE_CALL(CAM_GLBL_DBG_HIGH);
DeviceMessage msg;
msg.id = MESSAGE_ID_EXIT;
status_t status = mMessageQueue.send(&msg, MESSAGE_ID_EXIT);
status |= mMessageThread->requestExitAndWait();
status |= stopAllWorkers();
clearWorkers();
return status;
}
status_t
RKISP2ImguUnit::flush(void)
{
PERFORMANCE_ATRACE_NAME("RKISP2ImguUnit::flush");
HAL_TRACE_CALL(CAM_GLBL_DBG_HIGH);
DeviceMessage msg;
msg.id = MESSAGE_ID_FLUSH;
{
std::lock_guard<std::mutex> l(mFlushMutex);
mFlushing = true;
}
mMessageQueue.remove(MESSAGE_ID_POLL);
return mMessageQueue.send(&msg, MESSAGE_ID_FLUSH);
}
status_t
RKISP2ImguUnit::handleMessageFlush(void)
{
HAL_TRACE_CALL(CAM_GLBL_DBG_HIGH);
mPollerThread->flush(true);
// flush all video nodes
if (mCurPipeConfig) {
status_t status;
for (const auto &it : mCurPipeConfig->deviceWorkers) {
status = (*it).flushWorker();
if (status != OK) {
LOGE("Fail to flush wokers");
return status;
}
}
}
return NO_ERROR;
}
} /* namespace rksip2 */
} /* namespace camera2 */
} /* namespace android */