| /* |
| ** |
| ** Copyright 2016, Samsung 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_NDEBUG 0 */ |
| #define LOG_TAG "ExynosCameraPipeSync" |
| #include <cutils/log.h> |
| |
| #include "ExynosCameraPipeSync.h" |
| |
| namespace android { |
| |
| ExynosCameraPipeSync::~ExynosCameraPipeSync() |
| { |
| this->destroy(); |
| } |
| |
| status_t ExynosCameraPipeSync::create(__unused int32_t *sensorIds) |
| { |
| CLOGD(""); |
| |
| status_t ret; |
| |
| ret = ExynosCameraSWPipe::create(sensorIds); |
| if (ret < 0) { |
| CLOGE("startThread fail"); |
| return ret; |
| } |
| |
| char SuperClassName[EXYNOS_CAMERA_NAME_STR_SIZE] = {0,}; |
| snprintf(SuperClassName, sizeof(SuperClassName), "%s_%s_SWThread", m_name, "Sel"); |
| m_selectThread = ExynosCameraThreadFactory::createThread(this, &ExynosCameraPipeSync::m_selectThreadFunc, SuperClassName); |
| |
| snprintf(SuperClassName, sizeof(SuperClassName), "%s_%s_SWThread", m_name, "Remove"); |
| m_removeFrameThread = ExynosCameraThreadFactory::createThread(this, &ExynosCameraPipeSync::m_removeFrameThreadFunc, SuperClassName); |
| |
| m_lastTimeStamp = 0; |
| |
| m_inputFrameQ->setWaitTime(5000000000); |
| |
| CLOGI("create() is succeed (%d) M:%d, S:%d", getPipeId(), m_masterCameraId, m_slaveCameraId); |
| |
| return ret; |
| } |
| |
| status_t ExynosCameraPipeSync::destroy(void) |
| { |
| CLOGD(""); |
| |
| status_t ret; |
| |
| ret = ExynosCameraSWPipe::release(); |
| if (ret < 0) { |
| CLOGE("release fail"); |
| return ret; |
| } |
| |
| if (m_dualSelector != NULL) { |
| m_dualSelector->deinit(m_cameraId); |
| m_dualSelector = NULL; |
| } |
| |
| if (m_outputFrameQ != NULL) { |
| m_outputFrameQ->release(); |
| m_outputFrameQ = NULL; |
| } |
| |
| CLOGI("destroy() is succeed (%d)", getPipeId()); |
| |
| return ret; |
| } |
| |
| status_t ExynosCameraPipeSync::start(void) |
| { |
| CLOGD(""); |
| |
| m_flagTryStop = false; |
| |
| return NO_ERROR; |
| } |
| |
| status_t ExynosCameraPipeSync::stop(void) |
| { |
| CLOGD(""); |
| |
| status_t ret = NO_ERROR; |
| |
| m_flagTryStop = true; |
| |
| m_mainThread->requestExitAndWait(); |
| m_selectThread->requestExitAndWait(); |
| m_removeFrameThread->requestExitAndWait(); |
| |
| m_dualSelector->flush(m_cameraId); |
| |
| CLOGD(" thead exited"); |
| |
| if (m_inputFrameQ != NULL) |
| m_inputFrameQ->release(); |
| |
| if (m_outputFrameQ != NULL) |
| m_outputFrameQ->release(); |
| |
| if (m_notifyQ != NULL) |
| (m_notifyQ)->release(); |
| |
| if (m_removeFrameQ != NULL) |
| (m_removeFrameQ)->release(); |
| |
| m_flagTryStop = false; |
| |
| return ret; |
| } |
| |
| status_t ExynosCameraPipeSync::setBufferManager(ExynosCameraBufferManager **bufferManager) |
| { |
| CLOGD(""); |
| |
| status_t ret = NO_ERROR; |
| |
| for (int i = 0; i < MAX_NODE; i++) { |
| m_bufferManager[i] = bufferManager[i]; |
| |
| if (m_bufferManager[i] != NULL && m_dualSelector != NULL) { |
| CLOGI("bufferManager registered(%d)", m_bufferManager[i]->getAllocatedBufferCount()); |
| m_bufferManager[i]->dump(); |
| |
| m_dualSelector->deinit(m_cameraId); |
| |
| ret = m_dualSelector->registerNotifyQ(m_cameraId, &m_notifyQ); |
| if (ret != NO_ERROR) |
| CLOG_ASSERT("m_dualSelector->registerNotifyQ is fail"); |
| |
| ret = m_dualSelector->registerRemoveFrameQ(m_cameraId, &m_removeFrameQ); |
| if (ret != NO_ERROR) |
| CLOG_ASSERT("m_dualSelector->registerNotifyQ is fail"); |
| |
| ret = m_dualSelector->setInfo(m_cameraId, m_parameters, m_bufferManager[i]); |
| if (ret != NO_ERROR) |
| CLOG_ASSERT("m_dualSelector->setInfo is fail"); |
| |
| /* only master set ther hold count */ |
| if (m_cameraId == m_masterCameraId) { |
| ret = m_dualSelector->setFrameHoldCount(m_cameraId, m_bufferManager[i]->getNumOfAllowedMaxBuffer()); |
| if (ret != NO_ERROR) |
| CLOG_ASSERT("m_dualSelector->setFrameHoldCount is fail", m_bufferManager[i]->getAllocatedBufferCount()); |
| } |
| } |
| } |
| |
| return ret; |
| } |
| |
| status_t ExynosCameraPipeSync::startThread(void) |
| { |
| CLOGD(""); |
| |
| status_t ret = NO_ERROR; |
| |
| m_mainThread->run(m_name); |
| CLOGD("mainThread start"); |
| |
| /* only master camera's sync pipe run the selectThread */ |
| if (m_cameraId == m_masterCameraId) { |
| CLOGD("selectThread start(M:%d)", m_masterCameraId); |
| m_selectThread->run(m_name); |
| CLOGD("removeThread start(M:%d)", m_masterCameraId); |
| m_removeFrameThread->run(m_name); |
| } else { |
| CLOGD("don't start selectThread(M:%d)", m_masterCameraId); |
| } |
| |
| CLOGI("startThread is succeed (%d)", getPipeId()); |
| |
| return ret; |
| } |
| |
| status_t ExynosCameraPipeSync::stopThread(void) |
| { |
| CLOGD(""); |
| |
| status_t ret = NO_ERROR; |
| |
| m_mainThread->requestExit(); |
| m_selectThread->requestExit(); |
| m_removeFrameThread->requestExit(); |
| |
| if (m_inputFrameQ != NULL) |
| m_inputFrameQ->sendCmd(WAKE_UP); |
| |
| if (m_outputFrameQ != NULL) |
| m_outputFrameQ->sendCmd(WAKE_UP); |
| |
| if (m_removeFrameQ != NULL) |
| m_removeFrameQ->sendCmd(WAKE_UP); |
| |
| CLOGI("stopThread is succeed (%d)", getPipeId()); |
| |
| return ret; |
| } |
| |
| void ExynosCameraPipeSync::setDualSelector(ExynosCameraDualFrameSelector *dualSelector) |
| { |
| CLOGD(""); |
| |
| status_t ret = NO_ERROR; |
| |
| m_dualSelector = dualSelector; |
| |
| /* register the notifyQ, removeFrameQ from dual selector(only master camera) */ |
| if (m_cameraId == m_masterCameraId) { |
| m_dualSelector->flush(m_cameraId); |
| if (ret != NO_ERROR) |
| CLOG_ASSERT("im_dualSelector->flush() is fail"); |
| } |
| } |
| |
| status_t ExynosCameraPipeSync::m_run(void) |
| { |
| static int internalLogCount[CAMERA_ID_MAX]; |
| status_t ret = 0; |
| bool isSrc = true; |
| bool needSync = false; |
| ExynosCameraFrameSP_sptr_t newFrame = NULL; |
| ExynosCameraFrameEntity *entity = NULL; |
| |
| ret = m_inputFrameQ->waitAndPopProcessQ(&newFrame); |
| if (ret < 0) { |
| /* TODO: We need to make timeout duration depends on FPS */ |
| if (ret == TIMED_OUT) { |
| CLOGW("wait timeout"); |
| } else { |
| CLOGE("wait and pop fail, ret(%d)", ret); |
| /* TODO: doing exception handling */ |
| } |
| return ret; |
| } |
| |
| if (newFrame == NULL) { |
| CLOGE("new frame is NULL"); |
| return NO_ERROR; |
| } |
| |
| if (m_dualSelector == NULL) { |
| CLOGE("m_dualSelector is NULL"); |
| return NO_ERROR; |
| } |
| |
| /* check the frame type */ |
| switch (newFrame->getFrameType()) { |
| case FRAME_TYPE_INTERNAL: |
| if ((internalLogCount[m_cameraId]++ % 100) == 0) { |
| CLOGI("[INTERNAL_FRAME] frame(%d) type(%d), (%d)", |
| newFrame->getFrameCount(), newFrame->getFrameType(), internalLogCount[m_cameraId]); |
| } |
| goto func_internal_type; |
| break; |
| default: |
| internalLogCount[m_cameraId] = 0; |
| break; |
| } |
| |
| /* check the frame state */ |
| switch (newFrame->getFrameState()) { |
| case FRAME_STATE_SKIPPED: |
| case FRAME_STATE_INVALID: |
| CLOGE("frame(%d) state is invalid(%d)", |
| newFrame->getFrameCount(), newFrame->getFrameState()); |
| goto func_exit; |
| break; |
| default: |
| break; |
| } |
| |
| func_internal_type: |
| /* push the frame to dual selector */ |
| switch (newFrame->getSyncType()) { |
| case SYNC_TYPE_BYPASS: |
| if (m_cameraId == m_slaveCameraId) |
| CLOG_ASSERT("invalid cameraId(%d != %d) frame(%d)", m_cameraId, m_slaveCameraId, newFrame->getFrameCount()); |
| |
| needSync = false; |
| break; |
| case SYNC_TYPE_SYNC: |
| needSync = true; |
| break; |
| case SYNC_TYPE_SWITCH: |
| if (m_cameraId == m_masterCameraId) |
| CLOG_ASSERT("invalid cameraId(%d != %d) frame(%d)", m_cameraId, m_masterCameraId, newFrame->getFrameCount()); |
| |
| needSync = false; |
| break; |
| default: |
| CLOG_ASSERT("invalid sync type(%d) frame(%d)", m_cameraId, newFrame->getSyncType(), newFrame->getFrameCount()); |
| break; |
| } |
| |
| CLOGV("input frame (%d, %d, F%d)", isSrc, needSync, newFrame->getFrameCount()); |
| |
| /* push the frame to dual selector */ |
| ret = m_dualSelector->manageNormalFrameHoldList(m_cameraId, newFrame, getPipeId(), isSrc, OUTPUT_NODE_1); |
| if (ret != NO_ERROR) { |
| /* in case of error, release frame with buffer */ |
| if (newFrame->getFrameType() != FRAME_TYPE_INTERNAL) { |
| ExynosCameraBuffer buffer; |
| buffer.index = -2; |
| CLOGE("manageNormalFrameHoldList fail(%d, %d, F%d)", isSrc, needSync, newFrame->getFrameCount()); |
| newFrame->getSrcBuffer(getPipeId(), &buffer, OUTPUT_NODE_1); |
| m_dualSelector->releaseBuffer(m_cameraId, &buffer); |
| goto func_exit; |
| } |
| } |
| |
| /* return frame to frameDoneQ to remove from runningList in case of slave camera */ |
| if (m_cameraId == m_slaveCameraId) |
| goto func_exit; |
| |
| return NO_ERROR; |
| |
| func_exit: |
| /* comptle the frame to remove the processList */ |
| newFrame->setFrameState(FRAME_STATE_SKIPPED); |
| |
| ret = newFrame->setEntityState(getPipeId(), ENTITY_STATE_FRAME_DONE); |
| if (ret != NO_ERROR) { |
| CLOGE("setEntityState(%d, ENTITY_STATE_FRAME_DONE) fail", getPipeId()); |
| } |
| |
| m_outputFrameQ->pushProcessQ(&newFrame); |
| |
| return NO_ERROR; |
| } |
| |
| bool ExynosCameraPipeSync::m_selectThreadFunc(void) |
| { |
| int ret = 0; |
| |
| ret = m_select(); |
| if (ret < 0) { |
| if (ret != TIMED_OUT) |
| CLOGE("m_putBuffer fail"); |
| } |
| |
| return m_checkThreadLoop(); |
| } |
| |
| status_t ExynosCameraPipeSync::m_select(void) |
| { |
| int ret = 0; |
| int timeStamp = 0; |
| ExynosCameraDualFrameSelector::Message msg; |
| ExynosCameraFrameSP_sptr_t newFrame = NULL; |
| ExynosCameraBuffer srcBuffer; |
| ExynosCameraFrameEntity *entity = NULL; |
| |
| /* for DROP_FRAME */ |
| bool releaseMasterBuffer = false; |
| bool releaseSlaveBuffer = false; |
| int masterSrcIndex = 0; |
| int slaveSrcIndex = 0; |
| |
| CLOGV(""); |
| |
| ret = (m_notifyQ)->waitAndPopProcessQ(&msg); |
| if (ret < 0) { |
| /* TODO: We need to make timeout duration depends on FPS */ |
| if (ret == TIMED_OUT) { |
| CLOGW("wait timeout"); |
| } else { |
| CLOGE("wait and pop fail, ret(%d)", ret); |
| /* TODO: doing exception handling */ |
| } |
| return ret; |
| } |
| |
| /* 1. get the Frame from dual selector */ |
| newFrame = m_dualSelector->selectFrame(m_cameraId); |
| |
| if (newFrame == NULL) { |
| CLOGE("new frame is NULL"); |
| return NO_ERROR; |
| } |
| |
| CLOGV("pipeId:%d, selected frame(HF:%d, DF:%d, SyncType:%d, Type:%d, TimeStamp:%d", |
| getPipeId(), |
| newFrame->getFrameCount(), |
| newFrame->getMetaFrameCount(), |
| newFrame->getSyncType(), |
| newFrame->getFrameType(), |
| (int)(newFrame->getTimeStamp() / 1000000)); |
| |
| /* 2. check the timeStamp(ms) */ |
| timeStamp = newFrame->getTimeStamp() / 1000000; |
| if (abs(timeStamp - m_lastTimeStamp) < 5) { |
| CLOGW("forcely drop the frame due to timeStamp(%d vs %d)", timeStamp, m_lastTimeStamp); |
| goto DROP_FRAME; |
| } |
| |
| /* 3. set the entity state to done */ |
| ret = newFrame->setEntityState(getPipeId(), ENTITY_STATE_FRAME_DONE); |
| if (ret < 0) { |
| CLOGE("newFrame->setEntityState(pipeId(%d), ENTITY_STATE_FRAME_DONE), ret(%d)", getPipeId(), ret); |
| /* TODO: doing exception handling */ |
| return OK; |
| } |
| |
| /* 4. update last timestamp and push the frame to outputQ */ |
| m_lastTimeStamp = newFrame->getTimeStamp() / 1000000; |
| m_outputFrameQ->pushProcessQ(&newFrame); |
| |
| return NO_ERROR; |
| |
| DROP_FRAME: |
| switch (newFrame->getSyncType()) { |
| case SYNC_TYPE_SYNC: |
| releaseMasterBuffer = true; |
| releaseSlaveBuffer = true; |
| |
| masterSrcIndex = OUTPUT_NODE_1; |
| slaveSrcIndex = OUTPUT_NODE_2; |
| break; |
| case SYNC_TYPE_BYPASS: |
| releaseMasterBuffer = true; |
| |
| masterSrcIndex = OUTPUT_NODE_1; |
| break; |
| case SYNC_TYPE_SWITCH: |
| releaseSlaveBuffer = true; |
| |
| slaveSrcIndex = OUTPUT_NODE_1; |
| break; |
| default: |
| break; |
| } |
| |
| /* release master source buffer */ |
| if (releaseMasterBuffer == true) { |
| ret = newFrame->getSrcBuffer(getPipeId(), &srcBuffer, masterSrcIndex); |
| if (ret < 0) { |
| CLOGE("newFrame(%d)->getSrcBuffer(%d, %d) fail(%d)", |
| newFrame->getFrameCount(), getPipeId(), masterSrcIndex, ret); |
| } else { |
| ret = m_dualSelector->releaseBuffer(m_masterCameraId, &srcBuffer); |
| if (ret != NO_ERROR) { |
| CLOGE("m_dualSelector->releaseBuffer(%d, %d) pipeId:%d, frame:%d fail(%d)", |
| masterSrcIndex, srcBuffer.index, getPipeId(), newFrame->getFrameCount(), ret); |
| } |
| } |
| } |
| |
| /* release slave source buffer */ |
| if (releaseSlaveBuffer == true) { |
| ret = newFrame->getSrcBuffer(getPipeId(), &srcBuffer, slaveSrcIndex); |
| if (ret < 0) { |
| CLOGE("newFrame(%d)->getSrcBuffer(%d, %d) fail(%d)", |
| newFrame->getFrameCount(), getPipeId(), slaveSrcIndex, ret); |
| } else { |
| ret = m_dualSelector->releaseBuffer(m_slaveCameraId, &srcBuffer); |
| if (ret != NO_ERROR) { |
| CLOGE("m_dualSelector->releaseBuffer(%d, %d) pipeId:%d, frame:%d fail(%d)", |
| slaveSrcIndex, srcBuffer.index, getPipeId(), newFrame->getFrameCount(), ret); |
| } |
| } |
| } |
| |
| /* comptle the frame to remove the processList */ |
| newFrame->setFrameState(FRAME_STATE_SKIPPED); |
| |
| ret = newFrame->setEntityState(getPipeId(), ENTITY_STATE_FRAME_DONE); |
| if (ret < 0) { |
| CLOGE("setEntityState(%d, ENTITY_STATE_FRAME_DONE) frame:%d, fail(%d)", |
| getPipeId(), newFrame->getFrameCount(), ret); |
| /* TODO: doing exception handling */ |
| } |
| |
| m_outputFrameQ->pushProcessQ(&newFrame); |
| |
| return NO_ERROR; |
| } |
| |
| bool ExynosCameraPipeSync::m_removeFrameThreadFunc(void) |
| { |
| int ret = 0; |
| |
| ret = m_removeFrame(); |
| if (ret < 0) { |
| if (ret != TIMED_OUT) |
| CLOGE("m_putBuffer fail"); |
| } |
| |
| return m_checkThreadLoop(); |
| } |
| |
| status_t ExynosCameraPipeSync::m_removeFrame(void) |
| { |
| int ret = 0; |
| ExynosCameraFrameSP_sptr_t frame = NULL; |
| |
| CLOGV(""); |
| |
| ret = (m_removeFrameQ)->waitAndPopProcessQ(&frame); |
| if (ret < 0) { |
| /* TODO: We need to make timeout duration depends on FPS */ |
| if (ret == TIMED_OUT) { |
| /* CLOGW("wait timeout"); */ |
| } else { |
| CLOGE("wait and pop fail, ret(%d)", ret); |
| /* TODO: doing exception handling */ |
| } |
| return ret; |
| } |
| |
| CLOGV("remove frame(HF:%d, DF:%d, TimeStamp:%lld, SyncType:%d, Type:%d", |
| frame->getFrameCount(), |
| frame->getMetaFrameCount(), |
| frame->getTimeStamp(), |
| frame->getSyncType(), |
| frame->getFrameType()); |
| |
| frame->setFrameState(FRAME_STATE_SKIPPED); |
| |
| ret = frame->setEntityState(getPipeId(), ENTITY_STATE_FRAME_DONE); |
| if (ret < 0) { |
| CLOGE("setEntityState(%d, ENTITY_STATE_FRAME_DONE) frame:%d, fail(%d)", |
| getPipeId(), frame->getFrameCount(), ret); |
| /* TODO: doing exception handling */ |
| } |
| |
| m_outputFrameQ->pushProcessQ(&frame); |
| |
| return NO_ERROR; |
| } |
| |
| void ExynosCameraPipeSync::m_init(void) |
| { |
| m_masterCameraId = -1; |
| m_slaveCameraId = -1; |
| m_dualSelector = NULL; |
| m_notifyQ = NULL; |
| m_lastTimeStamp = 0; |
| } |
| }; /* namespace android */ |