blob: a451e8fee50a28adf1ea447a0a92a9bb778cb9eb [file] [log] [blame]
/*
**
** 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 "ExynosCameraPipeVRA"
#include <cutils/log.h>
#include "ExynosCameraPipeVRA.h"
#include "ExynosCameraPipeGSC.h"
namespace android {
ExynosCameraPipeVRA::~ExynosCameraPipeVRA()
{
this->destroy();
}
status_t ExynosCameraPipeVRA::create(int32_t *sensorIds)
{
status_t ret = NO_ERROR;
ret = ExynosCameraPipe::create(sensorIds);
int32_t nodeNum[1] = {PREVIEW_GSC_NODE_NUM};
m_gscPipe = (ExynosCameraPipe*)new ExynosCameraPipeGSC(m_cameraId, m_parameters, true, nodeNum);
m_gscPipe->setPipeId(PIPE_GSC_VRA);
m_gscPipe->setPipeName("PIPE_GSC_VRA");
ret = m_gscPipe->create(sensorIds);
if (ret != NO_ERROR) {
CLOGE("ERR(%s[%d]):Internal GSC Pipe creation fail!", __FUNCTION__, __LINE__);
return ret;
}
m_gscThread = ExynosCameraThreadFactory::createThread(this, &ExynosCameraPipeVRA::m_gscThreadFunc, "gscThread");
m_gscFrameQ = new frame_queue_t;
m_gscFrameDoneQ = new frame_queue_t;
return ret;
}
status_t ExynosCameraPipeVRA::destroy(void)
{
status_t ret = NO_ERROR;
ret = ExynosCameraPipe::destroy();
if (ret != NO_ERROR)
CLOGE("ERR(%s[%d]):Destroy fail!", __FUNCTION__, __LINE__);
ret = m_gscPipe->destroy();
if (ret != NO_ERROR)
CLOGE("ERR(%s[%d]):Internal GSC Pipe detstroy fail!", __FUNCTION__, __LINE__);
return ret;
}
status_t ExynosCameraPipeVRA::stop(void)
{
status_t ret = NO_ERROR;
m_gscThread->requestExitAndWait();
m_gscFrameQ->release();
m_gscFrameDoneQ->release();
ret = ExynosCameraPipe::stop();
if (ret != NO_ERROR)
CLOGE("ERR(%s[%d]):Pipe stop fail!", __FUNCTION__, __LINE__);
ret = m_gscPipe->stop();
if (ret != NO_ERROR)
CLOGE("ERR(%s[%d]):Internal GSC Pipe stop fail!", __FUNCTION__, __LINE__);
return ret;
}
status_t ExynosCameraPipeVRA::startThread(void)
{
status_t ret = NO_ERROR;
if (m_gscThread->isRunning() == false) {
ret = m_gscThread->run(m_name);
if (ret != NO_ERROR)
CLOGE("ERR(%s[%d]):Internal GSC Pipe startThread fail!", __FUNCTION__, __LINE__);
else
CLOGI("INFO(%s[%d]):Internal GSC Pipe startThread is succeed", __FUNCTION__, __LINE__);
} else {
CLOGW("WRN(%s[%d]):Internal GSC Thread is already running", __FUNCTION__, __LINE__);
}
return ret;
}
status_t ExynosCameraPipeVRA::stopThread(void)
{
status_t ret = NO_ERROR;
m_gscThread->requestExit();
m_gscFrameQ->sendCmd(WAKE_UP);
ret = ExynosCameraPipe::stopThread();
if (ret != NO_ERROR)
CLOGE("ERR(%s[%d]):Internal GSC Pipe stopThread fail!", __FUNCTION__, __LINE__);
return ret;
}
status_t ExynosCameraPipeVRA::getInputFrameQ(frame_queue_t **inputFrameQ)
{
*inputFrameQ = m_gscFrameQ;
if (*inputFrameQ == NULL)
CLOGE("ERR(%s[%d])inputFrameQ is NULL", __FUNCTION__, __LINE__);
return NO_ERROR;
}
status_t ExynosCameraPipeVRA::m_runScaler(void)
{
status_t ret = NO_ERROR;
ExynosCameraFrame *newFrame = NULL;
ExynosCameraFrame *doneFrame = NULL;
ExynosCameraBuffer srcBuf, dstBuf;
ExynosRect srcRect, dstRect;
struct camera2_stream *streamMeta = NULL;
uint32_t *mcscOutputCrop = NULL;
struct camera2_shot_ext *shot_ext;
int dstBufIndex = -2;
int gscPipeId = PIPE_GSC_VRA;
int waitCount = 0;
int vraWidth = 0, vraHeight = 0;
int32_t previewFormat = m_parameters->getHwPreviewFormat();
m_parameters->getHwVraInputSize(&vraWidth, &vraHeight);
while (m_gscFrameQ->getSizeOfProcessQ() > 1) {
ret = m_gscFrameQ->popProcessQ(&newFrame);
if (ret != NO_ERROR) {
CLOGE("ERR(%s[%d]):wait and pop fail, ret(%d)", __FUNCTION__, __LINE__, ret);
return ret;
}
if (newFrame == NULL) {
CLOGE("ERR(%s[%d]):newFrame is NULL", __FUNCTION__, __LINE__);
continue;
}
ret = newFrame->setEntityState(getPipeId(), ENTITY_STATE_FRAME_DONE);
if (ret != NO_ERROR) {
CLOGE("ERR(%s[%d]):set entity state fail, ret(%d)", __FUNCTION__, __LINE__, ret);
/* TODO: doing exception handling */
}
m_outputFrameQ->pushProcessQ(&newFrame);
}
while (m_gscFrameDoneQ->getSizeOfProcessQ() > 0) {
doneFrame = NULL;
ret = m_gscFrameDoneQ->popProcessQ(&doneFrame);
if (ret != NO_ERROR) {
CLOGE("ERR(%s[%d]):wait and pop fail, ret(%d)", __FUNCTION__, __LINE__, ret);
}
if (doneFrame == NULL) {
CLOGE("ERR(%s[%d]):newFrame is NULL", __FUNCTION__, __LINE__);
continue;
}
ret = doneFrame->getDstBuffer(gscPipeId, &dstBuf);
if (ret != NO_ERROR) {
CLOGE("ERR(%s[%d]):getDstBuffer fail. pipeId(%d), frameCount(%d), ret(%d)",
__FUNCTION__, __LINE__, getPipeId(), doneFrame->getFrameCount(), ret);
} else if (dstBuf.index >= 0) {
ret = m_bufferManager[OUTPUT_NODE]->putBuffer(dstBuf.index, EXYNOS_CAMERA_BUFFER_POSITION_IN_HAL);
if (ret != NO_ERROR) {
CLOGE("ERR(%s[%d]):m_bufferManager[%d]->putbuffer() fail, index(%d), ret(%d)",
__FUNCTION__, __LINE__, OUTPUT_NODE, dstBuf.index, ret);
}
}
ret = doneFrame->setEntityState(getPipeId(), ENTITY_STATE_FRAME_DONE);
if (ret != NO_ERROR) {
CLOGE("ERR(%s[%d]):set entity state fail, ret(%d)", __FUNCTION__, __LINE__, ret);
/* TODO: doing exception handling */
}
m_outputFrameQ->pushProcessQ(&doneFrame);
}
newFrame = NULL;
ret = m_gscFrameQ->waitAndPopProcessQ(&newFrame);
if (m_flagTryStop == true) {
CLOGD("DEBUG(%s[%d]):m_flagTryStop(%d)", __FUNCTION__, __LINE__, m_flagTryStop);
goto FUNC_EXIT;
}
if (ret != NO_ERROR) {
/* TODO: We need to make timeout duration depends on FPS */
if (ret == TIMED_OUT) {
#ifdef USE_CAMERA2_API_SUPPORT
/*
* TIMEOUT log print
* condition 1 : it is not reprocessing
* condition 2 : if it is reprocessing, but m_timeLogCount is equals or lower than 0
*/
if (!(m_parameters->isReprocessing() == true && m_timeLogCount <= 0)) {
m_timeLogCount--;
#endif
CLOGW("WARN(%s[%d]):wait timeout", __FUNCTION__, __LINE__);
m_mainNode->dumpState();
#ifdef USE_CAMERA2_API_SUPPORT
}
#endif
} else {
CLOGE("ERR(%s[%d]):wait and pop fail, ret(%d)", __FUNCTION__, __LINE__, ret);
/* TODO: doing exception handling */
}
goto FUNC_EXIT;
}
if (newFrame == NULL) {
CLOGE("ERR(%s[%d]):newFrame is NULL", __FUNCTION__, __LINE__);
goto FUNC_EXIT;
}
/* Get scaler source buffer */
ret = newFrame->getSrcBuffer(getPipeId(), &srcBuf);
if (ret != NO_ERROR) {
CLOGE("ERR(%s[%d]):getSrcBuffer fail, ret(%d)", __FUNCTION__, __LINE__, ret);
/* TODO: doing exception handling */
goto FUNC_EXIT;
} else if (srcBuf.index < 0) {
CLOGW("WARN(%s[%d]):[F%d B%d]No Src buffer. Skip VRA",
__FUNCTION__, __LINE__,
newFrame->getFrameCount(),
srcBuf.index);
goto FUNC_EXIT;
}
shot_ext = (camera2_shot_ext*)srcBuf.addr[srcBuf.planeCount-1];
entity_buffer_state_t srcBufferState;
ret = newFrame->getSrcBufferState(getPipeId(), &srcBufferState);
if (srcBuf.index < 0
|| shot_ext->fd_bypass == true
|| srcBufferState == ENTITY_BUFFER_STATE_ERROR) {
if (m_mainThread->isRunning() == true)
m_mainThread->requestExit();
goto FUNC_EXIT;
}
ret = newFrame->getDstBuffer(getPipeId(), &dstBuf);
if (ret != NO_ERROR) {
CLOGE("ERR(%s[%d]):getDstBuffer fail. pipeId(%d), frameCount(%d), ret(%d)",
__FUNCTION__, __LINE__, getPipeId(), newFrame->getFrameCount(), ret);
goto FUNC_EXIT;
}
if (dstBuf.index < 0) {
ret = m_bufferManager[OUTPUT_NODE]->getBuffer(&dstBufIndex, EXYNOS_CAMERA_BUFFER_POSITION_IN_HAL, &dstBuf);
if (ret != NO_ERROR) {
CLOGE("ERR(%s[%d]):Buffer manager getBuffer fail, manager(%d), frameCount(%d), ret(%d)",
__FUNCTION__, __LINE__, OUTPUT_NODE, newFrame->getFrameCount(), ret);
goto FUNC_EXIT;
}
}
/* Get size from metadata */
streamMeta = (struct camera2_stream*)srcBuf.addr[srcBuf.planeCount-1];
if (streamMeta == NULL) {
CLOGE("ERR(%s[%d]):srcBuf.addr is NULL, srcBuf.addr(0x%x)",__FUNCTION__, __LINE__, srcBuf.addr[srcBuf.planeCount-1]);
goto FUNC_EXIT;
}
/* Set size to GSC frame */
mcscOutputCrop = streamMeta->output_crop_region;
if (mcscOutputCrop[2] <= 0
|| mcscOutputCrop[3] <= 0) {
CLOGE("ERR(%s[%d]):MCSC output crop is zero(w:%d, h:%d)",
__FUNCTION__, __LINE__, mcscOutputCrop[2], mcscOutputCrop[3]);
goto FUNC_EXIT;
}
srcRect.x = 0;
srcRect.y = 0;
srcRect.w = mcscOutputCrop[2];
srcRect.h = mcscOutputCrop[3];
srcRect.fullW = mcscOutputCrop[2];
srcRect.fullH = mcscOutputCrop[3];
srcRect.colorFormat = previewFormat;
dstRect.x = 0;
dstRect.y = 0;
dstRect.w = vraWidth;
dstRect.h = vraHeight;
dstRect.fullW = vraWidth;
dstRect.fullH = vraHeight;
dstRect.colorFormat = m_parameters->getHwVraInputFormat();
ret = newFrame->setSrcRect(gscPipeId, srcRect);
ret = newFrame->setDstRect(gscPipeId, dstRect);
/* set buffers */
ret = newFrame->setSrcBuffer(gscPipeId, srcBuf);
if (ret != NO_ERROR) {
CLOGE("ERR(%s[%d]):setSrcBuffer fail, pipeId(%d), ret(%d)", __FUNCTION__, __LINE__, gscPipeId, ret);
goto FUNC_EXIT;
}
ret = newFrame->setDstBuffer(gscPipeId, dstBuf);
if (ret != NO_ERROR) {
CLOGE("ERR(%s[%d]):setDstBuffer fail, pipeId(%d), ret(%d)", __FUNCTION__, __LINE__, gscPipeId, ret);
goto FUNC_EXIT;
}
m_gscPipe->setOutputFrameQ(m_gscFrameDoneQ);
m_gscPipe->pushFrame(&newFrame);
/* Wait and Pop frame from GSC output Q */
CLOGV("INFO(%s[%d]):wait GSC output", __FUNCTION__, __LINE__);
RETRY:
waitCount = 0;
doneFrame = NULL;
do {
ret = m_gscFrameDoneQ->waitAndPopProcessQ(&doneFrame);
waitCount++;
} while (ret == TIMED_OUT && waitCount < 10);
if (ret != NO_ERROR) {
CLOGW("WARN(%s[%d]):GSC wait and pop error, ret(%d)", __FUNCTION__, __LINE__, ret);
return ret;
}
if (doneFrame == NULL) {
CLOGE("ERR(%s[%d]):gscFrame is NULL", __FUNCTION__, __LINE__);
return NO_ERROR;
}
if (newFrame->getFrameCount() != doneFrame->getFrameCount()) {
CLOGW("WARN(%s[%d]):FrameCount mismatch, Push(%d) Pop(%d)",
__FUNCTION__, __LINE__, newFrame->getFrameCount(), doneFrame->getFrameCount());
ret = doneFrame->getDstBuffer(gscPipeId, &dstBuf);
if (ret != NO_ERROR) {
CLOGE("ERR(%s[%d]):getDstBuffer fail. pipeId(%d), frameCount(%d), ret(%d)",
__FUNCTION__, __LINE__, getPipeId(), doneFrame->getFrameCount(), ret);
dstBuf.index = -2;
} else if (dstBuf.index >= 0) {
ret = m_bufferManager[OUTPUT_NODE]->putBuffer(dstBuf.index, EXYNOS_CAMERA_BUFFER_POSITION_IN_HAL);
if (ret != NO_ERROR) {
CLOGE("ERR(%s[%d]):m_bufferManager[%d]->putbuffer() fail, index(%d), ret(%d)",
__FUNCTION__, __LINE__, OUTPUT_NODE, dstBuf.index, ret);
}
}
ret = doneFrame->setEntityState(getPipeId(), ENTITY_STATE_FRAME_DONE);
if (ret != NO_ERROR) {
CLOGE("ERR(%s[%d]):set entity state fail, ret(%d)", __FUNCTION__, __LINE__, ret);
/* TODO: doing exception handling */
return INVALID_OPERATION;
}
m_outputFrameQ->pushProcessQ(&doneFrame);
goto RETRY;
}
CLOGV("INFO(%s[%d]):Get frame from GSC Pipe, frameCount(%d)", __FUNCTION__, __LINE__, newFrame->getFrameCount());
ret = doneFrame->getDstBuffer(gscPipeId, &dstBuf);
if (ret != NO_ERROR) {
CLOGE("ERR(%s[%d]):getDstBuffer fail. pipeId(%d), frameCount(%d), ret(%d)",
__FUNCTION__, __LINE__, gscPipeId, newFrame->getFrameCount(), ret);
goto FUNC_EXIT;
}
memcpy(dstBuf.addr[dstBuf.planeCount-1], srcBuf.addr[srcBuf.planeCount-1], dstBuf.size[dstBuf.planeCount-1]);
camera2_node_group node_group_info;
memset(&node_group_info, 0x0, sizeof(camera2_node_group));
node_group_info.leader.request = 1;
node_group_info.leader.input.cropRegion[0] = 0;
node_group_info.leader.input.cropRegion[1] = 0;
node_group_info.leader.input.cropRegion[2] = vraWidth;
node_group_info.leader.input.cropRegion[3] = vraHeight;
node_group_info.leader.output.cropRegion[0] = 0;
node_group_info.leader.output.cropRegion[1] = 0;
node_group_info.leader.output.cropRegion[2] = node_group_info.leader.input.cropRegion[2];
node_group_info.leader.output.cropRegion[3] = node_group_info.leader.input.cropRegion[3];
newFrame->storeNodeGroupInfo(&node_group_info, PERFRAME_INFO_VRA);
ret = newFrame->setDstBuffer(getPipeId(), dstBuf);
if (ret != NO_ERROR) {
CLOGE("ERR(%s[%d]):setDstBuffer fail, pipeId(%d), ret(%d)", __FUNCTION__, __LINE__, gscPipeId, ret);
goto FUNC_EXIT;
}
m_inputFrameQ->pushProcessQ(&newFrame);
if (m_mainThread->isRunning() == false) {
m_mainThread->run(m_name);
CLOGI("INFO(%s[%d]):startThread is succeed (%d)", __FUNCTION__, __LINE__, getPipeId());
}
return NO_ERROR;
FUNC_EXIT:
if (dstBuf.index >= 0) {
ret = m_bufferManager[OUTPUT_NODE]->putBuffer(dstBuf.index, EXYNOS_CAMERA_BUFFER_POSITION_IN_HAL);
if (ret != NO_ERROR) {
CLOGE("ERR(%s[%d]):m_bufferManager[%d]->putbuffer() fail, index(%d), ret(%d)",
__FUNCTION__, __LINE__, OUTPUT_NODE, dstBuf.index, ret);
}
}
if (newFrame != NULL) {
ret = newFrame->setEntityState(getPipeId(), ENTITY_STATE_FRAME_DONE);
if (ret != NO_ERROR) {
CLOGE("ERR(%s[%d]):set entity state fail, ret(%d)", __FUNCTION__, __LINE__, ret);
/* TODO: doing exception handling */
return INVALID_OPERATION;
}
m_outputFrameQ->pushProcessQ(&newFrame);
}
return NO_ERROR;
}
status_t ExynosCameraPipeVRA::m_putBuffer(void)
{
status_t ret = NO_ERROR;
ExynosCameraFrame *newFrame = NULL;
while (m_inputFrameQ->getSizeOfProcessQ() > 1) {
ret = m_inputFrameQ->popProcessQ(&newFrame);
if (ret != NO_ERROR) {
CLOGE("ERR(%s[%d]):wait and pop fail, ret(%d)", __FUNCTION__, __LINE__, ret);
return ret;
}
ret = newFrame->setEntityState(getPipeId(), ENTITY_STATE_FRAME_DONE);
if (ret != NO_ERROR) {
CLOGE("ERR(%s[%d]):set entity state fail, ret(%d)", __FUNCTION__, __LINE__, ret);
/* TODO: doing exception handling */
return INVALID_OPERATION;
}
m_outputFrameQ->pushProcessQ(&newFrame);
}
ret = ExynosCameraPipe::m_putBuffer();
if (ret != NO_ERROR)
CLOGE("ERR(%s[%d]):m_putBuffer() fail!", __FUNCTION__, __LINE__);
return ret;
}
bool ExynosCameraPipeVRA::m_gscThreadFunc(void)
{
status_t ret = NO_ERROR;
if (m_flagTryStop == true) {
usleep(5000);
return true;
}
/*
* Cycle is below
* m_gscFrameQ -> m_runScaler()(GSC) ->
* m_inputFrameQ -> m_putBuffer()(VRA) -> m_getBuffer()(VRA) ->
* m_outputFrameQ
*/
ret = m_runScaler();
if (ret != NO_ERROR) {
if (ret == TIMED_OUT)
return true;
CLOGE("ERR(%s[%d]):m_runScaler fail, ret(%d)", __FUNCTION__, __LINE__, ret);
/* TODO: doing exception handling */
return false;
}
return true;
}
bool ExynosCameraPipeVRA::m_mainThreadFunc(void)
{
status_t ret = NO_ERROR;
if (m_flagTryStop == true) {
usleep(5000);
return true;
}
ret = m_putBuffer();
if (ret != NO_ERROR) {
if (ret == TIMED_OUT)
return true;
CLOGE("ERR(%s[%d]):m_putBuffer fail, ret(%d)", __FUNCTION__, __LINE__, ret);
/* TODO: doing exception handling */
return false;
}
ret = m_getBuffer();
if (ret != NO_ERROR) {
if (ret == TIMED_OUT)
return true;
CLOGE("ERR(%s[%d]):m_getBuffer fail, ret(%d)", __FUNCTION__, __LINE__, ret);
/* TODO: doing exception handling */
return false;
}
return m_checkThreadLoop();
}
void ExynosCameraPipeVRA::m_init(void)
{
m_gscFrameQ = NULL;
m_gscFrameDoneQ = NULL;
m_gscPipe = NULL;
}
}; /* namespace android */