| /* |
| * Copyright (C) 2017-2020 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 "PSysProcessor" |
| |
| #include <set> |
| #include <utility> |
| |
| #include "iutils/Utils.h" |
| #include "iutils/CameraLog.h" |
| #include "iutils/CameraDump.h" |
| #include "iutils/SwImageConverter.h" |
| |
| #include "PlatformData.h" |
| #include "3a/AiqResultStorage.h" |
| #include "ParameterGenerator.h" |
| |
| #include "PSysProcessor.h" |
| |
| /* |
| * The sof event time margin is a tunning value |
| * it's based on sensor vblank, psys iterating time |
| * and thread scheduling |
| */ |
| #define SOF_EVENT_MARGIN (5000000) // 5ms |
| #define SOF_EVENT_MAX_MARGIN (60000000) // 60ms |
| |
| #define EXTREME_NEGATIVE_STRENGTH (-60) |
| #define EXTREME_POSITIVE_STRENGTH (20) |
| |
| #define DEFAULT_STRENGTH 0 |
| |
| using std::shared_ptr; |
| using std::unique_ptr; |
| |
| namespace icamera { |
| PSysProcessor::PSysProcessor(int cameraId, ParameterGenerator *pGenerator) : |
| mCameraId(cameraId), |
| mParameterGenerator(pGenerator), |
| mCurConfigMode(CAMERA_STREAM_CONFIGURATION_MODE_NORMAL), |
| mTuningMode(TUNING_MODE_MAX), |
| mRawPort(INVALID_PORT), |
| mSofSequence(-1), |
| mOpaqueRawPort(INVALID_PORT), |
| mHoldRawBuffers(false), |
| mLastStillTnrSequence(-1), |
| mStatus(PIPELINE_UNCREATED) |
| { |
| LOG1("@%s camera id:%d", __func__, mCameraId); |
| |
| mProcessThread = new ProcessThread(this); |
| CLEAR(mSofTimestamp); |
| } |
| |
| PSysProcessor::~PSysProcessor() |
| { |
| LOG1("@%s ", __func__); |
| |
| mProcessThread->join(); |
| delete mProcessThread; |
| } |
| |
| int PSysProcessor::configure(const std::vector<ConfigMode>& configModes) |
| { |
| //Create PSysDAGs actually |
| LOG1("@%s ", __func__); |
| CheckError(mStatus == PIPELINE_CREATED, -1, "@%s mStatus is in wrong status: PIPELINE_CREATED", __func__); |
| mConfigModes = configModes; |
| mSofSequence = -1; |
| |
| mLastStillTnrSequence = -1; |
| mHoldRawBuffers = false; |
| mOpaqueRawPort = INVALID_PORT; |
| mRawPort = INVALID_PORT; |
| |
| std::map<Port, stream_t> outputFrameInfo; |
| stream_t stillStream = {}, videoStream = {}; |
| for (auto &outFrameInfo : mOutputFrameInfo) { |
| // Check if it's required to output raw image from ISYS |
| if (outFrameInfo.second.format == V4L2_PIX_FMT_SGRBG12) { |
| mRawPort = outFrameInfo.first; |
| } else if (outFrameInfo.second.usage == CAMERA_STREAM_OPAQUE_RAW) { |
| mOpaqueRawPort = outFrameInfo.first; |
| mHoldRawBuffers = true; |
| } else if (outFrameInfo.second.usage == CAMERA_STREAM_STILL_CAPTURE) { |
| stillStream = outFrameInfo.second; |
| outputFrameInfo[outFrameInfo.first] = outFrameInfo.second; |
| } else { |
| videoStream = outFrameInfo.second; |
| outputFrameInfo[outFrameInfo.first] = outFrameInfo.second; |
| } |
| } |
| |
| // Hold raw for SDV case with GPU TNR enabled |
| if (PlatformData::isGpuTnrEnabled() && videoStream.width > 0 && stillStream.width > 0) { |
| // hold raw buffer for running TNR in still pipe |
| mHoldRawBuffers = true; |
| } |
| |
| int ret = OK; |
| //Create PSysDAG according to real configure mode |
| for (auto &cfg : mConfigModes) { |
| if (mPSysDAGs.find(cfg) != mPSysDAGs.end()) { |
| continue; |
| } |
| |
| TuningConfig tuningConfig; |
| ret = PlatformData::getTuningConfigByConfigMode(mCameraId, cfg, tuningConfig); |
| CheckError(ret != OK, ret, "%s: can't get config for mode %d", __func__, cfg); |
| |
| LOG1("Create PSysDAG for ConfigMode %d", cfg); |
| unique_ptr<PSysDAG> pSysDAG = unique_ptr<PSysDAG>(new PSysDAG(mCameraId, this)); |
| |
| pSysDAG->setFrameInfo(mInputFrameInfo, outputFrameInfo); |
| bool useTnrOutBuffer = mOpaqueRawPort != INVALID_PORT; |
| ret = pSysDAG->configure(tuningConfig.configMode, tuningConfig.tuningMode, useTnrOutBuffer); |
| CheckError(ret != OK, ret, "@%s configure psys dag failed:%d", __func__, ret); |
| |
| mPSysDAGs[tuningConfig.configMode] = std::move(pSysDAG); |
| |
| //Update default active config mode |
| mCurConfigMode = tuningConfig.configMode; |
| mTuningMode = tuningConfig.tuningMode; |
| } |
| |
| if (ret == OK) mStatus = PIPELINE_CREATED; |
| return ret; |
| |
| } |
| |
| int PSysProcessor::registerUserOutputBufs(Port port, const shared_ptr<CameraBuffer> &camBuffer) |
| { |
| for (auto &psysDAGPair : mPSysDAGs) { |
| if (!psysDAGPair.second) continue; |
| int ret = psysDAGPair.second->registerUserOutputBufs(port, camBuffer); |
| CheckError(ret != OK, BAD_VALUE, "%s, register user buffer failed, ret: %d", __func__, ret); |
| } |
| |
| return OK; |
| } |
| |
| int PSysProcessor::start() |
| { |
| PERF_CAMERA_ATRACE(); |
| LOG1("%s", __func__); |
| AutoMutex l(mBufferQueueLock); |
| int rawBufferNum = mHoldRawBuffers ? PlatformData::getMaxRawDataNum(mCameraId) : |
| PlatformData::getPreferredBufQSize(mCameraId); |
| |
| /* Should use MIN_BUFFER_COUNT to optimize frame latency when PSYS processing |
| * time is slower than ISYS |
| */ |
| bool needProducerBuffer = PlatformData::isIsysEnabled(mCameraId); |
| |
| if (needProducerBuffer) { |
| int ret = allocProducerBuffers(mCameraId, rawBufferNum); |
| CheckError(ret != OK, NO_MEMORY, "Allocating producer buffer failed:%d", ret); |
| } |
| |
| mThreadRunning = true; |
| mProcessThread->run("PsysProcessor", PRIORITY_NORMAL); |
| for (auto &psysDAGPair : mPSysDAGs) { |
| if (!psysDAGPair.second) continue; |
| psysDAGPair.second->start(); |
| if (needProducerBuffer && PlatformData::isNeedToPreRegisterBuffer(mCameraId)) { |
| psysDAGPair.second->registerInternalBufs(mInternalBuffers); |
| } |
| } |
| |
| return OK; |
| } |
| |
| void PSysProcessor::stop() |
| { |
| PERF_CAMERA_ATRACE(); |
| LOG1("%s", __func__); |
| |
| for (auto &psysDAGPair : mPSysDAGs) { |
| if (!psysDAGPair.second) continue; |
| psysDAGPair.second->stop(); |
| } |
| |
| mProcessThread->requestExit(); |
| { |
| AutoMutex l(mBufferQueueLock); |
| mThreadRunning = false; |
| //Wakeup the thread to exit |
| mFrameAvailableSignal.signal(); |
| mOutputAvailableSignal.signal(); |
| mFrameDoneSignal.signal(); |
| AutoMutex lMeta(mMetaQueueLock); |
| mMetaAvailableSignal.signal(); |
| } |
| { |
| AutoMutex l(mSofLock); |
| mSofCondition.signal(); |
| } |
| |
| mProcessThread->requestExitAndWait(); |
| |
| // Thread is not running. It is safe to clear the Queue |
| clearBufferQueues(); |
| } |
| |
| int PSysProcessor::setParameters(const Parameters& param) |
| { |
| LOG1("%s camera id:%d", __func__, mCameraId); |
| // Process image enhancement related settings. |
| camera_image_enhancement_t enhancement; |
| int ret = param.getImageEnhancement(enhancement); |
| AutoWMutex wl(mIspSettingsLock); |
| if (ret == OK) { |
| mIspSettings.manualSettings.manualSharpness = (char)enhancement.sharpness; |
| mIspSettings.manualSettings.manualBrightness = (char)enhancement.brightness; |
| mIspSettings.manualSettings.manualContrast = (char)enhancement.contrast; |
| mIspSettings.manualSettings.manualHue = (char)enhancement.hue; |
| mIspSettings.manualSettings.manualSaturation = (char)enhancement.saturation; |
| mIspSettings.eeSetting.strength = enhancement.sharpness; |
| } else { |
| mIspSettings.eeSetting.strength = static_cast<char>(DEFAULT_STRENGTH); |
| } |
| |
| camera_edge_mode_t manualEdgeMode; |
| ret = param.getEdgeMode(manualEdgeMode); |
| if (ret == OK) { |
| LOG2("%s: manual edge mode set: %d", __func__, manualEdgeMode); |
| switch (manualEdgeMode) { |
| case EDGE_MODE_OFF: |
| case EDGE_MODE_ZERO_SHUTTER_LAGE: |
| mIspSettings.eeSetting.feature_level = ia_isp_feature_level_high; |
| mIspSettings.eeSetting.strength = static_cast<char>(EXTREME_NEGATIVE_STRENGTH); |
| break; |
| case EDGE_MODE_FAST: |
| mIspSettings.eeSetting.feature_level = ia_isp_feature_level_high; |
| break; |
| case EDGE_MODE_HIGH_QUALITY: |
| mIspSettings.eeSetting.feature_level = ia_isp_feature_level_high; |
| mIspSettings.eeSetting.strength = static_cast<char>(EXTREME_POSITIVE_STRENGTH); |
| break; |
| default: |
| mIspSettings.eeSetting.feature_level = ia_isp_feature_level_high; |
| } |
| } else { |
| LOG2("%s: manual edge mode not set, default enabled", __func__); |
| mIspSettings.eeSetting.feature_level = ia_isp_feature_level_high; |
| } |
| LOG2("%s: ISP EE setting, level: %d, strength: %d", |
| __func__, static_cast<int>(mIspSettings.eeSetting.feature_level), |
| static_cast<int>(mIspSettings.eeSetting.strength)); |
| |
| camera_nr_mode_t manualNrMode; |
| camera_nr_level_t manualNrLevel; |
| |
| int manualNrModeSet = param.getNrMode(manualNrMode); |
| int manualNrLevelSet = param.getNrLevel(manualNrLevel); |
| |
| if (manualNrLevelSet == OK) { |
| mIspSettings.nrSetting.strength = static_cast<char>(manualNrLevel.overall); |
| } else { |
| mIspSettings.nrSetting.strength = static_cast<char>(DEFAULT_STRENGTH); |
| } |
| |
| if (manualNrModeSet == OK) { |
| LOG2("%s: manual NR mode set: %d", __func__, manualNrMode); |
| switch (manualNrMode) { |
| case NR_MODE_OFF: |
| case NR_MODE_AUTO: |
| case NR_MODE_MANUAL_NORMAL: |
| mIspSettings.nrSetting.feature_level = ia_isp_feature_level_high; |
| mIspSettings.nrSetting.strength = static_cast<char>(EXTREME_NEGATIVE_STRENGTH); |
| break; |
| case NR_MODE_MANUAL_EXPERT: |
| mIspSettings.nrSetting.feature_level = ia_isp_feature_level_high; |
| break; |
| case NR_MODE_HIGH_QUALITY: |
| mIspSettings.nrSetting.feature_level = ia_isp_feature_level_high; |
| mIspSettings.nrSetting.strength = static_cast<char>(EXTREME_POSITIVE_STRENGTH); |
| break; |
| default: |
| mIspSettings.nrSetting.feature_level = ia_isp_feature_level_high; |
| } |
| } else { |
| LOG2("%s: manual NR mode not set, default enabled", __func__); |
| mIspSettings.nrSetting.feature_level = ia_isp_feature_level_high; |
| } |
| LOG2("%s: ISP NR setting, level: %d, strength: %d", |
| __func__, static_cast<int>(mIspSettings.nrSetting.feature_level), |
| static_cast<int>(mIspSettings.nrSetting.strength)); |
| |
| camera_video_stabilization_mode_t stabilizationMode; |
| ret = param.getVideoStabilizationMode(stabilizationMode); |
| if (ret == OK) { |
| mIspSettings.videoStabilization = (stabilizationMode == VIDEO_STABILIZATION_MODE_ON); |
| } else { |
| mIspSettings.videoStabilization = false; |
| } |
| LOG2("%s: Video stablilization enabled:%d", __func__, mIspSettings.videoStabilization); |
| |
| return ret; |
| } |
| |
| int PSysProcessor::getParameters(Parameters& param) |
| { |
| LOG1("@%s ", __func__); |
| AutoRMutex rl(mIspSettingsLock); |
| camera_image_enhancement_t enhancement = { mIspSettings.manualSettings.manualSharpness, |
| mIspSettings.manualSettings.manualBrightness, |
| mIspSettings.manualSettings.manualContrast, |
| mIspSettings.manualSettings.manualHue, |
| mIspSettings.manualSettings.manualSaturation }; |
| int ret = param.setImageEnhancement(enhancement); |
| |
| ret |= mPSysDAGs[mCurConfigMode]->getParameters(param); |
| |
| return ret; |
| } |
| |
| /** |
| * Get available setting sequence from outBuf |
| */ |
| long PSysProcessor::getSettingSequence(const CameraBufferPortMap &outBuf) |
| { |
| long settingSequence = -1; |
| for (auto& output: outBuf) { |
| if (output.second) { |
| settingSequence = output.second->getSettingSequence(); |
| break; |
| } |
| } |
| return settingSequence; |
| } |
| |
| /** |
| * Check if the input frame should be skipped |
| * |
| * If the corresponding mSkip of AiqResult gotten from sequence is true, |
| * return true; otherwise return false. |
| */ |
| bool PSysProcessor::needSkipOutputFrame(long sequence) |
| { |
| // Check if need to skip output frame |
| const AiqResult* aiqResults = AiqResultStorage::getInstance(mCameraId)->getAiqResult(sequence); |
| if (aiqResults != nullptr && aiqResults->mSkip) { |
| LOG1("%s, sequence %ld", __func__, sequence); |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Check if 'inBuffer' can be used for 'settingSequence' to run PSys pipe. |
| * |
| * If 'settingSequence' is -1, it means the output buffer doesn't require particular |
| * input buffer, so it can run the pipe. |
| * If 'inputSequence' larger than 'settingSequence', the pipeline needs to |
| * run as well, otherwise the pipe doesn't need to run and this input buffer needs to |
| * be skipped. |
| */ |
| bool PSysProcessor::needExecutePipe(long settingSequence, long inputSequence) |
| { |
| if (settingSequence == -1 || inputSequence >= settingSequence) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Check if the input buffer need to be reused |
| * |
| * If 'settingSequence' is -1, it means the output buffer doesn't require particular |
| * input buffer, so the input buffer doesn't need to be reused. |
| * If 'inputSequence' larger than 'settingSequence', means the input buffer |
| * may be required by following output buffer, so it may be reused later. |
| */ |
| bool PSysProcessor::needHoldOnInputFrame(long settingSequence, long inputSequence) |
| { |
| if (settingSequence == -1 || inputSequence <= settingSequence) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /** |
| * Check if pipe needs to be switched according to AIQ result. |
| */ |
| bool PSysProcessor::needSwitchPipe(long sequence) |
| { |
| const AiqResult* aiqResults = AiqResultStorage::getInstance(mCameraId)->getAiqResult(sequence); |
| if (aiqResults == nullptr) { |
| LOG2("%s: not found sequence %ld in AiqResultStorage, no update for active modes", |
| __func__, sequence); |
| return false; |
| } |
| |
| TuningMode curTuningMode = aiqResults->mTuningMode; |
| LOG2("%s: aiqResults->mTuningMode = %d", __func__, curTuningMode); |
| |
| if (mTuningMode == curTuningMode) { |
| return false; |
| } |
| |
| for (auto cfg : mConfigModes) { |
| TuningMode tMode; |
| int ret = PlatformData::getTuningModeByConfigMode(mCameraId, cfg, tMode); |
| if (ret == OK && tMode == curTuningMode) { |
| mCurConfigMode = cfg; |
| mTuningMode = curTuningMode; |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| void PSysProcessor::handleEvent(EventData eventData) |
| { |
| LOG2("%s: got event type %d", __func__, eventData.type); |
| // Process registered events |
| switch (eventData.type) { |
| case EVENT_ISYS_SOF: |
| { |
| AutoMutex l(mSofLock); |
| |
| mSofSequence = eventData.data.sync.sequence; |
| gettimeofday(&mSofTimestamp, nullptr); |
| LOG2("%s, received SOF event sequence: %ld, timestamp: %ld", |
| __func__, eventData.data.sync.sequence, TIMEVAL2USECS(mSofTimestamp)); |
| mSofCondition.signal(); |
| } |
| break; |
| default: |
| LOGW("Unexpected event: %d", eventData.type); |
| break; |
| } |
| } |
| |
| // PSysProcessor ThreadLoop |
| int PSysProcessor::processNewFrame() { |
| PERF_CAMERA_ATRACE(); |
| LOG2("@%s, mCameraId:%d", __func__, mCameraId); |
| |
| CheckError(!mBufferProducer, INVALID_OPERATION, "No available producer"); |
| |
| int ret = OK; |
| CameraBufferPortMap srcBuffers, dstBuffers; |
| |
| if (!PlatformData::psysAlignWithSof(mCameraId)) { |
| { |
| ConditionLock lock(mBufferQueueLock); |
| ret = waitFreeBuffersInQueue(lock, srcBuffers, dstBuffers); |
| // Already stopped |
| if (!mThreadRunning) return -1; |
| |
| // Wait frame buffer time out should not involve thread exit. |
| if (ret != OK) { |
| LOG1("%s, cameraId: %d timeout happen, wait recovery", __func__, mCameraId); |
| return OK; |
| } |
| } |
| |
| ret = prepareTask(&srcBuffers, &dstBuffers); |
| CheckError(ret != OK, UNKNOWN_ERROR, "%s, Failed to process frame", __func__); |
| } else { |
| { |
| ConditionLock lock(mSofLock); |
| |
| timeval curTime; |
| gettimeofday(&curTime, nullptr); |
| int64_t sofInterval = TIMEVAL2NSECS(curTime) - TIMEVAL2NSECS(mSofTimestamp); |
| |
| // Wait next sof event when missing last one for a long time |
| if (sofInterval > SOF_EVENT_MARGIN && sofInterval < SOF_EVENT_MAX_MARGIN) { |
| LOG2("%s, need to wait next sof event. sofInterval: %ld", __func__, sofInterval); |
| ret = mSofCondition.waitRelative(lock, kWaitDuration * SLOWLY_MULTIPLIER); |
| |
| // Already stopped |
| if (!mThreadRunning) return -1; |
| |
| // Wait sof event time out should not involve thread exit. |
| if (ret != OK) { |
| LOG1("%s, cameraId: %d wait sof event timeout, recovery", __func__, mCameraId); |
| return OK; |
| } |
| } |
| } |
| |
| // push all the pending buffers to task |
| while (true) { |
| { |
| ConditionLock lock(mBufferQueueLock); |
| ret = waitFreeBuffersInQueue(lock, srcBuffers, dstBuffers, SOF_EVENT_MARGIN); |
| |
| // Return to wait next sof event if there isn't pending buffer. |
| if (ret != OK) { |
| LOG1("%s, cameraId: %d, there isn't pending buffer, recovery", |
| __func__, mCameraId); |
| return OK; |
| } |
| } |
| |
| { |
| AutoMutex l(mSofLock); |
| if (srcBuffers.begin()->second->getSequence() >= mSofSequence) { |
| LOG2("%s, run the frame in next sof: buffer sequence: %ld, sof sequence: %ld", |
| __func__, srcBuffers.begin()->second->getSequence(), mSofSequence); |
| return OK; |
| } |
| } |
| |
| ret = prepareTask(&srcBuffers, &dstBuffers); |
| CheckError(ret != OK, UNKNOWN_ERROR, "%s, Failed to process frame", __func__); |
| } |
| } |
| |
| return OK; |
| } |
| |
| void PSysProcessor::handleRawReprocessing(CameraBufferPortMap *srcBuffers, |
| CameraBufferPortMap *dstBuffers, bool *allBufDone, |
| bool *hasRawOutput, bool *hasRawInput) |
| { |
| std::shared_ptr<CameraBuffer> rawOutputBuffer = nullptr; |
| long settingSequence = -1; |
| CameraBufferPortMap videoBuf, stillBuf; |
| |
| for (const auto& item : *dstBuffers) { |
| if (item.second) { |
| if (item.second->getStreamUsage() == CAMERA_STREAM_OPAQUE_RAW) { |
| rawOutputBuffer = item.second; |
| } else if (item.second->getStreamUsage() == CAMERA_STREAM_STILL_CAPTURE) { |
| stillBuf[item.first] = item.second; |
| } else { |
| videoBuf[item.first] = item.second; |
| } |
| if (item.second->getSettingSequence() >= 0) { |
| settingSequence = item.second->getSettingSequence(); |
| } |
| } |
| } |
| |
| Port defaultPort = srcBuffers->begin()->first; |
| shared_ptr<CameraBuffer> mainBuf = (*srcBuffers)[defaultPort]; |
| long inputSequence = mainBuf->getSequence(); |
| |
| if (rawOutputBuffer) { |
| if (!needExecutePipe(settingSequence, inputSequence)) { |
| LOG2("%s, inputSequence %ld is smaller than settingSequence %ld, skip sensor frame.", |
| __func__, inputSequence, settingSequence); |
| return; |
| } |
| // Return opaque RAW data |
| uint64_t timestamp = TIMEVAL2NSECS(mainBuf->getTimestamp()); |
| sensor_raw_info_t opaqueRawInfo = { inputSequence, timestamp }; |
| |
| rawOutputBuffer->updateV4l2Buffer(*mainBuf->getV4L2Buffer().Get()); |
| |
| MEMCPY_S(rawOutputBuffer->getBufferAddr(), rawOutputBuffer->getBufferSize(), |
| &opaqueRawInfo, sizeof(opaqueRawInfo)); |
| LOG2("%s, timestamp %ld, inputSequence %ld, dstBufferSize %d, addr %p", __func__, |
| timestamp, inputSequence, rawOutputBuffer->getBufferSize(), |
| rawOutputBuffer->getBufferAddr()); |
| |
| // handle Shutter first |
| sendPsysBufReadyEvent(dstBuffers, settingSequence, timestamp); |
| |
| // Return opaque RAW buffer |
| for (auto &it : mBufferConsumerList) { |
| it->onFrameAvailable(mOpaqueRawPort, rawOutputBuffer); |
| } |
| *hasRawOutput = true; |
| |
| PlatformData::updateMakernoteTimeStamp(mCameraId, settingSequence, timestamp); |
| |
| if (stillBuf.empty() && videoBuf.empty()) { |
| *allBufDone = true; |
| } else { |
| // Remove from dstBuffers map |
| dstBuffers->erase(mOpaqueRawPort); |
| } |
| } else if (settingSequence != -1 && inputSequence > settingSequence) { |
| // handle Shutter first |
| sendPsysBufReadyEvent(dstBuffers, settingSequence, 0); |
| |
| // only one video buffer is supported |
| if (PlatformData::isGpuTnrEnabled() && videoBuf.size() == 1) { |
| shared_ptr<CameraBuffer> buf = videoBuf.begin()->second; |
| bool handled = mPSysDAGs[mCurConfigMode]->fetchTnrOutBuffer(settingSequence, buf); |
| if (handled) { |
| LOG2("%s, settingSequence %ld is found", __func__, settingSequence); |
| if (stillBuf.empty()) { |
| *hasRawInput = true; |
| *allBufDone = true; |
| } else { |
| (*dstBuffers)[videoBuf.begin()->first] = nullptr; |
| } |
| onBufferDone(settingSequence, videoBuf.begin()->first, buf); |
| } |
| } |
| if (mHoldRawBuffers && !(*allBufDone)) { |
| AutoMutex lock(mBufferMapLock); |
| // Find Raw buffer in mRawBufferMap |
| if (mRawBufferMap.find(settingSequence) != mRawBufferMap.end()) { |
| CameraBufferPortMap &mapBuf = mRawBufferMap[settingSequence]; |
| // Update source buffers |
| for (const auto& bufPortMap : mapBuf) { |
| (*srcBuffers)[bufPortMap.first] = bufPortMap.second; |
| } |
| *hasRawInput = true; |
| } |
| } |
| } |
| |
| LOG2("%s, hasRawInput %d, hasRawOutput %d, allBufDone %d, settingSequence %ld, inputSequence %ld", |
| __func__, *hasRawInput, *hasRawOutput, *allBufDone, settingSequence, inputSequence); |
| } |
| |
| bool PSysProcessor::isBufferHoldForRawReprocess(long sequence) |
| { |
| if (!mHoldRawBuffers) return false; |
| |
| AutoMutex lock(mBufferMapLock); |
| if (mRawBufferMap.find(sequence) == mRawBufferMap.end()) return false; |
| |
| return true; |
| } |
| |
| void PSysProcessor::saveRawBuffer(CameraBufferPortMap *srcBuffers) |
| { |
| // Save buffer into mRawBufferMap |
| CameraBufferPortMap mapBuf; |
| for (const auto& src : *srcBuffers) { |
| mapBuf[src.first] = src.second; |
| } |
| |
| Port defaultPort = srcBuffers->begin()->first; |
| shared_ptr<CameraBuffer> mainBuf = (*srcBuffers)[defaultPort]; |
| long inputSequence = mainBuf->getSequence(); |
| |
| LOG2("@%s, mCameraId:%d, inputSequence %ld", __func__, mCameraId, inputSequence); |
| |
| { |
| AutoMutex lock(mBufferMapLock); |
| mRawBufferMap[inputSequence] = mapBuf; |
| } |
| |
| returnRawBuffer(); |
| } |
| |
| void PSysProcessor::returnRawBuffer() |
| { |
| AutoMutex lock(mBufferMapLock); |
| // If too many buffers are holden in mRawQueue, return back to producer |
| if (mRawBufferMap.size() > (PlatformData::getMaxRawDataNum(mCameraId) - |
| PlatformData::getMaxRequestsInflight(mCameraId))) { |
| auto it = mRawBufferMap.cbegin(); |
| { |
| AutoMutex l(mBufferQueueLock); |
| if (mSequencesInflight.find(it->first) != mSequencesInflight.end()) { |
| // Raw buffer is in used and return it later |
| return; |
| } |
| } |
| |
| const CameraBufferPortMap &bufferPortMap = it->second; |
| for (auto &item : bufferPortMap) { |
| mBufferProducer->qbuf(item.first, item.second); |
| } |
| LOG2("@%s, returned sequence %ld", __func__, it->first); |
| mRawBufferMap.erase(it); |
| } |
| } |
| |
| status_t PSysProcessor::prepareTask(CameraBufferPortMap *srcBuffers, |
| CameraBufferPortMap *dstBuffers) { |
| CheckError(srcBuffers->empty() || dstBuffers->empty(), |
| UNKNOWN_ERROR, "%s, the input or output buffer is empty", __func__); |
| if (mHoldRawBuffers && mOpaqueRawPort == INVALID_PORT) { |
| saveRawBuffer(srcBuffers); |
| } |
| |
| bool allBufDone = false; |
| bool hasRawOutput = false; |
| bool hasRawInput = false; |
| if (mOpaqueRawPort != INVALID_PORT) { |
| handleRawReprocessing(srcBuffers, dstBuffers, &allBufDone, &hasRawOutput, &hasRawInput); |
| if (hasRawOutput) { |
| saveRawBuffer(srcBuffers); |
| } |
| if (allBufDone) { |
| sendPsysFrameDoneEvent(dstBuffers); |
| // If all buffers are handled |
| AutoMutex l(mBufferQueueLock); |
| if (hasRawOutput) { |
| for (auto& input: mInputQueue) { |
| input.second.pop(); |
| } |
| } |
| for (auto& output: mOutputQueue) { |
| output.second.pop(); |
| } |
| return OK; |
| } |
| } |
| |
| Port defaultPort = srcBuffers->begin()->first; |
| shared_ptr<CameraBuffer> mainBuf = (*srcBuffers)[defaultPort]; |
| long inputSequence = mainBuf->getSequence(); |
| TRACE_LOG_POINT("PSysProcessor", "input output buffer ready", MAKE_COLOR(inputSequence), |
| inputSequence); |
| uint64_t timestamp = TIMEVAL2NSECS(mainBuf->getTimestamp()); |
| LOG2("%s: input buffer sequence %ld timestamp %ld", __func__, inputSequence, timestamp); |
| |
| // Output raw image |
| if (mRawPort != INVALID_PORT) { |
| shared_ptr<CameraBuffer> dstBuf = nullptr; |
| |
| // Get output buffer and remove it from dstBuffers |
| for (auto &buffer : *dstBuffers) { |
| if (buffer.first == mRawPort) { |
| dstBuf = buffer.second; |
| CheckError(!dstBuf, UNKNOWN_ERROR, "%s, dstBuf for output raw is null", __func__); |
| dstBuf->updateV4l2Buffer(*mainBuf->getV4L2Buffer().Get()); |
| dstBuffers->erase(mRawPort); |
| break; |
| } |
| } |
| outputRawImage(mainBuf, dstBuf); |
| } |
| |
| long settingSequence = getSettingSequence(*dstBuffers); |
| bool needRunPipe = needExecutePipe(settingSequence, inputSequence); |
| bool holdOnInput = needHoldOnInputFrame(settingSequence, inputSequence); |
| LOG2("%s: dst sequence = %ld, src sequence = %ld, needRunPipe = %d, needReuseInput = %d", |
| __func__, settingSequence, inputSequence, needRunPipe, holdOnInput); |
| |
| { |
| AutoMutex l(mBufferQueueLock); |
| if (needRunPipe && !needSkipOutputFrame(inputSequence)) { |
| for (auto& output: mOutputQueue) { |
| output.second.pop(); |
| } |
| } |
| |
| // If input buffer will be used later, don't pop it from the queue. |
| if (!holdOnInput && !hasRawInput) { |
| for (auto& input: mInputQueue) { |
| input.second.pop(); |
| } |
| } |
| } |
| |
| if (needRunPipe) { |
| // Raw output already has been returned back, and don't need to handle again. |
| if (!hasRawOutput) { |
| sendPsysBufReadyEvent(dstBuffers, settingSequence, timestamp); |
| } |
| if (PlatformData::isGpuTnrEnabled()) { |
| handleStillPipeForTnr(inputSequence, dstBuffers); |
| } |
| dispatchTask(*srcBuffers, *dstBuffers); |
| } else if (!holdOnInput && !isBufferHoldForRawReprocess(inputSequence)) { |
| for (const auto& src : *srcBuffers) { |
| mBufferProducer->qbuf(src.first, src.second); |
| } |
| } |
| |
| return OK; |
| } |
| |
| void PSysProcessor::handleStillPipeForTnr(long sequence, CameraBufferPortMap *dstBuffers) |
| { |
| bool hasStill = false; |
| for (const auto& item : *dstBuffers) { |
| if (item.second && item.second->getStreamUsage() == CAMERA_STREAM_STILL_CAPTURE) { |
| hasStill = true; |
| break; |
| } |
| } |
| LOG2("@%s, seq %ld, hold raw %d, last still seq %ld, still %d", __func__, sequence, |
| mHoldRawBuffers, mLastStillTnrSequence, hasStill); |
| |
| bool bypass = mPSysDAGs[mCurConfigMode]->isBypassStillTnr(sequence); |
| if (!bypass && hasStill && sequence != (mLastStillTnrSequence + 1) && mHoldRawBuffers) { |
| CameraBufferPortMap fakeTaskBuffers = *dstBuffers; |
| for (const auto& item : fakeTaskBuffers) { |
| if (item.second && item.second->getStreamUsage() != CAMERA_STREAM_STILL_CAPTURE) { |
| fakeTaskBuffers[item.first] = nullptr; |
| } |
| } |
| for (int i = PlatformData::getTnrExtraFrameCount(mCameraId); i > 0; i--) { |
| CameraBufferPortMap srcBuf; |
| { |
| AutoMutex lock(mBufferMapLock); |
| if (sequence - i != mLastStillTnrSequence && |
| mRawBufferMap.find(sequence - i) != mRawBufferMap.end()) { |
| for (const auto& item : mRawBufferMap[sequence - i]) { |
| srcBuf[item.first] = item.second; |
| } |
| } |
| } |
| if (!srcBuf.empty()) { |
| dispatchTask(srcBuf, fakeTaskBuffers, true); |
| } |
| } |
| } |
| |
| if (hasStill) mLastStillTnrSequence = sequence; |
| } |
| |
| void PSysProcessor::dispatchTask(CameraBufferPortMap &inBuf, CameraBufferPortMap &outBuf, |
| bool fakeTask) |
| { |
| long currentSequence = inBuf.begin()->second->getSequence(); |
| TRACE_LOG_POINT("PSysProcessor", "start run PSYS", MAKE_COLOR(currentSequence), |
| currentSequence); |
| |
| LOG2("@%s, mCameraId:%d, seq %ld, fake task %d", __func__, mCameraId, currentSequence, fakeTask); |
| |
| { |
| ConditionLock lock(mBufferQueueLock); |
| |
| ConfigMode previousMode = mCurConfigMode; |
| bool needSwitch = needSwitchPipe(currentSequence); |
| |
| if (needSwitch) { |
| LOG1("Switch pipe for sequence:%ld, unprocessed buffer number:%zu", |
| currentSequence, mSequencesInflight.size()); |
| |
| // Deactive the PSysDag which is no longer used. |
| mPSysDAGs[previousMode]->pause(); |
| |
| // Before switching, need to wait all buffers in current pipe being processed. |
| while (!mSequencesInflight.empty()) { |
| int ret = mFrameDoneSignal.waitRelative(lock, kWaitDuration * SLOWLY_MULTIPLIER); |
| if (!mThreadRunning) { |
| LOG1("@%s: Processor is not active while waiting for frame done.", __func__); |
| return; |
| } |
| |
| if (ret == TIMED_OUT) { |
| LOGE("Waiting for frame done event timeout"); |
| return; |
| } |
| } |
| |
| // Activate the current used PSysDag. |
| mPSysDAGs[mCurConfigMode]->resume(); |
| } |
| mSequencesInflight.insert(currentSequence); |
| } // End of lock mBufferQueueLock |
| |
| // Prepare the task input paramerters including input and output buffers, settings etc. |
| PSysTaskData taskParam; |
| taskParam.mTuningMode = mTuningMode; |
| taskParam.mInputBuffers = inBuf; |
| taskParam.mOutputBuffers = outBuf; |
| taskParam.mFakeTask = fakeTask; |
| |
| long settingSequence = getSettingSequence(outBuf); |
| // Handle per-frame settings if output buffer requires |
| if (settingSequence > -1 && mParameterGenerator) { |
| Parameters params; |
| if (mParameterGenerator->getParameters(currentSequence, ¶ms, false) == OK) { |
| setParameters(params); |
| |
| // Dump raw image if makernote mode is MAKERNOTE_MODE_JPEG for IQ tune |
| camera_makernote_mode_t makernoteMode = MAKERNOTE_MODE_OFF; |
| int ret = params.getMakernoteMode(makernoteMode); |
| if (ret == OK && makernoteMode == MAKERNOTE_MODE_JPEG && |
| !fakeTask && CameraDump::isDumpTypeEnable(DUMP_JPEG_BUFFER)) { |
| CameraDump::dumpImage(mCameraId, inBuf[MAIN_PORT], M_PSYS, MAIN_PORT); |
| } |
| } |
| } |
| { |
| AutoRMutex rl(mIspSettingsLock); |
| mIspSettings.palOverride = nullptr; |
| taskParam.mIspSettings = mIspSettings; |
| } |
| |
| if (!mThreadRunning) return; |
| |
| mPSysDAGs[mCurConfigMode]->addTask(taskParam); |
| } |
| |
| void PSysProcessor::registerListener(EventType eventType, EventListener* eventListener) |
| { |
| // Only delegate stats event registration to deeper layer DAG and PipeExecutor |
| if ((eventType != EVENT_PSYS_STATS_BUF_READY) && (eventType != EVENT_PSYS_STATS_SIS_BUF_READY)) { |
| BufferQueue::registerListener(eventType, eventListener); |
| return; |
| } |
| |
| for (auto const& realModeDAGPair: mPSysDAGs) { |
| realModeDAGPair.second->registerListener(eventType, eventListener); |
| } |
| } |
| |
| void PSysProcessor::removeListener(EventType eventType, EventListener* eventListener) |
| { |
| // Only delegate stats event unregistration to deeper layer DAG and PipeExecutor |
| if ((eventType != EVENT_PSYS_STATS_BUF_READY) && (eventType != EVENT_PSYS_STATS_SIS_BUF_READY)) { |
| BufferQueue::removeListener(eventType, eventListener); |
| return; |
| } |
| |
| for (auto const& realModeDAGPair: mPSysDAGs) { |
| realModeDAGPair.second->removeListener(eventType, eventListener); |
| } |
| } |
| |
| void PSysProcessor::onBufferDone(int64_t sequence, Port port, |
| const std::shared_ptr<CameraBuffer> &camBuffer) |
| { |
| LOG2("@%s, mCameraId:%d, sequence %ld, port %d", __func__, mCameraId, sequence, port); |
| |
| if (CameraDump::isDumpTypeEnable(DUMP_PSYS_OUTPUT_BUFFER)) { |
| CameraDump::dumpImage(mCameraId, camBuffer, M_PSYS, port); |
| } |
| |
| if (!needSkipOutputFrame(sequence)) { |
| for (auto &it : mBufferConsumerList) { |
| it->onFrameAvailable(port, camBuffer); |
| } |
| } |
| } |
| |
| void PSysProcessor::sendPsysFrameDoneEvent(const CameraBufferPortMap* dstBuffers) |
| { |
| for (auto& dst : *dstBuffers) { |
| shared_ptr<CameraBuffer> outBuf = dst.second; |
| if (!outBuf || outBuf->getSequence() < 0) { |
| continue; |
| } |
| |
| EventData frameData; |
| frameData.type = EVENT_PSYS_FRAME; |
| frameData.buffer = nullptr; |
| frameData.data.frame.sequence = outBuf->getSequence(); |
| frameData.data.frame.timestamp.tv_sec = outBuf->getTimestamp().tv_sec; |
| frameData.data.frame.timestamp.tv_usec = outBuf->getTimestamp().tv_usec; |
| notifyListeners(frameData); |
| |
| LOG2("%s, frame done for sequence: %ld", __func__, frameData.data.frame.sequence); |
| break; |
| } |
| } |
| |
| void PSysProcessor::sendPsysBufReadyEvent(const CameraBufferPortMap* dstBuffers, |
| int64_t sequence, uint64_t timestamp) |
| { |
| for (const auto& output : *dstBuffers) { |
| if (output.second && output.second->getUsage() != BUFFER_USAGE_PSYS_INTERNAL) { |
| EventData event; |
| event.type = EVENT_PSYS_REQUEST_BUF_READY; |
| event.buffer = nullptr; |
| event.data.requestReady.timestamp |
| = timestamp > 0 ? timestamp : output.second->getUserBuffer()->timestamp; |
| event.data.requestReady.sequence = sequence; |
| notifyListeners(event); |
| break; |
| } |
| } |
| } |
| void PSysProcessor::onFrameDone(const PSysTaskData& result) |
| { |
| PERF_CAMERA_ATRACE(); |
| |
| long sequence = result.mInputBuffers.begin()->second->getSequence(); |
| LOG2("@%s, mCameraId:%d, sequence %ld", __func__, mCameraId, sequence); |
| TRACE_LOG_POINT("PSysProcessor", __func__, MAKE_COLOR(sequence), sequence); |
| |
| if (!result.mFakeTask) { |
| if (!needSkipOutputFrame(sequence)) { |
| sendPsysFrameDoneEvent(&result.mOutputBuffers); |
| } |
| |
| long settingSequence = getSettingSequence(result.mOutputBuffers); |
| bool holdOnInput = needHoldOnInputFrame(settingSequence, sequence); |
| bool hasRawOutput = isBufferHoldForRawReprocess(sequence); |
| |
| // Return buffer only if the buffer is not used in the future. |
| if (!holdOnInput && mBufferProducer && !hasRawOutput) { |
| for (const auto& src : result.mInputBuffers) { |
| mBufferProducer->qbuf(src.first, src.second); |
| |
| if (src.second->getStreamType() == CAMERA_STREAM_INPUT) { |
| for (auto &it : mBufferConsumerList) { |
| it->onFrameAvailable(src.first, src.second); |
| } |
| } |
| } |
| } |
| } |
| |
| { |
| AutoMutex l(mBufferQueueLock); |
| std::multiset<int64_t>::iterator it = mSequencesInflight.find(sequence); |
| if (it != mSequencesInflight.end()) { |
| mSequencesInflight.erase(it); |
| } |
| |
| if (mSequencesInflight.empty()) { |
| mFrameDoneSignal.signal(); |
| } |
| } |
| |
| returnRawBuffer(); |
| } |
| |
| void PSysProcessor::outputRawImage(shared_ptr<CameraBuffer> &srcBuf, shared_ptr<CameraBuffer> &dstBuf) |
| { |
| if ((srcBuf == nullptr) || (dstBuf == nullptr)) { |
| return; |
| } |
| |
| // Copy from source buffer |
| int srcBufferSize = srcBuf->getBufferSize(); |
| int srcMemoryType = srcBuf->getMemory(); |
| void* pSrcBuf = (srcMemoryType == V4L2_MEMORY_DMABUF) |
| ? CameraBuffer::mapDmaBufferAddr(srcBuf->getFd(), srcBufferSize) |
| : srcBuf->getBufferAddr(); |
| |
| int dstBufferSize = dstBuf->getBufferSize(); |
| int dstMemoryType = dstBuf->getMemory(); |
| void* pDstBuf = (dstMemoryType == V4L2_MEMORY_DMABUF) |
| ? CameraBuffer::mapDmaBufferAddr(dstBuf->getFd(), dstBufferSize) |
| : dstBuf->getBufferAddr(); |
| |
| MEMCPY_S(pDstBuf, dstBufferSize, pSrcBuf, srcBufferSize); |
| |
| if (srcMemoryType == V4L2_MEMORY_DMABUF) { |
| CameraBuffer::unmapDmaBufferAddr(pSrcBuf, srcBufferSize); |
| } |
| |
| if (dstMemoryType == V4L2_MEMORY_DMABUF) { |
| CameraBuffer::unmapDmaBufferAddr(pDstBuf, dstBufferSize); |
| } |
| |
| // Send output buffer to its consumer |
| for (auto &it : mBufferConsumerList) { |
| it->onFrameAvailable(mRawPort, dstBuf); |
| } |
| } |
| |
| } //namespace icamera |