diff options
Diffstat (limited to 'camera/QCamera2/HAL3/QCamera3PostProc.cpp')
-rw-r--r-- | camera/QCamera2/HAL3/QCamera3PostProc.cpp | 3142 |
1 files changed, 3142 insertions, 0 deletions
diff --git a/camera/QCamera2/HAL3/QCamera3PostProc.cpp b/camera/QCamera2/HAL3/QCamera3PostProc.cpp new file mode 100644 index 0000000..be53d4a --- /dev/null +++ b/camera/QCamera2/HAL3/QCamera3PostProc.cpp @@ -0,0 +1,3142 @@ +/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are +* met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following +* disclaimer in the documentation and/or other materials provided +* with the distribution. +* * Neither the name of The Linux Foundation nor the names of its +* contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS +* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +*/ + +#define LOG_TAG "QCamera3PostProc" + +// To remove +#include <cutils/properties.h> + +// System dependencies +#include <stdio.h> + +// Camera dependencies +#include "QCamera3Channel.h" +#include "QCamera3HWI.h" +#include "QCamera3PostProc.h" +#include "QCamera3Stream.h" +#include "QCameraTrace.h" + +extern "C" { +#include "mm_camera_dbg.h" +} + +#define ENABLE_MODEL_INFO_EXIF + +namespace qcamera { + +static const char ExifAsciiPrefix[] = + { 0x41, 0x53, 0x43, 0x49, 0x49, 0x0, 0x0, 0x0 }; // "ASCII\0\0\0" + +__unused +static const char ExifUndefinedPrefix[] = + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // "\0\0\0\0\0\0\0\0" + +#define EXIF_ASCII_PREFIX_SIZE 8 //(sizeof(ExifAsciiPrefix)) +#define FOCAL_LENGTH_DECIMAL_PRECISION 1000 + +/*=========================================================================== + * FUNCTION : QCamera3PostProcessor + * + * DESCRIPTION: constructor of QCamera3PostProcessor. + * + * PARAMETERS : + * @cam_ctrl : ptr to HWI object + * + * RETURN : None + *==========================================================================*/ +QCamera3PostProcessor::QCamera3PostProcessor(QCamera3ProcessingChannel* ch_ctrl) + : m_parent(ch_ctrl), + mJpegCB(NULL), + mJpegUserData(NULL), + mJpegClientHandle(0), + mJpegSessionId(0), + m_bThumbnailNeeded(TRUE), + m_pReprocChannel(NULL), + m_inputPPQ(releasePPInputData, this), + m_inputFWKPPQ(NULL, this), + m_ongoingPPQ(releaseOngoingPPData, this), + m_inputJpegQ(releaseJpegData, this), + m_ongoingJpegQ(releaseJpegData, this), + m_inputMetaQ(releaseMetadata, this), + m_jpegSettingsQ(NULL, this) +{ + memset(&mJpegHandle, 0, sizeof(mJpegHandle)); + memset(&mJpegMetadata, 0, sizeof(mJpegMetadata)); + pthread_mutex_init(&mReprocJobLock, NULL); +} + +/*=========================================================================== + * FUNCTION : ~QCamera3PostProcessor + * + * DESCRIPTION: deconstructor of QCamera3PostProcessor. + * + * PARAMETERS : None + * + * RETURN : None + *==========================================================================*/ +QCamera3PostProcessor::~QCamera3PostProcessor() +{ + pthread_mutex_destroy(&mReprocJobLock); +} + +/*=========================================================================== + * FUNCTION : init + * + * DESCRIPTION: initialization of postprocessor + * + * PARAMETERS : + * @memory : output buffer memory + * + * RETURN : int32_t type of status + * NO_ERROR -- success + * none-zero failure code + *==========================================================================*/ +int32_t QCamera3PostProcessor::init(QCamera3StreamMem *memory) +{ + ATRACE_CALL(); + mOutputMem = memory; + m_dataProcTh.launch(dataProcessRoutine, this); + + return NO_ERROR; +} + +/*=========================================================================== + * FUNCTION : deinit + * + * DESCRIPTION: de-initialization of postprocessor + * + * PARAMETERS : None + * + * RETURN : int32_t type of status + * NO_ERROR -- success + * none-zero failure code + *==========================================================================*/ +int32_t QCamera3PostProcessor::deinit() +{ + int rc = NO_ERROR; + m_dataProcTh.exit(); + + if (m_pReprocChannel != NULL) { + m_pReprocChannel->stop(); + delete m_pReprocChannel; + m_pReprocChannel = NULL; + } + + if(mJpegClientHandle > 0) { + rc = mJpegHandle.close(mJpegClientHandle); + LOGH("Jpeg closed, rc = %d, mJpegClientHandle = %x", + rc, mJpegClientHandle); + mJpegClientHandle = 0; + memset(&mJpegHandle, 0, sizeof(mJpegHandle)); + } + + mOutputMem = NULL; + return rc; +} + +/*=========================================================================== + * FUNCTION : initJpeg + * + * DESCRIPTION: initialization of jpeg through postprocessor + * + * PARAMETERS : + * @jpeg_cb : callback to handle jpeg event from mm-camera-interface + * @max_pic_dim : max picture dimensions + * @user_data : user data ptr for jpeg callback + * + * RETURN : int32_t type of status + * NO_ERROR -- success + * none-zero failure code + *==========================================================================*/ +int32_t QCamera3PostProcessor::initJpeg(jpeg_encode_callback_t jpeg_cb, + cam_dimension_t* max_pic_dim, + void *user_data) +{ + ATRACE_CALL(); + mJpegCB = jpeg_cb; + mJpegUserData = user_data; + mm_dimension max_size; + + if ((0 > max_pic_dim->width) || (0 > max_pic_dim->height)) { + LOGE("Negative dimension %dx%d", + max_pic_dim->width, max_pic_dim->height); + return BAD_VALUE; + } + + // set max pic size + memset(&max_size, 0, sizeof(mm_dimension)); + max_size.w = max_pic_dim->width; + max_size.h = max_pic_dim->height; + + // Pass OTP calibration data to JPEG + QCamera3HardwareInterface* hal_obj = (QCamera3HardwareInterface*)m_parent->mUserData; + mJpegMetadata.default_sensor_flip = FLIP_NONE; + mJpegMetadata.sensor_mount_angle = hal_obj->getSensorMountAngle(); + memcpy(&mJpegMetadata.otp_calibration_data, + hal_obj->getRelatedCalibrationData(), + sizeof(cam_related_system_calibration_data_t)); + mJpegClientHandle = jpeg_open(&mJpegHandle, NULL, max_size, &mJpegMetadata); + + if (!mJpegClientHandle) { + LOGE("jpeg_open did not work"); + return UNKNOWN_ERROR; + } + return NO_ERROR; +} + +/*=========================================================================== + * FUNCTION : start + * + * DESCRIPTION: start postprocessor. Data process thread and data notify thread + * will be launched. + * + * PARAMETERS : + * @config : reprocess configuration + * + * RETURN : int32_t type of status + * NO_ERROR -- success + * none-zero failure code + * + * NOTE : if any reprocess is needed, a reprocess channel/stream + * will be started. + *==========================================================================*/ +int32_t QCamera3PostProcessor::start(const reprocess_config_t &config) +{ + int32_t rc = NO_ERROR; + QCamera3HardwareInterface* hal_obj = (QCamera3HardwareInterface*)m_parent->mUserData; + + if (config.reprocess_type != REPROCESS_TYPE_NONE) { + if (m_pReprocChannel != NULL) { + m_pReprocChannel->stop(); + delete m_pReprocChannel; + m_pReprocChannel = NULL; + } + + // if reprocess is needed, start reprocess channel + LOGD("Setting input channel as pInputChannel"); + m_pReprocChannel = hal_obj->addOfflineReprocChannel(config, m_parent); + if (m_pReprocChannel == NULL) { + LOGE("cannot add reprocess channel"); + return UNKNOWN_ERROR; + } + /*start the reprocess channel only if buffers are already allocated, thus + only start it in an intermediate reprocess type, defer it for others*/ + if (config.reprocess_type == REPROCESS_TYPE_JPEG) { + rc = m_pReprocChannel->start(); + if (rc != 0) { + LOGE("cannot start reprocess channel"); + delete m_pReprocChannel; + m_pReprocChannel = NULL; + return rc; + } + } + } + m_dataProcTh.sendCmd(CAMERA_CMD_TYPE_START_DATA_PROC, TRUE, FALSE); + + return rc; +} + +/*=========================================================================== + * FUNCTION : flush + * + * DESCRIPTION: stop ongoing postprocess and jpeg jobs + * + * PARAMETERS : None + * + * RETURN : int32_t type of status + * NO_ERROR -- success + * none-zero failure code + * + *==========================================================================*/ +int32_t QCamera3PostProcessor::flush() +{ + int32_t rc = NO_ERROR; + qcamera_hal3_jpeg_data_t *jpeg_job = + (qcamera_hal3_jpeg_data_t *)m_ongoingJpegQ.dequeue(); + while (jpeg_job != NULL) { + rc = mJpegHandle.abort_job(jpeg_job->jobId); + releaseJpegJobData(jpeg_job); + free(jpeg_job); + + jpeg_job = (qcamera_hal3_jpeg_data_t *)m_ongoingJpegQ.dequeue(); + } + rc = releaseOfflineBuffers(true); + return rc; +} + +/*=========================================================================== + * FUNCTION : stop + * + * DESCRIPTION: stop postprocessor. Data process and notify thread will be stopped. + * + * PARAMETERS : None + * + * RETURN : int32_t type of status + * NO_ERROR -- success + * none-zero failure code + * + * NOTE : reprocess channel will be stopped and deleted if there is any + *==========================================================================*/ +int32_t QCamera3PostProcessor::stop() +{ + m_dataProcTh.sendCmd(CAMERA_CMD_TYPE_STOP_DATA_PROC, TRUE, TRUE); + + if (m_pReprocChannel != NULL) { + m_pReprocChannel->stop(); + delete m_pReprocChannel; + m_pReprocChannel = NULL; + } + + return NO_ERROR; +} + +/*=========================================================================== + * FUNCTION : getFWKJpegEncodeConfig + * + * DESCRIPTION: function to prepare encoding job information + * + * PARAMETERS : + * @encode_parm : param to be filled with encoding configuration + * @frame : framework input buffer + * @jpeg_settings : jpeg settings to be applied for encoding + * + * RETURN : int32_t type of status + * NO_ERROR -- success + * none-zero failure code + *==========================================================================*/ +int32_t QCamera3PostProcessor::getFWKJpegEncodeConfig( + mm_jpeg_encode_params_t& encode_parm, + qcamera_fwk_input_pp_data_t *frame, + jpeg_settings_t *jpeg_settings) +{ + LOGD("E"); + int32_t ret = NO_ERROR; + + if ((NULL == frame) || (NULL == jpeg_settings)) { + return BAD_VALUE; + } + + ssize_t bufSize = mOutputMem->getSize(jpeg_settings->out_buf_index); + if (BAD_INDEX == bufSize) { + LOGE("cannot retrieve buffer size for buffer %u", + jpeg_settings->out_buf_index); + return BAD_VALUE; + } + + encode_parm.jpeg_cb = mJpegCB; + encode_parm.userdata = mJpegUserData; + + if (jpeg_settings->thumbnail_size.width > 0 && + jpeg_settings->thumbnail_size.height > 0) + m_bThumbnailNeeded = TRUE; + else + m_bThumbnailNeeded = FALSE; + encode_parm.encode_thumbnail = m_bThumbnailNeeded; + + // get color format + cam_format_t img_fmt = frame->reproc_config.stream_format; + encode_parm.color_format = getColorfmtFromImgFmt(img_fmt); + + // get jpeg quality + encode_parm.quality = jpeg_settings->jpeg_quality; + if (encode_parm.quality <= 0) { + encode_parm.quality = 85; + } + + // get jpeg thumbnail quality + encode_parm.thumb_quality = jpeg_settings->jpeg_thumb_quality; + + cam_frame_len_offset_t main_offset = + frame->reproc_config.input_stream_plane_info.plane_info; + + encode_parm.num_src_bufs = 1; + encode_parm.src_main_buf[0].index = 0; + encode_parm.src_main_buf[0].buf_size = frame->input_buffer.frame_len; + encode_parm.src_main_buf[0].buf_vaddr = (uint8_t *) frame->input_buffer.buffer; + encode_parm.src_main_buf[0].fd = frame->input_buffer.fd; + encode_parm.src_main_buf[0].format = MM_JPEG_FMT_YUV; + encode_parm.src_main_buf[0].offset = main_offset; + + //Pass input thumbnail buffer info to encoder. + //Note: Use main buffer to encode thumbnail + if (m_bThumbnailNeeded == TRUE) { + encode_parm.num_tmb_bufs = 1; + encode_parm.src_thumb_buf[0] = encode_parm.src_main_buf[0]; + } + + //Pass output jpeg buffer info to encoder. + //mOutputMem is allocated by framework. + encode_parm.num_dst_bufs = 1; + encode_parm.dest_buf[0].index = 0; + encode_parm.dest_buf[0].buf_size = (size_t)bufSize; + encode_parm.dest_buf[0].buf_vaddr = (uint8_t *)mOutputMem->getPtr( + jpeg_settings->out_buf_index); + encode_parm.dest_buf[0].fd = mOutputMem->getFd( + jpeg_settings->out_buf_index); + encode_parm.dest_buf[0].format = MM_JPEG_FMT_YUV; + encode_parm.dest_buf[0].offset = main_offset; + + LOGD("X"); + return NO_ERROR; + + LOGD("X with error %d", ret); + return ret; +} + +/*=========================================================================== + * FUNCTION : getJpegEncodeConfig + * + * DESCRIPTION: function to prepare encoding job information + * + * PARAMETERS : + * @encode_parm : param to be filled with encoding configuration + * #main_stream : stream object where the input buffer comes from + * @jpeg_settings : jpeg settings to be applied for encoding + * + * RETURN : int32_t type of status + * NO_ERROR -- success + * none-zero failure code + *==========================================================================*/ +int32_t QCamera3PostProcessor::getJpegEncodeConfig( + mm_jpeg_encode_params_t& encode_parm, + QCamera3Stream *main_stream, + jpeg_settings_t *jpeg_settings) +{ + LOGD("E"); + int32_t ret = NO_ERROR; + ssize_t bufSize = 0; + + encode_parm.jpeg_cb = mJpegCB; + encode_parm.userdata = mJpegUserData; + + if (jpeg_settings->thumbnail_size.width > 0 && + jpeg_settings->thumbnail_size.height > 0) + m_bThumbnailNeeded = TRUE; + else + m_bThumbnailNeeded = FALSE; + encode_parm.encode_thumbnail = m_bThumbnailNeeded; + + // get color format + cam_format_t img_fmt = CAM_FORMAT_YUV_420_NV12; //default value + main_stream->getFormat(img_fmt); + encode_parm.color_format = getColorfmtFromImgFmt(img_fmt); + + // get jpeg quality + encode_parm.quality = jpeg_settings->jpeg_quality; + if (encode_parm.quality <= 0) { + encode_parm.quality = 85; + } + + // get jpeg thumbnail quality + encode_parm.thumb_quality = jpeg_settings->jpeg_thumb_quality; + + cam_frame_len_offset_t main_offset; + memset(&main_offset, 0, sizeof(cam_frame_len_offset_t)); + main_stream->getFrameOffset(main_offset); + + // src buf config + //Pass input main image buffer info to encoder. + QCamera3StreamMem *pStreamMem = main_stream->getStreamBufs(); + if (pStreamMem == NULL) { + LOGE("cannot get stream bufs from main stream"); + ret = BAD_VALUE; + goto on_error; + } + encode_parm.num_src_bufs = MIN(pStreamMem->getCnt(), MM_JPEG_MAX_BUF); + for (uint32_t i = 0; i < encode_parm.num_src_bufs; i++) { + if (pStreamMem != NULL) { + encode_parm.src_main_buf[i].index = i; + bufSize = pStreamMem->getSize(i); + if (BAD_INDEX == bufSize) { + LOGE("cannot retrieve buffer size for buffer %u", i); + ret = BAD_VALUE; + goto on_error; + } + encode_parm.src_main_buf[i].buf_size = (size_t)bufSize; + encode_parm.src_main_buf[i].buf_vaddr = (uint8_t *)pStreamMem->getPtr(i); + encode_parm.src_main_buf[i].fd = pStreamMem->getFd(i); + encode_parm.src_main_buf[i].format = MM_JPEG_FMT_YUV; + encode_parm.src_main_buf[i].offset = main_offset; + } + } + + //Pass input thumbnail buffer info to encoder. + //Note: Use main buffer to encode thumbnail + if (m_bThumbnailNeeded == TRUE) { + pStreamMem = main_stream->getStreamBufs(); + if (pStreamMem == NULL) { + LOGE("cannot get stream bufs from thumb stream"); + ret = BAD_VALUE; + goto on_error; + } + cam_frame_len_offset_t thumb_offset; + memset(&thumb_offset, 0, sizeof(cam_frame_len_offset_t)); + main_stream->getFrameOffset(thumb_offset); + encode_parm.num_tmb_bufs = MIN(pStreamMem->getCnt(), MM_JPEG_MAX_BUF); + for (uint32_t i = 0; i < encode_parm.num_tmb_bufs; i++) { + if (pStreamMem != NULL) { + encode_parm.src_thumb_buf[i].index = i; + bufSize = pStreamMem->getSize(i); + if (BAD_INDEX == bufSize) { + LOGE("cannot retrieve buffer size for buffer %u", i); + ret = BAD_VALUE; + goto on_error; + } + encode_parm.src_thumb_buf[i].buf_size = (uint32_t)bufSize; + encode_parm.src_thumb_buf[i].buf_vaddr = (uint8_t *)pStreamMem->getPtr(i); + encode_parm.src_thumb_buf[i].fd = pStreamMem->getFd(i); + encode_parm.src_thumb_buf[i].format = MM_JPEG_FMT_YUV; + encode_parm.src_thumb_buf[i].offset = thumb_offset; + } + } + } + + //Pass output jpeg buffer info to encoder. + //mJpegMem is allocated by framework. + bufSize = mOutputMem->getSize(jpeg_settings->out_buf_index); + if (BAD_INDEX == bufSize) { + LOGE("cannot retrieve buffer size for buffer %u", + jpeg_settings->out_buf_index); + ret = BAD_VALUE; + goto on_error; + } + encode_parm.num_dst_bufs = 1; + encode_parm.dest_buf[0].index = 0; + encode_parm.dest_buf[0].buf_size = (size_t)bufSize; + encode_parm.dest_buf[0].buf_vaddr = (uint8_t *)mOutputMem->getPtr( + jpeg_settings->out_buf_index); + encode_parm.dest_buf[0].fd = mOutputMem->getFd( + jpeg_settings->out_buf_index); + encode_parm.dest_buf[0].format = MM_JPEG_FMT_YUV; + encode_parm.dest_buf[0].offset = main_offset; + + LOGD("X"); + return NO_ERROR; + +on_error: + LOGD("X with error %d", ret); + return ret; +} + +int32_t QCamera3PostProcessor::processData(mm_camera_super_buf_t *input) { + return processData(input, NULL, 0); +} + +/*=========================================================================== + * FUNCTION : processData + * + * DESCRIPTION: enqueue data into dataProc thread + * + * PARAMETERS : + * @frame : process input frame + * @output : process output frame + * + * RETURN : int32_t type of status + * NO_ERROR -- success + * none-zero failure code + * + * NOTE : depends on if offline reprocess is needed, received frame will + * be sent to either input queue of postprocess or jpeg encoding + *==========================================================================*/ +int32_t QCamera3PostProcessor::processData(mm_camera_super_buf_t *input, + buffer_handle_t *output, uint32_t frameNumber) +{ + LOGD("E"); + pthread_mutex_lock(&mReprocJobLock); + + // enqueue to post proc input queue + qcamera_hal3_pp_buffer_t *pp_buffer = (qcamera_hal3_pp_buffer_t *)malloc( + sizeof(qcamera_hal3_pp_buffer_t)); + if (NULL == pp_buffer) { + LOGE("out of memory"); + return NO_MEMORY; + } + memset(pp_buffer, 0, sizeof(*pp_buffer)); + pp_buffer->input = input; + pp_buffer->output = output; + pp_buffer->frameNumber = frameNumber; + m_inputPPQ.enqueue((void *)pp_buffer); + if (!(m_inputMetaQ.isEmpty())) { + LOGD("meta queue is not empty, do next job"); + m_dataProcTh.sendCmd(CAMERA_CMD_TYPE_DO_NEXT_JOB, FALSE, FALSE); + } else + LOGD("metadata queue is empty"); + pthread_mutex_unlock(&mReprocJobLock); + + return NO_ERROR; +} + +/*=========================================================================== + * FUNCTION : needsReprocess + * + * DESCRIPTION: Determine if reprocess is needed. + * + * PARAMETERS : + * @frame : process frame + * + * RETURN : + * TRUE if frame needs to be reprocessed + * FALSE is frame does not need to be reprocessed + * + *==========================================================================*/ +bool QCamera3PostProcessor::needsReprocess(qcamera_fwk_input_pp_data_t *frame) +{ + metadata_buffer_t* meta = (metadata_buffer_t *) frame->metadata_buffer.buffer; + bool edgeModeOn = FALSE; + bool noiseRedModeOn = FALSE; + bool reproNotDone = TRUE; + + if (frame->reproc_config.reprocess_type == REPROCESS_TYPE_NONE) { + return FALSE; + } + + // edge detection + IF_META_AVAILABLE(cam_edge_application_t, edgeMode, + CAM_INTF_META_EDGE_MODE, meta) { + edgeModeOn = (CAM_EDGE_MODE_OFF != edgeMode->edge_mode); + } + + // noise reduction + IF_META_AVAILABLE(uint32_t, noiseRedMode, + CAM_INTF_META_NOISE_REDUCTION_MODE, meta) { + noiseRedModeOn = (CAM_NOISE_REDUCTION_MODE_OFF != *noiseRedMode); + } + + IF_META_AVAILABLE(uint8_t, reprocess_flags, + CAM_INTF_META_REPROCESS_FLAGS, meta) { + reproNotDone = FALSE; + } + + return (edgeModeOn || noiseRedModeOn || reproNotDone); +} + +/*=========================================================================== + * FUNCTION : processData + * + * DESCRIPTION: enqueue data into dataProc thread + * + * PARAMETERS : + * @frame : process frame + * + * RETURN : int32_t type of status + * NO_ERROR -- success + * none-zero failure code + * + * NOTE : depends on if offline reprocess is needed, received frame will + * be sent to either input queue of postprocess or jpeg encoding + *==========================================================================*/ +int32_t QCamera3PostProcessor::processData(qcamera_fwk_input_pp_data_t *frame) +{ + if (needsReprocess(frame)) { + ATRACE_INT("Camera:Reprocess", 1); + LOGH("scheduling framework reprocess"); + pthread_mutex_lock(&mReprocJobLock); + // enqueu to post proc input queue + m_inputFWKPPQ.enqueue((void *)frame); + m_dataProcTh.sendCmd(CAMERA_CMD_TYPE_DO_NEXT_JOB, FALSE, FALSE); + pthread_mutex_unlock(&mReprocJobLock); + } else { + jpeg_settings_t *jpeg_settings = (jpeg_settings_t *)m_jpegSettingsQ.dequeue(); + + if (jpeg_settings == NULL) { + LOGE("Cannot find jpeg settings"); + return BAD_VALUE; + } + + LOGH("no need offline reprocess, sending to jpeg encoding"); + qcamera_hal3_jpeg_data_t *jpeg_job = + (qcamera_hal3_jpeg_data_t *)malloc(sizeof(qcamera_hal3_jpeg_data_t)); + if (jpeg_job == NULL) { + LOGE("No memory for jpeg job"); + return NO_MEMORY; + } + + memset(jpeg_job, 0, sizeof(qcamera_hal3_jpeg_data_t)); + jpeg_job->fwk_frame = frame; + jpeg_job->jpeg_settings = jpeg_settings; + jpeg_job->metadata = + (metadata_buffer_t *) frame->metadata_buffer.buffer; + + // enqueu to jpeg input queue + m_inputJpegQ.enqueue((void *)jpeg_job); + m_dataProcTh.sendCmd(CAMERA_CMD_TYPE_DO_NEXT_JOB, FALSE, FALSE); + } + + return NO_ERROR; +} + +/*=========================================================================== + * FUNCTION : processPPMetadata + * + * DESCRIPTION: enqueue data into dataProc thread + * + * PARAMETERS : + * @frame : process metadata frame received from pic channel + * + * RETURN : int32_t type of status + * NO_ERROR -- success + * none-zero failure code + * + *==========================================================================*/ +int32_t QCamera3PostProcessor::processPPMetadata(mm_camera_super_buf_t *reproc_meta) +{ + LOGD("E"); + pthread_mutex_lock(&mReprocJobLock); + // enqueue to metadata input queue + m_inputMetaQ.enqueue((void *)reproc_meta); + if (!(m_inputPPQ.isEmpty())) { + LOGD("pp queue is not empty, do next job"); + m_dataProcTh.sendCmd(CAMERA_CMD_TYPE_DO_NEXT_JOB, FALSE, FALSE); + } else { + LOGD("pp queue is empty, not calling do next job"); + } + pthread_mutex_unlock(&mReprocJobLock); + return NO_ERROR; +} + +/*=========================================================================== + * FUNCTION : processJpegSettingData + * + * DESCRIPTION: enqueue jpegSetting into dataProc thread + * + * PARAMETERS : + * @jpeg_settings : jpeg settings data received from pic channel + * + * RETURN : int32_t type of status + * NO_ERROR -- success + * none-zero failure code + * + *==========================================================================*/ +int32_t QCamera3PostProcessor::processJpegSettingData( + jpeg_settings_t *jpeg_settings) +{ + if (!jpeg_settings) { + LOGE("invalid jpeg settings pointer"); + return -EINVAL; + } + return m_jpegSettingsQ.enqueue((void *)jpeg_settings); +} + +/*=========================================================================== + * FUNCTION : processPPData + * + * DESCRIPTION: process received frame after reprocess. + * + * PARAMETERS : + * @frame : received frame from reprocess channel. + * + * RETURN : int32_t type of status + * NO_ERROR -- success + * none-zero failure code + * + * NOTE : The frame after reprocess need to send to jpeg encoding. + *==========================================================================*/ +int32_t QCamera3PostProcessor::processPPData(mm_camera_super_buf_t *frame) +{ + qcamera_hal3_pp_data_t *job = (qcamera_hal3_pp_data_t *)m_ongoingPPQ.dequeue(); + ATRACE_INT("Camera:Reprocess", 0); + if (job == NULL || ((NULL == job->src_frame) && (NULL == job->fwk_src_frame))) { + LOGE("Cannot find reprocess job"); + return BAD_VALUE; + } + if (job->jpeg_settings == NULL) { + LOGE("Cannot find jpeg settings"); + return BAD_VALUE; + } + + qcamera_hal3_jpeg_data_t *jpeg_job = + (qcamera_hal3_jpeg_data_t *)malloc(sizeof(qcamera_hal3_jpeg_data_t)); + if (jpeg_job == NULL) { + LOGE("No memory for jpeg job"); + return NO_MEMORY; + } + + memset(jpeg_job, 0, sizeof(qcamera_hal3_jpeg_data_t)); + jpeg_job->src_frame = frame; + if(frame != job->src_frame) + jpeg_job->src_reproc_frame = job->src_frame; + if (NULL == job->fwk_src_frame) { + jpeg_job->metadata = job->metadata; + } else { + jpeg_job->metadata = + (metadata_buffer_t *) job->fwk_src_frame->metadata_buffer.buffer; + jpeg_job->fwk_src_buffer = job->fwk_src_frame; + } + jpeg_job->src_metadata = job->src_metadata; + jpeg_job->jpeg_settings = job->jpeg_settings; + + // free pp job buf + free(job); + + // enqueu reprocessed frame to jpeg input queue + m_inputJpegQ.enqueue((void *)jpeg_job); + + // wait up data proc thread + m_dataProcTh.sendCmd(CAMERA_CMD_TYPE_DO_NEXT_JOB, FALSE, FALSE); + + return NO_ERROR; +} + +/*=========================================================================== + * FUNCTION : dequeuePPJob + * + * DESCRIPTION: find a postprocessing job from ongoing pp queue by frame number + * + * PARAMETERS : + * @frameNumber : frame number for the pp job + * + * RETURN : ptr to a pp job struct. NULL if not found. + *==========================================================================*/ +qcamera_hal3_pp_data_t *QCamera3PostProcessor::dequeuePPJob(uint32_t frameNumber) { + qcamera_hal3_pp_data_t *pp_job = NULL; + pp_job = (qcamera_hal3_pp_data_t *)m_ongoingPPQ.dequeue(); + + if (pp_job == NULL) { + LOGE("Fatal: ongoing PP queue is empty"); + return NULL; + } + if (pp_job->fwk_src_frame && + (pp_job->fwk_src_frame->frameNumber != frameNumber)) { + LOGE("head of pp queue doesn't match requested frame number"); + } + return pp_job; +} + +/*=========================================================================== + * FUNCTION : findJpegJobByJobId + * + * DESCRIPTION: find a jpeg job from ongoing Jpeg queue by its job ID + * + * PARAMETERS : + * @jobId : job Id of the job + * + * RETURN : ptr to a jpeg job struct. NULL if not found. + * + * NOTE : Currently only one job is sending to mm-jpeg-interface for jpeg + * encoding. Therefore simply dequeue from the ongoing Jpeg Queue + * will serve the purpose to find the jpeg job. + *==========================================================================*/ +qcamera_hal3_jpeg_data_t *QCamera3PostProcessor::findJpegJobByJobId(uint32_t jobId) +{ + qcamera_hal3_jpeg_data_t * job = NULL; + if (jobId == 0) { + LOGE("not a valid jpeg jobId"); + return NULL; + } + + // currely only one jpeg job ongoing, so simply dequeue the head + job = (qcamera_hal3_jpeg_data_t *)m_ongoingJpegQ.dequeue(); + return job; +} + +/*=========================================================================== + * FUNCTION : releasePPInputData + * + * DESCRIPTION: callback function to release post process input data node + * + * PARAMETERS : + * @data : ptr to post process input data + * @user_data : user data ptr (QCamera3Reprocessor) + * + * RETURN : None + *==========================================================================*/ +void QCamera3PostProcessor::releasePPInputData(void *data, void *user_data) +{ + QCamera3PostProcessor *pme = (QCamera3PostProcessor *)user_data; + if (NULL != pme) { + qcamera_hal3_pp_buffer_t *buf = (qcamera_hal3_pp_buffer_t *)data; + if (NULL != buf) { + if (buf->input) { + pme->releaseSuperBuf(buf->input); + free(buf->input); + buf->input = NULL; + } + } + } +} + +/*=========================================================================== + * FUNCTION : releaseMetaData + * + * DESCRIPTION: callback function to release metadata camera buffer + * + * PARAMETERS : + * @data : ptr to post process input data + * @user_data : user data ptr (QCamera3Reprocessor) + * + * RETURN : None + *==========================================================================*/ +void QCamera3PostProcessor::releaseMetadata(void *data, void *user_data) +{ + QCamera3PostProcessor *pme = (QCamera3PostProcessor *)user_data; + if (NULL != pme) { + pme->m_parent->metadataBufDone((mm_camera_super_buf_t *)data); + } +} + +/*=========================================================================== + * FUNCTION : releaseJpegData + * + * DESCRIPTION: callback function to release jpeg job node + * + * PARAMETERS : + * @data : ptr to ongoing jpeg job data + * @user_data : user data ptr (QCamera3Reprocessor) + * + * RETURN : None + *==========================================================================*/ +void QCamera3PostProcessor::releaseJpegData(void *data, void *user_data) +{ + QCamera3PostProcessor *pme = (QCamera3PostProcessor *)user_data; + if (NULL != pme) { + pme->releaseJpegJobData((qcamera_hal3_jpeg_data_t *)data); + } +} + +/*=========================================================================== + * FUNCTION : releaseOngoingPPData + * + * DESCRIPTION: callback function to release ongoing postprocess job node + * + * PARAMETERS : + * @data : ptr to onging postprocess job + * @user_data : user data ptr (QCamera3Reprocessor) + * + * RETURN : None + *==========================================================================*/ +void QCamera3PostProcessor::releaseOngoingPPData(void *data, void *user_data) +{ + QCamera3PostProcessor *pme = (QCamera3PostProcessor *)user_data; + if (NULL != pme) { + qcamera_hal3_pp_data_t *pp_data = (qcamera_hal3_pp_data_t *)data; + + if (pp_data && pp_data->src_frame) + pme->releaseSuperBuf(pp_data->src_frame); + + pme->releasePPJobData(pp_data); + + } +} + +/*=========================================================================== + * FUNCTION : releaseSuperBuf + * + * DESCRIPTION: function to release a superbuf frame by returning back to kernel + * + * PARAMETERS : + * @super_buf : ptr to the superbuf frame + * + * RETURN : None + *==========================================================================*/ +void QCamera3PostProcessor::releaseSuperBuf(mm_camera_super_buf_t *super_buf) +{ + if (NULL != super_buf) { + if (m_parent != NULL) { + m_parent->bufDone(super_buf); + } + } +} + +/*=========================================================================== + * FUNCTION : releaseOfflineBuffers + * + * DESCRIPTION: function to release/unmap offline buffers if any + * + * PARAMETERS : + * @allBuffers : flag that asks to release all buffers or only one + * + * RETURN : int32_t type of status + * NO_ERROR -- success + * none-zero failure code + *==========================================================================*/ +int32_t QCamera3PostProcessor::releaseOfflineBuffers(bool allBuffers) +{ + int32_t rc = NO_ERROR; + + if(NULL != m_pReprocChannel) { + rc = m_pReprocChannel->unmapOfflineBuffers(allBuffers); + } + + return rc; +} + +/*=========================================================================== + * FUNCTION : releaseJpegJobData + * + * DESCRIPTION: function to release internal resources in jpeg job struct + * + * PARAMETERS : + * @job : ptr to jpeg job struct + * + * RETURN : None + * + * NOTE : original source frame need to be queued back to kernel for + * future use. Output buf of jpeg job need to be released since + * it's allocated for each job. Exif object need to be deleted. + *==========================================================================*/ +void QCamera3PostProcessor::releaseJpegJobData(qcamera_hal3_jpeg_data_t *job) +{ + ATRACE_CALL(); + int32_t rc = NO_ERROR; + LOGD("E"); + if (NULL != job) { + if (NULL != job->src_reproc_frame) { + free(job->src_reproc_frame); + job->src_reproc_frame = NULL; + } + + if (NULL != job->src_frame) { + if (NULL != m_pReprocChannel) { + rc = m_pReprocChannel->bufDone(job->src_frame); + if (NO_ERROR != rc) + LOGE("bufDone error: %d", rc); + } + free(job->src_frame); + job->src_frame = NULL; + } + + if (NULL != job->fwk_src_buffer) { + free(job->fwk_src_buffer); + job->fwk_src_buffer = NULL; + } else if (NULL != job->src_metadata) { + m_parent->metadataBufDone(job->src_metadata); + free(job->src_metadata); + job->src_metadata = NULL; + } + + if (NULL != job->fwk_frame) { + free(job->fwk_frame); + job->fwk_frame = NULL; + } + + if (NULL != job->pJpegExifObj) { + delete job->pJpegExifObj; + job->pJpegExifObj = NULL; + } + + if (NULL != job->jpeg_settings) { + free(job->jpeg_settings); + job->jpeg_settings = NULL; + } + } + /* Additional trigger to process any pending jobs in the input queue */ + m_dataProcTh.sendCmd(CAMERA_CMD_TYPE_DO_NEXT_JOB, FALSE, FALSE); + LOGD("X"); +} + +/*=========================================================================== + * FUNCTION : releasePPJobData + * + * DESCRIPTION: function to release internal resources in p pjob struct + * + * PARAMETERS : + * @job : ptr to pp job struct + * + * RETURN : None + * + * NOTE : Original source metadata buffer needs to be released and + * queued back to kernel for future use. src_frame, src_metadata, + * and fwk_src_frame structures need to be freed. + *==========================================================================*/ +void QCamera3PostProcessor::releasePPJobData(qcamera_hal3_pp_data_t *pp_job) +{ + ATRACE_CALL(); + LOGD("E"); + if (NULL != pp_job) { + if (NULL != pp_job->src_frame) { + free(pp_job->src_frame); + if (NULL != pp_job->src_metadata) { + m_parent->metadataBufDone(pp_job->src_metadata); + free(pp_job->src_metadata); + } + pp_job->src_frame = NULL; + pp_job->metadata = NULL; + } + + if (NULL != pp_job->fwk_src_frame) { + free(pp_job->fwk_src_frame); + pp_job->fwk_src_frame = NULL; + } + } + + /* Additional trigger to process any pending jobs in the input queue */ + m_dataProcTh.sendCmd(CAMERA_CMD_TYPE_DO_NEXT_JOB, FALSE, FALSE); + LOGD("X"); +} + +/*=========================================================================== + * FUNCTION : getColorfmtFromImgFmt + * + * DESCRIPTION: function to return jpeg color format based on its image format + * + * PARAMETERS : + * @img_fmt : image format + * + * RETURN : jpeg color format that can be understandable by omx lib + *==========================================================================*/ +mm_jpeg_color_format QCamera3PostProcessor::getColorfmtFromImgFmt(cam_format_t img_fmt) +{ + switch (img_fmt) { + case CAM_FORMAT_YUV_420_NV21: + case CAM_FORMAT_YUV_420_NV21_VENUS: + return MM_JPEG_COLOR_FORMAT_YCRCBLP_H2V2; + case CAM_FORMAT_YUV_420_NV21_ADRENO: + return MM_JPEG_COLOR_FORMAT_YCRCBLP_H2V2; + case CAM_FORMAT_YUV_420_NV12: + case CAM_FORMAT_YUV_420_NV12_VENUS: + return MM_JPEG_COLOR_FORMAT_YCBCRLP_H2V2; + case CAM_FORMAT_YUV_420_YV12: + return MM_JPEG_COLOR_FORMAT_YCBCRLP_H2V2; + case CAM_FORMAT_YUV_422_NV61: + return MM_JPEG_COLOR_FORMAT_YCRCBLP_H2V1; + case CAM_FORMAT_YUV_422_NV16: + return MM_JPEG_COLOR_FORMAT_YCBCRLP_H2V1; + default: + return MM_JPEG_COLOR_FORMAT_YCRCBLP_H2V2; + } +} + +/*=========================================================================== + * FUNCTION : getJpegImgTypeFromImgFmt + * + * DESCRIPTION: function to return jpeg encode image type based on its image format + * + * PARAMETERS : + * @img_fmt : image format + * + * RETURN : return jpeg source image format (YUV or Bitstream) + *==========================================================================*/ +mm_jpeg_format_t QCamera3PostProcessor::getJpegImgTypeFromImgFmt(cam_format_t img_fmt) +{ + switch (img_fmt) { + case CAM_FORMAT_YUV_420_NV21: + case CAM_FORMAT_YUV_420_NV21_ADRENO: + case CAM_FORMAT_YUV_420_NV12: + case CAM_FORMAT_YUV_420_NV12_VENUS: + case CAM_FORMAT_YUV_420_NV21_VENUS: + case CAM_FORMAT_YUV_420_YV12: + case CAM_FORMAT_YUV_422_NV61: + case CAM_FORMAT_YUV_422_NV16: + return MM_JPEG_FMT_YUV; + default: + return MM_JPEG_FMT_YUV; + } +} + +/*=========================================================================== + * FUNCTION : encodeFWKData + * + * DESCRIPTION: function to prepare encoding job information and send to + * mm-jpeg-interface to do the encoding job + * + * PARAMETERS : + * @jpeg_job_data : ptr to a struct saving job related information + * @needNewSess : flag to indicate if a new jpeg encoding session need + * to be created. After creation, this flag will be toggled + * + * RETURN : int32_t type of status + * NO_ERROR -- success + * none-zero failure code + *==========================================================================*/ +int32_t QCamera3PostProcessor::encodeFWKData(qcamera_hal3_jpeg_data_t *jpeg_job_data, + uint8_t &needNewSess) +{ + LOGD("E"); + int32_t ret = NO_ERROR; + mm_jpeg_job_t jpg_job; + uint32_t jobId = 0; + qcamera_fwk_input_pp_data_t *recvd_frame = NULL; + metadata_buffer_t *metadata = NULL; + jpeg_settings_t *jpeg_settings = NULL; + QCamera3HardwareInterface* hal_obj = NULL; + mm_jpeg_debug_exif_params_t *exif_debug_params = NULL; + bool needJpegExifRotation = false; + + if (NULL == jpeg_job_data) { + LOGE("Invalid jpeg job"); + return BAD_VALUE; + } + + recvd_frame = jpeg_job_data->fwk_frame; + if (NULL == recvd_frame) { + LOGE("Invalid input buffer"); + return BAD_VALUE; + } + + metadata = jpeg_job_data->metadata; + if (NULL == metadata) { + LOGE("Invalid metadata buffer"); + return BAD_VALUE; + } + + jpeg_settings = jpeg_job_data->jpeg_settings; + if (NULL == jpeg_settings) { + LOGE("Invalid jpeg settings buffer"); + return BAD_VALUE; + } + + if ((NULL != jpeg_job_data->src_frame) && (NULL != jpeg_job_data->src_frame)) { + LOGE("Unsupported case both framework and camera source buffers are invalid!"); + return BAD_VALUE; + } + + hal_obj = (QCamera3HardwareInterface*)m_parent->mUserData; + + if (mJpegClientHandle <= 0) { + LOGE("Error: bug here, mJpegClientHandle is 0"); + return UNKNOWN_ERROR; + } + + cam_dimension_t src_dim; + memset(&src_dim, 0, sizeof(cam_dimension_t)); + src_dim.width = recvd_frame->reproc_config.input_stream_dim.width; + src_dim.height = recvd_frame->reproc_config.input_stream_dim.height; + + cam_dimension_t dst_dim; + memset(&dst_dim, 0, sizeof(cam_dimension_t)); + dst_dim.width = recvd_frame->reproc_config.output_stream_dim.width; + dst_dim.height = recvd_frame->reproc_config.output_stream_dim.height; + + cam_rect_t crop; + memset(&crop, 0, sizeof(cam_rect_t)); + //TBD_later - Zoom event removed in stream + //main_stream->getCropInfo(crop); + + // Set JPEG encode crop in reprocess frame metadata + // If this JPEG crop info exist, encoder should do cropping + IF_META_AVAILABLE(cam_stream_crop_info_t, jpeg_crop, + CAM_INTF_PARM_JPEG_ENCODE_CROP, metadata) { + memcpy(&crop, &(jpeg_crop->crop), sizeof(cam_rect_t)); + } + + // Set JPEG encode crop in reprocess frame metadata + // If this JPEG scale info exist, encoder should do scaling + IF_META_AVAILABLE(cam_dimension_t, scale_dim, + CAM_INTF_PARM_JPEG_SCALE_DIMENSION, metadata) { + if (scale_dim->width != 0 && scale_dim->height != 0) { + dst_dim.width = scale_dim->width; + dst_dim.height = scale_dim->height; + } + } + + needJpegExifRotation = (hal_obj->needJpegExifRotation() || !needsReprocess(recvd_frame)); + + // If EXIF rotation metadata is added and used to match the JPEG orientation, + // it means CPP rotation is not involved, whether it is because CPP does not + // support rotation, or the reprocessed frame is not sent to CPP. + // Override CAM_INTF_PARM_ROTATION to 0 to avoid wrong CPP rotation info + // to be filled in to JPEG metadata. + if (needJpegExifRotation) { + cam_rotation_info_t rotation_info; + memset(&rotation_info, 0, sizeof(rotation_info)); + rotation_info.rotation = ROTATE_0; + rotation_info.streamId = 0; + ADD_SET_PARAM_ENTRY_TO_BATCH(metadata, CAM_INTF_PARM_ROTATION, rotation_info); + } + + LOGH("Need new session?:%d", needNewSess); + if (needNewSess) { + //creating a new session, so we must destroy the old one + if ( 0 < mJpegSessionId ) { + ret = mJpegHandle.destroy_session(mJpegSessionId); + if (ret != NO_ERROR) { + LOGE("Error destroying an old jpeg encoding session, id = %d", + mJpegSessionId); + return ret; + } + mJpegSessionId = 0; + } + // create jpeg encoding session + mm_jpeg_encode_params_t encodeParam; + memset(&encodeParam, 0, sizeof(mm_jpeg_encode_params_t)); + getFWKJpegEncodeConfig(encodeParam, recvd_frame, jpeg_settings); + LOGH("#src bufs:%d # tmb bufs:%d #dst_bufs:%d", + encodeParam.num_src_bufs,encodeParam.num_tmb_bufs,encodeParam.num_dst_bufs); + if (!needJpegExifRotation && + (jpeg_settings->jpeg_orientation == 90 || + jpeg_settings->jpeg_orientation == 270)) { + // swap src width and height, stride and scanline due to rotation + encodeParam.main_dim.src_dim.width = src_dim.height; + encodeParam.main_dim.src_dim.height = src_dim.width; + encodeParam.thumb_dim.src_dim.width = src_dim.height; + encodeParam.thumb_dim.src_dim.height = src_dim.width; + + int32_t temp = encodeParam.src_main_buf[0].offset.mp[0].stride; + encodeParam.src_main_buf[0].offset.mp[0].stride = + encodeParam.src_main_buf[0].offset.mp[0].scanline; + encodeParam.src_main_buf[0].offset.mp[0].scanline = temp; + + temp = encodeParam.src_thumb_buf[0].offset.mp[0].stride; + encodeParam.src_thumb_buf[0].offset.mp[0].stride = + encodeParam.src_thumb_buf[0].offset.mp[0].scanline; + encodeParam.src_thumb_buf[0].offset.mp[0].scanline = temp; + } else { + encodeParam.main_dim.src_dim = src_dim; + encodeParam.thumb_dim.src_dim = src_dim; + } + encodeParam.main_dim.dst_dim = dst_dim; + encodeParam.thumb_dim.dst_dim = jpeg_settings->thumbnail_size; + + if (needJpegExifRotation) { + encodeParam.thumb_rotation = (uint32_t)jpeg_settings->jpeg_orientation; + } + + LOGI("Src Buffer cnt = %d, res = %dX%d len = %d rot = %d " + "src_dim = %dX%d dst_dim = %dX%d", + encodeParam.num_src_bufs, + encodeParam.src_main_buf[0].offset.mp[0].stride, + encodeParam.src_main_buf[0].offset.mp[0].scanline, + encodeParam.src_main_buf[0].offset.frame_len, + encodeParam.rotation, + src_dim.width, src_dim.height, + dst_dim.width, dst_dim.height); + LOGI("Src THUMB buf_cnt = %d, res = %dX%d len = %d rot = %d " + "src_dim = %dX%d, dst_dim = %dX%d", + encodeParam.num_tmb_bufs, + encodeParam.src_thumb_buf[0].offset.mp[0].stride, + encodeParam.src_thumb_buf[0].offset.mp[0].scanline, + encodeParam.src_thumb_buf[0].offset.frame_len, + encodeParam.thumb_rotation, + encodeParam.thumb_dim.src_dim.width, + encodeParam.thumb_dim.src_dim.height, + encodeParam.thumb_dim.dst_dim.width, + encodeParam.thumb_dim.dst_dim.height); + + LOGH("#src bufs:%d # tmb bufs:%d #dst_bufs:%d", + encodeParam.num_src_bufs,encodeParam.num_tmb_bufs,encodeParam.num_dst_bufs); + + ret = mJpegHandle.create_session(mJpegClientHandle, &encodeParam, &mJpegSessionId); + if (ret != NO_ERROR) { + LOGE("Error creating a new jpeg encoding session, ret = %d", ret); + return ret; + } + needNewSess = FALSE; + } + + // Fill in new job + memset(&jpg_job, 0, sizeof(mm_jpeg_job_t)); + jpg_job.job_type = JPEG_JOB_TYPE_ENCODE; + jpg_job.encode_job.session_id = mJpegSessionId; + jpg_job.encode_job.src_index = 0; + jpg_job.encode_job.dst_index = 0; + + // Set main dim job parameters and handle rotation + if (!needJpegExifRotation && (jpeg_settings->jpeg_orientation == 90 || + jpeg_settings->jpeg_orientation == 270)) { + + jpg_job.encode_job.main_dim.src_dim.width = src_dim.height; + jpg_job.encode_job.main_dim.src_dim.height = src_dim.width; + + jpg_job.encode_job.main_dim.dst_dim.width = dst_dim.height; + jpg_job.encode_job.main_dim.dst_dim.height = dst_dim.width; + + jpg_job.encode_job.main_dim.crop.width = crop.height; + jpg_job.encode_job.main_dim.crop.height = crop.width; + jpg_job.encode_job.main_dim.crop.left = crop.top; + jpg_job.encode_job.main_dim.crop.top = crop.left; + } else { + jpg_job.encode_job.main_dim.src_dim = src_dim; + jpg_job.encode_job.main_dim.dst_dim = dst_dim; + jpg_job.encode_job.main_dim.crop = crop; + } + + // get 3a sw version info + cam_q3a_version_t sw_version; + memset(&sw_version, 0, sizeof(sw_version)); + if (hal_obj) + hal_obj->get3AVersion(sw_version); + + // get exif data + QCamera3Exif *pJpegExifObj = getExifData(metadata, jpeg_settings, needJpegExifRotation); + jpeg_job_data->pJpegExifObj = pJpegExifObj; + if (pJpegExifObj != NULL) { + jpg_job.encode_job.exif_info.exif_data = pJpegExifObj->getEntries(); + jpg_job.encode_job.exif_info.numOfEntries = + pJpegExifObj->getNumOfEntries(); + jpg_job.encode_job.exif_info.debug_data.sw_3a_version[0] = + sw_version.major_version; + jpg_job.encode_job.exif_info.debug_data.sw_3a_version[1] = + sw_version.minor_version; + jpg_job.encode_job.exif_info.debug_data.sw_3a_version[2] = + sw_version.patch_version; + jpg_job.encode_job.exif_info.debug_data.sw_3a_version[3] = + sw_version.new_feature_des; + } + + // thumbnail dim + LOGH("Thumbnail needed:%d", m_bThumbnailNeeded); + if (m_bThumbnailNeeded == TRUE) { + jpg_job.encode_job.thumb_dim.dst_dim = + jpeg_settings->thumbnail_size; + + if (!needJpegExifRotation && (jpeg_settings->jpeg_orientation == 90 || + jpeg_settings->jpeg_orientation == 270)) { + //swap the thumbnail destination width and height if it has + //already been rotated + int temp = jpg_job.encode_job.thumb_dim.dst_dim.width; + jpg_job.encode_job.thumb_dim.dst_dim.width = + jpg_job.encode_job.thumb_dim.dst_dim.height; + jpg_job.encode_job.thumb_dim.dst_dim.height = temp; + + jpg_job.encode_job.thumb_dim.src_dim.width = src_dim.height; + jpg_job.encode_job.thumb_dim.src_dim.height = src_dim.width; + + jpg_job.encode_job.thumb_dim.crop.width = crop.height; + jpg_job.encode_job.thumb_dim.crop.height = crop.width; + jpg_job.encode_job.thumb_dim.crop.left = crop.top; + jpg_job.encode_job.thumb_dim.crop.top = crop.left; + } else { + jpg_job.encode_job.thumb_dim.src_dim = src_dim; + jpg_job.encode_job.thumb_dim.crop = crop; + } + jpg_job.encode_job.thumb_index = 0; + } + + jpg_job.encode_job.cam_exif_params = hal_obj->get3AExifParams(); + exif_debug_params = jpg_job.encode_job.cam_exif_params.debug_params; + // Fill in exif debug data + // Allocate for a local copy of debug parameters + jpg_job.encode_job.cam_exif_params.debug_params = + (mm_jpeg_debug_exif_params_t *) malloc (sizeof(mm_jpeg_debug_exif_params_t)); + if (!jpg_job.encode_job.cam_exif_params.debug_params) { + LOGE("Out of Memory. Allocation failed for 3A debug exif params"); + return NO_MEMORY; + } + + jpg_job.encode_job.mobicat_mask = hal_obj->getMobicatMask(); + + if (metadata != NULL) { + // Fill in the metadata passed as parameter + jpg_job.encode_job.p_metadata = metadata; + + jpg_job.encode_job.p_metadata->is_mobicat_aec_params_valid = + jpg_job.encode_job.cam_exif_params.cam_3a_params_valid; + + if (jpg_job.encode_job.cam_exif_params.cam_3a_params_valid) { + jpg_job.encode_job.p_metadata->mobicat_aec_params = + jpg_job.encode_job.cam_exif_params.cam_3a_params; + } + + if (exif_debug_params) { + // Copy debug parameters locally. + memcpy(jpg_job.encode_job.cam_exif_params.debug_params, + exif_debug_params, (sizeof(mm_jpeg_debug_exif_params_t))); + /* Save a copy of 3A debug params */ + jpg_job.encode_job.p_metadata->is_statsdebug_ae_params_valid = + jpg_job.encode_job.cam_exif_params.debug_params->ae_debug_params_valid; + jpg_job.encode_job.p_metadata->is_statsdebug_awb_params_valid = + jpg_job.encode_job.cam_exif_params.debug_params->awb_debug_params_valid; + jpg_job.encode_job.p_metadata->is_statsdebug_af_params_valid = + jpg_job.encode_job.cam_exif_params.debug_params->af_debug_params_valid; + jpg_job.encode_job.p_metadata->is_statsdebug_asd_params_valid = + jpg_job.encode_job.cam_exif_params.debug_params->asd_debug_params_valid; + jpg_job.encode_job.p_metadata->is_statsdebug_stats_params_valid = + jpg_job.encode_job.cam_exif_params.debug_params->stats_debug_params_valid; + jpg_job.encode_job.p_metadata->is_statsdebug_bestats_params_valid = + jpg_job.encode_job.cam_exif_params.debug_params->bestats_debug_params_valid; + jpg_job.encode_job.p_metadata->is_statsdebug_bhist_params_valid = + jpg_job.encode_job.cam_exif_params.debug_params->bhist_debug_params_valid; + jpg_job.encode_job.p_metadata->is_statsdebug_3a_tuning_params_valid = + jpg_job.encode_job.cam_exif_params.debug_params->q3a_tuning_debug_params_valid; + + if (jpg_job.encode_job.cam_exif_params.debug_params->ae_debug_params_valid) { + jpg_job.encode_job.p_metadata->statsdebug_ae_data = + jpg_job.encode_job.cam_exif_params.debug_params->ae_debug_params; + } + if (jpg_job.encode_job.cam_exif_params.debug_params->awb_debug_params_valid) { + jpg_job.encode_job.p_metadata->statsdebug_awb_data = + jpg_job.encode_job.cam_exif_params.debug_params->awb_debug_params; + } + if (jpg_job.encode_job.cam_exif_params.debug_params->af_debug_params_valid) { + jpg_job.encode_job.p_metadata->statsdebug_af_data = + jpg_job.encode_job.cam_exif_params.debug_params->af_debug_params; + } + if (jpg_job.encode_job.cam_exif_params.debug_params->asd_debug_params_valid) { + jpg_job.encode_job.p_metadata->statsdebug_asd_data = + jpg_job.encode_job.cam_exif_params.debug_params->asd_debug_params; + } + if (jpg_job.encode_job.cam_exif_params.debug_params->stats_debug_params_valid) { + jpg_job.encode_job.p_metadata->statsdebug_stats_buffer_data = + jpg_job.encode_job.cam_exif_params.debug_params->stats_debug_params; + } + if (jpg_job.encode_job.cam_exif_params.debug_params->bestats_debug_params_valid) { + jpg_job.encode_job.p_metadata->statsdebug_bestats_buffer_data = + jpg_job.encode_job.cam_exif_params.debug_params->bestats_debug_params; + } + if (jpg_job.encode_job.cam_exif_params.debug_params->bhist_debug_params_valid) { + jpg_job.encode_job.p_metadata->statsdebug_bhist_data = + jpg_job.encode_job.cam_exif_params.debug_params->bhist_debug_params; + } + if (jpg_job.encode_job.cam_exif_params.debug_params->q3a_tuning_debug_params_valid) { + jpg_job.encode_job.p_metadata->statsdebug_3a_tuning_data = + jpg_job.encode_job.cam_exif_params.debug_params->q3a_tuning_debug_params; + } + } + } else { + LOGW("Metadata is null"); + } + + // Multi image info + if (hal_obj->isDeviceLinked() == TRUE) { + jpg_job.encode_job.multi_image_info.type = MM_JPEG_TYPE_JPEG; + jpg_job.encode_job.multi_image_info.num_of_images = 1; + jpg_job.encode_job.multi_image_info.enable_metadata = 1; + if (hal_obj->isMainCamera() == TRUE) { + jpg_job.encode_job.multi_image_info.is_primary = 1; + } else { + jpg_job.encode_job.multi_image_info.is_primary = 0; + } + } + + jpg_job.encode_job.hal_version = CAM_HAL_V3; + + //Start jpeg encoding + ret = mJpegHandle.start_job(&jpg_job, &jobId); + if (jpg_job.encode_job.cam_exif_params.debug_params) { + free(jpg_job.encode_job.cam_exif_params.debug_params); + } + if (ret == NO_ERROR) { + // remember job info + jpeg_job_data->jobId = jobId; + } + + LOGD("X"); + return ret; +} + +/*=========================================================================== + * FUNCTION : encodeData + * + * DESCRIPTION: function to prepare encoding job information and send to + * mm-jpeg-interface to do the encoding job + * + * PARAMETERS : + * @jpeg_job_data : ptr to a struct saving job related information + * @needNewSess : flag to indicate if a new jpeg encoding session need + * to be created. After creation, this flag will be toggled + * + * RETURN : int32_t type of status + * NO_ERROR -- success + * none-zero failure code + *==========================================================================*/ +int32_t QCamera3PostProcessor::encodeData(qcamera_hal3_jpeg_data_t *jpeg_job_data, + uint8_t &needNewSess) +{ + ATRACE_CALL(); + LOGD("E"); + int32_t ret = NO_ERROR; + mm_jpeg_job_t jpg_job; + uint32_t jobId = 0; + QCamera3Stream *main_stream = NULL; + mm_camera_buf_def_t *main_frame = NULL; + QCamera3Channel *srcChannel = NULL; + mm_camera_super_buf_t *recvd_frame = NULL; + metadata_buffer_t *metadata = NULL; + jpeg_settings_t *jpeg_settings = NULL; + QCamera3HardwareInterface* hal_obj = NULL; + mm_jpeg_debug_exif_params_t *exif_debug_params = NULL; + if (m_parent != NULL) { + hal_obj = (QCamera3HardwareInterface*)m_parent->mUserData; + } else { + LOGE("m_parent is NULL, Error"); + return BAD_VALUE; + } + bool needJpegExifRotation = false; + + recvd_frame = jpeg_job_data->src_frame; + metadata = jpeg_job_data->metadata; + jpeg_settings = jpeg_job_data->jpeg_settings; + + LOGD("encoding bufIndex: %u", + jpeg_job_data->src_frame->bufs[0]->buf_idx); + + QCamera3Channel *pChannel = NULL; + // first check picture channel + if (m_parent->getMyHandle() == recvd_frame->ch_id) { + pChannel = m_parent; + } + // check reprocess channel if not found + if (pChannel == NULL) { + if (m_pReprocChannel != NULL && + m_pReprocChannel->getMyHandle() == recvd_frame->ch_id) { + pChannel = m_pReprocChannel; + } + } + + srcChannel = pChannel; + + if (srcChannel == NULL) { + LOGE("No corresponding channel (ch_id = %d) exist, return here", + recvd_frame->ch_id); + return BAD_VALUE; + } + + // find snapshot frame and thumnail frame + //Note: In this version we will receive only snapshot frame. + for (uint32_t i = 0; i < recvd_frame->num_bufs; i++) { + QCamera3Stream *srcStream = + srcChannel->getStreamByHandle(recvd_frame->bufs[i]->stream_id); + if (srcStream != NULL) { + switch (srcStream->getMyType()) { + case CAM_STREAM_TYPE_SNAPSHOT: + case CAM_STREAM_TYPE_OFFLINE_PROC: + main_stream = srcStream; + main_frame = recvd_frame->bufs[i]; + break; + default: + break; + } + } + } + + if(NULL == main_frame){ + LOGE("Main frame is NULL"); + return BAD_VALUE; + } + + QCamera3StreamMem *memObj = (QCamera3StreamMem *)main_frame->mem_info; + if (NULL == memObj) { + LOGE("Memeory Obj of main frame is NULL"); + return NO_MEMORY; + } + + // clean and invalidate cache ops through mem obj of the frame + memObj->cleanInvalidateCache(main_frame->buf_idx); + + if (mJpegClientHandle <= 0) { + LOGE("Error: bug here, mJpegClientHandle is 0"); + return UNKNOWN_ERROR; + } + cam_dimension_t src_dim; + memset(&src_dim, 0, sizeof(cam_dimension_t)); + main_stream->getFrameDimension(src_dim); + + cam_dimension_t dst_dim; + memset(&dst_dim, 0, sizeof(cam_dimension_t)); + if (NO_ERROR != m_parent->getStreamSize(dst_dim)) { + LOGE("Failed to get size of the JPEG stream"); + return UNKNOWN_ERROR; + } + + needJpegExifRotation = hal_obj->needJpegExifRotation(); + IF_META_AVAILABLE(cam_rotation_info_t, rotation_info, CAM_INTF_PARM_ROTATION, metadata) { + if (jpeg_settings->jpeg_orientation != 0 && rotation_info->rotation == ROTATE_0) { + needJpegExifRotation = TRUE; + LOGH("Need EXIF JPEG ROTATION"); + } + } + + // Although in HAL3, legacy flip mode is not advertised + // default value of CAM_INTF_PARM_FLIP is still added here + // for jpge metadata + int32_t flipMode = 0; // no flip + ADD_SET_PARAM_ENTRY_TO_BATCH(metadata, CAM_INTF_PARM_FLIP, flipMode); + + LOGH("Need new session?:%d", needNewSess); + if (needNewSess) { + //creating a new session, so we must destroy the old one + if ( 0 < mJpegSessionId ) { + ret = mJpegHandle.destroy_session(mJpegSessionId); + if (ret != NO_ERROR) { + LOGE("Error destroying an old jpeg encoding session, id = %d", + mJpegSessionId); + return ret; + } + mJpegSessionId = 0; + } + // create jpeg encoding session + mm_jpeg_encode_params_t encodeParam; + memset(&encodeParam, 0, sizeof(mm_jpeg_encode_params_t)); + getJpegEncodeConfig(encodeParam, main_stream, jpeg_settings); + LOGH("#src bufs:%d # tmb bufs:%d #dst_bufs:%d", + encodeParam.num_src_bufs,encodeParam.num_tmb_bufs,encodeParam.num_dst_bufs); + if (!needJpegExifRotation && + (jpeg_settings->jpeg_orientation == 90 || + jpeg_settings->jpeg_orientation == 270)) { + //swap src width and height, stride and scanline due to rotation + encodeParam.main_dim.src_dim.width = src_dim.height; + encodeParam.main_dim.src_dim.height = src_dim.width; + encodeParam.thumb_dim.src_dim.width = src_dim.height; + encodeParam.thumb_dim.src_dim.height = src_dim.width; + + int32_t temp = encodeParam.src_main_buf[0].offset.mp[0].stride; + encodeParam.src_main_buf[0].offset.mp[0].stride = + encodeParam.src_main_buf[0].offset.mp[0].scanline; + encodeParam.src_main_buf[0].offset.mp[0].scanline = temp; + + temp = encodeParam.src_thumb_buf[0].offset.mp[0].stride; + encodeParam.src_thumb_buf[0].offset.mp[0].stride = + encodeParam.src_thumb_buf[0].offset.mp[0].scanline; + encodeParam.src_thumb_buf[0].offset.mp[0].scanline = temp; + } else { + encodeParam.main_dim.src_dim = src_dim; + encodeParam.thumb_dim.src_dim = src_dim; + } + encodeParam.main_dim.dst_dim = dst_dim; + encodeParam.thumb_dim.dst_dim = jpeg_settings->thumbnail_size; + + if (needJpegExifRotation) { + encodeParam.thumb_rotation = (uint32_t)jpeg_settings->jpeg_orientation; + } + + LOGI("Src Buffer cnt = %d, res = %dX%d len = %d rot = %d " + "src_dim = %dX%d dst_dim = %dX%d", + encodeParam.num_src_bufs, + encodeParam.src_main_buf[0].offset.mp[0].stride, + encodeParam.src_main_buf[0].offset.mp[0].scanline, + encodeParam.src_main_buf[0].offset.frame_len, + encodeParam.rotation, + src_dim.width, src_dim.height, + dst_dim.width, dst_dim.height); + LOGI("Src THUMB buf_cnt = %d, res = %dX%d len = %d rot = %d " + "src_dim = %dX%d, dst_dim = %dX%d", + encodeParam.num_tmb_bufs, + encodeParam.src_thumb_buf[0].offset.mp[0].stride, + encodeParam.src_thumb_buf[0].offset.mp[0].scanline, + encodeParam.src_thumb_buf[0].offset.frame_len, + encodeParam.thumb_rotation, + encodeParam.thumb_dim.src_dim.width, + encodeParam.thumb_dim.src_dim.height, + encodeParam.thumb_dim.dst_dim.width, + encodeParam.thumb_dim.dst_dim.height); + ret = mJpegHandle.create_session(mJpegClientHandle, &encodeParam, &mJpegSessionId); + if (ret != NO_ERROR) { + LOGE("Error creating a new jpeg encoding session, ret = %d", ret); + return ret; + } + needNewSess = FALSE; + } + + // Fill in new job + memset(&jpg_job, 0, sizeof(mm_jpeg_job_t)); + jpg_job.job_type = JPEG_JOB_TYPE_ENCODE; + jpg_job.encode_job.session_id = mJpegSessionId; + jpg_job.encode_job.src_index = (int32_t)main_frame->buf_idx; + jpg_job.encode_job.dst_index = 0; + + cam_rect_t crop; + memset(&crop, 0, sizeof(cam_rect_t)); + //TBD_later - Zoom event removed in stream + //main_stream->getCropInfo(crop); + + // Set main dim job parameters and handle rotation + if (!needJpegExifRotation && (jpeg_settings->jpeg_orientation == 90 || + jpeg_settings->jpeg_orientation == 270)) { + + jpg_job.encode_job.main_dim.src_dim.width = src_dim.height; + jpg_job.encode_job.main_dim.src_dim.height = src_dim.width; + + jpg_job.encode_job.main_dim.dst_dim.width = dst_dim.height; + jpg_job.encode_job.main_dim.dst_dim.height = dst_dim.width; + + jpg_job.encode_job.main_dim.crop.width = crop.height; + jpg_job.encode_job.main_dim.crop.height = crop.width; + jpg_job.encode_job.main_dim.crop.left = crop.top; + jpg_job.encode_job.main_dim.crop.top = crop.left; + } else { + jpg_job.encode_job.main_dim.src_dim = src_dim; + jpg_job.encode_job.main_dim.dst_dim = dst_dim; + jpg_job.encode_job.main_dim.crop = crop; + } + + // get 3a sw version info + cam_q3a_version_t sw_version; + memset(&sw_version, 0, sizeof(sw_version)); + + if (hal_obj) + hal_obj->get3AVersion(sw_version); + + // get exif data + QCamera3Exif *pJpegExifObj = getExifData(metadata, jpeg_settings, needJpegExifRotation); + jpeg_job_data->pJpegExifObj = pJpegExifObj; + if (pJpegExifObj != NULL) { + jpg_job.encode_job.exif_info.exif_data = pJpegExifObj->getEntries(); + jpg_job.encode_job.exif_info.numOfEntries = + pJpegExifObj->getNumOfEntries(); + jpg_job.encode_job.exif_info.debug_data.sw_3a_version[0] = + sw_version.major_version; + jpg_job.encode_job.exif_info.debug_data.sw_3a_version[1] = + sw_version.minor_version; + jpg_job.encode_job.exif_info.debug_data.sw_3a_version[2] = + sw_version.patch_version; + jpg_job.encode_job.exif_info.debug_data.sw_3a_version[3] = + sw_version.new_feature_des; + } + + // thumbnail dim + LOGH("Thumbnail needed:%d", m_bThumbnailNeeded); + if (m_bThumbnailNeeded == TRUE) { + jpg_job.encode_job.thumb_dim.dst_dim = + jpeg_settings->thumbnail_size; + + if (!needJpegExifRotation && + (jpeg_settings->jpeg_orientation == 90 || + jpeg_settings->jpeg_orientation == 270)) { + //swap the thumbnail destination width and height if it has + //already been rotated + int temp = jpg_job.encode_job.thumb_dim.dst_dim.width; + jpg_job.encode_job.thumb_dim.dst_dim.width = + jpg_job.encode_job.thumb_dim.dst_dim.height; + jpg_job.encode_job.thumb_dim.dst_dim.height = temp; + + jpg_job.encode_job.thumb_dim.src_dim.width = src_dim.height; + jpg_job.encode_job.thumb_dim.src_dim.height = src_dim.width; + + jpg_job.encode_job.thumb_dim.crop.width = crop.height; + jpg_job.encode_job.thumb_dim.crop.height = crop.width; + jpg_job.encode_job.thumb_dim.crop.left = crop.top; + jpg_job.encode_job.thumb_dim.crop.top = crop.left; + } else { + jpg_job.encode_job.thumb_dim.src_dim = src_dim; + jpg_job.encode_job.thumb_dim.crop = crop; + } + jpg_job.encode_job.thumb_index = main_frame->buf_idx; + LOGI("Thumbnail idx = %d src w/h (%dx%d), dst w/h (%dx%d)", + jpg_job.encode_job.thumb_index, + jpg_job.encode_job.thumb_dim.src_dim.width, + jpg_job.encode_job.thumb_dim.src_dim.height, + jpg_job.encode_job.thumb_dim.dst_dim.width, + jpg_job.encode_job.thumb_dim.dst_dim.height); + } + LOGI("Main image idx = %d src w/h (%dx%d), dst w/h (%dx%d)", + jpg_job.encode_job.src_index, + jpg_job.encode_job.main_dim.src_dim.width, + jpg_job.encode_job.main_dim.src_dim.height, + jpg_job.encode_job.main_dim.dst_dim.width, + jpg_job.encode_job.main_dim.dst_dim.height); + + jpg_job.encode_job.cam_exif_params = hal_obj->get3AExifParams(); + exif_debug_params = jpg_job.encode_job.cam_exif_params.debug_params; + + // Allocate for a local copy of debug parameters + jpg_job.encode_job.cam_exif_params.debug_params = + (mm_jpeg_debug_exif_params_t *) malloc (sizeof(mm_jpeg_debug_exif_params_t)); + if (!jpg_job.encode_job.cam_exif_params.debug_params) { + LOGE("Out of Memory. Allocation failed for 3A debug exif params"); + return NO_MEMORY; + } + + jpg_job.encode_job.mobicat_mask = hal_obj->getMobicatMask(); + + if (metadata != NULL) { + //Fill in the metadata passed as parameter + jpg_job.encode_job.p_metadata = metadata; + + jpg_job.encode_job.p_metadata->is_mobicat_aec_params_valid = + jpg_job.encode_job.cam_exif_params.cam_3a_params_valid; + + if (jpg_job.encode_job.cam_exif_params.cam_3a_params_valid) { + jpg_job.encode_job.p_metadata->mobicat_aec_params = + jpg_job.encode_job.cam_exif_params.cam_3a_params; + } + + if (exif_debug_params) { + // Copy debug parameters locally. + memcpy(jpg_job.encode_job.cam_exif_params.debug_params, + exif_debug_params, (sizeof(mm_jpeg_debug_exif_params_t))); + /* Save a copy of 3A debug params */ + jpg_job.encode_job.p_metadata->is_statsdebug_ae_params_valid = + jpg_job.encode_job.cam_exif_params.debug_params->ae_debug_params_valid; + jpg_job.encode_job.p_metadata->is_statsdebug_awb_params_valid = + jpg_job.encode_job.cam_exif_params.debug_params->awb_debug_params_valid; + jpg_job.encode_job.p_metadata->is_statsdebug_af_params_valid = + jpg_job.encode_job.cam_exif_params.debug_params->af_debug_params_valid; + jpg_job.encode_job.p_metadata->is_statsdebug_asd_params_valid = + jpg_job.encode_job.cam_exif_params.debug_params->asd_debug_params_valid; + jpg_job.encode_job.p_metadata->is_statsdebug_stats_params_valid = + jpg_job.encode_job.cam_exif_params.debug_params->stats_debug_params_valid; + jpg_job.encode_job.p_metadata->is_statsdebug_bestats_params_valid = + jpg_job.encode_job.cam_exif_params.debug_params->bestats_debug_params_valid; + jpg_job.encode_job.p_metadata->is_statsdebug_bhist_params_valid = + jpg_job.encode_job.cam_exif_params.debug_params->bhist_debug_params_valid; + jpg_job.encode_job.p_metadata->is_statsdebug_3a_tuning_params_valid = + jpg_job.encode_job.cam_exif_params.debug_params->q3a_tuning_debug_params_valid; + + if (jpg_job.encode_job.cam_exif_params.debug_params->ae_debug_params_valid) { + jpg_job.encode_job.p_metadata->statsdebug_ae_data = + jpg_job.encode_job.cam_exif_params.debug_params->ae_debug_params; + } + if (jpg_job.encode_job.cam_exif_params.debug_params->awb_debug_params_valid) { + jpg_job.encode_job.p_metadata->statsdebug_awb_data = + jpg_job.encode_job.cam_exif_params.debug_params->awb_debug_params; + } + if (jpg_job.encode_job.cam_exif_params.debug_params->af_debug_params_valid) { + jpg_job.encode_job.p_metadata->statsdebug_af_data = + jpg_job.encode_job.cam_exif_params.debug_params->af_debug_params; + } + if (jpg_job.encode_job.cam_exif_params.debug_params->asd_debug_params_valid) { + jpg_job.encode_job.p_metadata->statsdebug_asd_data = + jpg_job.encode_job.cam_exif_params.debug_params->asd_debug_params; + } + if (jpg_job.encode_job.cam_exif_params.debug_params->stats_debug_params_valid) { + jpg_job.encode_job.p_metadata->statsdebug_stats_buffer_data = + jpg_job.encode_job.cam_exif_params.debug_params->stats_debug_params; + } + if (jpg_job.encode_job.cam_exif_params.debug_params->bestats_debug_params_valid) { + jpg_job.encode_job.p_metadata->statsdebug_bestats_buffer_data = + jpg_job.encode_job.cam_exif_params.debug_params->bestats_debug_params; + } + if (jpg_job.encode_job.cam_exif_params.debug_params->bhist_debug_params_valid) { + jpg_job.encode_job.p_metadata->statsdebug_bhist_data = + jpg_job.encode_job.cam_exif_params.debug_params->bhist_debug_params; + } + if (jpg_job.encode_job.cam_exif_params.debug_params->q3a_tuning_debug_params_valid) { + jpg_job.encode_job.p_metadata->statsdebug_3a_tuning_data = + jpg_job.encode_job.cam_exif_params.debug_params->q3a_tuning_debug_params; + } + } + } else { + LOGW("Metadata is null"); + } + + // Multi image info + if (hal_obj->isDeviceLinked() == TRUE) { + jpg_job.encode_job.multi_image_info.type = MM_JPEG_TYPE_JPEG; + jpg_job.encode_job.multi_image_info.num_of_images = 1; + jpg_job.encode_job.multi_image_info.enable_metadata = 1; + if (hal_obj->isMainCamera() == TRUE) { + jpg_job.encode_job.multi_image_info.is_primary = 1; + } else { + jpg_job.encode_job.multi_image_info.is_primary = 0; + } + } + + jpg_job.encode_job.hal_version = CAM_HAL_V3; + + //Start jpeg encoding + ret = mJpegHandle.start_job(&jpg_job, &jobId); + if (jpg_job.encode_job.cam_exif_params.debug_params) { + free(jpg_job.encode_job.cam_exif_params.debug_params); + } + if (ret == NO_ERROR) { + // remember job info + jpeg_job_data->jobId = jobId; + } + + LOGD("X"); + return ret; +} + +/*=========================================================================== + * FUNCTION : dataProcessRoutine + * + * DESCRIPTION: data process routine that handles input data either from input + * Jpeg Queue to do jpeg encoding, or from input PP Queue to do + * reprocess. + * + * PARAMETERS : + * @data : user data ptr (QCamera3PostProcessor) + * + * RETURN : None + *==========================================================================*/ +void *QCamera3PostProcessor::dataProcessRoutine(void *data) +{ + int running = 1; + int ret; + uint8_t is_active = FALSE; + uint8_t needNewSess = TRUE; + mm_camera_super_buf_t *meta_buffer = NULL; + LOGD("E"); + QCamera3PostProcessor *pme = (QCamera3PostProcessor *)data; + QCameraCmdThread *cmdThread = &pme->m_dataProcTh; + cmdThread->setName("cam_data_proc"); + + do { + do { + ret = cam_sem_wait(&cmdThread->cmd_sem); + if (ret != 0 && errno != EINVAL) { + LOGE("cam_sem_wait error (%s)", + strerror(errno)); + return NULL; + } + } while (ret != 0); + + // we got notified about new cmd avail in cmd queue + camera_cmd_type_t cmd = cmdThread->getCmd(); + switch (cmd) { + case CAMERA_CMD_TYPE_START_DATA_PROC: + LOGH("start data proc"); + is_active = TRUE; + needNewSess = TRUE; + + pme->m_ongoingPPQ.init(); + pme->m_inputJpegQ.init(); + pme->m_inputPPQ.init(); + pme->m_inputFWKPPQ.init(); + pme->m_inputMetaQ.init(); + cam_sem_post(&cmdThread->sync_sem); + + break; + case CAMERA_CMD_TYPE_STOP_DATA_PROC: + { + LOGH("stop data proc"); + is_active = FALSE; + + // cancel all ongoing jpeg jobs + qcamera_hal3_jpeg_data_t *jpeg_job = + (qcamera_hal3_jpeg_data_t *)pme->m_ongoingJpegQ.dequeue(); + while (jpeg_job != NULL) { + pme->mJpegHandle.abort_job(jpeg_job->jobId); + + pme->releaseJpegJobData(jpeg_job); + free(jpeg_job); + + jpeg_job = (qcamera_hal3_jpeg_data_t *)pme->m_ongoingJpegQ.dequeue(); + } + + // destroy jpeg encoding session + if ( 0 < pme->mJpegSessionId ) { + pme->mJpegHandle.destroy_session(pme->mJpegSessionId); + pme->mJpegSessionId = 0; + } + + needNewSess = TRUE; + + // flush ongoing postproc Queue + pme->m_ongoingPPQ.flush(); + + // flush input jpeg Queue + pme->m_inputJpegQ.flush(); + + // flush input Postproc Queue + pme->m_inputPPQ.flush(); + + // flush framework input Postproc Queue + pme->m_inputFWKPPQ.flush(); + + pme->m_inputMetaQ.flush(); + + // signal cmd is completed + cam_sem_post(&cmdThread->sync_sem); + } + break; + case CAMERA_CMD_TYPE_DO_NEXT_JOB: + { + LOGH("Do next job, active is %d", is_active); + /* needNewSess is set to TRUE as postproc is not re-STARTed + * anymore for every captureRequest */ + needNewSess = TRUE; + if (is_active == TRUE) { + // check if there is any ongoing jpeg jobs + if (pme->m_ongoingJpegQ.isEmpty()) { + LOGD("ongoing jpeg queue is empty so doing the jpeg job"); + // no ongoing jpeg job, we are fine to send jpeg encoding job + qcamera_hal3_jpeg_data_t *jpeg_job = + (qcamera_hal3_jpeg_data_t *)pme->m_inputJpegQ.dequeue(); + + if (NULL != jpeg_job) { + // add into ongoing jpeg job Q + pme->m_ongoingJpegQ.enqueue((void *)jpeg_job); + + if (jpeg_job->fwk_frame) { + ret = pme->encodeFWKData(jpeg_job, needNewSess); + } else { + ret = pme->encodeData(jpeg_job, needNewSess); + } + if (NO_ERROR != ret) { + // dequeue the last one + pme->m_ongoingJpegQ.dequeue(false); + + pme->releaseJpegJobData(jpeg_job); + free(jpeg_job); + } + } + } + + // check if there are any framework pp jobs + if (!pme->m_inputFWKPPQ.isEmpty()) { + qcamera_fwk_input_pp_data_t *fwk_frame = + (qcamera_fwk_input_pp_data_t *) pme->m_inputFWKPPQ.dequeue(); + if (NULL != fwk_frame) { + qcamera_hal3_pp_data_t *pp_job = + (qcamera_hal3_pp_data_t *)malloc(sizeof(qcamera_hal3_pp_data_t)); + jpeg_settings_t *jpeg_settings = + (jpeg_settings_t *)pme->m_jpegSettingsQ.dequeue(); + if (pp_job != NULL) { + memset(pp_job, 0, sizeof(qcamera_hal3_pp_data_t)); + pp_job->jpeg_settings = jpeg_settings; + if (pme->m_pReprocChannel != NULL) { + if (NO_ERROR != pme->m_pReprocChannel->overrideFwkMetadata(fwk_frame)) { + LOGE("Failed to extract output crop"); + } + // add into ongoing PP job Q + pp_job->fwk_src_frame = fwk_frame; + pme->m_ongoingPPQ.enqueue((void *)pp_job); + ret = pme->m_pReprocChannel->doReprocessOffline(fwk_frame); + if (NO_ERROR != ret) { + // remove from ongoing PP job Q + pme->m_ongoingPPQ.dequeue(false); + } + } else { + LOGE("Reprocess channel is NULL"); + ret = -1; + } + } else { + LOGE("no mem for qcamera_hal3_pp_data_t"); + ret = -1; + } + + if (0 != ret) { + // free pp_job + if (pp_job != NULL) { + free(pp_job); + } + // free frame + if (fwk_frame != NULL) { + free(fwk_frame); + } + } + } + } + + LOGH("dequeuing pp frame"); + pthread_mutex_lock(&pme->mReprocJobLock); + if(!pme->m_inputPPQ.isEmpty() && !pme->m_inputMetaQ.isEmpty()) { + qcamera_hal3_pp_buffer_t *pp_buffer = + (qcamera_hal3_pp_buffer_t *)pme->m_inputPPQ.dequeue(); + meta_buffer = + (mm_camera_super_buf_t *)pme->m_inputMetaQ.dequeue(); + jpeg_settings_t *jpeg_settings = + (jpeg_settings_t *)pme->m_jpegSettingsQ.dequeue(); + pthread_mutex_unlock(&pme->mReprocJobLock); + qcamera_hal3_pp_data_t *pp_job = + (qcamera_hal3_pp_data_t *)malloc(sizeof(qcamera_hal3_pp_data_t)); + if (pp_job == NULL) { + LOGE("no mem for qcamera_hal3_pp_data_t"); + ret = -1; + } else if (meta_buffer == NULL) { + LOGE("failed to dequeue from m_inputMetaQ"); + ret = -1; + } else if (pp_buffer == NULL) { + LOGE("failed to dequeue from m_inputPPQ"); + ret = -1; + } else if (pp_buffer != NULL){ + memset(pp_job, 0, sizeof(qcamera_hal3_pp_data_t)); + pp_job->src_frame = pp_buffer->input; + pp_job->src_metadata = meta_buffer; + if (meta_buffer->bufs[0] != NULL) { + pp_job->metadata = (metadata_buffer_t *) + meta_buffer->bufs[0]->buffer; + } + pp_job->jpeg_settings = jpeg_settings; + pme->m_ongoingPPQ.enqueue((void *)pp_job); + if (pme->m_pReprocChannel != NULL) { + mm_camera_buf_def_t *meta_buffer_arg = NULL; + meta_buffer_arg = meta_buffer->bufs[0]; + qcamera_fwk_input_pp_data_t fwk_frame; + memset(&fwk_frame, 0, sizeof(qcamera_fwk_input_pp_data_t)); + fwk_frame.frameNumber = pp_buffer->frameNumber; + ret = pme->m_pReprocChannel->overrideMetadata( + pp_buffer, meta_buffer_arg, + pp_job->jpeg_settings, + fwk_frame); + if (NO_ERROR == ret) { + // add into ongoing PP job Q + ret = pme->m_pReprocChannel->doReprocessOffline( + &fwk_frame, true); + if (NO_ERROR != ret) { + // remove from ongoing PP job Q + pme->m_ongoingPPQ.dequeue(false); + } + } + } else { + LOGE("No reprocess. Calling processPPData directly"); + ret = pme->processPPData(pp_buffer->input); + } + } + + if (0 != ret) { + // free pp_job + if (pp_job != NULL) { + free(pp_job); + } + // free frame + if (pp_buffer != NULL) { + if (pp_buffer->input) { + pme->releaseSuperBuf(pp_buffer->input); + free(pp_buffer->input); + } + free(pp_buffer); + } + //free metadata + if (NULL != meta_buffer) { + pme->m_parent->metadataBufDone(meta_buffer); + free(meta_buffer); + } + } else { + if (pp_buffer != NULL) { + free(pp_buffer); + } + } + } else { + pthread_mutex_unlock(&pme->mReprocJobLock); + } + } else { + // not active, simply return buf and do no op + qcamera_hal3_jpeg_data_t *jpeg_job = + (qcamera_hal3_jpeg_data_t *)pme->m_inputJpegQ.dequeue(); + if (NULL != jpeg_job) { + free(jpeg_job); + } + + qcamera_hal3_pp_buffer_t* pp_buf = + (qcamera_hal3_pp_buffer_t *)pme->m_inputPPQ.dequeue(); + if (NULL != pp_buf) { + if (pp_buf->input) { + pme->releaseSuperBuf(pp_buf->input); + free(pp_buf->input); + pp_buf->input = NULL; + } + free(pp_buf); + } + mm_camera_super_buf_t *metadata = (mm_camera_super_buf_t *)pme->m_inputMetaQ.dequeue(); + if (metadata != NULL) { + pme->m_parent->metadataBufDone(metadata); + free(metadata); + } + qcamera_fwk_input_pp_data_t *fwk_frame = + (qcamera_fwk_input_pp_data_t *) pme->m_inputFWKPPQ.dequeue(); + if (NULL != fwk_frame) { + free(fwk_frame); + } + } + } + break; + case CAMERA_CMD_TYPE_EXIT: + running = 0; + break; + default: + break; + } + } while (running); + LOGD("X"); + return NULL; +} + +/* EXIF related helper methods */ + +/*=========================================================================== + * FUNCTION : getRational + * + * DESCRIPTION: compose rational struct + * + * PARAMETERS : + * @rat : ptr to struct to store rational info + * @num :num of the rational + * @denom : denom of the rational + * + * RETURN : int32_t type of status + * NO_ERROR -- success + * none-zero failure code + *==========================================================================*/ +int32_t getRational(rat_t *rat, int num, int denom) +{ + if ((0 > num) || (0 >= denom)) { + LOGE("Negative values"); + return BAD_VALUE; + } + if (NULL == rat) { + LOGE("NULL rat input"); + return BAD_VALUE; + } + rat->num = (uint32_t)num; + rat->denom = (uint32_t)denom; + return NO_ERROR; +} + +/*=========================================================================== + * FUNCTION : parseGPSCoordinate + * + * DESCRIPTION: parse GPS coordinate string + * + * PARAMETERS : + * @coord_str : [input] coordinate string + * @coord : [output] ptr to struct to store coordinate + * + * RETURN : int32_t type of status + * NO_ERROR -- success + * none-zero failure code + *==========================================================================*/ +int parseGPSCoordinate(const char *coord_str, rat_t* coord) +{ + if(coord == NULL) { + LOGE("error, invalid argument coord == NULL"); + return BAD_VALUE; + } + double degF = atof(coord_str); + if (degF < 0) { + degF = -degF; + } + double minF = (degF - (int) degF) * 60; + double secF = (minF - (int) minF) * 60; + + getRational(&coord[0], (int)degF, 1); + getRational(&coord[1], (int)minF, 1); + getRational(&coord[2], (int)(secF * 10000), 10000); + return NO_ERROR; +} + +/*=========================================================================== + * FUNCTION : getExifDateTime + * + * DESCRIPTION: query exif date time + * + * PARAMETERS : + * @dateTime : string to store exif date time + * @subsecTime : string to store exif subsec time + * + * RETURN : int32_t type of status + * NO_ERROR -- success + * none-zero failure code + *==========================================================================*/ +int32_t getExifDateTime(String8 &dateTime, String8 &subsecTime) +{ + int32_t ret = NO_ERROR; + + //get time and date from system + struct timeval tv; + struct tm timeinfo_data; + + int res = gettimeofday(&tv, NULL); + if (0 == res) { + struct tm *timeinfo = localtime_r(&tv.tv_sec, &timeinfo_data); + if (NULL != timeinfo) { + //Write datetime according to EXIF Spec + //"YYYY:MM:DD HH:MM:SS" (20 chars including \0) + dateTime = String8::format("%04d:%02d:%02d %02d:%02d:%02d", + timeinfo->tm_year + 1900, timeinfo->tm_mon + 1, + timeinfo->tm_mday, timeinfo->tm_hour, + timeinfo->tm_min, timeinfo->tm_sec); + //Write subsec according to EXIF Sepc + subsecTime = String8::format("%06ld", tv.tv_usec); + } else { + LOGE("localtime_r() error"); + ret = UNKNOWN_ERROR; + } + } else if (-1 == res) { + LOGE("gettimeofday() error: %s", strerror(errno)); + ret = UNKNOWN_ERROR; + } else { + LOGE("gettimeofday() unexpected return code: %d", res); + ret = UNKNOWN_ERROR; + } + + return ret; +} + +/*=========================================================================== + * FUNCTION : getExifFocalLength + * + * DESCRIPTION: get exif focal length + * + * PARAMETERS : + * @focalLength : ptr to rational struct to store focal length + * @value : focal length value + * + * RETURN : int32_t type of status + * NO_ERROR -- success + * none-zero failure code + *==========================================================================*/ +int32_t getExifFocalLength(rat_t *focalLength, float value) +{ + int focalLengthValue = + (int)(value * FOCAL_LENGTH_DECIMAL_PRECISION); + return getRational(focalLength, focalLengthValue, FOCAL_LENGTH_DECIMAL_PRECISION); +} + +/*=========================================================================== + * FUNCTION : getExifExpTimeInfo + * + * DESCRIPTION: get exif exposure time information + * + * PARAMETERS : + * @expoTimeInfo : rational exposure time value + * @value : exposure time value + * RETURN : nt32_t type of status + * NO_ERROR -- success + * none-zero failure code + *==========================================================================*/ +int32_t getExifExpTimeInfo(rat_t *expoTimeInfo, int64_t value) +{ + + int64_t cal_exposureTime; + if (value != 0) + cal_exposureTime = value; + else + cal_exposureTime = 60; + + return getRational(expoTimeInfo, 1, (int)cal_exposureTime); +} + +/*=========================================================================== + * FUNCTION : getExifGpsProcessingMethod + * + * DESCRIPTION: get GPS processing method + * + * PARAMETERS : + * @gpsProcessingMethod : string to store GPS process method + * @count : length of the string + * @value : the value of the processing method + * + * RETURN : int32_t type of status + * NO_ERROR -- success + * none-zero failure code + *==========================================================================*/ +int32_t getExifGpsProcessingMethod(char *gpsProcessingMethod, + uint32_t &count, char* value) +{ + if(value != NULL) { + memcpy(gpsProcessingMethod, ExifAsciiPrefix, EXIF_ASCII_PREFIX_SIZE); + count = EXIF_ASCII_PREFIX_SIZE; + strlcpy(gpsProcessingMethod + EXIF_ASCII_PREFIX_SIZE, + value, + GPS_PROCESSING_METHOD_SIZE); + count += (uint32_t)strlen(value); + gpsProcessingMethod[count++] = '\0'; // increase 1 for the last NULL char + return NO_ERROR; + } else { + return BAD_VALUE; + } +} + +/*=========================================================================== + * FUNCTION : getExifLatitude + * + * DESCRIPTION: get exif latitude + * + * PARAMETERS : + * @latitude : ptr to rational struct to store latitude info + * @latRef : character to indicate latitude reference + * @value : value of the latitude + * + * RETURN : int32_t type of status + * NO_ERROR -- success + * none-zero failure code + *==========================================================================*/ +int32_t getExifLatitude(rat_t *latitude, char *latRef, double value) +{ + char str[30]; + snprintf(str, sizeof(str), "%f", value); + if(str[0] != '\0') { + parseGPSCoordinate(str, latitude); + + //set Latitude Ref + float latitudeValue = strtof(str, 0); + if(latitudeValue < 0.0f) { + latRef[0] = 'S'; + } else { + latRef[0] = 'N'; + } + latRef[1] = '\0'; + return NO_ERROR; + }else{ + return BAD_VALUE; + } +} + +/*=========================================================================== + * FUNCTION : getExifLongitude + * + * DESCRIPTION: get exif longitude + * + * PARAMETERS : + * @longitude : ptr to rational struct to store longitude info + * @lonRef : character to indicate longitude reference + * @value : value of the longitude + * + * RETURN : int32_t type of status + * NO_ERROR -- success + * none-zero failure code + *==========================================================================*/ +int32_t getExifLongitude(rat_t *longitude, char *lonRef, double value) +{ + char str[30]; + snprintf(str, sizeof(str), "%f", value); + if(str[0] != '\0') { + parseGPSCoordinate(str, longitude); + + //set Longitude Ref + float longitudeValue = strtof(str, 0); + if(longitudeValue < 0.0f) { + lonRef[0] = 'W'; + } else { + lonRef[0] = 'E'; + } + lonRef[1] = '\0'; + return NO_ERROR; + }else{ + return BAD_VALUE; + } +} + +/*=========================================================================== + * FUNCTION : getExifAltitude + * + * DESCRIPTION: get exif altitude + * + * PARAMETERS : + * @altitude : ptr to rational struct to store altitude info + * @altRef : character to indicate altitude reference + * @argValue : altitude value + * + * RETURN : int32_t type of status + * NO_ERROR -- success + * none-zero failure code + *==========================================================================*/ +int32_t getExifAltitude(rat_t *altitude, char *altRef, double argValue) +{ + char str[30]; + snprintf(str, sizeof(str), "%f", argValue); + if (str[0] != '\0') { + double value = atof(str); + *altRef = 0; + if(value < 0){ + *altRef = 1; + value = -value; + } + return getRational(altitude, (int)(value * 1000), 1000); + } else { + return BAD_VALUE; + } +} + +/*=========================================================================== + * FUNCTION : getExifGpsDateTimeStamp + * + * DESCRIPTION: get exif GPS date time stamp + * + * PARAMETERS : + * @gpsDateStamp : GPS date time stamp string + * @bufLen : length of the string + * @gpsTimeStamp : ptr to rational struct to store time stamp info + * @value : timestamp value + * + * RETURN : int32_t type of status + * NO_ERROR -- success + * none-zero failure code + *==========================================================================*/ +int32_t getExifGpsDateTimeStamp(char *gpsDateStamp, uint32_t bufLen, + rat_t *gpsTimeStamp, int64_t value) +{ + char str[30]; + snprintf(str, sizeof(str), "%lld", (long long int)value); + if(str[0] != '\0') { + time_t unixTime = (time_t)atol(str); + struct tm *UTCTimestamp = gmtime(&unixTime); + if (UTCTimestamp != NULL && gpsDateStamp != NULL + && gpsTimeStamp != NULL) { + strftime(gpsDateStamp, bufLen, "%Y:%m:%d", UTCTimestamp); + + getRational(&gpsTimeStamp[0], UTCTimestamp->tm_hour, 1); + getRational(&gpsTimeStamp[1], UTCTimestamp->tm_min, 1); + getRational(&gpsTimeStamp[2], UTCTimestamp->tm_sec, 1); + return NO_ERROR; + } else { + LOGE("Could not get the timestamp"); + return BAD_VALUE; + } + } else { + return BAD_VALUE; + } +} + +/*=========================================================================== + * FUNCTION : getExifExposureValue + * + * DESCRIPTION: get exif GPS date time stamp + * + * PARAMETERS : + * @exposure_val : rational exposure value + * @exposure_comp : exposure compensation + * @step : exposure step + * + * RETURN : int32_t type of status + * NO_ERROR -- success + * none-zero failure code + *==========================================================================*/ +int32_t getExifExposureValue(srat_t* exposure_val, int32_t exposure_comp, + cam_rational_type_t step) +{ + exposure_val->num = exposure_comp * step.numerator; + exposure_val->denom = step.denominator; + return 0; +} + +/*=========================================================================== + * FUNCTION : getExifData + * + * DESCRIPTION: get exif data to be passed into jpeg encoding + * + * PARAMETERS : + * @metadata : metadata of the encoding request + * @jpeg_settings : jpeg_settings for encoding + * @needJpegExifRotation: check if rotation need to added in EXIF + * + * RETURN : exif data from user setting and GPS + *==========================================================================*/ +QCamera3Exif *QCamera3PostProcessor::getExifData(metadata_buffer_t *metadata, + jpeg_settings_t *jpeg_settings, bool needJpegExifRotation) +{ + QCamera3Exif *exif = new QCamera3Exif(); + if (exif == NULL) { + LOGE("No memory for QCamera3Exif"); + return NULL; + } + QCamera3HardwareInterface* hal_obj = NULL; + if (m_parent != NULL) { + hal_obj = (QCamera3HardwareInterface*)m_parent->mUserData; + } else { + LOGE("m_parent is NULL, Error"); + return NULL; + } + + int32_t rc = NO_ERROR; + uint32_t count = 0; + + // add exif entries + String8 dateTime; + String8 subsecTime; + rc = getExifDateTime(dateTime, subsecTime); + if (rc == NO_ERROR) { + exif->addEntry(EXIFTAGID_DATE_TIME, EXIF_ASCII, + (uint32_t)(dateTime.length() + 1), (void *)dateTime.string()); + exif->addEntry(EXIFTAGID_EXIF_DATE_TIME_ORIGINAL, EXIF_ASCII, + (uint32_t)(dateTime.length() + 1), (void *)dateTime.string()); + exif->addEntry(EXIFTAGID_EXIF_DATE_TIME_DIGITIZED, EXIF_ASCII, + (uint32_t)(dateTime.length() + 1), (void *)dateTime.string()); + exif->addEntry(EXIFTAGID_SUBSEC_TIME, EXIF_ASCII, + (uint32_t)(subsecTime.length() + 1), (void *)subsecTime.string()); + exif->addEntry(EXIFTAGID_SUBSEC_TIME_ORIGINAL, EXIF_ASCII, + (uint32_t)(subsecTime.length() + 1), (void *)subsecTime.string()); + exif->addEntry(EXIFTAGID_SUBSEC_TIME_DIGITIZED, EXIF_ASCII, + (uint32_t)(subsecTime.length() + 1), (void *)subsecTime.string()); + } else { + LOGW("getExifDateTime failed"); + } + + + if (metadata != NULL) { + IF_META_AVAILABLE(float, focal_length, CAM_INTF_META_LENS_FOCAL_LENGTH, metadata) { + rat_t focalLength; + rc = getExifFocalLength(&focalLength, *focal_length); + if (rc == NO_ERROR) { + exif->addEntry(EXIFTAGID_FOCAL_LENGTH, + EXIF_RATIONAL, + 1, + (void *)&(focalLength)); + } else { + LOGW("getExifFocalLength failed"); + } + } + + IF_META_AVAILABLE(int32_t, isoSpeed, CAM_INTF_META_SENSOR_SENSITIVITY, metadata) { + int16_t fwk_isoSpeed = (int16_t) *isoSpeed; + exif->addEntry(EXIFTAGID_ISO_SPEED_RATING, EXIF_SHORT, 1, (void *) &(fwk_isoSpeed)); + } + + + IF_META_AVAILABLE(int64_t, sensor_exposure_time, + CAM_INTF_META_SENSOR_EXPOSURE_TIME, metadata) { + rat_t sensorExpTime; + rc = getExifExpTimeInfo(&sensorExpTime, *sensor_exposure_time); + if (rc == NO_ERROR){ + exif->addEntry(EXIFTAGID_EXPOSURE_TIME, + EXIF_RATIONAL, + 1, + (void *)&(sensorExpTime)); + } else { + LOGW("getExifExpTimeInfo failed"); + } + } + + char* jpeg_gps_processing_method = jpeg_settings->gps_processing_method; + if (strlen(jpeg_gps_processing_method) > 0) { + char gpsProcessingMethod[EXIF_ASCII_PREFIX_SIZE + + GPS_PROCESSING_METHOD_SIZE]; + count = 0; + rc = getExifGpsProcessingMethod(gpsProcessingMethod, + count, + jpeg_gps_processing_method); + if(rc == NO_ERROR) { + exif->addEntry(EXIFTAGID_GPS_PROCESSINGMETHOD, + EXIF_ASCII, + count, + (void *)gpsProcessingMethod); + } else { + LOGW("getExifGpsProcessingMethod failed"); + } + } + + if (jpeg_settings->gps_coordinates_valid) { + + //latitude + rat_t latitude[3]; + char latRef[2]; + rc = getExifLatitude(latitude, latRef, + jpeg_settings->gps_coordinates[0]); + if(rc == NO_ERROR) { + exif->addEntry(EXIFTAGID_GPS_LATITUDE, + EXIF_RATIONAL, + 3, + (void *)latitude); + exif->addEntry(EXIFTAGID_GPS_LATITUDE_REF, + EXIF_ASCII, + 2, + (void *)latRef); + } else { + LOGW("getExifLatitude failed"); + } + + //longitude + rat_t longitude[3]; + char lonRef[2]; + rc = getExifLongitude(longitude, lonRef, + jpeg_settings->gps_coordinates[1]); + if(rc == NO_ERROR) { + exif->addEntry(EXIFTAGID_GPS_LONGITUDE, + EXIF_RATIONAL, + 3, + (void *)longitude); + + exif->addEntry(EXIFTAGID_GPS_LONGITUDE_REF, + EXIF_ASCII, + 2, + (void *)lonRef); + } else { + LOGW("getExifLongitude failed"); + } + + //altitude + rat_t altitude; + char altRef; + rc = getExifAltitude(&altitude, &altRef, + jpeg_settings->gps_coordinates[2]); + if(rc == NO_ERROR) { + exif->addEntry(EXIFTAGID_GPS_ALTITUDE, + EXIF_RATIONAL, + 1, + (void *)&(altitude)); + + exif->addEntry(EXIFTAGID_GPS_ALTITUDE_REF, + EXIF_BYTE, + 1, + (void *)&altRef); + } else { + LOGW("getExifAltitude failed"); + } + } + + if (jpeg_settings->gps_timestamp_valid) { + + char gpsDateStamp[20]; + rat_t gpsTimeStamp[3]; + rc = getExifGpsDateTimeStamp(gpsDateStamp, 20, gpsTimeStamp, + jpeg_settings->gps_timestamp); + if(rc == NO_ERROR) { + exif->addEntry(EXIFTAGID_GPS_DATESTAMP, EXIF_ASCII, + (uint32_t)(strlen(gpsDateStamp) + 1), + (void *)gpsDateStamp); + + exif->addEntry(EXIFTAGID_GPS_TIMESTAMP, + EXIF_RATIONAL, + 3, + (void *)gpsTimeStamp); + } else { + LOGW("getExifGpsDataTimeStamp failed"); + } + } + + IF_META_AVAILABLE(int32_t, exposure_comp, CAM_INTF_PARM_EXPOSURE_COMPENSATION, metadata) { + IF_META_AVAILABLE(cam_rational_type_t, comp_step, CAM_INTF_PARM_EV_STEP, metadata) { + srat_t exposure_val; + rc = getExifExposureValue(&exposure_val, *exposure_comp, *comp_step); + if(rc == NO_ERROR) { + exif->addEntry(EXIFTAGID_EXPOSURE_BIAS_VALUE, + EXIF_SRATIONAL, + 1, + (void *)(&exposure_val)); + } else { + LOGW("getExifExposureValue failed "); + } + } + } + } else { + LOGW("no metadata provided "); + } + +#ifdef ENABLE_MODEL_INFO_EXIF + + char value[PROPERTY_VALUE_MAX]; + if (property_get("ro.product.manufacturer", value, "QCOM-AA") > 0) { + exif->addEntry(EXIFTAGID_MAKE, EXIF_ASCII, + (uint32_t)(strlen(value) + 1), (void *)value); + } else { + LOGW("getExifMaker failed"); + } + + if (property_get("ro.product.model", value, "QCAM-AA") > 0) { + exif->addEntry(EXIFTAGID_MODEL, EXIF_ASCII, + (uint32_t)(strlen(value) + 1), (void *)value); + } else { + LOGW("getExifModel failed"); + } + + if (property_get("ro.build.description", value, "QCAM-AA") > 0) { + exif->addEntry(EXIFTAGID_SOFTWARE, EXIF_ASCII, + (uint32_t)(strlen(value) + 1), (void *)value); + } else { + LOGW("getExifSoftware failed"); + } + +#endif + + if (jpeg_settings->image_desc_valid) { + if (exif->addEntry(EXIFTAGID_IMAGE_DESCRIPTION, EXIF_ASCII, + strlen(jpeg_settings->image_desc)+1, + (void *)jpeg_settings->image_desc)) { + LOGW("Adding IMAGE_DESCRIPTION tag failed"); + } + } + + if (needJpegExifRotation) { + int16_t orientation; + switch (jpeg_settings->jpeg_orientation) { + case 0: + orientation = 1; + break; + case 90: + orientation = 6; + break; + case 180: + orientation = 3; + break; + case 270: + orientation = 8; + break; + default: + orientation = 1; + break; + } + exif->addEntry(EXIFTAGID_ORIENTATION, + EXIF_SHORT, + 1, + (void *)&orientation); + exif->addEntry(EXIFTAGID_TN_ORIENTATION, + EXIF_SHORT, + 1, + (void *)&orientation); + + } + + return exif; +} + +/*=========================================================================== + * FUNCTION : QCamera3Exif + * + * DESCRIPTION: constructor of QCamera3Exif + * + * PARAMETERS : None + * + * RETURN : None + *==========================================================================*/ +QCamera3Exif::QCamera3Exif() + : m_nNumEntries(0) +{ + memset(m_Entries, 0, sizeof(m_Entries)); +} + +/*=========================================================================== + * FUNCTION : ~QCamera3Exif + * + * DESCRIPTION: deconstructor of QCamera3Exif. Will release internal memory ptr. + * + * PARAMETERS : None + * + * RETURN : None + *==========================================================================*/ +QCamera3Exif::~QCamera3Exif() +{ + for (uint32_t i = 0; i < m_nNumEntries; i++) { + switch (m_Entries[i].tag_entry.type) { + case EXIF_BYTE: + { + if (m_Entries[i].tag_entry.count > 1 && + m_Entries[i].tag_entry.data._bytes != NULL) { + free(m_Entries[i].tag_entry.data._bytes); + m_Entries[i].tag_entry.data._bytes = NULL; + } + } + break; + case EXIF_ASCII: + { + if (m_Entries[i].tag_entry.data._ascii != NULL) { + free(m_Entries[i].tag_entry.data._ascii); + m_Entries[i].tag_entry.data._ascii = NULL; + } + } + break; + case EXIF_SHORT: + { + if (m_Entries[i].tag_entry.count > 1 && + m_Entries[i].tag_entry.data._shorts != NULL) { + free(m_Entries[i].tag_entry.data._shorts); + m_Entries[i].tag_entry.data._shorts = NULL; + } + } + break; + case EXIF_LONG: + { + if (m_Entries[i].tag_entry.count > 1 && + m_Entries[i].tag_entry.data._longs != NULL) { + free(m_Entries[i].tag_entry.data._longs); + m_Entries[i].tag_entry.data._longs = NULL; + } + } + break; + case EXIF_RATIONAL: + { + if (m_Entries[i].tag_entry.count > 1 && + m_Entries[i].tag_entry.data._rats != NULL) { + free(m_Entries[i].tag_entry.data._rats); + m_Entries[i].tag_entry.data._rats = NULL; + } + } + break; + case EXIF_UNDEFINED: + { + if (m_Entries[i].tag_entry.data._undefined != NULL) { + free(m_Entries[i].tag_entry.data._undefined); + m_Entries[i].tag_entry.data._undefined = NULL; + } + } + break; + case EXIF_SLONG: + { + if (m_Entries[i].tag_entry.count > 1 && + m_Entries[i].tag_entry.data._slongs != NULL) { + free(m_Entries[i].tag_entry.data._slongs); + m_Entries[i].tag_entry.data._slongs = NULL; + } + } + break; + case EXIF_SRATIONAL: + { + if (m_Entries[i].tag_entry.count > 1 && + m_Entries[i].tag_entry.data._srats != NULL) { + free(m_Entries[i].tag_entry.data._srats); + m_Entries[i].tag_entry.data._srats = NULL; + } + } + break; + default: + LOGW("Error, Unknown type"); + break; + } + } +} + +/*=========================================================================== + * FUNCTION : addEntry + * + * DESCRIPTION: function to add an entry to exif data + * + * PARAMETERS : + * @tagid : exif tag ID + * @type : data type + * @count : number of data in uint of its type + * @data : input data ptr + * + * RETURN : int32_t type of status + * NO_ERROR -- success + * none-zero failure code + *==========================================================================*/ +int32_t QCamera3Exif::addEntry(exif_tag_id_t tagid, + exif_tag_type_t type, + uint32_t count, + void *data) +{ + int32_t rc = NO_ERROR; + if(m_nNumEntries >= MAX_HAL3_EXIF_TABLE_ENTRIES) { + LOGE("Number of entries exceeded limit"); + return NO_MEMORY; + } + + m_Entries[m_nNumEntries].tag_id = tagid; + m_Entries[m_nNumEntries].tag_entry.type = type; + m_Entries[m_nNumEntries].tag_entry.count = count; + m_Entries[m_nNumEntries].tag_entry.copy = 1; + switch (type) { + case EXIF_BYTE: + { + if (count > 1) { + uint8_t *values = (uint8_t *)malloc(count); + if (values == NULL) { + LOGE("No memory for byte array"); + rc = NO_MEMORY; + } else { + memcpy(values, data, count); + m_Entries[m_nNumEntries].tag_entry.data._bytes = values; + } + } else { + m_Entries[m_nNumEntries].tag_entry.data._byte = + *(uint8_t *)data; + } + } + break; + case EXIF_ASCII: + { + char *str = NULL; + str = (char *)malloc(count + 1); + if (str == NULL) { + LOGE("No memory for ascii string"); + rc = NO_MEMORY; + } else { + memset(str, 0, count + 1); + memcpy(str, data, count); + m_Entries[m_nNumEntries].tag_entry.data._ascii = str; + } + } + break; + case EXIF_SHORT: + { + uint16_t *exif_data = (uint16_t *)data; + if (count > 1) { + uint16_t *values = + (uint16_t *)malloc(count * sizeof(uint16_t)); + if (values == NULL) { + LOGE("No memory for short array"); + rc = NO_MEMORY; + } else { + memcpy(values, exif_data, count * sizeof(uint16_t)); + m_Entries[m_nNumEntries].tag_entry.data._shorts = values; + } + } else { + m_Entries[m_nNumEntries].tag_entry.data._short = + *(uint16_t *)data; + } + } + break; + case EXIF_LONG: + { + uint32_t *exif_data = (uint32_t *)data; + if (count > 1) { + uint32_t *values = + (uint32_t *)malloc(count * sizeof(uint32_t)); + if (values == NULL) { + LOGE("No memory for long array"); + rc = NO_MEMORY; + } else { + memcpy(values, exif_data, count * sizeof(uint32_t)); + m_Entries[m_nNumEntries].tag_entry.data._longs = values; + } + } else { + m_Entries[m_nNumEntries].tag_entry.data._long = + *(uint32_t *)data; + } + } + break; + case EXIF_RATIONAL: + { + rat_t *exif_data = (rat_t *)data; + if (count > 1) { + rat_t *values = (rat_t *)malloc(count * sizeof(rat_t)); + if (values == NULL) { + LOGE("No memory for rational array"); + rc = NO_MEMORY; + } else { + memcpy(values, exif_data, count * sizeof(rat_t)); + m_Entries[m_nNumEntries].tag_entry.data._rats = values; + } + } else { + m_Entries[m_nNumEntries].tag_entry.data._rat = + *(rat_t *)data; + } + } + break; + case EXIF_UNDEFINED: + { + uint8_t *values = (uint8_t *)malloc(count); + if (values == NULL) { + LOGE("No memory for undefined array"); + rc = NO_MEMORY; + } else { + memcpy(values, data, count); + m_Entries[m_nNumEntries].tag_entry.data._undefined = values; + } + } + break; + case EXIF_SLONG: + { + int32_t *exif_data = (int32_t *)data; + if (count > 1) { + int32_t *values = + (int32_t *)malloc(count * sizeof(int32_t)); + if (values == NULL) { + LOGE("No memory for signed long array"); + rc = NO_MEMORY; + } else { + memcpy(values, exif_data, count * sizeof(int32_t)); + m_Entries[m_nNumEntries].tag_entry.data._slongs =values; + } + } else { + m_Entries[m_nNumEntries].tag_entry.data._slong = + *(int32_t *)data; + } + } + break; + case EXIF_SRATIONAL: + { + srat_t *exif_data = (srat_t *)data; + if (count > 1) { + srat_t *values = (srat_t *)malloc(count * sizeof(srat_t)); + if (values == NULL) { + LOGE("No memory for sign rational array"); + rc = NO_MEMORY; + } else { + memcpy(values, exif_data, count * sizeof(srat_t)); + m_Entries[m_nNumEntries].tag_entry.data._srats = values; + } + } else { + m_Entries[m_nNumEntries].tag_entry.data._srat = + *(srat_t *)data; + } + } + break; + default: + LOGE("Error, Unknown type"); + break; + } + + // Increase number of entries + m_nNumEntries++; + return rc; +} + +}; // namespace qcamera |